proxydi 0.0.12 → 0.1.0

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.
@@ -0,0 +1,27 @@
1
+ import { IProxyDiContainer as IProxyDiContainer, ContainerizedServiceInstance, ServiceInstanced, ServiceClass } from './types';
2
+ import { ProxyDiSettings, ServiceId } from './types';
3
+ export declare class ProxyDiContainer implements IProxyDiContainer {
4
+ private static idCounter;
5
+ readonly id: number;
6
+ readonly parent?: ProxyDiContainer;
7
+ private children;
8
+ /**
9
+ * Holds instances of services registered in particular this container
10
+ */
11
+ private serviceInstances;
12
+ private parentInstanceProxies;
13
+ private settings;
14
+ constructor(settings?: ProxyDiSettings, parent?: ProxyDiContainer);
15
+ registerService<T>(serviceId: ServiceId, instance: ServiceInstanced<T>): void;
16
+ private registerInstanceImplementation;
17
+ createService<T>(serviceId: ServiceId, ServiceClass: ServiceClass<T>): void;
18
+ isKnown(serviceId: ServiceId): boolean;
19
+ resolve<T>(serviceId: ServiceId): T & ContainerizedServiceInstance;
20
+ injectDependencies(injectionsOwner: any): void;
21
+ createChildContainer(): ProxyDiContainer;
22
+ removeService(serviceId: ServiceId | ContainerizedServiceInstance): void;
23
+ destroy(): void;
24
+ private findInstance;
25
+ private addChild;
26
+ private removeChild;
27
+ }
@@ -1,5 +1,6 @@
1
1
  import { IProxyDiContainer as IProxyDiContainer, ContainerizedDependency as ContainerizedDependency, DependencyClass } from './types';
2
2
  import { ContainerSettings as ContainerSettings, DependencyId } from './types';
3
+ import { MiddlewareRegistrator, MiddlewareRemover, MiddlewareResolver } from './middleware/middleware.api';
3
4
  /**
4
5
  * A dependency injection container
5
6
  */
@@ -24,18 +25,20 @@ export declare class ProxyDiContainer implements IProxyDiContainer {
24
25
  /**
25
26
  * Holds proxies for dependencies registered in parent containers to provide for it dependencies from this container
26
27
  */
27
- private parentDependencyProxies;
28
+ private inContextProxies;
28
29
  /**
29
30
  * Settings that control the behavior of the container and it's children
30
31
  */
31
32
  readonly settings: Required<ContainerSettings>;
32
- private middlewareListener;
33
+ private middlewareManager;
33
34
  /**
34
35
  * Creates a new instance of ProxyDiContainer.
35
36
  * @param settings Optional container settings to override defaults.
36
37
  * @param parent Optional parent container.
37
38
  */
38
39
  constructor(settings?: ContainerSettings, parent?: ProxyDiContainer);
40
+ registerMiddleware(middleware: MiddlewareRegistrator | MiddlewareRemover | MiddlewareResolver): void;
41
+ removeMiddleware(middleware: MiddlewareRegistrator | MiddlewareRemover | MiddlewareResolver): void;
39
42
  /**
40
43
  * Registers a dependency in the container. Could register eacher class or instance.
41
44
  * In case of class, it will be instantiated without any parameters.
@@ -45,7 +48,7 @@ export declare class ProxyDiContainer implements IProxyDiContainer {
45
48
  * @throws Error if dependency is already registered and rewriting is not allowed or if invalid dependency (not object) is provided and this it now allowed.
46
49
  * @returns Dependency instance, registered in container
47
50
  */
48
- register<T>(DependencyClass: DependencyClass<T>, dependencyId: DependencyId): T & ContainerizedDependency;
51
+ register<T>(DependencyClass: DependencyClass<T>, dependencyId?: DependencyId): T & ContainerizedDependency;
49
52
  register<T>(dependency: T extends new (...args: any[]) => any ? never : T, dependencyId: DependencyId): T & ContainerizedDependency;
50
53
  private createInstance;
51
54
  /**
@@ -62,6 +65,7 @@ export declare class ProxyDiContainer implements IProxyDiContainer {
62
65
  */
63
66
  resolve<T>(dependencyId: DependencyId): T & ContainerizedDependency;
64
67
  resolve<T extends DependencyClass<any>>(SomeClass: T): InstanceType<T> & ContainerizedDependency;
68
+ private resolveImpl;
65
69
  /**
66
70
  * Injects dependencies to the given object based on its defined injections metadata. Does not affect the container.
67
71
  * @param injectionsOwner The object to inject dependencies into.
@@ -0,0 +1,8 @@
1
+ import { ProxyDI } from './ProxyDI';
2
+ import { ServiceId } from './types';
3
+ export declare class ProxyFactory {
4
+ private container;
5
+ constructor(container: ProxyDI);
6
+ makeProxy<T>(serviceId: ServiceId): T;
7
+ }
8
+ export declare function isProxy(value: any): boolean;
@@ -0,0 +1,5 @@
1
+ import { ProxyDiContainer } from '../ProxyDiContainer';
2
+ export declare class TestableProxyDiContainer extends ProxyDiContainer {
3
+ getChildren(): any;
4
+ desreaseIdCounter(): void;
5
+ }
@@ -0,0 +1,6 @@
1
+ import { KindomQueen } from './Queen';
2
+ export declare class KindomKing {
3
+ readonly queen: KindomQueen;
4
+ readonly name = "I'm a king";
5
+ constructor(queen: KindomQueen);
6
+ }
@@ -0,0 +1,6 @@
1
+ import { KindomKing } from './King';
2
+ export declare class KindomQueen {
3
+ readonly king: KindomKing;
4
+ readonly name = "I'm a queen";
5
+ constructor(king: KindomKing);
6
+ }
@@ -1,5 +1,5 @@
1
- import { DependencyClass, DependencyId } from './types';
2
- export declare const autoInjectableClasses: Record<DependencyId, DependencyClass<any>>;
1
+ import { InstancedDependency, DependencyId } from './types';
2
+ export declare const autoInjectableClasses: Record<DependencyId, InstancedDependency<any>>;
3
3
  /**
4
4
  * Registers a class as an auto-injectable for dependency injection.
5
5
  *
@@ -10,4 +10,4 @@ export declare const autoInjectableClasses: Record<DependencyId, DependencyClass
10
10
  * will create an instance of the decorated class. However, if a container already has an instance with that identifier
11
11
  * prior to resolution, the decorated class will be ignored by that container.
12
12
  */
13
- export declare const autoInjectable: (dependencyId?: DependencyId) => (value: DependencyClass<any>, context: ClassDecoratorContext) => void;
13
+ export declare const autoInjectable: (dependencyId?: DependencyId) => (value: InstancedDependency<any>, context: ClassDecoratorContext) => void;
@@ -0,0 +1,13 @@
1
+ import { InstancedServiceClass, ServiceId } from './types';
2
+ export declare const autoInjectableServices: Record<ServiceId, InstancedServiceClass<any>>;
3
+ /**
4
+ * Decorator that registers a class as an auto-injectable service for dependency injection.
5
+ *
6
+ * @param serviceId - Optional service identifier. If omitted, the class name is used.
7
+ * @returns A class decorator function.
8
+ *
9
+ * Note: During dependency resolution, any container that does not have an instance for the specified service identifier
10
+ * will create an instance of the decorated class. However, if a container already has an instance with that identifier
11
+ * prior to resolution, the decorated class will be ignored by that container.
12
+ */
13
+ export declare const autoInjectableService: (serviceId?: ServiceId) => (value: InstancedServiceClass<any>, context: ClassDecoratorContext) => void;
package/dist/index.cjs CHANGED
@@ -1,5 +1,40 @@
1
1
  'use strict';
2
2
 
3
+ const injectableClasses = {};
4
+ const constructorInjections = {};
5
+ function injectable(dependencyOrDependencies, autoInjecions) {
6
+ return function (value, context) {
7
+ if ((context === null || context === undefined ? undefined : context.kind) !== 'class') {
8
+ throw new Error('@injectable decorator should decorate classes');
9
+ }
10
+ const name = dependencyOrDependencies
11
+ ? Array.isArray(dependencyOrDependencies)
12
+ ? context.name
13
+ : dependencyOrDependencies
14
+ : context.name;
15
+ const injectToConstructor = dependencyOrDependencies
16
+ ? Array.isArray(dependencyOrDependencies)
17
+ ? dependencyOrDependencies
18
+ : autoInjecions
19
+ : autoInjecions;
20
+ if (injectableClasses[name]) {
21
+ throw new Error(`ProxyDi has already regisered dependency ${String(name)} by @injectable`);
22
+ }
23
+ injectableClasses[name] = value;
24
+ if (injectToConstructor) {
25
+ constructorInjections[name] = injectToConstructor;
26
+ }
27
+ };
28
+ }
29
+ function findInjectableId(injectable) {
30
+ for (const [id, DependencyClass] of Object.entries(injectableClasses)) {
31
+ if (DependencyClass === injectable) {
32
+ return id;
33
+ }
34
+ }
35
+ throw new Error(`Class is not @injectable: ${injectable.name}`);
36
+ }
37
+
3
38
  const INJECTIONS = Symbol('injections');
4
39
  /**
5
40
  * This symbol constant defines a property name.
@@ -28,7 +63,11 @@ const IS_INSTANCE_PROXY = Symbol('isInstanceProxy');
28
63
  const inject = (dependencyId) => {
29
64
  return function (_value, context) {
30
65
  if ((context === null || context === undefined ? undefined : context.kind) === 'field') {
31
- const id = dependencyId ? dependencyId : context.name;
66
+ const id = dependencyId
67
+ ? typeof dependencyId === 'function'
68
+ ? findInjectableId(dependencyId)
69
+ : dependencyId
70
+ : context.name;
32
71
  const injection = {
33
72
  property: context.name,
34
73
  dependencyId: id,
@@ -47,41 +86,6 @@ const inject = (dependencyId) => {
47
86
  };
48
87
  };
49
88
 
50
- const injectableClasses = {};
51
- const constructorInjections = {};
52
- function injectable(dependencyOrDependencies, autoInjecions) {
53
- return function (value, context) {
54
- if ((context === null || context === undefined ? undefined : context.kind) !== 'class') {
55
- throw new Error('@injectable decorator should decorate classes');
56
- }
57
- const name = dependencyOrDependencies
58
- ? Array.isArray(dependencyOrDependencies)
59
- ? context.name
60
- : dependencyOrDependencies
61
- : context.name;
62
- const injectToConstructor = dependencyOrDependencies
63
- ? Array.isArray(dependencyOrDependencies)
64
- ? dependencyOrDependencies
65
- : autoInjecions
66
- : autoInjecions;
67
- if (injectableClasses[name]) {
68
- throw new Error(`ProxyDi has already regisered dependency ${String(name)} by @injectable`);
69
- }
70
- injectableClasses[name] = value;
71
- if (injectToConstructor) {
72
- constructorInjections[name] = injectToConstructor;
73
- }
74
- };
75
- }
76
- function findInjectableId(injectable) {
77
- for (const [id, DependencyClass] of Object.entries(injectableClasses)) {
78
- if (DependencyClass === injectable) {
79
- return id;
80
- }
81
- }
82
- throw new Error(`Class is not @injectable: ${injectable.name}`);
83
- }
84
-
85
89
  const DEFAULT_SETTINGS = {
86
90
  allowRegisterAnything: false,
87
91
  allowRewriteDependencies: false,
@@ -128,6 +132,7 @@ const makeInjectionProxy = (injection, injectionOwner, container) => {
128
132
  },
129
133
  });
130
134
  };
135
+
131
136
  function makeDependencyProxy(dependency) {
132
137
  const injectionValues = {};
133
138
  return new Proxy(dependency, {
@@ -148,18 +153,9 @@ function makeDependencyProxy(dependency) {
148
153
  }
149
154
 
150
155
  function makeConstructorDependencyProxy(container, dependencyId) {
151
- // let values: Record<string | symbol, any> = {};
152
- // let wasResolved = false;
153
156
  function getDependency() {
154
157
  if (container.isKnown(dependencyId)) {
155
158
  const dependency = container.resolve(dependencyId);
156
- // if (!wasResolved) {
157
- // for (const key in values) {
158
- // dependency[key] = values[key];
159
- // }
160
- // values = {};
161
- // wasResolved = true;
162
- // }
163
159
  return dependency;
164
160
  }
165
161
  else {
@@ -168,20 +164,12 @@ function makeConstructorDependencyProxy(container, dependencyId) {
168
164
  }
169
165
  return new Proxy({}, {
170
166
  get: function (target, prop, receiver) {
171
- // if (values[prop]) {
172
- // return values[prop];
173
- // }
174
167
  const dependency = getDependency();
175
168
  return Reflect.get(dependency, prop, receiver);
176
169
  },
177
170
  set: function (_target, prop, value) {
178
- // if (!container.isKnown(dependencyId)) {
179
- // values[prop] = value;
180
- // return true;
181
- // } else {
182
171
  const dependency = getDependency();
183
172
  return Reflect.set(dependency, prop, value);
184
- //}
185
173
  },
186
174
  has: function (_target, prop) {
187
175
  const dependency = getDependency();
@@ -204,56 +192,73 @@ function middleware() {
204
192
  };
205
193
  }
206
194
 
207
- class MiddlewareListener {
195
+ class MiddlewareManager {
208
196
  constructor(parent) {
209
197
  this.parent = parent;
210
- this.listeners = {
198
+ this.handlers = {
211
199
  register: [],
212
200
  remove: [],
201
+ resolve: [],
213
202
  };
214
203
  }
215
204
  add(middleware) {
216
- if (isRegistingMiddleware(middleware)) {
205
+ if (isRegistrator(middleware)) {
217
206
  middleware.onRegister && this.on('register', middleware.onRegister);
218
207
  }
219
- if (isRemovingMiddleware(middleware)) {
208
+ if (isRemover(middleware)) {
220
209
  middleware.onRemove && this.on('remove', middleware.onRemove);
221
210
  }
211
+ if (isResolver(middleware)) {
212
+ middleware.onResolve && this.on('resolve', middleware.onResolve);
213
+ }
222
214
  }
223
215
  remove(middleware) {
224
- if (isRegistingMiddleware(middleware)) {
216
+ if (isRegistrator(middleware)) {
225
217
  middleware.onRegister &&
226
218
  this.off('register', middleware.onRegister);
227
219
  }
228
- if (isRemovingMiddleware(middleware)) {
220
+ if (isRemover(middleware)) {
229
221
  middleware.onRemove && this.off('remove', middleware.onRemove);
230
222
  }
223
+ if (isResolver(middleware)) {
224
+ middleware.onResolve && this.off('resolve', middleware.onResolve);
225
+ }
231
226
  }
232
227
  on(event, listener) {
233
- this.listeners[event].push(listener);
228
+ this.handlers[event].push(listener);
234
229
  }
235
- onRegister(container, dependencyId, dependency) {
230
+ onRegister(context) {
236
231
  var _a;
237
- this.listeners.register.forEach((listener) => listener(container, dependencyId, dependency));
238
- (_a = this.parent) === null || _a === undefined ? undefined : _a.onRegister(container, dependencyId, dependency);
232
+ this.handlers.register.forEach((listener) => listener(context));
233
+ (_a = this.parent) === null || _a === undefined ? undefined : _a.onRegister(context);
239
234
  }
240
- onRemove(container, dependencyId) {
235
+ onRemove(context) {
241
236
  var _a;
242
- this.listeners.remove.forEach((listener) => listener(container, dependencyId));
243
- (_a = this.parent) === null || _a === undefined ? undefined : _a.onRemove(container, dependencyId);
237
+ this.handlers.remove.forEach((listener) => listener(context));
238
+ (_a = this.parent) === null || _a === undefined ? undefined : _a.onRemove(context);
239
+ }
240
+ onResolve(context) {
241
+ let result = context;
242
+ this.handlers.resolve.forEach((listener) => {
243
+ result = listener(result);
244
+ });
245
+ return result;
244
246
  }
245
247
  off(event, listener) {
246
- const index = this.listeners[event].indexOf(listener);
248
+ const index = this.handlers[event].indexOf(listener);
247
249
  if (index !== -1) {
248
- this.listeners[event].splice(index, 1);
250
+ this.handlers[event].splice(index, 1);
249
251
  }
250
252
  }
251
253
  }
252
- function isRegistingMiddleware(middleware) {
253
- return middleware.onRegister;
254
+ function isRegistrator(middleware) {
255
+ return !!middleware.onRegister;
254
256
  }
255
- function isRemovingMiddleware(middleware) {
256
- return middleware.onRemove;
257
+ function isRemover(middleware) {
258
+ return !!middleware.onRemove;
259
+ }
260
+ function isResolver(middleware) {
261
+ return !!middleware.onResolve;
257
262
  }
258
263
 
259
264
  /**
@@ -274,26 +279,63 @@ class ProxyDiContainer {
274
279
  /**
275
280
  * Holds proxies for dependencies registered in parent containers to provide for it dependencies from this container
276
281
  */
277
- this.parentDependencyProxies = {};
282
+ this.inContextProxies = {};
283
+ this.resolveImpl = (dependencyId) => {
284
+ const proxy = this.inContextProxies[dependencyId];
285
+ if (proxy) {
286
+ return proxy;
287
+ }
288
+ const instance = this.findDependency(dependencyId);
289
+ if (instance) {
290
+ if (instance[PROXYDI_CONTAINER] !== this &&
291
+ typeof instance === 'object' &&
292
+ this.settings.resolveInContainerContext) {
293
+ const proxy = makeDependencyProxy(instance);
294
+ this.injectDependenciesTo(proxy);
295
+ this.inContextProxies[dependencyId] = proxy;
296
+ return proxy;
297
+ }
298
+ return instance;
299
+ }
300
+ const InjectableClass = injectableClasses[dependencyId];
301
+ return this.register(InjectableClass, dependencyId);
302
+ };
278
303
  this.id = ProxyDiContainer.idCounter++;
279
- this.middlewareListener = new MiddlewareListener(parent === null || parent === undefined ? undefined : parent.middlewareListener);
304
+ this.middlewareManager = new MiddlewareManager(parent === null || parent === undefined ? undefined : parent.middlewareManager);
280
305
  if (parent) {
281
306
  this.parent = parent;
282
307
  this.parent.addChild(this);
283
308
  }
284
309
  this.settings = Object.assign(Object.assign({}, DEFAULT_SETTINGS), settings);
285
310
  }
286
- register(dependency, dependencyId) {
311
+ registerMiddleware(middleware) {
312
+ this.middlewareManager.add(middleware);
313
+ }
314
+ removeMiddleware(middleware) {
315
+ this.middlewareManager.remove(middleware);
316
+ }
317
+ register(dependency, dependecyId) {
287
318
  var _a;
288
- if (this.dependencies[dependencyId]) {
319
+ let id = dependecyId;
320
+ if (!id) {
321
+ if (typeof dependency === 'function') {
322
+ try {
323
+ id = findInjectableId(dependency);
324
+ }
325
+ catch (_b) {
326
+ id = dependency.name;
327
+ }
328
+ }
329
+ }
330
+ if (this.dependencies[id]) {
289
331
  if (!this.settings.allowRewriteDependencies) {
290
- throw new Error(`ProxyDi already has dependency for ${String(dependencyId)}`);
332
+ throw new Error(`ProxyDi already has dependency for ${String(id)}`);
291
333
  }
292
334
  }
293
335
  let instance;
294
336
  const isClass = typeof dependency === 'function';
295
337
  if (isClass) {
296
- instance = this.createInstance(dependency, dependencyId);
338
+ instance = this.createInstance(dependency, id);
297
339
  }
298
340
  else {
299
341
  instance = dependency;
@@ -304,15 +346,20 @@ class ProxyDiContainer {
304
346
  }
305
347
  if (isObject) {
306
348
  instance[PROXYDI_CONTAINER] = this;
307
- instance[DEPENDENCY_ID] = dependencyId;
349
+ instance[DEPENDENCY_ID] = id;
308
350
  }
309
351
  this.injectDependenciesTo(instance);
310
- this.dependencies[dependencyId] = instance;
352
+ this.dependencies[id] = instance;
311
353
  const constructorName = (_a = instance.constructor) === null || _a === undefined ? undefined : _a.name;
312
354
  if (constructorName && middlewaresClasses[constructorName]) {
313
- this.middlewareListener.add(instance);
355
+ this.middlewareManager.add(instance);
314
356
  }
315
- this.middlewareListener.onRegister(this, dependencyId, instance);
357
+ let context = {
358
+ container: this,
359
+ dependencyId: id,
360
+ dependency: instance,
361
+ };
362
+ this.middlewareManager.onRegister(context);
316
363
  return instance;
317
364
  }
318
365
  createInstance(Dependency, dependencyId) {
@@ -330,37 +377,32 @@ class ProxyDiContainer {
330
377
  * @returns True if the dependency is known, false otherwise.
331
378
  */
332
379
  isKnown(dependencyId) {
333
- return !!(this.parentDependencyProxies[dependencyId] ||
380
+ return !!(this.inContextProxies[dependencyId] ||
334
381
  this.dependencies[dependencyId] ||
335
382
  (this.parent && this.parent.isKnown(dependencyId)) ||
336
383
  injectableClasses[dependencyId]);
337
384
  }
338
385
  resolve(dependency) {
339
386
  if (typeof dependency === 'function') {
340
- const id = findInjectableId(dependency);
387
+ let id;
388
+ try {
389
+ id = findInjectableId(dependency);
390
+ }
391
+ catch (_a) {
392
+ id = dependency.name;
393
+ }
341
394
  return this.resolve(id);
342
395
  }
343
396
  if (!this.isKnown(dependency)) {
344
397
  throw new Error(`Can't resolve unknown dependency: ${String(dependency)}`);
345
398
  }
346
- const proxy = this.parentDependencyProxies[dependency];
347
- if (proxy) {
348
- return proxy;
349
- }
350
- const instance = this.findDependency(dependency);
351
- if (instance) {
352
- if (instance[PROXYDI_CONTAINER] !== this &&
353
- typeof instance === 'object' &&
354
- this.settings.resolveInContainerContext) {
355
- const proxy = makeDependencyProxy(instance);
356
- this.injectDependenciesTo(proxy);
357
- this.parentDependencyProxies[dependency] = proxy;
358
- return proxy;
359
- }
360
- return instance;
361
- }
362
- const InjectableClass = injectableClasses[dependency];
363
- return this.register(InjectableClass, dependency);
399
+ let context = {
400
+ container: this,
401
+ dependencyId: dependency,
402
+ dependency: this.resolveImpl(dependency),
403
+ };
404
+ context = this.middlewareManager.onResolve(context);
405
+ return context.dependency;
364
406
  }
365
407
  /**
366
408
  * Injects dependencies to the given object based on its defined injections metadata. Does not affect the container.
@@ -420,7 +462,7 @@ class ProxyDiContainer {
420
462
  if (dependency) {
421
463
  const constructorName = (_a = dependency.constructor) === null || _a === undefined ? undefined : _a.name;
422
464
  if (constructorName && middlewaresClasses[constructorName]) {
423
- this.middlewareListener.remove(dependency);
465
+ this.middlewareManager.remove(dependency);
424
466
  }
425
467
  const dependencyInjects = dependency[INJECTIONS]
426
468
  ? dependency[INJECTIONS]
@@ -430,7 +472,11 @@ class ProxyDiContainer {
430
472
  });
431
473
  delete dependency[DEPENDENCY_ID];
432
474
  delete this.dependencies[id];
433
- this.middlewareListener.onRemove(this, id);
475
+ this.middlewareManager.onRemove({
476
+ container: this,
477
+ dependencyId: id,
478
+ dependency,
479
+ });
434
480
  }
435
481
  }
436
482
  /**
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- export { inject } from './inject';
1
+ export { inject } from './inject.decorator';
2
2
  export { ProxyDiContainer } from './ProxyDiContainer';
3
- export { injectable } from './injectable';
4
- export { middleware } from './middleware/middleware';
5
- export { MiddlewareRegisteringListener, MiddlewareRemovingListener, } from './middleware/MiddlewareListener';
3
+ export { injectable } from './injectable.decorator';
4
+ export { middleware } from './middleware/middleware.decorator';
5
+ export { MiddlewareRegistrator, MiddlewareRemover, MiddlewareResolver, } from './middleware/middleware.api';
6
6
  export { Injection, DependencyId, DependencyClass, ContainerSettings, PROXYDI_CONTAINER, DEPENDENCY_ID, } from './types';
7
7
  export { resolveAll } from './resolveAll';