nano-injector 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Roman Pukhalskyi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,221 @@
1
+ # Nano-injector
2
+
3
+ Miniature dependency injection library for TypeScript and JavaScript.
4
+
5
+ # Motivation
6
+
7
+ There is a myriad of dependency injection libraries in typescript ecosystem. Most of them are built around decorators.
8
+ Decorators are an obvious solution for such kinds of tasks. But in terms of dependency injection, they have its
9
+ drawbacks - they are not type safe, which means that theoretically, it is possible to bind any value to the required type,
10
+ and they don't work well with interfaces and primitives, therefore some workaround solutions are required to overcome
11
+ this limitation.
12
+ Typically, injecting of an interface, using these libraries, looks like as follows:
13
+ ```typescript
14
+ // binding value
15
+ injector.bind('IStorage').toValue(new DefStorage())
16
+
17
+ // injecting it
18
+ @Inject('IStorage')
19
+ private storage: IStorage
20
+ ```
21
+ where string IStorage should be passed to Inject decorator in order to let the injector know which value should be injected
22
+ to the required property. Since it is just a string, any string can be passed, therefore any type can be injected,
23
+ and the compiler won't warn you about that. Also, there is type duplication, therefore the whole usage looks a bit ugly.
24
+
25
+ **Nano-injector** is addressing these issues, by doing dependency injection in a little bit different way - instead of using
26
+ decorators, it uses plain functions as dependencies providers. By doing so, the same workflow, using Nano-injector,
27
+ looks as follows:
28
+
29
+ ```typescript
30
+ // defining provider
31
+ const $IStorage = createProvider<IStorage>()
32
+
33
+ // binding it to the value
34
+ injector.bindProvider($IStorage).toValue(new DefStorage())
35
+
36
+ // injecting it
37
+ private storage = $IStorage()
38
+ ```
39
+ Here _**$IStorage**_ is the provider, which basically is a plain function, and which should be bound to desired value through
40
+ injector. Type of storage property compiler infers automatically, which reduces unneeded code duplication. Also, you can bind only
41
+ value of the type specified during provider creation, if not to do so, the compiler will warn you about that.
42
+
43
+ Also, unlike other libraries, Nano-injector is very small, with a very concise API.
44
+ More usage details you can find below, and in the example directory.
45
+
46
+ # Features
47
+
48
+ - Very simple
49
+ - Ultra lightweight
50
+ - Zero dependencies
51
+ - No decorators
52
+ - Type safe
53
+ - Injectable interfaces and primitives
54
+
55
+ # Installation
56
+ ```bash
57
+ npm i nano-injector
58
+ ```
59
+ # Usage
60
+ Importing dependencies:
61
+ ```typescript
62
+ import { Injector, createProvider } from 'nano-injector'
63
+ ```
64
+ Defining types and providers:
65
+ ```typescript
66
+ // to distinguish providers from any other entities $ sign is used
67
+ const $Clock = createProvider<(cb: () => void, time: number) => number>()
68
+ const $ClockRate = createProvider<number>()
69
+
70
+ const $CPU = createProvider<CPU>()
71
+ interface CPU {
72
+ readonly model: string
73
+ readonly numCores: number
74
+ readonly frequency: number
75
+ }
76
+
77
+ const $GPU = createProvider<GPU>()
78
+ interface GPU {
79
+ readonly model: string
80
+ readonly numRayTracingCores: number
81
+ readonly memorySize: number
82
+ }
83
+
84
+ const $RAM = createProvider<RAM>()
85
+ interface RAM {
86
+ readonly capacity: number
87
+ }
88
+
89
+ const $Motherboard = createProvider<Motherboard>()
90
+ interface Motherboard {
91
+ readonly cpu: CPU
92
+ readonly gpu: GPU
93
+ readonly ram: RAM
94
+ }
95
+
96
+ class GeForce911 implements GPU {
97
+ model = 'GeForce911'
98
+ numRayTracingCores = 100
99
+ memorySize = 2048
100
+ }
101
+
102
+ class DefMotherboard implements Motherboard {
103
+ constructor(
104
+ readonly model: string,
105
+ public cpu = $CPU(),
106
+ public gpu = $GPU(),
107
+ public ram = $RAM()
108
+ ) {}
109
+ }
110
+
111
+ class PC {
112
+ constructor(private name: string, private motherboard = $Motherboard()) {}
113
+ }
114
+ ```
115
+ Defining injector through which injection occurs:
116
+ ```typescript
117
+ const injector = new Injector()
118
+ ```
119
+ Binding providers:
120
+ ```typescript
121
+ // binding $CPU provider to exact value
122
+ injector.bindProvider($CPU).toValue({ numCores: 4, model: 't7', frequency: 2000 })
123
+
124
+ // binding $GPU provider to class which conforms to provider's returning type
125
+ // calling asSingleton specifies that value should be created only once
126
+ injector.bindProvider($GPU).toConstructor(GeForce911).asSingleton()
127
+
128
+ // binding $RAM provider to the factory which creates value conforming to
129
+ // provider's returning type
130
+ injector.bindProvider($RAM).toFactory(() => ({
131
+ capacity: Math.floor(Math.random() * 1024),
132
+ }))
133
+
134
+ injector
135
+ .bindProvider($Motherboard)
136
+ .toFactory(
137
+ () =>
138
+ // only one required parameter is passed, the rest are initialized
139
+ // automatically through providers
140
+ new DefMotherboard('Asus ABC-123')
141
+ )
142
+ .asSingleton()
143
+
144
+ // without ignoring compiler would tell you about wrong value's type
145
+ // @ts-expect-error
146
+ injector.bindProvider($ClockRate).toValue('asd')
147
+
148
+ injector.bindProvider($ClockRate).toValue(1000)
149
+
150
+ injector.bindProvider($Clock).toValue(setTimeout)
151
+ ```
152
+ Creating instances with all dependencies injected:
153
+ ```typescript
154
+ // the first parameter of PC constructor is required, so it is passed into the
155
+ // construction method
156
+ injector.createInstance(PC, 'My pc')
157
+ ```
158
+ Directly getting the bound to the providers values:
159
+ ```typescript
160
+ injector.getValue($CPU)
161
+ injector.getValue($GPU)
162
+ injector.getValue($RAM)
163
+
164
+ injector.getValue($Clock)(() => console.log('Tick!'), 1000)
165
+ ```
166
+ Manually creating instance with all its dependencies:
167
+ ```typescript
168
+ new DefMotherboard(
169
+ 'Asus xyz',
170
+ { frequency: 123, model: 'Intel xyz', numCores: 1 },
171
+ { model: '', memorySize: 0, numRayTracingCores: 1 },
172
+ { capacity: 123 },
173
+ )
174
+ ```
175
+ Calling function through injector:
176
+ ```typescript
177
+ injector.callFunc(() => {
178
+ // inside function all providers return bound to them values
179
+ console.log('Inside function')
180
+ console.log('CPU:', $CPU())
181
+ console.log('GPU:', $GPU())
182
+ console.log('RAM:', $RAM())
183
+ })
184
+ ```
185
+ It's also possible to bind few providers to the same value. The bound value should conform to
186
+ intersection of all providers' types:
187
+ ```typescript
188
+ injector.bindProvider($RAM, $CPU, $GPU).toValue({
189
+ capacity: 10,
190
+ frequency: 100,
191
+ memorySize: 200,
192
+ model: 'ATB-21',
193
+ numCores: 2,
194
+ numRayTracingCores: 60,
195
+ })
196
+ ```
197
+ Creating composition of injectors:
198
+ ```typescript
199
+ const childInjector = new Injector({ parent: injector })
200
+ ```
201
+ Overriding $CPU binding of parent injector inside child injector:
202
+ ```typescript
203
+ childInjector.bindProvider($CPU).toValue({ numCores: 1, model: 'z2', frequency: 999 })
204
+ ```
205
+ Injecting value to the existing instance:
206
+ ```typescript
207
+ const newMotherboard = new class {
208
+ gpu: GPU
209
+ cpu: CPU
210
+ }
211
+ injector.injectValues(newMotherboard, { cpu: $CPU, gpu: $GPU })
212
+ ```
213
+ # [Docs](https://protoukr.github.io/nano-injector/)
214
+
215
+ # Contributing
216
+ Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
217
+
218
+ Please make sure to update tests as appropriate.
219
+
220
+ ## License
221
+ [MIT](https://choosealicense.com/licenses/mit/)
package/lib/Binder.js ADDED
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Binder = void 0;
4
+ const InjectingError_1 = require("./InjectingError");
5
+ /**
6
+ * Class through which is defined how to create value for this binder
7
+ */
8
+ class Binder {
9
+ constructor(injector) {
10
+ this.injector = injector;
11
+ }
12
+ /**
13
+ * Creates the new value if needed through previously defined method, and returns it
14
+ */
15
+ getValue() {
16
+ if (this.value !== undefined) {
17
+ return this.value;
18
+ }
19
+ if (this.isSingleton) {
20
+ this.value = this.createValue();
21
+ return this.value;
22
+ }
23
+ return this.createValue();
24
+ }
25
+ /**
26
+ * Directly defines value for this binder
27
+ * @param value
28
+ */
29
+ toValue(value) {
30
+ this.value = value;
31
+ this.factory = undefined;
32
+ this.ctor = undefined;
33
+ return this;
34
+ }
35
+ /**
36
+ * Defines constructor as a method for creating value. All previously defined methods
37
+ * will be ignored
38
+ * @param ctor
39
+ */
40
+ toConstructor(ctor) {
41
+ this.ctor = ctor;
42
+ this.factory = undefined;
43
+ this.value = undefined;
44
+ return this;
45
+ }
46
+ /**
47
+ * Defines factory as a method for creating value. All previously defined methods
48
+ * will be ignored
49
+ * @param factory
50
+ */
51
+ toFactory(factory) {
52
+ this.factory = factory;
53
+ this.ctor = undefined;
54
+ this.value = undefined;
55
+ return this;
56
+ }
57
+ /**
58
+ * Defines whether value should be a singleton. If yes the value will be created only once
59
+ * and the same instance will be returned forever
60
+ * @param value
61
+ */
62
+ asSingleton(value = true) {
63
+ this.isSingleton = value;
64
+ return this;
65
+ }
66
+ /**
67
+ * Creates value through previously defined method
68
+ * @private
69
+ */
70
+ createValue() {
71
+ if (this.ctor != null) {
72
+ // eslint-disable-next-line new-cap
73
+ return new this.ctor();
74
+ }
75
+ if (this.factory != null) {
76
+ return this.factory(this.injector);
77
+ }
78
+ throw new InjectingError_1.InjectingError('Creation method isn\'t specified');
79
+ }
80
+ }
81
+ exports.Binder = Binder;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InjectingError = void 0;
4
+ /**
5
+ * Just an error class, an instance of which is fired in some cases
6
+ */
7
+ class InjectingError extends Error {
8
+ }
9
+ exports.InjectingError = InjectingError;
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Injector = exports.$Injector = void 0;
4
+ const Binder_1 = require("./Binder");
5
+ const Provider_1 = require("./Provider");
6
+ const InjectorsStack_1 = require("./InjectorsStack");
7
+ const InjectingError_1 = require("./InjectingError");
8
+ /**
9
+ * Provider which every injector binds itself to
10
+ */
11
+ exports.$Injector = (0, Provider_1.createProvider)();
12
+ /**
13
+ * Main entity in the library, which holds provider's bindings and through which
14
+ * dependencies are resolved
15
+ */
16
+ class Injector {
17
+ /**
18
+ * @param params.name name of the injector which can be useful mainly for debugging purposes
19
+ * @param params.parent parent injector for the composition of injectors
20
+ * @param params.logger specific log function if the custom output is required
21
+ */
22
+ constructor(params = {}) {
23
+ this.binders = new Map();
24
+ this.resolvingProviders = [];
25
+ const { name, parent, logger = console.warn } = params;
26
+ this.name = name;
27
+ this.parent = parent;
28
+ this.logger = logger;
29
+ this.bindProvider(exports.$Injector).toValue(this);
30
+ }
31
+ /**
32
+ * Creates new binder and joins it to the specified providers. If the provider
33
+ * is already bound, then overriding occurs
34
+ * @param providers providers which the binder should be joined to
35
+ */
36
+ bindProvider(...providers) {
37
+ const binder = new Binder_1.Binder(this);
38
+ providers.forEach((provider) => this.binders.set(provider, binder));
39
+ return binder;
40
+ }
41
+ /**
42
+ * Resolves all providers to their values and assigns them to the specified instance
43
+ * @param instance
44
+ * @param providers
45
+ */
46
+ injectValues(instance, providers) {
47
+ Object.entries(providers)
48
+ .filter(([, value]) => (0, Provider_1.isProvider)(value))
49
+ .forEach(([name, provider]) => {
50
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
51
+ instance[name] = this.getValue(provider);
52
+ });
53
+ }
54
+ /**
55
+ * Activates this injector and creates new instance of the type with the provided arguments
56
+ * @param type
57
+ * @param args
58
+ */
59
+ createInstance(type, ...args) {
60
+ // eslint-disable-next-line new-cap
61
+ return this.activateAndCall(() => new type(...args));
62
+ }
63
+ /**
64
+ * Activates this injector and calls the function with provided arguments
65
+ * @param func function which should be called
66
+ * @param args args which should be passed to the called function
67
+ */
68
+ callFunc(func, ...args) {
69
+ return this.activateAndCall(() => func(...args));
70
+ }
71
+ /**
72
+ * Returns bound to the specified provider value. If the value is not found
73
+ * undefined is returned
74
+ * @param provider
75
+ */
76
+ getValue(provider) {
77
+ const binder = this.getBinder(provider);
78
+ if (binder == null) {
79
+ return undefined;
80
+ }
81
+ this.checkCircularDependency(provider);
82
+ this.resolvingProviders.push(provider);
83
+ const value = this.activateAndCall(() => binder.getValue());
84
+ this.resolvingProviders.pop();
85
+ return value;
86
+ }
87
+ /**
88
+ * Finds binder for the specified provider recursively up to the root injector
89
+ * @param provider
90
+ * @private
91
+ */
92
+ getBinder(provider) {
93
+ const binder = this.binders.get(provider);
94
+ if (binder == null && this.parent != null) {
95
+ return this.parent.getBinder(provider);
96
+ }
97
+ return binder;
98
+ }
99
+ /**
100
+ * Checks is there circular dependency and throws error if so
101
+ * @param provider
102
+ * @private
103
+ */
104
+ checkCircularDependency(provider) {
105
+ const i = this.resolvingProviders.indexOf(provider);
106
+ if (i === -1) {
107
+ return;
108
+ }
109
+ const providers = [...this.resolvingProviders.slice(i), provider]
110
+ .map(Provider_1.getProviderName)
111
+ .map((name) => name !== null && name !== void 0 ? name : '?');
112
+ throw new InjectingError_1.InjectingError(`Circular dependency detected: ${providers.join('->')}`);
113
+ }
114
+ /**
115
+ * Temporary activates this injector calls provided function and returns its value
116
+ * @param func
117
+ * @private
118
+ */
119
+ activateAndCall(func) {
120
+ InjectorsStack_1.InjectorsStack.push(this);
121
+ const value = func();
122
+ InjectorsStack_1.InjectorsStack.pop();
123
+ return value;
124
+ }
125
+ }
126
+ exports.Injector = Injector;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InjectorsStack = void 0;
4
+ /**
5
+ * Private class for holding current active injector
6
+ */
7
+ class _InjectorsStack {
8
+ constructor() {
9
+ this.injectors = [];
10
+ }
11
+ get(provider) {
12
+ var _a;
13
+ return (_a = this.activeInjector) === null || _a === void 0 ? void 0 : _a.getValue(provider);
14
+ }
15
+ push(injector) {
16
+ if (this.activeInjector != null) {
17
+ this.injectors.push(this.activeInjector);
18
+ }
19
+ this.activeInjector = injector;
20
+ }
21
+ pop() {
22
+ this.activeInjector = this.injectors.pop();
23
+ }
24
+ }
25
+ exports.InjectorsStack = new _InjectorsStack();
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getProviderName = exports.getProviderID = exports.isProvider = exports.createProvider = void 0;
4
+ const InjectorsStack_1 = require("./InjectorsStack");
5
+ const InjectingError_1 = require("./InjectingError");
6
+ /**
7
+ * Symbol used for storing the id of a provider
8
+ */
9
+ const ID_SYMBOL = Symbol('id');
10
+ /**
11
+ * Symbol used for storing the name of a provider
12
+ */
13
+ const NAME_SYMBOL = Symbol('name');
14
+ let PROVIDER_ID = 0;
15
+ /**
16
+ * Creates the new provider for some value with a specific type
17
+ * @param name name for this provider used mainly for debugging purposes
18
+ */
19
+ function createProvider(name) {
20
+ function provider(...args) {
21
+ const value = InjectorsStack_1.InjectorsStack.get(provider);
22
+ if (value !== undefined) {
23
+ return value;
24
+ }
25
+ if (args.length !== 0) {
26
+ return args[0];
27
+ }
28
+ throw new InjectingError_1.InjectingError(`Value of ${name !== null && name !== void 0 ? name : 'unknown'} provider is not found`);
29
+ }
30
+ provider[NAME_SYMBOL] = name;
31
+ provider[ID_SYMBOL] = PROVIDER_ID++;
32
+ return provider;
33
+ }
34
+ exports.createProvider = createProvider;
35
+ /**
36
+ * Determines whether the received value is a provider
37
+ * @param value value which should be tested
38
+ */
39
+ function isProvider(value) {
40
+ if (typeof value !== 'function') {
41
+ return false;
42
+ }
43
+ const provider = value;
44
+ return typeof provider[ID_SYMBOL] !== 'undefined';
45
+ }
46
+ exports.isProvider = isProvider;
47
+ /**
48
+ * Returns the id of the specified provider
49
+ * @param provider
50
+ */
51
+ function getProviderID(provider) {
52
+ return provider[ID_SYMBOL];
53
+ }
54
+ exports.getProviderID = getProviderID;
55
+ /**
56
+ * Returns the name of the specified provider
57
+ * @param provider
58
+ */
59
+ function getProviderName(provider) {
60
+ return provider[NAME_SYMBOL];
61
+ }
62
+ exports.getProviderName = getProviderName;
package/lib/index.js ADDED
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
10
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ __exportStar(require("./Injector"), exports);
14
+ __exportStar(require("./Provider"), exports);
15
+ __exportStar(require("./InjectingError"), exports);
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "nano-injector",
3
+ "version": "1.0.1",
4
+ "description": "Miniature dependency injection library for TypeScript and JavaScript",
5
+ "main": "./lib/index.js",
6
+ "types": "./typings/index.d.ts",
7
+ "scripts": {
8
+ "clear": "rimraf typings docs lib",
9
+ "prepublishOnly": "npm run clear && npm test && npm run build",
10
+ "precommit": "npm run clear && npm run lint && npm test && npm run build && npm run docs",
11
+ "lint": "ts-standard",
12
+ "lintfix": "ts-standard --fix",
13
+ "build": "tsc",
14
+ "docs": "typedoc src/index.ts",
15
+ "test": "ts-mocha -p ./tsconfig.json ./test/*.test.ts"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+ssh://git@github.com/protoukr/nano-injector"
20
+ },
21
+ "files": [
22
+ "lib",
23
+ "typings"
24
+ ],
25
+ "keywords": [
26
+ "typescript",
27
+ "dependency injection",
28
+ "di",
29
+ "ioc",
30
+ "inversion of control"
31
+ ],
32
+ "author": "Roman Pukhalskyi",
33
+ "license": "MIT",
34
+ "bugs": {
35
+ "url": "https://github.com/protoukr/nano-injector/issues"
36
+ },
37
+ "homepage": "https://github.com/protoukr/nano-injector",
38
+ "ts-standard": {
39
+ "ignore": [
40
+ "lib",
41
+ "docs",
42
+ "typings"
43
+ ]
44
+ },
45
+ "devDependencies": {
46
+ "@types/chai": "^4.2.22",
47
+ "@types/mocha": "^8.2.3",
48
+ "chai": "^4.3.4",
49
+ "mocha": "^8.4.0",
50
+ "rimraf": "^3.0.2",
51
+ "ts-mocha": "^8.0.0",
52
+ "ts-node": "^10.4.0",
53
+ "ts-standard": "^11.0.0",
54
+ "typedoc": "^0.22.10",
55
+ "typescript": "^4.4.3"
56
+ }
57
+ }
@@ -0,0 +1,44 @@
1
+ import { Injector } from './Injector';
2
+ /**
3
+ * Class through which is defined how to create value for this binder
4
+ */
5
+ export declare class Binder<T> {
6
+ private readonly injector;
7
+ private value;
8
+ private ctor;
9
+ private factory;
10
+ private isSingleton;
11
+ constructor(injector: Injector);
12
+ /**
13
+ * Creates the new value if needed through previously defined method, and returns it
14
+ */
15
+ getValue(): T;
16
+ /**
17
+ * Directly defines value for this binder
18
+ * @param value
19
+ */
20
+ toValue(value: T): Binder<T>;
21
+ /**
22
+ * Defines constructor as a method for creating value. All previously defined methods
23
+ * will be ignored
24
+ * @param ctor
25
+ */
26
+ toConstructor(ctor: new () => T): Binder<T>;
27
+ /**
28
+ * Defines factory as a method for creating value. All previously defined methods
29
+ * will be ignored
30
+ * @param factory
31
+ */
32
+ toFactory(factory: (injector?: Injector) => T): Binder<T>;
33
+ /**
34
+ * Defines whether value should be a singleton. If yes the value will be created only once
35
+ * and the same instance will be returned forever
36
+ * @param value
37
+ */
38
+ asSingleton(value?: boolean): Binder<T>;
39
+ /**
40
+ * Creates value through previously defined method
41
+ * @private
42
+ */
43
+ private createValue;
44
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Just an error class, an instance of which is fired in some cases
3
+ */
4
+ export declare class InjectingError extends Error {
5
+ }
@@ -0,0 +1,77 @@
1
+ import { Binder } from './Binder';
2
+ import { Provider } from './Provider';
3
+ declare type UnionToIntersection<T> = (T extends any ? (k: T) => void : never) extends (k: infer R) => void ? R : never;
4
+ /**
5
+ * Provider which every injector binds itself to
6
+ */
7
+ export declare const $Injector: Provider<Injector>;
8
+ /**
9
+ * Main entity in the library, which holds provider's bindings and through which
10
+ * dependencies are resolved
11
+ */
12
+ export declare class Injector {
13
+ private readonly binders;
14
+ private readonly resolvingProviders;
15
+ private readonly name;
16
+ private readonly parent;
17
+ private readonly logger;
18
+ /**
19
+ * @param params.name name of the injector which can be useful mainly for debugging purposes
20
+ * @param params.parent parent injector for the composition of injectors
21
+ * @param params.logger specific log function if the custom output is required
22
+ */
23
+ constructor(params?: {
24
+ name?: string;
25
+ parent?: Injector;
26
+ logger?: (msg: string) => unknown;
27
+ });
28
+ /**
29
+ * Creates new binder and joins it to the specified providers. If the provider
30
+ * is already bound, then overriding occurs
31
+ * @param providers providers which the binder should be joined to
32
+ */
33
+ bindProvider<ProviderT extends Array<Provider<unknown>>, ValueT extends UnionToIntersection<ProviderT extends Array<Provider<infer R>> ? R : never>>(...providers: ProviderT): Binder<ValueT>;
34
+ /**
35
+ * Resolves all providers to their values and assigns them to the specified instance
36
+ * @param instance
37
+ * @param providers
38
+ */
39
+ injectValues<T extends object, K extends keyof T>(instance: T, providers: Record<K, Provider<T[K]>>): void;
40
+ /**
41
+ * Activates this injector and creates new instance of the type with the provided arguments
42
+ * @param type
43
+ * @param args
44
+ */
45
+ createInstance<ClassT extends new (...args: unknown[]) => unknown, CtorParamsT extends ConstructorParameters<ClassT>, InstT extends InstanceType<ClassT>>(type: ClassT, ...args: CtorParamsT): InstT;
46
+ /**
47
+ * Activates this injector and calls the function with provided arguments
48
+ * @param func function which should be called
49
+ * @param args args which should be passed to the called function
50
+ */
51
+ callFunc<FuncT extends (...args: unknown[]) => unknown, ParamsT extends Parameters<FuncT>, ReturnT extends ReturnType<FuncT>>(func: FuncT, ...args: ParamsT): ReturnT;
52
+ /**
53
+ * Returns bound to the specified provider value. If the value is not found
54
+ * undefined is returned
55
+ * @param provider
56
+ */
57
+ getValue<ProviderT extends Provider<unknown>, ValueT extends ProviderT extends Provider<infer R> ? R : never>(provider: ProviderT): ValueT | undefined;
58
+ /**
59
+ * Finds binder for the specified provider recursively up to the root injector
60
+ * @param provider
61
+ * @private
62
+ */
63
+ private getBinder;
64
+ /**
65
+ * Checks is there circular dependency and throws error if so
66
+ * @param provider
67
+ * @private
68
+ */
69
+ private checkCircularDependency;
70
+ /**
71
+ * Temporary activates this injector calls provided function and returns its value
72
+ * @param func
73
+ * @private
74
+ */
75
+ private activateAndCall;
76
+ }
77
+ export {};
@@ -0,0 +1,14 @@
1
+ import { Injector } from './Injector';
2
+ import { Provider } from './Provider';
3
+ /**
4
+ * Private class for holding current active injector
5
+ */
6
+ declare class _InjectorsStack {
7
+ private readonly injectors;
8
+ private activeInjector;
9
+ get<ProviderT extends Provider<unknown>, ValueT extends ProviderT extends Provider<infer R> ? R : never>(provider: ProviderT): ValueT | undefined;
10
+ push(injector: Injector): void;
11
+ pop(): void;
12
+ }
13
+ export declare const InjectorsStack: _InjectorsStack;
14
+ export {};
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Symbol used for storing the id of a provider
3
+ */
4
+ declare const ID_SYMBOL: unique symbol;
5
+ /**
6
+ * Symbol used for storing the name of a provider
7
+ */
8
+ declare const NAME_SYMBOL: unique symbol;
9
+ /**
10
+ * Provider is a second main concept in the library. Basically just it is just a function
11
+ * through which binding with the specific value occurs.
12
+ */
13
+ export interface Provider<T> {
14
+ readonly [ID_SYMBOL]: number;
15
+ readonly [NAME_SYMBOL]: string;
16
+ /**
17
+ * Returns bound to this provider value. Throws the exception if it's not found.
18
+ * If throwing the exception is not desired any value to the provider should
19
+ * be passed
20
+ */
21
+ (): T;
22
+ /**
23
+ * Returns bound to this provider value. Returns default one if the bound value
24
+ * is not found
25
+ * @param defValue
26
+ */
27
+ <DefValT>(defValue: DefValT): T | DefValT;
28
+ }
29
+ /**
30
+ * Creates the new provider for some value with a specific type
31
+ * @param name name for this provider used mainly for debugging purposes
32
+ */
33
+ export declare function createProvider<T>(name?: string): Provider<T>;
34
+ /**
35
+ * Determines whether the received value is a provider
36
+ * @param value value which should be tested
37
+ */
38
+ export declare function isProvider<T = unknown>(value: unknown): value is Provider<T>;
39
+ /**
40
+ * Returns the id of the specified provider
41
+ * @param provider
42
+ */
43
+ export declare function getProviderID(provider: Provider<unknown>): number;
44
+ /**
45
+ * Returns the name of the specified provider
46
+ * @param provider
47
+ */
48
+ export declare function getProviderName(provider: Provider<unknown>): string;
49
+ export {};
@@ -0,0 +1,3 @@
1
+ export * from './Injector';
2
+ export * from './Provider';
3
+ export * from './InjectingError';