Socket
Socket
Sign inDemoInstall

knifecycle

Package Overview
Dependencies
Maintainers
1
Versions
100
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

knifecycle - npm Package Compare versions

Comparing version 15.0.0 to 15.0.1

dist/build.d.ts

4

CHANGELOG.md

@@ -0,1 +1,5 @@

## [15.0.1](https://github.com/nfroidure/knifecycle/compare/v15.0.0...v15.0.1) (2023-05-28)
# [15.0.0](https://github.com/nfroidure/knifecycle/compare/v14.1.0...v15.0.0) (2023-05-28)

@@ -2,0 +6,0 @@

153

dist/build.js

@@ -1,2 +0,2 @@

import { SPECIAL_PROPS, parseDependencyDeclaration, initializer } from './util.js';
import { SPECIAL_PROPS, parseDependencyDeclaration, initializer, } from './util.js';
import { buildInitializationSequence } from './sequence.js';

@@ -18,8 +18,7 @@ /* Architecture Note #2: Build

initialize
*/ export default initializer({
*/
export default initializer({
name: 'buildInitializer',
type: 'service',
inject: [
'$autoload'
]
inject: ['$autoload'],
}, initInitializerBuilder);

@@ -40,52 +39,68 @@ /**

* });
*/ async function initInitializerBuilder({ $autoload }) {
*/
async function initInitializerBuilder({ $autoload, }) {
return buildInitializer;
/**
* Create a JavaScript module that initialize
* a set of dependencies with hardcoded
* import/awaits.
* @param {String[]} dependencies
* The main dependencies
* @return {Promise<String>}
* The JavaScript module content
* @example
* import initInitializerBuilder from 'knifecycle/dist/build';
*
* const buildInitializer = await initInitializerBuilder({
* $autoload: async () => {},
* });
*
* const content = await buildInitializer(['entryPoint']);
*/ async function buildInitializer(dependencies) {
const dependencyTrees = await Promise.all(dependencies.map((dependency)=>buildDependencyTree({
$autoload
}, dependency)));
* Create a JavaScript module that initialize
* a set of dependencies with hardcoded
* import/awaits.
* @param {String[]} dependencies
* The main dependencies
* @return {Promise<String>}
* The JavaScript module content
* @example
* import initInitializerBuilder from 'knifecycle/dist/build';
*
* const buildInitializer = await initInitializerBuilder({
* $autoload: async () => {},
* });
*
* const content = await buildInitializer(['entryPoint']);
*/
async function buildInitializer(dependencies) {
const dependencyTrees = await Promise.all(dependencies.map((dependency) => buildDependencyTree({ $autoload }, dependency)));
const dependenciesHash = buildDependenciesHash(dependencyTrees.filter(identity));
const batches = buildInitializationSequence({
__name: 'main',
__childNodes: dependencyTrees.filter(identity)
__childNodes: dependencyTrees.filter(identity),
});
batches.pop();
return `${batches.map((batch, index)=>`
// Definition batch #${index}${batch.map((name)=>{
if ('constant' === dependenciesHash[name].__initializer[SPECIAL_PROPS.TYPE]) {
return `
return `${batches
.map((batch, index) => `
// Definition batch #${index}${batch
.map((name) => {
if ('constant' ===
dependenciesHash[name].__initializer[SPECIAL_PROPS.TYPE]) {
return `
const ${name} = ${JSON.stringify(dependenciesHash[name].__initializer[SPECIAL_PROPS.VALUE], null, 2)};`;
}
return `
}
return `
import ${dependenciesHash[name].__initializerName} from '${dependenciesHash[name].__path}';`;
}).join('')}`).join('\n')}
})
.join('')}`)
.join('\n')}
export async function initialize(services = {}) {${batches.map((batch, index)=>`
export async function initialize(services = {}) {${batches
.map((batch, index) => `
// Initialization batch #${index}
const batch${index} = {${batch.map((name)=>{
if ('constant' === dependenciesHash[name].__initializer[SPECIAL_PROPS.TYPE]) {
return `
const batch${index} = {${batch
.map((name) => {
if ('constant' ===
dependenciesHash[name].__initializer[SPECIAL_PROPS.TYPE]) {
return `
${name}: Promise.resolve(${name}),`;
}
return `
${name}: ${dependenciesHash[name].__initializerName}({${dependenciesHash[name].__inject ? `${dependenciesHash[name].__inject.map(parseDependencyDeclaration).map(({ serviceName , mappedName })=>`
${serviceName}: services['${mappedName}'],`).join('')}` : ''}
})${'provider' === dependenciesHash[name].__type ? '.then(provider => provider.service)' : ''},`;
}).join('')}
}
return `
${name}: ${dependenciesHash[name].__initializerName}({${dependenciesHash[name].__inject
? `${dependenciesHash[name].__inject
.map(parseDependencyDeclaration)
.map(({ serviceName, mappedName }) => `
${serviceName}: services['${mappedName}'],`)
.join('')}`
: ''}
})${'provider' === dependenciesHash[name].__type
? '.then(provider => provider.service)'
: ''},`;
})
.join('')}
};

@@ -97,9 +112,15 @@

);
${batch.map((name)=>{
return `
${batch
.map((name) => {
return `
services['${name}'] = await batch${index}['${name}'];`;
}).join('')}
`).join('')}
return {${dependencies.map(parseDependencyDeclaration).map(({ serviceName , mappedName })=>`
${serviceName}: services['${mappedName}'],`).join('')}
})
.join('')}
`)
.join('')}
return {${dependencies
.map(parseDependencyDeclaration)
.map(({ serviceName, mappedName }) => `
${serviceName}: services['${mappedName}'],`)
.join('')}
};

@@ -110,25 +131,30 @@ }

}
async function buildDependencyTree({ $autoload }, dependencyDeclaration) {
const { mappedName , optional } = parseDependencyDeclaration(dependencyDeclaration);
async function buildDependencyTree({ $autoload, }, dependencyDeclaration) {
const { mappedName, optional } = parseDependencyDeclaration(dependencyDeclaration);
try {
const { path , initializer } = await $autoload(mappedName);
const { path, initializer } = await $autoload(mappedName);
const node = {
__name: mappedName,
__initializer: initializer,
__inject: initializer && initializer[SPECIAL_PROPS.INJECT] ? initializer[SPECIAL_PROPS.INJECT] : [],
__type: initializer && initializer[SPECIAL_PROPS.TYPE] ? initializer[SPECIAL_PROPS.TYPE] : 'provider',
__inject: initializer && initializer[SPECIAL_PROPS.INJECT]
? initializer[SPECIAL_PROPS.INJECT]
: [],
__type: initializer && initializer[SPECIAL_PROPS.TYPE]
? initializer[SPECIAL_PROPS.TYPE]
: 'provider',
__initializerName: 'init' + upperCaseFirst(mappedName),
__path: path,
__childNodes: []
__childNodes: [],
};
if (initializer[SPECIAL_PROPS.INJECT] && initializer[SPECIAL_PROPS.INJECT].length) {
const childNodes = await Promise.all(initializer[SPECIAL_PROPS.INJECT].map((childDependencyDeclaration)=>buildDependencyTree({
$autoload
}, childDependencyDeclaration)));
if (initializer[SPECIAL_PROPS.INJECT] &&
initializer[SPECIAL_PROPS.INJECT].length) {
const childNodes = await Promise.all(initializer[SPECIAL_PROPS.INJECT].map((childDependencyDeclaration) => buildDependencyTree({ $autoload }, childDependencyDeclaration)));
node.__childNodes = childNodes.filter(identity);
return node;
} else {
}
else {
return node;
}
} catch (err) {
}
catch (err) {
if (optional) {

@@ -141,3 +167,3 @@ return null;

function buildDependenciesHash(dependencyTrees, hash = {}) {
return dependencyTrees.reduce((hash, tree)=>buildHashFromNode(tree, hash), hash);
return dependencyTrees.reduce((hash, tree) => buildHashFromNode(tree, hash), hash);
}

@@ -150,3 +176,3 @@ function buildHashFromNode(node, hash = {}) {

}
(node?.__childNodes || []).forEach((childNode)=>{
(node?.__childNodes || []).forEach((childNode) => {
hash = buildHashFromNode(childNode, hash);

@@ -162,3 +188,2 @@ });

}
//# sourceMappingURL=build.js.map

@@ -6,6 +6,6 @@ import { describe, test } from '@jest/globals';

import { Knifecycle, initializer, constant } from './index.js';
describe('buildInitializer', ()=>{
describe('buildInitializer', () => {
async function aProvider() {
return {
service: 'PROVIDER_SERVICE'
service: 'PROVIDER_SERVICE',
};

@@ -18,21 +18,14 @@ }

type: 'service',
name: 'dep1'
name: 'dep1',
}, aProvider),
dep2: initializer({
inject: [
'dep1',
'NODE_ENV'
],
inject: ['dep1', 'NODE_ENV'],
type: 'provider',
name: 'dep2'
name: 'dep2',
}, aProvider),
dep3: initializer({
inject: [
'dep2',
'dep1',
'?depOpt'
],
inject: ['dep2', 'dep1', '?depOpt'],
type: 'service',
name: 'dep3'
}, aProvider)
name: 'dep3',
}, aProvider),
};

@@ -43,12 +36,14 @@ const initAutoloader = initializer({

inject: [],
singleton: true
}, async ()=>{
singleton: true,
}, async () => {
return async function $autoload(name) {
return mockedDepsHash[name] ? Promise.resolve({
path: `./services/${name}`,
initializer: mockedDepsHash[name]
}) : Promise.reject(new YError('E_UNMATCHED_DEPENDENCY', name));
return mockedDepsHash[name]
? Promise.resolve({
path: `./services/${name}`,
initializer: mockedDepsHash[name],
})
: Promise.reject(new YError('E_UNMATCHED_DEPENDENCY', name));
};
});
test('should build an initialization module', async ()=>{
test('should build an initialization module', async () => {
const $ = new Knifecycle();

@@ -58,9 +53,4 @@ $.register(constant('PWD', '~/my-project'));

$.register(initInitializerBuilder);
const { buildInitializer } = await $.run([
'buildInitializer'
]);
const content = await buildInitializer([
'dep1',
'finalMappedDep>dep3'
]);
const { buildInitializer } = await $.run(['buildInitializer']);
const content = await buildInitializer(['dep1', 'finalMappedDep>dep3']);
assert.equal(content, `

@@ -132,3 +122,2 @@ // Definition batch #0

});
//# sourceMappingURL=build.test.js.map

@@ -1,2 +0,3 @@

/* eslint max-len: ["warn", { "ignoreComments": true }] @typescript-eslint/no-this-alias: "warn" */ import { SPECIAL_PROPS, SPECIAL_PROPS_PREFIX, DECLARATION_SEPARATOR, OPTIONAL_FLAG, ALLOWED_INITIALIZER_TYPES, ALLOWED_SPECIAL_PROPS, parseInjections, readFunctionName, reuseSpecialProps, parseName, name, autoName, inject, useInject, mergeInject, autoInject, alsoInject, type, extra, singleton, initializer, constant, service, autoService, provider, autoProvider, wrapInitializer, handler, autoHandler, parseDependencyDeclaration, stringifyDependencyDeclaration, unwrapInitializerProperties } from './util.js';
/* eslint max-len: ["warn", { "ignoreComments": true }] @typescript-eslint/no-this-alias: "warn" */
import { SPECIAL_PROPS, SPECIAL_PROPS_PREFIX, DECLARATION_SEPARATOR, OPTIONAL_FLAG, ALLOWED_INITIALIZER_TYPES, ALLOWED_SPECIAL_PROPS, parseInjections, readFunctionName, reuseSpecialProps, parseName, name, autoName, inject, useInject, mergeInject, autoInject, alsoInject, type, extra, singleton, initializer, constant, service, autoService, provider, autoProvider, wrapInitializer, handler, autoHandler, parseDependencyDeclaration, stringifyDependencyDeclaration, unwrapInitializerProperties, } from './util.js';
import initInitializerBuilder from './build.js';

@@ -39,3 +40,4 @@ import { YError } from 'yerror';

set as a property.
*/ /* Architecture Note #1.1: OOP
*/
/* Architecture Note #1.1: OOP
The `knifecycle` use case is one of the rare use case where

@@ -49,3 +51,4 @@ [OOP](https://en.wikipedia.org/wiki/Object-oriented_programming)

your application global states.
*/ class Knifecycle {
*/
class Knifecycle {
_silosCounter;

@@ -60,11 +63,12 @@ _silosContexts;

/**
* Create a new Knifecycle instance
* @return {Knifecycle}
* The Knifecycle instance
* @example
*
* import Knifecycle from 'knifecycle'
*
* const $ = new Knifecycle();
*/ constructor(){
* Create a new Knifecycle instance
* @return {Knifecycle}
* The Knifecycle instance
* @example
*
* import Knifecycle from 'knifecycle'
*
* const $ = new Knifecycle();
*/
constructor() {
this._silosCounter = 0;

@@ -78,10 +82,6 @@ this._silosContexts = new Set();

this.register(constant(INSTANCE, this));
const initInjectorProvider = provider(async ({ $siloContext })=>({
service: async (dependenciesDeclarations)=>_buildFinalHash(await this._initializeDependencies($siloContext, $siloContext.name, dependenciesDeclarations, {
injectorContext: true,
autoloading: false
}), dependenciesDeclarations)
}), INJECTOR, [
SILO_CONTEXT
], // Despite its global definition, the injector
const initInjectorProvider = provider(async ({ $siloContext }) => ({
service: async (dependenciesDeclarations) => _buildFinalHash(await this._initializeDependencies($siloContext, $siloContext.name, dependenciesDeclarations, { injectorContext: true, autoloading: false }), dependenciesDeclarations),
}), INJECTOR, [SILO_CONTEXT],
// Despite its global definition, the injector
// depends on the silo context and then needs

@@ -93,31 +93,33 @@ // to be instanciated once per silo.

/* Architecture Note #1.3: Registering initializers
The first step to use `knifecycle` is to create a new
`Knifecycle` instance and register the previously
created initializers.
Initializers can be of three types:
- constants: a `constant` initializer resolves to
a constant value.
- services: a `service` initializer directly
resolve to the actual service it builds. It can
be objects, functions or litteral values.
- providers: they instead resolve to an object that
contains the service built into the `service` property
but also an optional `dispose` property exposing a
method to properly stop the service and a
`fatalErrorPromise` that will be rejected if an
unrecoverable error happens.
Initializers can be declared as singletons. This means
that they will be instanciated once for all for each
executions silos using them (we will cover this
topic later on).
*/ /**
* Register an initializer
* @param {Function} initializer
* An initializer
* @return {Knifecycle}
* The Knifecycle instance (for chaining)
*/ register(initializer) {
The first step to use `knifecycle` is to create a new
`Knifecycle` instance and register the previously
created initializers.
Initializers can be of three types:
- constants: a `constant` initializer resolves to
a constant value.
- services: a `service` initializer directly
resolve to the actual service it builds. It can
be objects, functions or litteral values.
- providers: they instead resolve to an object that
contains the service built into the `service` property
but also an optional `dispose` property exposing a
method to properly stop the service and a
`fatalErrorPromise` that will be rejected if an
unrecoverable error happens.
Initializers can be declared as singletons. This means
that they will be instanciated once for all for each
executions silos using them (we will cover this
topic later on).
*/
/**
* Register an initializer
* @param {Function} initializer
* An initializer
* @return {Knifecycle}
* The Knifecycle instance (for chaining)
*/
register(initializer) {
if (this.shutdownPromise) {

@@ -135,5 +137,5 @@ throw new YError(E_INSTANCE_DESTROYED);

}
initializer = provider(async ()=>({
service: value
}), initializer[SPECIAL_PROPS.NAME], [], true);
initializer = provider(async () => ({
service: value,
}), initializer[SPECIAL_PROPS.NAME], [], true);
// Needed for the build utils to still recognize

@@ -143,3 +145,4 @@ // this initializer as a constant value

initializer[SPECIAL_PROPS.TYPE] = 'constant';
} else if ('undefined' !== typeof initializer[SPECIAL_PROPS.VALUE]) {
}
else if ('undefined' !== typeof initializer[SPECIAL_PROPS.VALUE]) {
throw new YError(E_BAD_VALUED_NON_CONSTANT_INITIALIZER, initializer[SPECIAL_PROPS.NAME]);

@@ -154,14 +157,16 @@ }

}
const initializerDependsOfItself = initializer[SPECIAL_PROPS.INJECT].map(_pickServiceNameFromDeclaration).includes(initializer[SPECIAL_PROPS.NAME]);
const initializerDependsOfItself = initializer[SPECIAL_PROPS.INJECT]
.map(_pickServiceNameFromDeclaration)
.includes(initializer[SPECIAL_PROPS.NAME]);
if (initializerDependsOfItself) {
throw new YError(E_CIRCULAR_DEPENDENCY, initializer[SPECIAL_PROPS.NAME]);
}
initializer[SPECIAL_PROPS.INJECT].forEach((dependencyDeclaration)=>{
initializer[SPECIAL_PROPS.INJECT].forEach((dependencyDeclaration) => {
this._lookupCircularDependencies(initializer[SPECIAL_PROPS.NAME], dependencyDeclaration);
});
if (this._initializers.has(initializer[SPECIAL_PROPS.NAME])) {
const initializedAsSingleton = this._singletonsServicesHandles.has(initializer[SPECIAL_PROPS.NAME]) && this._singletonsServicesDescriptors.has(initializer[SPECIAL_PROPS.NAME]) && !this._singletonsServicesDescriptors.get(initializer[SPECIAL_PROPS.NAME])?.preloaded;
const initializedAsInstance = [
...this._silosContexts.values()
].some((siloContext)=>siloContext.servicesSequence.some((sequence)=>sequence.includes(initializer[SPECIAL_PROPS.NAME])));
const initializedAsSingleton = this._singletonsServicesHandles.has(initializer[SPECIAL_PROPS.NAME]) &&
this._singletonsServicesDescriptors.has(initializer[SPECIAL_PROPS.NAME]) &&
!this._singletonsServicesDescriptors.get(initializer[SPECIAL_PROPS.NAME])?.preloaded;
const initializedAsInstance = [...this._silosContexts.values()].some((siloContext) => siloContext.servicesSequence.some((sequence) => sequence.includes(initializer[SPECIAL_PROPS.NAME])));
if (initializedAsSingleton || initializedAsInstance) {

@@ -171,3 +176,4 @@ throw new YError('E_INITIALIZER_ALREADY_INSTANCIATED', initializer[SPECIAL_PROPS.NAME]);

debug(`Overridden an initializer: ${initializer[SPECIAL_PROPS.NAME]}`);
} else {
}
else {
debug(`Registered an initializer: ${initializer[SPECIAL_PROPS.NAME]}`);

@@ -185,3 +191,3 @@ }

// change functions to empty litteral objects
promise: initializer({})
promise: initializer({}),
});

@@ -199,8 +205,8 @@ }

declarationsStacks = declarationsStacks.concat(dependencyDeclaration);
dependencyProvider[SPECIAL_PROPS.INJECT].forEach((childDependencyDeclaration)=>{
dependencyProvider[SPECIAL_PROPS.INJECT].forEach((childDependencyDeclaration) => {
const childServiceName = _pickServiceNameFromDeclaration(childDependencyDeclaration);
if (rootServiceName === childServiceName) {
throw new YError(E_CIRCULAR_DEPENDENCY, ...[
rootServiceName
].concat(declarationsStacks).concat(childDependencyDeclaration));
throw new YError(E_CIRCULAR_DEPENDENCY, ...[rootServiceName]
.concat(declarationsStacks)
.concat(childDependencyDeclaration));
}

@@ -211,37 +217,40 @@ this._lookupCircularDependencies(rootServiceName, childDependencyDeclaration, declarationsStacks);

/**
* Outputs a Mermaid compatible dependency graph of the declared services.
* See [Mermaid docs](https://github.com/knsv/mermaid)
* @param {Object} options
* Options for generating the graph (destructured)
* @param {Array<Object>} options.shapes
* Various shapes to apply
* @param {Array<Object>} options.styles
* Various styles to apply
* @param {Object} options.classes
* A hash of various classes contents
* @return {String}
* Returns a string containing the Mermaid dependency graph
* @example
*
* import Knifecycle, { inject, constant, service } from 'knifecycle';
* import appInitializer from './app';
*
* const $ = new Knifecycle();
*
* $.register(constant('ENV', process.env));
* $.register(constant('OS', require('os')));
* $.register(service('app', inject(['ENV', 'OS'], appInitializer)));
* $.toMermaidGraph();
*
* // returns
* graph TD
* app-->ENV
* app-->OS
*/ toMermaidGraph({ shapes =[] , styles =[] , classes ={} } = {
* Outputs a Mermaid compatible dependency graph of the declared services.
* See [Mermaid docs](https://github.com/knsv/mermaid)
* @param {Object} options
* Options for generating the graph (destructured)
* @param {Array<Object>} options.shapes
* Various shapes to apply
* @param {Array<Object>} options.styles
* Various styles to apply
* @param {Object} options.classes
* A hash of various classes contents
* @return {String}
* Returns a string containing the Mermaid dependency graph
* @example
*
* import Knifecycle, { inject, constant, service } from 'knifecycle';
* import appInitializer from './app';
*
* const $ = new Knifecycle();
*
* $.register(constant('ENV', process.env));
* $.register(constant('OS', require('os')));
* $.register(service('app', inject(['ENV', 'OS'], appInitializer)));
* $.toMermaidGraph();
*
* // returns
* graph TD
* app-->ENV
* app-->OS
*/
toMermaidGraph({ shapes = [], styles = [], classes = {}, } = {
shapes: [],
styles: [],
classes: {}
classes: {},
}) {
const servicesProviders = this._initializers;
const links = Array.from(servicesProviders.keys()).filter((provider)=>!provider.startsWith('$')).reduce((links, serviceName)=>{
const links = Array.from(servicesProviders.keys())
.filter((provider) => !provider.startsWith('$'))
.reduce((links, serviceName) => {
const serviceProvider = servicesProviders.get(serviceName);

@@ -251,8 +260,5 @@ if (!serviceProvider[SPECIAL_PROPS.INJECT].length) {

}
return links.concat(serviceProvider[SPECIAL_PROPS.INJECT].map((dependencyDeclaration)=>{
return links.concat(serviceProvider[SPECIAL_PROPS.INJECT].map((dependencyDeclaration) => {
const dependedServiceName = _pickServiceNameFromDeclaration(dependencyDeclaration);
return {
serviceName,
dependedServiceName
};
return { serviceName, dependedServiceName };
}));

@@ -264,37 +270,41 @@ }, []);

}
return [
'graph TD'
].concat(links.map(({ serviceName , dependedServiceName })=>` ${_applyShapes(shapes, serviceName) || serviceName}-->${_applyShapes(shapes, dependedServiceName) || dependedServiceName}`)).concat(Object.keys(classes).map((className)=>` classDef ${className} ${classes[className]}`)).concat(Object.keys(classesApplications).map((serviceName)=>` class ${serviceName} ${classesApplications[serviceName]};`)).join('\n');
return ['graph TD']
.concat(links.map(({ serviceName, dependedServiceName }) => ` ${_applyShapes(shapes, serviceName) || serviceName}-->${_applyShapes(shapes, dependedServiceName) || dependedServiceName}`))
.concat(Object.keys(classes).map((className) => ` classDef ${className} ${classes[className]}`))
.concat(Object.keys(classesApplications).map((serviceName) => ` class ${serviceName} ${classesApplications[serviceName]};`))
.join('\n');
}
/* Architecture Note #1.4: Execution silos
Once every initializers are registered, we need a way to bring
them to life. Execution silos are where the magic happens.
For each call of the `run` method with given dependencies,
a new silo is created and the required environment to
run the actual code is leveraged.
Depending on your application design, you could run it
in only one execution silo or into several ones
according to the isolation level your wish to reach.
*/ /**
* Creates a new execution silo
* @param {String[]} dependenciesDeclarations
* Service name.
* @return {Promise}
* Service descriptor promise
* @example
*
* import Knifecycle, { constant } from 'knifecycle'
*
* const $ = new Knifecycle();
*
* $.register(constant('ENV', process.env));
* $.run(['ENV'])
* .then(({ ENV }) => {
* // Here goes your code
* })
*/ async run(dependenciesDeclarations) {
Once every initializers are registered, we need a way to bring
them to life. Execution silos are where the magic happens.
For each call of the `run` method with given dependencies,
a new silo is created and the required environment to
run the actual code is leveraged.
Depending on your application design, you could run it
in only one execution silo or into several ones
according to the isolation level your wish to reach.
*/
/**
* Creates a new execution silo
* @param {String[]} dependenciesDeclarations
* Service name.
* @return {Promise}
* Service descriptor promise
* @example
*
* import Knifecycle, { constant } from 'knifecycle'
*
* const $ = new Knifecycle();
*
* $.register(constant('ENV', process.env));
* $.run(['ENV'])
* .then(({ ENV }) => {
* // Here goes your code
* })
*/
async run(dependenciesDeclarations) {
const _this = this;
const internalDependencies = [
...new Set(dependenciesDeclarations.concat(DISPOSE))
...new Set(dependenciesDeclarations.concat(DISPOSE)),
];

@@ -306,3 +316,3 @@ const siloContext = {

servicesShutdownsPromises: new Map(),
errorsPromises: []
errorsPromises: [],
};

@@ -315,18 +325,20 @@ if (this.shutdownPromise) {

service: {
promise: new Promise((_resolve, reject)=>{
siloContext.throwFatalError = (err)=>{
promise: new Promise((_resolve, reject) => {
siloContext.throwFatalError = (err) => {
debug('Handled a fatal error', err);
reject(err);
};
})
}
}),
},
}));
// Make the siloContext available for internal injections
siloContext.servicesDescriptors.set(SILO_CONTEXT, Promise.resolve({
service: siloContext
service: siloContext,
}));
// Create a provider for the shutdown special dependency
siloContext.servicesDescriptors.set(DISPOSE, Promise.resolve({
service: async ()=>{
siloContext.shutdownPromise = siloContext.shutdownPromise || _shutdownNextServices(siloContext.servicesSequence);
service: async () => {
siloContext.shutdownPromise =
siloContext.shutdownPromise ||
_shutdownNextServices(siloContext.servicesSequence);
debug('Shutting down services');

@@ -340,6 +352,8 @@ await siloContext.shutdownPromise;

}
await Promise.all(reversedServiceSequence.pop().map(async (serviceName)=>{
await Promise.all(reversedServiceSequence.pop().map(async (serviceName) => {
const singletonServiceDescriptor = await _this._pickupSingletonServiceDescriptorPromise(serviceName);
const serviceDescriptor = singletonServiceDescriptor || await siloContext.servicesDescriptors.get(serviceName);
let serviceShutdownPromise = _this._singletonsServicesShutdownsPromises.get(serviceName) || siloContext.servicesShutdownsPromises.get(serviceName);
const serviceDescriptor = singletonServiceDescriptor ||
(await siloContext.servicesDescriptors.get(serviceName));
let serviceShutdownPromise = _this._singletonsServicesShutdownsPromises.get(serviceName) ||
siloContext.servicesShutdownsPromises.get(serviceName);
if (serviceShutdownPromise) {

@@ -349,3 +363,3 @@ debug('Reusing a service shutdown promise:', serviceName);

}
if (reversedServiceSequence.some((servicesDeclarations)=>servicesDeclarations.includes(serviceName))) {
if (reversedServiceSequence.some((servicesDeclarations) => servicesDeclarations.includes(serviceName))) {
debug('Delaying service shutdown:', serviceName);

@@ -364,3 +378,5 @@ return Promise.resolve();

debug('Shutting down a service:', serviceName);
serviceShutdownPromise = serviceDescriptor?.dispose ? serviceDescriptor.dispose() : Promise.resolve();
serviceShutdownPromise = serviceDescriptor?.dispose
? serviceDescriptor.dispose()
: Promise.resolve();
if (singletonServiceDescriptor) {

@@ -375,9 +391,6 @@ _this._singletonsServicesShutdownsPromises.set(serviceName, serviceShutdownPromise);

},
dispose: Promise.resolve.bind(Promise)
dispose: Promise.resolve.bind(Promise),
}));
this._silosContexts.add(siloContext);
const servicesHash = await this._initializeDependencies(siloContext, siloContext.name, internalDependencies, {
injectorContext: false,
autoloading: false
});
const servicesHash = await this._initializeDependencies(siloContext, siloContext.name, internalDependencies, { injectorContext: false, autoloading: false });
debug('Handling fatal errors:', siloContext.errorsPromises);

@@ -388,26 +401,28 @@ Promise.all(siloContext.errorsPromises).catch(siloContext.throwFatalError);

/**
* Destroy the Knifecycle instance
* @return {Promise}
* Full destruction promise
* @example
*
* import Knifecycle, { constant } from 'knifecycle'
*
* const $ = new Knifecycle();
*
* $.register(constant('ENV', process.env));
* $.run(['ENV'])
* .then(({ ENV }) => {
* // Here goes your code
*
* // Finally destroy the instance
* $.destroy()
* })
*/ async destroy() {
this.shutdownPromise = this.shutdownPromise || Promise.all([
...this._silosContexts
].map(async (siloContext)=>{
const $dispose = (await siloContext.servicesDescriptors.get(DISPOSE))?.service;
return $dispose();
})).then(()=>undefined);
* Destroy the Knifecycle instance
* @return {Promise}
* Full destruction promise
* @example
*
* import Knifecycle, { constant } from 'knifecycle'
*
* const $ = new Knifecycle();
*
* $.register(constant('ENV', process.env));
* $.run(['ENV'])
* .then(({ ENV }) => {
* // Here goes your code
*
* // Finally destroy the instance
* $.destroy()
* })
*/
async destroy() {
this.shutdownPromise =
this.shutdownPromise ||
Promise.all([...this._silosContexts].map(async (siloContext) => {
const $dispose = (await siloContext.servicesDescriptors.get(DISPOSE))
?.service;
return $dispose();
})).then(() => undefined);
debug('Shutting down Knifecycle instance.');

@@ -417,18 +432,19 @@ return this.shutdownPromise;

/**
* Initialize or return a service descriptor
* @param {Object} siloContext
* Current execution silo context
* @param {String} serviceName
* Service name.
* @param {Object} options
* Options for service retrieval
* @param {Boolean} options.injectorContext
* Flag indicating the injection were initiated by the $injector
* @param {Boolean} options.autoloading
* Flag to indicating $autoload dependencies on the fly loading
* @param {String} serviceProvider
* Service provider.
* @return {Promise}
* Service descriptor promise.
*/ async _getServiceDescriptor(siloContext, serviceName, { injectorContext , autoloading }) {
* Initialize or return a service descriptor
* @param {Object} siloContext
* Current execution silo context
* @param {String} serviceName
* Service name.
* @param {Object} options
* Options for service retrieval
* @param {Boolean} options.injectorContext
* Flag indicating the injection were initiated by the $injector
* @param {Boolean} options.autoloading
* Flag to indicating $autoload dependencies on the fly loading
* @param {String} serviceProvider
* Service provider.
* @return {Promise}
* Service descriptor promise.
*/
async _getServiceDescriptor(siloContext, serviceName, { injectorContext, autoloading, }) {
// Try to get service descriptior early from the silo context

@@ -444,3 +460,3 @@ let serviceDescriptorPromise = siloContext.servicesDescriptors.get(serviceName);

injectorContext,
autoloading
autoloading,
});

@@ -453,5 +469,7 @@ serviceDescriptorPromise = this._pickupSingletonServiceDescriptorPromise(serviceName);

this._singletonsServicesHandles.get(serviceName).add(siloContext.name);
} else {
serviceDescriptorPromise = siloContext.servicesDescriptors.get(serviceName);
}
else {
serviceDescriptorPromise =
siloContext.servicesDescriptors.get(serviceName);
}
if (serviceDescriptorPromise) {

@@ -465,7 +483,9 @@ return serviceDescriptorPromise;

if (injectorContext) {
debug('Warning: Instantiating a new service via the $injector. It may' + ' mean that you no longer need it if your worked around a circular' + ' dependency.');
debug('Warning: Instantiating a new service via the $injector. It may' +
' mean that you no longer need it if your worked around a circular' +
' dependency.');
}
serviceDescriptorPromise = this._initializeServiceDescriptor(siloContext, serviceName, initializer, {
autoloading: autoloading || AUTOLOAD === serviceName,
injectorContext
injectorContext,
});

@@ -478,5 +498,6 @@ if (initializer[SPECIAL_PROPS.SINGLETON]) {

preloaded: false,
promise: serviceDescriptorPromise
promise: serviceDescriptorPromise,
});
} else {
}
else {
siloContext.servicesDescriptors.set(serviceName, serviceDescriptorPromise);

@@ -486,9 +507,7 @@ }

if (AUTOLOAD === serviceName) {
siloContext.servicesSequence.unshift([
AUTOLOAD
]);
siloContext.servicesSequence.unshift([AUTOLOAD]);
}
return serviceDescriptorPromise;
}
async _findInitializer(siloContext, serviceName, { injectorContext , autoloading }) {
async _findInitializer(siloContext, serviceName, { injectorContext, autoloading, }) {
const initializer = this._initializers.get(serviceName);

@@ -510,3 +529,3 @@ if (initializer) {

}
initializerPromise = (async ()=>{
initializerPromise = (async () => {
if (!this._initializers.get(AUTOLOAD)) {

@@ -517,8 +536,7 @@ throw new YError(E_UNMATCHED_DEPENDENCY, serviceName);

try {
const autoloadingDescriptor = await this._getServiceDescriptor(siloContext, AUTOLOAD, {
injectorContext,
autoloading: true
});
const { initializer , path } = await autoloadingDescriptor.service(serviceName);
if (typeof initializer !== 'function' && (typeof initializer !== 'object' || initializer[SPECIAL_PROPS.TYPE] !== 'constant')) {
const autoloadingDescriptor = (await this._getServiceDescriptor(siloContext, AUTOLOAD, { injectorContext, autoloading: true }));
const { initializer, path } = await autoloadingDescriptor.service(serviceName);
if (typeof initializer !== 'function' &&
(typeof initializer !== 'object' ||
initializer[SPECIAL_PROPS.TYPE] !== 'constant')) {
throw new YError(E_BAD_AUTOLOADED_INITIALIZER, serviceName, initializer);

@@ -535,3 +553,4 @@ }

return this._initializers.get(serviceName);
} catch (err) {
}
catch (err) {
debug(`Could not load ${serviceName} via the auto loader.`);

@@ -553,16 +572,17 @@ throw err;

/**
* Initialize a service descriptor
* @param {Object} siloContext
* Current execution silo context
* @param {String} serviceName
* Service name.
* @param {Object} options
* Options for service retrieval
* @param {Boolean} options.injectorContext
* Flag indicating the injection were initiated by the $injector
* @param {Boolean} options.autoloading
* Flag to indicating $autoload dependendencies on the fly loading.
* @return {Promise}
* Service dependencies hash promise.
*/ async _initializeServiceDescriptor(siloContext, serviceName, initializer, { autoloading , injectorContext }) {
* Initialize a service descriptor
* @param {Object} siloContext
* Current execution silo context
* @param {String} serviceName
* Service name.
* @param {Object} options
* Options for service retrieval
* @param {Boolean} options.injectorContext
* Flag indicating the injection were initiated by the $injector
* @param {Boolean} options.autoloading
* Flag to indicating $autoload dependendencies on the fly loading.
* @return {Promise}
* Service dependencies hash promise.
*/
async _initializeServiceDescriptor(siloContext, serviceName, initializer, { autoloading, injectorContext, }) {
let serviceDescriptor;

@@ -575,3 +595,4 @@ debug('Initializing a service descriptor:', serviceName);

// a new one
await (this._singletonsServicesShutdownsPromises.get(serviceName) || Promise.resolve());
await (this._singletonsServicesShutdownsPromises.get(serviceName) ||
Promise.resolve());
// Anyway delete any shutdown promise before instanciating

@@ -581,9 +602,6 @@ // a new service

siloContext.servicesShutdownsPromises.delete(serviceName);
const servicesHash = await this._initializeDependencies(siloContext, serviceName, initializer[SPECIAL_PROPS.INJECT], {
injectorContext,
autoloading
});
const servicesHash = await this._initializeDependencies(siloContext, serviceName, initializer[SPECIAL_PROPS.INJECT], { injectorContext, autoloading });
debug('Successfully gathered service dependencies:', serviceName);
serviceDescriptor = await initializer(initializer[SPECIAL_PROPS.INJECT].reduce((finalHash, dependencyDeclaration)=>{
const { serviceName , mappedName } = parseDependencyDeclaration(dependencyDeclaration);
serviceDescriptor = await initializer(initializer[SPECIAL_PROPS.INJECT].reduce((finalHash, dependencyDeclaration) => {
const { serviceName, mappedName } = parseDependencyDeclaration(dependencyDeclaration);
finalHash[serviceName] = servicesHash[mappedName];

@@ -602,8 +620,7 @@ return finalHash;

siloContext.servicesDescriptors.set(serviceName, Promise.resolve(serviceDescriptor));
} catch (err) {
}
catch (err) {
debug('Error initializing a service descriptor:', serviceName, err.stack || 'no_stack_trace');
if (E_UNMATCHED_DEPENDENCY === err.code) {
throw YError.wrap(err, E_UNMATCHED_DEPENDENCY, ...[
serviceName
].concat(err.params));
throw YError.wrap(err, E_UNMATCHED_DEPENDENCY, ...[serviceName].concat(err.params));
}

@@ -615,32 +632,35 @@ throw err;

/**
* Initialize a service dependencies
* @param {Object} siloContext
* Current execution silo siloContext
* @param {String} serviceName
* Service name.
* @param {String} servicesDeclarations
* Dependencies declarations.
* @param {Object} options
* Options for service retrieval
* @param {Boolean} options.injectorContext
* Flag indicating the injection were initiated by the $injector
* @param {Boolean} options.autoloading
* Flag to indicating $autoload dependendencies on the fly loading.
* @return {Promise}
* Service dependencies hash promise.
*/ async _initializeDependencies(siloContext, serviceName, servicesDeclarations, { injectorContext =false , autoloading =false }) {
* Initialize a service dependencies
* @param {Object} siloContext
* Current execution silo siloContext
* @param {String} serviceName
* Service name.
* @param {String} servicesDeclarations
* Dependencies declarations.
* @param {Object} options
* Options for service retrieval
* @param {Boolean} options.injectorContext
* Flag indicating the injection were initiated by the $injector
* @param {Boolean} options.autoloading
* Flag to indicating $autoload dependendencies on the fly loading.
* @return {Promise}
* Service dependencies hash promise.
*/
async _initializeDependencies(siloContext, serviceName, servicesDeclarations, { injectorContext = false, autoloading = false, }) {
debug('Initializing dependencies:', serviceName, servicesDeclarations);
const servicesDescriptors = await Promise.all(servicesDeclarations.map(async (serviceDeclaration)=>{
const { mappedName , optional } = parseDependencyDeclaration(serviceDeclaration);
const servicesDescriptors = await Promise.all(servicesDeclarations.map(async (serviceDeclaration) => {
const { mappedName, optional } = parseDependencyDeclaration(serviceDeclaration);
try {
const serviceDescriptor = await this._getServiceDescriptor(siloContext, mappedName, {
injectorContext,
autoloading
autoloading,
});
return serviceDescriptor;
} catch (err) {
if (optional && [
'E_UNMATCHED_DEPENDENCY',
E_AUTOLOADER_DYNAMIC_DEPENDENCY
].includes(err.code)) {
}
catch (err) {
if (optional &&
[
'E_UNMATCHED_DEPENDENCY',
E_AUTOLOADER_DYNAMIC_DEPENDENCY,
].includes(err.code)) {
debug('Optional dependency not found:', serviceDeclaration, err.stack || 'no_stack_trace');

@@ -653,4 +673,6 @@ return;

debug('Initialized dependencies descriptors:', serviceName, servicesDeclarations, servicesDescriptors);
siloContext.servicesSequence.push(servicesDeclarations.filter((_, index)=>servicesDescriptors[index]).map(_pickMappedNameFromDeclaration));
const services = await Promise.all(servicesDescriptors.map(async (serviceDescriptor)=>{
siloContext.servicesSequence.push(servicesDeclarations
.filter((_, index) => servicesDescriptors[index])
.map(_pickMappedNameFromDeclaration));
const services = await Promise.all(servicesDescriptors.map(async (serviceDescriptor) => {
if (!serviceDescriptor) {

@@ -661,3 +683,3 @@ return undefined;

}));
return services.reduce((hash, service, index)=>{
return services.reduce((hash, service, index) => {
const mappedName = _pickMappedNameFromDeclaration(servicesDeclarations[index]);

@@ -669,13 +691,13 @@ hash[mappedName] = service;

}
export { SPECIAL_PROPS, SPECIAL_PROPS_PREFIX, DECLARATION_SEPARATOR, OPTIONAL_FLAG, ALLOWED_INITIALIZER_TYPES, ALLOWED_SPECIAL_PROPS, parseInjections, readFunctionName, parseName, Knifecycle, initializer, name, autoName, type, inject, useInject, mergeInject, autoInject, alsoInject, extra, singleton, reuseSpecialProps, wrapInitializer, constant, service, autoService, provider, autoProvider, handler, autoHandler, parseDependencyDeclaration, stringifyDependencyDeclaration, unwrapInitializerProperties, initInitializerBuilder };
export { SPECIAL_PROPS, SPECIAL_PROPS_PREFIX, DECLARATION_SEPARATOR, OPTIONAL_FLAG, ALLOWED_INITIALIZER_TYPES, ALLOWED_SPECIAL_PROPS, parseInjections, readFunctionName, parseName, Knifecycle, initializer, name, autoName, type, inject, useInject, mergeInject, autoInject, alsoInject, extra, singleton, reuseSpecialProps, wrapInitializer, constant, service, autoService, provider, autoProvider, handler, autoHandler, parseDependencyDeclaration, stringifyDependencyDeclaration, unwrapInitializerProperties, initInitializerBuilder, };
function _pickServiceNameFromDeclaration(dependencyDeclaration) {
const { serviceName } = parseDependencyDeclaration(dependencyDeclaration);
const { serviceName } = parseDependencyDeclaration(dependencyDeclaration);
return serviceName;
}
function _pickMappedNameFromDeclaration(dependencyDeclaration) {
const { mappedName } = parseDependencyDeclaration(dependencyDeclaration);
const { mappedName } = parseDependencyDeclaration(dependencyDeclaration);
return mappedName;
}
function _applyShapes(shapes, serviceName) {
return shapes.reduce((shapedService, shape)=>{
return shapes.reduce((shapedService, shape) => {
if (shapedService) {

@@ -688,10 +710,10 @@ return shapedService;

}
return shape.template.replace(/\$([0-9])+/g, ($, $1)=>matches[parseInt($1, 10)]);
return shape.template.replace(/\$([0-9])+/g, ($, $1) => matches[parseInt($1, 10)]);
}, '');
}
function _applyClasses(classes, styles, links) {
return links.reduce((classesApplications, link)=>Object.assign(classesApplications, _applyStyles(classes, styles, link)), {});
return links.reduce((classesApplications, link) => Object.assign(classesApplications, _applyStyles(classes, styles, link)), {});
}
function _applyStyles(classes, styles, { serviceName , dependedServiceName }) {
return styles.reduce((classesApplications, style)=>{
function _applyStyles(classes, styles, { serviceName, dependedServiceName, }) {
return styles.reduce((classesApplications, style) => {
if (style.pattern.test(serviceName) && !classesApplications[serviceName]) {

@@ -703,3 +725,4 @@ if (!classes[style.className]) {

}
if (style.pattern.test(dependedServiceName) && !classesApplications[dependedServiceName]) {
if (style.pattern.test(dependedServiceName) &&
!classesApplications[dependedServiceName]) {
if (!classes[style.className]) {

@@ -718,9 +741,9 @@ throw new YError(E_BAD_CLASS, style.className, dependedServiceName);

}
return servicePromise.then((_service_)=>({
service: _service_
}));
return servicePromise.then((_service_) => ({
service: _service_,
}));
}
function _buildFinalHash(servicesHash, dependenciesDeclarations) {
return dependenciesDeclarations.reduce((finalHash, dependencyDeclaration)=>{
const { serviceName , mappedName } = parseDependencyDeclaration(dependencyDeclaration);
return dependenciesDeclarations.reduce((finalHash, dependencyDeclaration) => {
const { serviceName, mappedName } = parseDependencyDeclaration(dependencyDeclaration);
finalHash[serviceName] = servicesHash[mappedName];

@@ -730,3 +753,2 @@ return finalHash;

}
//# sourceMappingURL=index.js.map

@@ -1,11 +0,12 @@

/* eslint max-nested-callbacks:0 */ import { describe, beforeEach, test } from '@jest/globals';
/* eslint max-nested-callbacks:0 */
import { describe, beforeEach, test } from '@jest/globals';
import assert from 'assert';
import sinon from 'sinon';
import { YError } from 'yerror';
import { SPECIAL_PROPS, Knifecycle, initializer, inject, constant, service, provider, singleton } from './index.js';
import { SPECIAL_PROPS, Knifecycle, initializer, inject, constant, service, provider, singleton, } from './index.js';
import { ALLOWED_INITIALIZER_TYPES } from './util.js';
describe('Knifecycle', ()=>{
describe('Knifecycle', () => {
let $;
const ENV = {
MY_ENV_VAR: 'plop'
MY_ENV_VAR: 'plop',
};

@@ -18,31 +19,27 @@ const time = Date.now.bind(Date);

return {
service: hash
service: hash,
};
}
beforeEach(()=>{
beforeEach(() => {
$ = new Knifecycle();
});
describe('register', ()=>{
describe('with constants', ()=>{
test('should work with an object', ()=>{
describe('register', () => {
describe('with constants', () => {
test('should work with an object', () => {
$.register(constant('ENV', ENV));
});
test('should work with a function', ()=>{
test('should work with a function', () => {
$.register(constant('time', time));
});
test('should work when overriding a previously set constant', async ()=>{
test('should work when overriding a previously set constant', async () => {
$.register(constant('TEST', 1));
$.register(constant('TEST', 2));
assert.deepEqual(await $.run([
'TEST'
]), {
TEST: 2
assert.deepEqual(await $.run(['TEST']), {
TEST: 2,
});
});
test('should fail when overriding an initialized constant', async ()=>{
test('should fail when overriding an initialized constant', async () => {
$.register(constant('TEST', 1));
assert.deepEqual(await $.run([
'TEST'
]), {
TEST: 1
assert.deepEqual(await $.run(['TEST']), {
TEST: 1,
});

@@ -52,3 +49,4 @@ try {

throw new YError('E_UNEXPECTED_SUCCESS');
} catch (err) {
}
catch (err) {
assert.equal(err.code, 'E_INITIALIZER_ALREADY_INSTANCIATED');

@@ -58,24 +56,21 @@ }

});
describe('with services', ()=>{
test('should work with a service', ()=>{
describe('with services', () => {
test('should work with a service', () => {
$.register(service(timeService, 'time'));
});
test('should work when overriding a previously set service', async ()=>{
$.register(service(async ()=>()=>1, 'test'));
$.register(service(async ()=>()=>2, 'test'));
const { test } = await $.run([
'test'
]);
test('should work when overriding a previously set service', async () => {
$.register(service(async () => () => 1, 'test'));
$.register(service(async () => () => 2, 'test'));
const { test } = await $.run(['test']);
assert.deepEqual(test(), 2);
});
test('should fail when overriding an initialized service', async ()=>{
$.register(service(async ()=>()=>1, 'test'));
const { test } = await $.run([
'test'
]);
test('should fail when overriding an initialized service', async () => {
$.register(service(async () => () => 1, 'test'));
const { test } = await $.run(['test']);
assert.deepEqual(test(), 1);
try {
$.register(service(async ()=>()=>2, 'test'));
$.register(service(async () => () => 2, 'test'));
throw new YError('E_UNEXPECTED_SUCCESS');
} catch (err) {
}
catch (err) {
assert.equal(err.code, 'E_INITIALIZER_ALREADY_INSTANCIATED');

@@ -85,27 +80,25 @@ }

});
describe('with providers', ()=>{
test('should work with a provider', ()=>{
describe('with providers', () => {
test('should work with a provider', () => {
$.register(service(hashProvider, 'hash'));
});
test('should work when overriding a previously set provider', async ()=>{
test('should work when overriding a previously set provider', async () => {
$.register(initializer({
type: 'provider',
name: 'test',
inject: []
}, async ()=>({
service: 1
})));
inject: [],
}, async () => ({
service: 1,
})));
$.register(initializer({
type: 'provider',
name: 'test',
inject: []
}, async ()=>({
service: 2
})));
const { test } = await $.run([
'test'
]);
inject: [],
}, async () => ({
service: 2,
})));
const { test } = await $.run(['test']);
assert.deepEqual(test, 2);
});
test('should work when overriding a previously set singleton provider', async ()=>{
test('should work when overriding a previously set singleton provider', async () => {
$.register(initializer({

@@ -115,19 +108,17 @@ type: 'provider',

inject: [],
singleton: true
}, async ()=>({
service: 1
})));
singleton: true,
}, async () => ({
service: 1,
})));
$.register(initializer({
type: 'provider',
name: 'test',
inject: []
}, async ()=>({
service: 2
})));
const { test } = await $.run([
'test'
]);
inject: [],
}, async () => ({
service: 2,
})));
const { test } = await $.run(['test']);
assert.deepEqual(test, 2);
});
test('should fail when overriding an initialized provider', async ()=>{
test('should fail when overriding an initialized provider', async () => {
$.register(initializer({

@@ -137,9 +128,7 @@ type: 'provider',

inject: [],
singleton: true
}, async ()=>({
service: 1
})));
const { test } = await $.run([
'test'
]);
singleton: true,
}, async () => ({
service: 1,
})));
const { test } = await $.run(['test']);
assert.deepEqual(test, 1);

@@ -150,8 +139,9 @@ try {

name: 'test',
inject: []
}, async ()=>({
service: 2
})));
inject: [],
}, async () => ({
service: 2,
})));
throw new YError('E_UNEXPECTED_SUCCESS');
} catch (err) {
}
catch (err) {
assert.equal(err.code, 'E_INITIALIZER_ALREADY_INSTANCIATED');

@@ -161,17 +151,15 @@ }

});
test('should fail when intitializer is no a function', ()=>{
assert.throws(()=>{
test('should fail when intitializer is no a function', () => {
assert.throws(() => {
$.register('not_a_function');
}, (err)=>{
}, (err) => {
assert.deepEqual(err.code, 'E_BAD_INITIALIZER');
assert.deepEqual(err.params, [
'not_a_function'
]);
assert.deepEqual(err.params, ['not_a_function']);
return true;
});
});
test('should fail with no service name', ()=>{
assert.throws(()=>{
$.register(async ()=>undefined);
}, (err)=>{
test('should fail with no service name', () => {
assert.throws(() => {
$.register(async () => undefined);
}, (err) => {
assert.deepEqual(err.code, 'E_ANONYMOUS_ANALYZER');

@@ -182,9 +170,9 @@ assert.deepEqual(err.params, []);

});
test('should fail with a bad service type', ()=>{
assert.throws(()=>{
const fn = async ()=>undefined;
test('should fail with a bad service type', () => {
assert.throws(() => {
const fn = async () => undefined;
fn[SPECIAL_PROPS.NAME] = 'test';
fn[SPECIAL_PROPS.TYPE] = 'not_allowed_type';
$.register(fn);
}, (err)=>{
}, (err) => {
assert.deepEqual(err.code, 'E_BAD_INITIALIZER_TYPE');

@@ -194,3 +182,3 @@ assert.deepEqual(err.params, [

'not_allowed_type',
ALLOWED_INITIALIZER_TYPES
ALLOWED_INITIALIZER_TYPES,
]);

@@ -200,5 +188,5 @@ return true;

});
test('should fail with an undefined constant', ()=>{
assert.throws(()=>{
const fn = async ()=>undefined;
test('should fail with an undefined constant', () => {
assert.throws(() => {
const fn = async () => undefined;
fn[SPECIAL_PROPS.NAME] = 'THE_NUMBER';

@@ -208,13 +196,11 @@ fn[SPECIAL_PROPS.TYPE] = 'constant';

$.register(fn);
}, (err)=>{
}, (err) => {
assert.deepEqual(err.code, 'E_UNDEFINED_CONSTANT_INITIALIZER');
assert.deepEqual(err.params, [
'THE_NUMBER'
]);
assert.deepEqual(err.params, ['THE_NUMBER']);
return true;
});
});
test('should fail with a non constant that has a value', ()=>{
assert.throws(()=>{
const fn = async ()=>undefined;
test('should fail with a non constant that has a value', () => {
assert.throws(() => {
const fn = async () => undefined;
fn[SPECIAL_PROPS.NAME] = 'myService';

@@ -224,23 +210,17 @@ fn[SPECIAL_PROPS.TYPE] = 'service';

$.register(fn);
}, (err)=>{
}, (err) => {
assert.deepEqual(err.code, 'E_BAD_VALUED_NON_CONSTANT_INITIALIZER');
assert.deepEqual(err.params, [
'myService'
]);
assert.deepEqual(err.params, ['myService']);
return true;
});
});
test('should fail with special autoload intitializer that is not a singleton', ()=>{
assert.throws(()=>{
test('should fail with special autoload intitializer that is not a singleton', () => {
assert.throws(() => {
$.register(initializer({
name: '$autoload',
type: 'provider'
}, async ()=>({
service: ()=>undefined
})));
}, (err)=>{
type: 'provider',
}, async () => ({ service: () => undefined })));
}, (err) => {
assert.deepEqual(err.code, 'E_BAD_AUTOLOADER');
assert.deepEqual(err.params, [
false
]);
assert.deepEqual(err.params, [false]);
return true;

@@ -250,71 +230,43 @@ });

});
describe('provider', ()=>{
test('should register provider', ()=>{
describe('provider', () => {
test('should register provider', () => {
$.register(provider(hashProvider, 'hash'));
});
test('should fail with direct circular dependencies', ()=>{
assert.throws(()=>{
$.register(provider(hashProvider, 'hash', [
'hash'
]));
}, (err)=>{
test('should fail with direct circular dependencies', () => {
assert.throws(() => {
$.register(provider(hashProvider, 'hash', ['hash']));
}, (err) => {
assert.deepEqual(err.code, 'E_CIRCULAR_DEPENDENCY');
assert.deepEqual(err.params, [
'hash'
]);
assert.deepEqual(err.params, ['hash']);
return true;
});
});
test('should fail with direct circular dependencies on mapped services', ()=>{
assert.throws(()=>{
$.register(provider(hashProvider, 'hash', [
'hash>lol'
]));
}, (err)=>{
test('should fail with direct circular dependencies on mapped services', () => {
assert.throws(() => {
$.register(provider(hashProvider, 'hash', ['hash>lol']));
}, (err) => {
assert.deepEqual(err.code, 'E_CIRCULAR_DEPENDENCY');
assert.deepEqual(err.params, [
'hash'
]);
assert.deepEqual(err.params, ['hash']);
return true;
});
});
test('should fail with circular dependencies', ()=>{
assert.throws(()=>{
$.register(provider(inject([
'hash3'
], hashProvider), 'hash'));
$.register(provider(inject([
'hash'
], hashProvider), 'hash1'));
$.register(provider(inject([
'hash1'
], hashProvider), 'hash2'));
$.register(provider(inject([
'hash'
], hashProvider), 'hash3'));
}, (err)=>{
test('should fail with circular dependencies', () => {
assert.throws(() => {
$.register(provider(inject(['hash3'], hashProvider), 'hash'));
$.register(provider(inject(['hash'], hashProvider), 'hash1'));
$.register(provider(inject(['hash1'], hashProvider), 'hash2'));
$.register(provider(inject(['hash'], hashProvider), 'hash3'));
}, (err) => {
assert.deepEqual(err.code, 'E_CIRCULAR_DEPENDENCY');
assert.deepEqual(err.params, [
'hash3',
'hash',
'hash3'
]);
assert.deepEqual(err.params, ['hash3', 'hash', 'hash3']);
return true;
});
});
test('should fail with deeper circular dependencies', ()=>{
assert.throws(()=>{
$.register(provider(inject([
'hash1'
], hashProvider), 'hash'));
$.register(provider(inject([
'hash2'
], hashProvider), 'hash1'));
$.register(provider(inject([
'hash3'
], hashProvider), 'hash2'));
$.register(provider(inject([
'hash'
], hashProvider), 'hash3'));
}, (err)=>{
test('should fail with deeper circular dependencies', () => {
assert.throws(() => {
$.register(provider(inject(['hash1'], hashProvider), 'hash'));
$.register(provider(inject(['hash2'], hashProvider), 'hash1'));
$.register(provider(inject(['hash3'], hashProvider), 'hash2'));
$.register(provider(inject(['hash'], hashProvider), 'hash3'));
}, (err) => {
assert.deepEqual(err.code, 'E_CIRCULAR_DEPENDENCY');

@@ -326,3 +278,3 @@ assert.deepEqual(err.params, [

'hash2',
'hash3'
'hash3',
]);

@@ -332,17 +284,9 @@ return true;

});
test('should fail with circular dependencies on mapped services', ()=>{
assert.throws(()=>{
$.register(provider(inject([
'hash3>aHash3'
], hashProvider), 'hash'));
$.register(provider(inject([
'hash>aHash'
], hashProvider), 'hash1'));
$.register(provider(inject([
'hash1>aHash1'
], hashProvider), 'hash2'));
$.register(provider(inject([
'hash>aHash'
], hashProvider), 'hash3'));
}, (err)=>{
test('should fail with circular dependencies on mapped services', () => {
assert.throws(() => {
$.register(provider(inject(['hash3>aHash3'], hashProvider), 'hash'));
$.register(provider(inject(['hash>aHash'], hashProvider), 'hash1'));
$.register(provider(inject(['hash1>aHash1'], hashProvider), 'hash2'));
$.register(provider(inject(['hash>aHash'], hashProvider), 'hash3'));
}, (err) => {
assert.deepEqual(err.code, 'E_CIRCULAR_DEPENDENCY');

@@ -352,3 +296,3 @@ assert.deepEqual(err.params, [

'hash>aHash',
'hash3>aHash3'
'hash3>aHash3',
]);

@@ -359,27 +303,19 @@ return true;

});
describe('run', ()=>{
test('should work with no dependencies', async ()=>{
describe('run', () => {
test('should work with no dependencies', async () => {
const dependencies = await $.run([]);
assert.deepEqual(dependencies, {});
});
test('should work with constant dependencies', async ()=>{
test('should work with constant dependencies', async () => {
$.register(constant('ENV', ENV));
$.register(constant('time', time));
const dependencies = await $.run([
'time',
'ENV'
]);
assert.deepEqual(Object.keys(dependencies), [
'time',
'ENV'
]);
const dependencies = await $.run(['time', 'ENV']);
assert.deepEqual(Object.keys(dependencies), ['time', 'ENV']);
assert.deepEqual(dependencies, {
ENV,
time
time,
});
});
test('should work with service dependencies', async ()=>{
const wrappedSampleService = inject([
'time'
], async function sampleService({ time }) {
test('should work with service dependencies', async () => {
const wrappedSampleService = inject(['time'], async function sampleService({ time }) {
return Promise.resolve(typeof time);

@@ -389,126 +325,61 @@ });

$.register(constant('time', time));
const dependencies = await $.run([
'sample'
]);
assert.deepEqual(Object.keys(dependencies), [
'sample'
]);
const dependencies = await $.run(['sample']);
assert.deepEqual(Object.keys(dependencies), ['sample']);
assert.deepEqual(dependencies, {
sample: 'function'
sample: 'function',
});
});
test('should work with simple dependencies', async ()=>{
test('should work with simple dependencies', async () => {
$.register(constant('ENV', ENV));
$.register(constant('time', time));
$.register(provider(hashProvider, 'hash', [
'ENV'
]));
const dependencies = await $.run([
'time',
'hash'
]);
assert.deepEqual(Object.keys(dependencies), [
'time',
'hash'
]);
$.register(provider(hashProvider, 'hash', ['ENV']));
const dependencies = await $.run(['time', 'hash']);
assert.deepEqual(Object.keys(dependencies), ['time', 'hash']);
assert.deepEqual(dependencies, {
hash: {
ENV
},
time
hash: { ENV },
time,
});
});
test('should work with given optional dependencies', async ()=>{
test('should work with given optional dependencies', async () => {
$.register(constant('ENV', ENV));
$.register(constant('DEBUG', {}));
$.register(constant('time', time));
$.register(provider(hashProvider, 'hash', [
'ENV',
'?DEBUG'
]));
const dependencies = await $.run([
'time',
'hash'
]);
assert.deepEqual(Object.keys(dependencies), [
'time',
'hash'
]);
$.register(provider(hashProvider, 'hash', ['ENV', '?DEBUG']));
const dependencies = await $.run(['time', 'hash']);
assert.deepEqual(Object.keys(dependencies), ['time', 'hash']);
assert.deepEqual(dependencies, {
hash: {
ENV,
DEBUG: {}
},
time
hash: { ENV, DEBUG: {} },
time,
});
});
test('should work with lacking optional dependencies', async ()=>{
test('should work with lacking optional dependencies', async () => {
$.register(constant('ENV', ENV));
$.register(constant('time', time));
$.register(provider(hashProvider, 'hash', [
'ENV',
'?DEBUG'
]));
const dependencies = await $.run([
'time',
'hash'
]);
assert.deepEqual(Object.keys(dependencies), [
'time',
'hash'
]);
$.register(provider(hashProvider, 'hash', ['ENV', '?DEBUG']));
const dependencies = await $.run(['time', 'hash']);
assert.deepEqual(Object.keys(dependencies), ['time', 'hash']);
assert.deepEqual(dependencies, {
hash: {
ENV,
DEBUG: undefined
},
time
hash: { ENV, DEBUG: undefined },
time,
});
});
test('should work with deeper dependencies', async ()=>{
test('should work with deeper dependencies', async () => {
$.register(constant('ENV', ENV));
$.register(constant('time', time));
$.register(provider(hashProvider, 'hash', [
'ENV'
]));
$.register(provider(hashProvider, 'hash1', [
'hash'
]));
$.register(provider(hashProvider, 'hash2', [
'hash1'
]));
$.register(provider(hashProvider, 'hash3', [
'hash2'
]));
$.register(provider(hashProvider, 'hash4', [
'hash3'
]));
$.register(provider(hashProvider, 'hash5', [
'hash4'
]));
const dependencies = await $.run([
'hash5',
'time'
]);
assert.deepEqual(Object.keys(dependencies), [
'hash5',
'time'
]);
$.register(provider(hashProvider, 'hash', ['ENV']));
$.register(provider(hashProvider, 'hash1', ['hash']));
$.register(provider(hashProvider, 'hash2', ['hash1']));
$.register(provider(hashProvider, 'hash3', ['hash2']));
$.register(provider(hashProvider, 'hash4', ['hash3']));
$.register(provider(hashProvider, 'hash5', ['hash4']));
const dependencies = await $.run(['hash5', 'time']);
assert.deepEqual(Object.keys(dependencies), ['hash5', 'time']);
});
test('should instanciate services once', async ()=>{
test('should instanciate services once', async () => {
const timeServiceStub = sinon.spy(timeService);
$.register(constant('ENV', ENV));
$.register(service(timeServiceStub, 'time'));
$.register(provider(hashProvider, 'hash', [
'ENV',
'time'
]));
$.register(provider(hashProvider, 'hash2', [
'ENV',
'time'
]));
$.register(provider(hashProvider, 'hash3', [
'ENV',
'time'
]));
$.register(provider(hashProvider, 'hash', ['ENV', 'time']));
$.register(provider(hashProvider, 'hash2', ['ENV', 'time']));
$.register(provider(hashProvider, 'hash3', ['ENV', 'time']));
const dependencies = await $.run([

@@ -518,3 +389,3 @@ 'hash',

'hash3',
'time'
'time',
]);

@@ -525,26 +396,20 @@ assert.deepEqual(Object.keys(dependencies), [

'hash3',
'time'
'time',
]);
assert.deepEqual(timeServiceStub.args, [
[
{}
]
]);
assert.deepEqual(timeServiceStub.args, [[{}]]);
});
test('should instanciate a single mapped service', async ()=>{
test('should instanciate a single mapped service', async () => {
const providerStub = sinon.stub().returns(Promise.resolve({
service: 'stub'
service: 'stub',
}));
const providerStub2 = sinon.stub().returns(Promise.resolve({
service: 'stub2'
service: 'stub2',
}));
$.register(provider(providerStub, 'mappedStub', [
'stub2>mappedStub2'
]));
$.register(provider(providerStub, 'mappedStub', ['stub2>mappedStub2']));
$.register(provider(providerStub2, 'mappedStub2'));
const dependencies = await $.run([
'stub>mappedStub'
'stub>mappedStub',
]);
assert.deepEqual(dependencies, {
stub: 'stub'
stub: 'stub',
});

@@ -554,146 +419,99 @@ assert.deepEqual(providerStub.args, [

{
stub2: 'stub2'
}
]
stub2: 'stub2',
},
],
]);
});
test('should instanciate several services with mappings', async ()=>{
test('should instanciate several services with mappings', async () => {
const timeServiceStub = sinon.spy(timeService);
$.register(constant('ENV', ENV));
$.register(singleton(service(timeServiceStub, 'aTime')));
$.register(provider(hashProvider, 'aHash', [
'ENV',
'time>aTime'
]));
$.register(provider(hashProvider, 'aHash2', [
'ENV',
'hash>aHash'
]));
$.register(provider(hashProvider, 'aHash3', [
'ENV',
'hash>aHash'
]));
$.register(provider(hashProvider, 'aHash', ['ENV', 'time>aTime']));
$.register(provider(hashProvider, 'aHash2', ['ENV', 'hash>aHash']));
$.register(provider(hashProvider, 'aHash3', ['ENV', 'hash>aHash']));
const dependencies = await $.run([
'hash2>aHash2',
'hash3>aHash3',
'time>aTime'
'time>aTime',
]);
assert.deepEqual(Object.keys(dependencies), [
'hash2',
'hash3',
'time'
]);
assert.deepEqual(timeServiceStub.args, [
[
{}
]
]);
assert.deepEqual(Object.keys(dependencies), ['hash2', 'hash3', 'time']);
assert.deepEqual(timeServiceStub.args, [[{}]]);
});
test('should fail with bad service', async ()=>{
$.register(service(()=>undefined, 'lol'));
test('should fail with bad service', async () => {
$.register(service((() => undefined), 'lol'));
try {
await $.run([
'lol'
]);
await $.run(['lol']);
throw new Error('E_UNEXPECTED_SUCCESS');
} catch (err) {
}
catch (err) {
assert.deepEqual(err.code, 'E_BAD_SERVICE_PROMISE');
assert.deepEqual(err.params, [
'lol'
]);
assert.deepEqual(err.params, ['lol']);
}
});
test('should fail with bad provider', async ()=>{
$.register(provider(()=>undefined, 'lol'));
test('should fail with bad provider', async () => {
$.register(provider((() => undefined), 'lol'));
try {
await $.run([
'lol'
]);
await $.run(['lol']);
throw new Error('E_UNEXPECTED_SUCCESS');
} catch (err) {
}
catch (err) {
assert.deepEqual(err.code, 'E_BAD_SERVICE_PROVIDER');
assert.deepEqual(err.params, [
'lol'
]);
assert.deepEqual(err.params, ['lol']);
}
});
test('should fail with bad service in a provider', async ()=>{
$.register(provider(()=>Promise.resolve(), 'lol'));
test('should fail with bad service in a provider', async () => {
$.register(provider(() => Promise.resolve(), 'lol'));
try {
await $.run([
'lol'
]);
await $.run(['lol']);
throw new Error('E_UNEXPECTED_SUCCESS');
} catch (err) {
}
catch (err) {
assert.deepEqual(err.code, 'E_BAD_SERVICE_PROVIDER');
assert.deepEqual(err.params, [
'lol'
]);
assert.deepEqual(err.params, ['lol']);
}
});
test('should fail with undeclared dependencies', async ()=>{
test('should fail with undeclared dependencies', async () => {
try {
await $.run([
'lol'
]);
await $.run(['lol']);
throw new Error('E_UNEXPECTED_SUCCESS');
} catch (err) {
}
catch (err) {
assert.deepEqual(err.code, 'E_UNMATCHED_DEPENDENCY');
assert.deepEqual(err.params, [
'lol'
]);
assert.deepEqual(err.params, ['lol']);
}
});
test('should fail with undeclared dependencies upstream', async ()=>{
test('should fail with undeclared dependencies upstream', async () => {
$.register(constant('ENV', ENV));
$.register(constant('time', time));
$.register(provider(hashProvider, 'hash', [
'ENV',
'hash2'
]));
$.register(provider(hashProvider, 'hash2', [
'ENV',
'lol'
]));
$.register(provider(hashProvider, 'hash', ['ENV', 'hash2']));
$.register(provider(hashProvider, 'hash2', ['ENV', 'lol']));
try {
await $.run([
'time',
'hash'
]);
await $.run(['time', 'hash']);
throw new Error('E_UNEXPECTED_SUCCESS');
} catch (err) {
}
catch (err) {
assert.deepEqual(err.code, 'E_UNMATCHED_DEPENDENCY');
assert.deepEqual(err.params, [
'hash',
'hash2',
'lol'
]);
assert.deepEqual(err.params, ['hash', 'hash2', 'lol']);
}
});
test('should provide a fatal error handler', async ()=>{
test('should provide a fatal error handler', async () => {
$.register(constant('ENV', ENV));
$.register(constant('time', time));
$.register(provider(hashProvider, 'hash', [
'ENV'
]));
$.register(provider(dbProvider, 'db', [
'ENV'
]));
$.register(provider(processProvider, 'process', [
'$fatalError'
]));
function processProvider({ $fatalError }) {
$.register(provider(hashProvider, 'hash', ['ENV']));
$.register(provider(dbProvider, 'db', ['ENV']));
$.register(provider(processProvider, 'process', ['$fatalError']));
function processProvider({ $fatalError, }) {
return Promise.resolve({
service: {
fatalErrorPromise: $fatalError.promise
}
fatalErrorPromise: $fatalError.promise,
},
});
}
async function dbProvider({ ENV }) {
async function dbProvider({ ENV }) {
let service;
const fatalErrorPromise = new Promise((resolve, reject)=>{
const fatalErrorPromise = new Promise((resolve, reject) => {
service = Promise.resolve({
resolve,
reject,
ENV
ENV,
});

@@ -703,10 +521,10 @@ });

service,
fatalErrorPromise
fatalErrorPromise,
};
}
const { process , db } = await $.run([
const { process, db } = await $.run([
'time',
'hash',
'db',
'process'
'process',
]);

@@ -717,3 +535,4 @@ try {

throw new Error('E_UNEXPECTED_SUCCESS');
} catch (err) {
}
catch (err) {
assert.deepEqual(err.message, 'E_DB_ERROR');

@@ -723,4 +542,4 @@ }

});
describe('autoload', ()=>{
test('should work with lacking autoloaded dependencies', async ()=>{
describe('autoload', () => {
test('should work with lacking autoloaded dependencies', async () => {
const autoloaderInitializer = initializer({

@@ -730,15 +549,12 @@ type: 'service',

inject: [],
singleton: true
}, async ()=>async (serviceName)=>({
path: '/path/of/debug',
initializer: initializer({
type: 'service',
name: 'DEBUG',
inject: []
}, async ()=>'THE_DEBUG:' + serviceName)
}));
const wrappedProvider = provider(hashProvider, 'hash', [
'ENV',
'?DEBUG'
]);
singleton: true,
}, async () => async (serviceName) => ({
path: '/path/of/debug',
initializer: initializer({
type: 'service',
name: 'DEBUG',
inject: [],
}, async () => 'THE_DEBUG:' + serviceName),
}));
const wrappedProvider = provider(hashProvider, 'hash', ['ENV', '?DEBUG']);
$.register(autoloaderInitializer);

@@ -748,65 +564,38 @@ $.register(wrappedProvider);

$.register(constant('time', time));
const dependencies = await $.run([
'time',
'hash'
]);
assert.deepEqual(Object.keys(dependencies), [
'time',
'hash'
]);
const dependencies = await $.run(['time', 'hash']);
assert.deepEqual(Object.keys(dependencies), ['time', 'hash']);
assert.deepEqual(dependencies, {
hash: {
ENV,
DEBUG: 'THE_DEBUG:DEBUG'
},
time
hash: { ENV, DEBUG: 'THE_DEBUG:DEBUG' },
time,
});
});
test('should work with deeper several lacking dependencies', async ()=>{
test('should work with deeper several lacking dependencies', async () => {
$.register(initializer({
name: '$autoload',
type: 'service',
singleton: true
}, async ()=>async (serviceName)=>({
path: `/path/to/${serviceName}`,
initializer: initializer({
type: 'provider',
name: serviceName,
inject: 'hash2' === serviceName ? [
'hash1'
] : 'hash4' === serviceName ? [
'hash3'
] : []
}, hashProvider)
})));
singleton: true,
}, async () => async (serviceName) => ({
path: `/path/to/${serviceName}`,
initializer: initializer({
type: 'provider',
name: serviceName,
inject: 'hash2' === serviceName
? ['hash1']
: 'hash4' === serviceName
? ['hash3']
: [],
}, hashProvider),
})));
$.register(constant('ENV', ENV));
$.register(constant('time', time));
$.register(provider(hashProvider, 'hash', [
'ENV'
]));
$.register(provider(hashProvider, 'hash1', [
'hash'
]));
$.register(provider(hashProvider, 'hash3', [
'hash2'
]));
$.register(provider(hashProvider, 'hash5', [
'hash4'
]));
const dependencies = await $.run([
'hash5',
'time'
]);
assert.deepEqual(Object.keys(dependencies), [
'hash5',
'time'
]);
$.register(provider(hashProvider, 'hash', ['ENV']));
$.register(provider(hashProvider, 'hash1', ['hash']));
$.register(provider(hashProvider, 'hash3', ['hash2']));
$.register(provider(hashProvider, 'hash5', ['hash4']));
const dependencies = await $.run(['hash5', 'time']);
assert.deepEqual(Object.keys(dependencies), ['hash5', 'time']);
});
test('should work with various dependencies', async ()=>{
$.register(provider(hashProvider, 'hash', [
'hash2'
]));
$.register(provider(hashProvider, 'hash3', [
'?ENV'
]));
test('should work with various dependencies', async () => {
$.register(provider(hashProvider, 'hash', ['hash2']));
$.register(provider(hashProvider, 'hash3', ['?ENV']));
$.register(constant('DEBUG', 1));

@@ -816,87 +605,56 @@ $.register(initializer({

name: '$autoload',
inject: [
'?ENV',
'DEBUG'
],
singleton: true
}, async ()=>async (serviceName)=>{
if ('ENV' === serviceName) {
throw new YError('E_UNMATCHED_DEPENDENCY');
}
return {
path: '/path/of/debug',
initializer: initializer({
type: 'service',
name: 'hash2',
inject: [
'hash3'
]
}, async ()=>'THE_HASH:' + serviceName)
};
}));
const dependencies = await $.run([
'hash',
'?ENV'
]);
assert.deepEqual(Object.keys(dependencies), [
'hash',
'ENV'
]);
inject: ['?ENV', 'DEBUG'],
singleton: true,
}, async () => async (serviceName) => {
if ('ENV' === serviceName) {
throw new YError('E_UNMATCHED_DEPENDENCY');
}
return {
path: '/path/of/debug',
initializer: initializer({
type: 'service',
name: 'hash2',
inject: ['hash3'],
}, async () => 'THE_HASH:' + serviceName),
};
}));
const dependencies = await $.run(['hash', '?ENV']);
assert.deepEqual(Object.keys(dependencies), ['hash', 'ENV']);
});
test('should instanciate services once', async ()=>{
test('should instanciate services once', async () => {
$.register(initializer({
name: '$autoload',
type: 'service',
singleton: true
}, async ()=>async (serviceName)=>({
path: `/path/to/${serviceName}`,
initializer: initializer({
type: 'provider',
name: serviceName,
inject: [
'ENV',
'time'
]
}, hashProvider)
})));
singleton: true,
}, async () => async (serviceName) => ({
path: `/path/to/${serviceName}`,
initializer: initializer({
type: 'provider',
name: serviceName,
inject: ['ENV', 'time'],
}, hashProvider),
})));
const timeServiceStub = sinon.spy(timeService);
$.register(constant('ENV', ENV));
$.register(service(timeServiceStub, 'time'));
$.register(provider(hashProvider, 'hash', [
'hash1',
'hash2',
'hash3'
]));
$.register(provider(hashProvider, 'hash_', [
'hash1',
'hash2',
'hash3'
]));
$.register(provider(hashProvider, 'hash', ['hash1', 'hash2', 'hash3']));
$.register(provider(hashProvider, 'hash_', ['hash1', 'hash2', 'hash3']));
const dependencies = await $.run([
'hash',
'hash_',
'hash3'
'hash3',
]);
assert.deepEqual(timeServiceStub.args, [
[
{}
]
]);
assert.deepEqual(Object.keys(dependencies), [
'hash',
'hash_',
'hash3'
]);
assert.deepEqual(timeServiceStub.args, [[{}]]);
assert.deepEqual(Object.keys(dependencies), ['hash', 'hash_', 'hash3']);
});
test('should fail when autoload does not exists', async ()=>{
test('should fail when autoload does not exists', async () => {
try {
await $.run([
'test'
]);
await $.run(['test']);
throw new YError('E_UNEXPECTED_SUCCESS');
} catch (err) {
}
catch (err) {
assert.equal(err.code, 'E_UNMATCHED_DEPENDENCY');
}
});
test('should fail when autoloaded dependencies are not found', async ()=>{
test('should fail when autoloaded dependencies are not found', async () => {
$.register(initializer({

@@ -906,19 +664,16 @@ type: 'service',

inject: [],
singleton: true
}, async ()=>async (serviceName)=>{
throw new YError('E_CANNOT_AUTOLOAD', serviceName);
}));
singleton: true,
}, async () => async (serviceName) => {
throw new YError('E_CANNOT_AUTOLOAD', serviceName);
}));
try {
await $.run([
'test'
]);
await $.run(['test']);
throw new YError('E_UNEXPECTED_SUCCESS');
} catch (err) {
}
catch (err) {
assert.equal(err.code, 'E_CANNOT_AUTOLOAD');
assert.deepEqual(err.params, [
'test'
]);
assert.deepEqual(err.params, ['test']);
}
});
test('should fail when autoloaded dependencies are not initializers', async ()=>{
test('should fail when autoloaded dependencies are not initializers', async () => {
$.register(initializer({

@@ -928,18 +683,14 @@ type: 'service',

inject: [],
singleton: true
}, async ()=>async ()=>'not_an_initializer'));
singleton: true,
}, async () => async () => 'not_an_initializer'));
try {
await $.run([
'test'
]);
await $.run(['test']);
throw new YError('E_UNEXPECTED_SUCCESS');
} catch (err) {
}
catch (err) {
assert.equal(err.code, 'E_BAD_AUTOLOADED_INITIALIZER');
assert.deepEqual(err.params, [
'test',
undefined
]);
assert.deepEqual(err.params, ['test', undefined]);
}
});
test('should fail when autoloaded dependencies are not right initializers', async ()=>{
test('should fail when autoloaded dependencies are not right initializers', async () => {
$.register(initializer({

@@ -949,86 +700,67 @@ type: 'service',

inject: [],
singleton: true
}, async ()=>async (serviceName)=>({
path: '/path/of/debug',
initializer: initializer({
type: 'service',
name: 'not-' + serviceName,
inject: []
}, async ()=>'THE_TEST:' + serviceName)
})));
singleton: true,
}, async () => async (serviceName) => ({
path: '/path/of/debug',
initializer: initializer({
type: 'service',
name: 'not-' + serviceName,
inject: [],
}, async () => 'THE_TEST:' + serviceName),
})));
try {
await $.run([
'test'
]);
await $.run(['test']);
throw new YError('E_UNEXPECTED_SUCCESS');
} catch (err) {
}
catch (err) {
assert.equal(err.code, 'E_AUTOLOADED_INITIALIZER_MISMATCH');
assert.deepEqual(err.params, [
'test',
'not-test'
]);
assert.deepEqual(err.params, ['test', 'not-test']);
}
});
test('should fail when autoload depends on existing autoloaded dependencies', async ()=>{
test('should fail when autoload depends on existing autoloaded dependencies', async () => {
$.register(initializer({
type: 'service',
name: '$autoload',
inject: [
'ENV'
],
singleton: true
}, async ()=>async (serviceName)=>({
path: '/path/of/debug',
initializer: initializer({
type: 'service',
name: 'DEBUG',
inject: []
}, async ()=>'THE_DEBUG:' + serviceName)
})));
inject: ['ENV'],
singleton: true,
}, async () => async (serviceName) => ({
path: '/path/of/debug',
initializer: initializer({
type: 'service',
name: 'DEBUG',
inject: [],
}, async () => 'THE_DEBUG:' + serviceName),
})));
try {
await $.run([
'test'
]);
await $.run(['test']);
throw new YError('E_UNEXPECTED_SUCCESS');
} catch (err) {
}
catch (err) {
assert.equal(err.code, 'E_AUTOLOADER_DYNAMIC_DEPENDENCY');
assert.deepEqual(err.params, [
'ENV'
]);
assert.deepEqual(err.params, ['ENV']);
}
});
test('should work when autoload depends on optional and unexisting autoloaded dependencies', async ()=>{
test('should work when autoload depends on optional and unexisting autoloaded dependencies', async () => {
$.register(initializer({
type: 'service',
name: '$autoload',
inject: [
'?ENV'
],
singleton: true
}, async ()=>async (serviceName)=>({
path: `/path/of/${serviceName}`,
initializer: initializer({
type: 'service',
name: serviceName,
inject: []
}, async ()=>`THE_${serviceName.toUpperCase()}:` + serviceName)
})));
const dependencies = await $.run([
'test'
]);
assert.deepEqual(Object.keys(dependencies), [
'test'
]);
inject: ['?ENV'],
singleton: true,
}, async () => async (serviceName) => ({
path: `/path/of/${serviceName}`,
initializer: initializer({
type: 'service',
name: serviceName,
inject: [],
}, async () => `THE_${serviceName.toUpperCase()}:` + serviceName),
})));
const dependencies = await $.run(['test']);
assert.deepEqual(Object.keys(dependencies), ['test']);
});
test.skip('should work when autoload depends on deeper optional and unexisting autoloaded dependencies', async ()=>{
test.skip('should work when autoload depends on deeper optional and unexisting autoloaded dependencies', async () => {
$.register(initializer({
type: 'service',
name: 'log',
inject: [
'?LOG_ROUTING',
'?LOGGER',
'?debug'
]
}, async ()=>{
return ()=>undefined;
inject: ['?LOG_ROUTING', '?LOGGER', '?debug'],
}, async () => {
return () => undefined;
}));

@@ -1040,36 +772,25 @@ $.register(constant('LOGGER', 'LOGGER_CONSTANT'));

name: '$autoload',
inject: [
'?ENV',
'?log'
],
singleton: true
}, async ()=>async (serviceName)=>({
path: `/path/of/${serviceName}`,
initializer: initializer({
type: 'service',
name: serviceName,
inject: []
}, async ()=>`THE_${serviceName.toUpperCase()}:` + serviceName)
})));
const dependencies = await $.run([
'test',
'log'
]);
assert.deepEqual(Object.keys(dependencies), [
'test',
'log'
]);
inject: ['?ENV', '?log'],
singleton: true,
}, async () => async (serviceName) => ({
path: `/path/of/${serviceName}`,
initializer: initializer({
type: 'service',
name: serviceName,
inject: [],
}, async () => `THE_${serviceName.toUpperCase()}:` + serviceName),
})));
const dependencies = await $.run(['test', 'log']);
assert.deepEqual(Object.keys(dependencies), ['test', 'log']);
});
});
describe('$injector', ()=>{
test('should work with no dependencies', async ()=>{
describe('$injector', () => {
test('should work with no dependencies', async () => {
$.register(constant('ENV', ENV));
$.register(constant('time', time));
$.register(provider(hashProvider, 'hash', [
'ENV'
]));
$.register(provider(hashProvider, 'hash', ['ENV']));
const dependencies = await $.run([
'time',
'hash',
'$injector'
'$injector',
]);

@@ -1079,3 +800,3 @@ assert.deepEqual(Object.keys(dependencies), [

'hash',
'$injector'
'$injector',
]);

@@ -1086,12 +807,10 @@ const injectDependencies = await dependencies.$injector([]);

});
test('should work with same dependencies then the running silo', async ()=>{
test('should work with same dependencies then the running silo', async () => {
$.register(constant('ENV', ENV));
$.register(constant('time', time));
$.register(provider(hashProvider, 'hash', [
'ENV'
]));
$.register(provider(hashProvider, 'hash', ['ENV']));
const dependencies = await $.run([
'time',
'hash',
'$injector'
'$injector',
]);

@@ -1101,29 +820,19 @@ assert.deepEqual(Object.keys(dependencies), [

'hash',
'$injector'
'$injector',
]);
const injectDependencies = await dependencies.$injector([
'time',
'hash'
]);
assert.deepEqual(Object.keys(injectDependencies), [
'time',
'hash'
]);
const injectDependencies = await dependencies.$injector(['time', 'hash']);
assert.deepEqual(Object.keys(injectDependencies), ['time', 'hash']);
assert.deepEqual(injectDependencies, {
hash: {
ENV
},
time
hash: { ENV },
time,
});
});
test('should work with name mapping', async ()=>{
test('should work with name mapping', async () => {
$.register(constant('ENV', ENV));
$.register(constant('time', time));
$.register(provider(hashProvider, 'hash', [
'ENV'
]));
$.register(provider(hashProvider, 'hash', ['ENV']));
const dependencies = await $.run([
'time',
'hash',
'$injector'
'$injector',
]);

@@ -1133,132 +842,73 @@ assert.deepEqual(Object.keys(dependencies), [

'hash',
'$injector'
'$injector',
]);
const injectDependencies = await dependencies.$injector([
'aTime>time',
'aHash>hash'
'aHash>hash',
]);
assert.deepEqual(Object.keys(injectDependencies), [
'aTime',
'aHash'
]);
assert.deepEqual(Object.keys(injectDependencies), ['aTime', 'aHash']);
assert.deepEqual(injectDependencies, {
aHash: {
ENV
},
aTime: time
aHash: { ENV },
aTime: time,
});
});
test('should work with non instanciated dependencies', async ()=>{
test('should work with non instanciated dependencies', async () => {
$.register(constant('ENV', ENV));
$.register(constant('time', time));
$.register(provider(hashProvider, 'hash', [
'ENV'
]));
$.register(provider(hashProvider, 'hash', ['ENV']));
const dependencies = await $.run([
'time',
'$injector'
'$injector',
]);
assert.deepEqual(Object.keys(dependencies), [
'time',
'$injector'
]);
const injectDependencies = await dependencies.$injector([
'time',
'hash'
]);
assert.deepEqual(Object.keys(injectDependencies), [
'time',
'hash'
]);
assert.deepEqual(Object.keys(dependencies), ['time', '$injector']);
const injectDependencies = await dependencies.$injector(['time', 'hash']);
assert.deepEqual(Object.keys(injectDependencies), ['time', 'hash']);
assert.deepEqual(injectDependencies, {
hash: {
ENV
},
time
hash: { ENV },
time,
});
});
test('should create dependencies when not declared as singletons', async ()=>{
test('should create dependencies when not declared as singletons', async () => {
$.register(constant('ENV', ENV));
$.register(provider(hashProvider, 'hash', [
'ENV'
]));
const [{ hash }, { hash: sameHash }] = await Promise.all([
$.run([
'hash'
]),
$.run([
'hash'
])
$.register(provider(hashProvider, 'hash', ['ENV']));
const [{ hash }, { hash: sameHash }] = await Promise.all([
$.run(['hash']),
$.run(['hash']),
]);
assert.notEqual(hash, sameHash);
const { hash: yaSameHash } = await $.run([
'hash'
]);
const { hash: yaSameHash } = await $.run(['hash']);
assert.notEqual(hash, yaSameHash);
});
test('should reuse dependencies when declared as singletons', async ()=>{
test('should reuse dependencies when declared as singletons', async () => {
$.register(constant('ENV', ENV));
$.register(provider(hashProvider, 'hash', [
'ENV'
], true));
$.register(provider(hashProvider, 'hash2', [
'ENV'
], true));
const [{ hash , hash2 }, { hash: sameHash , hash2: sameHash2 }] = await Promise.all([
$.run([
'hash'
]),
$.run([
'hash'
]),
$.run([
'hash2'
]),
$.run([
'hash2'
])
$.register(provider(hashProvider, 'hash', ['ENV'], true));
$.register(provider(hashProvider, 'hash2', ['ENV'], true));
const [{ hash, hash2 }, { hash: sameHash, hash2: sameHash2 }] = await Promise.all([
$.run(['hash']),
$.run(['hash']),
$.run(['hash2']),
$.run(['hash2']),
]);
assert.equal(hash, sameHash);
assert.equal(hash2, sameHash2);
const { hash: yaSameHash } = await $.run([
'hash'
]);
const { hash: yaSameHash } = await $.run(['hash']);
assert.equal(hash, yaSameHash);
});
});
describe('destroy', ()=>{
test('should work even with one silo and no dependencies', async ()=>{
describe('destroy', () => {
test('should work even with one silo and no dependencies', async () => {
assert.equal(typeof $.destroy, 'function');
const dependencies = await $.run([
'$instance'
]);
const dependencies = await $.run(['$instance']);
await dependencies.$instance.destroy();
});
test('should work with several silos and dependencies', async ()=>{
test('should work with several silos and dependencies', async () => {
$.register(constant('ENV', ENV));
$.register(constant('time', time));
$.register(provider(hashProvider, 'hash', [
'ENV'
], true));
$.register(provider(hashProvider, 'hash1', [
'ENV'
]));
$.register(provider(hashProvider, 'hash2', [
'ENV'
]));
$.register(provider(hashProvider, 'hash', ['ENV'], true));
$.register(provider(hashProvider, 'hash1', ['ENV']));
$.register(provider(hashProvider, 'hash2', ['ENV']));
const [dependencies] = await Promise.all([
$.run([
'$instance'
]),
$.run([
'ENV',
'hash',
'hash1',
'time'
]),
$.run([
'ENV',
'hash',
'hash2'
])
$.run(['$instance']),
$.run(['ENV', 'hash', 'hash1', 'time']),
$.run(['ENV', 'hash', 'hash2']),
]);

@@ -1268,19 +918,11 @@ assert.equal(typeof dependencies.$instance.destroy, 'function');

});
test('should work when trigered from several silos simultaneously', async ()=>{
test('should work when trigered from several silos simultaneously', async () => {
$.register(constant('ENV', ENV));
$.register(constant('time', time));
$.register(provider(hashProvider, 'hash', [
'ENV'
]));
$.register(provider(hashProvider, 'hash1', [
'ENV'
]));
$.register(provider(hashProvider, 'hash2', [
'ENV'
]));
$.register(provider(hashProvider, 'hash', ['ENV']));
$.register(provider(hashProvider, 'hash1', ['ENV']));
$.register(provider(hashProvider, 'hash2', ['ENV']));
const dependenciesBuckets = await Promise.all([
$.run(['$instance']),
$.run([
'$instance'
]),
$.run([
'$instance',

@@ -1290,30 +932,17 @@ 'ENV',

'hash1',
'time'
'time',
]),
$.run([
'$instance',
'ENV',
'hash',
'hash2'
])
$.run(['$instance', 'ENV', 'hash', 'hash2']),
]);
await Promise.all(dependenciesBuckets.map((dependencies)=>dependencies.$instance.destroy()));
await Promise.all(dependenciesBuckets.map((dependencies) => dependencies.$instance.destroy()));
});
test('should work when a silo shutdown is in progress', async ()=>{
test('should work when a silo shutdown is in progress', async () => {
$.register(constant('ENV', ENV));
$.register(constant('time', time));
$.register(provider(hashProvider, 'hash', [
'ENV'
]));
$.register(provider(hashProvider, 'hash1', [
'ENV'
]));
$.register(provider(hashProvider, 'hash2', [
'ENV'
]));
$.register(provider(hashProvider, 'hash', ['ENV']));
$.register(provider(hashProvider, 'hash1', ['ENV']));
$.register(provider(hashProvider, 'hash2', ['ENV']));
const [dependencies1, dependencies2] = await Promise.all([
$.run(['$instance']),
$.run([
'$instance'
]),
$.run([
'$dispose',

@@ -1323,37 +952,24 @@ 'ENV',

'hash1',
'time'
'time',
]),
$.run([
'ENV',
'hash',
'hash2'
])
$.run(['ENV', 'hash', 'hash2']),
]);
await Promise.all([
dependencies2.$dispose(),
dependencies1.$instance.destroy()
dependencies1.$instance.destroy(),
]);
});
test('should disallow new runs', async ()=>{
test('should disallow new runs', async () => {
$.register(constant('ENV', ENV));
$.register(constant('time', time));
$.register(provider(hashProvider, 'hash', [
'ENV'
]));
$.register(provider(hashProvider, 'hash1', [
'ENV'
]));
const dependencies = await $.run([
'$instance'
]);
$.register(provider(hashProvider, 'hash', ['ENV']));
$.register(provider(hashProvider, 'hash1', ['ENV']));
const dependencies = await $.run(['$instance']);
assert.equal(typeof dependencies.$instance.destroy, 'function');
await dependencies.$instance.destroy();
try {
await $.run([
'ENV',
'hash',
'hash1'
]);
await $.run(['ENV', 'hash', 'hash1']);
throw new YError('E_UNEXPECTED_SUCCES');
} catch (err) {
}
catch (err) {
assert.equal(err.code, 'E_INSTANCE_DESTROYED');

@@ -1363,11 +979,9 @@ }

});
describe('$dispose', ()=>{
test('should work with no dependencies', async ()=>{
const dependencies = await $.run([
'$dispose'
]);
describe('$dispose', () => {
test('should work with no dependencies', async () => {
const dependencies = await $.run(['$dispose']);
assert.equal(typeof dependencies.$dispose, 'function');
return dependencies.$dispose();
});
test('should work with constant dependencies', async ()=>{
test('should work with constant dependencies', async () => {
$.register(constant('ENV', ENV));

@@ -1378,38 +992,28 @@ $.register(constant('time', time));

'ENV',
'$dispose'
'$dispose',
]);
assert.deepEqual(Object.keys(dependencies), [
'time',
'ENV',
'$dispose'
]);
assert.deepEqual(Object.keys(dependencies), ['time', 'ENV', '$dispose']);
await dependencies.$dispose();
});
test('should work with simple dependencies', async ()=>{
test('should work with simple dependencies', async () => {
$.register(constant('ENV', ENV));
$.register(constant('time', time));
$.register(provider(hashProvider, 'hash', [
'ENV'
]));
$.register(provider(hashProvider, 'hash', ['ENV']));
const dependencies = await $.run([
'time',
'hash',
'$dispose'
'$dispose',
]);
assert.deepEqual(Object.keys(dependencies), [
'time',
'hash',
'$dispose'
]);
assert.deepEqual(Object.keys(dependencies), ['time', 'hash', '$dispose']);
await dependencies.$dispose();
});
test('should work with deeper dependencies', async ()=>{
test('should work with deeper dependencies', async () => {
let shutdownCallResolve;
let shutdownResolve;
const shutdownCallPromise = new Promise((resolve)=>{
const shutdownCallPromise = new Promise((resolve) => {
shutdownCallResolve = resolve;
});
const shutdownStub = sinon.spy(()=>{
const shutdownStub = sinon.spy(() => {
shutdownCallResolve();
return new Promise((resolve)=>{
return new Promise((resolve) => {
shutdownResolve = resolve;

@@ -1420,29 +1024,15 @@ });

$.register(constant('time', time));
$.register(provider(hashProvider, 'hash', [
'ENV'
]));
$.register(provider(hashProvider, 'hash1', [
'hash'
]));
$.register(provider(hashProvider, 'hash2', [
'hash1'
]));
$.register(provider(hashProvider, 'hash3', [
'hash2'
]));
$.register(provider(hashProvider, 'hash4', [
'hash3'
]));
$.register(provider(hashProvider, 'hash5', [
'hash4'
]));
$.register(provider(()=>Promise.resolve({
service: {
shutdownStub,
shutdownResolve
},
dispose: shutdownStub
}), 'shutdownChecker', [
'hash4'
]));
$.register(provider(hashProvider, 'hash', ['ENV']));
$.register(provider(hashProvider, 'hash1', ['hash']));
$.register(provider(hashProvider, 'hash2', ['hash1']));
$.register(provider(hashProvider, 'hash3', ['hash2']));
$.register(provider(hashProvider, 'hash4', ['hash3']));
$.register(provider(hashProvider, 'hash5', ['hash4']));
$.register(provider(() => Promise.resolve({
service: {
shutdownStub,
shutdownResolve,
},
dispose: shutdownStub,
}), 'shutdownChecker', ['hash4']));
const dependencies = await $.run([

@@ -1452,3 +1042,3 @@ 'hash5',

'$dispose',
'shutdownChecker'
'shutdownChecker',
]);

@@ -1459,8 +1049,6 @@ assert.deepEqual(Object.keys(dependencies), [

'$dispose',
'shutdownChecker'
'shutdownChecker',
]);
const finalPromise = shutdownCallPromise.then(()=>{
assert.deepEqual(shutdownStub.args, [
[]
]);
const finalPromise = shutdownCallPromise.then(() => {
assert.deepEqual(shutdownStub.args, [[]]);
shutdownResolve();

@@ -1471,11 +1059,11 @@ });

});
test('should work with deeper multi used dependencies', async ()=>{
test('should work with deeper multi used dependencies', async () => {
let shutdownCallResolve;
let shutdownResolve;
const shutdownCallPromise = new Promise((resolve)=>{
const shutdownCallPromise = new Promise((resolve) => {
shutdownCallResolve = resolve;
});
const shutdownStub = sinon.spy(()=>{
const shutdownStub = sinon.spy(() => {
shutdownCallResolve();
return new Promise((resolve)=>{
return new Promise((resolve) => {
shutdownResolve = resolve;

@@ -1485,20 +1073,12 @@ });

$.register(constant('ENV', ENV));
$.register(provider(hashProvider, 'hash', [
'ENV'
]));
$.register(provider(()=>Promise.resolve({
service: {
shutdownStub,
shutdownResolve
},
dispose: shutdownStub
}), 'shutdownChecker', [
'hash'
]));
$.register(provider(hashProvider, 'hash1', [
'shutdownChecker'
]));
$.register(provider(hashProvider, 'hash2', [
'shutdownChecker'
]));
$.register(provider(hashProvider, 'hash', ['ENV']));
$.register(provider(() => Promise.resolve({
service: {
shutdownStub,
shutdownResolve,
},
dispose: shutdownStub,
}), 'shutdownChecker', ['hash']));
$.register(provider(hashProvider, 'hash1', ['shutdownChecker']));
$.register(provider(hashProvider, 'hash2', ['shutdownChecker']));
const dependencies = await $.run([

@@ -1508,3 +1088,3 @@ 'hash1',

'$dispose',
'shutdownChecker'
'shutdownChecker',
]);

@@ -1515,8 +1095,6 @@ assert.deepEqual(Object.keys(dependencies), [

'$dispose',
'shutdownChecker'
'shutdownChecker',
]);
const finalPromise = shutdownCallPromise.then(()=>{
assert.deepEqual(shutdownStub.args, [
[]
]);
const finalPromise = shutdownCallPromise.then(() => {
assert.deepEqual(shutdownStub.args, [[]]);
shutdownResolve();

@@ -1527,56 +1105,37 @@ });

});
test('should delay service shutdown to their deeper dependencies', async ()=>{
const servicesShutdownCalls = sinon.spy(()=>Promise.resolve());
$.register(provider(()=>Promise.resolve({
service: {},
dispose: servicesShutdownCalls.bind(null, 'hash')
}), 'hash'));
$.register(provider(()=>Promise.resolve({
service: {},
dispose: servicesShutdownCalls.bind(null, 'hash1')
}), 'hash1', [
'hash'
]));
$.register(provider(()=>Promise.resolve({
service: {},
dispose: servicesShutdownCalls.bind(null, 'hash2')
}), 'hash2', [
'hash1',
'hash'
]));
test('should delay service shutdown to their deeper dependencies', async () => {
const servicesShutdownCalls = sinon.spy(() => Promise.resolve());
$.register(provider(() => Promise.resolve({
service: {},
dispose: servicesShutdownCalls.bind(null, 'hash'),
}), 'hash'));
$.register(provider(() => Promise.resolve({
service: {},
dispose: servicesShutdownCalls.bind(null, 'hash1'),
}), 'hash1', ['hash']));
$.register(provider(() => Promise.resolve({
service: {},
dispose: servicesShutdownCalls.bind(null, 'hash2'),
}), 'hash2', ['hash1', 'hash']));
const dependencies = await $.run([
'hash2',
'$dispose'
'$dispose',
]);
assert.deepEqual(Object.keys(dependencies), [
'hash2',
'$dispose'
]);
assert.deepEqual(Object.keys(dependencies), ['hash2', '$dispose']);
await dependencies.$dispose();
assert.deepEqual(servicesShutdownCalls.args, [
[
'hash2'
],
[
'hash1'
],
[
'hash'
]
['hash2'],
['hash1'],
['hash'],
]);
});
test('should not shutdown singleton dependencies if used elsewhere', async ()=>{
test('should not shutdown singleton dependencies if used elsewhere', async () => {
$.register(constant('ENV', ENV));
$.register(constant('time', time));
$.register(provider(hashProvider, 'hash', [
'ENV'
], true));
const { hash } = await $.run([
'time',
'hash'
]);
$.register(provider(hashProvider, 'hash', ['ENV'], true));
const { hash } = await $.run(['time', 'hash']);
const dependencies = await $.run([
'time',
'hash',
'$dispose'
'$dispose',
]);

@@ -1587,27 +1146,22 @@ assert.equal(dependencies.hash, hash);

'time',
'hash'
'hash',
]);
assert.equal(newDependencies.hash, hash);
});
test('should shutdown singleton dependencies if not used elsewhere', async ()=>{
test('should shutdown singleton dependencies if not used elsewhere', async () => {
$.register(constant('ENV', ENV));
$.register(constant('time', time));
$.register(provider(hashProvider, 'hash', [
'ENV'
], true));
const { hash , $dispose } = await $.run([
$.register(provider(hashProvider, 'hash', ['ENV'], true));
const { hash, $dispose } = await $.run([
'time',
'hash',
'$dispose'
'$dispose',
]);
await $dispose();
const dependencies = await $.run([
'time',
'hash'
]);
const dependencies = await $.run(['time', 'hash']);
assert.notEqual(dependencies.hash, hash);
});
});
describe('toMermaidGraph', ()=>{
test('should print nothing when no dependency', ()=>{
describe('toMermaidGraph', () => {
test('should print nothing when no dependency', () => {
$.register(constant('ENV', ENV));

@@ -1617,46 +1171,28 @@ $.register(constant('time', time));

});
test('should print a dependency graph', ()=>{
test('should print a dependency graph', () => {
$.register(constant('ENV', ENV));
$.register(constant('time', time));
$.register(provider(hashProvider, 'hash', [
'ENV'
]));
$.register(provider(hashProvider, 'hash1', [
'hash'
]));
$.register(provider(hashProvider, 'hash2', [
'hash1'
]));
$.register(provider(hashProvider, 'hash3', [
'hash2'
]));
$.register(provider(hashProvider, 'hash4', [
'hash3'
]));
$.register(provider(hashProvider, 'hash5', [
'hash4'
]));
assert.equal($.toMermaidGraph(), 'graph TD\n' + ' hash-->ENV\n' + ' hash1-->hash\n' + ' hash2-->hash1\n' + ' hash3-->hash2\n' + ' hash4-->hash3\n' + ' hash5-->hash4');
$.register(provider(hashProvider, 'hash', ['ENV']));
$.register(provider(hashProvider, 'hash1', ['hash']));
$.register(provider(hashProvider, 'hash2', ['hash1']));
$.register(provider(hashProvider, 'hash3', ['hash2']));
$.register(provider(hashProvider, 'hash4', ['hash3']));
$.register(provider(hashProvider, 'hash5', ['hash4']));
assert.equal($.toMermaidGraph(), 'graph TD\n' +
' hash-->ENV\n' +
' hash1-->hash\n' +
' hash2-->hash1\n' +
' hash3-->hash2\n' +
' hash4-->hash3\n' +
' hash5-->hash4');
});
test('should allow custom shapes', ()=>{
test('should allow custom shapes', () => {
$.register(constant('ENV', ENV));
$.register(constant('time', time));
$.register(provider(hashProvider, 'hash', [
'ENV'
]));
$.register(provider(hashProvider, 'hash1', [
'hash'
]));
$.register(provider(hashProvider, 'hash2', [
'hash1'
]));
$.register(provider(hashProvider, 'hash3', [
'hash2'
]));
$.register(provider(hashProvider, 'hash4', [
'hash3'
]));
$.register(provider(hashProvider, 'hash5', [
'hash4'
]));
$.register(provider(hashProvider, 'hash', ['ENV']));
$.register(provider(hashProvider, 'hash1', ['hash']));
$.register(provider(hashProvider, 'hash2', ['hash1']));
$.register(provider(hashProvider, 'hash3', ['hash2']));
$.register(provider(hashProvider, 'hash4', ['hash3']));
$.register(provider(hashProvider, 'hash5', ['hash4']));
assert.equal($.toMermaidGraph({

@@ -1666,39 +1202,33 @@ shapes: [

pattern: /^hash([0-9]+)$/,
template: '$0(($1))'
template: '$0(($1))',
},
{
pattern: /^[A-Z_]+$/,
template: '$0{$0}'
template: '$0{$0}',
},
{
pattern: /^.+$/,
template: '$0[$0]'
}
]
}), 'graph TD\n' + ' hash[hash]-->ENV{ENV}\n' + ' hash1((1))-->hash[hash]\n' + ' hash2((2))-->hash1((1))\n' + ' hash3((3))-->hash2((2))\n' + ' hash4((4))-->hash3((3))\n' + ' hash5((5))-->hash4((4))');
template: '$0[$0]',
},
],
}), 'graph TD\n' +
' hash[hash]-->ENV{ENV}\n' +
' hash1((1))-->hash[hash]\n' +
' hash2((2))-->hash1((1))\n' +
' hash3((3))-->hash2((2))\n' +
' hash4((4))-->hash3((3))\n' +
' hash5((5))-->hash4((4))');
});
test('should allow custom styles', ()=>{
test('should allow custom styles', () => {
$.register(constant('ENV', ENV));
$.register(constant('time', time));
$.register(provider(hashProvider, 'hash', [
'ENV'
]));
$.register(provider(hashProvider, 'hash1', [
'hash'
]));
$.register(provider(hashProvider, 'hash2', [
'hash1'
]));
$.register(provider(hashProvider, 'hash3', [
'hash2'
]));
$.register(provider(hashProvider, 'hash4', [
'hash3'
]));
$.register(provider(hashProvider, 'hash5', [
'hash4'
]));
$.register(provider(hashProvider, 'hash', ['ENV']));
$.register(provider(hashProvider, 'hash1', ['hash']));
$.register(provider(hashProvider, 'hash2', ['hash1']));
$.register(provider(hashProvider, 'hash3', ['hash2']));
$.register(provider(hashProvider, 'hash4', ['hash3']));
$.register(provider(hashProvider, 'hash5', ['hash4']));
assert.equal($.toMermaidGraph({
classes: {
exotic: 'fill:#f9f,stroke:#333,stroke-width:4px;'
exotic: 'fill:#f9f,stroke:#333,stroke-width:4px;',
},

@@ -1708,8 +1238,8 @@ styles: [

pattern: /^hash([0-9]+)$/,
className: 'exotic'
className: 'exotic',
},
{
pattern: /^hash([0-9]+)$/,
className: 'notapplied'
}
className: 'notapplied',
},
],

@@ -1719,18 +1249,29 @@ shapes: [

pattern: /^hash([0-9]+)$/,
template: '$0(($1))'
template: '$0(($1))',
},
{
pattern: /^[A-Z_]+$/,
template: '$0{$0}'
template: '$0{$0}',
},
{
pattern: /^.+$/,
template: '$0[$0]'
}
]
}), 'graph TD\n' + ' hash[hash]-->ENV{ENV}\n' + ' hash1((1))-->hash[hash]\n' + ' hash2((2))-->hash1((1))\n' + ' hash3((3))-->hash2((2))\n' + ' hash4((4))-->hash3((3))\n' + ' hash5((5))-->hash4((4))\n' + ' classDef exotic fill:#f9f,stroke:#333,stroke-width:4px;\n' + ' class hash1 exotic;\n' + ' class hash2 exotic;\n' + ' class hash3 exotic;\n' + ' class hash4 exotic;\n' + ' class hash5 exotic;');
template: '$0[$0]',
},
],
}), 'graph TD\n' +
' hash[hash]-->ENV{ENV}\n' +
' hash1((1))-->hash[hash]\n' +
' hash2((2))-->hash1((1))\n' +
' hash3((3))-->hash2((2))\n' +
' hash4((4))-->hash3((3))\n' +
' hash5((5))-->hash4((4))\n' +
' classDef exotic fill:#f9f,stroke:#333,stroke-width:4px;\n' +
' class hash1 exotic;\n' +
' class hash2 exotic;\n' +
' class hash3 exotic;\n' +
' class hash4 exotic;\n' +
' class hash5 exotic;');
});
});
});
//# sourceMappingURL=index.test.js.map

@@ -6,3 +6,3 @@ import { YError } from 'yerror';

let i = 0;
while(i < MAX_ITERATIONS){
while (i < MAX_ITERATIONS) {
const batch = recursivelyGetNextSequenceBatch(rootNode, batches);

@@ -25,13 +25,13 @@ if (0 === batch.length) {

}
if (nodeIsALeaf || node.__childNodes.every((childNode)=>nodeIsInBatches(batches, childNode))) {
if (nodeIsALeaf ||
node.__childNodes.every((childNode) => nodeIsInBatches(batches, childNode))) {
return batch.concat(node.__name);
}
return node.__childNodes.reduce((batch, childNode)=>[
...new Set(recursivelyGetNextSequenceBatch(childNode, batches, batch))
], batch);
return node.__childNodes.reduce((batch, childNode) => [
...new Set(recursivelyGetNextSequenceBatch(childNode, batches, batch)),
], batch);
}
function nodeIsInBatches(batches, node) {
return batches.some((batch)=>batch.includes(node.__name));
return batches.some((batch) => batch.includes(node.__name));
}
//# sourceMappingURL=sequence.js.map
import { describe, test } from '@jest/globals';
import assert from 'assert';
import { buildInitializationSequence } from './sequence.js';
describe('buildInitializationSequence()', ()=>{
test('should work with one level trees', ()=>{
describe('buildInitializationSequence()', () => {
test('should work with one level trees', () => {
const tree = {
__name: 'lol'
__name: 'lol',
};
assert.deepEqual(buildInitializationSequence(tree), [
[
'lol'
]
]);
assert.deepEqual(buildInitializationSequence(tree), [['lol']]);
});
test('should work with multi-level trees', ()=>{
test('should work with multi-level trees', () => {
const tree = {

@@ -27,10 +23,10 @@ __name: 'lol',

__name: 'lol 1.1.1',
__childNodes: []
}
]
__childNodes: [],
},
],
},
{
__name: 'lol 1.2'
}
]
__name: 'lol 1.2',
},
],
},

@@ -42,5 +38,5 @@ {

__name: 'lol 2.1',
__childNodes: []
}
]
__childNodes: [],
},
],
},

@@ -55,32 +51,18 @@ {

__name: 'lol 3.1.1',
__childNodes: []
}
]
}
]
}
]
__childNodes: [],
},
],
},
],
},
],
};
assert.deepEqual(buildInitializationSequence(tree), [
[
'lol 1.1.1',
'lol 1.2',
'lol 2.1',
'lol 3.1.1'
],
[
'lol 1.1',
'lol 2',
'lol 3.1'
],
[
'lol 1',
'lol 3'
],
[
'lol'
]
['lol 1.1.1', 'lol 1.2', 'lol 2.1', 'lol 3.1.1'],
['lol 1.1', 'lol 2', 'lol 3.1'],
['lol 1', 'lol 3'],
['lol'],
]);
});
test('should work with multi-level trees and cross dependencies', ()=>{
test('should work with multi-level trees and cross dependencies', () => {
const tree = {

@@ -97,8 +79,8 @@ __name: 'lol',

__name: 'lol 1.1.1',
__childNodes: []
}
]
__childNodes: [],
},
],
},
{
__name: 'lol 1.2'
__name: 'lol 1.2',
},

@@ -119,13 +101,13 @@ {

__name: 'lol 2.1',
__childNodes: []
}
]
}
]
}
]
}
]
}
]
__childNodes: [],
},
],
},
],
},
],
},
],
},
],
},

@@ -137,5 +119,5 @@ {

__name: 'lol 2.1',
__childNodes: []
}
]
__childNodes: [],
},
],
},

@@ -153,41 +135,23 @@ {

__name: 'lol 2.1',
__childNodes: []
}
]
}
]
}
]
}
]
__childNodes: [],
},
],
},
],
},
],
},
],
};
assert.deepEqual(buildInitializationSequence(tree), [
[
'lol 1.1.1',
'lol 1.2',
'lol 2.1'
],
[
'lol 1.1',
'lol 2'
],
[
'lol 3.1'
],
[
'lol 3'
],
[
'lol 1.3'
],
[
'lol 1'
],
[
'lol'
]
['lol 1.1.1', 'lol 1.2', 'lol 2.1'],
['lol 1.1', 'lol 2'],
['lol 3.1'],
['lol 3'],
['lol 1.3'],
['lol 1'],
['lol'],
]);
});
});
//# sourceMappingURL=sequence.test.js.map

@@ -1,2 +0,3 @@

/* eslint @typescript-eslint/ban-types:0 */ import { YError } from 'yerror';
/* eslint @typescript-eslint/ban-types:0 */
import { YError } from 'yerror';
import initDebug from 'debug';

@@ -25,3 +26,4 @@ const debug = initDebug('knifecycle');

create new initializers.
*/ export const DECLARATION_SEPARATOR = '>';
*/
export const DECLARATION_SEPARATOR = '>';
export const OPTIONAL_FLAG = '?';

@@ -31,3 +33,3 @@ export const ALLOWED_INITIALIZER_TYPES = [

'service',
'constant'
'constant',
];

@@ -41,5 +43,5 @@ export const SPECIAL_PROPS_PREFIX = '$';

EXTRA: `${SPECIAL_PROPS_PREFIX}extra`,
VALUE: `${SPECIAL_PROPS_PREFIX}value`
VALUE: `${SPECIAL_PROPS_PREFIX}value`,
};
export const ALLOWED_SPECIAL_PROPS = Object.keys(SPECIAL_PROPS).map((key)=>SPECIAL_PROPS[key]);
export const ALLOWED_SPECIAL_PROPS = Object.keys(SPECIAL_PROPS).map((key) => SPECIAL_PROPS[key]);
const E_BAD_INJECT_IN_CONSTANT = 'E_BAD_INJECT_IN_CONSTANT';

@@ -53,3 +55,5 @@ const E_CONSTANT_INJECTION = 'E_CONSTANT_INJECTION';

}
if (options && options.allowEmpty && source.match(/^\s*(?:async\s+function(?:\s+\w+)?|async)\s*\(\s*\)/)) {
if (options &&
options.allowEmpty &&
source.match(/^\s*(?:async\s+function(?:\s+\w+)?|async)\s*\(\s*\)/)) {
return [];

@@ -59,3 +63,11 @@ }

}
return matches[1].trim().replace(/,$/, '').split(/\s*,\s*/).map((s)=>s.trim()).filter((s)=>!s.startsWith('...')).map((injection)=>(injection.includes('=') ? '?' : '') + injection.split(/\s*=\s*/).shift().split(/\s*:\s*/).shift()).filter((injection)=>!/[)(\][]/.test(injection));
return matches[1]
.trim()
.replace(/,$/, '')
.split(/\s*,\s*/)
.map((s) => s.trim())
.filter((s) => !s.startsWith('...'))
.map((injection) => (injection.includes('=') ? '?' : '') +
injection.split(/\s*=\s*/).shift().split(/\s*:\s*/).shift())
.filter((injection) => !/[)(\][]/.test(injection));
}

@@ -73,15 +85,17 @@ export function readFunctionName(aFunction) {

export function parseName(functionName) {
return functionName.split(' ').pop().replace(/^init(?:ialize)?([A-Z])/, (_, $1)=>$1.toLowerCase());
return functionName.split(' ').pop().replace(/^init(?:ialize)?([A-Z])/, (_, $1) => $1.toLowerCase());
}
export function reuseSpecialProps(from, to, amend = {}) {
const uniqueInitializer = to.bind(null);
return [
...new Set(Object.keys(from).concat(Object.keys(amend)))
].filter((prop)=>prop.startsWith(SPECIAL_PROPS_PREFIX)).reduce((fn, prop)=>{
return [...new Set(Object.keys(from).concat(Object.keys(amend)))]
.filter((prop) => prop.startsWith(SPECIAL_PROPS_PREFIX))
.reduce((fn, prop) => {
const value = 'undefined' !== typeof amend[prop] ? amend[prop] : from[prop];
if (value instanceof Array) {
fn[prop] = value.concat();
} else if (value instanceof Object) {
}
else if (value instanceof Object) {
fn[prop] = Object.assign({}, value);
} else {
}
else {
fn[prop] = value;

@@ -114,3 +128,4 @@ }

* printAnswer(); // 42
*/ export function constant(name, value) {
*/
export function constant(name, value) {
const contantLooksLikeAnInitializer = value instanceof Function && value[SPECIAL_PROPS.INJECT];

@@ -125,3 +140,3 @@ if (contantLooksLikeAnInitializer) {

$singleton: true,
$value: value
$value: value,
};

@@ -158,3 +173,4 @@ }

* printAnswer(); // 42
*/ export function service(serviceBuilder, name, dependencies, singleton, extra) {
*/
export function service(serviceBuilder, name, dependencies, singleton, extra) {
if (!serviceBuilder) {

@@ -165,3 +181,6 @@ throw new YError('E_NO_SERVICE_BUILDER');

dependencies = dependencies || serviceBuilder[SPECIAL_PROPS.INJECT] || [];
singleton = typeof singleton === 'undefined' ? serviceBuilder[SPECIAL_PROPS.SINGLETON] || false : singleton;
singleton =
typeof singleton === 'undefined'
? serviceBuilder[SPECIAL_PROPS.SINGLETON] || false
: singleton;
extra = extra || serviceBuilder[SPECIAL_PROPS.EXTRA] || [];

@@ -174,3 +193,3 @@ debug(`Created an initializer from a service builder: ${name}.`);

[SPECIAL_PROPS.SINGLETON]: singleton,
[SPECIAL_PROPS.EXTRA]: extra
[SPECIAL_PROPS.EXTRA]: extra,
});

@@ -187,8 +206,7 @@ return uniqueInitializer;

* Returns a new initializer
*/ export function autoService(serviceBuilder) {
*/
export function autoService(serviceBuilder) {
const name = readFunctionName(serviceBuilder);
const source = serviceBuilder.toString();
const dependencies = parseInjections(source, {
allowEmpty: true
});
const dependencies = parseInjections(source, { allowEmpty: true });
return service(serviceBuilder, name, dependencies);

@@ -243,3 +261,4 @@ }

* }
*/ export function provider(providerBuilder, name, dependencies, singleton, extra) {
*/
export function provider(providerBuilder, name, dependencies, singleton, extra) {
if (!providerBuilder) {

@@ -250,3 +269,6 @@ throw new YError('E_NO_PROVIDER_BUILDER');

dependencies = dependencies || providerBuilder[SPECIAL_PROPS.INJECT] || [];
singleton = typeof singleton === 'undefined' ? providerBuilder[SPECIAL_PROPS.SINGLETON] || false : singleton;
singleton =
typeof singleton === 'undefined'
? providerBuilder[SPECIAL_PROPS.SINGLETON] || false
: singleton;
extra = extra || providerBuilder[SPECIAL_PROPS.EXTRA] || [];

@@ -259,3 +281,3 @@ debug(`Created an initializer from a provider builder: ${name || 'anonymous'}.`);

[SPECIAL_PROPS.SINGLETON]: singleton,
[SPECIAL_PROPS.EXTRA]: extra
[SPECIAL_PROPS.EXTRA]: extra,
});

@@ -272,12 +294,11 @@ return uniqueInitializer;

* Returns a new provider initializer
*/ export function autoProvider(providerBuilder) {
*/
export function autoProvider(providerBuilder) {
const name = readFunctionName(providerBuilder);
const source = providerBuilder.toString();
const dependencies = parseInjections(source, {
allowEmpty: true
});
const dependencies = parseInjections(source, { allowEmpty: true });
return provider(providerBuilder, name, dependencies);
}
export function wrapInitializer(wrapper, baseInitializer) {
return reuseSpecialProps(baseInitializer, async (services)=>{
return reuseSpecialProps(baseInitializer, async (services) => {
const baseInstance = await baseInitializer(services);

@@ -292,3 +313,3 @@ return wrapper(services, baseInstance);

const uniqueInitializer = reuseSpecialProps(initializer, initializer, {
[SPECIAL_PROPS.INJECT]: dependencies
[SPECIAL_PROPS.INJECT]: dependencies,
});

@@ -312,17 +333,21 @@ debug('Wrapped an initializer with dependencies:', dependencies);

const addedDependencies = dependencies.map(parseDependencyDeclaration);
const dedupedDependencies = currentDependencies.filter(({ serviceName })=>{
const declarationIsOverridden = addedDependencies.some(({ serviceName: addedServiceName })=>{
const dedupedDependencies = currentDependencies
.filter(({ serviceName }) => {
const declarationIsOverridden = addedDependencies.some(({ serviceName: addedServiceName }) => {
return addedServiceName === serviceName;
});
return !declarationIsOverridden;
}).concat(addedDependencies.map(({ serviceName , mappedName , optional })=>{
const isOptionalEverywhere = optional && currentDependencies.every(({ optional , mappedName: addedMappedName })=>{
return addedMappedName !== mappedName || optional;
});
})
.concat(addedDependencies.map(({ serviceName, mappedName, optional }) => {
const isOptionalEverywhere = optional &&
currentDependencies.every(({ optional, mappedName: addedMappedName }) => {
return addedMappedName !== mappedName || optional;
});
return {
serviceName,
mappedName,
optional: isOptionalEverywhere
optional: isOptionalEverywhere,
};
})).map(stringifyDependencyDeclaration);
}))
.map(stringifyDependencyDeclaration);
return inject(dedupedDependencies, initializer);

@@ -332,3 +357,5 @@ }

const uniqueInitializer = reuseSpecialProps(initializer, initializer, {
[SPECIAL_PROPS.EXTRA]: merge ? Object.assign(initializer[SPECIAL_PROPS.EXTRA] || {}, extraInformations) : extraInformations
[SPECIAL_PROPS.EXTRA]: merge
? Object.assign(initializer[SPECIAL_PROPS.EXTRA] || {}, extraInformations)
: extraInformations,
});

@@ -340,3 +367,3 @@ debug('Wrapped an initializer with extra informations:', extraInformations);

const uniqueInitializer = reuseSpecialProps(initializer, initializer, {
[SPECIAL_PROPS.SINGLETON]: isSingleton
[SPECIAL_PROPS.SINGLETON]: isSingleton,
});

@@ -348,3 +375,3 @@ debug('Marked an initializer as singleton:', isSingleton);

const uniqueInitializer = reuseSpecialProps(initializer, initializer, {
[SPECIAL_PROPS.NAME]: name
[SPECIAL_PROPS.NAME]: name,
});

@@ -359,3 +386,3 @@ debug('Wrapped an initializer with a name:', name);

const uniqueInitializer = reuseSpecialProps(initializer, initializer, {
[SPECIAL_PROPS.TYPE]: type
[SPECIAL_PROPS.TYPE]: type,
});

@@ -366,3 +393,3 @@ debug('Wrapped an initializer with a type:', type);

export function initializer(properties, initializer) {
const uniqueInitializer = reuseSpecialProps(initializer, initializer, Object.keys(properties).reduce((finalProperties, property)=>{
const uniqueInitializer = reuseSpecialProps(initializer, initializer, Object.keys(properties).reduce((finalProperties, property) => {
const finalProperty = SPECIAL_PROPS_PREFIX + property;

@@ -402,3 +429,4 @@ if (!ALLOWED_SPECIAL_PROPS.includes(finalProperty)) {

* }
*/ export function handler(handlerFunction, name, dependencies, singleton, extra) {
*/
export function handler(handlerFunction, name, dependencies, singleton, extra) {
name = name || handlerFunction[SPECIAL_PROPS.NAME];

@@ -414,4 +442,4 @@ dependencies = dependencies || handlerFunction[SPECIAL_PROPS.INJECT] || [];

singleton,
extra
}, async (dependencies)=>handlerFunction.bind(null, dependencies));
extra,
}, async (dependencies) => handlerFunction.bind(null, dependencies));
}

@@ -436,3 +464,4 @@ /**

* }
*/ export function autoHandler(handlerFunction) {
*/
export function autoHandler(handlerFunction) {
const name = readFunctionName(handlerFunction);

@@ -444,4 +473,4 @@ const source = handlerFunction.toString();

type: 'service',
inject: dependencies
}, async (dependencies)=>handlerFunction.bind(null, dependencies));
inject: dependencies,
}, async (dependencies) => handlerFunction.bind(null, dependencies));
}

@@ -457,3 +486,4 @@ /* Architecture Note #1.2.1: Dependencies declaration syntax

dependencies and remap their name at injection time.
*/ /**
*/
/**
* Explode a dependency declaration an returns its parts.

@@ -472,3 +502,4 @@ * @param {String} dependencyDeclaration

* }
*/ export function parseDependencyDeclaration(dependencyDeclaration) {
*/
export function parseDependencyDeclaration(dependencyDeclaration) {
const optional = dependencyDeclaration.startsWith(OPTIONAL_FLAG);

@@ -479,3 +510,3 @@ const [serviceName, mappedName] = (optional ? dependencyDeclaration.slice(1) : dependencyDeclaration).split(DECLARATION_SEPARATOR);

mappedName: mappedName || serviceName,
optional
optional,
};

@@ -498,4 +529,8 @@ }

* 'pgsql>db'
*/ export function stringifyDependencyDeclaration(dependencyDeclarationParts) {
return `${dependencyDeclarationParts.optional ? '?' : ''}${dependencyDeclarationParts.serviceName}${dependencyDeclarationParts.mappedName !== dependencyDeclarationParts.serviceName ? '>' + dependencyDeclarationParts.mappedName : ''}`;
*/
export function stringifyDependencyDeclaration(dependencyDeclarationParts) {
return `${dependencyDeclarationParts.optional ? '?' : ''}${dependencyDeclarationParts.serviceName}${dependencyDeclarationParts.mappedName !==
dependencyDeclarationParts.serviceName
? '>' + dependencyDeclarationParts.mappedName
: ''}`;
}

@@ -507,3 +542,4 @@ export function unwrapInitializerProperties(initializer) {

const properties = initializer;
if (typeof properties[SPECIAL_PROPS.NAME] !== 'string' || properties[SPECIAL_PROPS.NAME] === '') {
if (typeof properties[SPECIAL_PROPS.NAME] !== 'string' ||
properties[SPECIAL_PROPS.NAME] === '') {
throw new YError('E_ANONYMOUS_ANALYZER', properties[SPECIAL_PROPS.NAME]);

@@ -514,11 +550,14 @@ }

}
if (initializer[SPECIAL_PROPS.NAME] === '$autoload' && !initializer[SPECIAL_PROPS.SINGLETON]) {
if (initializer[SPECIAL_PROPS.NAME] === '$autoload' &&
!initializer[SPECIAL_PROPS.SINGLETON]) {
throw new YError('E_BAD_AUTOLOADER', initializer[SPECIAL_PROPS.SINGLETON] || false);
}
if (properties[SPECIAL_PROPS.TYPE] === 'constant') {
if ('undefined' === typeof initializer[SPECIAL_PROPS.VALUE]) {
if ('undefined' ===
typeof initializer[SPECIAL_PROPS.VALUE]) {
throw new YError('E_UNDEFINED_CONSTANT_INITIALIZER', properties[SPECIAL_PROPS.NAME]);
}
properties[SPECIAL_PROPS.SINGLETON] = true;
} else {
}
else {
if ('undefined' !== typeof initializer[SPECIAL_PROPS.VALUE]) {

@@ -528,8 +567,9 @@ throw new YError('E_BAD_VALUED_NON_CONSTANT_INITIALIZER', initializer[SPECIAL_PROPS.NAME]);

properties[SPECIAL_PROPS.INJECT] = properties[SPECIAL_PROPS.INJECT] || [];
properties[SPECIAL_PROPS.SINGLETON] = properties[SPECIAL_PROPS.SINGLETON] || false;
properties[SPECIAL_PROPS.EXTRA] = properties[SPECIAL_PROPS.EXTRA] || undefined;
properties[SPECIAL_PROPS.SINGLETON] =
properties[SPECIAL_PROPS.SINGLETON] || false;
properties[SPECIAL_PROPS.EXTRA] =
properties[SPECIAL_PROPS.EXTRA] || undefined;
}
return initializer;
}
//# sourceMappingURL=util.js.map
import { describe, test } from '@jest/globals';
import assert from 'assert';
import sinon from 'sinon';
import { reuseSpecialProps, wrapInitializer, parseDependencyDeclaration, name, autoName, type, inject, autoInject, alsoInject, useInject, mergeInject, parseInjections, singleton, extra, initializer, constant, service, autoService, provider, autoProvider, handler, autoHandler, SPECIAL_PROPS } from './util.js';
import { reuseSpecialProps, wrapInitializer, parseDependencyDeclaration, name, autoName, type, inject, autoInject, alsoInject, useInject, mergeInject, parseInjections, singleton, extra, initializer, constant, service, autoService, provider, autoProvider, handler, autoHandler, SPECIAL_PROPS, } from './util.js';
async function aProviderInitializer() {
return {
service: 'A_PROVIDER_SERVICE'
service: 'A_PROVIDER_SERVICE',
};

@@ -13,10 +13,12 @@ }

}
describe('reuseSpecialProps', ()=>{
test('should work', ()=>{
describe('reuseSpecialProps', () => {
test('should work', () => {
// We can safely ignore coverage here since the
// function are here just as placeholders
/* istanbul ignore next */ async function from() {
/* istanbul ignore next */
async function from() {
return 'from';
}
/* istanbul ignore next */ async function to() {
/* istanbul ignore next */
async function to() {
return 'to';

@@ -26,11 +28,5 @@ }

from.$type = 'service';
from.$inject = [
'ki',
'kooo',
'lol'
];
from.$inject = ['ki', 'kooo', 'lol'];
from.$singleton = false;
from.$extra = {
httpHandler: true
};
from.$extra = { httpHandler: true };
const newFn = reuseSpecialProps(from, to);

@@ -46,3 +42,3 @@ assert.notEqual(newFn, to);

const newFn2 = reuseSpecialProps(from, to, {
$name: 'yolo'
$name: 'yolo',
});

@@ -59,70 +55,40 @@ assert.notEqual(newFn2, to);

});
describe('wrapInitializer', ()=>{
test('should work with a service initializer', async ()=>{
describe('wrapInitializer', () => {
test('should work with a service initializer', async () => {
async function baseServiceInitializer() {
return ()=>'test';
return () => 'test';
}
const log = sinon.stub();
const newInitializer = wrapInitializer(async ({ log }, service)=>{
const newInitializer = wrapInitializer(async ({ log }, service) => {
log('Wrapping...');
return ()=>service() + '-wrapped';
}, service(baseServiceInitializer, 'baseServiceInitializer', [
'log',
'?test'
], false, {
httpHandler: false
return () => service() + '-wrapped';
}, service(baseServiceInitializer, 'baseServiceInitializer', ['log', '?test'], false, {
httpHandler: false,
}));
const newService = await newInitializer({
log
});
const newService = await newInitializer({ log });
assert.equal(newService(), 'test-wrapped');
assert.deepEqual(log.args, [
[
'Wrapping...'
]
]);
assert.deepEqual(newInitializer[SPECIAL_PROPS.INJECT], [
'log',
'?test'
]);
assert.deepEqual(log.args, [['Wrapping...']]);
assert.deepEqual(newInitializer[SPECIAL_PROPS.INJECT], ['log', '?test']);
});
test('should work with a provider initialzer', async ()=>{
test('should work with a provider initialzer', async () => {
async function baseInitializer() {
return {
service: ()=>'test'
};
return { service: () => 'test' };
}
const log = sinon.stub();
const baseProviderInitializer = provider(baseInitializer, 'baseInitializer', [
'log',
'?test'
], false, {
httpHandler: false
const baseProviderInitializer = provider(baseInitializer, 'baseInitializer', ['log', '?test'], false, {
httpHandler: false,
});
const newInitializer = wrapInitializer(async ({ log }, service)=>{
const newInitializer = wrapInitializer(async ({ log }, service) => {
log('Wrapping...');
return {
service: ()=>service.service() + '-wrapped'
};
return { service: () => service.service() + '-wrapped' };
}, baseProviderInitializer);
const newService = await newInitializer({
log
});
const newService = await newInitializer({ log });
assert.equal(newService.service(), 'test-wrapped');
assert.deepEqual(log.args, [
[
'Wrapping...'
]
]);
assert.deepEqual(newInitializer[SPECIAL_PROPS.INJECT], [
'log',
'?test'
]);
assert.deepEqual(log.args, [['Wrapping...']]);
assert.deepEqual(newInitializer[SPECIAL_PROPS.INJECT], ['log', '?test']);
});
});
describe('inject', ()=>{
test('should allow to decorate an initializer with dependencies', ()=>{
const dependencies = [
'ENV'
];
describe('inject', () => {
test('should allow to decorate an initializer with dependencies', () => {
const dependencies = ['ENV'];
const newInitializer = inject(dependencies, provider(aProviderInitializer, 'aProvider'));

@@ -133,6 +99,4 @@ assert.notEqual(newInitializer, aProviderInitializer);

});
test('should allow to decorate an initializer builder with dependencies', ()=>{
const dependencies = [
'ENV'
];
test('should allow to decorate an initializer builder with dependencies', () => {
const dependencies = ['ENV'];
const newInitializer = inject(dependencies, aProviderInitializer);

@@ -143,6 +107,4 @@ assert.notEqual(newInitializer, aProviderInitializer);

});
test('should allow to decorate an initializer with dependencies', ()=>{
const dependencies = [
'ENV'
];
test('should allow to decorate an initializer with dependencies', () => {
const dependencies = ['ENV'];
const newInitializer = inject(dependencies, service(aServiceInitializer, 'aService'));

@@ -153,6 +115,4 @@ assert.notEqual(newInitializer, aServiceInitializer);

});
test('should allow to decorate an initializer builder with dependencies', ()=>{
const dependencies = [
'ENV'
];
test('should allow to decorate an initializer builder with dependencies', () => {
const dependencies = ['ENV'];
const newInitializer = inject(dependencies, aServiceInitializer);

@@ -163,6 +123,4 @@ assert.notEqual(newInitializer, aServiceInitializer);

});
test('should allow to decorate an initializer with mapped dependencies', ()=>{
const dependencies = [
'ANOTHER_ENV>ENV'
];
test('should allow to decorate an initializer with mapped dependencies', () => {
const dependencies = ['ANOTHER_ENV>ENV'];
const newInitializer = inject(dependencies, aProviderInitializer);

@@ -173,21 +131,13 @@ assert.notEqual(newInitializer, aProviderInitializer);

});
test('should fail with a constant', ()=>{
assert.throws(()=>{
inject([
'test'
], constant('test', 'test'));
test('should fail with a constant', () => {
assert.throws(() => {
inject(['test'], constant('test', 'test'));
}, /E_BAD_INJECT_IN_CONSTANT/);
});
});
describe('useInject', ()=>{
test('should set the right dependencies', ()=>{
const fromDependencies = [
'ENV',
'CORS'
];
describe('useInject', () => {
test('should set the right dependencies', () => {
const fromDependencies = ['ENV', 'CORS'];
const fromInitializer = inject(fromDependencies, aProviderInitializer);
const toDependencies = [
'db',
'log'
];
const toDependencies = ['db', 'log'];
const toInitializer = inject(toDependencies, aProviderInitializer);

@@ -199,17 +149,11 @@ const newInitializer = useInject(fromInitializer, toInitializer);

assert.deepEqual(newInitializer[SPECIAL_PROPS.INJECT], [
...fromDependencies
...fromDependencies,
]);
});
});
describe('mergeInject', ()=>{
test('should amend dependencies', ()=>{
const fromDependencies = [
'ENV',
'CORS'
];
describe('mergeInject', () => {
test('should amend dependencies', () => {
const fromDependencies = ['ENV', 'CORS'];
const fromInitializer = inject(fromDependencies, aProviderInitializer);
const toDependencies = [
'db',
'log'
];
const toDependencies = ['db', 'log'];
const toInitializer = inject(toDependencies, aProviderInitializer);

@@ -222,16 +166,13 @@ const newInitializer = mergeInject(fromInitializer, toInitializer);

...toDependencies,
...fromDependencies
...fromDependencies,
]);
});
});
describe('autoInject', ()=>{
test('should allow to decorate an initializer with dependencies', ()=>{
const baseProvider = async ({ ENV , mysql: db })=>async ()=>({
ENV,
db
});
const dependencies = [
'ENV',
'mysql'
];
describe('autoInject', () => {
test('should allow to decorate an initializer with dependencies', () => {
const baseProvider = async ({ ENV, mysql: db }) => async () => ({
ENV,
db,
});
const dependencies = ['ENV', 'mysql'];
const newInitializer = autoInject(baseProvider);

@@ -242,13 +183,10 @@ assert.notEqual(newInitializer, baseProvider);

});
test('should allow to decorate an initializer with a function name', ()=>{
async function baseProvider({ ENV , mysql: db }) {
async ()=>({
ENV,
db
});
test('should allow to decorate an initializer with a function name', () => {
async function baseProvider({ ENV, mysql: db }) {
async () => ({
ENV,
db,
});
}
const dependencies = [
'ENV',
'mysql'
];
const dependencies = ['ENV', 'mysql'];
const newInitializer = autoInject(baseProvider);

@@ -259,14 +197,10 @@ assert.notEqual(newInitializer, baseProvider);

});
test('should allow to decorate an initializer with optional dependencies', ()=>{
const noop = ()=>undefined;
const baseProvider = async ({ ENV , log =noop , debug: aDebug = noop })=>async ()=>({
ENV,
log,
aDebug
});
const dependencies = [
'ENV',
'?log',
'?debug'
];
test('should allow to decorate an initializer with optional dependencies', () => {
const noop = () => undefined;
const baseProvider = async ({ ENV, log = noop, debug: aDebug = noop }) => async () => ({
ENV,
log,
aDebug,
});
const dependencies = ['ENV', '?log', '?debug'];
const newInitializer = autoInject(baseProvider);

@@ -277,14 +211,10 @@ assert.notEqual(newInitializer, baseProvider);

});
test('should allow to decorate an initializer with several arguments', ()=>{
const noop = ()=>undefined;
const baseProvider = async ({ ENV , log =noop , debug: aDebug = noop })=>async ()=>({
ENV,
log,
aDebug
});
const dependencies = [
'ENV',
'?log',
'?debug'
];
test('should allow to decorate an initializer with several arguments', () => {
const noop = () => undefined;
const baseProvider = async ({ ENV, log = noop, debug: aDebug = noop }) => async () => ({
ENV,
log,
aDebug,
});
const dependencies = ['ENV', '?log', '?debug'];
const newInitializer = autoInject(baseProvider);

@@ -295,14 +225,10 @@ assert.notEqual(newInitializer, baseProvider);

});
test('should allow to decorate an initializer with complex arguments', ()=>{
const noop = ()=>undefined;
const baseProvider = async ({ ENV , log =noop , debug: aDebug = noop })=>async ()=>({
ENV,
log,
aDebug
});
const dependencies = [
'ENV',
'?log',
'?debug'
];
test('should allow to decorate an initializer with complex arguments', () => {
const noop = () => undefined;
const baseProvider = async ({ ENV, log = noop, debug: aDebug = noop }) => async () => ({
ENV,
log,
aDebug,
});
const dependencies = ['ENV', '?log', '?debug'];
const newInitializer = autoInject(baseProvider);

@@ -313,16 +239,12 @@ assert.notEqual(newInitializer, baseProvider);

});
test('should fail with non async initializers', ()=>{
assert.throws(()=>{
autoInject(({ foo: bar = {
bar: 'foo'
} })=>{
test('should fail with non async initializers', () => {
assert.throws(() => {
autoInject((({ foo: bar = { bar: 'foo' } }) => {
return bar;
});
}));
}, /E_NON_ASYNC_INITIALIZER/);
});
test('should fail with too complex injections', ()=>{
assert.throws(()=>{
autoInject(async ({ foo: bar = {
bar: 'foo'
} })=>{
test('should fail with too complex injections', () => {
assert.throws(() => {
autoInject(async ({ foo: bar = { bar: 'foo' } }) => {
return bar;

@@ -332,47 +254,22 @@ });

});
test('should fail with no injections', ()=>{
assert.throws(()=>{
autoInject(async ()=>undefined);
test('should fail with no injections', () => {
assert.throws(() => {
autoInject(async () => undefined);
}, /E_AUTO_INJECTION_FAILURE/);
});
});
describe('alsoInject', ()=>{
test('should allow to decorate an initializer with dependencies', ()=>{
const newInitializer = alsoInject([
'ENV'
], inject([
'TEST'
], aProviderInitializer));
describe('alsoInject', () => {
test('should allow to decorate an initializer with dependencies', () => {
const newInitializer = alsoInject(['ENV'], inject(['TEST'], aProviderInitializer));
assert.notEqual(newInitializer, aProviderInitializer);
assert.deepEqual(newInitializer[SPECIAL_PROPS.INJECT], [
'TEST',
'ENV'
]);
assert.deepEqual(newInitializer[SPECIAL_PROPS.INJECT], ['TEST', 'ENV']);
});
test('should allow to decorate an initializer with dependencies', ()=>{
const newInitializer = alsoInject([
'ENV'
], aProviderInitializer);
test('should allow to decorate an initializer with dependencies', () => {
const newInitializer = alsoInject(['ENV'], aProviderInitializer);
assert.notEqual(newInitializer, aProviderInitializer);
assert.deepEqual(newInitializer[SPECIAL_PROPS.INJECT], [
'ENV'
]);
assert.deepEqual(newInitializer[SPECIAL_PROPS.INJECT], ['ENV']);
});
test('should dedupe dependencies', ()=>{
const baseProvider = inject([
'?TEST'
], aProviderInitializer);
const newInitializer = alsoInject([
'ENV',
'?NODE_ENV',
'?TEST',
'TEST2',
'db>mysql'
], alsoInject([
'ENV',
'NODE_ENV',
'?TEST',
'?TEST2',
'mysql'
], baseProvider));
test('should dedupe dependencies', () => {
const baseProvider = inject(['?TEST'], aProviderInitializer);
const newInitializer = alsoInject(['ENV', '?NODE_ENV', '?TEST', 'TEST2', 'db>mysql'], alsoInject(['ENV', 'NODE_ENV', '?TEST', '?TEST2', 'mysql'], baseProvider));
assert.notEqual(newInitializer, baseProvider);

@@ -385,17 +282,8 @@ assert.deepEqual(newInitializer[SPECIAL_PROPS.INJECT], [

'TEST2',
'db>mysql'
'db>mysql',
]);
});
test('should preserve single optional dependencies', ()=>{
const baseProvider = inject([
'ENV',
'?TEST'
], aProviderInitializer);
const newInitializer = alsoInject([
'ENV',
'?TEST2'
], alsoInject([
'ENV',
'?TEST3'
], baseProvider));
test('should preserve single optional dependencies', () => {
const baseProvider = inject(['ENV', '?TEST'], aProviderInitializer);
const newInitializer = alsoInject(['ENV', '?TEST2'], alsoInject(['ENV', '?TEST3'], baseProvider));
assert.notEqual(newInitializer, baseProvider);

@@ -406,14 +294,8 @@ assert.deepEqual(newInitializer[SPECIAL_PROPS.INJECT], [

'ENV',
'?TEST2'
'?TEST2',
]);
});
test('should preserve mapped dependencies', ()=>{
const baseProvider = inject([
'mysql',
'?sftp'
], aProviderInitializer);
const newInitializer = alsoInject([
'db>mysql',
'?ftp>sftp'
], baseProvider);
test('should preserve mapped dependencies', () => {
const baseProvider = inject(['mysql', '?sftp'], aProviderInitializer);
const newInitializer = alsoInject(['db>mysql', '?ftp>sftp'], baseProvider);
assert.notEqual(newInitializer, baseProvider);

@@ -424,22 +306,8 @@ assert.deepEqual(newInitializer[SPECIAL_PROPS.INJECT], [

'db>mysql',
'?ftp>sftp'
'?ftp>sftp',
]);
});
test('should solve dependencies alias name clash', ()=>{
const baseProvider = inject([
'?TEST'
], aProviderInitializer);
const newInitializer = alsoInject([
'ENV',
'?NODE_ENV',
'?TEST',
'db>mysql',
'?log>logly'
], alsoInject([
'ENV',
'NODE_ENV',
'?TEST',
'db>pg',
'?log>logger'
], baseProvider));
test('should solve dependencies alias name clash', () => {
const baseProvider = inject(['?TEST'], aProviderInitializer);
const newInitializer = alsoInject(['ENV', '?NODE_ENV', '?TEST', 'db>mysql', '?log>logly'], alsoInject(['ENV', 'NODE_ENV', '?TEST', 'db>pg', '?log>logger'], baseProvider));
assert.notEqual(newInitializer, aProviderInitializer);

@@ -451,22 +319,8 @@ assert.deepEqual(newInitializer[SPECIAL_PROPS.INJECT], [

'db>mysql',
'?log>logly'
'?log>logly',
]);
});
test('should solve dependencies alias name clash', ()=>{
const baseProvider = inject([
'?TEST'
], aProviderInitializer);
const newInitializer = alsoInject([
'ENV',
'?NODE_ENV',
'?TEST',
'db>mysql',
'?log>logly'
], alsoInject([
'ENV',
'NODE_ENV',
'?TEST',
'db>pg',
'?log>logger'
], baseProvider));
test('should solve dependencies alias name clash', () => {
const baseProvider = inject(['?TEST'], aProviderInitializer);
const newInitializer = alsoInject(['ENV', '?NODE_ENV', '?TEST', 'db>mysql', '?log>logly'], alsoInject(['ENV', 'NODE_ENV', '?TEST', 'db>pg', '?log>logger'], baseProvider));
assert.notEqual(newInitializer, aProviderInitializer);

@@ -478,8 +332,8 @@ assert.deepEqual(newInitializer[SPECIAL_PROPS.INJECT], [

'db>mysql',
'?log>logly'
'?log>logly',
]);
});
});
describe('parseInjections', ()=>{
test('should work with TypeScript dependencies', ()=>{
describe('parseInjections', () => {
test('should work with TypeScript dependencies', () => {
assert.deepEqual(parseInjections(`async function initNexmo({

@@ -493,23 +347,13 @@ ENV,

log: Function;
}): Promise<SMSService> {}`), [
'ENV',
'NEXMO',
'?log'
]);
}): Promise<SMSService> {}`), ['ENV', 'NEXMO', '?log']);
});
test('should allow to decorate an initializer with dependencies', ()=>{
const newInitializer = alsoInject([
'ENV'
], aProviderInitializer);
test('should allow to decorate an initializer with dependencies', () => {
const newInitializer = alsoInject(['ENV'], aProviderInitializer);
assert.notEqual(newInitializer, aProviderInitializer);
assert.deepEqual(newInitializer[SPECIAL_PROPS.INJECT], [
'ENV'
]);
assert.deepEqual(newInitializer[SPECIAL_PROPS.INJECT], ['ENV']);
});
});
describe('singleton', ()=>{
test('should allow to decorate an initializer with singleton option', ()=>{
const dependencies = [
'ANOTHER_ENV>ENV'
];
describe('singleton', () => {
test('should allow to decorate an initializer with singleton option', () => {
const dependencies = ['ANOTHER_ENV>ENV'];
const newInitializer = inject(dependencies, singleton(aProviderInitializer, true));

@@ -521,6 +365,4 @@ assert.notEqual(newInitializer, aProviderInitializer);

});
test('should allow to be used several times', ()=>{
const dependencies = [
'ANOTHER_ENV>ENV'
];
test('should allow to be used several times', () => {
const dependencies = ['ANOTHER_ENV>ENV'];
const newInitializer = inject(dependencies, singleton(singleton(aProviderInitializer), false));

@@ -533,7 +375,5 @@ assert.notEqual(newInitializer, aProviderInitializer);

});
describe('name', ()=>{
test('should allow to decorate an initializer with a name', ()=>{
const dependencies = [
'ANOTHER_ENV>ENV'
];
describe('name', () => {
test('should allow to decorate an initializer with a name', () => {
const dependencies = ['ANOTHER_ENV>ENV'];
const baseName = 'hash';

@@ -547,7 +387,5 @@ const newInitializer = inject(dependencies, name(baseName, aProviderInitializer));

});
describe('autoName', ()=>{
test('should allow to decorate an initializer with its function name', ()=>{
const dependencies = [
'ANOTHER_ENV>ENV'
];
describe('autoName', () => {
test('should allow to decorate an initializer with its function name', () => {
const dependencies = ['ANOTHER_ENV>ENV'];
const baseName = 'hash';

@@ -562,6 +400,4 @@ const newInitializer = inject(dependencies, autoName(async function hash() {

});
test('should allow to decorate an initializer with its init like function name', ()=>{
const dependencies = [
'ANOTHER_ENV>ENV'
];
test('should allow to decorate an initializer with its init like function name', () => {
const dependencies = ['ANOTHER_ENV>ENV'];
const baseName = 'hash';

@@ -576,6 +412,4 @@ const newInitializer = inject(dependencies, autoName(async function initHash() {

});
test('should allow to decorate an initializer with its initialize like function name', ()=>{
const dependencies = [
'ANOTHER_ENV>ENV'
];
test('should allow to decorate an initializer with its initialize like function name', () => {
const dependencies = ['ANOTHER_ENV>ENV'];
const baseName = 'hash';

@@ -590,6 +424,4 @@ const newInitializer = inject(dependencies, autoName(async function initializeHash() {

});
test('should allow to decorate a bounded initializer', ()=>{
const dependencies = [
'ANOTHER_ENV>ENV'
];
test('should allow to decorate a bounded initializer', () => {
const dependencies = ['ANOTHER_ENV>ENV'];
const baseName = 'hash';

@@ -605,13 +437,11 @@ const newInitializer = autoName(inject(dependencies, singleton(async function initializeHash() {

});
test('should fail with anonymous functions', ()=>{
assert.throws(()=>{
autoName(async ()=>undefined);
test('should fail with anonymous functions', () => {
assert.throws(() => {
autoName(async () => undefined);
}, /E_AUTO_NAMING_FAILURE/);
});
});
describe('extra', ()=>{
test('should allow to decorate an initializer with extra infos', ()=>{
const extraInformations = {
httpHandler: true
};
describe('extra', () => {
test('should allow to decorate an initializer with extra infos', () => {
const extraInformations = { httpHandler: true };
const newInitializer = extra(extraInformations, aProviderInitializer);

@@ -622,6 +452,4 @@ assert.notEqual(newInitializer, aProviderInitializer);

});
test('should allow to decorate an initializer with extra infos', ()=>{
const extraInformations = {
httpHandler: true
};
test('should allow to decorate an initializer with extra infos', () => {
const extraInformations = { httpHandler: true };
const newInitializer = extra(extraInformations, aProviderInitializer, true);

@@ -632,10 +460,5 @@ assert.notEqual(newInitializer, aProviderInitializer);

});
test('should allow to decorate an initializer with additional extra infos', ()=>{
const baseExtraInformations = {
yolo: true,
httpHandler: false
};
const additionalExtraInformations = {
httpHandler: true
};
test('should allow to decorate an initializer with additional extra infos', () => {
const baseExtraInformations = { yolo: true, httpHandler: false };
const additionalExtraInformations = { httpHandler: true };
const newInitializer = extra(baseExtraInformations, extra(additionalExtraInformations, aProviderInitializer), true);

@@ -647,11 +470,9 @@ assert.notEqual(newInitializer, aProviderInitializer);

...baseExtraInformations,
...baseExtraInformations
...baseExtraInformations,
});
});
});
describe('type', ()=>{
test('should allow to decorate an initializer with a type', ()=>{
const dependencies = [
'ANOTHER_ENV>ENV'
];
describe('type', () => {
test('should allow to decorate an initializer with a type', () => {
const dependencies = ['ANOTHER_ENV>ENV'];
const baseName = 'hash';

@@ -667,7 +488,5 @@ const baseType = 'service';

});
describe('initializer', ()=>{
test('should allow to decorate an initializer with every properties', ()=>{
const dependencies = [
'ANOTHER_ENV>ENV'
];
describe('initializer', () => {
test('should allow to decorate an initializer with every properties', () => {
const dependencies = ['ANOTHER_ENV>ENV'];
const baseName = 'hash';

@@ -679,3 +498,3 @@ const baseType = 'service';

singleton: true,
name: baseName
name: baseName,
}, aServiceInitializer);

@@ -689,13 +508,13 @@ assert.notEqual(newInitializer, aProviderInitializer);

});
test('should fail with bad properties', ()=>{
assert.throws(()=>{
test('should fail with bad properties', () => {
assert.throws(() => {
initializer({
name: 'yolo',
yolo: ''
}, async ()=>undefined);
yolo: '',
}, async () => undefined);
}, /E_BAD_PROPERTY/);
});
});
describe('constant', ()=>{
test('should allow to create an initializer from a constant', async ()=>{
describe('constant', () => {
test('should allow to create an initializer from a constant', async () => {
const baseName = 'THE_VALUE';

@@ -708,19 +527,13 @@ const baseValue = 42;

});
test('should fail with dependencies since it makes no sense', ()=>{
assert.throws(()=>{
constant('time', inject([
'hash3'
], async ()=>undefined));
test('should fail with dependencies since it makes no sense', () => {
assert.throws(() => {
constant('time', inject(['hash3'], async () => undefined));
}, /E_CONSTANT_INJECTION/);
});
});
describe('service', ()=>{
test('should allow to create an initializer from a service builder', async ()=>{
const aServiceBuilder = async (_services)=>'A_SERVICE';
const dependencies = [
'ANOTHER_ENV>ENV'
];
const extraData = {
cool: true
};
describe('service', () => {
test('should allow to create an initializer from a service builder', async () => {
const aServiceBuilder = async (_services) => 'A_SERVICE';
const dependencies = ['ANOTHER_ENV>ENV'];
const extraData = { cool: true };
const baseName = 'hash';

@@ -737,10 +550,6 @@ const baseType = 'service';

});
test('should allow to create an initializer from a generic service builder', async ()=>{
const aServiceBuilder = async (_services)=>'';
const dependencies = [
'ANOTHER_ENV>ENV'
];
const extraData = {
nice: true
};
test('should allow to create an initializer from a generic service builder', async () => {
const aServiceBuilder = async (_services) => '';
const dependencies = ['ANOTHER_ENV>ENV'];
const extraData = { nice: true };
const baseName = 'hash';

@@ -757,4 +566,4 @@ const baseType = 'service';

});
test('should fail with no service builder', ()=>{
assert.throws(()=>{
test('should fail with no service builder', () => {
assert.throws(() => {
service(undefined);

@@ -764,5 +573,5 @@ }, /E_NO_SERVICE_BUILDER/);

});
describe('autoService', ()=>{
test('should detect the service details', ()=>{
const baseServiceBuilder = async function initializeMySQL({ ENV }) {
describe('autoService', () => {
test('should detect the service details', () => {
const baseServiceBuilder = async function initializeMySQL({ ENV }) {
return ENV;

@@ -772,9 +581,7 @@ };

assert.notEqual(newInitializer, baseServiceBuilder);
assert.deepEqual(newInitializer[SPECIAL_PROPS.INJECT], [
'ENV'
]);
assert.deepEqual(newInitializer[SPECIAL_PROPS.INJECT], ['ENV']);
assert.equal(newInitializer[SPECIAL_PROPS.NAME], 'mySQL');
assert.equal(newInitializer[SPECIAL_PROPS.TYPE], 'service');
});
test('should detect the service details even with no dependencies', ()=>{
test('should detect the service details even with no dependencies', () => {
const baseServiceBuilder = async function initializeMySQL() {

@@ -790,13 +597,7 @@ return;

});
describe('provider', ()=>{
test('should allow to create an initializer from a provider builder', async ()=>{
const aProviderInitializerBuilder = async ()=>({
service: 'A_SERVICE'
});
const dependencies = [
'ANOTHER_ENV>ENV'
];
const extraData = {
singleton: true
};
describe('provider', () => {
test('should allow to create an initializer from a provider builder', async () => {
const aProviderInitializerBuilder = async () => ({ service: 'A_SERVICE' });
const dependencies = ['ANOTHER_ENV>ENV'];
const extraData = { singleton: true };
const baseName = 'hash';

@@ -813,12 +614,8 @@ const baseType = 'provider';

});
test('should allow to create an initializer from a provider builder', async ()=>{
const aServiceBuilder = async (_services)=>({
service: 'A_SERVICE'
});
const dependencies = [
'ANOTHER_ENV>ENV'
];
const extraData = {
extra: true
};
test('should allow to create an initializer from a provider builder', async () => {
const aServiceBuilder = async (_services) => ({
service: 'A_SERVICE',
});
const dependencies = ['ANOTHER_ENV>ENV'];
const extraData = { extra: true };
const baseName = 'hash';

@@ -835,4 +632,4 @@ const baseType = 'provider';

});
test('should fail with no provider builder', ()=>{
assert.throws(()=>{
test('should fail with no provider builder', () => {
assert.throws(() => {
provider(undefined);

@@ -842,22 +639,16 @@ }, /E_NO_PROVIDER_BUILDER/);

});
describe('autoProvider', ()=>{
test('should detect the provider details', ()=>{
const baseInitializer = async function initializeMySQL({ ENV }) {
return {
service: ENV
};
describe('autoProvider', () => {
test('should detect the provider details', () => {
const baseInitializer = async function initializeMySQL({ ENV, }) {
return { service: ENV };
};
const newInitializer = autoProvider(baseInitializer);
assert.notEqual(newInitializer, baseInitializer);
assert.deepEqual(newInitializer[SPECIAL_PROPS.INJECT], [
'ENV'
]);
assert.deepEqual(newInitializer[SPECIAL_PROPS.INJECT], ['ENV']);
assert.equal(newInitializer[SPECIAL_PROPS.NAME], 'mySQL');
assert.equal(newInitializer[SPECIAL_PROPS.TYPE], 'provider');
});
test('should detect the provider details even with no dependencies', ()=>{
test('should detect the provider details even with no dependencies', () => {
const baseInitializer = async function initializeMySQL() {
return {
service: 'A_SERVICE'
};
return { service: 'A_SERVICE' };
};

@@ -871,12 +662,9 @@ const newInitializer = autoProvider(baseInitializer);

});
describe('handler', ()=>{
test('should work', async ()=>{
describe('handler', () => {
test('should work', async () => {
const baseName = 'sampleHandler';
const injectedServices = [
'kikooo',
'lol'
];
const injectedServices = ['kikooo', 'lol'];
const services = {
kikooo: 'kikooo',
lol: 'lol'
lol: 'lol',
};

@@ -890,31 +678,23 @@ const theInitializer = handler(sampleHandler, baseName, injectedServices);

deps: services,
args: [
'test'
]
args: ['test'],
});
async function sampleHandler(deps, ...args) {
return {
deps,
args
};
return { deps, args };
}
});
test('should fail with no name', ()=>{
assert.throws(()=>{
handler(async ()=>undefined);
test('should fail with no name', () => {
assert.throws(() => {
handler(async () => undefined);
}, /E_NO_HANDLER_NAME/);
});
});
describe('autoHandler', ()=>{
test('should work', async ()=>{
describe('autoHandler', () => {
test('should work', async () => {
const services = {
kikooo: 'kikooo',
lol: 'lol'
lol: 'lol',
};
const theInitializer = autoHandler(sampleHandler);
assert.deepEqual(theInitializer.$name, sampleHandler.name);
assert.deepEqual(theInitializer.$inject, [
'kikooo',
'lol'
]);
assert.deepEqual(theInitializer.$inject, ['kikooo', 'lol']);
const theHandler = await theInitializer(services);

@@ -924,27 +704,16 @@ const result = await theHandler('test');

deps: services,
args: [
'test'
]
args: ['test'],
});
async function sampleHandler({ kikooo , lol }, ...args) {
return {
deps: {
kikooo,
lol
},
args
};
async function sampleHandler({ kikooo, lol }, ...args) {
return { deps: { kikooo, lol }, args };
}
});
test('should work with spread services', async ()=>{
test('should work with spread services', async () => {
const services = {
kikooo: 'kikooo',
lol: 'lol'
lol: 'lol',
};
const theInitializer = autoHandler(sampleHandler);
assert.deepEqual(theInitializer.$name, sampleHandler.name);
assert.deepEqual(theInitializer.$inject, [
'kikooo',
'lol'
]);
assert.deepEqual(theInitializer.$inject, ['kikooo', 'lol']);
const theHandler = await theInitializer(services);

@@ -954,40 +723,30 @@ const result = await theHandler('test');

deps: services,
args: [
'test'
]
args: ['test'],
});
async function sampleHandler({ kikooo , lol , ...services }, ...args) {
return {
deps: {
kikooo,
lol,
...services
},
args
};
async function sampleHandler({ kikooo, lol, ...services }, ...args) {
return { deps: { kikooo, lol, ...services }, args };
}
});
test('should fail for anonymous functions', ()=>{
assert.throws(()=>{
autoHandler(async ()=>undefined);
test('should fail for anonymous functions', () => {
assert.throws(() => {
autoHandler(async () => undefined);
}, /E_AUTO_NAMING_FAILURE/);
});
});
describe('parseDependencyDeclaration', ()=>{
test('should work', ()=>{
describe('parseDependencyDeclaration', () => {
test('should work', () => {
assert.deepEqual(parseDependencyDeclaration('db>pgsql'), {
serviceName: 'db',
mappedName: 'pgsql',
optional: false
optional: false,
});
});
test('should work with unmapped names', ()=>{
test('should work with unmapped names', () => {
assert.deepEqual(parseDependencyDeclaration('?pgsql'), {
serviceName: 'pgsql',
mappedName: 'pgsql',
optional: true
optional: true,
});
});
});
//# sourceMappingURL=util.test.js.map

@@ -28,3 +28,3 @@ {

"name": "knifecycle",
"version": "15.0.0",
"version": "15.0.1",
"description": "Manage your NodeJS processes's lifecycle automatically with an unobtrusive dependency injection implementation.",

@@ -50,3 +50,3 @@ "main": "dist/index.js",

"architecture": "jsarch 'src/**/*.ts' > ARCHITECTURE.md && git add ARCHITECTURE.md",
"build": "rimraf 'dist' && swc ./src -s -d dist -C jsc.target=es2022",
"build": "rimraf 'dist' && tsc --outDir dist",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md",

@@ -65,3 +65,5 @@ "cli": "env NODE_ENV=${NODE_ENV:-cli}",

"preversion": "npm run build && npm run lint && npm t && npm run doc && npm run architecture && npm run metapak -- -s",
"rebuild": "swc ./src -s -d dist -C jsc.target=es2022",
"test": "npm run jest",
"type-check": "tsc --pretty --noEmit",
"version": "npm run changelog"

@@ -99,4 +101,4 @@ },

"jsdoc-to-markdown": "^8.0.0",
"metapak": "^5.0.0",
"metapak-nfroidure": "^14.1.1",
"metapak": "^5.0.1",
"metapak-nfroidure": "^14.1.3",
"prettier": "^2.8.8",

@@ -103,0 +105,0 @@ "rimraf": "^5.0.1",

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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