@vercube/di 0.0.1-alpha.15
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/dist/Common/BaseDecorators.d.ts +31 -0
- package/dist/Decorators/Init.d.ts +6 -0
- package/dist/Decorators/Inject.d.ts +7 -0
- package/dist/Decorators/InjectOptional.d.ts +7 -0
- package/dist/Domain/Container.d.ts +154 -0
- package/dist/Domain/ContainerEvents.d.ts +18 -0
- package/dist/Domain/Engine.d.ts +55 -0
- package/dist/Types/IOCTypes.d.ts +48 -0
- package/dist/Utils/Utils.d.ts +79 -0
- package/dist/index.cjs +599 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.mjs +582 -0
- package/package.json +25 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
|
|
2
|
+
//#region packages/di/src/Common/BaseDecorators.ts
|
|
3
|
+
var BaseDecorator = class {
|
|
4
|
+
/** Holds options object that is passed as 2nd argument in createDecorator() factory */
|
|
5
|
+
options;
|
|
6
|
+
/** Holds class instance that is decorated */
|
|
7
|
+
instance;
|
|
8
|
+
/** Holds class prototype that is decorated */
|
|
9
|
+
prototype;
|
|
10
|
+
/** Holds property name that was decorated */
|
|
11
|
+
propertyName;
|
|
12
|
+
/** Holds property descriptor that was decorated */
|
|
13
|
+
descriptor;
|
|
14
|
+
/** Holds property index if decorators if for Method Property */
|
|
15
|
+
propertyIndex;
|
|
16
|
+
/**
|
|
17
|
+
* This method is called when decorator is created and ready to be used.
|
|
18
|
+
*/
|
|
19
|
+
created() {}
|
|
20
|
+
/**
|
|
21
|
+
* This method is called when decorator is destroyed for cleanup tasks (like unregistering listeners, clearing timers).
|
|
22
|
+
* For standard services it is called at the end of SSR requests and for Vue components it is called when component is
|
|
23
|
+
* destroyed.
|
|
24
|
+
*/
|
|
25
|
+
destroyed() {}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region packages/di/src/Types/IOCTypes.ts
|
|
30
|
+
let IOC;
|
|
31
|
+
(function(_IOC) {
|
|
32
|
+
let ServiceFactoryType = /* @__PURE__ */ function(ServiceFactoryType$1) {
|
|
33
|
+
ServiceFactoryType$1["CLASS"] = "CLASS";
|
|
34
|
+
ServiceFactoryType$1["CLASS_SINGLETON"] = "CLASS_SINGLETON";
|
|
35
|
+
ServiceFactoryType$1["INSTANCE"] = "INSTANCE";
|
|
36
|
+
return ServiceFactoryType$1;
|
|
37
|
+
}({});
|
|
38
|
+
_IOC.ServiceFactoryType = ServiceFactoryType;
|
|
39
|
+
let InjectMethod = /* @__PURE__ */ function(InjectMethod$1) {
|
|
40
|
+
InjectMethod$1["LAZY"] = "LAZY";
|
|
41
|
+
InjectMethod$1["STATIC"] = "STATIC";
|
|
42
|
+
return InjectMethod$1;
|
|
43
|
+
}({});
|
|
44
|
+
_IOC.InjectMethod = InjectMethod;
|
|
45
|
+
let DependencyType = /* @__PURE__ */ function(DependencyType$1) {
|
|
46
|
+
DependencyType$1[DependencyType$1["STANDARD"] = 0] = "STANDARD";
|
|
47
|
+
DependencyType$1[DependencyType$1["OPTIONAL"] = 1] = "OPTIONAL";
|
|
48
|
+
return DependencyType$1;
|
|
49
|
+
}({});
|
|
50
|
+
_IOC.DependencyType = DependencyType;
|
|
51
|
+
})(IOC || (IOC = {}));
|
|
52
|
+
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region packages/di/src/Domain/Engine.ts
|
|
55
|
+
/**
|
|
56
|
+
* This map holds metadata for ALL classes in system along with their dependencies. Original idea was
|
|
57
|
+
* to store those informations in object prototype, but accessing this map is blazing fast with Map
|
|
58
|
+
* container (<1ms).
|
|
59
|
+
*/
|
|
60
|
+
const classMap = new Map();
|
|
61
|
+
const ROOT_PROTO = Object.getPrototypeOf({});
|
|
62
|
+
/**
|
|
63
|
+
* This method registers @Inject() in particular class.
|
|
64
|
+
* @param prototype class prototype for which we register @Inject()
|
|
65
|
+
* @param propertyName name of property that is inejcted
|
|
66
|
+
* @param dependency what we should inject there
|
|
67
|
+
* @param type type of dependency (standard or optional dependency)
|
|
68
|
+
*/
|
|
69
|
+
function registerInject(prototype, propertyName, dependency, type) {
|
|
70
|
+
let entry = classMap.get(prototype);
|
|
71
|
+
if (!entry) {
|
|
72
|
+
const newEntry = { deps: [] };
|
|
73
|
+
entry = newEntry;
|
|
74
|
+
classMap.set(prototype, entry);
|
|
75
|
+
}
|
|
76
|
+
const newDep = {
|
|
77
|
+
propertyName,
|
|
78
|
+
dependency,
|
|
79
|
+
type
|
|
80
|
+
};
|
|
81
|
+
entry.deps.push(newDep);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Returns classmap entry for particular class. It holds information about @Injects for this particular class.
|
|
85
|
+
* @param classType type of class to check
|
|
86
|
+
* @returns class map entry or null if cannot be found
|
|
87
|
+
*/
|
|
88
|
+
function getEntryForClass(classType) {
|
|
89
|
+
const entry = classMap.get(classType.prototype);
|
|
90
|
+
return entry === void 0 ? null : entry;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Returns array of dependencies for particular class instance.
|
|
94
|
+
* @param instance class instance
|
|
95
|
+
* @returns array of @Inject dependencies defined for this class
|
|
96
|
+
*/
|
|
97
|
+
function getDeps(instance) {
|
|
98
|
+
const prototype = Object.getPrototypeOf(instance);
|
|
99
|
+
if (!prototype) return [];
|
|
100
|
+
const entry = classMap.get(prototype);
|
|
101
|
+
return entry !== void 0 && entry.deps !== void 0 ? entry.deps : [];
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* This method injects stuff into class, using container passed as first argument. We need container as
|
|
105
|
+
* inject execution is always context based.
|
|
106
|
+
* @param container container instance that is providing dependencies
|
|
107
|
+
* @param instance class instance to inject
|
|
108
|
+
* @param method inject method, "lazy" queries dep during property access while "static" injects during class creation
|
|
109
|
+
*/
|
|
110
|
+
function injectDeps(container, instance, method) {
|
|
111
|
+
let prototype = Object.getPrototypeOf(instance);
|
|
112
|
+
if (!prototype) return;
|
|
113
|
+
/**
|
|
114
|
+
* Here we will traverse through prototype chain until we hit dead end (ROOT_PROTO). This is because we
|
|
115
|
+
* must process inject for current class and also for every base class.
|
|
116
|
+
*
|
|
117
|
+
* So we will use "do" loop to iterate full prototype chain.
|
|
118
|
+
*/
|
|
119
|
+
do {
|
|
120
|
+
const entry = classMap.get(prototype);
|
|
121
|
+
if (entry) for (const iter of entry.deps) {
|
|
122
|
+
const propertyName = iter.propertyName;
|
|
123
|
+
const dependency = iter.dependency;
|
|
124
|
+
const type = iter.type;
|
|
125
|
+
if (Object.prototype.hasOwnProperty.call(instance, propertyName) && instance[propertyName] !== void 0) continue;
|
|
126
|
+
if (type === IOC.DependencyType.OPTIONAL) {
|
|
127
|
+
Object.defineProperty(instance, propertyName, { get: function() {
|
|
128
|
+
return container.getOptional(dependency);
|
|
129
|
+
} });
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
switch (method) {
|
|
133
|
+
case IOC.InjectMethod.LAZY: {
|
|
134
|
+
Object.defineProperty(instance, propertyName, { get: function() {
|
|
135
|
+
return container.get(dependency);
|
|
136
|
+
} });
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
case IOC.InjectMethod.STATIC: {
|
|
140
|
+
instance[propertyName] = container.get(dependency);
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
default: throw new Error(`IOCEngine.injectDeps() - invalid inject method ${method}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
prototype = Object.getPrototypeOf(prototype);
|
|
147
|
+
} while (prototype && prototype !== ROOT_PROTO);
|
|
148
|
+
}
|
|
149
|
+
const IOCEngine = {
|
|
150
|
+
registerInject,
|
|
151
|
+
getEntryForClass,
|
|
152
|
+
injectDeps,
|
|
153
|
+
getDeps
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
//#endregion
|
|
157
|
+
//#region packages/di/src/Decorators/Inject.ts
|
|
158
|
+
function Inject(key) {
|
|
159
|
+
return (target, propertyName) => {
|
|
160
|
+
IOCEngine.registerInject(target, propertyName, key, IOC.DependencyType.STANDARD);
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
//#endregion
|
|
165
|
+
//#region packages/di/src/Decorators/InjectOptional.ts
|
|
166
|
+
function InjectOptional(key) {
|
|
167
|
+
return (target, propertyName) => {
|
|
168
|
+
IOCEngine.registerInject(target, propertyName, key, IOC.DependencyType.OPTIONAL);
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region packages/di/src/Decorators/Init.ts
|
|
174
|
+
var InitDecorator = class extends BaseDecorator {
|
|
175
|
+
/**
|
|
176
|
+
* Called when decorator is initialized.
|
|
177
|
+
*/
|
|
178
|
+
created() {
|
|
179
|
+
if (typeof this.instance[this.propertyName] === "function") this.instance[this.propertyName]();
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
function Init() {
|
|
183
|
+
return createDecorator(InitDecorator, {});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region packages/di/src/Domain/ContainerEvents.ts
|
|
188
|
+
var ContainerEvents = class {
|
|
189
|
+
fOnExpanded = [];
|
|
190
|
+
/**
|
|
191
|
+
* Registers to container "onExpanded" event.
|
|
192
|
+
* @param handler event handler
|
|
193
|
+
*/
|
|
194
|
+
onExpanded(handler) {
|
|
195
|
+
this.fOnExpanded.push(handler);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Calls on expanded event.
|
|
199
|
+
* @param serviceKeys key of service we have installed
|
|
200
|
+
*/
|
|
201
|
+
callOnExpanded(serviceKeys) {
|
|
202
|
+
for (const handler of this.fOnExpanded) handler(serviceKeys);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
//#endregion
|
|
207
|
+
//#region packages/di/src/Utils/Utils.ts
|
|
208
|
+
function Identity(name) {
|
|
209
|
+
return Symbol(name);
|
|
210
|
+
}
|
|
211
|
+
function createDecorator(decoratorClass, params) {
|
|
212
|
+
return function internalDecorator(target, propertyName, descriptor) {
|
|
213
|
+
if (!target.__decorators) target.__decorators = [];
|
|
214
|
+
target.__decorators.push({
|
|
215
|
+
classType: decoratorClass,
|
|
216
|
+
params,
|
|
217
|
+
target,
|
|
218
|
+
propertyName,
|
|
219
|
+
descriptor
|
|
220
|
+
});
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* This map holds map of Container:DecoratorMetadata values. For every container created,
|
|
225
|
+
* we must hold array of decorated instances, we realize it by holding map where key is
|
|
226
|
+
* container (for easier removal later) and value is IContainerDecoratorMetadataObject.
|
|
227
|
+
*/
|
|
228
|
+
const containerMap = new Map();
|
|
229
|
+
/**
|
|
230
|
+
* Helper function to query data from container
|
|
231
|
+
* @param container container to get metadata fro
|
|
232
|
+
* @return metadata object
|
|
233
|
+
*/
|
|
234
|
+
function getContainerMetadata(container) {
|
|
235
|
+
if (!containerMap.has(container)) containerMap.set(container, { decoratedInstances: new Map() });
|
|
236
|
+
return containerMap.get(container);
|
|
237
|
+
}
|
|
238
|
+
function initializeDecorators(target, container) {
|
|
239
|
+
const prototype = Object.getPrototypeOf(target);
|
|
240
|
+
if (prototype.__decorators) for (const entry of prototype.__decorators) {
|
|
241
|
+
const instance = container.resolve(entry.classType);
|
|
242
|
+
if (instance) {
|
|
243
|
+
instance.options = entry.params;
|
|
244
|
+
instance.instance = target;
|
|
245
|
+
instance.prototype = prototype;
|
|
246
|
+
instance.propertyName = entry.propertyName;
|
|
247
|
+
instance.descriptor = entry.descriptor;
|
|
248
|
+
instance.propertyIndex = typeof entry.descriptor === "number" ? entry.descriptor : -1;
|
|
249
|
+
instance.created();
|
|
250
|
+
}
|
|
251
|
+
const { decoratedInstances } = getContainerMetadata(container);
|
|
252
|
+
const instanceList = decoratedInstances.get(target) ?? [];
|
|
253
|
+
instanceList.push(instance);
|
|
254
|
+
decoratedInstances.set(target, instanceList);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function destroyDecorators(target, container) {
|
|
258
|
+
const { decoratedInstances } = getContainerMetadata(container);
|
|
259
|
+
const instanceList = decoratedInstances.get(target);
|
|
260
|
+
if (instanceList) for (const instance of instanceList) instance.destroyed();
|
|
261
|
+
decoratedInstances.delete(target);
|
|
262
|
+
}
|
|
263
|
+
function initializeContainer(container) {
|
|
264
|
+
container.flushQueue();
|
|
265
|
+
}
|
|
266
|
+
function destroyContainer(container) {
|
|
267
|
+
container.getAllServices().forEach((service) => destroyDecorators(service, container));
|
|
268
|
+
containerMap.delete(container);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
//#endregion
|
|
272
|
+
//#region packages/di/src/Domain/Container.ts
|
|
273
|
+
var Container = class Container {
|
|
274
|
+
fLocked = false;
|
|
275
|
+
fDefaultParams = { createLocked: false };
|
|
276
|
+
fServices = new Map();
|
|
277
|
+
fNewQueue = new Map();
|
|
278
|
+
fSingletonInstances = new Map();
|
|
279
|
+
fInjectMethod = IOC.InjectMethod.STATIC;
|
|
280
|
+
fContainerEvents = new ContainerEvents();
|
|
281
|
+
/**
|
|
282
|
+
* Constructor for container.
|
|
283
|
+
* @param params initial params for container
|
|
284
|
+
*/
|
|
285
|
+
constructor(params) {
|
|
286
|
+
this.fLocked = params?.createLocked ?? false;
|
|
287
|
+
this.fDefaultParams = Object.assign(this.fDefaultParams, params);
|
|
288
|
+
this.fInjectMethod = params?.injectMethod ?? IOC.InjectMethod.STATIC;
|
|
289
|
+
this.bindInstance(Container, this);
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Returns array of all service keys. This basically returns keys from all .bindXXX calls.
|
|
293
|
+
* @returns {Array} array of service keys
|
|
294
|
+
*/
|
|
295
|
+
get servicesKeys() {
|
|
296
|
+
return [...this.fServices.keys()];
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Returns events handler.
|
|
300
|
+
*/
|
|
301
|
+
get events() {
|
|
302
|
+
return this.fContainerEvents;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Binds particular key to container in singleton scope. Multiple queries/injects of this
|
|
306
|
+
* service will always return the same instance.
|
|
307
|
+
*
|
|
308
|
+
* @param key key of service, preferably class or abstract class
|
|
309
|
+
* @param value implementation
|
|
310
|
+
*/
|
|
311
|
+
bind(key, value) {
|
|
312
|
+
const newDef = {
|
|
313
|
+
serviceKey: key,
|
|
314
|
+
serviceValue: value ?? key,
|
|
315
|
+
type: IOC.ServiceFactoryType.CLASS_SINGLETON
|
|
316
|
+
};
|
|
317
|
+
if (typeof key === "symbol" && !value) throw new Error("Container - provide implementation for binds with symbols.");
|
|
318
|
+
const existingServiceDef = this.fServices.get(key);
|
|
319
|
+
if (existingServiceDef) this.internalDispose(existingServiceDef);
|
|
320
|
+
this.fServices.set(key, newDef);
|
|
321
|
+
this.fNewQueue.set(key, newDef);
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Binds particular key to container in transient scope. Every query/@Inject of this service
|
|
325
|
+
* will have totally brand-new instance of class.
|
|
326
|
+
* @param key key of service, preferably class or abstract class
|
|
327
|
+
* @param value implementation
|
|
328
|
+
*/
|
|
329
|
+
bindTransient(key, value) {
|
|
330
|
+
const newDef = {
|
|
331
|
+
serviceKey: key,
|
|
332
|
+
serviceValue: value ?? key,
|
|
333
|
+
type: IOC.ServiceFactoryType.CLASS
|
|
334
|
+
};
|
|
335
|
+
const existingServiceDef = this.fServices.get(key);
|
|
336
|
+
if (existingServiceDef) this.internalDispose(existingServiceDef);
|
|
337
|
+
this.fServices.set(key, newDef);
|
|
338
|
+
this.fNewQueue.set(key, newDef);
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Binds particular key class to an existing class instance. If you use this method,
|
|
342
|
+
* class wont be instantiated automatically. The common use case is to
|
|
343
|
+
* share single class instance between two or more containers.
|
|
344
|
+
*
|
|
345
|
+
* @param key key of service, preferably class or abstract class
|
|
346
|
+
* @param value instance of class to be used as resolution
|
|
347
|
+
*/
|
|
348
|
+
bindInstance(key, value) {
|
|
349
|
+
const newDef = {
|
|
350
|
+
serviceKey: key,
|
|
351
|
+
serviceValue: value,
|
|
352
|
+
type: IOC.ServiceFactoryType.INSTANCE
|
|
353
|
+
};
|
|
354
|
+
const existingServiceDef = this.fServices.get(key);
|
|
355
|
+
if (existingServiceDef) this.internalDispose(existingServiceDef);
|
|
356
|
+
this.fServices.set(key, newDef);
|
|
357
|
+
this.fNewQueue.set(key, newDef);
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Binds mocked instance to a particular service ID. Its designed to be used in unit tests, where you can quickly
|
|
361
|
+
* replace real IOC implementation with a partial stub. Please note, you are responsible to provide enough data
|
|
362
|
+
* for test to pass, TypeScript wont check it.
|
|
363
|
+
*
|
|
364
|
+
* Example:
|
|
365
|
+
*
|
|
366
|
+
* container.bind(HttpServer, {
|
|
367
|
+
* listen: jest.fn(),
|
|
368
|
+
* });
|
|
369
|
+
*
|
|
370
|
+
* @param key service to be replaced
|
|
371
|
+
* @param mockInstance mock instance
|
|
372
|
+
*/
|
|
373
|
+
bindMock(key, mockInstance) {
|
|
374
|
+
const newDef = {
|
|
375
|
+
serviceKey: key,
|
|
376
|
+
serviceValue: mockInstance,
|
|
377
|
+
type: IOC.ServiceFactoryType.INSTANCE
|
|
378
|
+
};
|
|
379
|
+
const existingServiceDef = this.fServices.get(key);
|
|
380
|
+
if (existingServiceDef) this.internalDispose(existingServiceDef);
|
|
381
|
+
this.fServices.set(key, newDef);
|
|
382
|
+
this.fNewQueue.set(key, newDef);
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Returns implementation for a particular key class. This is the same as @Inject,
|
|
386
|
+
* but triggered programitically.
|
|
387
|
+
* @param key key used in .bind() function to bind key class to implementation class
|
|
388
|
+
* @returns service for identifier
|
|
389
|
+
*/
|
|
390
|
+
get(key) {
|
|
391
|
+
return this.internalGet(key);
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Returns implementation for a particular key class. This is the same as @Inject,
|
|
395
|
+
* but triggered programitically.
|
|
396
|
+
* @param key key used in .bind() function to bind key class to implementation class
|
|
397
|
+
* @returns service for identifier
|
|
398
|
+
*/
|
|
399
|
+
getOptional(key) {
|
|
400
|
+
return this.internalGetOptional(key);
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Uses the container provider to register multiple things in IOC container at once.
|
|
404
|
+
* @param provider provider that will register new services into IOC container
|
|
405
|
+
*/
|
|
406
|
+
use(provider) {
|
|
407
|
+
provider(this);
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Expands container during runtime, adding new services to it.
|
|
411
|
+
* @param providers functor that is used to expand the container
|
|
412
|
+
* @param flush whether container should be flushed now or not
|
|
413
|
+
*/
|
|
414
|
+
expand(providers, flush = true) {
|
|
415
|
+
const preLockState = this.fLocked;
|
|
416
|
+
const allProviders = Array.isArray(providers) ? providers : [providers];
|
|
417
|
+
try {
|
|
418
|
+
this.fLocked = false;
|
|
419
|
+
for (const provider of allProviders) this.use(provider);
|
|
420
|
+
const newKeys = [...this.fNewQueue.keys()].filter((k) => !this.fSingletonInstances.has(k));
|
|
421
|
+
this.fContainerEvents.callOnExpanded(newKeys);
|
|
422
|
+
if (flush) this.flushQueue();
|
|
423
|
+
} finally {
|
|
424
|
+
this.fLocked = preLockState;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Creates instance of particular class using, respecting all @Injects inside created class.
|
|
429
|
+
* @param classType type of class to instantiate
|
|
430
|
+
* @param method (optional) inject method
|
|
431
|
+
* @returns new class instance with dependencies
|
|
432
|
+
*/
|
|
433
|
+
resolve(classType, method = IOC.InjectMethod.LAZY) {
|
|
434
|
+
const newInstance = new classType();
|
|
435
|
+
this.internalProcessInjects(newInstance, method);
|
|
436
|
+
return newInstance;
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Returns all IOC services registered in container.
|
|
440
|
+
* @returns array with all registered services
|
|
441
|
+
*/
|
|
442
|
+
getAllServices() {
|
|
443
|
+
return this.servicesKeys.map((k) => this.get(k));
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Unlocks the container, allowing things to be retrieved and used.
|
|
447
|
+
*/
|
|
448
|
+
unlock() {
|
|
449
|
+
this.fLocked = false;
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Locks the container, disabling to add new services here.
|
|
453
|
+
*/
|
|
454
|
+
lock() {
|
|
455
|
+
this.fLocked = true;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Flushes new services queue, registering them into container.
|
|
459
|
+
*/
|
|
460
|
+
flushQueue() {
|
|
461
|
+
if (this.fNewQueue.size === 0) return;
|
|
462
|
+
const values = [...this.fNewQueue.values()];
|
|
463
|
+
for (const def of values) {
|
|
464
|
+
if (def.type !== IOC.ServiceFactoryType.CLASS_SINGLETON) continue;
|
|
465
|
+
const instance = this.internalResolve(def);
|
|
466
|
+
initializeDecorators(instance, this);
|
|
467
|
+
}
|
|
468
|
+
this.fNewQueue.clear();
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Internally retrieve dependency from container.
|
|
472
|
+
* @param key key to get
|
|
473
|
+
* @param parent parent (for debugging purposes)
|
|
474
|
+
* @returns queried instance
|
|
475
|
+
*/
|
|
476
|
+
internalGet(key, parent) {
|
|
477
|
+
const serviceDef = this.fServices.get(key);
|
|
478
|
+
if (!serviceDef) throw new Error(`Unresolved dependency for [${this.getKeyDescription(key)}]`);
|
|
479
|
+
return this.internalResolve(serviceDef);
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Internally retrieve dependency from container.
|
|
483
|
+
* @param key key to get
|
|
484
|
+
* @param parent parent
|
|
485
|
+
* @returns queried instance
|
|
486
|
+
*/
|
|
487
|
+
internalGetOptional(key) {
|
|
488
|
+
const serviceDef = this.fServices.get(key);
|
|
489
|
+
if (!serviceDef) return null;
|
|
490
|
+
return this.internalResolve(serviceDef);
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Internally resolves service def, turning it into class instance with deps injected.
|
|
494
|
+
* @param serviceDef service def to resolve
|
|
495
|
+
* @returns class instance
|
|
496
|
+
*/
|
|
497
|
+
internalResolve(serviceDef) {
|
|
498
|
+
switch (serviceDef.type) {
|
|
499
|
+
case IOC.ServiceFactoryType.INSTANCE: return serviceDef.serviceValue;
|
|
500
|
+
case IOC.ServiceFactoryType.CLASS_SINGLETON: {
|
|
501
|
+
if (!this.fSingletonInstances.has(serviceDef.serviceKey)) {
|
|
502
|
+
const constructor = serviceDef.serviceValue;
|
|
503
|
+
const instance = new constructor();
|
|
504
|
+
this.fSingletonInstances.set(serviceDef.serviceKey, instance);
|
|
505
|
+
this.internalProcessInjects(instance, this.fInjectMethod);
|
|
506
|
+
return instance;
|
|
507
|
+
}
|
|
508
|
+
return this.fSingletonInstances.get(serviceDef.serviceKey);
|
|
509
|
+
}
|
|
510
|
+
case IOC.ServiceFactoryType.CLASS: {
|
|
511
|
+
const constructor = serviceDef.serviceValue;
|
|
512
|
+
const instance = new constructor();
|
|
513
|
+
this.internalProcessInjects(instance, this.fInjectMethod);
|
|
514
|
+
return instance;
|
|
515
|
+
}
|
|
516
|
+
default: throw new Error(`Container - invalid factory type: ${serviceDef.type}`);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Internally inject deps for particular class..
|
|
521
|
+
* @param instance instance to inject
|
|
522
|
+
* @param method method for injecting dependencies, either lazy or static
|
|
523
|
+
*/
|
|
524
|
+
internalProcessInjects(instance, method) {
|
|
525
|
+
if (method === IOC.InjectMethod.LAZY) {
|
|
526
|
+
IOCEngine.injectDeps(this, instance, IOC.InjectMethod.LAZY);
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
const processQueue = [];
|
|
530
|
+
const elementSet = new Set();
|
|
531
|
+
const toProcessElements = [];
|
|
532
|
+
processQueue.push(instance);
|
|
533
|
+
toProcessElements.push(instance);
|
|
534
|
+
while (processQueue.length > 0) {
|
|
535
|
+
const element = processQueue.pop();
|
|
536
|
+
const deps = IOCEngine.getDeps(element);
|
|
537
|
+
for (const inj of deps) if (!elementSet.has(inj.dependency)) {
|
|
538
|
+
const isOptional = inj.type === IOC.DependencyType.OPTIONAL;
|
|
539
|
+
const childInstance = isOptional ? this.internalGetOptional(inj.dependency) : this.internalGet(inj.dependency, instance);
|
|
540
|
+
elementSet.add(inj.dependency);
|
|
541
|
+
if (!isOptional) {
|
|
542
|
+
processQueue.push(childInstance);
|
|
543
|
+
toProcessElements.push(childInstance);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
for (const el of toProcessElements) IOCEngine.injectDeps(this, el, IOC.InjectMethod.STATIC);
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Disposes a module, clearing everything allocated to it.
|
|
551
|
+
* @param def def that should be disposed
|
|
552
|
+
*/
|
|
553
|
+
internalDispose(def) {
|
|
554
|
+
switch (def.type) {
|
|
555
|
+
case IOC.ServiceFactoryType.INSTANCE: {
|
|
556
|
+
destroyDecorators(def.serviceValue, this);
|
|
557
|
+
break;
|
|
558
|
+
}
|
|
559
|
+
case IOC.ServiceFactoryType.CLASS_SINGLETON: {
|
|
560
|
+
const existingInstance = this.fSingletonInstances.get(def.serviceKey);
|
|
561
|
+
if (existingInstance) destroyDecorators(existingInstance, this);
|
|
562
|
+
break;
|
|
563
|
+
}
|
|
564
|
+
case IOC.ServiceFactoryType.CLASS: break;
|
|
565
|
+
default: throw new Error(`Container::internalDispose() - invalid def type: ${def.type}`);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Describes particular key for better error messaging.
|
|
570
|
+
* @param key service key
|
|
571
|
+
* @returns string representation of service key
|
|
572
|
+
*/
|
|
573
|
+
getKeyDescription(key) {
|
|
574
|
+
if (typeof key === "symbol") return key.description;
|
|
575
|
+
else if (typeof key === "function") return key.name;
|
|
576
|
+
else if (typeof key === "object") return key.constructor?.name ?? "Unknown object";
|
|
577
|
+
return "Unknown";
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
//#endregion
|
|
582
|
+
export { BaseDecorator, Container, IOC, Identity, Init, Inject, InjectOptional, createDecorator, destroyContainer, destroyDecorators, initializeContainer, initializeDecorators };
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vercube/di",
|
|
3
|
+
"version": "0.0.1-alpha.15",
|
|
4
|
+
"description": "Dependencie Injection module for Vercube framework",
|
|
5
|
+
"repository": "@vercube/di",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"type": "module",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"main": "./dist/index.cjs",
|
|
17
|
+
"module": "./dist/index.mjs",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
}
|
|
25
|
+
}
|