Socket
Socket
Sign inDemoInstall

huject

Package Overview
Dependencies
2
Maintainers
1
Versions
14
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.2.3 to 1.3.0

src/containerfactory.js

4

Changelog.md

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

# Version 1.3
Possibility to inject ContainerFactoryInterface to access to container and create objects dynamically
@Factory decorator to auto-create factories
# Version 1.2

@@ -2,0 +6,0 @@ @Optional decorator for constructor argument injection and property injection.

declare module Huject {
class Container {
export class Container {
/**

@@ -76,2 +76,17 @@ * @constructor

/**
* Interface for creating objects from container dynamically
*/
export class ContainerFactoryInterface {
/**
* Create object using the container. Will create new instance for each call
* @param definition Class or string definition
* @param constructorArgs Optional constructor arguments. Overrides constructor arguments in definition
*/
public make<T extends Function>(definition: T, constructorArgs?: Array<any>): T;
public make(definition: string, constructorArgs?: Array<any>): any;
}
interface Definition {

@@ -224,2 +239,28 @@ /**

export function Optional(target: Object, propertyKey: string|symbol, parameterIndex: number);
/**
* Specify that class should be base for auto-factory
* @param target
* @example
* @Factory
* class MyModelFactory {
* @Factory
* public createModel(): Model {return null;}
* }
*/
export function Factory<TFunction extends Function>(target: TFunction): TFunction | void;
/**
* Specify that method should be used for auto-factory
* @param target
* @param propertyKey
* @param descriptor
* @example
* @Factory
* class MyModelFactory {
* @Factory
* public createModel(): Model {return null;}
* }
*/
export function Factory<T>(target: Object, propertyKey: string|symbol, descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T> | void;
}

@@ -226,0 +267,0 @@

2

package.json
{
"name": "huject",
"version": "1.2.3",
"version": "1.3.0",
"description": "Typescript dependency injection container for humans",

@@ -5,0 +5,0 @@ "main": "./src/index.js",

@@ -12,3 +12,6 @@ # Typescript dependency injection for humans!

* Property and constructor dependencies autowiring
* Simple API, just 3 methods to go
* Simple API, just 3 methods and few decorators to go
* Optional injection (from version 1.2)
* Injecting container as factory (**new** 1.3 version)
* Auto-Creating object factories! (**new** 1.3 version)

@@ -233,2 +236,4 @@ ## Installation

@Optional
// Used for creating auto factories (See below)
@Factory
```

@@ -407,2 +412,92 @@

## Factories
I'm pleased to introduce to you new feature starting from version 1.3: Auto-Factory and ContainerFactoryInterface
### ContainerFactoryInterface
You can now inject ContainerFactoryInterface into your service/controller and create object from container dynamically, by request:
```typescript
import {ContainerFactoryInterface} from 'huject';
import {Model} from './Model';
class MyController {
@Inject
public objectFactory: ContainerFactoryInterface;
public method(): void {
let myModel = this.objectFactory.make(Model, ['param1', '50, ...]);
...
}
}
```
No need to pre-register ContainerFactoryInterface (unless you want to redefine it). Also objects created by this method will always have FACTORY scope (and it will return new object at each call). The object will be resolved by container so it will get all benefits from dependencies autowiring. You can also pass constructor arguments to make()
Interface is simple:
```typescript
export class ContainerFactoryInterface {
/**
* Create object using the container. Will create new instance for each call
* @param definition Class or string definition
* @param constructorArgs Optional constructor arguments. Overrides constructor arguments in definition
*/
public make<T extends Function>(definition: T, constructorArgs?: Array<any>): T;
public make(definition: string, constructorArgs?: Array<any>): any;
}
```
### Auto-Factories
By using decorators and typehints you can tell container to create factory for you:
import {Factory} From 'huject'
```typescript
class MyModel {
public constructor(num?: number) {
....
}
}
class AnotherModel {
}
// class name doesn't matter, don't forget to specify @Factory for both methods and class!
@Factory
class MyModelFactory {
@Factory
public createModel(num?: number): MyModel {return null;} // Add something to function body to stop TS compiler complain about no function return
@Factory
public createAnotherModel(): AnotherModel { throw new Error(); } // You can throw here to make sure too. That probably will never happen
}
// You can extend factories
@Factory
class CoolFactory extends MyModelFactory {
@Factory
public createCoolModel(): MyModel {return null;}
}
// Just inject factory to controller. No pre-registration needed
class Controller {
@Inject
public factory: CoolFactory;
public method(): void {
let myModel = factory.createModel(40); // Call factory inherited method
let anotherModel = factory.createAnotherModel();
...
}
}
```
The return type annotation is required. Also it should be only constructor function. For others types it will throw an error. The return type will be resolved by container, so autowiring is possible (properly best to use property injection for that). You can also pass constructor arguments - just define them in factory and pass when calling factory method.
No need to pre-register neither factory or classes used to create instances by factories in container.
## Typescript interfaces and implementation->interface binding

@@ -409,0 +504,0 @@ In typescript the interfaces are not a real objects in javascript realtime. I'd suggest you initially were going to write something like it:

/// <reference path="../typings/tsd.d.ts" />
/// <reference path="../node_modules/reflect-metadata/reflect-metadata.d.ts" />
require('es6-collections');
require('reflect-metadata');
var definition_1 = require('./definition');
var resolver_1 = require("./resolver");
var containerfactoryinterface_1 = require("./containerfactoryinterface");
var containerfactory_1 = require("./containerfactory");
/**

@@ -19,4 +20,5 @@ * DI Container class

this.allowUnregisteredResolve = false;
this.definitions = new Map();
this.singletonObjects = new WeakMap();
this.resolver = new resolver_1.ContainerResolver();
var containerFactory = new containerfactory_1.ContainerFactory(this.resolver);
this.register(containerfactoryinterface_1.ContainerFactoryInterface, containerFactory);
}

@@ -26,16 +28,4 @@ /**

* This is useful to avoid many stub container.register(Class) class, but you need to be careful to not create
* interface instead of implementation or use abstract classes (available from typescript 1.6) as interface classes
* interface instead of implementation
* @param allow true to allow, false to disallow
* @example
* import Service from './service'
*
* @ConstructorInject
* class Controller {
* private service: Service;
* public constructor(myService: Service} {
* this.service = myService
* }
* }
*
* let controller = container.resolve(Controller);
*/

@@ -86,3 +76,3 @@ Container.prototype.setAllowUnregisteredResolving = function (allow) {

// 2. register('string', Class, [constructorArgs])
// 4. register('string', 'number|string|object|boolean')
// 3. register('string', 'number|string|object|boolean')
if (typeof implementationOrConstructorArgs === "function") {

@@ -98,3 +88,3 @@ // Case 1,2

}
this.definitions.set(definition, def);
this.resolver.addDefinition(definition, def);
return def;

@@ -108,3 +98,3 @@ };

def = new definition_1.Definition(definition, callable, null, definition_1.FactoryMethod.SINGLETON, definition_1.DefinitionObjectType.CALLABLE);
this.definitions.set(definition, def);
this.resolver.addDefinition(definition, def);
return def;

@@ -118,156 +108,4 @@ };

Container.prototype.resolve = function (definition, method) {
var internalDefinition = this.definitions.get(definition);
if (!internalDefinition) {
if (!this.allowUnregisteredResolve || typeof definition !== "function") {
throw new Error("Unknown definition " + definition.toString());
}
else if (typeof definition === "function") {
internalDefinition = this.register(definition);
}
}
var constructor = this.resolveDefinition(internalDefinition);
var resolveMethod = internalDefinition.method;
if (typeof method !== 'undefined') {
resolveMethod = method;
}
switch (resolveMethod) {
case definition_1.FactoryMethod.SINGLETON:
if (!this.singletonObjects.has(internalDefinition)) {
if (internalDefinition.definitionObjectType == definition_1.DefinitionObjectType.CALLABLE) {
this.singletonObjects.set(internalDefinition, constructor.call(this));
}
else {
var obj = new constructor();
this.resolveParameters(obj);
this.singletonObjects.set(internalDefinition, obj);
}
}
return this.singletonObjects.get(internalDefinition);
break;
case definition_1.FactoryMethod.FACTORY:
if (internalDefinition.definitionObjectType == definition_1.DefinitionObjectType.CALLABLE) {
return constructor.call(this);
}
else {
var obj = new constructor();
this.resolveParameters(obj);
return obj;
}
break;
case definition_1.FactoryMethod.OBJECT:
return constructor;
break;
}
return this.resolver.resolve(definition, method, undefined, !this.allowUnregisteredResolve);
};
/**
* Resolves definition
* @private
* @param definition
*/
Container.prototype.resolveDefinition = function (definition) {
if (definition.definitionObjectType == definition_1.DefinitionObjectType.CALLABLE || definition.method == definition_1.FactoryMethod.OBJECT) {
return definition.definitionConstructor;
}
var constructor = this.resolveConstructor(definition);
var constructorArguments = [];
if (Reflect.hasOwnMetadata("inject:constructor", constructor)) {
// Resolve constructor dependencies
var dependencies = Reflect.getOwnMetadata("design:paramtypes", constructor);
var resolvedDeps = [];
if (dependencies) {
for (var i = 0; i < dependencies.length; i++) {
var dep = dependencies[i];
var method = Reflect.getOwnMetadata('inject:constructor:param' + i + ':method', constructor);
// Use literal for resolving if specified
if (Reflect.hasOwnMetadata('inject:constructor:param' + i + ':literal', constructor)) {
dep = Reflect.getOwnMetadata('inject:constructor:param' + i + ':literal', constructor);
}
var resolvedDep = void 0;
try {
resolvedDep = this.resolve(dep, method);
}
catch (e) {
// Pass null if @Optional
if (Reflect.hasOwnMetadata('inject:constructor:param' + i + ':optional', constructor)) {
resolvedDep = null;
}
else {
// Rethrow
throw e;
}
}
resolvedDeps.push(resolvedDep);
}
}
constructorArguments = resolvedDeps;
}
else {
// No constructor injection, lookup for constructor arguments in definition
constructorArguments = this.resolveConstructorArguments(definition);
if (!constructorArguments) {
constructorArguments = [];
}
}
var newConstructor = function () {
constructor.apply(this, constructorArguments);
};
newConstructor.prototype = constructor.prototype;
return newConstructor;
};
/**
* Injects parameters into object
* @param object
*/
Container.prototype.resolveParameters = function (object) {
var test = Reflect.getMetadataKeys(object);
for (var key in object) {
if (Reflect.hasMetadata("inject:property", object, key)) {
var method = Reflect.getMetadata("inject:property", object, key);
var paramDefinition = void 0;
if (Reflect.hasMetadata('inject:property:literal', object, key)) {
// Resolve property by string literal
paramDefinition = Reflect.getMetadata('inject:property:literal', object, key);
}
else {
// Resolve property by typehint
paramDefinition = Reflect.getMetadata('design:type', object, key);
}
var resolvedObj = void 0;
try {
resolvedObj = this.resolve(paramDefinition, method);
object[key] = resolvedObj;
}
catch (e) {
if (!Reflect.hasMetadata('inject:property:optional', object, key)) {
throw e;
}
}
}
}
};
/**
* Resolves constructor by looking in definition chain
* @private
* @param definition
*/
Container.prototype.resolveConstructor = function (definition) {
var constructor = definition.definitionConstructor;
if (this.definitions.has(constructor) && constructor != definition.key) {
constructor = this.resolveConstructor(this.definitions.get(constructor));
}
return constructor;
};
/**
* Resolves constructor arguments from definition chain
* @private
* @param definition
* @returns {Array<any>}
*/
Container.prototype.resolveConstructorArguments = function (definition) {
var constructorArgs = definition.constructorArgs;
if (!constructorArgs && this.definitions.has(definition.definitionConstructor) && (definition.definitionConstructor != definition.key)) {
constructorArgs = this.resolveConstructorArguments(this.definitions.get(definition.definitionConstructor));
}
return constructorArgs;
};
return Container;

@@ -274,0 +112,0 @@ })();

/// <reference path="../typings/tsd.d.ts" />
/// <reference path="../node_modules/reflect-metadata/reflect-metadata.d.ts" />
import 'es6-collections';
import 'reflect-metadata';
import {Definition, FactoryMethod, DefinitionObjectType} from './definition';
import {ContainerResolver} from "./resolver";
import {ContainerFactoryInterface} from "./containerfactoryinterface";
import {ContainerFactory} from "./containerfactory";

@@ -14,12 +15,6 @@ /**

/**
* Definition map
* @type {Map}
* Resolver
*/
private definitions: Map<string|Function, Definition>;
private resolver: ContainerResolver;
/**
* Singleton objects
* @type {WeakMap<Definition, Object>}
*/
private singletonObjects: WeakMap<Definition, Object>;

@@ -36,4 +31,6 @@ /**

public constructor() {
this.definitions = new Map<string|Function, Definition>();
this.singletonObjects = new WeakMap<Definition, Object>();
this.resolver = new ContainerResolver();
let containerFactory = new ContainerFactory(this.resolver);
this.register(ContainerFactoryInterface, containerFactory);
}

@@ -44,16 +41,4 @@

* This is useful to avoid many stub container.register(Class) class, but you need to be careful to not create
* interface instead of implementation or use abstract classes (available from typescript 1.6) as interface classes
* interface instead of implementation
* @param allow true to allow, false to disallow
* @example
* import Service from './service'
*
* @ConstructorInject
* class Controller {
* private service: Service;
* public constructor(myService: Service} {
* this.service = myService
* }
* }
*
* let controller = container.resolve(Controller);
*/

@@ -101,3 +86,3 @@ public setAllowUnregisteredResolving(allow: Boolean) {

// 2. register('string', Class, [constructorArgs])
// 4. register('string', 'number|string|object|boolean')
// 3. register('string', 'number|string|object|boolean')
if (typeof implementationOrConstructorArgs === "function") {

@@ -113,3 +98,3 @@ // Case 1,2

}
this.definitions.set(definition, def);
this.resolver.addDefinition(definition, def);
return def;

@@ -124,3 +109,3 @@ }

def = new Definition(definition, callable, null, FactoryMethod.SINGLETON, DefinitionObjectType.CALLABLE);
this.definitions.set(definition, def);
this.resolver.addDefinition(definition, def);
return def;

@@ -135,160 +120,4 @@ }

public resolve(definition: string|Function, method?: FactoryMethod): any {
let internalDefinition = this.definitions.get(definition);
if (!internalDefinition) {
if (!this.allowUnregisteredResolve || typeof definition !== "function") {
throw new Error(`Unknown definition ${definition.toString()}`);
} else if (typeof definition === "function") {
internalDefinition = this.register(definition);
}
}
let constructor = this.resolveDefinition(internalDefinition);
let resolveMethod = internalDefinition.method;
if (typeof method !== 'undefined') {
resolveMethod = method;
}
switch (resolveMethod) {
case FactoryMethod.SINGLETON:
if (!this.singletonObjects.has(internalDefinition)) {
if (internalDefinition.definitionObjectType == DefinitionObjectType.CALLABLE) {
this.singletonObjects.set(internalDefinition, constructor.call(this));
} else {
let obj = new constructor();
this.resolveParameters(obj);
this.singletonObjects.set(internalDefinition, obj);
}
}
return this.singletonObjects.get(internalDefinition);
break;
case FactoryMethod.FACTORY:
if (internalDefinition.definitionObjectType == DefinitionObjectType.CALLABLE) {
return constructor.call(this);
} else {
let obj = new constructor();
this.resolveParameters(obj);
return obj;
}
break;
case FactoryMethod.OBJECT:
return constructor;
break;
}
return this.resolver.resolve(definition, method, undefined, !this.allowUnregisteredResolve);
}
/**
* Resolves definition
* @private
* @param definition
*/
private resolveDefinition(definition: Definition): any {
if (definition.definitionObjectType == DefinitionObjectType.CALLABLE || definition.method == FactoryMethod.OBJECT) {
return definition.definitionConstructor;
}
let constructor = this.resolveConstructor(definition);
let constructorArguments = [];
if (Reflect.hasOwnMetadata("inject:constructor", constructor)) {
// Resolve constructor dependencies
let dependencies = Reflect.getOwnMetadata("design:paramtypes", constructor);
let resolvedDeps = [];
if (dependencies) {
for (let i = 0; i < dependencies.length; i++) {
let dep = dependencies[i];
let method = Reflect.getOwnMetadata('inject:constructor:param' + i + ':method', constructor);
// Use literal for resolving if specified
if (Reflect.hasOwnMetadata('inject:constructor:param' + i + ':literal', constructor)) {
dep = Reflect.getOwnMetadata('inject:constructor:param' + i + ':literal', constructor);
}
let resolvedDep;
try {
resolvedDep = this.resolve(dep, method);
} catch (e) {
// Pass null if @Optional
if (Reflect.hasOwnMetadata('inject:constructor:param' + i + ':optional', constructor)) {
resolvedDep = null;
} else {
// Rethrow
throw e;
}
}
resolvedDeps.push(resolvedDep);
}
}
constructorArguments = resolvedDeps;
} else {
// No constructor injection, lookup for constructor arguments in definition
constructorArguments = this.resolveConstructorArguments(definition);
if (!constructorArguments) {
constructorArguments = [];
}
}
let newConstructor = function () {
constructor.apply(this, constructorArguments);
};
newConstructor.prototype = constructor.prototype;
return newConstructor;
}
/**
* Injects parameters into object
* @param object
*/
private resolveParameters(object: Object) {
let test = Reflect.getMetadataKeys(object);
for (let key in object) {
if (Reflect.hasMetadata("inject:property", object, key)) {
let method: FactoryMethod = Reflect.getMetadata("inject:property", object, key);
let paramDefinition: string|Function;
if (Reflect.hasMetadata('inject:property:literal', object, key)) {
// Resolve property by string literal
paramDefinition = Reflect.getMetadata('inject:property:literal', object, key);
} else {
// Resolve property by typehint
paramDefinition = Reflect.getMetadata('design:type', object, key);
}
let resolvedObj;
try {
resolvedObj = this.resolve(paramDefinition, method);
object[key] = resolvedObj;
} catch (e) {
if (!Reflect.hasMetadata('inject:property:optional', object, key)) {
throw e;
}
}
}
}
}
/**
* Resolves constructor by looking in definition chain
* @private
* @param definition
*/
private resolveConstructor(definition: Definition) {
let constructor = definition.definitionConstructor;
if (this.definitions.has(constructor) && constructor != definition.key) {
constructor = this.resolveConstructor(this.definitions.get(constructor));
}
return constructor;
}
/**
* Resolves constructor arguments from definition chain
* @private
* @param definition
* @returns {Array<any>}
*/
private resolveConstructorArguments(definition: Definition): Array<any> {
let constructorArgs = definition.constructorArgs;
if (!constructorArgs && this.definitions.has(definition.definitionConstructor) && (definition.definitionConstructor != definition.key)) {
constructorArgs = this.resolveConstructorArguments(this.definitions.get(definition.definitionConstructor));
}
return constructorArgs;
}
}

@@ -131,2 +131,28 @@ /// <reference path="../typings/tsd.d.ts" />

exports.Optional = Optional;
/**
* Decorator for creating auto-factories
*/
function Factory() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i - 0] = arguments[_i];
}
var target;
switch (args.length) {
case 1:
target = args[0];
Reflect.defineMetadata('inject:autofactory', true, target);
return target;
break;
case 3:
target = args[0];
var propertyKey = args[1];
Reflect.defineMetadata('inject:factorymethod', true, target, propertyKey);
return args[2];
break;
default:
throw new Error("@Factory decorator is not allowed here");
}
}
exports.Factory = Factory;
//# sourceMappingURL=decorators.js.map

@@ -132,2 +132,25 @@ /// <reference path="../typings/tsd.d.ts" />

}
}
/**
* Decorator for creating auto-factories
*/
export function Factory(...args: any[]): any {
let target;
switch (args.length) {
case 1:
target = args[0];
Reflect.defineMetadata('inject:autofactory', true, target);
return target;
break;
case 3:
target = args[0];
let propertyKey = args[1];
Reflect.defineMetadata('inject:factorymethod', true, target, propertyKey);
return args[2];
break;
default:
throw new Error("@Factory decorator is not allowed here");
}
}

@@ -51,2 +51,5 @@ /// <reference path="../typings/tsd.d.ts" />

Definition.prototype.as = function (method) {
if (this.method == FactoryMethod.OBJECT) {
throw new Error("You're trying to override factory method for object");
}
this.method = method;

@@ -53,0 +56,0 @@ return this;

@@ -74,2 +74,5 @@ /// <reference path="../typings/tsd.d.ts" />

public as(method: FactoryMethod) {
if (this.method == FactoryMethod.OBJECT) {
throw new Error("You're trying to override factory method for object");
}
this.method = method;

@@ -76,0 +79,0 @@ return this;

@@ -12,2 +12,5 @@ /**

exports.Optional = decorators_1.Optional;
exports.Factory = decorators_1.Factory;
var containerfactoryinterface_1 = require('./containerfactoryinterface');
exports.ContainerFactoryInterface = containerfactoryinterface_1.ContainerFactoryInterface;
//# sourceMappingURL=index.js.map

@@ -7,4 +7,5 @@ /**

export {FactoryMethod} from './definition';
export {ConstructorInject, Inject, Optional} from './decorators';
export {ConstructorInject, Inject, Optional, Factory} from './decorators';
export {ContainerFactoryInterface} from './containerfactoryinterface';

@@ -11,4 +11,4 @@ {

"exclude": [
"node_modules"
"node_modules"
]
}

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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc