Initial commit

This commit is contained in:
2025-03-07 19:22:02 +01:00
commit 4a98255d83
55743 changed files with 5280367 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
The MIT License
Copyright (c) 2015-2020 TypeStack
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+88
View File
@@ -0,0 +1,88 @@
# TypeDI
![Build Status](https://github.com/typestack/typedi/workflows/CI/badge.svg)
[![codecov](https://codecov.io/gh/typestack/typedi/branch/master/graph/badge.svg)](https://codecov.io/gh/typestack/typedi)
[![npm version](https://badge.fury.io/js/typedi.svg)](https://badge.fury.io/js/typedi)
[![Dependency Status](https://david-dm.org/typestack/typedi.svg)](https://david-dm.org/typestack/typedi)
TypeDI is a [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) tool for TypeScript and JavaScript. With it you can build well-structured and easily testable applications in Node or in the browser.
Main features includes:
- property based injection
- constructor based injection
- singleton and transient services
- support for multiple DI containers
## Installation
> Note: This installation guide is for usage with TypeScript, if you wish to use
> TypeDI without Typescript please read the documentation about how get started.
To start using TypeDI install the required packages via NPM:
```bash
npm install typedi reflect-metadata
```
Import the `reflect-metadata` package at the **first line** of your application:
```ts
import 'reflect-metadata';
// Your other imports and initialization code
// comes here after you imported the reflect-metadata package!
```
As a last step, you need to enable emitting decorator metadata in your Typescript config. Add these two lines to your `tsconfig.json` file under the `compilerOptions` key:
```json
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
```
Now you are ready to use TypeDI with Typescript!
## Basic Usage
```ts
import { Container, Service } from 'typedi';
@Service()
class ExampleInjectedService {
printMessage() {
console.log('I am alive!');
}
}
@Service()
class ExampleService {
constructor(
// because we annotated ExampleInjectedService with the @Service()
// decorator TypeDI will automatically inject an instance of
// ExampleInjectedService here when the ExampleService class is requested
// from TypeDI.
private injectedService: ExampleInjectedService
) {}
}
const serviceInstance = Container.get(ExampleService);
// we request an instance of ExampleService from TypeDI
serviceInstance.injectedService.printMessage();
// logs "I am alive!" to the console
```
## Documentation
The detailed usage guide and API documentation for the project can be found:
- at [docs.typestack.community/typedi][docs-stable]
- in the `./docs` folder of the repository
[docs-stable]: https://docs.typestack.community/typedi/
[docs-development]: https://docs.typestack.community/typedi/v/develop/
## Contributing
Please read our [contributing guidelines](./CONTRIBUTING.md) to get started.
+693
View File
@@ -0,0 +1,693 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ClassTransformer = {}));
}(this, (function (exports) { 'use strict';
/**
* Used to create unique typed service identifier.
* Useful when service has only interface, but don't have a class.
*/
var Token = /** @class */ (function () {
/**
* @param name Token name, optional and only used for debugging purposes.
*/
function Token(name) {
this.name = name;
}
return Token;
}());
var __extends = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
/**
* Thrown when requested service was not found.
*/
var ServiceNotFoundError = /** @class */ (function (_super) {
__extends(ServiceNotFoundError, _super);
function ServiceNotFoundError(identifier) {
var _a, _b;
var _this = _super.call(this) || this;
_this.name = 'ServiceNotFoundError';
/** Normalized identifier name used in the error message. */
_this.normalizedIdentifier = '<UNKNOWN_IDENTIFIER>';
if (typeof identifier === 'string') {
_this.normalizedIdentifier = identifier;
}
else if (identifier instanceof Token) {
_this.normalizedIdentifier = "Token<" + (identifier.name || 'UNSET_NAME') + ">";
}
else if (identifier && (identifier.name || ((_a = identifier.prototype) === null || _a === void 0 ? void 0 : _a.name))) {
_this.normalizedIdentifier =
"MaybeConstructable<" + identifier.name + ">" ||
"MaybeConstructable<" + ((_b = identifier.prototype) === null || _b === void 0 ? void 0 : _b.name) + ">";
}
return _this;
}
Object.defineProperty(ServiceNotFoundError.prototype, "message", {
get: function () {
return ("Service with \"" + this.normalizedIdentifier + "\" identifier was not found in the container. " +
"Register it before usage via explicitly calling the \"Container.set\" function or using the \"@Service()\" decorator.");
},
enumerable: false,
configurable: true
});
return ServiceNotFoundError;
}(Error));
var __extends$1 = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
/**
* Thrown when DI cannot inject value into property decorated by @Inject decorator.
*/
var CannotInstantiateValueError = /** @class */ (function (_super) {
__extends$1(CannotInstantiateValueError, _super);
function CannotInstantiateValueError(identifier) {
var _a, _b;
var _this = _super.call(this) || this;
_this.name = 'CannotInstantiateValueError';
/** Normalized identifier name used in the error message. */
_this.normalizedIdentifier = '<UNKNOWN_IDENTIFIER>';
// TODO: Extract this to a helper function and share between this and NotFoundError.
if (typeof identifier === 'string') {
_this.normalizedIdentifier = identifier;
}
else if (identifier instanceof Token) {
_this.normalizedIdentifier = "Token<" + (identifier.name || 'UNSET_NAME') + ">";
}
else if (identifier && (identifier.name || ((_a = identifier.prototype) === null || _a === void 0 ? void 0 : _a.name))) {
_this.normalizedIdentifier =
"MaybeConstructable<" + identifier.name + ">" ||
"MaybeConstructable<" + ((_b = identifier.prototype) === null || _b === void 0 ? void 0 : _b.name) + ">";
}
return _this;
}
Object.defineProperty(CannotInstantiateValueError.prototype, "message", {
get: function () {
return ("Cannot instantiate the requested value for the \"" + this.normalizedIdentifier + "\" identifier. " +
"The related metadata doesn't contain a factory or a type to instantiate.");
},
enumerable: false,
configurable: true
});
return CannotInstantiateValueError;
}(Error));
var EMPTY_VALUE = Symbol('EMPTY_VALUE');
var __assign = (undefined && undefined.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __spreadArrays = (undefined && undefined.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
/**
* TypeDI can have multiple containers.
* One container is ContainerInstance.
*/
var ContainerInstance = /** @class */ (function () {
function ContainerInstance(id) {
/** All registered services in the container. */
this.services = [];
this.id = id;
}
ContainerInstance.prototype.has = function (identifier) {
return !!this.findService(identifier);
};
ContainerInstance.prototype.get = function (identifier) {
var globalContainer = Container.of(undefined);
var globalService = globalContainer.findService(identifier);
var scopedService = this.findService(identifier);
if (globalService && globalService.global === true)
return this.getServiceValue(globalService);
if (scopedService)
return this.getServiceValue(scopedService);
/** If it's the first time requested in the child container we load it from parent and set it. */
if (globalService && this !== globalContainer) {
var clonedService = __assign({}, globalService);
clonedService.value = EMPTY_VALUE;
/**
* We need to immediately set the empty value from the root container
* to prevent infinite lookup in cyclic dependencies.
*/
this.set(clonedService);
var value = this.getServiceValue(clonedService);
this.set(__assign(__assign({}, clonedService), { value: value }));
return value;
}
if (globalService)
return this.getServiceValue(globalService);
throw new ServiceNotFoundError(identifier);
};
ContainerInstance.prototype.getMany = function (identifier) {
var _this = this;
return this.findAllServices(identifier).map(function (service) { return _this.getServiceValue(service); });
};
ContainerInstance.prototype.set = function (identifierOrServiceMetadata, value) {
var _this = this;
if (identifierOrServiceMetadata instanceof Array) {
identifierOrServiceMetadata.forEach(function (data) { return _this.set(data); });
return this;
}
if (typeof identifierOrServiceMetadata === 'string' || identifierOrServiceMetadata instanceof Token) {
return this.set({
id: identifierOrServiceMetadata,
type: null,
value: value,
factory: undefined,
global: false,
multiple: false,
eager: false,
transient: false,
});
}
if (typeof identifierOrServiceMetadata === 'function') {
return this.set({
id: identifierOrServiceMetadata,
// TODO: remove explicit casting
type: identifierOrServiceMetadata,
value: value,
factory: undefined,
global: false,
multiple: false,
eager: false,
transient: false,
});
}
var newService = __assign({ id: new Token('UNREACHABLE'), type: null, factory: undefined, value: EMPTY_VALUE, global: false, multiple: false, eager: false, transient: false }, identifierOrServiceMetadata);
var service = this.findService(newService.id);
if (service && service.multiple !== true) {
Object.assign(service, newService);
}
else {
this.services.push(newService);
}
if (newService.eager) {
this.get(newService.id);
}
return this;
};
/**
* Removes services with a given service identifiers.
*/
ContainerInstance.prototype.remove = function (identifierOrIdentifierArray) {
var _this = this;
if (Array.isArray(identifierOrIdentifierArray)) {
identifierOrIdentifierArray.forEach(function (id) { return _this.remove(id); });
}
else {
this.services = this.services.filter(function (service) {
if (service.id === identifierOrIdentifierArray) {
_this.destroyServiceInstance(service);
return false;
}
return true;
});
}
return this;
};
/**
* Completely resets the container by removing all previously registered services from it.
*/
ContainerInstance.prototype.reset = function (options) {
var _this = this;
if (options === void 0) { options = { strategy: 'resetValue' }; }
switch (options.strategy) {
case 'resetValue':
this.services.forEach(function (service) { return _this.destroyServiceInstance(service); });
break;
case 'resetServices':
this.services.forEach(function (service) { return _this.destroyServiceInstance(service); });
this.services = [];
break;
default:
throw new Error('Received invalid reset strategy.');
}
return this;
};
/**
* Returns all services registered with the given identifier.
*/
ContainerInstance.prototype.findAllServices = function (identifier) {
return this.services.filter(function (service) { return service.id === identifier; });
};
/**
* Finds registered service in the with a given service identifier.
*/
ContainerInstance.prototype.findService = function (identifier) {
return this.services.find(function (service) { return service.id === identifier; });
};
/**
* Gets the value belonging to `serviceMetadata.id`.
*
* - if `serviceMetadata.value` is already set it is immediately returned
* - otherwise the requested type is resolved to the value saved to `serviceMetadata.value` and returned
*/
ContainerInstance.prototype.getServiceValue = function (serviceMetadata) {
var _a;
var value = EMPTY_VALUE;
/**
* If the service value has been set to anything prior to this call we return that value.
* NOTE: This part builds on the assumption that transient dependencies has no value set ever.
*/
if (serviceMetadata.value !== EMPTY_VALUE) {
return serviceMetadata.value;
}
/** If both factory and type is missing, we cannot resolve the requested ID. */
if (!serviceMetadata.factory && !serviceMetadata.type) {
throw new CannotInstantiateValueError(serviceMetadata.id);
}
/**
* If a factory is defined it takes priority over creating an instance via `new`.
* The return value of the factory is not checked, we believe by design that the user knows what he/she is doing.
*/
if (serviceMetadata.factory) {
/**
* If we received the factory in the [Constructable<Factory>, "functionName"] format, we need to create the
* factory first and then call the specified function on it.
*/
if (serviceMetadata.factory instanceof Array) {
var factoryInstance = void 0;
try {
/** Try to get the factory from TypeDI first, if failed, fall back to simply initiating the class. */
factoryInstance = this.get(serviceMetadata.factory[0]);
}
catch (error) {
if (error instanceof ServiceNotFoundError) {
factoryInstance = new serviceMetadata.factory[0]();
}
else {
throw error;
}
}
value = factoryInstance[serviceMetadata.factory[1]](this, serviceMetadata.id);
}
else {
/** If only a simple function was provided we simply call it. */
value = serviceMetadata.factory(this, serviceMetadata.id);
}
}
/**
* If no factory was provided and only then, we create the instance from the type if it was set.
*/
if (!serviceMetadata.factory && serviceMetadata.type) {
var constructableTargetType = serviceMetadata.type;
// setup constructor parameters for a newly initialized service
var paramTypes = ((_a = Reflect) === null || _a === void 0 ? void 0 : _a.getMetadata('design:paramtypes', constructableTargetType)) || [];
var params = this.initializeParams(constructableTargetType, paramTypes);
// "extra feature" - always pass container instance as the last argument to the service function
// this allows us to support javascript where we don't have decorators and emitted metadata about dependencies
// need to be injected, and user can use provided container to get instances he needs
params.push(this);
value = new (constructableTargetType.bind.apply(constructableTargetType, __spreadArrays([void 0], params)))();
// TODO: Calling this here, leads to infinite loop, because @Inject decorator registerds a handler
// TODO: which calls Container.get, which will check if the requested type has a value set and if not
// TODO: it will start the instantiation process over. So this is currently called outside of the if branch
// TODO: after the current value has been assigned to the serviceMetadata.
// this.applyPropertyHandlers(constructableTargetType, value as Constructable<unknown>);
}
/** If this is not a transient service, and we resolved something, then we set it as the value. */
if (!serviceMetadata.transient && value !== EMPTY_VALUE) {
serviceMetadata.value = value;
}
if (value === EMPTY_VALUE) {
/** This branch should never execute, but better to be safe than sorry. */
throw new CannotInstantiateValueError(serviceMetadata.id);
}
if (serviceMetadata.type) {
this.applyPropertyHandlers(serviceMetadata.type, value);
}
return value;
};
/**
* Initializes all parameter types for a given target service class.
*/
ContainerInstance.prototype.initializeParams = function (target, paramTypes) {
var _this = this;
return paramTypes.map(function (paramType, index) {
var paramHandler = Container.handlers.find(function (handler) {
/**
* @Inject()-ed values are stored as parameter handlers and they reference their target
* when created. So when a class is extended the @Inject()-ed values are not inherited
* because the handler still points to the old object only.
*
* As a quick fix a single level parent lookup is added via `Object.getPrototypeOf(target)`,
* however this should be updated to a more robust solution.
*
* TODO: Add proper inheritance handling: either copy the handlers when a class is registered what
* TODO: has it's parent already registered as dependency or make the lookup search up to the base Object.
*/
return ((handler.object === target || handler.object === Object.getPrototypeOf(target)) && handler.index === index);
});
if (paramHandler)
return paramHandler.value(_this);
if (paramType && paramType.name && !_this.isPrimitiveParamType(paramType.name)) {
return _this.get(paramType);
}
return undefined;
});
};
/**
* Checks if given parameter type is primitive type or not.
*/
ContainerInstance.prototype.isPrimitiveParamType = function (paramTypeName) {
return ['string', 'boolean', 'number', 'object'].includes(paramTypeName.toLowerCase());
};
/**
* Applies all registered handlers on a given target class.
*/
ContainerInstance.prototype.applyPropertyHandlers = function (target, instance) {
var _this = this;
Container.handlers.forEach(function (handler) {
if (typeof handler.index === 'number')
return;
if (handler.object.constructor !== target && !(target.prototype instanceof handler.object.constructor))
return;
if (handler.propertyName) {
instance[handler.propertyName] = handler.value(_this);
}
});
};
/**
* Checks if the given service metadata contains a destroyable service instance and destroys it in place. If the service
* contains a callable function named `destroy` it is called but not awaited and the return value is ignored..
*
* @param serviceMetadata the service metadata containing the instance to destroy
* @param force when true the service will be always destroyed even if it's cannot be re-created
*/
ContainerInstance.prototype.destroyServiceInstance = function (serviceMetadata, force) {
if (force === void 0) { force = false; }
/** We reset value only if we can re-create it (aka type or factory exists). */
var shouldResetValue = force || !!serviceMetadata.type || !!serviceMetadata.factory;
if (shouldResetValue) {
/** If we wound a function named destroy we call it without any params. */
if (typeof (serviceMetadata === null || serviceMetadata === void 0 ? void 0 : serviceMetadata.value)['destroy'] === 'function') {
try {
serviceMetadata.value.destroy();
}
catch (error) {
/** We simply ignore the errors from the destroy function. */
}
}
serviceMetadata.value = EMPTY_VALUE;
}
};
return ContainerInstance;
}());
/**
* Service container.
*/
var Container = /** @class */ (function () {
function Container() {
}
/**
* Gets a separate container instance for the given instance id.
*/
Container.of = function (containerId) {
if (containerId === void 0) { containerId = 'default'; }
if (containerId === 'default')
return this.globalInstance;
var container = this.instances.find(function (instance) { return instance.id === containerId; });
if (!container) {
container = new ContainerInstance(containerId);
this.instances.push(container);
// TODO: Why we are not reseting here? Let's reset here. (I have added the commented code.)
// container.reset();
}
return container;
};
Container.has = function (identifier) {
return this.globalInstance.has(identifier);
};
Container.get = function (identifier) {
return this.globalInstance.get(identifier);
};
Container.getMany = function (id) {
return this.globalInstance.getMany(id);
};
Container.set = function (identifierOrServiceMetadata, value) {
this.globalInstance.set(identifierOrServiceMetadata, value);
return this;
};
/**
* Removes services with a given service identifiers.
*/
Container.remove = function (identifierOrIdentifierArray) {
this.globalInstance.remove(identifierOrIdentifierArray);
return this;
};
/**
* Completely resets the container by removing all previously registered services and handlers from it.
*/
Container.reset = function (containerId) {
if (containerId === void 0) { containerId = 'default'; }
if (containerId == 'default') {
this.globalInstance.reset();
this.instances.forEach(function (instance) { return instance.reset(); });
}
else {
var instance = this.instances.find(function (instance) { return instance.id === containerId; });
if (instance) {
instance.reset();
this.instances.splice(this.instances.indexOf(instance), 1);
}
}
return this;
};
/**
* Registers a new handler.
*/
Container.registerHandler = function (handler) {
this.handlers.push(handler);
return this;
};
/**
* Helper method that imports given services.
*/
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
Container.import = function (services) {
return this;
};
/**
* All registered handlers. The @Inject() decorator uses handlers internally to mark a property for injection.
**/
Container.handlers = [];
/** Global container instance. */
Container.globalInstance = new ContainerInstance('default');
/** Other containers created using Container.of method. */
Container.instances = [];
return Container;
}());
var __extends$2 = (undefined && undefined.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
/**
* Thrown when DI cannot inject value into property decorated by @Inject decorator.
*/
var CannotInjectValueError = /** @class */ (function (_super) {
__extends$2(CannotInjectValueError, _super);
function CannotInjectValueError(target, propertyName) {
var _this = _super.call(this) || this;
_this.target = target;
_this.propertyName = propertyName;
_this.name = 'CannotInjectValueError';
return _this;
}
Object.defineProperty(CannotInjectValueError.prototype, "message", {
get: function () {
return ("Cannot inject value into \"" + this.target.constructor.name + "." + this.propertyName + "\". " +
"Please make sure you setup reflect-metadata properly and you don't use interfaces without service tokens as injection value.");
},
enumerable: false,
configurable: true
});
return CannotInjectValueError;
}(Error));
/**
* Helper function used in inject decorators to resolve the received identifier to
* an eager type when possible or to a lazy type when cyclic dependencies are possibly involved.
*
* @param typeOrIdentifier a service identifier or a function returning a type acting as service identifier or nothing
* @param target the class definition of the target of the decorator
* @param propertyName the name of the property in case of a PropertyDecorator
* @param index the index of the parameter in the constructor in case of ParameterDecorator
*/
function resolveToTypeWrapper(typeOrIdentifier, target, propertyName, index) {
/**
* ? We want to error out as soon as possible when looking up services to inject, however
* ? we cannot determine the type at decorator execution when cyclic dependencies are involved
* ? because calling the received `() => MyType` function right away would cause a JS error:
* ? "Cannot access 'MyType' before initialization", so we need to execute the function in the handler,
* ? when the classes are already created. To overcome this, we use a wrapper:
* ? - the lazyType is executed in the handler so we never have a JS error
* ? - the eagerType is checked when decorator is running and an error is raised if an unknown type is encountered
*/
var typeWrapper;
/** If requested type is explicitly set via a string ID or token, we set it explicitly. */
if ((typeOrIdentifier && typeof typeOrIdentifier === 'string') || typeOrIdentifier instanceof Token) {
typeWrapper = { eagerType: typeOrIdentifier, lazyType: function () { return typeOrIdentifier; } };
}
/** If requested type is explicitly set via a () => MyClassType format, we set it explicitly. */
if (typeOrIdentifier && typeof typeOrIdentifier === 'function') {
/** We set eagerType to null, preventing the raising of the CannotInjectValueError in decorators. */
typeWrapper = { eagerType: null, lazyType: function () { return typeOrIdentifier(); } };
}
/** If no explicit type is set and handler registered for a class property, we need to get the property type. */
if (!typeOrIdentifier && propertyName) {
var identifier_1 = Reflect.getMetadata('design:type', target, propertyName);
typeWrapper = { eagerType: identifier_1, lazyType: function () { return identifier_1; } };
}
/** If no explicit type is set and handler registered for a constructor parameter, we need to get the parameter types. */
if (!typeOrIdentifier && typeof index == 'number' && Number.isInteger(index)) {
var paramTypes = Reflect.getMetadata('design:paramtypes', target, propertyName);
/** It's not guaranteed, that we find any types for the constructor. */
var identifier_2 = paramTypes === null || paramTypes === void 0 ? void 0 : paramTypes[index];
typeWrapper = { eagerType: identifier_2, lazyType: function () { return identifier_2; } };
}
return typeWrapper;
}
function InjectMany(typeOrIdentifier) {
return function (target, propertyName, index) {
var typeWrapper = resolveToTypeWrapper(typeOrIdentifier, target, propertyName, index);
/** If no type was inferred, or the general Object type was inferred we throw an error. */
if (typeWrapper === undefined || typeWrapper.eagerType === undefined || typeWrapper.eagerType === Object) {
throw new CannotInjectValueError(target, propertyName);
}
Container.registerHandler({
object: target,
propertyName: propertyName,
index: index,
value: function (containerInstance) {
var evaluatedLazyType = typeWrapper.lazyType();
/** If no type was inferred lazily, or the general Object type was inferred we throw an error. */
if (evaluatedLazyType === undefined || evaluatedLazyType === Object) {
throw new CannotInjectValueError(target, propertyName);
}
return containerInstance.getMany(evaluatedLazyType);
},
});
};
}
function Inject(typeOrIdentifier) {
return function (target, propertyName, index) {
var typeWrapper = resolveToTypeWrapper(typeOrIdentifier, target, propertyName, index);
/** If no type was inferred, or the general Object type was inferred we throw an error. */
if (typeWrapper === undefined || typeWrapper.eagerType === undefined || typeWrapper.eagerType === Object) {
throw new CannotInjectValueError(target, propertyName);
}
Container.registerHandler({
object: target,
propertyName: propertyName,
index: index,
value: function (containerInstance) {
var evaluatedLazyType = typeWrapper.lazyType();
/** If no type was inferred lazily, or the general Object type was inferred we throw an error. */
if (evaluatedLazyType === undefined || evaluatedLazyType === Object) {
throw new CannotInjectValueError(target, propertyName);
}
return containerInstance.get(evaluatedLazyType);
},
});
};
}
function Service(optionsOrServiceIdentifier) {
return function (targetConstructor) {
var serviceMetadata = {
id: targetConstructor,
// TODO: Let's investigate why we receive Function type instead of a constructable.
type: targetConstructor,
factory: undefined,
multiple: false,
global: false,
eager: false,
transient: false,
value: EMPTY_VALUE,
};
if (optionsOrServiceIdentifier instanceof Token || typeof optionsOrServiceIdentifier === 'string') {
/** We received a Token or string ID. */
serviceMetadata.id = optionsOrServiceIdentifier;
}
else if (optionsOrServiceIdentifier) {
/** We received a ServiceOptions object. */
serviceMetadata.id = optionsOrServiceIdentifier.id || targetConstructor;
serviceMetadata.factory = optionsOrServiceIdentifier.factory || undefined;
serviceMetadata.multiple = optionsOrServiceIdentifier.multiple || false;
serviceMetadata.global = optionsOrServiceIdentifier.global || false;
serviceMetadata.eager = optionsOrServiceIdentifier.eager || false;
serviceMetadata.transient = optionsOrServiceIdentifier.transient || false;
}
Container.set(serviceMetadata);
};
}
/**
* We have a hard dependency on reflect-metadata package.
* Without the dependency lookup wont work. So we should warn the users
* when it's not loaded.
*/
exports.CannotInjectValueError = CannotInjectValueError;
exports.CannotInstantiateValueError = CannotInstantiateValueError;
exports.Container = Container;
exports.ContainerInstance = ContainerInstance;
exports.Inject = Inject;
exports.InjectMany = InjectMany;
exports.Service = Service;
exports.ServiceNotFoundError = ServiceNotFoundError;
exports.Token = Token;
exports.default = Container;
Object.defineProperty(exports, '__esModule', { value: true });
})));
//# sourceMappingURL=typedi.umd.js.map
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+304
View File
@@ -0,0 +1,304 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ContainerInstance = void 0;
const container_class_1 = require("./container.class");
const service_not_found_error_1 = require("./error/service-not-found.error");
const cannot_instantiate_value_error_1 = require("./error/cannot-instantiate-value.error");
const token_class_1 = require("./token.class");
const empty_const_1 = require("./empty.const");
/**
* TypeDI can have multiple containers.
* One container is ContainerInstance.
*/
class ContainerInstance {
constructor(id) {
/** All registered services in the container. */
this.services = [];
this.id = id;
}
has(identifier) {
return !!this.findService(identifier);
}
get(identifier) {
const globalContainer = container_class_1.Container.of(undefined);
const globalService = globalContainer.findService(identifier);
const scopedService = this.findService(identifier);
if (globalService && globalService.global === true)
return this.getServiceValue(globalService);
if (scopedService)
return this.getServiceValue(scopedService);
/** If it's the first time requested in the child container we load it from parent and set it. */
if (globalService && this !== globalContainer) {
const clonedService = { ...globalService };
clonedService.value = empty_const_1.EMPTY_VALUE;
/**
* We need to immediately set the empty value from the root container
* to prevent infinite lookup in cyclic dependencies.
*/
this.set(clonedService);
const value = this.getServiceValue(clonedService);
this.set({ ...clonedService, value });
return value;
}
if (globalService)
return this.getServiceValue(globalService);
throw new service_not_found_error_1.ServiceNotFoundError(identifier);
}
getMany(identifier) {
return this.findAllServices(identifier).map(service => this.getServiceValue(service));
}
set(identifierOrServiceMetadata, value) {
if (identifierOrServiceMetadata instanceof Array) {
identifierOrServiceMetadata.forEach(data => this.set(data));
return this;
}
if (typeof identifierOrServiceMetadata === 'string' || identifierOrServiceMetadata instanceof token_class_1.Token) {
return this.set({
id: identifierOrServiceMetadata,
type: null,
value: value,
factory: undefined,
global: false,
multiple: false,
eager: false,
transient: false,
});
}
if (typeof identifierOrServiceMetadata === 'function') {
return this.set({
id: identifierOrServiceMetadata,
// TODO: remove explicit casting
type: identifierOrServiceMetadata,
value: value,
factory: undefined,
global: false,
multiple: false,
eager: false,
transient: false,
});
}
const newService = {
id: new token_class_1.Token('UNREACHABLE'),
type: null,
factory: undefined,
value: empty_const_1.EMPTY_VALUE,
global: false,
multiple: false,
eager: false,
transient: false,
...identifierOrServiceMetadata,
};
const service = this.findService(newService.id);
if (service && service.multiple !== true) {
Object.assign(service, newService);
}
else {
this.services.push(newService);
}
if (newService.eager) {
this.get(newService.id);
}
return this;
}
/**
* Removes services with a given service identifiers.
*/
remove(identifierOrIdentifierArray) {
if (Array.isArray(identifierOrIdentifierArray)) {
identifierOrIdentifierArray.forEach(id => this.remove(id));
}
else {
this.services = this.services.filter(service => {
if (service.id === identifierOrIdentifierArray) {
this.destroyServiceInstance(service);
return false;
}
return true;
});
}
return this;
}
/**
* Completely resets the container by removing all previously registered services from it.
*/
reset(options = { strategy: 'resetValue' }) {
switch (options.strategy) {
case 'resetValue':
this.services.forEach(service => this.destroyServiceInstance(service));
break;
case 'resetServices':
this.services.forEach(service => this.destroyServiceInstance(service));
this.services = [];
break;
default:
throw new Error('Received invalid reset strategy.');
}
return this;
}
/**
* Returns all services registered with the given identifier.
*/
findAllServices(identifier) {
return this.services.filter(service => service.id === identifier);
}
/**
* Finds registered service in the with a given service identifier.
*/
findService(identifier) {
return this.services.find(service => service.id === identifier);
}
/**
* Gets the value belonging to `serviceMetadata.id`.
*
* - if `serviceMetadata.value` is already set it is immediately returned
* - otherwise the requested type is resolved to the value saved to `serviceMetadata.value` and returned
*/
getServiceValue(serviceMetadata) {
var _a;
let value = empty_const_1.EMPTY_VALUE;
/**
* If the service value has been set to anything prior to this call we return that value.
* NOTE: This part builds on the assumption that transient dependencies has no value set ever.
*/
if (serviceMetadata.value !== empty_const_1.EMPTY_VALUE) {
return serviceMetadata.value;
}
/** If both factory and type is missing, we cannot resolve the requested ID. */
if (!serviceMetadata.factory && !serviceMetadata.type) {
throw new cannot_instantiate_value_error_1.CannotInstantiateValueError(serviceMetadata.id);
}
/**
* If a factory is defined it takes priority over creating an instance via `new`.
* The return value of the factory is not checked, we believe by design that the user knows what he/she is doing.
*/
if (serviceMetadata.factory) {
/**
* If we received the factory in the [Constructable<Factory>, "functionName"] format, we need to create the
* factory first and then call the specified function on it.
*/
if (serviceMetadata.factory instanceof Array) {
let factoryInstance;
try {
/** Try to get the factory from TypeDI first, if failed, fall back to simply initiating the class. */
factoryInstance = this.get(serviceMetadata.factory[0]);
}
catch (error) {
if (error instanceof service_not_found_error_1.ServiceNotFoundError) {
factoryInstance = new serviceMetadata.factory[0]();
}
else {
throw error;
}
}
value = factoryInstance[serviceMetadata.factory[1]](this, serviceMetadata.id);
}
else {
/** If only a simple function was provided we simply call it. */
value = serviceMetadata.factory(this, serviceMetadata.id);
}
}
/**
* If no factory was provided and only then, we create the instance from the type if it was set.
*/
if (!serviceMetadata.factory && serviceMetadata.type) {
const constructableTargetType = serviceMetadata.type;
// setup constructor parameters for a newly initialized service
const paramTypes = ((_a = Reflect) === null || _a === void 0 ? void 0 : _a.getMetadata('design:paramtypes', constructableTargetType)) || [];
const params = this.initializeParams(constructableTargetType, paramTypes);
// "extra feature" - always pass container instance as the last argument to the service function
// this allows us to support javascript where we don't have decorators and emitted metadata about dependencies
// need to be injected, and user can use provided container to get instances he needs
params.push(this);
value = new constructableTargetType(...params);
// TODO: Calling this here, leads to infinite loop, because @Inject decorator registerds a handler
// TODO: which calls Container.get, which will check if the requested type has a value set and if not
// TODO: it will start the instantiation process over. So this is currently called outside of the if branch
// TODO: after the current value has been assigned to the serviceMetadata.
// this.applyPropertyHandlers(constructableTargetType, value as Constructable<unknown>);
}
/** If this is not a transient service, and we resolved something, then we set it as the value. */
if (!serviceMetadata.transient && value !== empty_const_1.EMPTY_VALUE) {
serviceMetadata.value = value;
}
if (value === empty_const_1.EMPTY_VALUE) {
/** This branch should never execute, but better to be safe than sorry. */
throw new cannot_instantiate_value_error_1.CannotInstantiateValueError(serviceMetadata.id);
}
if (serviceMetadata.type) {
this.applyPropertyHandlers(serviceMetadata.type, value);
}
return value;
}
/**
* Initializes all parameter types for a given target service class.
*/
initializeParams(target, paramTypes) {
return paramTypes.map((paramType, index) => {
const paramHandler = container_class_1.Container.handlers.find(handler => {
/**
* @Inject()-ed values are stored as parameter handlers and they reference their target
* when created. So when a class is extended the @Inject()-ed values are not inherited
* because the handler still points to the old object only.
*
* As a quick fix a single level parent lookup is added via `Object.getPrototypeOf(target)`,
* however this should be updated to a more robust solution.
*
* TODO: Add proper inheritance handling: either copy the handlers when a class is registered what
* TODO: has it's parent already registered as dependency or make the lookup search up to the base Object.
*/
return ((handler.object === target || handler.object === Object.getPrototypeOf(target)) && handler.index === index);
});
if (paramHandler)
return paramHandler.value(this);
if (paramType && paramType.name && !this.isPrimitiveParamType(paramType.name)) {
return this.get(paramType);
}
return undefined;
});
}
/**
* Checks if given parameter type is primitive type or not.
*/
isPrimitiveParamType(paramTypeName) {
return ['string', 'boolean', 'number', 'object'].includes(paramTypeName.toLowerCase());
}
/**
* Applies all registered handlers on a given target class.
*/
applyPropertyHandlers(target, instance) {
container_class_1.Container.handlers.forEach(handler => {
if (typeof handler.index === 'number')
return;
if (handler.object.constructor !== target && !(target.prototype instanceof handler.object.constructor))
return;
if (handler.propertyName) {
instance[handler.propertyName] = handler.value(this);
}
});
}
/**
* Checks if the given service metadata contains a destroyable service instance and destroys it in place. If the service
* contains a callable function named `destroy` it is called but not awaited and the return value is ignored..
*
* @param serviceMetadata the service metadata containing the instance to destroy
* @param force when true the service will be always destroyed even if it's cannot be re-created
*/
destroyServiceInstance(serviceMetadata, force = false) {
/** We reset value only if we can re-create it (aka type or factory exists). */
const shouldResetValue = force || !!serviceMetadata.type || !!serviceMetadata.factory;
if (shouldResetValue) {
/** If we wound a function named destroy we call it without any params. */
if (typeof (serviceMetadata === null || serviceMetadata === void 0 ? void 0 : serviceMetadata.value)['destroy'] === 'function') {
try {
serviceMetadata.value.destroy();
}
catch (error) {
/** We simply ignore the errors from the destroy function. */
}
}
serviceMetadata.value = empty_const_1.EMPTY_VALUE;
}
}
}
exports.ContainerInstance = ContainerInstance;
//# sourceMappingURL=container-instance.class.js.map
File diff suppressed because one or more lines are too long
+85
View File
@@ -0,0 +1,85 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Container = void 0;
const container_instance_class_1 = require("./container-instance.class");
/**
* Service container.
*/
class Container {
/**
* Gets a separate container instance for the given instance id.
*/
static of(containerId = 'default') {
if (containerId === 'default')
return this.globalInstance;
let container = this.instances.find(instance => instance.id === containerId);
if (!container) {
container = new container_instance_class_1.ContainerInstance(containerId);
this.instances.push(container);
// TODO: Why we are not reseting here? Let's reset here. (I have added the commented code.)
// container.reset();
}
return container;
}
static has(identifier) {
return this.globalInstance.has(identifier);
}
static get(identifier) {
return this.globalInstance.get(identifier);
}
static getMany(id) {
return this.globalInstance.getMany(id);
}
static set(identifierOrServiceMetadata, value) {
this.globalInstance.set(identifierOrServiceMetadata, value);
return this;
}
/**
* Removes services with a given service identifiers.
*/
static remove(identifierOrIdentifierArray) {
this.globalInstance.remove(identifierOrIdentifierArray);
return this;
}
/**
* Completely resets the container by removing all previously registered services and handlers from it.
*/
static reset(containerId = 'default') {
if (containerId == 'default') {
this.globalInstance.reset();
this.instances.forEach(instance => instance.reset());
}
else {
const instance = this.instances.find(instance => instance.id === containerId);
if (instance) {
instance.reset();
this.instances.splice(this.instances.indexOf(instance), 1);
}
}
return this;
}
/**
* Registers a new handler.
*/
static registerHandler(handler) {
this.handlers.push(handler);
return this;
}
/**
* Helper method that imports given services.
*/
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
static import(services) {
return this;
}
}
exports.Container = Container;
/**
* All registered handlers. The @Inject() decorator uses handlers internally to mark a property for injection.
**/
Container.handlers = [];
/** Global container instance. */
Container.globalInstance = new container_instance_class_1.ContainerInstance('default');
/** Other containers created using Container.of method. */
Container.instances = [];
//# sourceMappingURL=container.class.js.map
File diff suppressed because one or more lines are too long
+30
View File
@@ -0,0 +1,30 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.InjectMany = void 0;
const container_class_1 = require("../container.class");
const cannot_inject_value_error_1 = require("../error/cannot-inject-value.error");
const resolve_to_type_wrapper_util_1 = require("../utils/resolve-to-type-wrapper.util");
function InjectMany(typeOrIdentifier) {
return function (target, propertyName, index) {
const typeWrapper = resolve_to_type_wrapper_util_1.resolveToTypeWrapper(typeOrIdentifier, target, propertyName, index);
/** If no type was inferred, or the general Object type was inferred we throw an error. */
if (typeWrapper === undefined || typeWrapper.eagerType === undefined || typeWrapper.eagerType === Object) {
throw new cannot_inject_value_error_1.CannotInjectValueError(target, propertyName);
}
container_class_1.Container.registerHandler({
object: target,
propertyName: propertyName,
index: index,
value: containerInstance => {
const evaluatedLazyType = typeWrapper.lazyType();
/** If no type was inferred lazily, or the general Object type was inferred we throw an error. */
if (evaluatedLazyType === undefined || evaluatedLazyType === Object) {
throw new cannot_inject_value_error_1.CannotInjectValueError(target, propertyName);
}
return containerInstance.getMany(evaluatedLazyType);
},
});
};
}
exports.InjectMany = InjectMany;
//# sourceMappingURL=inject-many.decorator.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"inject-many.decorator.js","sourceRoot":"","sources":["../../../src/decorators/inject-many.decorator.ts"],"names":[],"mappings":";;;AAAA,wDAA+C;AAE/C,kFAA4E;AAC5E,wFAA6E;AAW7E,SAAgB,UAAU,CACxB,gBAA0F;IAE1F,OAAO,UAAU,MAAc,EAAE,YAA6B,EAAE,KAAc;QAC5E,MAAM,WAAW,GAAG,mDAAoB,CAAC,gBAAgB,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC;QAExF,0FAA0F;QAC1F,IAAI,WAAW,KAAK,SAAS,IAAI,WAAW,CAAC,SAAS,KAAK,SAAS,IAAI,WAAW,CAAC,SAAS,KAAK,MAAM,EAAE;YACxG,MAAM,IAAI,kDAAsB,CAAC,MAAgC,EAAE,YAAsB,CAAC,CAAC;SAC5F;QAED,2BAAS,CAAC,eAAe,CAAC;YACxB,MAAM,EAAE,MAAgC;YACxC,YAAY,EAAE,YAAsB;YACpC,KAAK,EAAE,KAAK;YACZ,KAAK,EAAE,iBAAiB,CAAC,EAAE;gBACzB,MAAM,iBAAiB,GAAG,WAAW,CAAC,QAAQ,EAAE,CAAC;gBAEjD,iGAAiG;gBACjG,IAAI,iBAAiB,KAAK,SAAS,IAAI,iBAAiB,KAAK,MAAM,EAAE;oBACnE,MAAM,IAAI,kDAAsB,CAAC,MAAgC,EAAE,YAAsB,CAAC,CAAC;iBAC5F;gBAED,OAAO,iBAAiB,CAAC,OAAO,CAAU,iBAAiB,CAAC,CAAC;YAC/D,CAAC;SACF,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC;AA3BD,gCA2BC","sourcesContent":["import { Container } from '../container.class';\nimport { Token } from '../token.class';\nimport { CannotInjectValueError } from '../error/cannot-inject-value.error';\nimport { resolveToTypeWrapper } from '../utils/resolve-to-type-wrapper.util';\nimport { Constructable } from '../types/constructable.type';\nimport { ServiceIdentifier } from '../types/service-identifier.type';\n\n/**\n * Injects a list of services into a class property or constructor parameter.\n */\nexport function InjectMany(): Function;\nexport function InjectMany(type?: (type?: any) => Function): Function;\nexport function InjectMany(serviceName?: string): Function;\nexport function InjectMany(token: Token<any>): Function;\nexport function InjectMany(\n typeOrIdentifier?: ((type?: never) => Constructable<unknown>) | ServiceIdentifier<unknown>\n): Function {\n return function (target: Object, propertyName: string | Symbol, index?: number): void {\n const typeWrapper = resolveToTypeWrapper(typeOrIdentifier, target, propertyName, index);\n\n /** If no type was inferred, or the general Object type was inferred we throw an error. */\n if (typeWrapper === undefined || typeWrapper.eagerType === undefined || typeWrapper.eagerType === Object) {\n throw new CannotInjectValueError(target as Constructable<unknown>, propertyName as string);\n }\n\n Container.registerHandler({\n object: target as Constructable<unknown>,\n propertyName: propertyName as string,\n index: index,\n value: containerInstance => {\n const evaluatedLazyType = typeWrapper.lazyType();\n\n /** If no type was inferred lazily, or the general Object type was inferred we throw an error. */\n if (evaluatedLazyType === undefined || evaluatedLazyType === Object) {\n throw new CannotInjectValueError(target as Constructable<unknown>, propertyName as string);\n }\n\n return containerInstance.getMany<unknown>(evaluatedLazyType);\n },\n });\n };\n}\n"]}
+30
View File
@@ -0,0 +1,30 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Inject = void 0;
const container_class_1 = require("../container.class");
const cannot_inject_value_error_1 = require("../error/cannot-inject-value.error");
const resolve_to_type_wrapper_util_1 = require("../utils/resolve-to-type-wrapper.util");
function Inject(typeOrIdentifier) {
return function (target, propertyName, index) {
const typeWrapper = resolve_to_type_wrapper_util_1.resolveToTypeWrapper(typeOrIdentifier, target, propertyName, index);
/** If no type was inferred, or the general Object type was inferred we throw an error. */
if (typeWrapper === undefined || typeWrapper.eagerType === undefined || typeWrapper.eagerType === Object) {
throw new cannot_inject_value_error_1.CannotInjectValueError(target, propertyName);
}
container_class_1.Container.registerHandler({
object: target,
propertyName: propertyName,
index: index,
value: containerInstance => {
const evaluatedLazyType = typeWrapper.lazyType();
/** If no type was inferred lazily, or the general Object type was inferred we throw an error. */
if (evaluatedLazyType === undefined || evaluatedLazyType === Object) {
throw new cannot_inject_value_error_1.CannotInjectValueError(target, propertyName);
}
return containerInstance.get(evaluatedLazyType);
},
});
};
}
exports.Inject = Inject;
//# sourceMappingURL=inject.decorator.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"inject.decorator.js","sourceRoot":"","sources":["../../../src/decorators/inject.decorator.ts"],"names":[],"mappings":";;;AAAA,wDAA+C;AAE/C,kFAA4E;AAG5E,wFAA6E;AAS7E,SAAgB,MAAM,CACpB,gBAA0F;IAE1F,OAAO,UAAU,MAAc,EAAE,YAA6B,EAAE,KAAc;QAC5E,MAAM,WAAW,GAAG,mDAAoB,CAAC,gBAAgB,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC;QAExF,0FAA0F;QAC1F,IAAI,WAAW,KAAK,SAAS,IAAI,WAAW,CAAC,SAAS,KAAK,SAAS,IAAI,WAAW,CAAC,SAAS,KAAK,MAAM,EAAE;YACxG,MAAM,IAAI,kDAAsB,CAAC,MAAgC,EAAE,YAAsB,CAAC,CAAC;SAC5F;QAED,2BAAS,CAAC,eAAe,CAAC;YACxB,MAAM,EAAE,MAAgC;YACxC,YAAY,EAAE,YAAsB;YACpC,KAAK,EAAE,KAAK;YACZ,KAAK,EAAE,iBAAiB,CAAC,EAAE;gBACzB,MAAM,iBAAiB,GAAG,WAAW,CAAC,QAAQ,EAAE,CAAC;gBAEjD,iGAAiG;gBACjG,IAAI,iBAAiB,KAAK,SAAS,IAAI,iBAAiB,KAAK,MAAM,EAAE;oBACnE,MAAM,IAAI,kDAAsB,CAAC,MAAgC,EAAE,YAAsB,CAAC,CAAC;iBAC5F;gBAED,OAAO,iBAAiB,CAAC,GAAG,CAAU,iBAAiB,CAAC,CAAC;YAC3D,CAAC;SACF,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC;AA3BD,wBA2BC","sourcesContent":["import { Container } from '../container.class';\nimport { Token } from '../token.class';\nimport { CannotInjectValueError } from '../error/cannot-inject-value.error';\nimport { ServiceIdentifier } from '../types/service-identifier.type';\nimport { Constructable } from '../types/constructable.type';\nimport { resolveToTypeWrapper } from '../utils/resolve-to-type-wrapper.util';\n\n/**\n * Injects a service into a class property or constructor parameter.\n */\nexport function Inject(): Function;\nexport function Inject(typeFn: (type?: never) => Constructable<unknown>): Function;\nexport function Inject(serviceName?: string): Function;\nexport function Inject(token: Token<unknown>): Function;\nexport function Inject(\n typeOrIdentifier?: ((type?: never) => Constructable<unknown>) | ServiceIdentifier<unknown>\n): ParameterDecorator | PropertyDecorator {\n return function (target: Object, propertyName: string | Symbol, index?: number): void {\n const typeWrapper = resolveToTypeWrapper(typeOrIdentifier, target, propertyName, index);\n\n /** If no type was inferred, or the general Object type was inferred we throw an error. */\n if (typeWrapper === undefined || typeWrapper.eagerType === undefined || typeWrapper.eagerType === Object) {\n throw new CannotInjectValueError(target as Constructable<unknown>, propertyName as string);\n }\n\n Container.registerHandler({\n object: target as Constructable<unknown>,\n propertyName: propertyName as string,\n index: index,\n value: containerInstance => {\n const evaluatedLazyType = typeWrapper.lazyType();\n\n /** If no type was inferred lazily, or the general Object type was inferred we throw an error. */\n if (evaluatedLazyType === undefined || evaluatedLazyType === Object) {\n throw new CannotInjectValueError(target as Constructable<unknown>, propertyName as string);\n }\n\n return containerInstance.get<unknown>(evaluatedLazyType);\n },\n });\n };\n}\n"]}
+37
View File
@@ -0,0 +1,37 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Service = void 0;
const container_class_1 = require("../container.class");
const token_class_1 = require("../token.class");
const empty_const_1 = require("../empty.const");
function Service(optionsOrServiceIdentifier) {
return targetConstructor => {
const serviceMetadata = {
id: targetConstructor,
// TODO: Let's investigate why we receive Function type instead of a constructable.
type: targetConstructor,
factory: undefined,
multiple: false,
global: false,
eager: false,
transient: false,
value: empty_const_1.EMPTY_VALUE,
};
if (optionsOrServiceIdentifier instanceof token_class_1.Token || typeof optionsOrServiceIdentifier === 'string') {
/** We received a Token or string ID. */
serviceMetadata.id = optionsOrServiceIdentifier;
}
else if (optionsOrServiceIdentifier) {
/** We received a ServiceOptions object. */
serviceMetadata.id = optionsOrServiceIdentifier.id || targetConstructor;
serviceMetadata.factory = optionsOrServiceIdentifier.factory || undefined;
serviceMetadata.multiple = optionsOrServiceIdentifier.multiple || false;
serviceMetadata.global = optionsOrServiceIdentifier.global || false;
serviceMetadata.eager = optionsOrServiceIdentifier.eager || false;
serviceMetadata.transient = optionsOrServiceIdentifier.transient || false;
}
container_class_1.Container.set(serviceMetadata);
};
}
exports.Service = Service;
//# sourceMappingURL=service.decorator.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"service.decorator.js","sourceRoot":"","sources":["../../../src/decorators/service.decorator.ts"],"names":[],"mappings":";;;AAAA,wDAA+C;AAC/C,gDAAuC;AAGvC,gDAA6C;AAU7C,SAAgB,OAAO,CAAI,0BAAoE;IAC7F,OAAO,iBAAiB,CAAC,EAAE;QACzB,MAAM,eAAe,GAAuB;YAC1C,EAAE,EAAE,iBAAiB;YACrB,mFAAmF;YACnF,IAAI,EAAG,iBAAiD;YACxD,OAAO,EAAE,SAAS;YAClB,QAAQ,EAAE,KAAK;YACf,MAAM,EAAE,KAAK;YACb,KAAK,EAAE,KAAK;YACZ,SAAS,EAAE,KAAK;YAChB,KAAK,EAAE,yBAAW;SACnB,CAAC;QAEF,IAAI,0BAA0B,YAAY,mBAAK,IAAI,OAAO,0BAA0B,KAAK,QAAQ,EAAE;YACjG,wCAAwC;YACxC,eAAe,CAAC,EAAE,GAAG,0BAA0B,CAAC;SACjD;aAAM,IAAI,0BAA0B,EAAE;YACrC,2CAA2C;YAC3C,eAAe,CAAC,EAAE,GAAI,0BAA8C,CAAC,EAAE,IAAI,iBAAiB,CAAC;YAC7F,eAAe,CAAC,OAAO,GAAI,0BAA8C,CAAC,OAAO,IAAI,SAAS,CAAC;YAC/F,eAAe,CAAC,QAAQ,GAAI,0BAA8C,CAAC,QAAQ,IAAI,KAAK,CAAC;YAC7F,eAAe,CAAC,MAAM,GAAI,0BAA8C,CAAC,MAAM,IAAI,KAAK,CAAC;YACzF,eAAe,CAAC,KAAK,GAAI,0BAA8C,CAAC,KAAK,IAAI,KAAK,CAAC;YACvF,eAAe,CAAC,SAAS,GAAI,0BAA8C,CAAC,SAAS,IAAI,KAAK,CAAC;SAChG;QAED,2BAAS,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACjC,CAAC,CAAC;AACJ,CAAC;AA7BD,0BA6BC","sourcesContent":["import { Container } from '../container.class';\nimport { Token } from '../token.class';\nimport { ServiceMetadata } from '../interfaces/service-metadata.interface';\nimport { ServiceOptions } from '../interfaces/service-options.interface';\nimport { EMPTY_VALUE } from '../empty.const';\nimport { Constructable } from '../types/constructable.type';\n\n/**\n * Marks class as a service that can be injected using Container.\n */\nexport function Service<T = unknown>(): Function;\nexport function Service<T = unknown>(name: string): Function;\nexport function Service<T = unknown>(token: Token<unknown>): Function;\nexport function Service<T = unknown>(options?: ServiceOptions<T>): Function;\nexport function Service<T>(optionsOrServiceIdentifier?: ServiceOptions<T> | Token<any> | string): ClassDecorator {\n return targetConstructor => {\n const serviceMetadata: ServiceMetadata<T> = {\n id: targetConstructor,\n // TODO: Let's investigate why we receive Function type instead of a constructable.\n type: (targetConstructor as unknown) as Constructable<T>,\n factory: undefined,\n multiple: false,\n global: false,\n eager: false,\n transient: false,\n value: EMPTY_VALUE,\n };\n\n if (optionsOrServiceIdentifier instanceof Token || typeof optionsOrServiceIdentifier === 'string') {\n /** We received a Token or string ID. */\n serviceMetadata.id = optionsOrServiceIdentifier;\n } else if (optionsOrServiceIdentifier) {\n /** We received a ServiceOptions object. */\n serviceMetadata.id = (optionsOrServiceIdentifier as ServiceMetadata).id || targetConstructor;\n serviceMetadata.factory = (optionsOrServiceIdentifier as ServiceMetadata).factory || undefined;\n serviceMetadata.multiple = (optionsOrServiceIdentifier as ServiceMetadata).multiple || false;\n serviceMetadata.global = (optionsOrServiceIdentifier as ServiceMetadata).global || false;\n serviceMetadata.eager = (optionsOrServiceIdentifier as ServiceMetadata).eager || false;\n serviceMetadata.transient = (optionsOrServiceIdentifier as ServiceMetadata).transient || false;\n }\n\n Container.set(serviceMetadata);\n };\n}\n"]}
+5
View File
@@ -0,0 +1,5 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EMPTY_VALUE = void 0;
exports.EMPTY_VALUE = Symbol('EMPTY_VALUE');
//# sourceMappingURL=empty.const.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"empty.const.js","sourceRoot":"","sources":["../../src/empty.const.ts"],"names":[],"mappings":";;;AAAa,QAAA,WAAW,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC","sourcesContent":["export const EMPTY_VALUE = Symbol('EMPTY_VALUE');\n"]}
+20
View File
@@ -0,0 +1,20 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CannotInjectValueError = void 0;
/**
* Thrown when DI cannot inject value into property decorated by @Inject decorator.
*/
class CannotInjectValueError extends Error {
constructor(target, propertyName) {
super();
this.target = target;
this.propertyName = propertyName;
this.name = 'CannotInjectValueError';
}
get message() {
return (`Cannot inject value into "${this.target.constructor.name}.${this.propertyName}". ` +
`Please make sure you setup reflect-metadata properly and you don't use interfaces without service tokens as injection value.`);
}
}
exports.CannotInjectValueError = CannotInjectValueError;
//# sourceMappingURL=cannot-inject-value.error.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"cannot-inject-value.error.js","sourceRoot":"","sources":["../../../src/error/cannot-inject-value.error.ts"],"names":[],"mappings":";;;AAEA;;GAEG;AACH,MAAa,sBAAuB,SAAQ,KAAK;IAU/C,YAAoB,MAA8B,EAAU,YAAoB;QAC9E,KAAK,EAAE,CAAC;QADU,WAAM,GAAN,MAAM,CAAwB;QAAU,iBAAY,GAAZ,YAAY,CAAQ;QATzE,SAAI,GAAG,wBAAwB,CAAC;IAWvC,CAAC;IATD,IAAI,OAAO;QACT,OAAO,CACL,6BAA6B,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,IAAI,IAAI,CAAC,YAAY,KAAK;YACnF,8HAA8H,CAC/H,CAAC;IACJ,CAAC;CAKF;AAbD,wDAaC","sourcesContent":["import { Constructable } from '../types/constructable.type';\n\n/**\n * Thrown when DI cannot inject value into property decorated by @Inject decorator.\n */\nexport class CannotInjectValueError extends Error {\n public name = 'CannotInjectValueError';\n\n get message(): string {\n return (\n `Cannot inject value into \"${this.target.constructor.name}.${this.propertyName}\". ` +\n `Please make sure you setup reflect-metadata properly and you don't use interfaces without service tokens as injection value.`\n );\n }\n\n constructor(private target: Constructable<unknown>, private propertyName: string) {\n super();\n }\n}\n"]}
+34
View File
@@ -0,0 +1,34 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CannotInstantiateValueError = void 0;
const token_class_1 = require("../token.class");
/**
* Thrown when DI cannot inject value into property decorated by @Inject decorator.
*/
class CannotInstantiateValueError extends Error {
constructor(identifier) {
var _a, _b;
super();
this.name = 'CannotInstantiateValueError';
/** Normalized identifier name used in the error message. */
this.normalizedIdentifier = '<UNKNOWN_IDENTIFIER>';
// TODO: Extract this to a helper function and share between this and NotFoundError.
if (typeof identifier === 'string') {
this.normalizedIdentifier = identifier;
}
else if (identifier instanceof token_class_1.Token) {
this.normalizedIdentifier = `Token<${identifier.name || 'UNSET_NAME'}>`;
}
else if (identifier && (identifier.name || ((_a = identifier.prototype) === null || _a === void 0 ? void 0 : _a.name))) {
this.normalizedIdentifier =
`MaybeConstructable<${identifier.name}>` ||
`MaybeConstructable<${(_b = identifier.prototype) === null || _b === void 0 ? void 0 : _b.name}>`;
}
}
get message() {
return (`Cannot instantiate the requested value for the "${this.normalizedIdentifier}" identifier. ` +
`The related metadata doesn't contain a factory or a type to instantiate.`);
}
}
exports.CannotInstantiateValueError = CannotInstantiateValueError;
//# sourceMappingURL=cannot-instantiate-value.error.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"cannot-instantiate-value.error.js","sourceRoot":"","sources":["../../../src/error/cannot-instantiate-value.error.ts"],"names":[],"mappings":";;;AACA,gDAAuC;AAEvC;;GAEG;AACH,MAAa,2BAA4B,SAAQ,KAAK;IAapD,YAAY,UAA6B;;QACvC,KAAK,EAAE,CAAC;QAbH,SAAI,GAAG,6BAA6B,CAAC;QAE5C,4DAA4D;QACpD,yBAAoB,GAAW,sBAAsB,CAAC;QAY5D,oFAAoF;QACpF,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE;YAClC,IAAI,CAAC,oBAAoB,GAAG,UAAU,CAAC;SACxC;aAAM,IAAI,UAAU,YAAY,mBAAK,EAAE;YACtC,IAAI,CAAC,oBAAoB,GAAG,SAAS,UAAU,CAAC,IAAI,IAAI,YAAY,GAAG,CAAC;SACzE;aAAM,IAAI,UAAU,IAAI,CAAC,UAAU,CAAC,IAAI,WAAI,UAAU,CAAC,SAAS,0CAAE,IAAI,CAAA,CAAC,EAAE;YACxE,IAAI,CAAC,oBAAoB;gBACvB,sBAAsB,UAAU,CAAC,IAAI,GAAG;oBACxC,sBAAsB,MAAC,UAAU,CAAC,SAA8B,0CAAE,IAAI,GAAG,CAAC;SAC7E;IACH,CAAC;IApBD,IAAI,OAAO;QACT,OAAO,CACL,mDAAmD,IAAI,CAAC,oBAAoB,gBAAgB;YAC5F,0EAA0E,CAC3E,CAAC;IACJ,CAAC;CAgBF;AA3BD,kEA2BC","sourcesContent":["import { ServiceIdentifier } from '../types/service-identifier.type';\nimport { Token } from '../token.class';\n\n/**\n * Thrown when DI cannot inject value into property decorated by @Inject decorator.\n */\nexport class CannotInstantiateValueError extends Error {\n public name = 'CannotInstantiateValueError';\n\n /** Normalized identifier name used in the error message. */\n private normalizedIdentifier: string = '<UNKNOWN_IDENTIFIER>';\n\n get message(): string {\n return (\n `Cannot instantiate the requested value for the \"${this.normalizedIdentifier}\" identifier. ` +\n `The related metadata doesn't contain a factory or a type to instantiate.`\n );\n }\n\n constructor(identifier: ServiceIdentifier) {\n super();\n\n // TODO: Extract this to a helper function and share between this and NotFoundError.\n if (typeof identifier === 'string') {\n this.normalizedIdentifier = identifier;\n } else if (identifier instanceof Token) {\n this.normalizedIdentifier = `Token<${identifier.name || 'UNSET_NAME'}>`;\n } else if (identifier && (identifier.name || identifier.prototype?.name)) {\n this.normalizedIdentifier =\n `MaybeConstructable<${identifier.name}>` ||\n `MaybeConstructable<${(identifier.prototype as { name: string })?.name}>`;\n }\n }\n}\n"]}
+33
View File
@@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ServiceNotFoundError = void 0;
const token_class_1 = require("../token.class");
/**
* Thrown when requested service was not found.
*/
class ServiceNotFoundError extends Error {
constructor(identifier) {
var _a, _b;
super();
this.name = 'ServiceNotFoundError';
/** Normalized identifier name used in the error message. */
this.normalizedIdentifier = '<UNKNOWN_IDENTIFIER>';
if (typeof identifier === 'string') {
this.normalizedIdentifier = identifier;
}
else if (identifier instanceof token_class_1.Token) {
this.normalizedIdentifier = `Token<${identifier.name || 'UNSET_NAME'}>`;
}
else if (identifier && (identifier.name || ((_a = identifier.prototype) === null || _a === void 0 ? void 0 : _a.name))) {
this.normalizedIdentifier =
`MaybeConstructable<${identifier.name}>` ||
`MaybeConstructable<${(_b = identifier.prototype) === null || _b === void 0 ? void 0 : _b.name}>`;
}
}
get message() {
return (`Service with "${this.normalizedIdentifier}" identifier was not found in the container. ` +
`Register it before usage via explicitly calling the "Container.set" function or using the "@Service()" decorator.`);
}
}
exports.ServiceNotFoundError = ServiceNotFoundError;
//# sourceMappingURL=service-not-found.error.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"service-not-found.error.js","sourceRoot":"","sources":["../../../src/error/service-not-found.error.ts"],"names":[],"mappings":";;;AACA,gDAAuC;AAEvC;;GAEG;AACH,MAAa,oBAAqB,SAAQ,KAAK;IAa7C,YAAY,UAA6B;;QACvC,KAAK,EAAE,CAAC;QAbH,SAAI,GAAG,sBAAsB,CAAC;QAErC,4DAA4D;QACpD,yBAAoB,GAAW,sBAAsB,CAAC;QAY5D,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE;YAClC,IAAI,CAAC,oBAAoB,GAAG,UAAU,CAAC;SACxC;aAAM,IAAI,UAAU,YAAY,mBAAK,EAAE;YACtC,IAAI,CAAC,oBAAoB,GAAG,SAAS,UAAU,CAAC,IAAI,IAAI,YAAY,GAAG,CAAC;SACzE;aAAM,IAAI,UAAU,IAAI,CAAC,UAAU,CAAC,IAAI,WAAI,UAAU,CAAC,SAAS,0CAAE,IAAI,CAAA,CAAC,EAAE;YACxE,IAAI,CAAC,oBAAoB;gBACvB,sBAAsB,UAAU,CAAC,IAAI,GAAG;oBACxC,sBAAsB,MAAC,UAAU,CAAC,SAA8B,0CAAE,IAAI,GAAG,CAAC;SAC7E;IACH,CAAC;IAnBD,IAAI,OAAO;QACT,OAAO,CACL,iBAAiB,IAAI,CAAC,oBAAoB,+CAA+C;YACzF,mHAAmH,CACpH,CAAC;IACJ,CAAC;CAeF;AA1BD,oDA0BC","sourcesContent":["import { ServiceIdentifier } from '../types/service-identifier.type';\nimport { Token } from '../token.class';\n\n/**\n * Thrown when requested service was not found.\n */\nexport class ServiceNotFoundError extends Error {\n public name = 'ServiceNotFoundError';\n\n /** Normalized identifier name used in the error message. */\n private normalizedIdentifier: string = '<UNKNOWN_IDENTIFIER>';\n\n get message(): string {\n return (\n `Service with \"${this.normalizedIdentifier}\" identifier was not found in the container. ` +\n `Register it before usage via explicitly calling the \"Container.set\" function or using the \"@Service()\" decorator.`\n );\n }\n\n constructor(identifier: ServiceIdentifier) {\n super();\n\n if (typeof identifier === 'string') {\n this.normalizedIdentifier = identifier;\n } else if (identifier instanceof Token) {\n this.normalizedIdentifier = `Token<${identifier.name || 'UNSET_NAME'}>`;\n } else if (identifier && (identifier.name || identifier.prototype?.name)) {\n this.normalizedIdentifier =\n `MaybeConstructable<${identifier.name}>` ||\n `MaybeConstructable<${(identifier.prototype as { name: string })?.name}>`;\n }\n }\n}\n"]}
+36
View File
@@ -0,0 +1,36 @@
"use strict";
/**
* We have a hard dependency on reflect-metadata package.
* Without the dependency lookup wont work. So we should warn the users
* when it's not loaded.
*/
// if(!Reflect || !(Reflect as any).getMetadata) {
// throw new Error('Reflect.getMetadata is not a function. Please import the "reflect-metadata" package at the first line of your application.');
// }
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Token = exports.Container = exports.ContainerInstance = void 0;
const container_class_1 = require("./container.class");
__exportStar(require("./decorators/inject-many.decorator"), exports);
__exportStar(require("./decorators/inject.decorator"), exports);
__exportStar(require("./decorators/service.decorator"), exports);
__exportStar(require("./error/cannot-inject-value.error"), exports);
__exportStar(require("./error/cannot-instantiate-value.error"), exports);
__exportStar(require("./error/service-not-found.error"), exports);
var container_instance_class_1 = require("./container-instance.class");
Object.defineProperty(exports, "ContainerInstance", { enumerable: true, get: function () { return container_instance_class_1.ContainerInstance; } });
var container_class_2 = require("./container.class");
Object.defineProperty(exports, "Container", { enumerable: true, get: function () { return container_class_2.Container; } });
var token_class_1 = require("./token.class");
Object.defineProperty(exports, "Token", { enumerable: true, get: function () { return token_class_1.Token; } });
exports.default = container_class_1.Container;
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA;;;;GAIG;AACH,kDAAkD;AAClD,mJAAmJ;AACnJ,IAAI;;;;;;;;;;;;;AAEJ,uDAA8C;AAE9C,qEAAmD;AACnD,gEAA8C;AAC9C,iEAA+C;AAE/C,oEAAkD;AAClD,yEAAuD;AACvD,kEAAgD;AAQhD,uEAA+D;AAAtD,6HAAA,iBAAiB,OAAA;AAC1B,qDAA8C;AAArC,4GAAA,SAAS,OAAA;AAClB,6CAAsC;AAA7B,oGAAA,KAAK,OAAA;AAEd,kBAAe,2BAAS,CAAC","sourcesContent":["/**\n * We have a hard dependency on reflect-metadata package.\n * Without the dependency lookup wont work. So we should warn the users\n * when it's not loaded.\n */\n// if(!Reflect || !(Reflect as any).getMetadata) {\n// throw new Error('Reflect.getMetadata is not a function. Please import the \"reflect-metadata\" package at the first line of your application.');\n// }\n\nimport { Container } from './container.class';\n\nexport * from './decorators/inject-many.decorator';\nexport * from './decorators/inject.decorator';\nexport * from './decorators/service.decorator';\n\nexport * from './error/cannot-inject-value.error';\nexport * from './error/cannot-instantiate-value.error';\nexport * from './error/service-not-found.error';\n\nexport { Handler } from './interfaces/handler.interface';\nexport { ServiceMetadata } from './interfaces/service-metadata.interface';\nexport { ServiceOptions } from './interfaces/service-options.interface';\nexport { Constructable } from './types/constructable.type';\nexport { ServiceIdentifier } from './types/service-identifier.type';\n\nexport { ContainerInstance } from './container-instance.class';\nexport { Container } from './container.class';\nexport { Token } from './token.class';\n\nexport default Container;\n"]}
+3
View File
@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=handler.interface.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"handler.interface.js","sourceRoot":"","sources":["../../../src/interfaces/handler.interface.ts"],"names":[],"mappings":"","sourcesContent":["import { ContainerInstance } from '../container-instance.class';\nimport { Constructable } from '../types/constructable.type';\n\n/**\n * Used to register special \"handler\" which will be executed on a service class during its initialization.\n * It can be used to create custom decorators and set/replace service class properties and constructor parameters.\n */\nexport interface Handler<T = unknown> {\n /**\n * Service object used to apply handler to.\n */\n object: Constructable<T>;\n\n /**\n * Class property name to set/replace value of.\n * Used if handler is applied on a class property.\n */\n propertyName?: string;\n\n /**\n * Parameter index to set/replace value of.\n * Used if handler is applied on a constructor parameter.\n */\n index?: number;\n\n /**\n * Factory function that produces value that will be set to class property or constructor parameter.\n * Accepts container instance which requested the value.\n */\n value: (container: ContainerInstance) => any;\n}\n"]}
+3
View File
@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=service-metadata.interface.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"service-metadata.interface.js","sourceRoot":"","sources":["../../../src/interfaces/service-metadata.interface.ts"],"names":[],"mappings":"","sourcesContent":["import { Constructable } from '../types/constructable.type';\nimport { ServiceIdentifier } from '../types/service-identifier.type';\n\n/**\n * Service metadata is used to initialize service and store its state.\n */\nexport interface ServiceMetadata<Type = unknown> {\n /** Unique identifier of the referenced service. */\n id: ServiceIdentifier;\n\n /**\n * Class definition of the service what is used to initialize given service.\n * This property maybe null if the value of the service is set manually.\n * If id is not set then it serves as service id.\n */\n type: Constructable<Type> | null;\n\n /**\n * Indicates if this service must be global and same instance must be used across all containers.\n */\n global: boolean;\n\n /**\n * Indicates whether a new instance of this class must be created for each class injecting this class.\n * Global option is ignored when this option is used.\n */\n transient: boolean;\n\n /**\n * Allows to setup multiple instances the different classes under a single service id string or token.\n */\n multiple: boolean;\n\n /**\n * Indicates whether a new instance should be created as soon as the class is registered.\n * By default the registered classes are only instantiated when they are requested from the container.\n */\n eager?: boolean;\n\n /**\n * Factory function used to initialize this service.\n * Can be regular function (\"createCar\" for example),\n * or other service which produces this instance ([CarFactory, \"createCar\"] for example).\n */\n factory: [Constructable<unknown>, string] | CallableFunction | undefined;\n\n /**\n * Instance of the target class.\n */\n value: unknown | Symbol;\n}\n"]}
+3
View File
@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=service-options.interface.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"service-options.interface.js","sourceRoot":"","sources":["../../../src/interfaces/service-options.interface.ts"],"names":[],"mappings":"","sourcesContent":["import { ServiceMetadata } from './service-metadata.interface';\n\n/**\n * The public ServiceOptions is partial object of ServiceMetadata and either one\n * of the following is set: `type`, `factory`, `value` but not more than one.\n */\nexport type ServiceOptions<T = unknown> =\n | Omit<Partial<ServiceMetadata<T>>, 'type' | 'factory'>\n | Omit<Partial<ServiceMetadata<T>>, 'value' | 'factory'>\n | Omit<Partial<ServiceMetadata<T>>, 'value' | 'type'>;\n"]}
+17
View File
@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Token = void 0;
/**
* Used to create unique typed service identifier.
* Useful when service has only interface, but don't have a class.
*/
class Token {
/**
* @param name Token name, optional and only used for debugging purposes.
*/
constructor(name) {
this.name = name;
}
}
exports.Token = Token;
//# sourceMappingURL=token.class.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"token.class.js","sourceRoot":"","sources":["../../src/token.class.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,MAAa,KAAK;IAChB;;OAEG;IACH,YAAmB,IAAa;QAAb,SAAI,GAAJ,IAAI,CAAS;IAAG,CAAC;CACrC;AALD,sBAKC","sourcesContent":["/**\n * Used to create unique typed service identifier.\n * Useful when service has only interface, but don't have a class.\n */\nexport class Token<T> {\n /**\n * @param name Token name, optional and only used for debugging purposes.\n */\n constructor(public name?: string) {}\n}\n"]}
+3
View File
@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=abstract-constructable.type.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"abstract-constructable.type.js","sourceRoot":"","sources":["../../../src/types/abstract-constructable.type.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Generic type for abstract class definitions.\n *\n * Explanation: This describes a callable Function with a prototype Which is\n * what an abstract class is - no constructor, just the prototype.\n */\nexport type AbstractConstructable<T> = CallableFunction & { prototype: T };\n"]}
+3
View File
@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=constructable.type.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"constructable.type.js","sourceRoot":"","sources":["../../../src/types/constructable.type.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Generic type for class definitions.\n * Example usage:\n * ```\n * function createSomeInstance(myClassDefinition: Constructable<MyClass>) {\n * return new myClassDefinition()\n * }\n * ```\n */\nexport type Constructable<T> = new (...args: any[]) => T;\n"]}
+3
View File
@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=service-identifier.type.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"service-identifier.type.js","sourceRoot":"","sources":["../../../src/types/service-identifier.type.ts"],"names":[],"mappings":"","sourcesContent":["import { Token } from '../token.class';\nimport { Constructable } from './constructable.type';\nimport { AbstractConstructable } from './abstract-constructable.type';\n\n/**\n * Unique service identifier.\n * Can be some class type, or string id, or instance of Token.\n */\nexport type ServiceIdentifier<T = unknown> =\n | Constructable<T>\n | AbstractConstructable<T>\n | CallableFunction\n | Token<T>\n | string;\n"]}
+49
View File
@@ -0,0 +1,49 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveToTypeWrapper = void 0;
const token_class_1 = require("../token.class");
/**
* Helper function used in inject decorators to resolve the received identifier to
* an eager type when possible or to a lazy type when cyclic dependencies are possibly involved.
*
* @param typeOrIdentifier a service identifier or a function returning a type acting as service identifier or nothing
* @param target the class definition of the target of the decorator
* @param propertyName the name of the property in case of a PropertyDecorator
* @param index the index of the parameter in the constructor in case of ParameterDecorator
*/
function resolveToTypeWrapper(typeOrIdentifier, target, propertyName, index) {
/**
* ? We want to error out as soon as possible when looking up services to inject, however
* ? we cannot determine the type at decorator execution when cyclic dependencies are involved
* ? because calling the received `() => MyType` function right away would cause a JS error:
* ? "Cannot access 'MyType' before initialization", so we need to execute the function in the handler,
* ? when the classes are already created. To overcome this, we use a wrapper:
* ? - the lazyType is executed in the handler so we never have a JS error
* ? - the eagerType is checked when decorator is running and an error is raised if an unknown type is encountered
*/
let typeWrapper;
/** If requested type is explicitly set via a string ID or token, we set it explicitly. */
if ((typeOrIdentifier && typeof typeOrIdentifier === 'string') || typeOrIdentifier instanceof token_class_1.Token) {
typeWrapper = { eagerType: typeOrIdentifier, lazyType: () => typeOrIdentifier };
}
/** If requested type is explicitly set via a () => MyClassType format, we set it explicitly. */
if (typeOrIdentifier && typeof typeOrIdentifier === 'function') {
/** We set eagerType to null, preventing the raising of the CannotInjectValueError in decorators. */
typeWrapper = { eagerType: null, lazyType: () => typeOrIdentifier() };
}
/** If no explicit type is set and handler registered for a class property, we need to get the property type. */
if (!typeOrIdentifier && propertyName) {
const identifier = Reflect.getMetadata('design:type', target, propertyName);
typeWrapper = { eagerType: identifier, lazyType: () => identifier };
}
/** If no explicit type is set and handler registered for a constructor parameter, we need to get the parameter types. */
if (!typeOrIdentifier && typeof index == 'number' && Number.isInteger(index)) {
const paramTypes = Reflect.getMetadata('design:paramtypes', target, propertyName);
/** It's not guaranteed, that we find any types for the constructor. */
const identifier = paramTypes === null || paramTypes === void 0 ? void 0 : paramTypes[index];
typeWrapper = { eagerType: identifier, lazyType: () => identifier };
}
return typeWrapper;
}
exports.resolveToTypeWrapper = resolveToTypeWrapper;
//# sourceMappingURL=resolve-to-type-wrapper.util.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"resolve-to-type-wrapper.util.js","sourceRoot":"","sources":["../../../src/utils/resolve-to-type-wrapper.util.ts"],"names":[],"mappings":";;;AAAA,gDAAuC;AAIvC;;;;;;;;GAQG;AACH,SAAgB,oBAAoB,CAClC,gBAAqG,EACrG,MAAc,EACd,YAA6B,EAC7B,KAAc;IAEd;;;;;;;;OAQG;IACH,IAAI,WAAoG,CAAC;IAEzG,0FAA0F;IAC1F,IAAI,CAAC,gBAAgB,IAAI,OAAO,gBAAgB,KAAK,QAAQ,CAAC,IAAI,gBAAgB,YAAY,mBAAK,EAAE;QACnG,WAAW,GAAG,EAAE,SAAS,EAAE,gBAAgB,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,gBAAgB,EAAE,CAAC;KACjF;IAED,gGAAgG;IAChG,IAAI,gBAAgB,IAAI,OAAO,gBAAgB,KAAK,UAAU,EAAE;QAC9D,qGAAqG;QACrG,WAAW,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAE,gBAAqC,EAAE,EAAE,CAAC;KAC7F;IAED,gHAAgH;IAChH,IAAI,CAAC,gBAAgB,IAAI,YAAY,EAAE;QACrC,MAAM,UAAU,GAAI,OAAe,CAAC,WAAW,CAAC,aAAa,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC;QAErF,WAAW,GAAG,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC;KACrE;IAED,yHAAyH;IACzH,IAAI,CAAC,gBAAgB,IAAI,OAAO,KAAK,IAAI,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE;QAC5E,MAAM,UAAU,GAAyB,OAAe,CAAC,WAAW,CAAC,mBAAmB,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC;QAChH,uEAAuE;QACvE,MAAM,UAAU,GAAG,UAAU,aAAV,UAAU,uBAAV,UAAU,CAAG,KAAK,CAAC,CAAC;QAEvC,WAAW,GAAG,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC;KACrE;IAED,OAAO,WAAW,CAAC;AACrB,CAAC;AA7CD,oDA6CC","sourcesContent":["import { Token } from '../token.class';\nimport { Constructable } from '../types/constructable.type';\nimport { ServiceIdentifier } from '../types/service-identifier.type';\n\n/**\n * Helper function used in inject decorators to resolve the received identifier to\n * an eager type when possible or to a lazy type when cyclic dependencies are possibly involved.\n *\n * @param typeOrIdentifier a service identifier or a function returning a type acting as service identifier or nothing\n * @param target the class definition of the target of the decorator\n * @param propertyName the name of the property in case of a PropertyDecorator\n * @param index the index of the parameter in the constructor in case of ParameterDecorator\n */\nexport function resolveToTypeWrapper(\n typeOrIdentifier: ((type?: never) => Constructable<unknown>) | ServiceIdentifier<unknown> | undefined,\n target: Object,\n propertyName: string | Symbol,\n index?: number\n): { eagerType: ServiceIdentifier | null; lazyType: (type?: never) => ServiceIdentifier } {\n /**\n * ? We want to error out as soon as possible when looking up services to inject, however\n * ? we cannot determine the type at decorator execution when cyclic dependencies are involved\n * ? because calling the received `() => MyType` function right away would cause a JS error:\n * ? \"Cannot access 'MyType' before initialization\", so we need to execute the function in the handler,\n * ? when the classes are already created. To overcome this, we use a wrapper:\n * ? - the lazyType is executed in the handler so we never have a JS error\n * ? - the eagerType is checked when decorator is running and an error is raised if an unknown type is encountered\n */\n let typeWrapper!: { eagerType: ServiceIdentifier | null; lazyType: (type?: never) => ServiceIdentifier };\n\n /** If requested type is explicitly set via a string ID or token, we set it explicitly. */\n if ((typeOrIdentifier && typeof typeOrIdentifier === 'string') || typeOrIdentifier instanceof Token) {\n typeWrapper = { eagerType: typeOrIdentifier, lazyType: () => typeOrIdentifier };\n }\n\n /** If requested type is explicitly set via a () => MyClassType format, we set it explicitly. */\n if (typeOrIdentifier && typeof typeOrIdentifier === 'function') {\n /** We set eagerType to null, preventing the raising of the CannotInjectValueError in decorators. */\n typeWrapper = { eagerType: null, lazyType: () => (typeOrIdentifier as CallableFunction)() };\n }\n\n /** If no explicit type is set and handler registered for a class property, we need to get the property type. */\n if (!typeOrIdentifier && propertyName) {\n const identifier = (Reflect as any).getMetadata('design:type', target, propertyName);\n\n typeWrapper = { eagerType: identifier, lazyType: () => identifier };\n }\n\n /** If no explicit type is set and handler registered for a constructor parameter, we need to get the parameter types. */\n if (!typeOrIdentifier && typeof index == 'number' && Number.isInteger(index)) {\n const paramTypes: ServiceIdentifier[] = (Reflect as any).getMetadata('design:paramtypes', target, propertyName);\n /** It's not guaranteed, that we find any types for the constructor. */\n const identifier = paramTypes?.[index];\n\n typeWrapper = { eagerType: identifier, lazyType: () => identifier };\n }\n\n return typeWrapper;\n}\n"]}
+300
View File
@@ -0,0 +1,300 @@
import { Container } from './container.class';
import { ServiceNotFoundError } from './error/service-not-found.error';
import { CannotInstantiateValueError } from './error/cannot-instantiate-value.error';
import { Token } from './token.class';
import { EMPTY_VALUE } from './empty.const';
/**
* TypeDI can have multiple containers.
* One container is ContainerInstance.
*/
export class ContainerInstance {
constructor(id) {
/** All registered services in the container. */
this.services = [];
this.id = id;
}
has(identifier) {
return !!this.findService(identifier);
}
get(identifier) {
const globalContainer = Container.of(undefined);
const globalService = globalContainer.findService(identifier);
const scopedService = this.findService(identifier);
if (globalService && globalService.global === true)
return this.getServiceValue(globalService);
if (scopedService)
return this.getServiceValue(scopedService);
/** If it's the first time requested in the child container we load it from parent and set it. */
if (globalService && this !== globalContainer) {
const clonedService = { ...globalService };
clonedService.value = EMPTY_VALUE;
/**
* We need to immediately set the empty value from the root container
* to prevent infinite lookup in cyclic dependencies.
*/
this.set(clonedService);
const value = this.getServiceValue(clonedService);
this.set({ ...clonedService, value });
return value;
}
if (globalService)
return this.getServiceValue(globalService);
throw new ServiceNotFoundError(identifier);
}
getMany(identifier) {
return this.findAllServices(identifier).map(service => this.getServiceValue(service));
}
set(identifierOrServiceMetadata, value) {
if (identifierOrServiceMetadata instanceof Array) {
identifierOrServiceMetadata.forEach(data => this.set(data));
return this;
}
if (typeof identifierOrServiceMetadata === 'string' || identifierOrServiceMetadata instanceof Token) {
return this.set({
id: identifierOrServiceMetadata,
type: null,
value: value,
factory: undefined,
global: false,
multiple: false,
eager: false,
transient: false,
});
}
if (typeof identifierOrServiceMetadata === 'function') {
return this.set({
id: identifierOrServiceMetadata,
// TODO: remove explicit casting
type: identifierOrServiceMetadata,
value: value,
factory: undefined,
global: false,
multiple: false,
eager: false,
transient: false,
});
}
const newService = {
id: new Token('UNREACHABLE'),
type: null,
factory: undefined,
value: EMPTY_VALUE,
global: false,
multiple: false,
eager: false,
transient: false,
...identifierOrServiceMetadata,
};
const service = this.findService(newService.id);
if (service && service.multiple !== true) {
Object.assign(service, newService);
}
else {
this.services.push(newService);
}
if (newService.eager) {
this.get(newService.id);
}
return this;
}
/**
* Removes services with a given service identifiers.
*/
remove(identifierOrIdentifierArray) {
if (Array.isArray(identifierOrIdentifierArray)) {
identifierOrIdentifierArray.forEach(id => this.remove(id));
}
else {
this.services = this.services.filter(service => {
if (service.id === identifierOrIdentifierArray) {
this.destroyServiceInstance(service);
return false;
}
return true;
});
}
return this;
}
/**
* Completely resets the container by removing all previously registered services from it.
*/
reset(options = { strategy: 'resetValue' }) {
switch (options.strategy) {
case 'resetValue':
this.services.forEach(service => this.destroyServiceInstance(service));
break;
case 'resetServices':
this.services.forEach(service => this.destroyServiceInstance(service));
this.services = [];
break;
default:
throw new Error('Received invalid reset strategy.');
}
return this;
}
/**
* Returns all services registered with the given identifier.
*/
findAllServices(identifier) {
return this.services.filter(service => service.id === identifier);
}
/**
* Finds registered service in the with a given service identifier.
*/
findService(identifier) {
return this.services.find(service => service.id === identifier);
}
/**
* Gets the value belonging to `serviceMetadata.id`.
*
* - if `serviceMetadata.value` is already set it is immediately returned
* - otherwise the requested type is resolved to the value saved to `serviceMetadata.value` and returned
*/
getServiceValue(serviceMetadata) {
var _a;
let value = EMPTY_VALUE;
/**
* If the service value has been set to anything prior to this call we return that value.
* NOTE: This part builds on the assumption that transient dependencies has no value set ever.
*/
if (serviceMetadata.value !== EMPTY_VALUE) {
return serviceMetadata.value;
}
/** If both factory and type is missing, we cannot resolve the requested ID. */
if (!serviceMetadata.factory && !serviceMetadata.type) {
throw new CannotInstantiateValueError(serviceMetadata.id);
}
/**
* If a factory is defined it takes priority over creating an instance via `new`.
* The return value of the factory is not checked, we believe by design that the user knows what he/she is doing.
*/
if (serviceMetadata.factory) {
/**
* If we received the factory in the [Constructable<Factory>, "functionName"] format, we need to create the
* factory first and then call the specified function on it.
*/
if (serviceMetadata.factory instanceof Array) {
let factoryInstance;
try {
/** Try to get the factory from TypeDI first, if failed, fall back to simply initiating the class. */
factoryInstance = this.get(serviceMetadata.factory[0]);
}
catch (error) {
if (error instanceof ServiceNotFoundError) {
factoryInstance = new serviceMetadata.factory[0]();
}
else {
throw error;
}
}
value = factoryInstance[serviceMetadata.factory[1]](this, serviceMetadata.id);
}
else {
/** If only a simple function was provided we simply call it. */
value = serviceMetadata.factory(this, serviceMetadata.id);
}
}
/**
* If no factory was provided and only then, we create the instance from the type if it was set.
*/
if (!serviceMetadata.factory && serviceMetadata.type) {
const constructableTargetType = serviceMetadata.type;
// setup constructor parameters for a newly initialized service
const paramTypes = ((_a = Reflect) === null || _a === void 0 ? void 0 : _a.getMetadata('design:paramtypes', constructableTargetType)) || [];
const params = this.initializeParams(constructableTargetType, paramTypes);
// "extra feature" - always pass container instance as the last argument to the service function
// this allows us to support javascript where we don't have decorators and emitted metadata about dependencies
// need to be injected, and user can use provided container to get instances he needs
params.push(this);
value = new constructableTargetType(...params);
// TODO: Calling this here, leads to infinite loop, because @Inject decorator registerds a handler
// TODO: which calls Container.get, which will check if the requested type has a value set and if not
// TODO: it will start the instantiation process over. So this is currently called outside of the if branch
// TODO: after the current value has been assigned to the serviceMetadata.
// this.applyPropertyHandlers(constructableTargetType, value as Constructable<unknown>);
}
/** If this is not a transient service, and we resolved something, then we set it as the value. */
if (!serviceMetadata.transient && value !== EMPTY_VALUE) {
serviceMetadata.value = value;
}
if (value === EMPTY_VALUE) {
/** This branch should never execute, but better to be safe than sorry. */
throw new CannotInstantiateValueError(serviceMetadata.id);
}
if (serviceMetadata.type) {
this.applyPropertyHandlers(serviceMetadata.type, value);
}
return value;
}
/**
* Initializes all parameter types for a given target service class.
*/
initializeParams(target, paramTypes) {
return paramTypes.map((paramType, index) => {
const paramHandler = Container.handlers.find(handler => {
/**
* @Inject()-ed values are stored as parameter handlers and they reference their target
* when created. So when a class is extended the @Inject()-ed values are not inherited
* because the handler still points to the old object only.
*
* As a quick fix a single level parent lookup is added via `Object.getPrototypeOf(target)`,
* however this should be updated to a more robust solution.
*
* TODO: Add proper inheritance handling: either copy the handlers when a class is registered what
* TODO: has it's parent already registered as dependency or make the lookup search up to the base Object.
*/
return ((handler.object === target || handler.object === Object.getPrototypeOf(target)) && handler.index === index);
});
if (paramHandler)
return paramHandler.value(this);
if (paramType && paramType.name && !this.isPrimitiveParamType(paramType.name)) {
return this.get(paramType);
}
return undefined;
});
}
/**
* Checks if given parameter type is primitive type or not.
*/
isPrimitiveParamType(paramTypeName) {
return ['string', 'boolean', 'number', 'object'].includes(paramTypeName.toLowerCase());
}
/**
* Applies all registered handlers on a given target class.
*/
applyPropertyHandlers(target, instance) {
Container.handlers.forEach(handler => {
if (typeof handler.index === 'number')
return;
if (handler.object.constructor !== target && !(target.prototype instanceof handler.object.constructor))
return;
if (handler.propertyName) {
instance[handler.propertyName] = handler.value(this);
}
});
}
/**
* Checks if the given service metadata contains a destroyable service instance and destroys it in place. If the service
* contains a callable function named `destroy` it is called but not awaited and the return value is ignored..
*
* @param serviceMetadata the service metadata containing the instance to destroy
* @param force when true the service will be always destroyed even if it's cannot be re-created
*/
destroyServiceInstance(serviceMetadata, force = false) {
/** We reset value only if we can re-create it (aka type or factory exists). */
const shouldResetValue = force || !!serviceMetadata.type || !!serviceMetadata.factory;
if (shouldResetValue) {
/** If we wound a function named destroy we call it without any params. */
if (typeof (serviceMetadata === null || serviceMetadata === void 0 ? void 0 : serviceMetadata.value)['destroy'] === 'function') {
try {
serviceMetadata.value.destroy();
}
catch (error) {
/** We simply ignore the errors from the destroy function. */
}
}
serviceMetadata.value = EMPTY_VALUE;
}
}
}
//# sourceMappingURL=container-instance.class.js.map
File diff suppressed because one or more lines are too long
+81
View File
@@ -0,0 +1,81 @@
import { ContainerInstance } from './container-instance.class';
/**
* Service container.
*/
export class Container {
/**
* Gets a separate container instance for the given instance id.
*/
static of(containerId = 'default') {
if (containerId === 'default')
return this.globalInstance;
let container = this.instances.find(instance => instance.id === containerId);
if (!container) {
container = new ContainerInstance(containerId);
this.instances.push(container);
// TODO: Why we are not reseting here? Let's reset here. (I have added the commented code.)
// container.reset();
}
return container;
}
static has(identifier) {
return this.globalInstance.has(identifier);
}
static get(identifier) {
return this.globalInstance.get(identifier);
}
static getMany(id) {
return this.globalInstance.getMany(id);
}
static set(identifierOrServiceMetadata, value) {
this.globalInstance.set(identifierOrServiceMetadata, value);
return this;
}
/**
* Removes services with a given service identifiers.
*/
static remove(identifierOrIdentifierArray) {
this.globalInstance.remove(identifierOrIdentifierArray);
return this;
}
/**
* Completely resets the container by removing all previously registered services and handlers from it.
*/
static reset(containerId = 'default') {
if (containerId == 'default') {
this.globalInstance.reset();
this.instances.forEach(instance => instance.reset());
}
else {
const instance = this.instances.find(instance => instance.id === containerId);
if (instance) {
instance.reset();
this.instances.splice(this.instances.indexOf(instance), 1);
}
}
return this;
}
/**
* Registers a new handler.
*/
static registerHandler(handler) {
this.handlers.push(handler);
return this;
}
/**
* Helper method that imports given services.
*/
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
static import(services) {
return this;
}
}
/**
* All registered handlers. The @Inject() decorator uses handlers internally to mark a property for injection.
**/
Container.handlers = [];
/** Global container instance. */
Container.globalInstance = new ContainerInstance('default');
/** Other containers created using Container.of method. */
Container.instances = [];
//# sourceMappingURL=container.class.js.map
File diff suppressed because one or more lines are too long
+26
View File
@@ -0,0 +1,26 @@
import { Container } from '../container.class';
import { CannotInjectValueError } from '../error/cannot-inject-value.error';
import { resolveToTypeWrapper } from '../utils/resolve-to-type-wrapper.util';
export function InjectMany(typeOrIdentifier) {
return function (target, propertyName, index) {
const typeWrapper = resolveToTypeWrapper(typeOrIdentifier, target, propertyName, index);
/** If no type was inferred, or the general Object type was inferred we throw an error. */
if (typeWrapper === undefined || typeWrapper.eagerType === undefined || typeWrapper.eagerType === Object) {
throw new CannotInjectValueError(target, propertyName);
}
Container.registerHandler({
object: target,
propertyName: propertyName,
index: index,
value: containerInstance => {
const evaluatedLazyType = typeWrapper.lazyType();
/** If no type was inferred lazily, or the general Object type was inferred we throw an error. */
if (evaluatedLazyType === undefined || evaluatedLazyType === Object) {
throw new CannotInjectValueError(target, propertyName);
}
return containerInstance.getMany(evaluatedLazyType);
},
});
};
}
//# sourceMappingURL=inject-many.decorator.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"inject-many.decorator.js","sourceRoot":"","sources":["../../../src/decorators/inject-many.decorator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAW7E,MAAM,UAAU,UAAU,CACxB,gBAA0F;IAE1F,OAAO,UAAU,MAAc,EAAE,YAA6B,EAAE,KAAc;QAC5E,MAAM,WAAW,GAAG,oBAAoB,CAAC,gBAAgB,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC;QAExF,0FAA0F;QAC1F,IAAI,WAAW,KAAK,SAAS,IAAI,WAAW,CAAC,SAAS,KAAK,SAAS,IAAI,WAAW,CAAC,SAAS,KAAK,MAAM,EAAE;YACxG,MAAM,IAAI,sBAAsB,CAAC,MAAgC,EAAE,YAAsB,CAAC,CAAC;SAC5F;QAED,SAAS,CAAC,eAAe,CAAC;YACxB,MAAM,EAAE,MAAgC;YACxC,YAAY,EAAE,YAAsB;YACpC,KAAK,EAAE,KAAK;YACZ,KAAK,EAAE,iBAAiB,CAAC,EAAE;gBACzB,MAAM,iBAAiB,GAAG,WAAW,CAAC,QAAQ,EAAE,CAAC;gBAEjD,iGAAiG;gBACjG,IAAI,iBAAiB,KAAK,SAAS,IAAI,iBAAiB,KAAK,MAAM,EAAE;oBACnE,MAAM,IAAI,sBAAsB,CAAC,MAAgC,EAAE,YAAsB,CAAC,CAAC;iBAC5F;gBAED,OAAO,iBAAiB,CAAC,OAAO,CAAU,iBAAiB,CAAC,CAAC;YAC/D,CAAC;SACF,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import { Container } from '../container.class';\nimport { Token } from '../token.class';\nimport { CannotInjectValueError } from '../error/cannot-inject-value.error';\nimport { resolveToTypeWrapper } from '../utils/resolve-to-type-wrapper.util';\nimport { Constructable } from '../types/constructable.type';\nimport { ServiceIdentifier } from '../types/service-identifier.type';\n\n/**\n * Injects a list of services into a class property or constructor parameter.\n */\nexport function InjectMany(): Function;\nexport function InjectMany(type?: (type?: any) => Function): Function;\nexport function InjectMany(serviceName?: string): Function;\nexport function InjectMany(token: Token<any>): Function;\nexport function InjectMany(\n typeOrIdentifier?: ((type?: never) => Constructable<unknown>) | ServiceIdentifier<unknown>\n): Function {\n return function (target: Object, propertyName: string | Symbol, index?: number): void {\n const typeWrapper = resolveToTypeWrapper(typeOrIdentifier, target, propertyName, index);\n\n /** If no type was inferred, or the general Object type was inferred we throw an error. */\n if (typeWrapper === undefined || typeWrapper.eagerType === undefined || typeWrapper.eagerType === Object) {\n throw new CannotInjectValueError(target as Constructable<unknown>, propertyName as string);\n }\n\n Container.registerHandler({\n object: target as Constructable<unknown>,\n propertyName: propertyName as string,\n index: index,\n value: containerInstance => {\n const evaluatedLazyType = typeWrapper.lazyType();\n\n /** If no type was inferred lazily, or the general Object type was inferred we throw an error. */\n if (evaluatedLazyType === undefined || evaluatedLazyType === Object) {\n throw new CannotInjectValueError(target as Constructable<unknown>, propertyName as string);\n }\n\n return containerInstance.getMany<unknown>(evaluatedLazyType);\n },\n });\n };\n}\n"]}
+26
View File
@@ -0,0 +1,26 @@
import { Container } from '../container.class';
import { CannotInjectValueError } from '../error/cannot-inject-value.error';
import { resolveToTypeWrapper } from '../utils/resolve-to-type-wrapper.util';
export function Inject(typeOrIdentifier) {
return function (target, propertyName, index) {
const typeWrapper = resolveToTypeWrapper(typeOrIdentifier, target, propertyName, index);
/** If no type was inferred, or the general Object type was inferred we throw an error. */
if (typeWrapper === undefined || typeWrapper.eagerType === undefined || typeWrapper.eagerType === Object) {
throw new CannotInjectValueError(target, propertyName);
}
Container.registerHandler({
object: target,
propertyName: propertyName,
index: index,
value: containerInstance => {
const evaluatedLazyType = typeWrapper.lazyType();
/** If no type was inferred lazily, or the general Object type was inferred we throw an error. */
if (evaluatedLazyType === undefined || evaluatedLazyType === Object) {
throw new CannotInjectValueError(target, propertyName);
}
return containerInstance.get(evaluatedLazyType);
},
});
};
}
//# sourceMappingURL=inject.decorator.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"inject.decorator.js","sourceRoot":"","sources":["../../../src/decorators/inject.decorator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAG5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAS7E,MAAM,UAAU,MAAM,CACpB,gBAA0F;IAE1F,OAAO,UAAU,MAAc,EAAE,YAA6B,EAAE,KAAc;QAC5E,MAAM,WAAW,GAAG,oBAAoB,CAAC,gBAAgB,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC;QAExF,0FAA0F;QAC1F,IAAI,WAAW,KAAK,SAAS,IAAI,WAAW,CAAC,SAAS,KAAK,SAAS,IAAI,WAAW,CAAC,SAAS,KAAK,MAAM,EAAE;YACxG,MAAM,IAAI,sBAAsB,CAAC,MAAgC,EAAE,YAAsB,CAAC,CAAC;SAC5F;QAED,SAAS,CAAC,eAAe,CAAC;YACxB,MAAM,EAAE,MAAgC;YACxC,YAAY,EAAE,YAAsB;YACpC,KAAK,EAAE,KAAK;YACZ,KAAK,EAAE,iBAAiB,CAAC,EAAE;gBACzB,MAAM,iBAAiB,GAAG,WAAW,CAAC,QAAQ,EAAE,CAAC;gBAEjD,iGAAiG;gBACjG,IAAI,iBAAiB,KAAK,SAAS,IAAI,iBAAiB,KAAK,MAAM,EAAE;oBACnE,MAAM,IAAI,sBAAsB,CAAC,MAAgC,EAAE,YAAsB,CAAC,CAAC;iBAC5F;gBAED,OAAO,iBAAiB,CAAC,GAAG,CAAU,iBAAiB,CAAC,CAAC;YAC3D,CAAC;SACF,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import { Container } from '../container.class';\nimport { Token } from '../token.class';\nimport { CannotInjectValueError } from '../error/cannot-inject-value.error';\nimport { ServiceIdentifier } from '../types/service-identifier.type';\nimport { Constructable } from '../types/constructable.type';\nimport { resolveToTypeWrapper } from '../utils/resolve-to-type-wrapper.util';\n\n/**\n * Injects a service into a class property or constructor parameter.\n */\nexport function Inject(): Function;\nexport function Inject(typeFn: (type?: never) => Constructable<unknown>): Function;\nexport function Inject(serviceName?: string): Function;\nexport function Inject(token: Token<unknown>): Function;\nexport function Inject(\n typeOrIdentifier?: ((type?: never) => Constructable<unknown>) | ServiceIdentifier<unknown>\n): ParameterDecorator | PropertyDecorator {\n return function (target: Object, propertyName: string | Symbol, index?: number): void {\n const typeWrapper = resolveToTypeWrapper(typeOrIdentifier, target, propertyName, index);\n\n /** If no type was inferred, or the general Object type was inferred we throw an error. */\n if (typeWrapper === undefined || typeWrapper.eagerType === undefined || typeWrapper.eagerType === Object) {\n throw new CannotInjectValueError(target as Constructable<unknown>, propertyName as string);\n }\n\n Container.registerHandler({\n object: target as Constructable<unknown>,\n propertyName: propertyName as string,\n index: index,\n value: containerInstance => {\n const evaluatedLazyType = typeWrapper.lazyType();\n\n /** If no type was inferred lazily, or the general Object type was inferred we throw an error. */\n if (evaluatedLazyType === undefined || evaluatedLazyType === Object) {\n throw new CannotInjectValueError(target as Constructable<unknown>, propertyName as string);\n }\n\n return containerInstance.get<unknown>(evaluatedLazyType);\n },\n });\n };\n}\n"]}
+33
View File
@@ -0,0 +1,33 @@
import { Container } from '../container.class';
import { Token } from '../token.class';
import { EMPTY_VALUE } from '../empty.const';
export function Service(optionsOrServiceIdentifier) {
return targetConstructor => {
const serviceMetadata = {
id: targetConstructor,
// TODO: Let's investigate why we receive Function type instead of a constructable.
type: targetConstructor,
factory: undefined,
multiple: false,
global: false,
eager: false,
transient: false,
value: EMPTY_VALUE,
};
if (optionsOrServiceIdentifier instanceof Token || typeof optionsOrServiceIdentifier === 'string') {
/** We received a Token or string ID. */
serviceMetadata.id = optionsOrServiceIdentifier;
}
else if (optionsOrServiceIdentifier) {
/** We received a ServiceOptions object. */
serviceMetadata.id = optionsOrServiceIdentifier.id || targetConstructor;
serviceMetadata.factory = optionsOrServiceIdentifier.factory || undefined;
serviceMetadata.multiple = optionsOrServiceIdentifier.multiple || false;
serviceMetadata.global = optionsOrServiceIdentifier.global || false;
serviceMetadata.eager = optionsOrServiceIdentifier.eager || false;
serviceMetadata.transient = optionsOrServiceIdentifier.transient || false;
}
Container.set(serviceMetadata);
};
}
//# sourceMappingURL=service.decorator.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"service.decorator.js","sourceRoot":"","sources":["../../../src/decorators/service.decorator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAGvC,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAU7C,MAAM,UAAU,OAAO,CAAI,0BAAoE;IAC7F,OAAO,iBAAiB,CAAC,EAAE;QACzB,MAAM,eAAe,GAAuB;YAC1C,EAAE,EAAE,iBAAiB;YACrB,mFAAmF;YACnF,IAAI,EAAG,iBAAiD;YACxD,OAAO,EAAE,SAAS;YAClB,QAAQ,EAAE,KAAK;YACf,MAAM,EAAE,KAAK;YACb,KAAK,EAAE,KAAK;YACZ,SAAS,EAAE,KAAK;YAChB,KAAK,EAAE,WAAW;SACnB,CAAC;QAEF,IAAI,0BAA0B,YAAY,KAAK,IAAI,OAAO,0BAA0B,KAAK,QAAQ,EAAE;YACjG,wCAAwC;YACxC,eAAe,CAAC,EAAE,GAAG,0BAA0B,CAAC;SACjD;aAAM,IAAI,0BAA0B,EAAE;YACrC,2CAA2C;YAC3C,eAAe,CAAC,EAAE,GAAI,0BAA8C,CAAC,EAAE,IAAI,iBAAiB,CAAC;YAC7F,eAAe,CAAC,OAAO,GAAI,0BAA8C,CAAC,OAAO,IAAI,SAAS,CAAC;YAC/F,eAAe,CAAC,QAAQ,GAAI,0BAA8C,CAAC,QAAQ,IAAI,KAAK,CAAC;YAC7F,eAAe,CAAC,MAAM,GAAI,0BAA8C,CAAC,MAAM,IAAI,KAAK,CAAC;YACzF,eAAe,CAAC,KAAK,GAAI,0BAA8C,CAAC,KAAK,IAAI,KAAK,CAAC;YACvF,eAAe,CAAC,SAAS,GAAI,0BAA8C,CAAC,SAAS,IAAI,KAAK,CAAC;SAChG;QAED,SAAS,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACjC,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import { Container } from '../container.class';\nimport { Token } from '../token.class';\nimport { ServiceMetadata } from '../interfaces/service-metadata.interface';\nimport { ServiceOptions } from '../interfaces/service-options.interface';\nimport { EMPTY_VALUE } from '../empty.const';\nimport { Constructable } from '../types/constructable.type';\n\n/**\n * Marks class as a service that can be injected using Container.\n */\nexport function Service<T = unknown>(): Function;\nexport function Service<T = unknown>(name: string): Function;\nexport function Service<T = unknown>(token: Token<unknown>): Function;\nexport function Service<T = unknown>(options?: ServiceOptions<T>): Function;\nexport function Service<T>(optionsOrServiceIdentifier?: ServiceOptions<T> | Token<any> | string): ClassDecorator {\n return targetConstructor => {\n const serviceMetadata: ServiceMetadata<T> = {\n id: targetConstructor,\n // TODO: Let's investigate why we receive Function type instead of a constructable.\n type: (targetConstructor as unknown) as Constructable<T>,\n factory: undefined,\n multiple: false,\n global: false,\n eager: false,\n transient: false,\n value: EMPTY_VALUE,\n };\n\n if (optionsOrServiceIdentifier instanceof Token || typeof optionsOrServiceIdentifier === 'string') {\n /** We received a Token or string ID. */\n serviceMetadata.id = optionsOrServiceIdentifier;\n } else if (optionsOrServiceIdentifier) {\n /** We received a ServiceOptions object. */\n serviceMetadata.id = (optionsOrServiceIdentifier as ServiceMetadata).id || targetConstructor;\n serviceMetadata.factory = (optionsOrServiceIdentifier as ServiceMetadata).factory || undefined;\n serviceMetadata.multiple = (optionsOrServiceIdentifier as ServiceMetadata).multiple || false;\n serviceMetadata.global = (optionsOrServiceIdentifier as ServiceMetadata).global || false;\n serviceMetadata.eager = (optionsOrServiceIdentifier as ServiceMetadata).eager || false;\n serviceMetadata.transient = (optionsOrServiceIdentifier as ServiceMetadata).transient || false;\n }\n\n Container.set(serviceMetadata);\n };\n}\n"]}
+2
View File
@@ -0,0 +1,2 @@
export const EMPTY_VALUE = Symbol('EMPTY_VALUE');
//# sourceMappingURL=empty.const.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"empty.const.js","sourceRoot":"","sources":["../../src/empty.const.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC","sourcesContent":["export const EMPTY_VALUE = Symbol('EMPTY_VALUE');\n"]}
+16
View File
@@ -0,0 +1,16 @@
/**
* Thrown when DI cannot inject value into property decorated by @Inject decorator.
*/
export class CannotInjectValueError extends Error {
constructor(target, propertyName) {
super();
this.target = target;
this.propertyName = propertyName;
this.name = 'CannotInjectValueError';
}
get message() {
return (`Cannot inject value into "${this.target.constructor.name}.${this.propertyName}". ` +
`Please make sure you setup reflect-metadata properly and you don't use interfaces without service tokens as injection value.`);
}
}
//# sourceMappingURL=cannot-inject-value.error.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"cannot-inject-value.error.js","sourceRoot":"","sources":["../../../src/error/cannot-inject-value.error.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAU/C,YAAoB,MAA8B,EAAU,YAAoB;QAC9E,KAAK,EAAE,CAAC;QADU,WAAM,GAAN,MAAM,CAAwB;QAAU,iBAAY,GAAZ,YAAY,CAAQ;QATzE,SAAI,GAAG,wBAAwB,CAAC;IAWvC,CAAC;IATD,IAAI,OAAO;QACT,OAAO,CACL,6BAA6B,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,IAAI,IAAI,CAAC,YAAY,KAAK;YACnF,8HAA8H,CAC/H,CAAC;IACJ,CAAC;CAKF","sourcesContent":["import { Constructable } from '../types/constructable.type';\n\n/**\n * Thrown when DI cannot inject value into property decorated by @Inject decorator.\n */\nexport class CannotInjectValueError extends Error {\n public name = 'CannotInjectValueError';\n\n get message(): string {\n return (\n `Cannot inject value into \"${this.target.constructor.name}.${this.propertyName}\". ` +\n `Please make sure you setup reflect-metadata properly and you don't use interfaces without service tokens as injection value.`\n );\n }\n\n constructor(private target: Constructable<unknown>, private propertyName: string) {\n super();\n }\n}\n"]}
+30
View File
@@ -0,0 +1,30 @@
import { Token } from '../token.class';
/**
* Thrown when DI cannot inject value into property decorated by @Inject decorator.
*/
export class CannotInstantiateValueError extends Error {
constructor(identifier) {
var _a, _b;
super();
this.name = 'CannotInstantiateValueError';
/** Normalized identifier name used in the error message. */
this.normalizedIdentifier = '<UNKNOWN_IDENTIFIER>';
// TODO: Extract this to a helper function and share between this and NotFoundError.
if (typeof identifier === 'string') {
this.normalizedIdentifier = identifier;
}
else if (identifier instanceof Token) {
this.normalizedIdentifier = `Token<${identifier.name || 'UNSET_NAME'}>`;
}
else if (identifier && (identifier.name || ((_a = identifier.prototype) === null || _a === void 0 ? void 0 : _a.name))) {
this.normalizedIdentifier =
`MaybeConstructable<${identifier.name}>` ||
`MaybeConstructable<${(_b = identifier.prototype) === null || _b === void 0 ? void 0 : _b.name}>`;
}
}
get message() {
return (`Cannot instantiate the requested value for the "${this.normalizedIdentifier}" identifier. ` +
`The related metadata doesn't contain a factory or a type to instantiate.`);
}
}
//# sourceMappingURL=cannot-instantiate-value.error.js.map
@@ -0,0 +1 @@
{"version":3,"file":"cannot-instantiate-value.error.js","sourceRoot":"","sources":["../../../src/error/cannot-instantiate-value.error.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAEvC;;GAEG;AACH,MAAM,OAAO,2BAA4B,SAAQ,KAAK;IAapD,YAAY,UAA6B;;QACvC,KAAK,EAAE,CAAC;QAbH,SAAI,GAAG,6BAA6B,CAAC;QAE5C,4DAA4D;QACpD,yBAAoB,GAAW,sBAAsB,CAAC;QAY5D,oFAAoF;QACpF,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE;YAClC,IAAI,CAAC,oBAAoB,GAAG,UAAU,CAAC;SACxC;aAAM,IAAI,UAAU,YAAY,KAAK,EAAE;YACtC,IAAI,CAAC,oBAAoB,GAAG,SAAS,UAAU,CAAC,IAAI,IAAI,YAAY,GAAG,CAAC;SACzE;aAAM,IAAI,UAAU,IAAI,CAAC,UAAU,CAAC,IAAI,WAAI,UAAU,CAAC,SAAS,0CAAE,IAAI,CAAA,CAAC,EAAE;YACxE,IAAI,CAAC,oBAAoB;gBACvB,sBAAsB,UAAU,CAAC,IAAI,GAAG;oBACxC,sBAAsB,MAAC,UAAU,CAAC,SAA8B,0CAAE,IAAI,GAAG,CAAC;SAC7E;IACH,CAAC;IApBD,IAAI,OAAO;QACT,OAAO,CACL,mDAAmD,IAAI,CAAC,oBAAoB,gBAAgB;YAC5F,0EAA0E,CAC3E,CAAC;IACJ,CAAC;CAgBF","sourcesContent":["import { ServiceIdentifier } from '../types/service-identifier.type';\nimport { Token } from '../token.class';\n\n/**\n * Thrown when DI cannot inject value into property decorated by @Inject decorator.\n */\nexport class CannotInstantiateValueError extends Error {\n public name = 'CannotInstantiateValueError';\n\n /** Normalized identifier name used in the error message. */\n private normalizedIdentifier: string = '<UNKNOWN_IDENTIFIER>';\n\n get message(): string {\n return (\n `Cannot instantiate the requested value for the \"${this.normalizedIdentifier}\" identifier. ` +\n `The related metadata doesn't contain a factory or a type to instantiate.`\n );\n }\n\n constructor(identifier: ServiceIdentifier) {\n super();\n\n // TODO: Extract this to a helper function and share between this and NotFoundError.\n if (typeof identifier === 'string') {\n this.normalizedIdentifier = identifier;\n } else if (identifier instanceof Token) {\n this.normalizedIdentifier = `Token<${identifier.name || 'UNSET_NAME'}>`;\n } else if (identifier && (identifier.name || identifier.prototype?.name)) {\n this.normalizedIdentifier =\n `MaybeConstructable<${identifier.name}>` ||\n `MaybeConstructable<${(identifier.prototype as { name: string })?.name}>`;\n }\n }\n}\n"]}
+29
View File
@@ -0,0 +1,29 @@
import { Token } from '../token.class';
/**
* Thrown when requested service was not found.
*/
export class ServiceNotFoundError extends Error {
constructor(identifier) {
var _a, _b;
super();
this.name = 'ServiceNotFoundError';
/** Normalized identifier name used in the error message. */
this.normalizedIdentifier = '<UNKNOWN_IDENTIFIER>';
if (typeof identifier === 'string') {
this.normalizedIdentifier = identifier;
}
else if (identifier instanceof Token) {
this.normalizedIdentifier = `Token<${identifier.name || 'UNSET_NAME'}>`;
}
else if (identifier && (identifier.name || ((_a = identifier.prototype) === null || _a === void 0 ? void 0 : _a.name))) {
this.normalizedIdentifier =
`MaybeConstructable<${identifier.name}>` ||
`MaybeConstructable<${(_b = identifier.prototype) === null || _b === void 0 ? void 0 : _b.name}>`;
}
}
get message() {
return (`Service with "${this.normalizedIdentifier}" identifier was not found in the container. ` +
`Register it before usage via explicitly calling the "Container.set" function or using the "@Service()" decorator.`);
}
}
//# sourceMappingURL=service-not-found.error.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"service-not-found.error.js","sourceRoot":"","sources":["../../../src/error/service-not-found.error.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAEvC;;GAEG;AACH,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAa7C,YAAY,UAA6B;;QACvC,KAAK,EAAE,CAAC;QAbH,SAAI,GAAG,sBAAsB,CAAC;QAErC,4DAA4D;QACpD,yBAAoB,GAAW,sBAAsB,CAAC;QAY5D,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE;YAClC,IAAI,CAAC,oBAAoB,GAAG,UAAU,CAAC;SACxC;aAAM,IAAI,UAAU,YAAY,KAAK,EAAE;YACtC,IAAI,CAAC,oBAAoB,GAAG,SAAS,UAAU,CAAC,IAAI,IAAI,YAAY,GAAG,CAAC;SACzE;aAAM,IAAI,UAAU,IAAI,CAAC,UAAU,CAAC,IAAI,WAAI,UAAU,CAAC,SAAS,0CAAE,IAAI,CAAA,CAAC,EAAE;YACxE,IAAI,CAAC,oBAAoB;gBACvB,sBAAsB,UAAU,CAAC,IAAI,GAAG;oBACxC,sBAAsB,MAAC,UAAU,CAAC,SAA8B,0CAAE,IAAI,GAAG,CAAC;SAC7E;IACH,CAAC;IAnBD,IAAI,OAAO;QACT,OAAO,CACL,iBAAiB,IAAI,CAAC,oBAAoB,+CAA+C;YACzF,mHAAmH,CACpH,CAAC;IACJ,CAAC;CAeF","sourcesContent":["import { ServiceIdentifier } from '../types/service-identifier.type';\nimport { Token } from '../token.class';\n\n/**\n * Thrown when requested service was not found.\n */\nexport class ServiceNotFoundError extends Error {\n public name = 'ServiceNotFoundError';\n\n /** Normalized identifier name used in the error message. */\n private normalizedIdentifier: string = '<UNKNOWN_IDENTIFIER>';\n\n get message(): string {\n return (\n `Service with \"${this.normalizedIdentifier}\" identifier was not found in the container. ` +\n `Register it before usage via explicitly calling the \"Container.set\" function or using the \"@Service()\" decorator.`\n );\n }\n\n constructor(identifier: ServiceIdentifier) {\n super();\n\n if (typeof identifier === 'string') {\n this.normalizedIdentifier = identifier;\n } else if (identifier instanceof Token) {\n this.normalizedIdentifier = `Token<${identifier.name || 'UNSET_NAME'}>`;\n } else if (identifier && (identifier.name || identifier.prototype?.name)) {\n this.normalizedIdentifier =\n `MaybeConstructable<${identifier.name}>` ||\n `MaybeConstructable<${(identifier.prototype as { name: string })?.name}>`;\n }\n }\n}\n"]}
+20
View File
@@ -0,0 +1,20 @@
/**
* We have a hard dependency on reflect-metadata package.
* Without the dependency lookup wont work. So we should warn the users
* when it's not loaded.
*/
// if(!Reflect || !(Reflect as any).getMetadata) {
// throw new Error('Reflect.getMetadata is not a function. Please import the "reflect-metadata" package at the first line of your application.');
// }
import { Container } from './container.class';
export * from './decorators/inject-many.decorator';
export * from './decorators/inject.decorator';
export * from './decorators/service.decorator';
export * from './error/cannot-inject-value.error';
export * from './error/cannot-instantiate-value.error';
export * from './error/service-not-found.error';
export { ContainerInstance } from './container-instance.class';
export { Container } from './container.class';
export { Token } from './token.class';
export default Container;
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,kDAAkD;AAClD,mJAAmJ;AACnJ,IAAI;AAEJ,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,cAAc,oCAAoC,CAAC;AACnD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gCAAgC,CAAC;AAE/C,cAAc,mCAAmC,CAAC;AAClD,cAAc,wCAAwC,CAAC;AACvD,cAAc,iCAAiC,CAAC;AAQhD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAEtC,eAAe,SAAS,CAAC","sourcesContent":["/**\n * We have a hard dependency on reflect-metadata package.\n * Without the dependency lookup wont work. So we should warn the users\n * when it's not loaded.\n */\n// if(!Reflect || !(Reflect as any).getMetadata) {\n// throw new Error('Reflect.getMetadata is not a function. Please import the \"reflect-metadata\" package at the first line of your application.');\n// }\n\nimport { Container } from './container.class';\n\nexport * from './decorators/inject-many.decorator';\nexport * from './decorators/inject.decorator';\nexport * from './decorators/service.decorator';\n\nexport * from './error/cannot-inject-value.error';\nexport * from './error/cannot-instantiate-value.error';\nexport * from './error/service-not-found.error';\n\nexport { Handler } from './interfaces/handler.interface';\nexport { ServiceMetadata } from './interfaces/service-metadata.interface';\nexport { ServiceOptions } from './interfaces/service-options.interface';\nexport { Constructable } from './types/constructable.type';\nexport { ServiceIdentifier } from './types/service-identifier.type';\n\nexport { ContainerInstance } from './container-instance.class';\nexport { Container } from './container.class';\nexport { Token } from './token.class';\n\nexport default Container;\n"]}
+2
View File
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=handler.interface.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"handler.interface.js","sourceRoot":"","sources":["../../../src/interfaces/handler.interface.ts"],"names":[],"mappings":"","sourcesContent":["import { ContainerInstance } from '../container-instance.class';\nimport { Constructable } from '../types/constructable.type';\n\n/**\n * Used to register special \"handler\" which will be executed on a service class during its initialization.\n * It can be used to create custom decorators and set/replace service class properties and constructor parameters.\n */\nexport interface Handler<T = unknown> {\n /**\n * Service object used to apply handler to.\n */\n object: Constructable<T>;\n\n /**\n * Class property name to set/replace value of.\n * Used if handler is applied on a class property.\n */\n propertyName?: string;\n\n /**\n * Parameter index to set/replace value of.\n * Used if handler is applied on a constructor parameter.\n */\n index?: number;\n\n /**\n * Factory function that produces value that will be set to class property or constructor parameter.\n * Accepts container instance which requested the value.\n */\n value: (container: ContainerInstance) => any;\n}\n"]}
+2
View File
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=service-metadata.interface.js.map
@@ -0,0 +1 @@
{"version":3,"file":"service-metadata.interface.js","sourceRoot":"","sources":["../../../src/interfaces/service-metadata.interface.ts"],"names":[],"mappings":"","sourcesContent":["import { Constructable } from '../types/constructable.type';\nimport { ServiceIdentifier } from '../types/service-identifier.type';\n\n/**\n * Service metadata is used to initialize service and store its state.\n */\nexport interface ServiceMetadata<Type = unknown> {\n /** Unique identifier of the referenced service. */\n id: ServiceIdentifier;\n\n /**\n * Class definition of the service what is used to initialize given service.\n * This property maybe null if the value of the service is set manually.\n * If id is not set then it serves as service id.\n */\n type: Constructable<Type> | null;\n\n /**\n * Indicates if this service must be global and same instance must be used across all containers.\n */\n global: boolean;\n\n /**\n * Indicates whether a new instance of this class must be created for each class injecting this class.\n * Global option is ignored when this option is used.\n */\n transient: boolean;\n\n /**\n * Allows to setup multiple instances the different classes under a single service id string or token.\n */\n multiple: boolean;\n\n /**\n * Indicates whether a new instance should be created as soon as the class is registered.\n * By default the registered classes are only instantiated when they are requested from the container.\n */\n eager?: boolean;\n\n /**\n * Factory function used to initialize this service.\n * Can be regular function (\"createCar\" for example),\n * or other service which produces this instance ([CarFactory, \"createCar\"] for example).\n */\n factory: [Constructable<unknown>, string] | CallableFunction | undefined;\n\n /**\n * Instance of the target class.\n */\n value: unknown | Symbol;\n}\n"]}
+2
View File
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=service-options.interface.js.map
@@ -0,0 +1 @@
{"version":3,"file":"service-options.interface.js","sourceRoot":"","sources":["../../../src/interfaces/service-options.interface.ts"],"names":[],"mappings":"","sourcesContent":["import { ServiceMetadata } from './service-metadata.interface';\n\n/**\n * The public ServiceOptions is partial object of ServiceMetadata and either one\n * of the following is set: `type`, `factory`, `value` but not more than one.\n */\nexport type ServiceOptions<T = unknown> =\n | Omit<Partial<ServiceMetadata<T>>, 'type' | 'factory'>\n | Omit<Partial<ServiceMetadata<T>>, 'value' | 'factory'>\n | Omit<Partial<ServiceMetadata<T>>, 'value' | 'type'>;\n"]}
+13
View File
@@ -0,0 +1,13 @@
/**
* Used to create unique typed service identifier.
* Useful when service has only interface, but don't have a class.
*/
export class Token {
/**
* @param name Token name, optional and only used for debugging purposes.
*/
constructor(name) {
this.name = name;
}
}
//# sourceMappingURL=token.class.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"token.class.js","sourceRoot":"","sources":["../../src/token.class.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,OAAO,KAAK;IAChB;;OAEG;IACH,YAAmB,IAAa;QAAb,SAAI,GAAJ,IAAI,CAAS;IAAG,CAAC;CACrC","sourcesContent":["/**\n * Used to create unique typed service identifier.\n * Useful when service has only interface, but don't have a class.\n */\nexport class Token<T> {\n /**\n * @param name Token name, optional and only used for debugging purposes.\n */\n constructor(public name?: string) {}\n}\n"]}
+2
View File
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=abstract-constructable.type.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"abstract-constructable.type.js","sourceRoot":"","sources":["../../../src/types/abstract-constructable.type.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Generic type for abstract class definitions.\n *\n * Explanation: This describes a callable Function with a prototype Which is\n * what an abstract class is - no constructor, just the prototype.\n */\nexport type AbstractConstructable<T> = CallableFunction & { prototype: T };\n"]}
+2
View File
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=constructable.type.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"constructable.type.js","sourceRoot":"","sources":["../../../src/types/constructable.type.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Generic type for class definitions.\n * Example usage:\n * ```\n * function createSomeInstance(myClassDefinition: Constructable<MyClass>) {\n * return new myClassDefinition()\n * }\n * ```\n */\nexport type Constructable<T> = new (...args: any[]) => T;\n"]}
+2
View File
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=service-identifier.type.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"service-identifier.type.js","sourceRoot":"","sources":["../../../src/types/service-identifier.type.ts"],"names":[],"mappings":"","sourcesContent":["import { Token } from '../token.class';\nimport { Constructable } from './constructable.type';\nimport { AbstractConstructable } from './abstract-constructable.type';\n\n/**\n * Unique service identifier.\n * Can be some class type, or string id, or instance of Token.\n */\nexport type ServiceIdentifier<T = unknown> =\n | Constructable<T>\n | AbstractConstructable<T>\n | CallableFunction\n | Token<T>\n | string;\n"]}
+45
View File
@@ -0,0 +1,45 @@
import { Token } from '../token.class';
/**
* Helper function used in inject decorators to resolve the received identifier to
* an eager type when possible or to a lazy type when cyclic dependencies are possibly involved.
*
* @param typeOrIdentifier a service identifier or a function returning a type acting as service identifier or nothing
* @param target the class definition of the target of the decorator
* @param propertyName the name of the property in case of a PropertyDecorator
* @param index the index of the parameter in the constructor in case of ParameterDecorator
*/
export function resolveToTypeWrapper(typeOrIdentifier, target, propertyName, index) {
/**
* ? We want to error out as soon as possible when looking up services to inject, however
* ? we cannot determine the type at decorator execution when cyclic dependencies are involved
* ? because calling the received `() => MyType` function right away would cause a JS error:
* ? "Cannot access 'MyType' before initialization", so we need to execute the function in the handler,
* ? when the classes are already created. To overcome this, we use a wrapper:
* ? - the lazyType is executed in the handler so we never have a JS error
* ? - the eagerType is checked when decorator is running and an error is raised if an unknown type is encountered
*/
let typeWrapper;
/** If requested type is explicitly set via a string ID or token, we set it explicitly. */
if ((typeOrIdentifier && typeof typeOrIdentifier === 'string') || typeOrIdentifier instanceof Token) {
typeWrapper = { eagerType: typeOrIdentifier, lazyType: () => typeOrIdentifier };
}
/** If requested type is explicitly set via a () => MyClassType format, we set it explicitly. */
if (typeOrIdentifier && typeof typeOrIdentifier === 'function') {
/** We set eagerType to null, preventing the raising of the CannotInjectValueError in decorators. */
typeWrapper = { eagerType: null, lazyType: () => typeOrIdentifier() };
}
/** If no explicit type is set and handler registered for a class property, we need to get the property type. */
if (!typeOrIdentifier && propertyName) {
const identifier = Reflect.getMetadata('design:type', target, propertyName);
typeWrapper = { eagerType: identifier, lazyType: () => identifier };
}
/** If no explicit type is set and handler registered for a constructor parameter, we need to get the parameter types. */
if (!typeOrIdentifier && typeof index == 'number' && Number.isInteger(index)) {
const paramTypes = Reflect.getMetadata('design:paramtypes', target, propertyName);
/** It's not guaranteed, that we find any types for the constructor. */
const identifier = paramTypes === null || paramTypes === void 0 ? void 0 : paramTypes[index];
typeWrapper = { eagerType: identifier, lazyType: () => identifier };
}
return typeWrapper;
}
//# sourceMappingURL=resolve-to-type-wrapper.util.js.map
@@ -0,0 +1 @@
{"version":3,"file":"resolve-to-type-wrapper.util.js","sourceRoot":"","sources":["../../../src/utils/resolve-to-type-wrapper.util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAIvC;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAClC,gBAAqG,EACrG,MAAc,EACd,YAA6B,EAC7B,KAAc;IAEd;;;;;;;;OAQG;IACH,IAAI,WAAoG,CAAC;IAEzG,0FAA0F;IAC1F,IAAI,CAAC,gBAAgB,IAAI,OAAO,gBAAgB,KAAK,QAAQ,CAAC,IAAI,gBAAgB,YAAY,KAAK,EAAE;QACnG,WAAW,GAAG,EAAE,SAAS,EAAE,gBAAgB,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,gBAAgB,EAAE,CAAC;KACjF;IAED,gGAAgG;IAChG,IAAI,gBAAgB,IAAI,OAAO,gBAAgB,KAAK,UAAU,EAAE;QAC9D,qGAAqG;QACrG,WAAW,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAE,gBAAqC,EAAE,EAAE,CAAC;KAC7F;IAED,gHAAgH;IAChH,IAAI,CAAC,gBAAgB,IAAI,YAAY,EAAE;QACrC,MAAM,UAAU,GAAI,OAAe,CAAC,WAAW,CAAC,aAAa,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC;QAErF,WAAW,GAAG,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC;KACrE;IAED,yHAAyH;IACzH,IAAI,CAAC,gBAAgB,IAAI,OAAO,KAAK,IAAI,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE;QAC5E,MAAM,UAAU,GAAyB,OAAe,CAAC,WAAW,CAAC,mBAAmB,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC;QAChH,uEAAuE;QACvE,MAAM,UAAU,GAAG,UAAU,aAAV,UAAU,uBAAV,UAAU,CAAG,KAAK,CAAC,CAAC;QAEvC,WAAW,GAAG,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC;KACrE;IAED,OAAO,WAAW,CAAC;AACrB,CAAC","sourcesContent":["import { Token } from '../token.class';\nimport { Constructable } from '../types/constructable.type';\nimport { ServiceIdentifier } from '../types/service-identifier.type';\n\n/**\n * Helper function used in inject decorators to resolve the received identifier to\n * an eager type when possible or to a lazy type when cyclic dependencies are possibly involved.\n *\n * @param typeOrIdentifier a service identifier or a function returning a type acting as service identifier or nothing\n * @param target the class definition of the target of the decorator\n * @param propertyName the name of the property in case of a PropertyDecorator\n * @param index the index of the parameter in the constructor in case of ParameterDecorator\n */\nexport function resolveToTypeWrapper(\n typeOrIdentifier: ((type?: never) => Constructable<unknown>) | ServiceIdentifier<unknown> | undefined,\n target: Object,\n propertyName: string | Symbol,\n index?: number\n): { eagerType: ServiceIdentifier | null; lazyType: (type?: never) => ServiceIdentifier } {\n /**\n * ? We want to error out as soon as possible when looking up services to inject, however\n * ? we cannot determine the type at decorator execution when cyclic dependencies are involved\n * ? because calling the received `() => MyType` function right away would cause a JS error:\n * ? \"Cannot access 'MyType' before initialization\", so we need to execute the function in the handler,\n * ? when the classes are already created. To overcome this, we use a wrapper:\n * ? - the lazyType is executed in the handler so we never have a JS error\n * ? - the eagerType is checked when decorator is running and an error is raised if an unknown type is encountered\n */\n let typeWrapper!: { eagerType: ServiceIdentifier | null; lazyType: (type?: never) => ServiceIdentifier };\n\n /** If requested type is explicitly set via a string ID or token, we set it explicitly. */\n if ((typeOrIdentifier && typeof typeOrIdentifier === 'string') || typeOrIdentifier instanceof Token) {\n typeWrapper = { eagerType: typeOrIdentifier, lazyType: () => typeOrIdentifier };\n }\n\n /** If requested type is explicitly set via a () => MyClassType format, we set it explicitly. */\n if (typeOrIdentifier && typeof typeOrIdentifier === 'function') {\n /** We set eagerType to null, preventing the raising of the CannotInjectValueError in decorators. */\n typeWrapper = { eagerType: null, lazyType: () => (typeOrIdentifier as CallableFunction)() };\n }\n\n /** If no explicit type is set and handler registered for a class property, we need to get the property type. */\n if (!typeOrIdentifier && propertyName) {\n const identifier = (Reflect as any).getMetadata('design:type', target, propertyName);\n\n typeWrapper = { eagerType: identifier, lazyType: () => identifier };\n }\n\n /** If no explicit type is set and handler registered for a constructor parameter, we need to get the parameter types. */\n if (!typeOrIdentifier && typeof index == 'number' && Number.isInteger(index)) {\n const paramTypes: ServiceIdentifier[] = (Reflect as any).getMetadata('design:paramtypes', target, propertyName);\n /** It's not guaranteed, that we find any types for the constructor. */\n const identifier = paramTypes?.[index];\n\n typeWrapper = { eagerType: identifier, lazyType: () => identifier };\n }\n\n return typeWrapper;\n}\n"]}
+318
View File
@@ -0,0 +1,318 @@
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
import { Container } from './container.class';
import { ServiceNotFoundError } from './error/service-not-found.error';
import { CannotInstantiateValueError } from './error/cannot-instantiate-value.error';
import { Token } from './token.class';
import { EMPTY_VALUE } from './empty.const';
/**
* TypeDI can have multiple containers.
* One container is ContainerInstance.
*/
var ContainerInstance = /** @class */ (function () {
function ContainerInstance(id) {
/** All registered services in the container. */
this.services = [];
this.id = id;
}
ContainerInstance.prototype.has = function (identifier) {
return !!this.findService(identifier);
};
ContainerInstance.prototype.get = function (identifier) {
var globalContainer = Container.of(undefined);
var globalService = globalContainer.findService(identifier);
var scopedService = this.findService(identifier);
if (globalService && globalService.global === true)
return this.getServiceValue(globalService);
if (scopedService)
return this.getServiceValue(scopedService);
/** If it's the first time requested in the child container we load it from parent and set it. */
if (globalService && this !== globalContainer) {
var clonedService = __assign({}, globalService);
clonedService.value = EMPTY_VALUE;
/**
* We need to immediately set the empty value from the root container
* to prevent infinite lookup in cyclic dependencies.
*/
this.set(clonedService);
var value = this.getServiceValue(clonedService);
this.set(__assign(__assign({}, clonedService), { value: value }));
return value;
}
if (globalService)
return this.getServiceValue(globalService);
throw new ServiceNotFoundError(identifier);
};
ContainerInstance.prototype.getMany = function (identifier) {
var _this = this;
return this.findAllServices(identifier).map(function (service) { return _this.getServiceValue(service); });
};
ContainerInstance.prototype.set = function (identifierOrServiceMetadata, value) {
var _this = this;
if (identifierOrServiceMetadata instanceof Array) {
identifierOrServiceMetadata.forEach(function (data) { return _this.set(data); });
return this;
}
if (typeof identifierOrServiceMetadata === 'string' || identifierOrServiceMetadata instanceof Token) {
return this.set({
id: identifierOrServiceMetadata,
type: null,
value: value,
factory: undefined,
global: false,
multiple: false,
eager: false,
transient: false,
});
}
if (typeof identifierOrServiceMetadata === 'function') {
return this.set({
id: identifierOrServiceMetadata,
// TODO: remove explicit casting
type: identifierOrServiceMetadata,
value: value,
factory: undefined,
global: false,
multiple: false,
eager: false,
transient: false,
});
}
var newService = __assign({ id: new Token('UNREACHABLE'), type: null, factory: undefined, value: EMPTY_VALUE, global: false, multiple: false, eager: false, transient: false }, identifierOrServiceMetadata);
var service = this.findService(newService.id);
if (service && service.multiple !== true) {
Object.assign(service, newService);
}
else {
this.services.push(newService);
}
if (newService.eager) {
this.get(newService.id);
}
return this;
};
/**
* Removes services with a given service identifiers.
*/
ContainerInstance.prototype.remove = function (identifierOrIdentifierArray) {
var _this = this;
if (Array.isArray(identifierOrIdentifierArray)) {
identifierOrIdentifierArray.forEach(function (id) { return _this.remove(id); });
}
else {
this.services = this.services.filter(function (service) {
if (service.id === identifierOrIdentifierArray) {
_this.destroyServiceInstance(service);
return false;
}
return true;
});
}
return this;
};
/**
* Completely resets the container by removing all previously registered services from it.
*/
ContainerInstance.prototype.reset = function (options) {
var _this = this;
if (options === void 0) { options = { strategy: 'resetValue' }; }
switch (options.strategy) {
case 'resetValue':
this.services.forEach(function (service) { return _this.destroyServiceInstance(service); });
break;
case 'resetServices':
this.services.forEach(function (service) { return _this.destroyServiceInstance(service); });
this.services = [];
break;
default:
throw new Error('Received invalid reset strategy.');
}
return this;
};
/**
* Returns all services registered with the given identifier.
*/
ContainerInstance.prototype.findAllServices = function (identifier) {
return this.services.filter(function (service) { return service.id === identifier; });
};
/**
* Finds registered service in the with a given service identifier.
*/
ContainerInstance.prototype.findService = function (identifier) {
return this.services.find(function (service) { return service.id === identifier; });
};
/**
* Gets the value belonging to `serviceMetadata.id`.
*
* - if `serviceMetadata.value` is already set it is immediately returned
* - otherwise the requested type is resolved to the value saved to `serviceMetadata.value` and returned
*/
ContainerInstance.prototype.getServiceValue = function (serviceMetadata) {
var _a;
var value = EMPTY_VALUE;
/**
* If the service value has been set to anything prior to this call we return that value.
* NOTE: This part builds on the assumption that transient dependencies has no value set ever.
*/
if (serviceMetadata.value !== EMPTY_VALUE) {
return serviceMetadata.value;
}
/** If both factory and type is missing, we cannot resolve the requested ID. */
if (!serviceMetadata.factory && !serviceMetadata.type) {
throw new CannotInstantiateValueError(serviceMetadata.id);
}
/**
* If a factory is defined it takes priority over creating an instance via `new`.
* The return value of the factory is not checked, we believe by design that the user knows what he/she is doing.
*/
if (serviceMetadata.factory) {
/**
* If we received the factory in the [Constructable<Factory>, "functionName"] format, we need to create the
* factory first and then call the specified function on it.
*/
if (serviceMetadata.factory instanceof Array) {
var factoryInstance = void 0;
try {
/** Try to get the factory from TypeDI first, if failed, fall back to simply initiating the class. */
factoryInstance = this.get(serviceMetadata.factory[0]);
}
catch (error) {
if (error instanceof ServiceNotFoundError) {
factoryInstance = new serviceMetadata.factory[0]();
}
else {
throw error;
}
}
value = factoryInstance[serviceMetadata.factory[1]](this, serviceMetadata.id);
}
else {
/** If only a simple function was provided we simply call it. */
value = serviceMetadata.factory(this, serviceMetadata.id);
}
}
/**
* If no factory was provided and only then, we create the instance from the type if it was set.
*/
if (!serviceMetadata.factory && serviceMetadata.type) {
var constructableTargetType = serviceMetadata.type;
// setup constructor parameters for a newly initialized service
var paramTypes = ((_a = Reflect) === null || _a === void 0 ? void 0 : _a.getMetadata('design:paramtypes', constructableTargetType)) || [];
var params = this.initializeParams(constructableTargetType, paramTypes);
// "extra feature" - always pass container instance as the last argument to the service function
// this allows us to support javascript where we don't have decorators and emitted metadata about dependencies
// need to be injected, and user can use provided container to get instances he needs
params.push(this);
value = new (constructableTargetType.bind.apply(constructableTargetType, __spreadArrays([void 0], params)))();
// TODO: Calling this here, leads to infinite loop, because @Inject decorator registerds a handler
// TODO: which calls Container.get, which will check if the requested type has a value set and if not
// TODO: it will start the instantiation process over. So this is currently called outside of the if branch
// TODO: after the current value has been assigned to the serviceMetadata.
// this.applyPropertyHandlers(constructableTargetType, value as Constructable<unknown>);
}
/** If this is not a transient service, and we resolved something, then we set it as the value. */
if (!serviceMetadata.transient && value !== EMPTY_VALUE) {
serviceMetadata.value = value;
}
if (value === EMPTY_VALUE) {
/** This branch should never execute, but better to be safe than sorry. */
throw new CannotInstantiateValueError(serviceMetadata.id);
}
if (serviceMetadata.type) {
this.applyPropertyHandlers(serviceMetadata.type, value);
}
return value;
};
/**
* Initializes all parameter types for a given target service class.
*/
ContainerInstance.prototype.initializeParams = function (target, paramTypes) {
var _this = this;
return paramTypes.map(function (paramType, index) {
var paramHandler = Container.handlers.find(function (handler) {
/**
* @Inject()-ed values are stored as parameter handlers and they reference their target
* when created. So when a class is extended the @Inject()-ed values are not inherited
* because the handler still points to the old object only.
*
* As a quick fix a single level parent lookup is added via `Object.getPrototypeOf(target)`,
* however this should be updated to a more robust solution.
*
* TODO: Add proper inheritance handling: either copy the handlers when a class is registered what
* TODO: has it's parent already registered as dependency or make the lookup search up to the base Object.
*/
return ((handler.object === target || handler.object === Object.getPrototypeOf(target)) && handler.index === index);
});
if (paramHandler)
return paramHandler.value(_this);
if (paramType && paramType.name && !_this.isPrimitiveParamType(paramType.name)) {
return _this.get(paramType);
}
return undefined;
});
};
/**
* Checks if given parameter type is primitive type or not.
*/
ContainerInstance.prototype.isPrimitiveParamType = function (paramTypeName) {
return ['string', 'boolean', 'number', 'object'].includes(paramTypeName.toLowerCase());
};
/**
* Applies all registered handlers on a given target class.
*/
ContainerInstance.prototype.applyPropertyHandlers = function (target, instance) {
var _this = this;
Container.handlers.forEach(function (handler) {
if (typeof handler.index === 'number')
return;
if (handler.object.constructor !== target && !(target.prototype instanceof handler.object.constructor))
return;
if (handler.propertyName) {
instance[handler.propertyName] = handler.value(_this);
}
});
};
/**
* Checks if the given service metadata contains a destroyable service instance and destroys it in place. If the service
* contains a callable function named `destroy` it is called but not awaited and the return value is ignored..
*
* @param serviceMetadata the service metadata containing the instance to destroy
* @param force when true the service will be always destroyed even if it's cannot be re-created
*/
ContainerInstance.prototype.destroyServiceInstance = function (serviceMetadata, force) {
if (force === void 0) { force = false; }
/** We reset value only if we can re-create it (aka type or factory exists). */
var shouldResetValue = force || !!serviceMetadata.type || !!serviceMetadata.factory;
if (shouldResetValue) {
/** If we wound a function named destroy we call it without any params. */
if (typeof (serviceMetadata === null || serviceMetadata === void 0 ? void 0 : serviceMetadata.value)['destroy'] === 'function') {
try {
serviceMetadata.value.destroy();
}
catch (error) {
/** We simply ignore the errors from the destroy function. */
}
}
serviceMetadata.value = EMPTY_VALUE;
}
};
return ContainerInstance;
}());
export { ContainerInstance };
//# sourceMappingURL=container-instance.class.js.map
File diff suppressed because one or more lines are too long
+87
View File
@@ -0,0 +1,87 @@
import { ContainerInstance } from './container-instance.class';
/**
* Service container.
*/
var Container = /** @class */ (function () {
function Container() {
}
/**
* Gets a separate container instance for the given instance id.
*/
Container.of = function (containerId) {
if (containerId === void 0) { containerId = 'default'; }
if (containerId === 'default')
return this.globalInstance;
var container = this.instances.find(function (instance) { return instance.id === containerId; });
if (!container) {
container = new ContainerInstance(containerId);
this.instances.push(container);
// TODO: Why we are not reseting here? Let's reset here. (I have added the commented code.)
// container.reset();
}
return container;
};
Container.has = function (identifier) {
return this.globalInstance.has(identifier);
};
Container.get = function (identifier) {
return this.globalInstance.get(identifier);
};
Container.getMany = function (id) {
return this.globalInstance.getMany(id);
};
Container.set = function (identifierOrServiceMetadata, value) {
this.globalInstance.set(identifierOrServiceMetadata, value);
return this;
};
/**
* Removes services with a given service identifiers.
*/
Container.remove = function (identifierOrIdentifierArray) {
this.globalInstance.remove(identifierOrIdentifierArray);
return this;
};
/**
* Completely resets the container by removing all previously registered services and handlers from it.
*/
Container.reset = function (containerId) {
if (containerId === void 0) { containerId = 'default'; }
if (containerId == 'default') {
this.globalInstance.reset();
this.instances.forEach(function (instance) { return instance.reset(); });
}
else {
var instance = this.instances.find(function (instance) { return instance.id === containerId; });
if (instance) {
instance.reset();
this.instances.splice(this.instances.indexOf(instance), 1);
}
}
return this;
};
/**
* Registers a new handler.
*/
Container.registerHandler = function (handler) {
this.handlers.push(handler);
return this;
};
/**
* Helper method that imports given services.
*/
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
Container.import = function (services) {
return this;
};
/**
* All registered handlers. The @Inject() decorator uses handlers internally to mark a property for injection.
**/
Container.handlers = [];
/** Global container instance. */
Container.globalInstance = new ContainerInstance('default');
/** Other containers created using Container.of method. */
Container.instances = [];
return Container;
}());
export { Container };
//# sourceMappingURL=container.class.js.map
File diff suppressed because one or more lines are too long
+26
View File
@@ -0,0 +1,26 @@
import { Container } from '../container.class';
import { CannotInjectValueError } from '../error/cannot-inject-value.error';
import { resolveToTypeWrapper } from '../utils/resolve-to-type-wrapper.util';
export function InjectMany(typeOrIdentifier) {
return function (target, propertyName, index) {
var typeWrapper = resolveToTypeWrapper(typeOrIdentifier, target, propertyName, index);
/** If no type was inferred, or the general Object type was inferred we throw an error. */
if (typeWrapper === undefined || typeWrapper.eagerType === undefined || typeWrapper.eagerType === Object) {
throw new CannotInjectValueError(target, propertyName);
}
Container.registerHandler({
object: target,
propertyName: propertyName,
index: index,
value: function (containerInstance) {
var evaluatedLazyType = typeWrapper.lazyType();
/** If no type was inferred lazily, or the general Object type was inferred we throw an error. */
if (evaluatedLazyType === undefined || evaluatedLazyType === Object) {
throw new CannotInjectValueError(target, propertyName);
}
return containerInstance.getMany(evaluatedLazyType);
},
});
};
}
//# sourceMappingURL=inject-many.decorator.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"inject-many.decorator.js","sourceRoot":"","sources":["../../../src/decorators/inject-many.decorator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAW7E,MAAM,UAAU,UAAU,CACxB,gBAA0F;IAE1F,OAAO,UAAU,MAAc,EAAE,YAA6B,EAAE,KAAc;QAC5E,IAAM,WAAW,GAAG,oBAAoB,CAAC,gBAAgB,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC;QAExF,0FAA0F;QAC1F,IAAI,WAAW,KAAK,SAAS,IAAI,WAAW,CAAC,SAAS,KAAK,SAAS,IAAI,WAAW,CAAC,SAAS,KAAK,MAAM,EAAE;YACxG,MAAM,IAAI,sBAAsB,CAAC,MAAgC,EAAE,YAAsB,CAAC,CAAC;SAC5F;QAED,SAAS,CAAC,eAAe,CAAC;YACxB,MAAM,EAAE,MAAgC;YACxC,YAAY,EAAE,YAAsB;YACpC,KAAK,EAAE,KAAK;YACZ,KAAK,EAAE,UAAA,iBAAiB;gBACtB,IAAM,iBAAiB,GAAG,WAAW,CAAC,QAAQ,EAAE,CAAC;gBAEjD,iGAAiG;gBACjG,IAAI,iBAAiB,KAAK,SAAS,IAAI,iBAAiB,KAAK,MAAM,EAAE;oBACnE,MAAM,IAAI,sBAAsB,CAAC,MAAgC,EAAE,YAAsB,CAAC,CAAC;iBAC5F;gBAED,OAAO,iBAAiB,CAAC,OAAO,CAAU,iBAAiB,CAAC,CAAC;YAC/D,CAAC;SACF,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import { Container } from '../container.class';\nimport { Token } from '../token.class';\nimport { CannotInjectValueError } from '../error/cannot-inject-value.error';\nimport { resolveToTypeWrapper } from '../utils/resolve-to-type-wrapper.util';\nimport { Constructable } from '../types/constructable.type';\nimport { ServiceIdentifier } from '../types/service-identifier.type';\n\n/**\n * Injects a list of services into a class property or constructor parameter.\n */\nexport function InjectMany(): Function;\nexport function InjectMany(type?: (type?: any) => Function): Function;\nexport function InjectMany(serviceName?: string): Function;\nexport function InjectMany(token: Token<any>): Function;\nexport function InjectMany(\n typeOrIdentifier?: ((type?: never) => Constructable<unknown>) | ServiceIdentifier<unknown>\n): Function {\n return function (target: Object, propertyName: string | Symbol, index?: number): void {\n const typeWrapper = resolveToTypeWrapper(typeOrIdentifier, target, propertyName, index);\n\n /** If no type was inferred, or the general Object type was inferred we throw an error. */\n if (typeWrapper === undefined || typeWrapper.eagerType === undefined || typeWrapper.eagerType === Object) {\n throw new CannotInjectValueError(target as Constructable<unknown>, propertyName as string);\n }\n\n Container.registerHandler({\n object: target as Constructable<unknown>,\n propertyName: propertyName as string,\n index: index,\n value: containerInstance => {\n const evaluatedLazyType = typeWrapper.lazyType();\n\n /** If no type was inferred lazily, or the general Object type was inferred we throw an error. */\n if (evaluatedLazyType === undefined || evaluatedLazyType === Object) {\n throw new CannotInjectValueError(target as Constructable<unknown>, propertyName as string);\n }\n\n return containerInstance.getMany<unknown>(evaluatedLazyType);\n },\n });\n };\n}\n"]}
+26
View File
@@ -0,0 +1,26 @@
import { Container } from '../container.class';
import { CannotInjectValueError } from '../error/cannot-inject-value.error';
import { resolveToTypeWrapper } from '../utils/resolve-to-type-wrapper.util';
export function Inject(typeOrIdentifier) {
return function (target, propertyName, index) {
var typeWrapper = resolveToTypeWrapper(typeOrIdentifier, target, propertyName, index);
/** If no type was inferred, or the general Object type was inferred we throw an error. */
if (typeWrapper === undefined || typeWrapper.eagerType === undefined || typeWrapper.eagerType === Object) {
throw new CannotInjectValueError(target, propertyName);
}
Container.registerHandler({
object: target,
propertyName: propertyName,
index: index,
value: function (containerInstance) {
var evaluatedLazyType = typeWrapper.lazyType();
/** If no type was inferred lazily, or the general Object type was inferred we throw an error. */
if (evaluatedLazyType === undefined || evaluatedLazyType === Object) {
throw new CannotInjectValueError(target, propertyName);
}
return containerInstance.get(evaluatedLazyType);
},
});
};
}
//# sourceMappingURL=inject.decorator.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"inject.decorator.js","sourceRoot":"","sources":["../../../src/decorators/inject.decorator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAG5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAS7E,MAAM,UAAU,MAAM,CACpB,gBAA0F;IAE1F,OAAO,UAAU,MAAc,EAAE,YAA6B,EAAE,KAAc;QAC5E,IAAM,WAAW,GAAG,oBAAoB,CAAC,gBAAgB,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC;QAExF,0FAA0F;QAC1F,IAAI,WAAW,KAAK,SAAS,IAAI,WAAW,CAAC,SAAS,KAAK,SAAS,IAAI,WAAW,CAAC,SAAS,KAAK,MAAM,EAAE;YACxG,MAAM,IAAI,sBAAsB,CAAC,MAAgC,EAAE,YAAsB,CAAC,CAAC;SAC5F;QAED,SAAS,CAAC,eAAe,CAAC;YACxB,MAAM,EAAE,MAAgC;YACxC,YAAY,EAAE,YAAsB;YACpC,KAAK,EAAE,KAAK;YACZ,KAAK,EAAE,UAAA,iBAAiB;gBACtB,IAAM,iBAAiB,GAAG,WAAW,CAAC,QAAQ,EAAE,CAAC;gBAEjD,iGAAiG;gBACjG,IAAI,iBAAiB,KAAK,SAAS,IAAI,iBAAiB,KAAK,MAAM,EAAE;oBACnE,MAAM,IAAI,sBAAsB,CAAC,MAAgC,EAAE,YAAsB,CAAC,CAAC;iBAC5F;gBAED,OAAO,iBAAiB,CAAC,GAAG,CAAU,iBAAiB,CAAC,CAAC;YAC3D,CAAC;SACF,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import { Container } from '../container.class';\nimport { Token } from '../token.class';\nimport { CannotInjectValueError } from '../error/cannot-inject-value.error';\nimport { ServiceIdentifier } from '../types/service-identifier.type';\nimport { Constructable } from '../types/constructable.type';\nimport { resolveToTypeWrapper } from '../utils/resolve-to-type-wrapper.util';\n\n/**\n * Injects a service into a class property or constructor parameter.\n */\nexport function Inject(): Function;\nexport function Inject(typeFn: (type?: never) => Constructable<unknown>): Function;\nexport function Inject(serviceName?: string): Function;\nexport function Inject(token: Token<unknown>): Function;\nexport function Inject(\n typeOrIdentifier?: ((type?: never) => Constructable<unknown>) | ServiceIdentifier<unknown>\n): ParameterDecorator | PropertyDecorator {\n return function (target: Object, propertyName: string | Symbol, index?: number): void {\n const typeWrapper = resolveToTypeWrapper(typeOrIdentifier, target, propertyName, index);\n\n /** If no type was inferred, or the general Object type was inferred we throw an error. */\n if (typeWrapper === undefined || typeWrapper.eagerType === undefined || typeWrapper.eagerType === Object) {\n throw new CannotInjectValueError(target as Constructable<unknown>, propertyName as string);\n }\n\n Container.registerHandler({\n object: target as Constructable<unknown>,\n propertyName: propertyName as string,\n index: index,\n value: containerInstance => {\n const evaluatedLazyType = typeWrapper.lazyType();\n\n /** If no type was inferred lazily, or the general Object type was inferred we throw an error. */\n if (evaluatedLazyType === undefined || evaluatedLazyType === Object) {\n throw new CannotInjectValueError(target as Constructable<unknown>, propertyName as string);\n }\n\n return containerInstance.get<unknown>(evaluatedLazyType);\n },\n });\n };\n}\n"]}
+33
View File
@@ -0,0 +1,33 @@
import { Container } from '../container.class';
import { Token } from '../token.class';
import { EMPTY_VALUE } from '../empty.const';
export function Service(optionsOrServiceIdentifier) {
return function (targetConstructor) {
var serviceMetadata = {
id: targetConstructor,
// TODO: Let's investigate why we receive Function type instead of a constructable.
type: targetConstructor,
factory: undefined,
multiple: false,
global: false,
eager: false,
transient: false,
value: EMPTY_VALUE,
};
if (optionsOrServiceIdentifier instanceof Token || typeof optionsOrServiceIdentifier === 'string') {
/** We received a Token or string ID. */
serviceMetadata.id = optionsOrServiceIdentifier;
}
else if (optionsOrServiceIdentifier) {
/** We received a ServiceOptions object. */
serviceMetadata.id = optionsOrServiceIdentifier.id || targetConstructor;
serviceMetadata.factory = optionsOrServiceIdentifier.factory || undefined;
serviceMetadata.multiple = optionsOrServiceIdentifier.multiple || false;
serviceMetadata.global = optionsOrServiceIdentifier.global || false;
serviceMetadata.eager = optionsOrServiceIdentifier.eager || false;
serviceMetadata.transient = optionsOrServiceIdentifier.transient || false;
}
Container.set(serviceMetadata);
};
}
//# sourceMappingURL=service.decorator.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"service.decorator.js","sourceRoot":"","sources":["../../../src/decorators/service.decorator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAGvC,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAU7C,MAAM,UAAU,OAAO,CAAI,0BAAoE;IAC7F,OAAO,UAAA,iBAAiB;QACtB,IAAM,eAAe,GAAuB;YAC1C,EAAE,EAAE,iBAAiB;YACrB,mFAAmF;YACnF,IAAI,EAAG,iBAAiD;YACxD,OAAO,EAAE,SAAS;YAClB,QAAQ,EAAE,KAAK;YACf,MAAM,EAAE,KAAK;YACb,KAAK,EAAE,KAAK;YACZ,SAAS,EAAE,KAAK;YAChB,KAAK,EAAE,WAAW;SACnB,CAAC;QAEF,IAAI,0BAA0B,YAAY,KAAK,IAAI,OAAO,0BAA0B,KAAK,QAAQ,EAAE;YACjG,wCAAwC;YACxC,eAAe,CAAC,EAAE,GAAG,0BAA0B,CAAC;SACjD;aAAM,IAAI,0BAA0B,EAAE;YACrC,2CAA2C;YAC3C,eAAe,CAAC,EAAE,GAAI,0BAA8C,CAAC,EAAE,IAAI,iBAAiB,CAAC;YAC7F,eAAe,CAAC,OAAO,GAAI,0BAA8C,CAAC,OAAO,IAAI,SAAS,CAAC;YAC/F,eAAe,CAAC,QAAQ,GAAI,0BAA8C,CAAC,QAAQ,IAAI,KAAK,CAAC;YAC7F,eAAe,CAAC,MAAM,GAAI,0BAA8C,CAAC,MAAM,IAAI,KAAK,CAAC;YACzF,eAAe,CAAC,KAAK,GAAI,0BAA8C,CAAC,KAAK,IAAI,KAAK,CAAC;YACvF,eAAe,CAAC,SAAS,GAAI,0BAA8C,CAAC,SAAS,IAAI,KAAK,CAAC;SAChG;QAED,SAAS,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IACjC,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import { Container } from '../container.class';\nimport { Token } from '../token.class';\nimport { ServiceMetadata } from '../interfaces/service-metadata.interface';\nimport { ServiceOptions } from '../interfaces/service-options.interface';\nimport { EMPTY_VALUE } from '../empty.const';\nimport { Constructable } from '../types/constructable.type';\n\n/**\n * Marks class as a service that can be injected using Container.\n */\nexport function Service<T = unknown>(): Function;\nexport function Service<T = unknown>(name: string): Function;\nexport function Service<T = unknown>(token: Token<unknown>): Function;\nexport function Service<T = unknown>(options?: ServiceOptions<T>): Function;\nexport function Service<T>(optionsOrServiceIdentifier?: ServiceOptions<T> | Token<any> | string): ClassDecorator {\n return targetConstructor => {\n const serviceMetadata: ServiceMetadata<T> = {\n id: targetConstructor,\n // TODO: Let's investigate why we receive Function type instead of a constructable.\n type: (targetConstructor as unknown) as Constructable<T>,\n factory: undefined,\n multiple: false,\n global: false,\n eager: false,\n transient: false,\n value: EMPTY_VALUE,\n };\n\n if (optionsOrServiceIdentifier instanceof Token || typeof optionsOrServiceIdentifier === 'string') {\n /** We received a Token or string ID. */\n serviceMetadata.id = optionsOrServiceIdentifier;\n } else if (optionsOrServiceIdentifier) {\n /** We received a ServiceOptions object. */\n serviceMetadata.id = (optionsOrServiceIdentifier as ServiceMetadata).id || targetConstructor;\n serviceMetadata.factory = (optionsOrServiceIdentifier as ServiceMetadata).factory || undefined;\n serviceMetadata.multiple = (optionsOrServiceIdentifier as ServiceMetadata).multiple || false;\n serviceMetadata.global = (optionsOrServiceIdentifier as ServiceMetadata).global || false;\n serviceMetadata.eager = (optionsOrServiceIdentifier as ServiceMetadata).eager || false;\n serviceMetadata.transient = (optionsOrServiceIdentifier as ServiceMetadata).transient || false;\n }\n\n Container.set(serviceMetadata);\n };\n}\n"]}
+2
View File
@@ -0,0 +1,2 @@
export var EMPTY_VALUE = Symbol('EMPTY_VALUE');
//# sourceMappingURL=empty.const.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"empty.const.js","sourceRoot":"","sources":["../../src/empty.const.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,IAAM,WAAW,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC","sourcesContent":["export const EMPTY_VALUE = Symbol('EMPTY_VALUE');\n"]}
+37
View File
@@ -0,0 +1,37 @@
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
/**
* Thrown when DI cannot inject value into property decorated by @Inject decorator.
*/
var CannotInjectValueError = /** @class */ (function (_super) {
__extends(CannotInjectValueError, _super);
function CannotInjectValueError(target, propertyName) {
var _this = _super.call(this) || this;
_this.target = target;
_this.propertyName = propertyName;
_this.name = 'CannotInjectValueError';
return _this;
}
Object.defineProperty(CannotInjectValueError.prototype, "message", {
get: function () {
return ("Cannot inject value into \"" + this.target.constructor.name + "." + this.propertyName + "\". " +
"Please make sure you setup reflect-metadata properly and you don't use interfaces without service tokens as injection value.");
},
enumerable: false,
configurable: true
});
return CannotInjectValueError;
}(Error));
export { CannotInjectValueError };
//# sourceMappingURL=cannot-inject-value.error.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"cannot-inject-value.error.js","sourceRoot":"","sources":["../../../src/error/cannot-inject-value.error.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAEA;;GAEG;AACH;IAA4C,0CAAK;IAU/C,gCAAoB,MAA8B,EAAU,YAAoB;QAAhF,YACE,iBAAO,SACR;QAFmB,YAAM,GAAN,MAAM,CAAwB;QAAU,kBAAY,GAAZ,YAAY,CAAQ;QATzE,UAAI,GAAG,wBAAwB,CAAC;;IAWvC,CAAC;IATD,sBAAI,2CAAO;aAAX;YACE,OAAO,CACL,gCAA6B,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,SAAI,IAAI,CAAC,YAAY,SAAK;gBACnF,8HAA8H,CAC/H,CAAC;QACJ,CAAC;;;OAAA;IAKH,6BAAC;AAAD,CAAC,AAbD,CAA4C,KAAK,GAahD","sourcesContent":["import { Constructable } from '../types/constructable.type';\n\n/**\n * Thrown when DI cannot inject value into property decorated by @Inject decorator.\n */\nexport class CannotInjectValueError extends Error {\n public name = 'CannotInjectValueError';\n\n get message(): string {\n return (\n `Cannot inject value into \"${this.target.constructor.name}.${this.propertyName}\". ` +\n `Please make sure you setup reflect-metadata properly and you don't use interfaces without service tokens as injection value.`\n );\n }\n\n constructor(private target: Constructable<unknown>, private propertyName: string) {\n super();\n }\n}\n"]}
+51
View File
@@ -0,0 +1,51 @@
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
import { Token } from '../token.class';
/**
* Thrown when DI cannot inject value into property decorated by @Inject decorator.
*/
var CannotInstantiateValueError = /** @class */ (function (_super) {
__extends(CannotInstantiateValueError, _super);
function CannotInstantiateValueError(identifier) {
var _a, _b;
var _this = _super.call(this) || this;
_this.name = 'CannotInstantiateValueError';
/** Normalized identifier name used in the error message. */
_this.normalizedIdentifier = '<UNKNOWN_IDENTIFIER>';
// TODO: Extract this to a helper function and share between this and NotFoundError.
if (typeof identifier === 'string') {
_this.normalizedIdentifier = identifier;
}
else if (identifier instanceof Token) {
_this.normalizedIdentifier = "Token<" + (identifier.name || 'UNSET_NAME') + ">";
}
else if (identifier && (identifier.name || ((_a = identifier.prototype) === null || _a === void 0 ? void 0 : _a.name))) {
_this.normalizedIdentifier =
"MaybeConstructable<" + identifier.name + ">" ||
"MaybeConstructable<" + ((_b = identifier.prototype) === null || _b === void 0 ? void 0 : _b.name) + ">";
}
return _this;
}
Object.defineProperty(CannotInstantiateValueError.prototype, "message", {
get: function () {
return ("Cannot instantiate the requested value for the \"" + this.normalizedIdentifier + "\" identifier. " +
"The related metadata doesn't contain a factory or a type to instantiate.");
},
enumerable: false,
configurable: true
});
return CannotInstantiateValueError;
}(Error));
export { CannotInstantiateValueError };
//# sourceMappingURL=cannot-instantiate-value.error.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"cannot-instantiate-value.error.js","sourceRoot":"","sources":["../../../src/error/cannot-instantiate-value.error.ts"],"names":[],"mappings":";;;;;;;;;;;;;AACA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAEvC;;GAEG;AACH;IAAiD,+CAAK;IAapD,qCAAY,UAA6B;;QAAzC,YACE,iBAAO,SAYR;QAzBM,UAAI,GAAG,6BAA6B,CAAC;QAE5C,4DAA4D;QACpD,0BAAoB,GAAW,sBAAsB,CAAC;QAY5D,oFAAoF;QACpF,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE;YAClC,KAAI,CAAC,oBAAoB,GAAG,UAAU,CAAC;SACxC;aAAM,IAAI,UAAU,YAAY,KAAK,EAAE;YACtC,KAAI,CAAC,oBAAoB,GAAG,YAAS,UAAU,CAAC,IAAI,IAAI,YAAY,OAAG,CAAC;SACzE;aAAM,IAAI,UAAU,IAAI,CAAC,UAAU,CAAC,IAAI,WAAI,UAAU,CAAC,SAAS,0CAAE,IAAI,CAAA,CAAC,EAAE;YACxE,KAAI,CAAC,oBAAoB;gBACvB,wBAAsB,UAAU,CAAC,IAAI,MAAG;oBACxC,+BAAuB,UAAU,CAAC,SAA8B,0CAAE,IAAI,OAAG,CAAC;SAC7E;;IACH,CAAC;IApBD,sBAAI,gDAAO;aAAX;YACE,OAAO,CACL,sDAAmD,IAAI,CAAC,oBAAoB,oBAAgB;gBAC5F,0EAA0E,CAC3E,CAAC;QACJ,CAAC;;;OAAA;IAgBH,kCAAC;AAAD,CAAC,AA3BD,CAAiD,KAAK,GA2BrD","sourcesContent":["import { ServiceIdentifier } from '../types/service-identifier.type';\nimport { Token } from '../token.class';\n\n/**\n * Thrown when DI cannot inject value into property decorated by @Inject decorator.\n */\nexport class CannotInstantiateValueError extends Error {\n public name = 'CannotInstantiateValueError';\n\n /** Normalized identifier name used in the error message. */\n private normalizedIdentifier: string = '<UNKNOWN_IDENTIFIER>';\n\n get message(): string {\n return (\n `Cannot instantiate the requested value for the \"${this.normalizedIdentifier}\" identifier. ` +\n `The related metadata doesn't contain a factory or a type to instantiate.`\n );\n }\n\n constructor(identifier: ServiceIdentifier) {\n super();\n\n // TODO: Extract this to a helper function and share between this and NotFoundError.\n if (typeof identifier === 'string') {\n this.normalizedIdentifier = identifier;\n } else if (identifier instanceof Token) {\n this.normalizedIdentifier = `Token<${identifier.name || 'UNSET_NAME'}>`;\n } else if (identifier && (identifier.name || identifier.prototype?.name)) {\n this.normalizedIdentifier =\n `MaybeConstructable<${identifier.name}>` ||\n `MaybeConstructable<${(identifier.prototype as { name: string })?.name}>`;\n }\n }\n}\n"]}
+50
View File
@@ -0,0 +1,50 @@
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
import { Token } from '../token.class';
/**
* Thrown when requested service was not found.
*/
var ServiceNotFoundError = /** @class */ (function (_super) {
__extends(ServiceNotFoundError, _super);
function ServiceNotFoundError(identifier) {
var _a, _b;
var _this = _super.call(this) || this;
_this.name = 'ServiceNotFoundError';
/** Normalized identifier name used in the error message. */
_this.normalizedIdentifier = '<UNKNOWN_IDENTIFIER>';
if (typeof identifier === 'string') {
_this.normalizedIdentifier = identifier;
}
else if (identifier instanceof Token) {
_this.normalizedIdentifier = "Token<" + (identifier.name || 'UNSET_NAME') + ">";
}
else if (identifier && (identifier.name || ((_a = identifier.prototype) === null || _a === void 0 ? void 0 : _a.name))) {
_this.normalizedIdentifier =
"MaybeConstructable<" + identifier.name + ">" ||
"MaybeConstructable<" + ((_b = identifier.prototype) === null || _b === void 0 ? void 0 : _b.name) + ">";
}
return _this;
}
Object.defineProperty(ServiceNotFoundError.prototype, "message", {
get: function () {
return ("Service with \"" + this.normalizedIdentifier + "\" identifier was not found in the container. " +
"Register it before usage via explicitly calling the \"Container.set\" function or using the \"@Service()\" decorator.");
},
enumerable: false,
configurable: true
});
return ServiceNotFoundError;
}(Error));
export { ServiceNotFoundError };
//# sourceMappingURL=service-not-found.error.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"service-not-found.error.js","sourceRoot":"","sources":["../../../src/error/service-not-found.error.ts"],"names":[],"mappings":";;;;;;;;;;;;;AACA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAEvC;;GAEG;AACH;IAA0C,wCAAK;IAa7C,8BAAY,UAA6B;;QAAzC,YACE,iBAAO,SAWR;QAxBM,UAAI,GAAG,sBAAsB,CAAC;QAErC,4DAA4D;QACpD,0BAAoB,GAAW,sBAAsB,CAAC;QAY5D,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE;YAClC,KAAI,CAAC,oBAAoB,GAAG,UAAU,CAAC;SACxC;aAAM,IAAI,UAAU,YAAY,KAAK,EAAE;YACtC,KAAI,CAAC,oBAAoB,GAAG,YAAS,UAAU,CAAC,IAAI,IAAI,YAAY,OAAG,CAAC;SACzE;aAAM,IAAI,UAAU,IAAI,CAAC,UAAU,CAAC,IAAI,WAAI,UAAU,CAAC,SAAS,0CAAE,IAAI,CAAA,CAAC,EAAE;YACxE,KAAI,CAAC,oBAAoB;gBACvB,wBAAsB,UAAU,CAAC,IAAI,MAAG;oBACxC,+BAAuB,UAAU,CAAC,SAA8B,0CAAE,IAAI,OAAG,CAAC;SAC7E;;IACH,CAAC;IAnBD,sBAAI,yCAAO;aAAX;YACE,OAAO,CACL,oBAAiB,IAAI,CAAC,oBAAoB,mDAA+C;gBACzF,uHAAmH,CACpH,CAAC;QACJ,CAAC;;;OAAA;IAeH,2BAAC;AAAD,CAAC,AA1BD,CAA0C,KAAK,GA0B9C","sourcesContent":["import { ServiceIdentifier } from '../types/service-identifier.type';\nimport { Token } from '../token.class';\n\n/**\n * Thrown when requested service was not found.\n */\nexport class ServiceNotFoundError extends Error {\n public name = 'ServiceNotFoundError';\n\n /** Normalized identifier name used in the error message. */\n private normalizedIdentifier: string = '<UNKNOWN_IDENTIFIER>';\n\n get message(): string {\n return (\n `Service with \"${this.normalizedIdentifier}\" identifier was not found in the container. ` +\n `Register it before usage via explicitly calling the \"Container.set\" function or using the \"@Service()\" decorator.`\n );\n }\n\n constructor(identifier: ServiceIdentifier) {\n super();\n\n if (typeof identifier === 'string') {\n this.normalizedIdentifier = identifier;\n } else if (identifier instanceof Token) {\n this.normalizedIdentifier = `Token<${identifier.name || 'UNSET_NAME'}>`;\n } else if (identifier && (identifier.name || identifier.prototype?.name)) {\n this.normalizedIdentifier =\n `MaybeConstructable<${identifier.name}>` ||\n `MaybeConstructable<${(identifier.prototype as { name: string })?.name}>`;\n }\n }\n}\n"]}
+20
View File
@@ -0,0 +1,20 @@
/**
* We have a hard dependency on reflect-metadata package.
* Without the dependency lookup wont work. So we should warn the users
* when it's not loaded.
*/
// if(!Reflect || !(Reflect as any).getMetadata) {
// throw new Error('Reflect.getMetadata is not a function. Please import the "reflect-metadata" package at the first line of your application.');
// }
import { Container } from './container.class';
export * from './decorators/inject-many.decorator';
export * from './decorators/inject.decorator';
export * from './decorators/service.decorator';
export * from './error/cannot-inject-value.error';
export * from './error/cannot-instantiate-value.error';
export * from './error/service-not-found.error';
export { ContainerInstance } from './container-instance.class';
export { Container } from './container.class';
export { Token } from './token.class';
export default Container;
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,kDAAkD;AAClD,mJAAmJ;AACnJ,IAAI;AAEJ,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,cAAc,oCAAoC,CAAC;AACnD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gCAAgC,CAAC;AAE/C,cAAc,mCAAmC,CAAC;AAClD,cAAc,wCAAwC,CAAC;AACvD,cAAc,iCAAiC,CAAC;AAQhD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAEtC,eAAe,SAAS,CAAC","sourcesContent":["/**\n * We have a hard dependency on reflect-metadata package.\n * Without the dependency lookup wont work. So we should warn the users\n * when it's not loaded.\n */\n// if(!Reflect || !(Reflect as any).getMetadata) {\n// throw new Error('Reflect.getMetadata is not a function. Please import the \"reflect-metadata\" package at the first line of your application.');\n// }\n\nimport { Container } from './container.class';\n\nexport * from './decorators/inject-many.decorator';\nexport * from './decorators/inject.decorator';\nexport * from './decorators/service.decorator';\n\nexport * from './error/cannot-inject-value.error';\nexport * from './error/cannot-instantiate-value.error';\nexport * from './error/service-not-found.error';\n\nexport { Handler } from './interfaces/handler.interface';\nexport { ServiceMetadata } from './interfaces/service-metadata.interface';\nexport { ServiceOptions } from './interfaces/service-options.interface';\nexport { Constructable } from './types/constructable.type';\nexport { ServiceIdentifier } from './types/service-identifier.type';\n\nexport { ContainerInstance } from './container-instance.class';\nexport { Container } from './container.class';\nexport { Token } from './token.class';\n\nexport default Container;\n"]}
+2
View File
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=handler.interface.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"handler.interface.js","sourceRoot":"","sources":["../../../src/interfaces/handler.interface.ts"],"names":[],"mappings":"","sourcesContent":["import { ContainerInstance } from '../container-instance.class';\nimport { Constructable } from '../types/constructable.type';\n\n/**\n * Used to register special \"handler\" which will be executed on a service class during its initialization.\n * It can be used to create custom decorators and set/replace service class properties and constructor parameters.\n */\nexport interface Handler<T = unknown> {\n /**\n * Service object used to apply handler to.\n */\n object: Constructable<T>;\n\n /**\n * Class property name to set/replace value of.\n * Used if handler is applied on a class property.\n */\n propertyName?: string;\n\n /**\n * Parameter index to set/replace value of.\n * Used if handler is applied on a constructor parameter.\n */\n index?: number;\n\n /**\n * Factory function that produces value that will be set to class property or constructor parameter.\n * Accepts container instance which requested the value.\n */\n value: (container: ContainerInstance) => any;\n}\n"]}

Some files were not shown because too many files have changed in this diff Show More