Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@tsed/di

Package Overview
Dependencies
Maintainers
5
Versions
1030
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@tsed/di - npm Package Compare versions

Comparing version 5.15.0 to 5.16.0

lib/class/Container.d.ts

74

lib/class/Provider.d.ts

@@ -1,13 +0,53 @@

import { RegistryKey, Store, Type } from "@tsed/core";
import { Store, Type } from "@tsed/core";
import { ProviderScope } from "../interfaces";
import { IProvider } from "../interfaces/IProvider";
import { ProviderType } from "../interfaces/ProviderType";
import { TokenProvider } from "../interfaces/TokenProvider";
export declare class Provider<T> implements IProvider<T> {
/**
*
*/
type: ProviderType | any;
/**
*
*/
injectable: boolean;
/**
*
*/
instance: T;
/**
*
*/
deps: any[];
/**
*
*/
useFactory: Function;
/**
*
*/
useValue: any;
/**
*
*/
protected _provide: TokenProvider;
/**
*
*/
protected _useClass: Type<T>;
/**
*
*/
protected _instance: T;
protected _type: ProviderType | any;
protected _provide: RegistryKey;
/**
*
*/
protected _scope: ProviderScope;
/**
*
*/
private _store;
[key: string]: any;
constructor(provide: RegistryKey);
constructor(token: TokenProvider);
/**

@@ -27,3 +67,3 @@ *

/**
*
* Create a new store if the given value is a class. Otherwise the value is ignored.
* @param value

@@ -34,25 +74,11 @@ */

*
* @returns {T}
* @returns {string}
*/
readonly className: string;
/**
*
* @param value
*/
instance: T;
/**
*
* @returns {any}
*/
readonly name: string;
/**
*
* @param value
*/
type: any;
/**
*
* @returns {string}
*/
readonly className: string;
/**
*
* @returns {Store}

@@ -70,3 +96,7 @@ */

scope: ProviderScope;
/**
*
*/
clone(): Provider<any>;
toString(): string;
}

@@ -5,9 +5,16 @@ "use strict";

const core_1 = require("@tsed/core");
const interfaces_1 = require("../interfaces");
const ProviderType_1 = require("../interfaces/ProviderType");
class Provider {
constructor(provide) {
this._type = ProviderType_1.ProviderType.PROVIDER;
this._provide = core_1.getClassOrSymbol(provide);
this._useClass = core_1.getClass(this._provide);
this._store = core_1.Store.from(this._provide);
constructor(token) {
/**
*
*/
this.type = ProviderType_1.ProviderType.PROVIDER;
/**
*
*/
this.injectable = true;
this.provide = token;
this.useClass = token;
}

@@ -26,3 +33,3 @@ /**

set provide(value) {
this._provide = value;
this._provide = core_1.isClass(value) ? core_1.getClass(value) : value;
}

@@ -37,42 +44,22 @@ /**

/**
*
* Create a new store if the given value is a class. Otherwise the value is ignored.
* @param value
*/
set useClass(value) {
this._store = core_1.Store.from(value);
this._useClass = value;
if (core_1.isClass(value)) {
this._useClass = core_1.getClass(value);
this._store = core_1.Store.from(value);
}
}
/**
*
* @returns {T}
* @returns {string}
*/
get instance() {
return this._instance;
get className() {
return this.name;
}
/**
*
* @param value
*/
set instance(value) {
this._instance = value;
}
/**
*
* @returns {any}
*/
get type() {
return this._type;
}
/**
*
* @param value
*/
set type(value) {
this._type = value;
}
/**
*
* @returns {string}
*/
get className() {
get name() {
return core_1.nameOf(this.provide);

@@ -92,3 +79,3 @@ }

get scope() {
return this.store.get("scope");
return this._store ? this.store.get("scope") : this._scope;
}

@@ -100,14 +87,48 @@ /**

set scope(scope) {
this.store.set("scope", scope);
this._store ? this.store.set("scope", scope) : this._scope;
}
/**
*
*/
clone() {
const provider = new Provider(this._provide);
provider._type = this._type;
provider.useClass = this._useClass;
provider._instance = this._instance;
const provider = new (core_1.getClass(this))(this.provide);
core_1.getKeys(this).forEach(key => {
provider[key] = this[key];
});
return provider;
}
toString() {
return `Token:${this.name}`;
}
}
tslib_1.__decorate([
core_1.Enumerable(),
tslib_1.__metadata("design:type", Object)
], Provider.prototype, "type", void 0);
tslib_1.__decorate([
core_1.Enumerable(),
tslib_1.__metadata("design:type", Boolean)
], Provider.prototype, "injectable", void 0);
tslib_1.__decorate([
core_1.Enumerable(),
tslib_1.__metadata("design:type", Object)
], Provider.prototype, "instance", void 0);
tslib_1.__decorate([
core_1.Enumerable(),
tslib_1.__metadata("design:type", Array)
], Provider.prototype, "deps", void 0);
tslib_1.__decorate([
core_1.Enumerable(),
tslib_1.__metadata("design:type", Function)
], Provider.prototype, "useFactory", void 0);
tslib_1.__decorate([
core_1.Enumerable(),
tslib_1.__metadata("design:type", Object)
], Provider.prototype, "useValue", void 0);
tslib_1.__decorate([
core_1.NotEnumerable(),
tslib_1.__metadata("design:type", Object)
], Provider.prototype, "_provide", void 0);
tslib_1.__decorate([
core_1.NotEnumerable(),
tslib_1.__metadata("design:type", core_1.Type)

@@ -121,4 +142,4 @@ ], Provider.prototype, "_useClass", void 0);

core_1.NotEnumerable(),
tslib_1.__metadata("design:type", Object)
], Provider.prototype, "_type", void 0);
tslib_1.__metadata("design:type", String)
], Provider.prototype, "_scope", void 0);
tslib_1.__decorate([

@@ -128,4 +149,14 @@ core_1.NotEnumerable(),

], Provider.prototype, "_store", void 0);
tslib_1.__decorate([
core_1.Enumerable(),
tslib_1.__metadata("design:type", core_1.Type),
tslib_1.__metadata("design:paramtypes", [core_1.Type])
], Provider.prototype, "useClass", null);
tslib_1.__decorate([
core_1.Enumerable(),
tslib_1.__metadata("design:type", String),
tslib_1.__metadata("design:paramtypes", [String])
], Provider.prototype, "scope", null);
exports.Provider = Provider;
//# sourceMappingURL=Provider.js.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@tsed/core");
const InjectablePropertyType_1 = require("../interfaces/InjectablePropertyType");
/**

@@ -43,3 +44,3 @@ * Return value from ServerSettingsService.

[propertyKey]: {
bindingType: "constant",
bindingType: InjectablePropertyType_1.InjectablePropertyType.CONSTANT,
propertyKey,

@@ -46,0 +47,0 @@ expression,

import { IProvider } from "../interfaces";
/**
* The decorators `@Injectable()` declare a new service can be injected in other service or controller on there `constructor`.
* All services annotated with `@Injectable()` are constructed one time.
* The decorators `@Injectable()` declare a new service can be injected in other service, controller, interceptor, etc.. on there `constructor`.
* All classes annotated with `@Injectable()` are built one time, excepted if you change the default provider configuration.
*
* > `@Service()` use the `reflect-metadata` to collect and inject service on controllers or other services.
* <<< @/docs/docs/snippets/providers/getting-started-injectable.ts
*
* ::: tip
* `@Injectable()` use the `reflect-metadata` to collect and inject the built provided to other services.
* :::
*
* ### Options
*
* - type (@@ProviderType@@ or `string`): Kind of provider. (Default: `ProviderType.PROVIDER`)
* - scope (@@ProviderScope@): Kind of provider. (Default: `ProviderScope.SINGLETON`)
* - deps (`Type<any>`): List of class or provider which will be injected to the constructor (Note: This options override default metadata generated by Typescript).
*
* @returns {Function}
* @decorator
*/
export declare function Injectable(provider?: Partial<IProvider<any>>): Function;
export declare function Injectable(options?: Partial<IProvider<any>>): Function;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const interfaces_1 = require("../interfaces");
const ProviderRegistry_1 = require("../registries/ProviderRegistry");
/**
* The decorators `@Injectable()` declare a new service can be injected in other service or controller on there `constructor`.
* All services annotated with `@Injectable()` are constructed one time.
* The decorators `@Injectable()` declare a new service can be injected in other service, controller, interceptor, etc.. on there `constructor`.
* All classes annotated with `@Injectable()` are built one time, excepted if you change the default provider configuration.
*
* > `@Service()` use the `reflect-metadata` to collect and inject service on controllers or other services.
* <<< @/docs/docs/snippets/providers/getting-started-injectable.ts
*
* ::: tip
* `@Injectable()` use the `reflect-metadata` to collect and inject the built provided to other services.
* :::
*
* ### Options
*
* - type (@@ProviderType@@ or `string`): Kind of provider. (Default: `ProviderType.PROVIDER`)
* - scope (@@ProviderScope@): Kind of provider. (Default: `ProviderScope.SINGLETON`)
* - deps (`Type<any>`): List of class or provider which will be injected to the constructor (Note: This options override default metadata generated by Typescript).
*
* @returns {Function}
* @decorator
*/
function Injectable(provider = {}) {
function Injectable(options = { scope: interfaces_1.ProviderScope.SINGLETON }) {
return (provide) => {
ProviderRegistry_1.registerProvider(Object.assign({}, provider, { provide }));
ProviderRegistry_1.registerProvider(Object.assign({}, options, { provide }));
};

@@ -17,0 +28,0 @@ }

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const ProviderRegistry_1 = require("../registries/ProviderRegistry");
const GlobalProviders_1 = require("../registries/GlobalProviders");
/**

@@ -12,3 +12,3 @@ * Override a provider which is already registered in ProviderRegistry.

return (target) => {
ProviderRegistry_1.ProviderRegistry.get(originalProvider).useClass = target;
GlobalProviders_1.GlobalProviders.get(originalProvider).useClass = target;
};

@@ -15,0 +15,0 @@ }

@@ -12,5 +12,3 @@ "use strict";

function Scope(scope = ProviderScope_1.ProviderScope.REQUEST) {
return core_1.Store.decorate(store => {
store.set("scope", scope);
});
return core_1.StoreSet("scope", scope);
}

@@ -17,0 +15,0 @@ exports.Scope = Scope;

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@tsed/core");
const InjectablePropertyType_1 = require("../interfaces/InjectablePropertyType");
/**

@@ -43,3 +44,3 @@ * Return value from ServerSettingsService.

[propertyKey]: {
bindingType: "value",
bindingType: InjectablePropertyType_1.InjectablePropertyType.VALUE,
propertyKey,

@@ -46,0 +47,0 @@ expression,

@@ -1,8 +0,7 @@

import { Type } from "@tsed/core";
/**
* @private
*/
import { TokenProvider } from "../interfaces";
export declare class InjectionError extends Error {
name: string;
constructor(target: Type<any>, serviceName: string, message?: string);
tokens: TokenProvider[];
origin: any;
constructor(token: TokenProvider, origin?: any);
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@tsed/core");
/**
* @private
*/
class InjectionError extends Error {
constructor(target, serviceName, message = "not found") {
super(`Service ${core_1.nameOf(target)} > ${serviceName} ${message}.`);
constructor(token, origin) {
super(core_1.isString(origin) ? origin : "");
this.name = "INJECTION_ERROR";
this.tokens = [];
this.tokens = [token];
if (origin) {
if (core_1.isString(origin)) {
this.origin = {
message: origin,
stack: this.stack
};
}
else {
if (origin.tokens) {
this.tokens = this.tokens.concat(origin.tokens);
this.origin = origin.origin;
}
}
}
this.message = "Injection failed on " + this.tokens.map(token => core_1.nameOf(token)).join(" > ") + "\nOrigin: " + this.origin.message;
}

@@ -12,0 +26,0 @@ }

export * from "./class/Provider";
export * from "./class/ProviderStorable";
export * from "./class/Container";
export * from "./class/LocalsContainer";
export * from "./interfaces";

@@ -4,0 +6,0 @@ export * from "./decorators/scope";

@@ -6,2 +6,4 @@ "use strict";

tslib_1.__exportStar(require("./class/ProviderStorable"), exports);
tslib_1.__exportStar(require("./class/Container"), exports);
tslib_1.__exportStar(require("./class/LocalsContainer"), exports);
tslib_1.__exportStar(require("./interfaces"), exports);

@@ -8,0 +10,0 @@ tslib_1.__exportStar(require("./decorators/scope"), exports);

@@ -1,12 +0,15 @@

export * from "./IInjectableMethod";
export * from "./IDILogger";
export * from "./IDISettings";
export * from "./IInjectableProperties";
export * from "./IInterceptor";
export * from "./IInterceptorContext";
export * from "./IInvokeOptions";
export * from "./IProvider";
export * from "./IDILogger";
export * from "./IDISettings";
export * from "./InjectablePropertyType";
export * from "./OnInit";
export * from "./OnDestroy";
export * from "./InjectablePropertyType";
export * from "./OnInit";
export * from "./ProviderScope";
export * from "./ProviderScope";
export * from "./ProviderType";

@@ -13,0 +16,0 @@ export * from "./RegistrySettings";

@@ -5,5 +5,7 @@ "use strict";

tslib_1.__exportStar(require("./InjectablePropertyType"), exports);
tslib_1.__exportStar(require("./InjectablePropertyType"), exports);
tslib_1.__exportStar(require("./ProviderScope"), exports);
tslib_1.__exportStar(require("./ProviderScope"), exports);
tslib_1.__exportStar(require("./ProviderType"), exports);
//# sourceMappingURL=index.js.map
import { Type } from "@tsed/core";
import { ProviderScope } from "./ProviderScope";
import { ProviderType } from "./ProviderType";

@@ -13,2 +14,14 @@ import { TokenProvider } from "./TokenProvider";

/**
* Provider type
*/
type?: ProviderType | string;
/**
* Instance build by the injector
*/
instance?: T;
/**
* Define dependencies to build the provider
*/
deps?: TokenProvider[];
/**
* Class to instantiate for the `token`.

@@ -18,10 +31,14 @@ */

/**
*
* Provide a function to build the provider
*/
instance?: T;
useFactory?: Function;
/**
* Provider type
* Provide predefined value
*/
type: ProviderType | any;
useValue?: any;
/**
* Scope used by the injector to build the provider.
*/
scope?: ProviderScope;
/**
*

@@ -28,0 +45,0 @@ */

export declare enum ProviderScope {
SINGLETON = "singleton",
REQUEST = "request"
REQUEST = "request",
INSTANCE = "instance"
}

@@ -7,4 +7,5 @@ "use strict";

ProviderScope["REQUEST"] = "request";
ProviderScope["INSTANCE"] = "instance";
})(ProviderScope = exports.ProviderScope || (exports.ProviderScope = {}));
//# sourceMappingURL=ProviderScope.js.map
export declare enum ProviderType {
VALUE = "value",
FACTORY = "factory",

@@ -6,6 +7,6 @@ SERVICE = "service",

CONTROLLER = "controller",
INTERCEPTOR = "interceptor",
CONVERTER = "converter",
INTERCEPTOR = "interceptor",
FILTER = "filter",
MIDDLEWARE = "middleware"
}

@@ -5,2 +5,3 @@ "use strict";

(function (ProviderType) {
ProviderType["VALUE"] = "value";
ProviderType["FACTORY"] = "factory";

@@ -10,4 +11,5 @@ ProviderType["SERVICE"] = "service";

ProviderType["CONTROLLER"] = "controller";
ProviderType["INTERCEPTOR"] = "interceptor";
// NOT STANDARD
ProviderType["CONVERTER"] = "converter";
ProviderType["INTERCEPTOR"] = "interceptor";
ProviderType["FILTER"] = "filter";

@@ -14,0 +16,0 @@ ProviderType["MIDDLEWARE"] = "middleware";

@@ -7,10 +7,9 @@ import { Registry } from "@tsed/core";

injectable?: boolean;
buildable: boolean;
/**
*
* @param target
* @param provider
* @param {Map<string | Function, any>} locals
* @param {any[]} designParamTypes
* @param deps
*/
onInvoke?(target: Provider<any>, locals: Map<string | Function, any>, designParamTypes: any[]): void;
onInvoke?(provider: Provider<any>, locals: Map<string | Function, any>, deps: any[]): void;
}

@@ -18,3 +18,3 @@ import { Registry, Type } from "@tsed/core";

*/
createRegistry(type: string, model: Type<Provider<any>>, options: Partial<RegistrySettings>): TypedProvidersRegistry;
createRegistry(type: string, model: Type<Provider<any>>, options?: Partial<RegistrySettings>): TypedProvidersRegistry;
/**

@@ -21,0 +21,0 @@ *

@@ -21,3 +21,3 @@ "use strict";

*/
createRegistry(type, model, options) {
createRegistry(type, model, options = { injectable: true }) {
const registry = new core_1.Registry(model, {

@@ -28,4 +28,3 @@ onCreate: this.set.bind(this)

registry,
injectable: true,
buildable: true
injectable: true
}, options));

@@ -55,4 +54,3 @@ return registry;

registry: this,
injectable: true,
buildable: true
injectable: true
};

@@ -59,0 +57,0 @@ }

@@ -1,8 +0,3 @@

import { IProvider, TypedProvidersRegistry } from "../interfaces";
import { IProvider } from "../interfaces";
/**
*
* @type {GlobalProviderRegistry}
*/
export declare const ProviderRegistry: TypedProvidersRegistry;
/**
* Register a provider configuration.

@@ -77,2 +72,24 @@ * @param {IProvider<any>} provider

/**
* Add a new value in the `ProviderRegistry`.
*
* #### Example with symbol definition
*
*
* ```typescript
* import {registerValue, InjectorService} from "@tsed/common";
*
* const MyValue = Symbol.from("MyValue")
*
* registerValue(MyValue, "myValue");
*
* @Service()
* export class OtherService {
* constructor(@Inject(MyValue) myValue: string){
* console.log(myValue); /// "myValue"
* }
* }
* ```
*/
export declare const registerValue: (provider: any, value?: any) => void;
/**
* Add a new service in the `ProviderRegistry`. This service will be built when `InjectorService` will be loaded.

@@ -79,0 +96,0 @@ *

@@ -11,3 +11,3 @@ "use strict";

// tslint:disable-next-line: variable-name
exports.ProviderRegistry = GlobalProviders_1.GlobalProviders.getRegistry(interfaces_1.ProviderType.PROVIDER);
GlobalProviders_1.GlobalProviders.getRegistry(interfaces_1.ProviderType.PROVIDER);
/**

@@ -17,6 +17,3 @@ *

*/
GlobalProviders_1.GlobalProviders.createRegistry(interfaces_1.ProviderType.SERVICE, Provider_1.Provider, {
injectable: true,
buildable: true
});
GlobalProviders_1.GlobalProviders.createRegistry(interfaces_1.ProviderType.SERVICE, Provider_1.Provider);
/**

@@ -26,6 +23,3 @@ *`

*/
GlobalProviders_1.GlobalProviders.createRegistry(interfaces_1.ProviderType.FACTORY, Provider_1.Provider, {
injectable: true,
buildable: false
});
GlobalProviders_1.GlobalProviders.createRegistry(interfaces_1.ProviderType.FACTORY, Provider_1.Provider);
/**

@@ -35,6 +29,3 @@ *

*/
GlobalProviders_1.GlobalProviders.createRegistry(interfaces_1.ProviderType.INTERCEPTOR, Provider_1.Provider, {
injectable: true,
buildable: true
});
GlobalProviders_1.GlobalProviders.createRegistry(interfaces_1.ProviderType.INTERCEPTOR, Provider_1.Provider);
/**

@@ -44,4 +35,3 @@ *

GlobalProviders_1.GlobalProviders.createRegistry(interfaces_1.ProviderType.CONTROLLER, Provider_1.Provider, {
injectable: false,
buildable: true
injectable: false
});

@@ -122,4 +112,50 @@ /**

*/
exports.registerFactory = GlobalProviders_1.GlobalProviders.createRegisterFn(interfaces_1.ProviderType.FACTORY);
exports.registerFactory = (provider, instance) => {
if (!provider.provide) {
provider = {
provide: provider
};
}
provider = Object.assign({
scope: interfaces_1.ProviderScope.SINGLETON,
useFactory() {
return instance;
}
}, provider, { type: interfaces_1.ProviderType.FACTORY });
GlobalProviders_1.GlobalProviders.getRegistry(interfaces_1.ProviderType.FACTORY).merge(provider.provide, provider);
};
/**
* Add a new value in the `ProviderRegistry`.
*
* #### Example with symbol definition
*
*
* ```typescript
* import {registerValue, InjectorService} from "@tsed/common";
*
* const MyValue = Symbol.from("MyValue")
*
* registerValue(MyValue, "myValue");
*
* @Service()
* export class OtherService {
* constructor(@Inject(MyValue) myValue: string){
* console.log(myValue); /// "myValue"
* }
* }
* ```
*/
exports.registerValue = (provider, value) => {
if (!provider.provide) {
provider = {
provide: provider
};
}
provider = Object.assign({
scope: interfaces_1.ProviderScope.SINGLETON,
useValue: value
}, provider, { type: interfaces_1.ProviderType.VALUE });
GlobalProviders_1.GlobalProviders.getRegistry(interfaces_1.ProviderType.VALUE).merge(provider.provide, provider);
};
/**
* Add a new service in the `ProviderRegistry`. This service will be built when `InjectorService` will be loaded.

@@ -126,0 +162,0 @@ *

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

import { Container } from "../class/Container";
import { LocalsContainer } from "../class/LocalsContainer";
import { Provider } from "../class/Provider";
import { IDILogger, IDISettings, IInjectableMethod, IInjectablePropertyService, IInjectablePropertyValue, ProviderScope, ProviderType, TokenProvider } from "../interfaces";
import { IDILogger, IDISettings, IInjectablePropertyService, IInjectablePropertyValue, IInvokeOptions, ProviderScope, TokenProvider } from "../interfaces";
/**

@@ -26,3 +28,3 @@ * This service contain all services collected by `@Service` or services declared manually with `InjectorService.factory()` or `InjectorService.service()`.

*/
export declare class InjectorService extends Map<TokenProvider, Provider<any>> {
export declare class InjectorService extends Container {
settings: IDISettings;

@@ -34,9 +36,7 @@ logger: IDILogger;

constructor();
scopeOf(providerType: ProviderType): ProviderScope;
/**
* The getProvider() method returns a specified element from a Map object.
* @returns {T} Returns the element associated with the specified key or undefined if the key can't be found in the Map object.
* @param token
* Retrieve default scope for a given provider.
* @param provider
*/
getProvider(token: TokenProvider): Provider<any> | undefined;
scopeOf(provider: Provider<any>): ProviderScope;
/**

@@ -49,8 +49,2 @@ * Clone a provider from GlobalProviders and the given token. forkProvider method build automatically the provider if the instance parameter ins't given.

/**
*
* @param {ProviderType} type
* @returns {[RegistryKey , Provider<any>][]}
*/
getProviders(type?: ProviderType | string): Provider<any>[];
/**
* Return a list of instance build by the injector.

@@ -81,3 +75,2 @@ */

* The has() method returns a boolean indicating whether an element with the specified key exists or not.
* @param key
* @returns {boolean}

@@ -87,3 +80,2 @@ * @param token

has(token: TokenProvider): boolean;
destroy(): Promise<void>;
/**

@@ -107,7 +99,6 @@ * Invoke the class and inject all services that required by the class constructor.

* @param locals Optional object. If preset then any argument Class are read from this object first, before the `InjectorService` is consulted.
* @param designParamTypes Optional object. List of injectable types.
* @param requiredScope
* @param options
* @returns {T} The class constructed.
*/
invoke<T>(token: TokenProvider, locals?: Map<string | Function, any>, designParamTypes?: any[], requiredScope?: boolean): T;
invoke<T>(token: TokenProvider, locals?: Map<TokenProvider, any>, options?: Partial<IInvokeOptions<T>>): T;
/**

@@ -118,3 +109,3 @@ * Build all providers from GlobalProviders or from given providers parameters and emit `$onInit` event.

*/
load(): Promise<any>;
load(container?: Map<TokenProvider, Provider<any>>): Promise<LocalsContainer<any>>;
/**

@@ -176,41 +167,14 @@ *

*
* ```typescript
* import {InjectorService} from "@tsed/common";
*
* class MyService {
* constructor(injectorService: InjectorService) {
* injectorService.invokeMethod(this.method, {
* this,
* methodName: 'method'
* });
* }
*
* method(otherService: OtherService) {}
* }
* ```
*
* @returns {any}
* @param handler The injectable method to invoke. Method parameters are injected according method signature.
* @param options Object to configure the invocation.
* @deprecated
* @param target
* @param locals
* @param options
* @private
*/
invokeMethod(handler: any, options: IInjectableMethod<any>): any;
private _invoke;
/**
* Emit an event to all service. See service [lifecycle hooks](/docs/services.md#lifecycle-hooks).
* @param eventName The event name to emit at all services.
* @param args List of the parameters to give to each services.
* @returns {Promise<any[]>} A list of promises.
*/
emit(eventName: string, ...args: any[]): Promise<void>;
/**
*
* @returns {any}
* Create options to invoke a provider or class.
* @param token
* @param options
*/
private mapServices;
/**
*
* @returns {Map<Type<any>, any>}
*/
private build;
private mapInvokeOptions;
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
var InjectorService_1;
const core_1 = require("@tsed/core");
const Container_1 = require("../class/Container");
const LocalsContainer_1 = require("../class/LocalsContainer");
const injectable_1 = require("../decorators/injectable");
const InjectionError_1 = require("../errors/InjectionError");
const InjectionScopeError_1 = require("../errors/InjectionScopeError");
const interfaces_1 = require("../interfaces");
const GlobalProviders_1 = require("../registries/GlobalProviders");
const ProviderRegistry_1 = require("../registries/ProviderRegistry");
/**

@@ -33,3 +35,3 @@ * This service contain all services collected by `@Service` or services declared manually with `InjectorService.factory()` or `InjectorService.service()`.

*/
class InjectorService extends Map {
let InjectorService = InjectorService_1 = class InjectorService extends Container_1.Container {
constructor() {

@@ -40,14 +42,11 @@ super();

this.scopes = {};
this.forkProvider(InjectorService, this);
const provider = this.addProvider(InjectorService_1).getProvider(InjectorService_1);
provider.instance = this;
}
scopeOf(providerType) {
return this.scopes[providerType] || interfaces_1.ProviderScope.SINGLETON;
}
/**
* The getProvider() method returns a specified element from a Map object.
* @returns {T} Returns the element associated with the specified key or undefined if the key can't be found in the Map object.
* @param token
* Retrieve default scope for a given provider.
* @param provider
*/
getProvider(token) {
return super.get(core_1.getClassOrSymbol(token));
scopeOf(provider) {
return provider.scope || this.scopes[provider.type] || interfaces_1.ProviderScope.SINGLETON;
}

@@ -60,4 +59,6 @@ /**

forkProvider(token, instance) {
const provider = GlobalProviders_1.GlobalProviders.get(token).clone();
this.set(token, provider);
const provider = this.addProvider(token).getProvider(token);
if (!instance) {
instance = /* await */ this.invoke(token);
}
provider.instance = instance;

@@ -67,16 +68,6 @@ return provider;

/**
*
* @param {ProviderType} type
* @returns {[RegistryKey , Provider<any>][]}
*/
getProviders(type) {
return Array.from(this)
.filter(([key, provider]) => (type ? provider.type === type : true))
.map(([key, provider]) => provider);
}
/**
* Return a list of instance build by the injector.
*/
toArray() {
return Array.from(this.values()).map(provider => provider.instance);
return super.toArray().map(provider => provider.instance);
}

@@ -107,3 +98,2 @@ /**

* The has() method returns a boolean indicating whether an element with the specified key exists or not.
* @param key
* @returns {boolean}

@@ -115,8 +105,2 @@ * @param token

}
destroy() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
yield this.emit("$onDestroy");
this.clear();
});
}
/**

@@ -140,25 +124,31 @@ * Invoke the class and inject all services that required by the class constructor.

* @param locals Optional object. If preset then any argument Class are read from this object first, before the `InjectorService` is consulted.
* @param designParamTypes Optional object. List of injectable types.
* @param requiredScope
* @param options
* @returns {T} The class constructed.
*/
invoke(token, locals = new Map(), designParamTypes, requiredScope = false) {
const { onInvoke } = GlobalProviders_1.GlobalProviders.getRegistrySettings(token);
invoke(token, locals = new LocalsContainer_1.LocalsContainer(), options = {}) {
const provider = this.getProvider(token);
const parentScope = core_1.Store.from(token).get("scope");
if (!designParamTypes) {
designParamTypes = core_1.Metadata.getParamTypes(token);
let instance;
if (locals.has(token)) {
instance = locals.get(token);
}
if (provider && onInvoke) {
onInvoke(provider, locals, designParamTypes);
else if (!provider || options.rebuild) {
instance = /* await */ this._invoke(token, locals, options);
}
const services = designParamTypes.map(serviceType => this.mapServices({
serviceType,
target: token,
locals,
requiredScope,
parentScope
}));
const instance = new token(...services);
this.bindInjectableProperties(instance);
else {
switch (this.scopeOf(provider)) {
case interfaces_1.ProviderScope.SINGLETON:
if (!this.has(token)) {
provider.instance = /* await */ this._invoke(token, locals, options);
}
instance = this.get(token);
break;
case interfaces_1.ProviderScope.REQUEST:
instance = /* await */ this._invoke(token, locals, options);
locals.set(token, instance);
break;
case interfaces_1.ProviderScope.INSTANCE:
instance = /* await */ this._invoke(provider.provide, locals, options);
break;
}
}
return instance;

@@ -171,12 +161,26 @@ }

*/
load() {
load(container = GlobalProviders_1.GlobalProviders) {
const _super = Object.create(null, {
toArray: { get: () => super.toArray }
});
return tslib_1.__awaiter(this, void 0, void 0, function* () {
// TODO copy all provider from GlobalProvider registry. In future this action will be performed from Bootstrap class
GlobalProviders_1.GlobalProviders.forEach((p, k) => {
if (!this.has(k)) {
this.set(k, p.clone());
const locals = new LocalsContainer_1.LocalsContainer();
// Clone all providers in the container
container.forEach((provider, token) => {
if (!this.hasProvider(token)) {
this.setProvider(token, provider.clone());
}
});
this.build();
return Promise.all([this.emit("$onInit")]);
const providers = _super.toArray.call(this);
for (const provider of providers) {
if (!locals.has(provider.provide) && this.scopeOf(provider) === interfaces_1.ProviderScope.SINGLETON) {
/* await */
this.invoke(provider.provide, locals);
}
if (provider.instance) {
locals.set(provider.provide, provider.instance);
}
}
yield locals.emit("$onInit");
return locals;
});

@@ -316,139 +320,88 @@ }

*
* ```typescript
* import {InjectorService} from "@tsed/common";
*
* class MyService {
* constructor(injectorService: InjectorService) {
* injectorService.invokeMethod(this.method, {
* this,
* methodName: 'method'
* });
* }
*
* method(otherService: OtherService) {}
* }
* ```
*
* @returns {any}
* @param handler The injectable method to invoke. Method parameters are injected according method signature.
* @param options Object to configure the invocation.
* @deprecated
* @param target
* @param locals
* @param options
* @private
*/
invokeMethod(handler, options) {
let { designParamTypes } = options;
const { locals = new Map(), target, methodName } = options;
if (handler.$injected) {
return handler.call(target, locals);
_invoke(target, locals, options = {}) {
const { token, deps, construct, isBindable } = this.mapInvokeOptions(target, options);
const provider = this.getProvider(target);
if (provider) {
if (!provider.injectable && options.parent) {
throw new InjectionError_1.InjectionError(token, `${core_1.nameOf(token)} ${provider.type} is not injectable to another provider`);
}
const { onInvoke } = GlobalProviders_1.GlobalProviders.getRegistrySettings(target);
if (onInvoke) {
onInvoke(provider, locals, deps);
}
}
if (!designParamTypes) {
designParamTypes = core_1.Metadata.getParamTypes(core_1.prototypeOf(target), methodName);
}
const services = designParamTypes.map((serviceType) => this.mapServices({
serviceType,
target,
locals,
requiredScope: false,
parentScope: false
}));
return handler(...services);
}
/**
* Emit an event to all service. See service [lifecycle hooks](/docs/services.md#lifecycle-hooks).
* @param eventName The event name to emit at all services.
* @param args List of the parameters to give to each services.
* @returns {Promise<any[]>} A list of promises.
*/
emit(eventName, ...args) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
this.logger.debug("\x1B[1mCall hook", eventName, "\x1B[22m");
const providers = this.getProviders();
for (const provider of providers) {
const service = provider.instance;
if (service && eventName in service) {
const startTime = new Date().getTime();
this.logger.debug(`Call ${core_1.nameOf(provider.provide)}.${eventName}()`);
yield service[eventName](...args);
this.logger.debug(`Run ${core_1.nameOf(provider.provide)}.${eventName}() in ${new Date().getTime() - startTime} ms`);
}
let instance;
try {
const services = [];
for (const dependency of deps) {
const service = /* await */ this.invoke(dependency, locals, { parent: token });
services.push(service);
}
});
}
/**
*
* @returns {any}
* @param options
*/
mapServices(options) {
const { serviceType, target, locals, parentScope, requiredScope } = options;
const serviceName = typeof serviceType === "function" ? core_1.nameOf(serviceType) : serviceType;
const localService = locals.get(serviceName) || locals.get(serviceType);
if (localService) {
return localService;
instance = construct(services);
}
const provider = this.getProvider(serviceType);
if (!provider) {
throw new InjectionError_1.InjectionError(target, serviceName.toString());
catch (error) {
throw new InjectionError_1.InjectionError(token, error);
}
const { buildable, injectable } = GlobalProviders_1.GlobalProviders.getRegistrySettings(provider.type);
const scopeReq = provider.scope === interfaces_1.ProviderScope.REQUEST;
if (!injectable) {
throw new InjectionError_1.InjectionError(target, serviceName.toString(), "not injectable");
if (instance === undefined) {
throw new InjectionError_1.InjectionError(token, `Unable to create new instance from undefined value. Check your provider declaration for ${core_1.nameOf(token)}`);
}
if (!buildable || (provider.instance && !scopeReq)) {
return provider.instance;
if (instance && isBindable) {
this.bindInjectableProperties(instance);
}
if (scopeReq && requiredScope && !parentScope) {
throw new InjectionScopeError_1.InjectionScopeError(provider.useClass, target);
}
try {
const instance = this.invoke(provider.useClass, locals, undefined, requiredScope);
locals.set(provider.provide, instance);
return instance;
}
catch (er) {
const error = new InjectionError_1.InjectionError(target, serviceName.toString(), "injection failed");
error.origin = er;
throw error;
}
return instance;
}
/**
*
* @returns {Map<Type<any>, any>}
* Create options to invoke a provider or class.
* @param token
* @param options
*/
build() {
const locals = new Map();
this.forEach(provider => {
const token = core_1.nameOf(provider.provide);
const settings = GlobalProviders_1.GlobalProviders.getRegistrySettings(provider.type);
const useClass = core_1.nameOf(provider.useClass);
if (settings.buildable) {
const defaultScope = this.scopeOf(provider.type);
if (defaultScope && !provider.scope) {
provider.scope = defaultScope;
}
if (!locals.has(provider.provide)) {
provider.instance = this.invoke(provider.useClass, locals);
}
else if (provider.scope === interfaces_1.ProviderScope.SINGLETON) {
provider.instance = locals.get(provider.provide);
}
this.logger.debug(core_1.nameOf(provider.provide), "built", token === useClass ? "" : `from class ${useClass}`);
mapInvokeOptions(token, options) {
const { useScope = false } = options;
let deps = options.deps;
let scope = options.scope;
let construct = (deps) => new token(...deps);
let isBindable = false;
if (this.hasProvider(token)) {
const provider = this.getProvider(token);
scope = scope || this.scopeOf(provider);
deps = deps || provider.deps;
if (provider.useValue) {
construct = () => (core_1.isFunction(provider.useValue) ? provider.useValue() : provider.useValue);
}
else {
provider.scope = interfaces_1.ProviderScope.SINGLETON;
this.logger.debug(core_1.nameOf(provider.provide), "loaded");
else if (provider.useFactory) {
construct = (deps) => provider.useFactory(...deps);
}
if (provider.instance) {
locals.set(provider.provide, provider.instance);
else if (provider.useClass) {
isBindable = true;
deps = deps || core_1.Metadata.getParamTypes(provider.useClass);
construct = (deps) => new provider.useClass(...deps);
}
});
return locals;
}
else {
deps = deps || core_1.Metadata.getParamTypes(token);
}
return {
token,
scope: scope || core_1.Store.from(token).get("scope") || interfaces_1.ProviderScope.SINGLETON,
deps: deps || [],
useScope,
isBindable,
construct
};
}
}
};
InjectorService = InjectorService_1 = tslib_1.__decorate([
injectable_1.Injectable({
scope: interfaces_1.ProviderScope.SINGLETON,
global: true
}),
tslib_1.__metadata("design:paramtypes", [])
], InjectorService);
exports.InjectorService = InjectorService;
/**
* Create the first service InjectorService
*/
ProviderRegistry_1.registerFactory(InjectorService);
//# sourceMappingURL=InjectorService.js.map
{
"name": "@tsed/di",
"version": "5.15.0",
"version": "5.16.0",
"description": "DI module for Ts.ED Framework",

@@ -11,3 +11,3 @@ "main": "lib/index.js",

"peerDependencies": {
"@tsed/core": "5.15.0"
"@tsed/core": "5.16.0"
},

@@ -28,3 +28,3 @@ "devDependencies": {},

"license": "MIT",
"gitHead": "69c4068db560b6ca7e042e847617d45c1af1920a"
"gitHead": "6aebb315554a5499718a10f10efe43faa689c705"
}

@@ -16,2 +16,113 @@ # @tsed/di

```json
{
"compilerOptions": {
"target": "es2016",
"lib": ["es2016"],
"typeRoots": [
"./node_modules/@types"
],
"module": "commonjs",
"moduleResolution": "node",
"experimentalDecorators":true,
"emitDecoratorMetadata": true,
"allowSyntheticDefaultImports": true
},
"exclude": [
"node_modules"
]
}
```
## Introduction
Basically, almost everything may be considered as a provider – service, factory, intereceptors, and so on.
All of them can inject dependencies, meaning, they can create various relationships with each other.
But in fact, a provider is nothing else than just a simple class annotated with an `@Injectable()` decorator.
## Usage
Here is a basic usage to declare an injectable service to another one:
```typescript
import {Injectable} from "@tsed/di";
import {Calendar} from "./models/calendar";
@Injectable()
export class CalendarsService {
private readonly calendars: Calendar[] = [];
create(calendar: Calendar) {
this.calendars.push(calendar);
}
findAll(): Calendar[] {
return this.calendars;
}
}
```
Here's a CalendarsService, a basic class with one property and two methods. The only new trait is that it uses the `@Injectable()` decorator.
The `@Injectable()` attaches the metadata, thereby Ts.ED knows that this class is a provider.
Now we have the service class already done, let's use it inside a controller:
```typescript
import { Controller, Post, Body, Get } from '@tsed/common';
import { CalendarsService } from './CalendarsService';
import { Calendar } from './models/Calendar';
@Controller('/calendars')
export class CalendarService {
constructor(private readonly calendarsService: CalendarsService) {}
@Post()
async create(@Body() calendar: Calendar) {
this.calendarsService.create(calendar);
}
@Get()
async findAll(): Promise<Calendar[]> {
return this.calendarsService.findAll();
}
}
```
> Note: Controller isn't a part of `@tsed/di`. `@Controller` decorator are defined by `@tsed/common` package because it's a specific provider
used by the Ts.ED framework. Ts.ED DI allow you to define your own Provider and decorator.
Finally, we can load the injector and use:
```typescript
import {InjectorService} from "@tsed/di";
import {CalendarCtrl} from "./CalendarCtrl";
async function bootstrap() {
const injector = new InjectorService()
// Load all providers registered via @Injectable decorator
await injector.load()
const calendarController = injector.get<CalendarCtrl>()
await calendarController.create(new Calendar())
// And finally destroy injector and his instances (see injector hooks)
await injector.destroy()
}
bootstrap()
```
## Custom providers
To organize your code Ts.ED DI provide different kind of providers:
- Provider can be declared with `@Injectable`,
- Service can be declared with `@Service`,
- Interceptor can be declared with `@Interceptor`,
- Factory and Value can be declared with `registerFactory` and `registerValue`.
See more details on our documentation https://tsed.io/providers.html
## Contributors

@@ -18,0 +129,0 @@ Please read [contributing guidelines here](https://tsed.io/CONTRIBUTING.html)

@@ -1,16 +0,61 @@

import {getClass, getClassOrSymbol, nameOf, NotEnumerable, RegistryKey, Store, Type} from "@tsed/core";
import {Enumerable, getClass, getKeys, isClass, nameOf, NotEnumerable, Store, Type} from "@tsed/core";
import {ProviderScope} from "../interfaces";
import {IProvider} from "../interfaces/IProvider";
import {ProviderType} from "../interfaces/ProviderType";
import {TokenProvider} from "../interfaces/TokenProvider";
export class Provider<T> implements IProvider<T> {
/**
*
*/
@Enumerable()
public type: ProviderType | any = ProviderType.PROVIDER;
/**
*
*/
@Enumerable()
public injectable: boolean = true;
/**
*
*/
@Enumerable()
public instance: T;
/**
*
*/
@Enumerable()
public deps: any[];
/**
*
*/
@Enumerable()
public useFactory: Function;
/**
*
*/
@Enumerable()
public useValue: any;
/**
*
*/
@NotEnumerable()
protected _provide: TokenProvider;
/**
*
*/
@NotEnumerable()
protected _useClass: Type<T>;
/**
*
*/
@NotEnumerable()
protected _instance: T;
/**
*
*/
@NotEnumerable()
protected _type: ProviderType | any = ProviderType.PROVIDER;
protected _provide: RegistryKey;
protected _scope: ProviderScope;
/**
*
*/
@NotEnumerable()

@@ -21,6 +66,5 @@ private _store: Store;

constructor(provide: RegistryKey) {
this._provide = getClassOrSymbol(provide);
this._useClass = getClass(this._provide);
this._store = Store.from(this._provide);
constructor(token: TokenProvider) {
this.provide = token;
this.useClass = token;
}

@@ -41,3 +85,3 @@

set provide(value: any) {
this._provide = value;
this._provide = isClass(value) ? getClass(value) : value;
}

@@ -54,8 +98,11 @@

/**
*
* Create a new store if the given value is a class. Otherwise the value is ignored.
* @param value
*/
@Enumerable()
set useClass(value: Type<T>) {
this._store = Store.from(value);
this._useClass = value;
if (isClass(value)) {
this._useClass = getClass(value);
this._store = Store.from(value);
}
}

@@ -65,6 +112,6 @@

*
* @returns {T}
* @returns {string}
*/
get instance(): T {
return this._instance;
get className() {
return this.name;
}

@@ -74,29 +121,4 @@

*
* @param value
*/
set instance(value: T) {
this._instance = value;
}
/**
*
* @returns {any}
*/
get type(): any {
return this._type;
}
/**
*
* @param value
*/
set type(value: any) {
this._type = value;
}
/**
*
* @returns {string}
*/
get className() {
get name() {
return nameOf(this.provide);

@@ -118,3 +140,3 @@ }

get scope(): ProviderScope {
return this.store.get("scope");
return this._store ? this.store.get("scope") : this._scope;
}

@@ -126,14 +148,23 @@

*/
@Enumerable()
set scope(scope: ProviderScope) {
this.store.set("scope", scope);
this._store ? this.store.set("scope", scope) : this._scope;
}
/**
*
*/
clone(): Provider<any> {
const provider = new Provider(this._provide);
provider._type = this._type;
provider.useClass = this._useClass;
provider._instance = this._instance;
const provider = new (getClass(this))(this.provide);
getKeys(this).forEach(key => {
provider[key] = this[key];
});
return provider;
}
toString() {
return `Token:${this.name}`;
}
}
import {Store} from "@tsed/core";
import {IInjectableProperties} from "../interfaces/IInjectableProperties";
import {InjectablePropertyType} from "../interfaces/InjectablePropertyType";

@@ -43,3 +44,3 @@ /**

[propertyKey]: {
bindingType: "constant",
bindingType: InjectablePropertyType.CONSTANT,
propertyKey,

@@ -46,0 +47,0 @@ expression,

@@ -1,18 +0,28 @@

import {IProvider} from "../interfaces";
import {Type} from "@tsed/core";
import {IProvider, ProviderScope} from "../interfaces";
import {registerProvider} from "../registries/ProviderRegistry";
import {Type} from "@tsed/core";
/**
* The decorators `@Injectable()` declare a new service can be injected in other service or controller on there `constructor`.
* All services annotated with `@Injectable()` are constructed one time.
* The decorators `@Injectable()` declare a new service can be injected in other service, controller, interceptor, etc.. on there `constructor`.
* All classes annotated with `@Injectable()` are built one time, excepted if you change the default provider configuration.
*
* > `@Service()` use the `reflect-metadata` to collect and inject service on controllers or other services.
* <<< @/docs/docs/snippets/providers/getting-started-injectable.ts
*
* ::: tip
* `@Injectable()` use the `reflect-metadata` to collect and inject the built provided to other services.
* :::
*
* ### Options
*
* - type (@@ProviderType@@ or `string`): Kind of provider. (Default: `ProviderType.PROVIDER`)
* - scope (@@ProviderScope@): Kind of provider. (Default: `ProviderScope.SINGLETON`)
* - deps (`Type<any>`): List of class or provider which will be injected to the constructor (Note: This options override default metadata generated by Typescript).
*
* @returns {Function}
* @decorator
*/
export function Injectable(provider: Partial<IProvider<any>> = {}): Function {
export function Injectable(options: Partial<IProvider<any>> = {scope: ProviderScope.SINGLETON}): Function {
return (provide: Type<any>) => {
registerProvider({
...provider,
...options,
provide

@@ -19,0 +29,0 @@ });

import {Type} from "@tsed/core";
import {ProviderRegistry} from "../registries/ProviderRegistry";
import {GlobalProviders} from "../registries/GlobalProviders";

@@ -12,4 +12,4 @@ /**

return (target: Type<any>): void => {
ProviderRegistry.get(originalProvider)!.useClass = target;
GlobalProviders.get(originalProvider)!.useClass = target;
};
}

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

import {Store} from "@tsed/core";
import {StoreSet} from "@tsed/core";
import {ProviderScope} from "../interfaces/ProviderScope";

@@ -11,5 +11,3 @@

export function Scope(scope: "request" | "singleton" | ProviderScope = ProviderScope.REQUEST) {
return Store.decorate(store => {
store.set("scope", scope);
});
return StoreSet("scope", scope);
}
import {Store} from "@tsed/core";
import {IInjectableProperties} from "../interfaces/IInjectableProperties";
import {InjectablePropertyType} from "../interfaces/InjectablePropertyType";

@@ -43,3 +44,3 @@ /**

[propertyKey]: {
bindingType: "value",
bindingType: InjectablePropertyType.VALUE,
propertyKey,

@@ -46,0 +47,0 @@ expression,

@@ -1,12 +0,31 @@

import {nameOf, Type} from "@tsed/core";
import {isString, nameOf} from "@tsed/core";
import {TokenProvider} from "../interfaces";
/**
* @private
*/
export class InjectionError extends Error {
name = "INJECTION_ERROR";
constructor(target: Type<any>, serviceName: string, message = "not found") {
super(`Service ${nameOf(target)} > ${serviceName} ${message}.`);
public tokens: TokenProvider[] = [];
public origin: any;
constructor(token: TokenProvider, origin?: any) {
super(isString(origin) ? origin : "");
this.tokens = [token];
if (origin) {
if (isString(origin)) {
this.origin = {
message: origin,
stack: this.stack
};
} else {
if (origin.tokens) {
this.tokens = this.tokens.concat(origin.tokens);
this.origin = origin.origin;
}
}
}
this.message = "Injection failed on " + this.tokens.map(token => nameOf(token)).join(" > ") + "\nOrigin: " + this.origin.message;
}
}
export * from "./class/Provider";
export * from "./class/ProviderStorable";
export * from "./class/Container";
export * from "./class/LocalsContainer";
export * from "./interfaces";

@@ -4,0 +6,0 @@ export * from "./decorators/scope";

@@ -1,12 +0,15 @@

export * from "./IInjectableMethod";
export * from "./IDILogger";
export * from "./IDISettings";
export * from "./IInjectableProperties";
export * from "./IInterceptor";
export * from "./IInterceptorContext";
export * from "./IInvokeOptions";
export * from "./IProvider";
export * from "./IDILogger";
export * from "./IDISettings";
export * from "./InjectablePropertyType";
export * from "./OnInit";
export * from "./OnDestroy";
export * from "./InjectablePropertyType";
export * from "./OnInit";
export * from "./ProviderScope";
export * from "./ProviderScope";
export * from "./ProviderType";

@@ -13,0 +16,0 @@ export * from "./RegistrySettings";

import {Type} from "@tsed/core";
import {ProviderScope} from "./ProviderScope";
import {ProviderType} from "./ProviderType";

@@ -13,17 +14,30 @@ import {TokenProvider} from "./TokenProvider";

provide: TokenProvider;
/**
* Provider type
*/
type?: ProviderType | string;
/**
* Instance build by the injector
*/
instance?: T;
/**
* Define dependencies to build the provider
*/
deps?: TokenProvider[];
/**
* Class to instantiate for the `token`.
*/
useClass?: Type<T>;
/**
*
* Provide a function to build the provider
*/
instance?: T;
useFactory?: Function;
/**
* Provider type
* Provide predefined value
*/
type: ProviderType | any;
useValue?: any;
/**
* Scope used by the injector to build the provider.
*/
scope?: ProviderScope;

@@ -30,0 +44,0 @@ /**

export enum ProviderScope {
SINGLETON = "singleton",
REQUEST = "request"
REQUEST = "request",
INSTANCE = "instance"
}
export enum ProviderType {
VALUE = "value",
FACTORY = "factory",

@@ -6,6 +7,8 @@ SERVICE = "service",

CONTROLLER = "controller",
INTERCEPTOR = "interceptor",
// NOT STANDARD
CONVERTER = "converter",
INTERCEPTOR = "interceptor",
FILTER = "filter",
MIDDLEWARE = "middleware"
}

@@ -8,11 +8,10 @@ import {Registry} from "@tsed/core";

injectable?: boolean;
buildable: boolean;
/**
*
* @param target
* @param provider
* @param {Map<string | Function, any>} locals
* @param {any[]} designParamTypes
* @param deps
*/
onInvoke?(target: Provider<any>, locals: Map<string | Function, any>, designParamTypes: any[]): void;
onInvoke?(provider: Provider<any>, locals: Map<string | Function, any>, deps: any[]): void;
}

@@ -23,3 +23,7 @@ import {Registry, Type} from "@tsed/core";

*/
createRegistry(type: string, model: Type<Provider<any>>, options: Partial<RegistrySettings>): TypedProvidersRegistry {
createRegistry(
type: string,
model: Type<Provider<any>>,
options: Partial<RegistrySettings> = {injectable: true}
): TypedProvidersRegistry {
const registry = new Registry<Provider<any>, IProvider<any>>(model, {

@@ -34,4 +38,3 @@ onCreate: this.set.bind(this)

registry,
injectable: true,
buildable: true
injectable: true
},

@@ -68,4 +71,3 @@ options

registry: this,
injectable: true,
buildable: true
injectable: true
};

@@ -72,0 +74,0 @@ }

import {Provider} from "../class/Provider";
import {IProvider, ProviderType, TypedProvidersRegistry} from "../interfaces";
import {IProvider, ProviderScope, ProviderType} from "../interfaces";
import {GlobalProviders} from "./GlobalProviders";

@@ -10,4 +10,3 @@

// tslint:disable-next-line: variable-name
export const ProviderRegistry: TypedProvidersRegistry = GlobalProviders.getRegistry(ProviderType.PROVIDER);
GlobalProviders.getRegistry(ProviderType.PROVIDER);
/**

@@ -17,6 +16,3 @@ *

*/
GlobalProviders.createRegistry(ProviderType.SERVICE, Provider, {
injectable: true,
buildable: true
});
GlobalProviders.createRegistry(ProviderType.SERVICE, Provider);
/**

@@ -26,6 +22,4 @@ *`

*/
GlobalProviders.createRegistry(ProviderType.FACTORY, Provider, {
injectable: true,
buildable: false
});
GlobalProviders.createRegistry(ProviderType.FACTORY, Provider);
/**

@@ -35,6 +29,3 @@ *

*/
GlobalProviders.createRegistry(ProviderType.INTERCEPTOR, Provider, {
injectable: true,
buildable: true
});
GlobalProviders.createRegistry(ProviderType.INTERCEPTOR, Provider);
/**

@@ -44,4 +35,3 @@ *

GlobalProviders.createRegistry(ProviderType.CONTROLLER, Provider, {
injectable: false,
buildable: true
injectable: false
});

@@ -124,4 +114,62 @@

*/
export const registerFactory = GlobalProviders.createRegisterFn(ProviderType.FACTORY);
export const registerFactory = (provider: any | IProvider<any>, instance?: any): void => {
if (!provider.provide) {
provider = {
provide: provider
};
}
provider = Object.assign(
{
scope: ProviderScope.SINGLETON,
useFactory() {
return instance;
}
},
provider,
{type: ProviderType.FACTORY}
);
GlobalProviders.getRegistry(ProviderType.FACTORY).merge(provider.provide, provider);
};
/**
* Add a new value in the `ProviderRegistry`.
*
* #### Example with symbol definition
*
*
* ```typescript
* import {registerValue, InjectorService} from "@tsed/common";
*
* const MyValue = Symbol.from("MyValue")
*
* registerValue(MyValue, "myValue");
*
* @Service()
* export class OtherService {
* constructor(@Inject(MyValue) myValue: string){
* console.log(myValue); /// "myValue"
* }
* }
* ```
*/
export const registerValue = (provider: any | IProvider<any>, value?: any): void => {
if (!provider.provide) {
provider = {
provide: provider
};
}
provider = Object.assign(
{
scope: ProviderScope.SINGLETON,
useValue: value
},
provider,
{type: ProviderType.VALUE}
);
GlobalProviders.getRegistry(ProviderType.VALUE).merge(provider.provide, provider);
};
/**
* Add a new service in the `ProviderRegistry`. This service will be built when `InjectorService` will be loaded.

@@ -128,0 +176,0 @@ *

@@ -1,9 +0,10 @@

import {deepClone, getClass, getClassOrSymbol, Metadata, nameOf, prototypeOf, RegistryKey, Store, Type} from "@tsed/core";
import {deepClone, getClass, getClassOrSymbol, isFunction, Metadata, nameOf, prototypeOf, Store} from "@tsed/core";
import {Container} from "../class/Container";
import {LocalsContainer} from "../class/LocalsContainer";
import {Provider} from "../class/Provider";
import {Injectable} from "../decorators/injectable";
import {InjectionError} from "../errors/InjectionError";
import {InjectionScopeError} from "../errors/InjectionScopeError";
import {
IDILogger,
IDISettings,
IInjectableMethod,
IInjectableProperties,

@@ -14,10 +15,20 @@ IInjectablePropertyService,

IInterceptorContext,
IInvokeOptions,
InjectablePropertyType,
ProviderScope,
ProviderType,
TokenProvider
} from "../interfaces";
import {GlobalProviders} from "../registries/GlobalProviders";
import {registerFactory} from "../registries/ProviderRegistry";
interface IInvokeSettings {
token: TokenProvider;
parent?: TokenProvider;
scope: ProviderScope;
useScope: boolean;
isBindable: boolean;
deps: any[];
construct(deps: TokenProvider[]): any;
}
/**

@@ -46,3 +57,7 @@ * This service contain all services collected by `@Service` or services declared manually with `InjectorService.factory()` or `InjectorService.service()`.

*/
export class InjectorService extends Map<TokenProvider, Provider<any>> {
@Injectable({
scope: ProviderScope.SINGLETON,
global: true
})
export class InjectorService extends Container {
public settings: IDISettings = new Map();

@@ -54,16 +69,12 @@ public logger: IDILogger = console;

super();
this.forkProvider(InjectorService, this);
const provider = this.addProvider(InjectorService).getProvider(InjectorService)!;
provider.instance = this;
}
scopeOf(providerType: ProviderType) {
return this.scopes[providerType] || ProviderScope.SINGLETON;
}
/**
* The getProvider() method returns a specified element from a Map object.
* @returns {T} Returns the element associated with the specified key or undefined if the key can't be found in the Map object.
* @param token
* Retrieve default scope for a given provider.
* @param provider
*/
public getProvider(token: TokenProvider): Provider<any> | undefined {
return super.get(getClassOrSymbol(token));
public scopeOf(provider: Provider<any>) {
return provider.scope || this.scopes[provider.type] || ProviderScope.SINGLETON;
}

@@ -76,6 +87,9 @@

*/
public forkProvider(token: TokenProvider, instance?: any): Provider<any> {
const provider = GlobalProviders.get(token)!.clone();
this.set(token, provider);
public /* async */ forkProvider(token: TokenProvider, instance?: any): Provider<any> {
const provider = this.addProvider(token).getProvider(token)!;
if (!instance) {
instance = /* await */ this.invoke(token);
}
provider.instance = instance;

@@ -87,17 +101,6 @@

/**
*
* @param {ProviderType} type
* @returns {[RegistryKey , Provider<any>][]}
*/
getProviders(type?: ProviderType | string): Provider<any>[] {
return Array.from(this)
.filter(([key, provider]) => (type ? provider.type === type : true))
.map(([key, provider]) => provider);
}
/**
* Return a list of instance build by the injector.
*/
public toArray(): any[] {
return Array.from(this.values()).map(provider => provider.instance);
return super.toArray().map(provider => provider.instance);
}

@@ -130,3 +133,2 @@

* The has() method returns a boolean indicating whether an element with the specified key exists or not.
* @param key
* @returns {boolean}

@@ -139,7 +141,2 @@ * @param token

async destroy() {
await this.emit("$onDestroy");
this.clear();
}
/**

@@ -163,38 +160,38 @@ * Invoke the class and inject all services that required by the class constructor.

* @param locals Optional object. If preset then any argument Class are read from this object first, before the `InjectorService` is consulted.
* @param designParamTypes Optional object. List of injectable types.
* @param requiredScope
* @param options
* @returns {T} The class constructed.
*/
invoke<T>(
public /* async */ invoke<T>(
token: TokenProvider,
locals: Map<string | Function, any> = new Map(),
designParamTypes?: any[],
requiredScope: boolean = false
locals: Map<TokenProvider, any> = new LocalsContainer(),
options: Partial<IInvokeOptions<T>> = {}
): T {
const {onInvoke} = GlobalProviders.getRegistrySettings(token);
const provider = this.getProvider(token);
const parentScope = Store.from(token).get("scope");
let instance: any;
if (!designParamTypes) {
designParamTypes = Metadata.getParamTypes(token);
}
if (locals.has(token)) {
instance = locals.get(token);
} else if (!provider || options.rebuild) {
instance = /* await */ this._invoke(token, locals, options);
} else {
switch (this.scopeOf(provider)) {
case ProviderScope.SINGLETON:
if (!this.has(token)) {
provider.instance = /* await */ this._invoke(token, locals, options);
}
if (provider && onInvoke) {
onInvoke(provider, locals, designParamTypes);
}
instance = this.get<T>(token)!;
break;
const services = designParamTypes.map(serviceType =>
this.mapServices({
serviceType,
target: token,
locals,
requiredScope,
parentScope
})
);
case ProviderScope.REQUEST:
instance = /* await */ this._invoke(token, locals, options);
locals.set(token, instance);
break;
const instance = new token(...services);
case ProviderScope.INSTANCE:
instance = /* await */ this._invoke(provider.provide, locals, options);
break;
}
}
this.bindInjectableProperties(instance);
return instance;

@@ -208,13 +205,28 @@ }

*/
async load(): Promise<any> {
// TODO copy all provider from GlobalProvider registry. In future this action will be performed from Bootstrap class
GlobalProviders.forEach((p, k) => {
if (!this.has(k)) {
this.set(k, p.clone());
async load(container: Map<TokenProvider, Provider<any>> = GlobalProviders): Promise<LocalsContainer<any>> {
const locals = new LocalsContainer();
// Clone all providers in the container
container.forEach((provider, token) => {
if (!this.hasProvider(token)) {
this.setProvider(token, provider.clone());
}
});
this.build();
const providers = super.toArray();
return Promise.all([this.emit("$onInit")]);
for (const provider of providers) {
if (!locals.has(provider.provide) && this.scopeOf(provider) === ProviderScope.SINGLETON) {
/* await */
this.invoke(provider.provide, locals);
}
if (provider.instance) {
locals.set(provider.provide, provider.instance);
}
}
await locals.emit("$onInit");
return locals;
}

@@ -371,162 +383,94 @@

*
* ```typescript
* import {InjectorService} from "@tsed/common";
*
* class MyService {
* constructor(injectorService: InjectorService) {
* injectorService.invokeMethod(this.method, {
* this,
* methodName: 'method'
* });
* }
*
* method(otherService: OtherService) {}
* }
* ```
*
* @returns {any}
* @param handler The injectable method to invoke. Method parameters are injected according method signature.
* @param options Object to configure the invocation.
* @deprecated
* @param target
* @param locals
* @param options
* @private
*/
public invokeMethod(handler: any, options: IInjectableMethod<any>): any {
let {designParamTypes} = options;
const {locals = new Map<any, any>(), target, methodName} = options;
private /* async */ _invoke<T>(
target: TokenProvider,
locals: Map<TokenProvider, any>,
options: Partial<IInvokeOptions<T>> = {}
): Promise<T> {
const {token, deps, construct, isBindable} = this.mapInvokeOptions(target, options);
const provider = this.getProvider(target);
if (handler.$injected) {
return handler.call(target, locals);
}
if (provider) {
if (!provider.injectable && options.parent) {
throw new InjectionError(token, `${nameOf(token)} ${provider.type} is not injectable to another provider`);
}
if (!designParamTypes) {
designParamTypes = Metadata.getParamTypes(prototypeOf(target), methodName);
const {onInvoke} = GlobalProviders.getRegistrySettings(target);
if (onInvoke) {
onInvoke(provider, locals, deps);
}
}
const services = designParamTypes.map((serviceType: any) =>
this.mapServices({
serviceType,
target,
locals,
requiredScope: false,
parentScope: false
})
);
let instance: any;
return handler(...services);
}
/**
* Emit an event to all service. See service [lifecycle hooks](/docs/services.md#lifecycle-hooks).
* @param eventName The event name to emit at all services.
* @param args List of the parameters to give to each services.
* @returns {Promise<any[]>} A list of promises.
*/
public async emit(eventName: string, ...args: any[]) {
this.logger.debug("\x1B[1mCall hook", eventName, "\x1B[22m");
const providers = this.getProviders();
for (const provider of providers) {
const service = provider.instance;
if (service && eventName in service) {
const startTime = new Date().getTime();
this.logger.debug(`Call ${nameOf(provider.provide)}.${eventName}()`);
await service[eventName](...args);
this.logger.debug(`Run ${nameOf(provider.provide)}.${eventName}() in ${new Date().getTime() - startTime} ms`);
try {
const services = [];
for (const dependency of deps) {
const service = /* await */ this.invoke(dependency, locals, {parent: token});
services.push(service);
}
}
}
/**
*
* @returns {any}
* @param options
*/
private mapServices(options: any) {
const {serviceType, target, locals, parentScope, requiredScope} = options;
const serviceName = typeof serviceType === "function" ? nameOf(serviceType) : serviceType;
const localService = locals.get(serviceName) || locals.get(serviceType);
if (localService) {
return localService;
instance = construct(services);
} catch (error) {
throw new InjectionError(token, error);
}
const provider = this.getProvider(serviceType);
if (!provider) {
throw new InjectionError(target, serviceName.toString());
if (instance === undefined) {
throw new InjectionError(
token,
`Unable to create new instance from undefined value. Check your provider declaration for ${nameOf(token)}`
);
}
const {buildable, injectable} = GlobalProviders.getRegistrySettings(provider.type);
const scopeReq = provider.scope === ProviderScope.REQUEST;
if (!injectable) {
throw new InjectionError(target, serviceName.toString(), "not injectable");
if (instance && isBindable) {
this.bindInjectableProperties(instance);
}
if (!buildable || (provider.instance && !scopeReq)) {
return provider.instance;
}
if (scopeReq && requiredScope && !parentScope) {
throw new InjectionScopeError(provider.useClass, target);
}
try {
const instance = this.invoke<any>(provider.useClass, locals, undefined, requiredScope);
locals.set(provider.provide, instance);
return instance;
} catch (er) {
const error = new InjectionError(target, serviceName.toString(), "injection failed");
(error as any).origin = er;
throw error;
}
return instance;
}
/**
*
* @returns {Map<Type<any>, any>}
* Create options to invoke a provider or class.
* @param token
* @param options
*/
private build(): Map<Type<any>, any> {
const locals: Map<Type<any>, any> = new Map();
private mapInvokeOptions(token: TokenProvider, options: Partial<IInvokeOptions<any>>): IInvokeSettings {
const {useScope = false} = options;
let deps: TokenProvider[] | undefined = options.deps;
let scope = options.scope;
let construct = (deps: TokenProvider[]) => new token(...deps);
let isBindable = false;
this.forEach(provider => {
const token = nameOf(provider.provide);
const settings = GlobalProviders.getRegistrySettings(provider.type);
const useClass = nameOf(provider.useClass);
if (this.hasProvider(token)) {
const provider = this.getProvider(token)!;
if (settings.buildable) {
const defaultScope: ProviderScope = this.scopeOf(provider.type);
scope = scope || this.scopeOf(provider);
deps = deps || provider.deps;
if (defaultScope && !provider.scope) {
provider.scope = defaultScope;
}
if (!locals.has(provider.provide)) {
provider.instance = this.invoke(provider.useClass, locals);
} else if (provider.scope === ProviderScope.SINGLETON) {
provider.instance = locals.get(provider.provide);
}
this.logger.debug(nameOf(provider.provide), "built", token === useClass ? "" : `from class ${useClass}`);
} else {
provider.scope = ProviderScope.SINGLETON;
this.logger.debug(nameOf(provider.provide), "loaded");
if (provider.useValue) {
construct = () => (isFunction(provider.useValue) ? provider.useValue() : provider.useValue);
} else if (provider.useFactory) {
construct = (deps: TokenProvider[]) => provider.useFactory(...deps);
} else if (provider.useClass) {
isBindable = true;
deps = deps || Metadata.getParamTypes(provider.useClass);
construct = (deps: TokenProvider[]) => new provider.useClass(...deps);
}
} else {
deps = deps || Metadata.getParamTypes(token);
}
if (provider.instance) {
locals.set(provider.provide, provider.instance);
}
});
return locals;
return {
token,
scope: scope || Store.from(token).get("scope") || ProviderScope.SINGLETON,
deps: deps! || [],
useScope,
isBindable,
construct
};
}
}
/**
* Create the first service InjectorService
*/
registerFactory(InjectorService);
import {expect} from "chai";
import {getKeys} from "../../../core/src/utils";
import {Provider} from "../../src/class/Provider";
import {ProviderScope} from "../../src/interfaces";
class T1 {}
class T1 {
}
class T2 {}
const S1 = Symbol.for("S1");
const S2 = Symbol.for("S2");
describe("Provider", () => {
describe("className", () => {
it("should return the class name", () => {
expect(new Provider(T1).className).to.eq("T1");
});
describe("when is a class", () => {
it("should wrap the token provided", () => {
const provider = new Provider(T1);
provider.scope = ProviderScope.REQUEST;
provider.customProp = "test";
it("should return the symbol name", () => {
expect(new Provider(S1).className).to.eq("S1");
expect(provider.provide).to.eq(T1);
expect(provider.useClass).to.eq(T1);
expect(!!provider.store).to.eq(true);
expect(getKeys(provider)).to.deep.eq([
"type",
"injectable",
"customProp",
"useClass",
"scope",
"instance",
"deps",
"useFactory",
"useValue"
]);
expect(provider.clone()).to.deep.eq(provider);
});
});
describe("provide", () => {
it("should equal to the class provided", () => {
expect(new Provider(T2).provide).to.equal(T2);
describe("when is a symbol", () => {
it("should wrap the token provided", () => {
const provider = new Provider(S1);
expect(provider.provide).to.eq(S1);
expect(!!provider.useClass).to.eq(false);
expect(!!provider.store).to.eq(false);
});
it("should equal to the symbol provided", () => {
expect(new Provider(S2).provide).to.equal(S2);
it("should should return scope", () => {
const provider = new Provider(T1);
});
});
describe("useClass", () => {
it("should not equal to the class provided", () => {
expect(new Provider(class {}).provide).not.to.equal(T2);
describe("when is a string", () => {
it("should wrap the token provided", () => {
const provider = new Provider("test");
expect(provider.provide).to.eq("test");
expect(!!provider.useClass).to.eq(false);
expect(!!provider.store).to.eq(false);
});
});
describe("instance", () => {
it("should have an instance", () => {
it("should should return scope", () => {
const provider = new Provider(T1);
provider.useClass = T2;
provider.instance = new provider.useClass();
expect(provider.instance).instanceOf(T2);
});
});
describe("type", () => {
it("should set a type of provider", () => {
describe("clone()", () => {
it("should clone a provider", () => {
const provider = new Provider(T1);
provider.type = "typeTest";
expect(provider.type).to.equal("typeTest");
provider.type = "provider";
provider.scope = ProviderScope.REQUEST;
});
});
describe("className", () => {
it("should return the class name", () => {
expect(new Provider(T1).className).to.eq("T1");
});
it("should return the symbol name", () => {
expect(new Provider(S1).className).to.eq("S1");
});
});
describe("name", () => {
it("should return the class name", () => {
expect(new Provider(T1).name).to.eq("T1");
});
});
describe("toString()", () => {
it("should return the class name", () => {
expect(new Provider(T1).toString()).to.eq("Token:T1");
});
});
});
import {Store} from "@tsed/core";
import {expect} from "chai";
import {Constant} from "../../src";
class Test {}
class Test {
}
describe("@Constant()", () => {
before(() => {
it("should store metadata", () => {
// WHEN
Constant("expression")(Test, "test");
this.store = Store.from(Test).get("injectableProperties");
});
it("should store metadata", () => {
expect(this.store).to.deep.eq({
// THEN
const store = Store.from(Test).get("injectableProperties");
store.should.deep.eq({
test: {

@@ -16,0 +17,0 @@ bindingType: "constant",

@@ -6,18 +6,22 @@ import {descriptorOf, Metadata, Store} from "@tsed/core";

class Test {
test() {}
}
describe("@Inject()", () => {
describe("used on unsupported decorator type", () => {
before(() => {
it("should store metadata", () => {
// GIVEN
class Test {
test() {
}
}
// WHEN
let actualError;
try {
Inject()(Test, "test", descriptorOf(Test, "test"));
} catch (er) {
this.error = er;
actualError = er;
}
});
it("should store metadata", () => {
expect(this.error.message).to.deep.eq("Inject cannot used as method.static at Test.test");
// THEN
expect(actualError.message).to.deep.eq("Inject cannot used as method.static at Test.test");
});

@@ -28,14 +32,23 @@ });

before(() => {
this.getTypeStub = Sinon.stub(Metadata, "getType").returns(String);
Inject()(Test.prototype, "test", descriptorOf(Test, "test"));
this.store = Store.from(Test).get("injectableProperties");
Sinon.stub(Metadata, "getType").returns(String);
});
after(() => {
this.getTypeStub.restore();
// @ts-ignore
Metadata.getType.restore();
});
it("should store metadata", () => {
expect(this.store).to.deep.eq({
// GIVEN
class Test {
test() {
}
}
// WHEN
Inject()(Test.prototype, "test", descriptorOf(Test, "test"));
// THEN
const store = Store.from(Test).get("injectableProperties");
store.should.deep.eq({
test: {

@@ -51,8 +64,18 @@ bindingType: "method",

before(() => {
Inject(String)(Test.prototype, "test");
this.store = Store.from(Test).get("injectableProperties");
});
it("should store metadata", () => {
expect(this.store).to.deep.eq({
// GIVEN
class Test {
test() {
}
}
// WHEN
Inject(String)(Test.prototype, "test");
// THEN
const store = Store.from(Test).get("injectableProperties");
store.should.deep.eq({
test: {

@@ -68,19 +91,27 @@ bindingType: "property",

describe("used on constructor/params", () => {
const sandbox = Sinon.createSandbox();
before(() => {
this.getParamTypesStub = Sinon.stub(Metadata, "getParamTypes").returns([]);
this.setParamTypesStub = Sinon.stub(Metadata, "setParamTypes");
Inject(String)(Test.prototype, undefined, 0);
sandbox.stub(Metadata, "getParamTypes");
sandbox.stub(Metadata, "setParamTypes");
});
after(() => {
this.getParamTypesStub.restore();
this.setParamTypesStub.restore();
sandbox.restore();
});
it("should call Metadata.getParamTypes()", () => {
this.getParamTypesStub.should.have.been.calledWithExactly(Test.prototype, undefined);
});
// GIVEN
class Test {
test() {
}
}
it("should call Metadata.setParamTypes()", () => {
this.setParamTypesStub.should.have.been.calledWithExactly(Test.prototype, undefined, [String]);
// @ts-ignore
Metadata.getParamTypes.returns([]);
// WHEN
Inject(String)(Test.prototype, undefined, 0);
// THEN
Metadata.getParamTypes.should.have.been.calledWithExactly(Test.prototype, undefined);
Metadata.setParamTypes.should.have.been.calledWithExactly(Test.prototype, undefined, [String]);
});

@@ -90,21 +121,29 @@ });

describe("used on method/params", () => {
const sandbox = Sinon.createSandbox();
before(() => {
this.getParamTypesStub = Sinon.stub(Metadata, "getParamTypes").returns([]);
this.setParamTypesStub = Sinon.stub(Metadata, "setParamTypes");
Inject(String)(Test.prototype, "propertyKey", 0);
sandbox.stub(Metadata, "getParamTypes");
sandbox.stub(Metadata, "setParamTypes");
});
after(() => {
this.getParamTypesStub.restore();
this.setParamTypesStub.restore();
sandbox.restore();
});
it("should call Metadata.getParamTypes()", () => {
this.getParamTypesStub.should.have.been.calledWithExactly(Test.prototype, "propertyKey");
});
// GIVEN
class Test {
test() {
}
}
it("should call Metadata.setParamTypes()", () => {
this.setParamTypesStub.should.have.been.calledWithExactly(Test.prototype, "propertyKey", [String]);
// @ts-ignore
Metadata.getParamTypes.returns([]);
// WHEN
Inject(String)(Test.prototype, "propertyKey", 0);
// THEN
Metadata.getParamTypes.should.have.been.calledWithExactly(Test.prototype, "propertyKey");
Metadata.setParamTypes.should.have.been.calledWithExactly(Test.prototype, "propertyKey", [String]);
});
});
});
import * as Sinon from "sinon";
import {Injectable} from "../../src/decorators/injectable";
import {Injectable, ProviderScope} from "../../src";
import * as ProviderRegistry from "../../src/registries/ProviderRegistry";

@@ -8,8 +8,6 @@

class Test {}
describe("with options", () => {
before(() => {
sandbox.stub(ProviderRegistry, "registerProvider");
Injectable({options: "options"})(Test);
});

@@ -22,2 +20,10 @@

it("should called registerProvider", () => {
// GIVEN
class Test {
}
// WHEN
Injectable({options: "options"})(Test);
// THEN
ProviderRegistry.registerProvider.should.have.been.calledWithExactly({

@@ -33,4 +39,2 @@ options: "options",

sandbox.stub(ProviderRegistry, "registerProvider");
Injectable()(Test);
});

@@ -43,4 +47,13 @@

it("should called registerProvider", () => {
// GIVEN
class Test {
}
// WHEN
Injectable()(Test);
// THEN
ProviderRegistry.registerProvider.should.have.been.calledWithExactly({
provide: Test
provide: Test,
scope: ProviderScope.SINGLETON
});

@@ -47,0 +60,0 @@ });

import {Store} from "@tsed/core";
import {expect} from "chai";
import {IInterceptor, IInterceptorContext, InjectablePropertyType, Intercept} from "../../src";
describe("@Intercept", () => {
class TestInterceptor implements IInterceptor {
aroundInvoke(ctx: IInterceptorContext<any>, options?: any) {
it("should store metadata", () => {
// GIVEN
class TestInterceptor implements IInterceptor {
aroundInvoke(ctx: IInterceptorContext<any>, options?: any) {
return "";
return "";
}
}
}
it("should store metadata", () => {
// WHEN

@@ -15,0 +15,0 @@ class TestService {

@@ -5,18 +5,24 @@ import {GlobalProviders, ProviderType} from "@tsed/di";

class Test {
}
describe("@Interceptor", () => {
const interceptorRegistry = GlobalProviders.getRegistry(ProviderType.INTERCEPTOR);
before(() => {
this.serviceStub = Sinon.stub(GlobalProviders.getRegistry(ProviderType.INTERCEPTOR), "merge");
Interceptor()(Test);
Sinon.stub(interceptorRegistry, "merge");
});
after(() => {
this.serviceStub.restore();
// @ts-ignore
interceptorRegistry.merge.restore();
});
it("should set metadata", () => {
this.serviceStub.should.have.been.calledWithExactly(Test, {
// GIVEN
class Test {
}
// WHEN
Interceptor()(Test);
// THEN
interceptorRegistry.merge.should.have.been.calledWithExactly(Test, {
instance: undefined,

@@ -23,0 +29,0 @@ provide: Test,

import {Store} from "@tsed/core";
import {expect} from "chai";
import {Scope} from "../../src/decorators/scope";
import {Scope} from "../../src";

@@ -10,9 +9,6 @@ class Test {

describe("when parameters is given", () => {
before(() => {
it("should set metadata", () => {
Scope("request")(Test);
this.store = Store.from(Test);
});
it("should set metadata", () => {
expect(this.store.get("scope")).to.eq("request");
Store.from(Test).get("scope").should.eq("request");
});

@@ -23,10 +19,11 @@ });

before(() => {
Scope()(Test);
this.store = Store.from(Test);
});
it("should set metadata", () => {
expect(this.store.get("scope")).to.eq("request");
Scope()(Test);
Store.from(Test).get("scope").should.eq("request");
});
});
});

@@ -1,19 +0,25 @@

import {GlobalProviders, ProviderType, Service} from "@tsed/common";
import {GlobalProviders, ProviderType, Service} from "@tsed/di";
import * as Sinon from "sinon";
class Test {}
class Test {
}
describe("Service", () => {
const serviceRegistry = GlobalProviders.getRegistry(ProviderType.SERVICE);
before(() => {
this.serviceStub = Sinon.stub(GlobalProviders.getRegistry(ProviderType.SERVICE), "merge");
Service()(Test);
Sinon.stub(serviceRegistry, "merge");
});
after(() => {
this.serviceStub.restore();
// @ts-ignore
serviceRegistry.merge.restore();
});
it("should set metadata", () => {
this.serviceStub.should.have.been.calledWithExactly(Test, {
// WHEN
Service()(Test);
// THEN
serviceRegistry.merge.should.have.been.calledWithExactly(Test, {
instance: undefined,

@@ -20,0 +26,0 @@ provide: Test,

import {Store} from "@tsed/core";
import {expect} from "chai";
import {Value} from "../../src";
class Test {}
describe("@Value()", () => {
before(() => {
it("should store metadata", () => {
// GIVEN
class Test {
}
// WHEN
Value("expression")(Test, "test");
this.store = Store.from(Test).get("injectableProperties");
});
it("should store metadata", () => {
expect(this.store).to.deep.eq({
// THEN
Store.from(Test).get("injectableProperties").should.deep.eq({
test: {

@@ -16,0 +17,0 @@ bindingType: "value",

@@ -1,20 +0,11 @@

import {expect} from "chai";
import {InjectionError} from "../../src/errors/InjectionError";
describe("InjectionError", () => {
before(() => {
this.errorInstance = new InjectionError(class Target {}, "SERVICE");
});
it("should create new instance of InjectionError", () => {
const error = new InjectionError(class Target {
}, "SERVICE");
after(() => {
delete this.errorInstance;
error.message.should.equal("Injection failed on Target\nOrigin: SERVICE");
error.name.should.equal("INJECTION_ERROR");
});
it("should have a message", () => {
expect(this.errorInstance.message).to.equal("Service Target > SERVICE not found.");
});
it("should have a name", () => {
expect(this.errorInstance.name).to.equal("INJECTION_ERROR");
});
});
import {Registry} from "@tsed/core";
import {expect} from "chai";
import * as Sinon from "sinon";
import {GlobalProviderRegistry} from "../../src";
import {Provider} from "../../src/class/Provider";
import {GlobalProviderRegistry, Provider} from "../../src";
const sandbox = Sinon.createSandbox();
describe("GlobalProviderRegistry", () => {
describe("createRegistry()", () => {
before(() => {
this.providers = new GlobalProviderRegistry();
this.setStub = Sinon.stub(this.providers._registries, "set");
this.result = this.providers.createRegistry("test", Provider, {
options: "options",
buildable: false,
it("should create registry", () => {
// GIVEN
const providers = new GlobalProviderRegistry();
// @ts-ignore
const setStub = sandbox.stub(providers._registries, "set");
// WHEN
const result = providers.createRegistry("test", Provider, {
injectable: false
});
});
it("should create registry", () => {
expect(this.result).to.be.instanceOf(Registry);
});
// THEN
result.should.be.instanceOf(Registry);
it("should call registries.set", () => {
this.setStub.should.have.been.calledWithExactly("test", {
registry: this.result,
options: "options",
injectable: false,
buildable: false
setStub.should.have.been.calledWithExactly("test", {
registry: result,
injectable: false
});

@@ -35,24 +34,21 @@ });

describe("when type is a string", () => {
before(() => {
this.providers = new GlobalProviderRegistry();
this.providersGetStub = Sinon.stub(this.providers, "get");
this.hasStub = Sinon.stub(this.providers._registries, "has").returns(true);
this.getStub = Sinon.stub(this.providers._registries, "get").returns("instance");
this.result = this.providers.getRegistrySettings("type");
});
it("should return registry settings", () => {
// GIVEN
const providers = new GlobalProviderRegistry();
Sinon.stub(providers, "get");
it("should not call providers.get", () => {
return this.providersGetStub.should.not.have.been.called;
});
// @ts-ignore
const hasStub = Sinon.stub(providers._registries, "has").returns(true);
// @ts-ignore
const getStub = Sinon.stub(providers._registries, "get").returns("instance");
it("should call registries.has", () => {
this.hasStub.should.have.been.calledWithExactly("type");
});
// WHEN
const result = providers.getRegistrySettings("type");
it("should call registries.get", () => {
this.getStub.should.have.been.calledWithExactly("type");
});
// THEN
expect(result).to.eq("instance");
getStub.should.have.been.calledWithExactly("type");
hasStub.should.have.been.calledWithExactly("type");
it("should return settings", () => {
expect(this.result).to.eq("instance");
return providers.get.should.not.have.been.called;
});

@@ -62,26 +58,28 @@ });

describe("when type is a Type", () => {
class Test {}
before(() => {
this.providers = new GlobalProviderRegistry();
this.providersGetStub = Sinon.stub(this.providers, "get").returns({type: "type"});
this.hasStub = Sinon.stub(this.providers._registries, "has").returns(true);
this.getStub = Sinon.stub(this.providers._registries, "get").returns("instance");
this.result = this.providers.getRegistrySettings(Test);
});
it("should call providers.get", () => {
return this.providersGetStub.should.have.been.calledWithExactly(Test);
});
it("should call registries.has", () => {
this.hasStub.should.have.been.calledWithExactly("type");
});
it("should return registry settings", () => {
// GIVEN
class Test {
}
it("should call registries.get", () => {
this.getStub.should.have.been.calledWithExactly("type");
});
const providers = new GlobalProviderRegistry();
Sinon.stub(providers, "get").returns({type: "type"} as Provider<any>);
it("should return settings", () => {
expect(this.result).to.eq("instance");
// @ts-ignore
const hasStub = Sinon.stub(providers._registries, "has").returns(true);
// @ts-ignore
const getStub = Sinon.stub(providers._registries, "get").returns("instance");
// WHEN
const result = providers.getRegistrySettings(Test);
// THEN
result.should.eq("instance");
hasStub.should.have.been.calledWithExactly("type");
getStub.should.have.been.calledWithExactly("type");
providers.get.should.have.been.calledWithExactly(Test);
});

@@ -91,28 +89,22 @@ });

describe("when type is a string but is unknow", () => {
before(() => {
this.providers = new GlobalProviderRegistry();
this.providersGetStub = Sinon.stub(this.providers, "get");
this.hasStub = Sinon.stub(this.providers._registries, "has").returns(false);
this.getStub = Sinon.stub(this.providers._registries, "get").returns("instance");
this.result = this.providers.getRegistrySettings("type");
});
it("should not call providers.get", () => {
return this.providersGetStub.should.not.have.been.called;
});
// GIVEN
const providers = new GlobalProviderRegistry();
Sinon.stub(providers, "get");
// @ts-ignore
const hasStub = Sinon.stub(providers._registries, "has").returns(false);
// @ts-ignore
const getStub = Sinon.stub(providers._registries, "get").returns("instance");
it("should call registries.has", () => {
this.hasStub.should.have.been.calledWithExactly("type");
});
// WHEN
const result = providers.getRegistrySettings("type");
it("should not call registries.get", () => {
return this.getStub.should.not.have.been.called;
});
// THEN
result.should.deep.eq({
registry: providers,
injectable: true
});
hasStub.should.have.been.calledWithExactly("type");
it("should return settings", () => {
expect(this.result).to.deep.eq({
registry: this.providers,
injectable: true,
buildable: true
});
return providers.get.should.not.have.been.called && getStub.should.not.have.been.called;
});

@@ -123,16 +115,19 @@ });

describe("createRegisterFn()", () => {
before(() => {
this.providers = new GlobalProviderRegistry();
this.registryStub = {
it("should create a register function", () => {
// GIVEN
const providers = new GlobalProviderRegistry();
const registryStub = {
merge: Sinon.stub()
};
this.getRegistryStub = Sinon.stub(this.providers, "getRegistry").returns(this.registryStub);
this.providers.createRegisterFn("type")("provide");
});
it("should call getRegistryStub()", () => {
this.getRegistryStub.should.have.been.calledWithExactly("type");
});
it("should merge options", () => {
this.registryStub.merge.should.have.been.calledWithExactly("provide", {
// @ts-ignore
Sinon.stub(providers, "getRegistry").returns(registryStub);
// WHEN
const fn = providers.createRegisterFn("type");
fn("provide");
// THEN
providers.getRegistry.should.have.been.calledWithExactly("type");
registryStub.merge.should.have.been.calledWithExactly("provide", {
provide: "provide",

@@ -146,16 +141,17 @@ instance: undefined,

describe("getRegistry()", () => {
before(() => {
this.providers = new GlobalProviderRegistry();
this.getRegistrySettingsStub = Sinon.stub(this.providers, "getRegistrySettings").returns({registry: "registry"});
this.result = this.providers.getRegistry("type");
});
it("should call getRegistrySettings and return the registry", () => {
// GIVEN
const providers = new GlobalProviderRegistry();
it("should call getRegistrySettings", () => {
this.getRegistrySettingsStub.should.have.been.calledWithExactly("type");
});
// @ts-ignore
Sinon.stub(providers, "getRegistrySettings").returns({registry: "registry"});
it("should return the registry", () => {
expect(this.result).to.eq("registry");
// WHEN
const result = providers.getRegistry("type");
// THEN
providers.getRegistrySettings.should.have.been.calledWithExactly("type");
result.should.eq("registry");
});
});
});
import * as Sinon from "sinon";
import {ProviderRegistry, registerProvider} from "../../src";
import {
GlobalProviders,
ProviderScope,
ProviderType,
registerFactory,
registerProvider,
registerValue
} from "../../src";
const sandbox = Sinon.createSandbox();
describe("ProviderRegistry", () => {
describe("registerProvider()", () => {
describe("when provide field is not given", () => {
before(() => {
this.mergeStub = Sinon.stub(ProviderRegistry, "merge");
this.hasStub = Sinon.stub(ProviderRegistry, "has").returns(false);
try {
registerProvider({provide: undefined});
} catch (er) {
this.error = er;
}
before(() => {
sandbox.stub(GlobalProviders, "merge");
sandbox.stub(GlobalProviders, "has").returns(false);
});
after(() => {
sandbox.restore();
});
afterEach(() => {
sandbox.resetHistory();
});
it("should throw an error when provide field is not given ", () => {
// GIVEN
// @ts-ignore
GlobalProviders.has.returns(false);
let actualError;
try {
registerProvider({provide: undefined});
} catch (er) {
actualError = er;
}
actualError.message.should.deep.eq("Provider.provide is required");
});
it("should add provider", () => {
class Test {
}
registerProvider({provide: Test});
GlobalProviders.merge.should.have.been.calledWithExactly(Test, {
provide: Test
});
});
});
describe("registerValue()", () => {
before(() => {
sandbox.stub(GlobalProviders, "merge");
sandbox.stub(GlobalProviders, "has").returns(false);
});
after(() => {
this.hasStub.restore();
this.mergeStub.restore();
after(() => {
sandbox.restore();
});
afterEach(() => {
sandbox.resetHistory();
});
it("should add provider (1)", () => {
const token = Symbol.for("CustomTokenValue");
registerValue(token, "myValue");
GlobalProviders.merge.should.have.been.calledWithExactly(token, {
provide: token,
useValue: "myValue",
scope: ProviderScope.SINGLETON,
type: ProviderType.VALUE
});
});
it("should throw an error", () => {
this.error.message.should.deep.eq("Provider.provide is required");
it("should add provider", () => {
const token = Symbol.for("CustomTokenValue");
registerValue({provide: token, useValue: "myValue", scope: ProviderScope.REQUEST});
GlobalProviders.merge.should.have.been.calledWithExactly(token, {
provide: token,
useValue: "myValue",
scope: ProviderScope.REQUEST,
type: ProviderType.VALUE
});
});
});
describe("registerFactory()", () => {
before(() => {
sandbox.stub(GlobalProviders.getRegistry(ProviderType.FACTORY), "merge");
});
describe("when a configuration is given", () => {
class Test {}
after(() => {
sandbox.restore();
});
afterEach(() => {
sandbox.resetHistory();
});
before(() => {
this.mergeStub = Sinon.stub(ProviderRegistry, "merge");
registerProvider({provide: Test});
it("should add provider (1)", () => {
const token = Symbol.for("CustomTokenFactory");
registerFactory(token, {factory: "factory"});
const factoryRegistry = GlobalProviders.getRegistry(ProviderType.FACTORY);
factoryRegistry.merge.should.have.been.calledWithExactly(token, {
provide: token,
useFactory: Sinon.match.func,
scope: ProviderScope.SINGLETON,
type: ProviderType.FACTORY
});
after(() => {
this.mergeStub.restore();
// @ts-ignore
factoryRegistry.merge.args[0][1].useFactory().should.deep.eq({factory: "factory"});
});
it("should add provider (2)", () => {
const token = Symbol.for("CustomTokenFactory");
registerFactory({
provide: token,
scope: ProviderScope.REQUEST,
useFactory() {
return {factory: "factory"};
}
});
it("should call ProviderRegistry.merge()", () => {
this.mergeStub.should.have.been.calledWithExactly(Test, {
provide: Test
});
GlobalProviders.getRegistry(ProviderType.FACTORY).merge.should.have.been.calledWithExactly(token, {
provide: token,
useFactory: Sinon.match.func,
scope: ProviderScope.REQUEST,
type: ProviderType.FACTORY
});

@@ -44,0 +140,0 @@ });

@@ -1,9 +0,7 @@

import {GlobalProviders, Inject, Provider, ProviderScope, ProviderType} from "@tsed/common";
import {Metadata, Store} from "@tsed/core";
import {inject} from "@tsed/testing";
import {Store} from "@tsed/core";
import {Inject, InjectorService, Provider, ProviderScope} from "@tsed/di";
import {expect} from "chai";
import * as Sinon from "sinon";
import {$log} from "ts-log-debug";
import {TestContext} from "../../../testing/src";
import {InjectorService} from "../../src";
import {GlobalProviders, LocalsContainer} from "../../src";
import {ProviderType} from "../../src/interfaces";

@@ -35,20 +33,2 @@ class Test {

describe("InjectorService", () => {
before(() => {
this.injector = new InjectorService();
});
describe("invoke test with Inject decorator", () => {
before(() => {
this.instance = this.injector.invoke(Test);
});
it("should bind the method", () => {
expect(this.instance.test("test")).to.be.instanceOf(InjectorService);
});
it("should bind the property", () => {
expect(this.instance.prop).to.be.instanceOf(InjectorService);
});
});
describe("has()", () => {

@@ -80,1074 +60,351 @@ it("should return true", () => {

describe("forEach()", () => {
before(() => {
this.list = [];
this.injector.forEach((item: any) => {
this.list.push(item);
});
});
it("should return the list", () => {
expect(this.list.length).to.eq(this.injector.size);
});
});
describe("forkProvider()", () => {
class Test {
}
describe("keys()", () => {
before(() => {
this.list = Array.from(this.injector.keys());
});
it("should return the list", () => {
expect(this.list).to.be.an("array");
});
});
it("should return a provider", async () => {
// GIVEN
const injector = new InjectorService();
describe("entries()", () => {
before(() => {
this.list = Array.from(this.injector.entries());
});
it("should return the list", () => {
expect(this.list[0]).to.be.an("array");
});
});
// WHEN
const provider = await injector.forkProvider(InjectorService);
describe("values()", () => {
before(() => {
this.list = Array.from(this.injector.values());
// THEN
provider.should.be.instanceof(Provider);
provider.provide.should.eq(InjectorService);
});
it("should return the list", () => {
expect(this.list).to.be.an("array");
expect(this.list[0].instance).to.be.instanceof(InjectorService);
});
});
describe("Array.from()", () => {
before(() => {
this.list = Array.from(this.injector);
});
describe("invoke()", () => {
describe("when we call invoke with rebuild options (SINGLETON)", () => {
it("should invoke the provider from container", async () => {
// GIVEN
const token = class Test {
};
it("should return a list", () => {
expect(this.list).to.be.an("array");
});
});
const provider = new Provider<any>(token);
provider.scope = ProviderScope.SINGLETON;
provider.deps = [InjectorService];
describe("getProvider()", () => {
before(
inject([InjectorService], (injector: InjectorService) => {
this.provider = injector.getProvider(InjectorService);
})
);
after(TestContext.reset);
const injector = new InjectorService();
injector.set(token, provider);
it("should return a provider", () => {
expect(this.provider).to.be.instanceOf(Provider);
});
});
await injector.load();
describe("getProviders()", () => {
describe("with type ProviderType.MIDDLEWARE", () => {
before(
inject([InjectorService], (injector: InjectorService) => {
this.providers = injector.getProviders(ProviderType.MIDDLEWARE);
this.hasOther = this.providers.find((item: any) => item.type !== ProviderType.MIDDLEWARE);
})
);
Sinon.spy(injector as any, "_invoke");
Sinon.spy(injector as any, "invoke");
Sinon.spy(injector, "get");
Sinon.spy(injector, "getProvider");
it("should return a list", () => {
expect(this.providers.length > 0).to.be.true;
});
const locals = new Map();
it("sohuld return a list", () => {
expect(this.providers[0]).to.be.instanceOf(Provider);
});
// WHEN
it("should have only provider typed as CONVERTER", () => {
expect(this.hasOther).to.be.undefined;
});
});
const result1 = await injector.invoke(token, locals);
const result2 = await injector.invoke(token, locals, {rebuild: true});
describe("without type", () => {
before(
inject([InjectorService], (injector: InjectorService) => {
this.providers = injector.getProviders();
this.hasOther = this.providers.find((item: any) => item.type === ProviderType.MIDDLEWARE);
})
);
it("sohuld return a list", () => {
expect(this.providers.length > 0).to.be.true;
});
it("should return a list", () => {
expect(this.providers[0]).to.be.instanceOf(Provider);
});
it("should have only provider typed as CONVERTER", () => {
expect(!!this.hasOther).to.be.true;
});
});
});
describe("mapServices()", () => {
describe("when serviceType is a string", () => {
before(() => {
this.injector = new InjectorService();
this.symbol = "ServiceName";
const locals = new Map();
locals.set(this.symbol, "ServiceInstanceName");
this.result = this.injector.mapServices({
serviceType: this.symbol,
locals
// THEN
result1.should.not.eq(result2);
injector.getProvider.should.have.been.calledWithExactly(token);
injector.get.should.have.been.calledWithExactly(token);
(injector as any)._invoke.should.have.been.calledWithExactly(token, locals, {rebuild: true});
(injector as any).invoke.should.have.been.calledWithExactly(InjectorService, locals, {
parent: token
});
});
it("should return the service instance from the locals map", () => {
expect(this.result).to.eq("ServiceInstanceName");
});
});
describe("when serviceType is a class from locals", () => {
before(() => {
this.injector = new InjectorService();
this.symbol = class Test {
describe("when provider is a SINGLETON", () => {
it("should invoke the provider from container", async () => {
// GIVEN
const token = class Test {
};
const locals = new Map();
locals.set(this.symbol, new this.symbol());
const provider = new Provider<any>(token);
provider.scope = ProviderScope.SINGLETON;
this.result = this.injector.mapServices({
serviceType: this.symbol,
locals
});
});
const injector = new InjectorService();
injector.set(token, provider);
it("should return the service instance from the locals map", () => {
expect(this.result).to.be.instanceOf(this.symbol);
});
});
await injector.load();
describe("when serviceType is a class from registry (unknow)", () => {
before(() => {
this.injector = new InjectorService();
Sinon.spy(injector as any, "_invoke");
Sinon.spy(injector, "get");
Sinon.spy(injector, "getProvider");
this.symbol = class Test {
};
const locals = new Map();
this.getStub = Sinon.stub(this.injector, "getProvider");
try {
this.result = this.injector.mapServices({
serviceType: this.symbol,
locals,
target: class ServiceTest {
}
});
} catch (er) {
this.error = er;
}
});
// WHEN
after(() => {
this.getStub.restore();
});
const result1 = await injector.invoke(token, locals);
const result2 = await injector.invoke(token, locals);
it("should call GlobalProviders.has", () => {
this.getStub.should.have.been.calledWithExactly(this.symbol);
});
// THEN
result1.should.eq(result2);
injector.getProvider.should.have.been.calledWithExactly(token);
injector.get.should.have.been.calledWithExactly(token);
it("should throw an error", () => {
expect(this.error.message).to.eq("Service ServiceTest > Test not found.");
return (injector as any)._invoke.should.not.have.been.called;
});
});
describe("when serviceType is a class from registry (know, buildable, instance undefined)", () => {
before(() => {
this.injector = new InjectorService();
this.symbol = class Test {
describe("when provider is a REQUEST", () => {
it("should invoke a request from local container", async () => {
// GIVEN
const token = class Test {
};
this.locals = new Map();
this.getStub = Sinon.stub(this.injector, "getProvider").returns({
instance: undefined,
useClass: "useClass",
type: "provider"
});
const provider = new Provider<any>(token);
provider.scope = ProviderScope.REQUEST;
this.getRegistrySettingsStub = Sinon.stub(GlobalProviders, "getRegistrySettings").returns({
registry: GlobalProviders,
buildable: true,
injectable: true
});
const injector = new InjectorService();
injector.set(token, provider);
this.invokeStub = Sinon.stub(this.injector, "invoke").returns("instance");
await injector.load();
this.result = this.injector.mapServices({
serviceType: this.symbol,
locals: this.locals,
requiredScope: true,
target: class ServiceTest {
}
});
});
Sinon.spy(injector as any, "_invoke");
Sinon.spy(injector, "get");
Sinon.spy(injector, "getProvider");
after(() => {
this.getStub.restore();
this.invokeStub.restore();
this.getRegistrySettingsStub.restore();
});
const locals = new Map(); // LocalContainer for the first request
const locals2 = new Map(); // LocalContainer for the second request
it("should call GlobalProviders.get", () => {
this.getStub.should.have.been.calledWithExactly(this.symbol);
});
// WHEN REQ1
const result1 = await injector.invoke(token, locals);
const result2 = await injector.invoke(token, locals);
it("should call GlobalProviders.getRegistrySettings", () => {
this.getRegistrySettingsStub.should.be.calledWithExactly("provider");
});
it("should build instance and return the service", () => {
this.invokeStub.should.have.been.calledWithExactly("useClass", this.locals, undefined, true);
});
it("should return the service instance", () => {
expect(this.result).to.deep.eq("instance");
});
});
// WHEN REQ2
const result3 = await injector.invoke(token, locals2);
describe("when serviceType is a class from registry (know, instance defined, not buildable)", () => {
before(() => {
this.injector = new InjectorService();
// THEN
result1.should.eq(result2);
result2.should.not.eq(result3);
this.symbol = class Test {
};
injector.getProvider.should.have.been.calledWithExactly(token);
(injector as any)._invoke.should.have.been.calledWithExactly(token, locals, {});
locals.get(token).should.eq(result1);
locals2.get(token).should.eq(result3);
this.locals = new Map();
this.getStub = Sinon.stub(this.injector, "getProvider").returns({
instance: {instance: "instance"},
useClass: "useClass",
type: "provider"
});
this.getRegistrySettingsStub = Sinon.stub(GlobalProviders, "getRegistrySettings").returns({
registry: GlobalProviders,
buildable: false,
injectable: true
});
this.invokeStub = Sinon.stub(this.injector, "invoke").returns("instance");
this.result = this.injector.mapServices({
serviceType: this.symbol,
locals: this.locals,
requiredScope: true,
target: class ServiceTest {
}
});
return injector.get.should.not.have.been.called;
});
after(() => {
this.getStub.restore();
this.invokeStub.restore();
this.getRegistrySettingsStub.restore();
});
it("should call GlobalProviders.get", () => {
this.getStub.should.have.been.calledWithExactly(this.symbol);
});
it("should call GlobalProviders.getRegistrySettings", () => {
this.getRegistrySettingsStub.should.be.calledWithExactly("provider");
});
it("should build instance and return the service", () => {
return this.invokeStub.should.not.have.been.called;
});
it("should return the service instance", () => {
expect(this.result).to.deep.eq({instance: "instance"});
});
});
describe("when serviceType is a class from registry (know, instance defined, buildable, SINGLETON)", () => {
before(() => {
this.injector = new InjectorService();
this.symbol = class Test {
describe("when provider is a INSTANCE", () => {
it("should invoke a new instance", async () => {
// GIVEN
const token = class Test {
};
this.locals = new Map();
this.getStub = Sinon.stub(this.injector, "getProvider").returns({
instance: {instance: "instance"},
useClass: "useClass",
type: "provider",
scope: ProviderScope.SINGLETON
});
const provider = new Provider<any>(token);
provider.scope = ProviderScope.INSTANCE;
this.getRegistrySettingsStub = Sinon.stub(GlobalProviders, "getRegistrySettings").returns({
registry: GlobalProviders,
buildable: true,
injectable: true
});
const injector = new InjectorService();
injector.set(token, provider);
this.invokeStub = Sinon.stub(this.injector, "invoke").returns("instance");
await injector.load();
this.result = this.injector.mapServices({
serviceType: this.symbol,
locals: this.locals,
requiredScope: true,
target: class ServiceTest {
}
});
});
Sinon.spy(injector as any, "_invoke");
Sinon.spy(injector, "get");
Sinon.spy(injector, "getProvider");
after(() => {
this.getStub.restore();
this.invokeStub.restore();
this.getRegistrySettingsStub.restore();
});
const locals = new Map(); // LocalContainer for the first request
it("should call GlobalProviders.get", () => {
this.getStub.should.have.been.calledWithExactly(this.symbol);
});
// WHEN REQ1
const result1 = await injector.invoke(token, locals);
const result2 = await injector.invoke(token, locals);
it("should call GlobalProviders.getRegistrySettings", () => {
this.getRegistrySettingsStub.should.be.calledWithExactly("provider");
});
it("should not build instance", () => {
return this.invokeStub.should.not.have.been.called;
});
it("should return the service instance", () => {
expect(this.result).to.deep.eq({instance: "instance"});
});
});
// THEN
result1.should.not.eq(result2);
describe("when serviceType is a class from registry (know, instance defined, buildable, REQUEST)", () => {
before(() => {
this.injector = new InjectorService();
injector.getProvider.should.have.been.calledWithExactly(token);
(injector as any)._invoke.should.have.been.calledWithExactly(token, locals, {});
locals.has(token).should.eq(false);
this.symbol = class Test {
};
this.locals = new Map();
this.getStub = Sinon.stub(this.injector, "getProvider").returns({
instance: {instance: "instance"},
useClass: "useClass",
type: "provider",
scope: ProviderScope.REQUEST
});
this.getRegistrySettingsStub = Sinon.stub(GlobalProviders, "getRegistrySettings").returns({
registry: GlobalProviders,
buildable: true,
injectable: true
});
this.invokeStub = Sinon.stub(this.injector, "invoke").returns("instance");
this.result = this.injector.mapServices({
serviceType: this.symbol,
locals: this.locals,
requiredScope: true,
parentScope: true,
target: class ServiceTest {
}
});
return injector.get.should.not.have.been.called;
});
after(() => {
this.getStub.restore();
this.invokeStub.restore();
this.getRegistrySettingsStub.restore();
});
it("should call GlobalProviders.get", () => {
this.getStub.should.have.been.calledWithExactly(this.symbol);
});
it("should call GlobalProviders.getRegistrySettings", () => {
this.getRegistrySettingsStub.should.be.calledWithExactly("provider");
});
it("should build instance and return the service", () => {
return this.invokeStub.should.have.been.calledWithExactly("useClass", this.locals, undefined, true);
});
it("should return the service instance", () => {
expect(this.result).to.deep.eq("instance");
});
});
describe("when serviceType is a class from registry (know, instance defined, buildable, SCOPE ERROR)", () => {
describe("when provider is a SINGLETON", () => {
before(() => {
this.injector = new InjectorService();
this.symbol = class Test {
};
this.locals = new Map();
this.getStub = Sinon.stub(this.injector, "getProvider").returns({
instance: {instance: "instance"},
useClass: "useClass",
type: "provider",
scope: ProviderScope.REQUEST
});
this.getRegistrySettingsStub = Sinon.stub(GlobalProviders, "getRegistrySettings").returns({
registry: GlobalProviders,
buildable: true,
injectable: true
});
this.invokeStub = Sinon.stub(this.injector, "invoke").returns("instance");
try {
this.result = this.injector.mapServices({
serviceType: this.symbol,
locals: this.locals,
requiredScope: true,
parentScope: false,
target: class ServiceTest {
}
});
} catch (er) {
this.error = er;
}
Sinon.stub(GlobalProviders, "getRegistrySettings");
});
after(() => {
this.getStub.restore();
this.invokeStub.restore();
this.getRegistrySettingsStub.restore();
// @ts-ignore
GlobalProviders.getRegistrySettings.restore();
});
it("should call GlobalProviders.get", () => {
this.getStub.should.have.been.calledWithExactly(this.symbol);
});
it("should call GlobalProviders.getRegistrySettings", () => {
this.getRegistrySettingsStub.should.be.calledWithExactly("provider");
});
it("should not build instance", () => {
return this.invokeStub.should.not.have.been.called;
});
it("should throw an error", () => {
expect(this.error.message).to.eq(
"Service of type useClass can not be injected as it is request scoped, while ServiceTest is singleton scoped"
);
});
});
describe("when serviceType is a class from registry (INJECTION ERROR)", () => {
before(() => {
this.injector = new InjectorService();
this.symbol = class Test {
it("should invoke the provider from container", async () => {
// GIVEN
const token = class Test {
};
this.locals = new Map();
this.getStub = Sinon.stub(this.injector, "getProvider").returns({
instance: {instance: "instance"},
useClass: "useClass",
type: "provider",
scope: ProviderScope.REQUEST
});
this.getRegistrySettingsStub = Sinon.stub(GlobalProviders, "getRegistrySettings").returns({
registry: GlobalProviders,
buildable: true,
injectable: true
});
this.invokeStub = Sinon.stub(this.injector, "invoke").throws(new Error("Origin Error"));
try {
this.result = this.injector.mapServices({
serviceType: this.symbol,
locals: this.locals,
requiredScope: true,
parentScope: true,
target: class ServiceTest {
}
});
} catch (er) {
this.error = er;
}
});
after(() => {
this.getStub.restore();
this.invokeStub.restore();
this.getRegistrySettingsStub.restore();
});
it("should call GlobalProviders.get", () => {
this.getStub.should.have.been.calledWithExactly(this.symbol);
});
it("should call GlobalProviders.getRegistrySettings", () => {
this.getRegistrySettingsStub.should.be.calledWithExactly("provider");
});
it("should build instance and return the service", () => {
return this.invokeStub.should.have.been.calledWithExactly("useClass", this.locals, undefined, true);
});
it("should throw an error", () => {
expect(this.error.message).to.deep.eq("Service ServiceTest > Test injection failed.");
});
it("should throw an error with origin error", () => {
expect(this.error.origin.message).to.deep.eq("Origin Error");
});
});
describe("when serviceType is a class from registry (NOT INJECTABLE)", () => {
before(() => {
this.injector = new InjectorService();
this.symbol = class Test {
const registry = {
onInvoke: Sinon.stub()
};
this.locals = new Map();
this.getStub = Sinon.stub(this.injector, "getProvider").returns({
instance: {instance: "instance"},
useClass: "useClass",
type: "provider",
scope: ProviderScope.REQUEST
});
// @ts-ignore
GlobalProviders.getRegistrySettings.returns(registry);
this.getRegistrySettingsStub = Sinon.stub(GlobalProviders, "getRegistrySettings").returns({
registry: GlobalProviders,
buildable: true,
injectable: false
});
const provider = new Provider<any>(token);
provider.scope = ProviderScope.SINGLETON;
this.invokeStub = Sinon.stub(this.injector, "invoke");
const injector = new InjectorService();
injector.set(token, provider);
try {
this.result = this.injector.mapServices({
serviceType: this.symbol,
locals: this.locals,
requiredScope: true,
parentScope: true,
target: class ServiceTest {
}
});
} catch (er) {
this.error = er;
}
});
// WHEN
const result = await injector.invoke(token);
after(() => {
this.getStub.restore();
this.invokeStub.restore();
this.getRegistrySettingsStub.restore();
// THEN
result.should.instanceof(token);
registry.onInvoke.should.have.been.calledWithExactly(provider, Sinon.match.instanceOf(LocalsContainer), []);
});
it("should call GlobalProviders.get", () => {
this.getStub.should.have.been.calledWithExactly(this.symbol);
});
it("should call GlobalProviders.getRegistrySettings", () => {
this.getRegistrySettingsStub.should.be.calledWithExactly("provider");
});
it("should not build service", () => {
return this.invokeStub.should.not.have.been.called;
});
it("should throw an error", () => {
expect(this.error.message).to.deep.eq("Service ServiceTest > Test not injectable.");
});
});
});
describe("build()", () => {
class Test {
}
describe("when provider is a Value (useValue)", () => {
it("should invoke the provider from container (1)", async () => {
// GIVEN
const token = Symbol.for("TokenValue");
describe("when the provider is buildable", () => {
before(() => {
this.injector = new InjectorService();
this.injector.logger = $log;
const provider = new Provider<any>(token);
provider.scope = ProviderScope.SINGLETON;
provider.useValue = "TEST";
this.injector.scopes = {
[ProviderType.CONTROLLER]: ProviderScope.REQUEST
};
this.provider = new Provider(Test);
this.provider.type = "controller";
const injector = new InjectorService();
injector.set(token, provider);
this.injector.set(Test, this.provider);
await injector.load();
this.getRegistrySettingsStub = Sinon.stub(GlobalProviders, "getRegistrySettings").returns({
registry: GlobalProviders,
buildable: true
});
// WHEN
const result = await injector.invoke(token);
this.invokeStub = Sinon.stub(this.injector, "invoke").returns(new Test());
this.locals = this.injector.build();
// THEN
result.should.eq("TEST");
});
after(() => {
this.getRegistrySettingsStub.restore();
this.invokeStub.restore();
});
it("should call GlobalProviders.getRegistrySettings()", () => {
this.getRegistrySettingsStub.should.have.been.calledWithExactly("controller");
});
it("should invoke the provider from container (2)", async () => {
// GIVEN
const token = Symbol.for("TokenValue");
it("should call InjectorService.invoke()", () => {
this.invokeStub.should.have.been.calledWithExactly(Test, Sinon.match.instanceOf(Map));
});
const provider = new Provider<any>(token);
provider.scope = ProviderScope.SINGLETON;
provider.useValue = () => "TEST";
it("should create an instance", () => {
expect(this.provider.instance).to.be.instanceOf(Test);
});
const injector = new InjectorService();
injector.set(token, provider);
it("should set the default scope", () => {
expect(this.provider.scope).to.eq(ProviderScope.REQUEST);
});
await injector.load();
it("should store the instance in locals map", () => {
expect(this.locals.get(Test)).to.be.instanceOf(Test);
});
});
describe("when the provider is not buildable", () => {
before(() => {
this.injector = new InjectorService();
this.injector.logger = $log;
this.provider = new Provider(Test);
this.provider.type = "factory";
this.provider.instance = new Test();
// WHEN
const result = await injector.invoke(token);
this.injector.set(Test, this.provider);
this.getRegistrySettingsStub = Sinon.stub(GlobalProviders, "getRegistrySettings").returns({
registry: GlobalProviders,
buildable: false
});
this.invokeStub = Sinon.stub(this.injector, "invoke");
this.locals = this.injector.build();
// THEN
result.should.eq("TEST");
});
after(() => {
this.getRegistrySettingsStub.restore();
this.invokeStub.restore();
});
it("should call GlobalProviders.getRegistrySettings()", () => {
this.getRegistrySettingsStub.should.have.been.calledWithExactly("factory");
});
it("should call InjectorService.invoke()", () => {
return this.invokeStub.should.not.have.been.called;
});
it("should create an instance", () => {
expect(this.provider.instance).to.be.instanceOf(Test);
});
it("should set the default scope", () => {
expect(this.provider.scope).to.eq(ProviderScope.SINGLETON);
});
it("should store the instance in locals map", () => {
expect(this.locals.get(Test)).to.be.instanceOf(Test);
});
});
});
describe("when provider is a Factory (useFactory)", () => {
it("should invoke the provider from container", async () => {
// GIVEN
const token = Symbol.for("TokenFactory");
describe("invoke()", () => {
class Test {
args: any[];
const provider = new Provider<any>(token);
provider.scope = ProviderScope.SINGLETON;
provider.useFactory = () => ({factory: "factory"});
constructor(...args: any[]) {
this.args = args;
}
}
const injector = new InjectorService();
injector.set(token, provider);
class TestDep {
}
await injector.load();
describe("when designParamsTypes is not given", () => {
before(
inject([InjectorService], (injectorService: InjectorService) => {
this.provider = new Provider(Test);
this.registrySettings = {
onInvoke: Sinon.stub()
};
// WHEN
const result = await injector.invoke(token);
this.designParamTypes = [TestDep];
this.getRegistrySettingsStub = Sinon.stub(GlobalProviders, "getRegistrySettings").returns(this.registrySettings);
this.getParamTypesStub = Sinon.stub(Metadata, "getParamTypes").returns(this.designParamTypes);
this.getStub = Sinon.stub(injectorService, "getProvider").returns(this.provider);
this.mapServicesStub = Sinon.stub(injectorService as any, "mapServices").returns((this.dep = new TestDep()));
Store.from(Test).set("scope", "request");
this.locals = new Map();
this.result = injectorService.invoke(Test, this.locals, undefined, false);
})
);
after(TestContext.reset);
after(() => {
this.mapServicesStub.restore();
this.getStub.restore();
this.getRegistrySettingsStub.restore();
this.getParamTypesStub.restore();
// THEN
result.should.deep.eq({factory: "factory"});
});
it("should call GlobalProviders.getRegistrySettings method", () => {
this.getRegistrySettingsStub.should.have.been.calledWithExactly(Test);
});
it("should call GlobalProviders.get method", () => {
this.getStub.should.have.been.calledWithExactly(Test);
});
it("should call Metadata.getParamTypes method", () => {
this.getParamTypesStub.should.have.been.calledWithExactly(Test);
});
it("should call settings.onInvoke method", () => {
this.registrySettings.onInvoke.should.have.been.calledWithExactly(this.provider, this.locals, this.designParamTypes);
});
it("should call injectorService.mapServices method", () => {
this.mapServicesStub.should.have.been.calledWithExactly({
target: Test,
serviceType: TestDep,
locals: this.locals,
requiredScope: false,
parentScope: "request"
});
});
it("should return a new instance of the given service", () => {
expect(this.result).to.instanceOf(Test);
});
it("should injected services into the given service constructor", () => {
expect(this.result.args).to.deep.eq([this.dep]);
});
});
describe("when designParamsTypes is given", () => {
before(
inject([InjectorService], (injectorService: InjectorService) => {
this.provider = new Provider(Test);
describe("when provider is an unknow provider", () => {
it("should invoke the class from given parameter", async () => {
// GIVEN
const token = class {
};
this.registrySettings = {
onInvoke: Sinon.stub()
};
const injector = new InjectorService();
this.designParamTypes = [TestDep];
// WHEN
const result = await injector.invoke(token);
this.getRegistrySettingsStub = Sinon.stub(GlobalProviders, "getRegistrySettings").returns(this.registrySettings);
this.getParamTypesStub = Sinon.stub(Metadata, "getParamTypes");
this.getStub = Sinon.stub(injectorService, "getProvider").returns(this.provider);
this.mapServicesStub = Sinon.stub(injectorService as any, "mapServices").returns((this.dep = new TestDep()));
Store.from(Test).set("scope", "request");
this.locals = new Map();
this.result = injectorService.invoke(Test, this.locals, this.designParamTypes, false);
})
);
after(TestContext.reset);
after(() => {
this.mapServicesStub.restore();
this.getStub.restore();
this.getRegistrySettingsStub.restore();
this.getParamTypesStub.restore();
// THEN
result.should.instanceof(token);
});
it("should call GlobalProviders.getRegistrySettings method", () => {
this.getRegistrySettingsStub.should.have.been.calledWithExactly(Test);
});
it("should call GlobalProviders.get method", () => {
this.getStub.should.have.been.calledWithExactly(Test);
});
it("shouldn't call Metadata.getParamTypes method", () => {
return this.getParamTypesStub.should.not.have.been.called;
});
it("should call settings.onInvoke method", () => {
this.registrySettings.onInvoke.should.have.been.calledWithExactly(this.provider, this.locals, this.designParamTypes);
});
it("should call injectorService.mapServices method", () => {
this.mapServicesStub.should.have.been.calledWithExactly({
target: Test,
serviceType: TestDep,
locals: this.locals,
requiredScope: false,
parentScope: "request"
});
});
it("should return a new instance of the given service", () => {
expect(this.result).to.instanceOf(Test);
});
it("should injected services into the given service constructor", () => {
expect(this.result.args).to.deep.eq([this.dep]);
});
});
describe("when onInvoke is empty", () => {
before(
inject([InjectorService], (injectorService: InjectorService) => {
this.provider = new Provider(Test);
this.registrySettings = {};
this.designParamTypes = [TestDep];
describe("when one of dependencies isn't injectable", () => {
it("should throw InjectionError", async () => {
// GIVEN
const token2 = class Ctrl {
};
const token3 = class Test {
};
this.getRegistrySettingsStub = Sinon.stub(GlobalProviders, "getRegistrySettings").returns(this.registrySettings);
const provider2 = new Provider<any>(token2);
provider2.scope = ProviderScope.SINGLETON;
provider2.type = ProviderType.CONTROLLER;
provider2.useClass = token2;
provider2.injectable = false;
this.getParamTypesStub = Sinon.stub(Metadata, "getParamTypes").returns(this.designParamTypes);
const provider3 = new Provider<any>(token3);
provider3.scope = ProviderScope.SINGLETON;
provider3.deps = [token2];
this.getStub = Sinon.stub(injectorService, "getProvider").returns(this.provider);
const injector = new InjectorService();
injector.set(token2, provider2);
injector.set(token3, provider3);
this.mapServicesStub = Sinon.stub(injectorService as any, "mapServices").returns((this.dep = new TestDep()));
// WHEN
let actualError;
try {
await injector.invoke(token3);
} catch (er) {
actualError = er;
}
Store.from(Test).set("scope", "request");
this.locals = new Map();
this.result = injectorService.invoke(Test, this.locals, undefined, false);
})
);
after(TestContext.reset);
after(() => {
this.mapServicesStub.restore();
this.getStub.restore();
this.getRegistrySettingsStub.restore();
this.getParamTypesStub.restore();
// THEN
actualError.message.should.eq("Injection failed on Test > Ctrl\nOrigin: Ctrl controller is not injectable to another provider");
});
it("should call GlobalProviders.getRegistrySettings method", () => {
this.getRegistrySettingsStub.should.have.been.calledWithExactly(Test);
});
it("should call GlobalProviders.get method", () => {
this.getStub.should.have.been.calledWithExactly(Test);
});
it("should call Metadata.getParamTypes method", () => {
this.getParamTypesStub.should.have.been.calledWithExactly(Test);
});
it("should call injectorService.mapServices method", () => {
this.mapServicesStub.should.have.been.calledWithExactly({
target: Test,
serviceType: TestDep,
locals: this.locals,
requiredScope: false,
parentScope: "request"
});
});
it("should return a new instance of the given service", () => {
expect(this.result).to.instanceOf(Test);
});
it("should injected services into the given service constructor", () => {
expect(this.result.args).to.deep.eq([this.dep]);
});
});
describe("when provider didn't exists", () => {
before(
inject([InjectorService], (injectorService: InjectorService) => {
this.registrySettings = {
onInvoke: Sinon.stub()
};
this.designParamTypes = [TestDep];
describe("when error occur", () => {
it("should throw InjectionError", async () => {
// GIVEN
const token1 = Symbol.for("TokenValue");
const token2 = Symbol.for("TokenFactory");
const token3 = class Test {
};
this.getRegistrySettingsStub = Sinon.stub(GlobalProviders, "getRegistrySettings").returns(this.registrySettings);
const provider1 = new Provider<any>(token1);
provider1.scope = ProviderScope.SINGLETON;
provider1.useValue = () => undefined; // should throw error because instance is undefined
this.getParamTypesStub = Sinon.stub(Metadata, "getParamTypes").returns(this.designParamTypes);
const provider2 = new Provider<any>(token2);
provider2.scope = ProviderScope.SINGLETON;
provider2.deps = [token1];
provider2.useFactory = () => ({});
this.getStub = Sinon.stub(injectorService, "getProvider").returns(undefined);
const provider3 = new Provider<any>(token3);
provider3.scope = ProviderScope.SINGLETON;
provider3.deps = [token2];
this.mapServicesStub = Sinon.stub(injectorService as any, "mapServices").returns((this.dep = new TestDep()));
const injector = new InjectorService();
injector.set(token1, provider1);
injector.set(token2, provider2);
injector.set(token3, provider3);
Store.from(Test).set("scope", "request");
// WHEN
let actualError;
try {
await injector.invoke(token3);
} catch (er) {
actualError = er;
}
this.locals = new Map();
this.result = injectorService.invoke(Test, this.locals, undefined, false);
})
);
after(TestContext.reset);
after(() => {
this.mapServicesStub.restore();
this.getStub.restore();
this.getRegistrySettingsStub.restore();
this.getParamTypesStub.restore();
// THEN
actualError.message.should.eq("Injection failed on Test > TokenFactory > TokenValue\nOrigin: Unable to create new instance from undefined value. Check your provider declaration for TokenValue");
});
it("should call GlobalProviders.getRegistrySettings method", () => {
this.getRegistrySettingsStub.should.have.been.calledWithExactly(Test);
});
it("should call GlobalProviders.get method", () => {
this.getStub.should.have.been.calledWithExactly(Test);
});
it("should call Metadata.getParamTypes method", () => {
this.getParamTypesStub.should.have.been.calledWithExactly(Test);
});
it("shouldn't call settings.onInvoke method", () => {
return this.registrySettings.onInvoke.should.not.have.been.called;
});
it("should call injectorService.mapServices method", () => {
this.mapServicesStub.should.have.been.calledWithExactly({
target: Test,
serviceType: TestDep,
locals: this.locals,
requiredScope: false,
parentScope: "request"
});
});
it("should return a new instance of the given service", () => {
expect(this.result).to.instanceOf(Test);
});
it("should injected services into the given service constructor", () => {
expect(this.result.args).to.deep.eq([this.dep]);
});
});
});
describe("invokeMethod()", () => {
class InjectTest {
}
describe("when designParamTypes is given", () => {
before(
inject([InjectorService], (injector: InjectorService) => {
this.injector = injector;
this.mapServicesStub = Sinon.stub(this.injector, "mapServices");
this.mapServicesStub.returns(new InjectTest());
this.getParamsTypesStub = Sinon.stub(Metadata, "getParamTypes");
this.handler = Sinon.stub();
this.injector.invokeMethod(this.handler, {
target: Test,
methodName: "test",
locals: "locals",
designParamTypes: [InjectTest]
});
})
);
after(TestContext.reset);
after(() => {
this.mapServicesStub.restore();
this.getParamsTypesStub.restore();
});
it("should call injectorService.mapServices()", () => {
this.mapServicesStub.should.have.been.calledWithExactly({
serviceType: InjectTest,
target: Test,
locals: "locals",
requiredScope: false,
parentScope: false
});
});
it("shouldn't call Metadata.getParamTypes()", () => {
this.getParamsTypesStub.should.not.have.been.called;
});
it("should call the handler", () => {
this.handler.should.have.been.calledWithExactly(new InjectTest());
});
});
describe("when designParamTypes is not given", () => {
before(
inject([InjectorService], (injector: InjectorService) => {
this.injector = injector;
this.mapServicesStub = Sinon.stub(this.injector, "mapServices");
this.mapServicesStub.returns(new InjectTest());
this.getParamsTypesStub = Sinon.stub(Metadata, "getParamTypes").returns([InjectTest]);
this.handler = Sinon.stub();
this.injector.invokeMethod(this.handler, {
target: Test,
methodName: "test",
locals: "locals"
});
})
);
after(TestContext.reset);
after(() => {
this.mapServicesStub.restore();
this.getParamsTypesStub.restore();
});
it("shouldn't call Metadata.getParamTypes()", () => {
this.getParamsTypesStub.should.have.been.calledWithExactly(Test.prototype, "test");
});
it("should call injectorService.mapServices()", () => {
this.mapServicesStub.should.have.been.calledWithExactly({
serviceType: InjectTest,
target: Test,
locals: "locals",
requiredScope: false,
parentScope: false
});
});
it("should call the handler", () => {
this.handler.should.have.been.calledWithExactly(new InjectTest());
});
});
describe("when handler is already injected", () => {
before(
inject([InjectorService], (injector: InjectorService) => {
this.injector = injector;
this.mapServicesStub = Sinon.stub(this.injector, "mapServices");
this.mapServicesStub.returns(new InjectTest());
this.getParamsTypesStub = Sinon.stub(Metadata, "getParamTypes");
this.handler = Sinon.stub();
this.handler.$injected = true;
this.injector.invokeMethod(this.handler, {
target: Test,
methodName: "test",
locals: "locals"
});
})
);
after(TestContext.reset);
after(() => {
this.mapServicesStub.restore();
this.getParamsTypesStub.restore();
});
it("shouldn't call Metadata.getParamTypes()", () => {
this.getParamsTypesStub.should.not.have.been.called;
});
it("shouldn't call injectorService.mapServices()", () => {
this.mapServicesStub.should.not.have.been.called;
});
it("should call the handler", () => {
this.handler.should.have.been.calledWithExactly("locals");
});
});
});
describe("bindInjectableProperties()", () => {

@@ -1249,3 +506,3 @@ const sandbox = Sinon.createSandbox();

describe("bindValue()", () => {
it("should bind a property with a value", () => {
it("should bind a property with a value (1)", () => {
// GIVEN

@@ -1262,6 +519,18 @@ const injector = new InjectorService();

});
it("should bind a property with a value (2)", () => {
// GIVEN
const injector = new InjectorService();
const instance = new Test();
// WHEN
injector.bindValue(instance, {propertyKey: "value", expression: "UNKNOW", defaultValue: "test2"} as any);
// THEN
expect(instance.value).to.eq("test2");
});
});
describe("bindConstant()", () => {
it("should bind a property with a value", () => {
it("should bind a property with a value (1)", () => {
// GIVEN

@@ -1287,2 +556,14 @@ const injector = new InjectorService();

});
it("should bind a property with a value (2)", () => {
// GIVEN
const injector = new InjectorService();
const instance = new Test();
// WHEN
injector.bindConstant(instance, {propertyKey: "constant", expression: "UNKNOW", defaultValue: "test"} as any);
// THEN
expect(instance.constant).to.eq("test");
});
});

@@ -1294,5 +575,4 @@

after(() => sandbox.restore());
afterEach(() => sandbox.resetHistory());
it("should bind the method and return result", async () => {
it("should bind the method", async () => {
// GIVEN

@@ -1306,6 +586,10 @@ class InterceptorTest {

const injector = new InjectorService();
injector.addProvider(InterceptorTest);
await injector.load();
const instance = new Test();
const originalMethod = instance["test3"];
const originalMethod = instance["test"];
sandbox.stub(injector, "get").returns(new InterceptorTest());
sandbox.spy(injector, "get");

@@ -1327,37 +611,3 @@ // WHEN

});
it("should bind the method and throw error", async () => {
// GIVEN
class InterceptorTest {
aroundInvoke(ctx: any) {
return ctx.proceed(new Error());
}
}
const injector = new InjectorService();
const instance = new Test();
const originalMethod = instance["test3"];
sandbox.stub(injector, "get").returns(new InterceptorTest());
// WHEN
injector.bindInterceptor(instance, {
bindingType: "interceptor",
propertyKey: "test3",
useType: InterceptorTest
} as any);
let actualError;
try {
(instance as any).test3("test");
} catch (er) {
actualError = er;
}
// THEN
expect(originalMethod).should.not.eq(instance.test3);
injector.get.should.have.been.calledWithExactly(InterceptorTest);
actualError.should.instanceOf(Error);
});
});
});

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

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

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

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