New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@ogre-tools/injectable

Package Overview
Dependencies
Maintainers
2
Versions
88
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ogre-tools/injectable - npm Package Compare versions

Comparing version 5.0.1 to 5.1.0

reports/webpack-build-stats.json

43

CHANGELOG.md

@@ -6,8 +6,47 @@ # Change Log

### [5.0.1](https://github.com/ogre-works/ogre-tools/compare/v5.0.0...v5.0.1) (2022-02-16)
## [5.1.0](https://github.com/ogre-works/ogre-tools/compare/v5.0.0...v5.1.0) (2022-03-11)
### Features
* Add "setup" to branch-tags of dependency graphing ([da315b2](https://github.com/ogre-works/ogre-tools/commit/da315b2dbcaa3e248b7133642db232e5e28bdc00))
* Add branch tags for dependency graphing ([9e43197](https://github.com/ogre-works/ogre-tools/commit/9e431972a7d0722b4bbffaacefb91e7d7cb9bb2c))
* Add injection context for withInjectables ([d69a57d](https://github.com/ogre-works/ogre-tools/commit/d69a57d7d1428342b76bb0d6ef272ede6c85858c))
* Expose instantiation parameters to error monitoring for better messages ([7b13267](https://github.com/ogre-works/ogre-tools/commit/7b13267fcb0798a1ac4879389747139ee348041d))
* **graphing:** Add appearance for sync/async ([a1523cf](https://github.com/ogre-works/ogre-tools/commit/a1523cf9ee3059adb52a6f8598d2fdcde6642055))
* **graphing:** Introduce graph customizers to eg. make reactive dependencies stick out ([8877020](https://github.com/ogre-works/ogre-tools/commit/8877020ab7e756d732f204f1b996b91e1343badc))
* **graphing:** Make graph more eye-ballable by adding colors and symbols ([d31bc57](https://github.com/ogre-works/ogre-tools/commit/d31bc57dcfc04677de0d4201475d9bdb763e1343))
* **graphing:** Make injection tokens stand out ([17796b5](https://github.com/ogre-works/ogre-tools/commit/17796b5d1608be6ddcd223dba456a79aa7908ae9))
* **graphing:** Make link and link info text color customizable separately ([aa42120](https://github.com/ogre-works/ogre-tools/commit/aa42120a70babd163dd817cd25583a090a7b3d18))
* **graphing:** Make link and link info text colors customizable ([2057001](https://github.com/ogre-works/ogre-tools/commit/205700110f2fbea33c90c9ff1f23848e0db6ccdc))
* Hide irrelevant data in dependency graphing ([7e38ac3](https://github.com/ogre-works/ogre-tools/commit/7e38ac34c219d6cba23c37e94decf2d04964d7df))
* **injectable:** Make setups able to inject many ([1dfca82](https://github.com/ogre-works/ogre-tools/commit/1dfca82428959204d352ba86292e2d418656fef2))
* Introduce ad-hoc injectables that do not require registration ([82c7a43](https://github.com/ogre-works/ogre-tools/commit/82c7a43ba530325c090334108827c92f700dfbd3))
* Introduce error monitoring for all injectables that return a function ([6f458f2](https://github.com/ogre-works/ogre-tools/commit/6f458f27fb028435f8d3147e78a9ba3dc4c98bd5))
* Introduce injectable extension for dependency graphing using Plant UML ([a32d206](https://github.com/ogre-works/ogre-tools/commit/a32d206a5d14feb7a61544ed47b5bdef9219e831))
* Introduce late registration ([5524f0e](https://github.com/ogre-works/ogre-tools/commit/5524f0e61a86ddbb60f0d33db5347fba90ba19b7))
* Introduce variation point for error handling of instantiation ([481b4d5](https://github.com/ogre-works/ogre-tools/commit/481b4d5a3a3995a5f2f0383e7986fa27ddaddf5c))
* Introduce variation point for global decoration of "instantiate" ([fe5e1a9](https://github.com/ogre-works/ogre-tools/commit/fe5e1a991884d589b60934e4dda1377b69202baa))
* Introduce variation point for targeted decoration of "instantiate" ([70b8918](https://github.com/ogre-works/ogre-tools/commit/70b8918f415c450638cade56098c1f8a4588b23c))
* Make error monitoring possible for TypeScript ([d0fd4ef](https://github.com/ogre-works/ogre-tools/commit/d0fd4ef7c4525189918397069dc2e6ff1de0eff0))
* Make injection tokens display more pretty in dependency graphing ([dc55d12](https://github.com/ogre-works/ogre-tools/commit/dc55d12a3f7d30ee957843e734968294d2e95efe))
* Make setuppables display more pretty in dependency graphing ([1fbdf74](https://github.com/ogre-works/ogre-tools/commit/1fbdf74405a7b7d97ec4680eed12149ad036d725))
* Permit instance key of any type ([0a68f24](https://github.com/ogre-works/ogre-tools/commit/0a68f24079b8d887e6b586bd1d2d83df7efc63e3))
* Show lifecycle names in dependency graphing ([9c0cf20](https://github.com/ogre-works/ogre-tools/commit/9c0cf205d7116d8438d307aee8b5752c37588851))
### Bug Fixes
* Fix typing of getting injectable for injection token ([6412b3e](https://github.com/ogre-works/ogre-tools/commit/6412b3e083b6c3db02167813628d8d0e9062b82c))
* Add missing export for error monitoring in JS ([55fd4d2](https://github.com/ogre-works/ogre-tools/commit/55fd4d20241bd044be5668d80df4fdc493311da4))
* complete fix ([2bba4f3](https://github.com/ogre-works/ogre-tools/commit/2bba4f30cee996559a2135e718c87686f38f9bde))
* Define nodes before links in dependency graphing for human-readability ([af2c0d3](https://github.com/ogre-works/ogre-tools/commit/af2c0d3e4a67efebaa58eaf8c1d31bcfc5dd1f79))
* Faulty type parameter value ([17cfa76](https://github.com/ogre-works/ogre-tools/commit/17cfa76954a97671466cdcc7ae2c2433720ab5a8))
* Fix bad import ([16f06ce](https://github.com/ogre-works/ogre-tools/commit/16f06ce96b8e7b078e3e3d4df3ecc8fb266299e7))
* Fix typing of getting injectable for injection token ([53ca8ad](https://github.com/ogre-works/ogre-tools/commit/53ca8ad616d4c1ffa6fdde1811f2a404527bc585))
* lifecycleEnum.keyedSingleton not working ([06a86dc](https://github.com/ogre-works/ogre-tools/commit/06a86dc9fc19777616d1367300a8b13846ae7a0e))
* Make also injectMany comply to error monitoring for instantiation ([eb8baf7](https://github.com/ogre-works/ogre-tools/commit/eb8baf7b6fa0d71d7d5d9841b9e90093a5098799))
* Make setuppables display correctly in dependency graphing ([30425e4](https://github.com/ogre-works/ogre-tools/commit/30425e4de36b82ca97983390f54ff9e7d7b88ec7))
* Present dependency graph in correct order and with tokens ([17a5b6c](https://github.com/ogre-works/ogre-tools/commit/17a5b6c4654e9bc5ab0c5f0c499840d08beead13))
* Resolve PR comments ([bb2e1de](https://github.com/ogre-works/ogre-tools/commit/bb2e1debdcfe901f998eabcaba1941222e917950))
* **typings:** Improve typings to work with arbitrary injection params ([c1d900a](https://github.com/ogre-works/ogre-tools/commit/c1d900a22ae9d609e3b70b3d0a034dd5e81901b0))

@@ -14,0 +53,0 @@

@@ -6,2 +6,23 @@ import getInjectionToken from './src/getInjectionToken/getInjectionToken';

export { getInjectionToken, getInjectable, lifecycleEnum, createContainer };
import {
registerDependencyGraphing,
plantUmlDependencyGraphInjectable,
dependencyGraphCustomizerToken,
} from './src/dependency-injection-container/extensions/dependency-graphing/dependency-graphing';
import {
registerErrorMonitoring,
errorMonitorInjectionToken,
} from './src/dependency-injection-container/extensions/error-monitoring/error-monitoring';
export {
getInjectionToken,
getInjectable,
lifecycleEnum,
createContainer,
registerDependencyGraphing,
plantUmlDependencyGraphInjectable,
dependencyGraphCustomizerToken,
registerErrorMonitoring,
errorMonitorInjectionToken,
};

271

ogre-tools-injectable.d.ts
/// <reference types="jest" />
declare module '@ogre-tools/injectable' {
type InferFromInjectable<T> = T extends NormalInjectable<
infer TInstance,
infer TInstantiationParameter
>
? [TInstance, TInstantiationParameter]
: never;
type ValueType =
| string
| number
| boolean
| symbol
| bigint
| object
| Array<any>;
export type TentativeTuple<T> = T extends ValueType ? [T] : [undefined?];
export interface DiContainer extends DiContainerForInjection<false> {
purge: (injectableKey: NormalInjectable<any, any>) => void;
purge: (injectableKey: Injectable<any, any, any>) => void;
runSetups: () => Promise<void>;
override<TInjectable extends NormalInjectable<unknown, unknown>>(
injectable: TInjectable,
instantiateStub: (
di: DiContainerForInstantiate,
...instantiationParameter: TentativeTuple<
InferFromInjectable<TInjectable>[1]
>
) => InferFromInjectable<TInjectable>[0],
override<
InjectionInstance extends InjectionTokenInstance,
InjectionTokenInstance,
InstantiationParam,
>(
injectable: Injectable<
InjectionInstance,
InjectionTokenInstance,
InstantiationParam
>,
instantiateStub: Instantiate<InjectionInstance, InstantiationParam>,
): void;
register(injectable: NormalInjectable<any, any>): void;
register(injectable: InjectionTokenInjectable<any>): void;
register<
InjectionInstance extends InjectionTokenInstance,
InjectionTokenInstance,
InstantiationParam,
>(
injectable: Injectable<
InjectionInstance,
InjectionTokenInstance,
InstantiationParam
>,
): void;
preventSideEffects: () => void;
}
export interface DiContainerForSetup extends DiContainerForInjection<true> {}
export type Instantiate<InjectionInstance, InstantiationParam> = {
(
di: DiContainerForInstantiate,
param: InstantiationParam extends void ? void : InstantiationParam,
): InjectionInstance;
};
export interface DiContainerForInstantiate
extends DiContainerForInjection<false> {}
export type DiContainerForSetup = DiContainerForInjection<true>;
export type DiContainerForInstantiate = DiContainerForInjection<false>;
type InjectionToken<TInstance, TInstantiationParameter> = {
template: TInstance;
instantiationParameter: TInstantiationParameter;
export interface InjectionToken<InjectionInstance, InstantiationParam> {
template: InjectionInstance;
instantiationParameter: InstantiationParam;
key: Symbol;
};
}
interface CommonInjectable {
export interface Injectable<
InjectionInstance extends InjectionTokenInstance,
InjectionTokenInstance,
InstantiationParam,
> {
id: string;
setup?: (di: DiContainerForSetup) => void | Promise<void>;
causesSideEffects?: boolean;
lifecycle?: ILifecycle;
injectionToken?: InjectionToken<InjectionTokenInstance, InstantiationParam>;
instantiate: Instantiate<InjectionInstance, InstantiationParam>;
lifecycle: ILifecycle<InstantiationParam>;
}
interface InjectionTokenInjectable<
TInjectionToken extends InjectionToken<unknown, unknown>,
> extends CommonInjectable {
injectionToken: TInjectionToken;
type InjectableLifecycle<InstantiationParam> = InstantiationParam extends void
? {
lifecycle?: ILifecycle<void>;
}
: {
lifecycle: ILifecycle<InstantiationParam>;
};
instantiate: (
di: DiContainerForInstantiate,
instantiationParameter: InferFromToken<TInjectionToken>[1],
) => InferFromToken<TInjectionToken>[0];
}
type GetInjectableOptions<
InjectionInstance extends InjectionTokenInstance,
InjectionTokenInstance,
InstantiationParam,
> = InjectableLifecycle<InstantiationParam> &
Omit<
Injectable<InjectionInstance, InjectionTokenInstance, InstantiationParam>,
'lifecycle'
>;
interface NormalInjectable<TInstance, TInstantiationParameter>
extends CommonInjectable {
instantiate: (
di: DiContainerForInstantiate,
instantiationParameter: TInstantiationParameter,
) => TInstance;
}
export function getInjectable<
TInjectionToken extends InjectionToken<unknown, unknown>,
InjectionInstance extends InjectionTokenInstance,
InjectionTokenInstance,
InstantiationParam = void,
>(
options: InjectionTokenInjectable<TInjectionToken>,
): InjectionTokenInjectable<TInjectionToken>;
options: GetInjectableOptions<
InjectionInstance,
InjectionTokenInstance,
InstantiationParam
>,
): Injectable<InjectionInstance, InjectionTokenInstance, InstantiationParam>;
export function getInjectable<TInstance, TInstantiationParameter>(
options: NormalInjectable<TInstance, TInstantiationParameter>,
): NormalInjectable<TInstance, TInstantiationParameter>;
export function getInjectionToken<
InjectionInstance,
InstantiationParam = void,
>({ id: string }): InjectionToken<InjectionInstance, InstantiationParam>;
export function getInjectionToken<TInstance, TInstantiationParameter = void>({
id: string,
}): InjectionToken<TInstance, TInstantiationParameter>;
type SelectiveAsync<
IsAsync extends boolean,
InjectionInstance,
> = IsAsync extends true ? Promise<InjectionInstance> : InjectionInstance;
export interface Injectable<
TInjectionToken,
TInstance,
TInstantiationParameter,
> {
id: string;
setup?: (di: DiContainerForSetup) => void | Promise<void>;
causesSideEffects?: boolean;
lifecycle?: ILifecycle;
injectionToken?: TInjectionToken;
interface Inject<IsAsync extends boolean> {
<InjectionInstance>(
key:
| Injectable<InjectionInstance, unknown, void>
| InjectionToken<InjectionInstance, void>,
): SelectiveAsync<IsAsync, InjectionInstance>;
<InjectionInstance, InstantiationParam>(
key:
| Injectable<InjectionInstance, unknown, InstantiationParam>
| InjectionToken<InjectionInstance, InstantiationParam>,
param: InstantiationParam,
): SelectiveAsync<IsAsync, InjectionInstance>;
}
instantiate: (
di: DiContainerForInstantiate,
instantiationParameter: TInstantiationParameter,
) => TInstance;
interface InjectMany<IsAsync extends boolean> {
<InjectionInstance>(
key:
| Injectable<InjectionInstance, unknown, void>
| InjectionToken<InjectionInstance, void>,
): SelectiveAsync<IsAsync, InjectionInstance[]>;
<InjectionInstance, InstantiationParam>(
key:
| Injectable<InjectionInstance, unknown, InstantiationParam>
| InjectionToken<InjectionInstance, InstantiationParam>,
param: InstantiationParam,
): SelectiveAsync<IsAsync, InjectionInstance[]>;
}
interface DiContainerForInjection<TReturnAsPromise extends boolean> {
inject<TInjectable extends NormalInjectable<unknown, unknown>>(
injectableKey: TInjectable,
...instantiationParameter: TentativeTuple<
InferFromInjectable<TInjectable>[1]
>
): TReturnAsPromise extends true
? Promise<InferFromInjectable<TInjectable>[0]>
: InferFromInjectable<TInjectable>[0];
inject: Inject<TReturnAsPromise>;
injectMany: InjectMany<TReturnAsPromise>;
inject<TInjectionToken extends InjectionToken<unknown, unknown>>(
injectionToken: TInjectionToken,
...instantiationParameter: TentativeTuple<
TInjectionToken['instantiationParameter']
>
): TReturnAsPromise extends true
? Promise<TInjectionToken['template']>
: TInjectionToken['template'];
register<
InjectionInstance extends InjectionTokenInstance,
InjectionTokenInstance,
InstantiationParam,
>(
injectable: Injectable<
InjectionInstance,
InjectionTokenInstance,
InstantiationParam
>,
): void;
}
injectMany<TInjectionToken extends InjectionToken<unknown, unknown>>(
injectionToken: TInjectionToken,
...instantiationParameter: TentativeTuple<
TInjectionToken['instantiationParameter']
>
): TReturnAsPromise extends true
? Promise<TInjectionToken['template'][]>
: TInjectionToken['template'][];
export interface ILifecycle<InstantiationParam> {
getInstanceKey: (di: DiContainer, params: InstantiationParam) => any;
}
type InferFromToken<T> = T extends InjectionToken<
infer TInstance,
infer TInstantiationParameter
>
? [TInstance, TInstantiationParameter]
: never;
const storedInstanceKey: unique symbol;
const nonStoredInstanceKey: unique symbol;
export interface ILifecycle {
getInstanceKey: (di: DiContainer, param: unknown) => string | number;
}
// ASDs
export const lifecycleEnum: {
singleton: ILifecycle;
singleton: (di: DiContainer, param: void) => typeof storedInstanceKey;
keyedSingleton: <TInstantiationParameter>(keyedSingletonOptions: {
getInstanceKey: (
di: DiContainer,
instantiationParameter: TInstantiationParameter,
) => string | number;
}) => ILifecycle;
keyedSingleton<InstantiationParam>(
options: ILifecycle<InstantiationParam>,
): typeof options;
transient: ILifecycle;
transient: {
getInstanceKey: (di: DiContainer) => typeof nonStoredInstanceKey;
};
};
export function createContainer(...getRequireContexts: any[]): DiContainer;
export function registerErrorMonitoring(di: DiContainer): void;
export const errorMonitorInjectionToken: InjectionToken<
(error: {
context: { id: string; instantiationParameter: any }[];
error: any;
}) => void | Promise<void>,
void
>;
interface Customizer {
shouldCustomize: (instance: any) => boolean;
// Todo: add proper typing
customizeLink: (link: any) => void;
customizeNode: (node: any) => void;
}
export const dependencyGraphCustomizerToken: InjectionToken<Customizer, void>;
export function registerDependencyGraphing(di: DiContainer): void;
export const plantUmlDependencyGraphInjectable: InjectionToken<string, void>;
}
{
"name": "@ogre-tools/injectable",
"private": false,
"version": "5.0.1",
"version": "5.1.0",
"description": "A brutal dependency injection container",

@@ -13,4 +13,4 @@ "repository": {

"keywords": [
"js",
"functional-programming"
"dependency-injection",
"js"
],

@@ -20,9 +20,6 @@ "author": "Ogre Works",

"dependencies": {
"@ogre-tools/fp": "^5.0.1",
"@ogre-tools/fp": "^5.1.0",
"lodash": "^4.17.21"
},
"scripts": {
"build": "webpack"
},
"gitHead": "5e4de86e2f77a9b9a159192959741bc308751027",
"gitHead": "862acd435ed8aca0e32b448e3af0d4e0574866c5",
"bugs": {

@@ -29,0 +26,0 @@ "url": "https://github.com/ogre-works/ogre-tools/issues"

import getInjectionToken from '../getInjectionToken/getInjectionToken';
import getInjectable from '../getInjectable/getInjectable';
import getDi from '../test-utils/getDiForUnitTesting';
import lifecycleEnum from './lifecycleEnum';

@@ -119,3 +120,3 @@ describe('createContainer.injection-token', () => {

const someOtherInjectionToken = getInjectionToken({
id: 'some-injection-token',
id: 'some-other-injection-token',
});

@@ -126,3 +127,3 @@

injectionToken: someOtherInjectionToken,
instantiate: di => di.injectMany(parentInjectable),
instantiate: di => di.injectMany(someInjectionToken),
});

@@ -133,3 +134,3 @@

injectionToken: someInjectionToken,
instantiate: di => di.injectMany(childInjectable),
instantiate: di => di.injectMany(someOtherInjectionToken),
});

@@ -140,5 +141,5 @@

expect(() => {
di.injectMany(parentInjectable, undefined, ['some-bogus-context']);
di.injectMany(someInjectionToken);
}).toThrow(
'Cycle of injectables encountered: "some-parent-injectable" -> "some-child-injectable" -> "some-parent-injectable"',
'Cycle of injectables encountered: "some-injection-token" -> "some-parent-injectable" -> "some-other-injection-token" -> "some-child-injectable" -> "some-injection-token"',
);

@@ -160,2 +161,30 @@ });

});
it('given injectables with a dependency cycle, when injecting many with custom root context, throws error with the custom context', () => {
const injectionToken = getInjectionToken({ id: 'some-injection-token' });
const childInjectable = getInjectable({
id: 'some-child-injectable',
instantiate: di => di.inject(parentInjectable),
});
const parentInjectable = getInjectable({
id: 'some-parent-injectable',
instantiate: di => di.inject(childInjectable),
injectionToken,
});
const di = getDi(parentInjectable, childInjectable);
expect(() => {
di.injectMany(injectionToken, undefined, {
injectable: {
id: 'some-custom-context-id',
lifecycle: lifecycleEnum.transient,
},
});
}).toThrow(
'Cycle of injectables encountered: "some-custom-context-id" -> "some-injection-token" -> "some-parent-injectable" -> "some-child-injectable" -> "some-parent-injectable"',
);
});
});

@@ -0,3 +1,4 @@

import getDi from '../test-utils/getDiForUnitTesting';
import getInjectable from '../getInjectable/getInjectable';
import getDi from '../test-utils/getDiForUnitTesting';
import lifecycleEnum from './lifecycleEnum';

@@ -52,6 +53,6 @@ describe('createContainer.injection', () => {

it('given async injectables with a dependency cycle, when injected, throws', () => {
it('given sync injectables with a dependency cycle, when injected with custom root context, throws error with the custom context', () => {
const childInjectable = getInjectable({
id: 'some-child-injectable',
instantiate: async di => await di.inject(parentInjectable),
instantiate: di => di.inject(parentInjectable),
});

@@ -61,3 +62,3 @@

id: 'some-parent-injectable',
instantiate: async di => await di.inject(childInjectable),
instantiate: di => di.inject(childInjectable),
});

@@ -67,13 +68,18 @@

const actualPromise = di.inject(parentInjectable);
return expect(actualPromise).rejects.toThrow(
'Cycle of injectables encountered: "some-parent-injectable" -> "some-child-injectable" -> "some-parent-injectable"',
expect(() => {
di.inject(parentInjectable, undefined, {
injectable: {
id: 'some-custom-context-id',
lifecycle: lifecycleEnum.transient,
},
});
}).toThrow(
'Cycle of injectables encountered: "some-custom-context-id" -> "some-parent-injectable" -> "some-child-injectable" -> "some-parent-injectable"',
);
});
it('given injectables with a dependency cycle, when injected with bogus context, throws error without bogus context', () => {
it('given async injectables with a dependency cycle, when injected, throws', () => {
const childInjectable = getInjectable({
id: 'some-child-injectable',
instantiate: di => di.inject(parentInjectable),
instantiate: async di => await di.inject(parentInjectable),
});

@@ -83,3 +89,3 @@

id: 'some-parent-injectable',
instantiate: di => di.inject(childInjectable),
instantiate: async di => await di.inject(childInjectable),
});

@@ -89,5 +95,5 @@

expect(() => {
di.inject(parentInjectable, undefined, ['some-bogus-context']);
}).toThrow(
const actualPromise = di.inject(parentInjectable);
return expect(actualPromise).rejects.toThrow(
'Cycle of injectables encountered: "some-parent-injectable" -> "some-child-injectable" -> "some-parent-injectable"',

@@ -94,0 +100,0 @@ );

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

import get from 'lodash/fp/get';
import conforms from 'lodash/fp/conforms';

@@ -8,16 +7,23 @@ import filter from 'lodash/fp/filter';

import forEach from 'lodash/fp/forEach';
import get from 'lodash/fp/get';
import getCycles from './getCycles/getCycles';
import getInjectionToken from '../getInjectionToken/getInjectionToken';
import has from 'lodash/fp/has';
import invoke from 'lodash/fp/invoke';
import isFunction from 'lodash/fp/isFunction';
import isUndefined from 'lodash/fp/isUndefined';
import join from 'lodash/fp/join';
import last from 'lodash/fp/last';
import { nonStoredInstanceKey } from './lifecycleEnum';
import map from 'lodash/fp/map';
import matches from 'lodash/fp/matches';
import not from 'lodash/fp/negate';
import isEqual from 'lodash/fp/isEqual';
import once from 'lodash/fp/once';
import reject from 'lodash/fp/reject';
import sortBy from 'lodash/fp/sortBy';
import last from 'lodash/fp/last';
import join from 'lodash/fp/join';
import reject from 'lodash/fp/reject';
import tap from 'lodash/fp/tap';
import { pipeline } from '@ogre-tools/fp';
import isUndefined from 'lodash/fp/isUndefined';
import lifecycleEnum, { nonStoredInstanceKey } from './lifecycleEnum';
import getCycles from './getCycles/getCycles';
import curry from 'lodash/fp/curry';
import overSome from 'lodash/fp/overSome';

@@ -33,48 +39,95 @@ export default (...listOfGetRequireContexts) => {

const privateDi = {
inject: (alias, instantiationParameter, context = []) => {
const originalInjectable = getRelatedInjectable({
const privateInject = (alias, instantiationParameter, context = []) => {
checkForTooManyMatches(injectables, alias);
const relatedInjectables = getRelatedInjectables({ injectables, alias });
if (relatedInjectables.length === 0 && alias.adHoc === true) {
privateDi.register(alias);
} else {
checkForNoMatches(injectables, alias, context);
}
const originalInjectable = getRelatedInjectable({
injectables,
alias,
context,
});
const overriddenInjectable = getOverridingInjectable({
overridingInjectables,
alias,
});
const injectable = overriddenInjectable || originalInjectable;
if (!setupsHaveBeenRan && !setupsAreBeingRan && injectable.setup) {
throw new Error(
`Tried to inject setuppable "${injectable.id}" before setups are ran.`,
);
}
if (sideEffectsArePrevented && injectable.causesSideEffects) {
throw new Error(
`Tried to inject "${injectable.id}" when side-effects are prevented.`,
);
}
return getInstance({
injectable,
instantiationParameter,
di: privateDi,
injectableMap,
context,
injectMany: nonDecoratedPrivateInjectMany,
});
};
const nonDecoratedPrivateInjectMany = (
injectionToken,
instantiationParameter,
oldContext = [],
) => {
const newContext = [...oldContext, { injectable: injectionToken }];
return pipeline(
getRelatedInjectables({
injectables,
alias,
context,
});
alias: injectionToken,
}),
const overriddenInjectable = getOverridingInjectable({
overridingInjectables,
alias,
});
map(injectable =>
decoratedPrivateInject(injectable, instantiationParameter, newContext),
),
);
};
const injectable = overriddenInjectable || originalInjectable;
const withInjectionDecorators = withInjectionDecoratorsFor({
injectMany: nonDecoratedPrivateInjectMany,
});
if (!setupsHaveBeenRan && !setupsAreBeingRan && injectable.setup) {
const decoratedPrivateInject = withInjectionDecorators(privateInject);
const decoratedPrivateInjectMany = withInjectionDecorators(
nonDecoratedPrivateInjectMany,
);
const privateDi = {
inject: decoratedPrivateInject,
injectMany: decoratedPrivateInjectMany,
register: externalInjectable => {
if (setupsHaveBeenRan && !!externalInjectable.setup) {
throw new Error(
`Tried to inject setuppable "${injectable.id}" before setups are ran.`,
`Tried to register setuppable "${externalInjectable.id}" after setups have already ran.`,
);
}
if (sideEffectsArePrevented && injectable.causesSideEffects) {
if (setupsAreBeingRan && !!externalInjectable.setup) {
throw new Error(
`Tried to inject "${injectable.id}" when side-effects are prevented.`,
`Tried to register setuppable "${externalInjectable.id}" during setup.`,
);
}
return getInstance({
injectable,
instantiationParameter,
di: privateDi,
injectableMap,
context,
});
},
injectMany: (alias, instantiationParameter, context = []) =>
pipeline(
getRelatedInjectables({ injectables, alias }),
map(injectable =>
privateDi.inject(injectable, instantiationParameter, context),
),
),
register: externalInjectable => {
if (!externalInjectable.id) {

@@ -90,14 +143,9 @@ throw new Error('Tried to register injectable without ID.');

const lifecycle = externalInjectable.lifecycle || lifecycleEnum.singleton;
const internalInjectable = {
...externalInjectable,
lifecycle,
...(externalInjectable.setup
? { setup: once(externalInjectable.setup) }
: {}),
// Todo: spread-ternary
setup: externalInjectable.setup
? once(externalInjectable.setup)
: undefined,
permitSideEffects: function () {

@@ -148,6 +196,8 @@ this.causesSideEffects = false;

const diForSetupsFor = setuppable => ({
injectMany: publicDi.injectMany,
inject: async (alias, parameter) => {
const targetSetuppable = injectables.find(
conforms({
id: x => x === alias.id,
id: isEqual(alias.id),
setup: isFunction,

@@ -178,4 +228,11 @@ }),

return privateDi.inject(alias, parameter);
return privateDi.inject(alias, parameter, [
{
injectable: setuppable,
isSetup: true,
},
]);
},
register: privateDi.register,
});

@@ -229,4 +286,16 @@

...privateDi,
inject: (alias, parameter) => privateDi.inject(alias, parameter),
injectMany: (alias, parameter) => privateDi.injectMany(alias, parameter),
inject: (alias, parameter, customContextItem) =>
privateDi.inject(
alias,
parameter,
customContextItem ? [customContextItem] : undefined,
),
injectMany: (alias, parameter, customContextItem) =>
privateDi.injectMany(
alias,
parameter,
customContextItem ? [customContextItem] : undefined,
),
};

@@ -249,32 +318,11 @@

const isRelatedTo = alias => injectable =>
injectable.id === alias.id ||
(injectable.injectionToken && injectable.injectionToken === alias);
const isRelatedTo = curry(
(alias, injectable) =>
injectable.id === alias.id ||
(injectable.injectionToken && injectable.injectionToken === alias),
);
const getRelatedInjectable = ({ injectables, alias, context }) => {
const relatedInjectables = getRelatedInjectables({ injectables, alias });
const getRelatedInjectable = ({ injectables, alias }) =>
pipeline(getRelatedInjectables({ injectables, alias }), first);
if (relatedInjectables.length === 0) {
const errorContextString = [...context, { id: alias.id }]
.map(get('id'))
.join('" -> "');
throw new Error(
`Tried to inject non-registered injectable "${errorContextString}".`,
);
}
if (relatedInjectables.length > 1) {
throw new Error(
`Tried to inject single injectable for injection token "${
alias.id
}" but found multiple injectables: "${relatedInjectables
.map(relatedInjectable => relatedInjectable.id)
.join('", "')}"`,
);
}
return first(relatedInjectables);
};
const getRelatedInjectables = ({ injectables, alias }) =>

@@ -299,7 +347,18 @@ pipeline(injectables, filter(isRelatedTo(alias)));

const newContext = [...oldContext, { id: injectable.id }];
const newContext = [
...oldContext,
{
injectable,
instantiationParameter,
},
];
const injectableCausingCycle = pipeline(
oldContext,
find({ id: injectable.id }),
find(
contextItem =>
contextItem.injectable.id === injectable.id &&
contextItem.isSetup !== true,
),
);

@@ -310,3 +369,3 @@

`Cycle of injectables encountered: "${newContext
.map(get('id'))
.map(get('injectable.id'))
.join('" -> "')}"`,

@@ -323,2 +382,6 @@ );

di.injectMany(alias, parameter, newContext),
context: newContext,
register: di.register,
};

@@ -337,3 +400,12 @@

const newInstance = injectable.instantiate(
const instantiateWithDecorators = pipeline(
injectable.instantiate,
withInstantiationDecoratorsFor({
injectMany: di.injectMany,
injectable,
}),
);
const newInstance = instantiateWithDecorators(
minimalDi,

@@ -349,1 +421,93 @@ ...(isUndefined(instantiationParameter) ? [] : [instantiationParameter]),

};
export const instantiationDecoratorToken = getInjectionToken({
id: 'instantiate-decorator-token',
decorable: false,
});
export const injectionDecoratorToken = getInjectionToken({
id: 'injection-decorator-token',
decorable: false,
});
const withInstantiationDecoratorsFor = ({ injectMany, injectable }) => {
const isRelevantDecorator = isRelevantDecoratorFor(injectable);
return toBeDecorated =>
(...args) => {
if (injectable.decorable === false) {
return toBeDecorated(...args);
}
const decorators = pipeline(
injectMany(instantiationDecoratorToken),
filter(isRelevantDecorator),
map('decorate'),
);
return pipeline(toBeDecorated, ...decorators)(...args);
};
};
const withInjectionDecoratorsFor =
({ injectMany }) =>
toBeDecorated =>
(alias, ...args) => {
if (alias.decorable === false) {
return toBeDecorated(alias, ...args);
}
const isRelevantDecorator = isRelevantDecoratorFor(alias);
const decorators = pipeline(
injectMany(injectionDecoratorToken),
filter(isRelevantDecorator),
map('decorate'),
);
return pipeline(toBeDecorated, ...decorators)(alias, ...args);
};
const isGlobalDecorator = not(has('target'));
const isTargetedDecoratorFor = injectable =>
conforms({
target: alias => isRelatedTo(alias, injectable),
});
const isRelevantDecoratorFor = injectable =>
overSome([isGlobalDecorator, isTargetedDecoratorFor(injectable)]);
const checkForNoMatches = (injectables, alias, context) => {
const relatedInjectables = getRelatedInjectables({
injectables,
alias,
});
if (relatedInjectables.length === 0) {
const errorContextString = [...context, { injectable: { id: alias.id } }]
.map(get('injectable.id'))
.join('" -> "');
throw new Error(
`Tried to inject non-registered injectable "${errorContextString}".`,
);
}
};
const checkForTooManyMatches = (injectables, alias) => {
const relatedInjectables = getRelatedInjectables({
injectables,
alias,
});
if (relatedInjectables.length > 1) {
throw new Error(
`Tried to inject single injectable for injection token "${
alias.id
}" but found multiple injectables: "${relatedInjectables
.map(relatedInjectable => relatedInjectable.id)
.join('", "')}"`,
);
}
};

@@ -6,5 +6,6 @@ import asyncFn from '@async-fn/jest';

import getDi from '../test-utils/getDiForUnitTesting';
import getInjectionToken from '../getInjectionToken/getInjectionToken';
describe('createContainer.setuppable', () => {
it('given setuppables with a dependency cycle, when setupped, throws most complex cycle in system', () => {
it('given setuppables with a dependency cycle when injecting single, when setupped, throws most complex cycle in system', () => {
const someRootSetuppable = getInjectable({

@@ -43,3 +44,35 @@ id: 'some-root-injectable',

it('given setup for injectable, when setups are ran, runs the setup with a way to inject', async () => {
xit('given setuppables with a dependency cycle when injecting many, when setupped, throws most complex cycle in system', async () => {
const someToken = getInjectionToken({ id: 'some-token' });
const someSetuppable = getInjectable({
id: 'some-injectable',
setup: async di => {
await di.injectMany(someOtherToken);
},
instantiate: () => 'irrelevant',
injectionToken: someToken,
});
const someOtherToken = getInjectionToken({ id: 'some-other-token' });
const someOtherSetuppable = getInjectable({
id: 'some-other-injectable',
setup: async di => {
await di.injectMany(someToken);
},
instantiate: () => 'irrelevant',
injectionToken: someOtherToken,
});
const di = getDi(someSetuppable, someOtherSetuppable);
return expect(di.runSetups()).rejects.toThrow(
'Cycle of setuppables encountered: "some-root-injectable" -> "some-parent-injectable" -> "some-child-injectable" -> "some-parent-injectable"',
);
});
it('given setup for injectable, when setups are ran, runs the setup with a ways to inject', async () => {
let instanceFromSetup;

@@ -51,3 +84,6 @@

setup: async di => {
instanceFromSetup = await di.inject(someInjectable, 'some-parameter');
instanceFromSetup = {
single: await di.inject(someInjectable, 'some-parameter'),
many: await di.injectMany(someInjectionToken, 'some-parameter'),
};
},

@@ -59,10 +95,24 @@ });

lifecycle: lifecycleEnum.transient,
instantiate: (di, parameter) => `some-instance: "${parameter}"`,
instantiate: (di, parameter) => `some-single-instance: "${parameter}"`,
});
const di = getDi(someSetuppable, someInjectable);
const someInjectionToken = getInjectionToken({
id: 'some-injection-token',
});
const someTokenInjectable = getInjectable({
id: 'some-token-injectable',
lifecycle: lifecycleEnum.transient,
instantiate: (di, parameter) => `some-of-many-instances: "${parameter}"`,
injectionToken: someInjectionToken,
});
const di = getDi(someSetuppable, someInjectable, someTokenInjectable);
await di.runSetups();
expect(instanceFromSetup).toBe('some-instance: "some-parameter"');
expect(instanceFromSetup).toEqual({
single: 'some-single-instance: "some-parameter"',
many: ['some-of-many-instances: "some-parameter"'],
});
});

@@ -69,0 +119,0 @@

export const nonStoredInstanceKey = Symbol('non-stored-instance-key');
export const storedInstanceKey = Symbol('stored-instance-key');
export default {
singleton: {
getInstanceKey: () => 'singleton',
name: 'Singleton',
shortName: 'S',
color: 'lightGreen',
getInstanceKey: () => storedInstanceKey,
},
keyedSingleton: ({ getInstanceKey }) => ({ getInstanceKey }),
keyedSingleton: ({ getInstanceKey }) => ({
name: 'Keyed',
shortName: 'K',
color: 'pink',
getInstanceKey,
}),
transient: {
name: 'Transient',
shortName: 'T',
color: 'orchid',
getInstanceKey: () => nonStoredInstanceKey,
},
};

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

import identity from 'lodash/fp/identity';
import lifecycleEnum from '../dependency-injection-container/lifecycleEnum';
// Note: this function exists only for typed presence in TypeScript.
// It has little purpose in JavaScript.
export default identity;
export default ({ lifecycle = lifecycleEnum.singleton, ...injectable }) => ({
lifecycle,
...injectable,
});
// Note: this function exists only for typed presence in TypeScript.
// It has little purpose in JavaScript.
export default ({ id }) => ({ id });
export const injectionTokenSymbol = Symbol('injection-token');
export default ({ id, decorable = true }) => ({
id,
aliasType: injectionTokenSymbol,
decorable,
});

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

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