proxydi 0.2.0 → 0.3.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.
package/CHANGELOG.md CHANGED
@@ -4,9 +4,19 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [[0.3.0](https://www.npmjs.com/package/proxydi/v/0.3.0)] - 2026-01-02
8
+
7
9
  ### Added
8
10
 
9
- - Added test coverage for Symbol-based dependency IDs with `@injectable`, `@inject`, and `@injectAll` decorators
11
+ - `@injectable` accepts multiple dependency identifiers and always registers under the class name as a default ID
12
+ - `register()` can directly bind a single dependency to multiple dependency IDs
13
+ - `@inject` accepts `ResolveScope` to resolve dependencies from children/parent/current containers
14
+ - `resolve()` and `resolveAll()` accept optional `ResolveScope` to define where dependencies are searched
15
+ - Robust removal logic for multi-ID registrations; removing by ID leaves other IDs intact, removing by instance clears all IDs
16
+
17
+ ### Breaking
18
+
19
+ - One dependency ID can now have multiple registrations; `resolve()` returns the first registered instance, while `resolveAll()`/`@injectAll` return all matching instances.
10
20
 
11
21
  ## [[0.2.0](https://www.npmjs.com/package/proxydi/v/0.2.0)] - 2026-01-01
12
22
 
@@ -20,9 +30,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
20
30
 
21
31
  - Fixed bug where passing class constructor as `dependencyId` in `register()` would store dependency under function reference instead of class name, making it impossible to resolve with `resolve(Class)`
22
32
 
23
- ### Removed
33
+ ### Breaking
24
34
 
25
- - **BREAKING:** Constructor injection support removed from `@injectable` decorator. This experimental feature caused issues with circular dependencies and added unnecessary complexity. Use field injection with `@inject` decorator instead.
35
+ - Constructor injection support removed from `@injectable` decorator. This experimental feature caused issues with circular dependencies and added unnecessary complexity. Use field injection with `@inject` decorator instead.
26
36
 
27
37
  ## [[0.1.3](https://www.npmjs.com/package/proxydi/v/0.1.3)] - 2025-03-26
28
38
 
package/README.md CHANGED
@@ -345,6 +345,77 @@ Therefore, after the container has been baked, the performance impact becomes ze
345
345
 
346
346
  To be continued...
347
347
 
348
+ ## Multiple Dependencies (v0.3.0+)
349
+
350
+ Sometimes you need to register multiple instances for the same dependency ID (e.g., plugins, event handlers).
351
+
352
+ ### Registering multiple instances
353
+
354
+ You can register multiple dependencies with the same ID using `DuplicateStrategy.AlwaysAdd`. By default, `register()` uses `DuplicateStrategy.ReplaceIfSingleElseAdd` which maintains backward compatibility (single instance replaces existing).
355
+
356
+ ```typescript
357
+ import { DuplicateStrategy } from 'proxydi';
358
+
359
+ const container = new ProxyDiContainer();
360
+
361
+ // Register multiple handlers
362
+ container.register(new Handler1(), {
363
+ dependencyId: 'EventHandler',
364
+ duplicateStrategy: DuplicateStrategy.AlwaysAdd,
365
+ });
366
+ container.register(new Handler2(), {
367
+ dependencyId: 'EventHandler',
368
+ duplicateStrategy: DuplicateStrategy.AlwaysAdd,
369
+ });
370
+ ```
371
+
372
+ ### Resolving multiple instances
373
+
374
+ Use `@injectAll` or `resolveAll` to retrieve all registered instances:
375
+
376
+ ```typescript
377
+ class EventService {
378
+ @injectAll('EventHandler') private handlers: EventHandler[];
379
+
380
+ emit(event) {
381
+ this.handlers.forEach((h) => h.handle(event));
382
+ }
383
+ }
384
+ ```
385
+
386
+ > **Note:** `resolve('EventHandler')` or `@inject('EventHandler')` will return the **first** registered instance and log a warning if multiple instances exist.
387
+
388
+ ### Auto-injectable arrays
389
+
390
+ You can also use `@injectable` with an array of IDs. These classes will be automatically discovered and registered when using `resolveAll`.
391
+
392
+ ```typescript
393
+ @injectable(['EventHandler'])
394
+ class Handler1 {}
395
+
396
+ @injectable(['EventHandler'])
397
+ class Handler2 {}
398
+
399
+ // ... later ...
400
+ const handlers = container.resolveAll('EventHandler'); // [Handler1, Handler2]
401
+ ```
402
+
403
+ ## Resolve Scope
404
+
405
+ You can control where dependencies are searched using `ResolveScope` in both `@inject` and `@injectAll`.
406
+
407
+ ```typescript
408
+ import { ResolveScope } from 'proxydi';
409
+
410
+ // Search only in children containers
411
+ @injectAll('Plugin', ResolveScope.Children)
412
+ private plugins: Plugin[];
413
+
414
+ // Search only in the current container (ignore parent)
415
+ @inject('Config', ResolveScope.Current)
416
+ private localConfig: Config;
417
+ ```
418
+
348
419
  ## Motivation
349
420
 
350
421
  The world and software changes, they become more complex over time. But the main imperative in software development stays the same - managing the complexity. Despite the tendency that software is written more often by artificial intelligence than humans, complexity stays complexity. The less complex conceptions any kind of intelligence should operate, the more efficient it will be.
@@ -1,5 +1,5 @@
1
- import { IProxyDiContainer as IProxyDiContainer, ContainerizedDependency as ContainerizedDependency, DependencyClass } from './types';
2
- import { ContainerSettings as ContainerSettings, DependencyId } from './types';
1
+ import { IProxyDiContainer as IProxyDiContainer, ContainerizedDependency as ContainerizedDependency, DependencyClass, ResolveScope, DependencyId, RegisterOptions } from './types';
2
+ import { ContainerSettings as ContainerSettings } from './types';
3
3
  import { MiddlewareRegistrator, MiddlewareRemover, MiddlewareResolver } from './middleware/middleware.api';
4
4
  /**
5
5
  * A dependency injection container
@@ -26,6 +26,10 @@ export declare class ProxyDiContainer implements IProxyDiContainer {
26
26
  * Holds proxies for dependencies registered in parent containers to provide for it dependencies from this container
27
27
  */
28
28
  private inContextProxies;
29
+ /**
30
+ * Mapping from dependency instance to all IDs under which it was registered
31
+ */
32
+ private dependencyIds;
29
33
  /**
30
34
  * Settings that control the behavior of the container and it's children
31
35
  */
@@ -44,18 +48,18 @@ export declare class ProxyDiContainer implements IProxyDiContainer {
44
48
  * In case of class, it will be instantiated without any parameters.
45
49
  *
46
50
  * @param dependency The dependency instance or dependency class.
47
- * @param dependencyId The unique identifier for the dependency in this container. Can be a string, symbol, or class constructor (which will be normalized to class name).
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.
51
+ * @param dependencyId The unique identifier(s) for the dependency in this container. Can be a string, symbol, array or class constructor (which will be normalized to class name or @injectable IDs).
49
52
  * @returns Dependency instance, registered in container
50
53
  */
51
- register<T>(DependencyClass: DependencyClass<T>, dependencyId?: DependencyId | DependencyClass<any>): T & ContainerizedDependency;
52
- register<T>(dependency: T extends new (...args: any[]) => any ? never : T, dependencyId?: DependencyId | DependencyClass<any>): T & ContainerizedDependency;
54
+ register<T>(DependencyClass: DependencyClass<T>, options?: RegisterOptions | DependencyId | DependencyId[] | DependencyClass<any>): T & ContainerizedDependency;
55
+ register<T>(dependency: T extends new (...args: any[]) => any ? never : T, options?: RegisterOptions | DependencyId | DependencyId[] | DependencyClass<any>): T & ContainerizedDependency;
53
56
  /**
54
57
  * Checks if a dependency with the given ID is known to the container or its ancestors which means that it can be resolved by this container
55
58
  * @param dependencyId The identifier of the dependency. Can be a string, symbol, or class constructor (which will be normalized to class name).
56
59
  * @returns True if the dependency is known, false otherwise.
57
60
  */
58
- isKnown(dependencyId: DependencyId | DependencyClass<any>): boolean;
61
+ isKnown(dependencyId: DependencyId | DependencyClass<any>, scope?: ResolveScope): boolean;
62
+ private isKnownById;
59
63
  /**
60
64
  * Checks if a dependency with the given ID exists in this container only (does not check parents)
61
65
  * @param dependencyId The identifier of the dependency. Can be a string, symbol, or class constructor (which will be normalized to class name).
@@ -68,9 +72,17 @@ export declare class ProxyDiContainer implements IProxyDiContainer {
68
72
  * @returns The resolved dependency instance with container metadata.
69
73
  * @throws Error if the dependency cannot be found or is not auto injectable.
70
74
  */
71
- resolve<T>(dependencyId: DependencyId): T & ContainerizedDependency;
72
- resolve<T extends DependencyClass<any>>(SomeClass: T): InstanceType<T> & ContainerizedDependency;
73
- private resolveImpl;
75
+ resolve<T>(dependency: DependencyId, scope?: ResolveScope): T & ContainerizedDependency;
76
+ resolve<T extends DependencyClass<any>>(dependency: T, scope?: ResolveScope): InstanceType<T> & ContainerizedDependency;
77
+ resolveAll<T>(dependencyId: DependencyId, scope?: ResolveScope): (T & ContainerizedDependency)[];
78
+ resolveAll<T extends DependencyClass<any>>(dependencyId: T, scope?: ResolveScope): (InstanceType<T> & ContainerizedDependency)[];
79
+ private resolveById;
80
+ private autoRegisterInjectable;
81
+ private autoRegisterAllInjectables;
82
+ private getContextProxy;
83
+ private recursiveResolveAll;
84
+ private findFirstInScope;
85
+ private dedupe;
74
86
  /**
75
87
  * Injects dependencies to the given object based on its defined injections metadata. Does not affect the container.
76
88
  * @param injectionsOwner The object to inject dependencies into.
@@ -82,8 +94,7 @@ export declare class ProxyDiContainer implements IProxyDiContainer {
82
94
  */
83
95
  registerInjectables(): this;
84
96
  /**
85
- * Finalizes dependency injections, prevents further rewriting of dependencies,
86
- * and recursively bakes injections for child containers.
97
+ * Finalizes dependency injections and recursively bakes injections for child containers.
87
98
  */
88
99
  bakeInjections(): void;
89
100
  /**
@@ -101,18 +112,6 @@ export declare class ProxyDiContainer implements IProxyDiContainer {
101
112
  * recursively destroying child containers and removing itself from its parent.
102
113
  */
103
114
  destroy(): void;
104
- /**
105
- * Recursively finds a dependency by its ID from this container or its parent.
106
- * @param dependencyId The identifier of the dependency to find.
107
- * @returns The dependency if found, otherwise undefined.
108
- */
109
- private findDependency;
110
- /**
111
- * Normalizes dependency identifier by converting class constructors to their names.
112
- * @param id The dependency identifier (string, symbol, or class constructor).
113
- * @returns Normalized dependency identifier (string or symbol).
114
- */
115
- private normalizeDependencyId;
116
115
  /**
117
116
  * All direct descendants of this container
118
117
  */
@@ -134,4 +133,13 @@ export declare class ProxyDiContainer implements IProxyDiContainer {
134
133
  * @param id The identifier of the child container to remove.
135
134
  */
136
135
  private removeChild;
136
+ private normalizeDependencyIds;
137
+ private normalizeRegisterOptions;
138
+ private normalizeToIds;
139
+ private addDependencyInstance;
140
+ private notifyOnRegister;
141
+ private getAllOwnDependencies;
142
+ private removeById;
143
+ private removeByInstance;
144
+ private removeDependencyBinding;
137
145
  }