decorator-dependency-injection 1.0.3 → 1.0.4

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/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![npm version](https://badge.fury.io/js/decorator-dependency-injection.svg)](http://badge.fury.io/js/decorator-dependency-injection)
4
4
  [![Build Status](https://github.com/mallocator/decorator-dependency-injection/actions/workflows/release.yml/badge.svg)](https://github.com/mallocator/decorator-dependency-injection/actions/workflows/release.yml)
5
+ [![Coverage](https://img.shields.io/badge/coverage-93%25-brightgreen)](https://github.com/mallocator/decorator-dependency-injection)
5
6
 
6
7
  ## Description
7
8
 
@@ -61,9 +62,9 @@ The ```@Singleton``` decorator is used to inject a single instance of a dependen
61
62
  want to share the same instance of a class across multiple classes.
62
63
 
63
64
  ```javascript
64
- import {Singleton} from 'decorator-dependency-injection';
65
+ import {Singleton, Inject} from 'decorator-dependency-injection';
65
66
 
66
- @Singleton
67
+ @Singleton()
67
68
  class Dependency {
68
69
  }
69
70
 
@@ -78,9 +79,9 @@ The ```@Factory``` decorator is used to inject a new instance of a dependency in
78
79
  This is useful when you want to create a new instance of a class each time it is injected.
79
80
 
80
81
  ```javascript
81
- import {Factory} from 'decorator-dependency-injection';
82
+ import {Factory, Inject} from 'decorator-dependency-injection';
82
83
 
83
- @Factory
84
+ @Factory()
84
85
  class Dependency {
85
86
  }
86
87
 
@@ -89,23 +90,23 @@ class Consumer {
89
90
  }
90
91
  ```
91
92
 
92
- ### LazyInject
93
+ ### InjectLazy
93
94
 
94
95
  ```@Inject``` annotated properties are evaluated during instance initialization. That means that all properties should
95
96
  be accessible in the constructor. That also means that we're creating an instance no matter if you access the property
96
- or not. If you want to only create an instance when you access the property, you can use the ```@LazyInject```
97
+ or not. If you want to only create an instance when you access the property, you can use the ```@InjectLazy```
97
98
  decorator. This will create the instance only when the property is accessed for the first time. Note that this also
98
- works from the constructor, same as the regular ```@Inject```.
99
+ works from the constructor, same as the regular ```@Inject```.
99
100
 
100
101
  ```javascript
101
- import {LazyInject} from 'decorator-dependency-injection';
102
+ import {Singleton, InjectLazy} from 'decorator-dependency-injection';
102
103
 
103
- @Singleton
104
+ @Singleton()
104
105
  class Dependency {
105
106
  }
106
107
 
107
108
  class Consumer {
108
- @LazyInject(Dependency) dependency // creates an instance only when the property is accessed
109
+ @InjectLazy(Dependency) dependency // creates an instance only when the property is accessed
109
110
  }
110
111
  ```
111
112
 
@@ -138,9 +139,9 @@ will only be passed to the dependency the first time it is created.
138
139
  You can mock dependencies by using the ```@Mock``` decorator with a function that returns the mock dependency.
139
140
 
140
141
  ```javascript
141
- import {Factory, Inject, Mock} from 'decorator-dependency-injection'
142
+ import {Factory, Inject, Mock, resetMock} from 'decorator-dependency-injection'
142
143
 
143
- @Factory
144
+ @Factory()
144
145
  class Dependency {
145
146
  method() {
146
147
  return 'real'
@@ -177,18 +178,29 @@ The `resetMock` utility function allows you to remove any active mock for a depe
177
178
  implementation. This is useful for cleaning up after tests or switching between real and mock dependencies.
178
179
 
179
180
  ```javascript
180
- import {resetMock} from 'decorator-dependency-injection';
181
+ import {resetMock, resetMocks} from 'decorator-dependency-injection';
181
182
 
182
183
  resetMock(Dependency); // Restores the original Dependency implementation
184
+ resetMocks(); // Restores all mocked dependencies
185
+ ```
186
+
187
+ ### Clearing the Container
188
+
189
+ For complete test isolation, you can clear all registered instances from the container:
190
+
191
+ ```javascript
192
+ import {clearContainer} from 'decorator-dependency-injection';
193
+
194
+ clearContainer(); // Removes all registered singletons, factories, and mocks
183
195
  ```
184
196
 
185
197
  You can also use the ```@Mock``` decorator as a proxy instead of a full mock. Any method calls not implemented in the
186
198
  mock will be passed to the real dependency.
187
199
 
188
200
  ```javascript
189
- import {Factory, Inject, Mock} from 'decorator-dependency-injection'
201
+ import {Factory, Inject, Mock, resetMock} from 'decorator-dependency-injection'
190
202
 
191
- @Factory
203
+ @Factory()
192
204
  class Dependency {
193
205
  method() {
194
206
  return 'real'
@@ -225,6 +237,45 @@ const consumer = new Consumer() // prints 'real other'
225
237
 
226
238
  For more examples, see the tests in the ```test``` directory.
227
239
 
240
+ ## Advanced Usage
241
+
242
+ ### Using Isolated Containers
243
+
244
+ For advanced scenarios like parallel test execution or module isolation, you can create separate containers:
245
+
246
+ ```javascript
247
+ import {Container} from 'decorator-dependency-injection';
248
+
249
+ const container1 = new Container();
250
+ const container2 = new Container();
251
+
252
+ class MyService {}
253
+
254
+ // Register the same class in different containers
255
+ container1.registerSingleton(MyService);
256
+ container2.registerSingleton(MyService);
257
+
258
+ // Each container maintains its own singleton instance
259
+ const ctx1 = container1.getContext(MyService);
260
+ const ctx2 = container2.getContext(MyService);
261
+
262
+ const instance1 = container1.getInstance(ctx1, []);
263
+ const instance2 = container2.getInstance(ctx2, []);
264
+
265
+ console.log(instance1 === instance2); // false - different containers
266
+ ```
267
+
268
+ ### Accessing the Default Container
269
+
270
+ You can access the default global container for programmatic registration:
271
+
272
+ ```javascript
273
+ import {getContainer} from 'decorator-dependency-injection';
274
+
275
+ const container = getContainer();
276
+ console.log(container.has(MyService)); // Check if a class is registered
277
+ ```
278
+
228
279
  ## Running the tests
229
280
 
230
281
  To run the tests, run the following command in the project root.
@@ -238,4 +289,5 @@ npm test
238
289
  - 1.0.0 - Initial release
239
290
  - 1.0.1 - Automated release with GitHub Actions
240
291
  - 1.0.2 - Added proxy option to @Mock decorator
241
- - 1.0.3 - Added @LazyInject decorator
292
+ - 1.0.3 - Added @InjectLazy decorator
293
+ - 1.0.4 - Added Container abstraction, clearContainer(), TypeScript definitions, improved proxy support
@@ -0,0 +1,73 @@
1
+ import { defineConfig } from "eslint/config";
2
+ import js from "@eslint/js";
3
+ import babelParser from "@babel/eslint-parser";
4
+
5
+ export default defineConfig([
6
+ // Recommended base config
7
+ js.configs.recommended,
8
+
9
+ // Global ignores
10
+ {
11
+ ignores: ["node_modules/**", "coverage/**", "docs/**", ".history/**"]
12
+ },
13
+
14
+ // Source files configuration
15
+ {
16
+ name: "source-files",
17
+ files: ["index.js", "src/**/*.js"],
18
+ languageOptions: {
19
+ ecmaVersion: 2022,
20
+ sourceType: "module",
21
+ parser: babelParser,
22
+ parserOptions: {
23
+ requireConfigFile: true,
24
+ babelOptions: {
25
+ configFile: "./babel.config.json"
26
+ }
27
+ },
28
+ globals: {
29
+ console: "readonly",
30
+ process: "readonly"
31
+ }
32
+ },
33
+ rules: {
34
+ "no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
35
+ "prefer-const": "error",
36
+ "no-var": "error",
37
+ "eqeqeq": ["error", "always"],
38
+ "no-throw-literal": "error"
39
+ }
40
+ },
41
+
42
+ // Test files with relaxed rules and Jest globals
43
+ {
44
+ name: "test-files",
45
+ files: ["test/**/*.js"],
46
+ languageOptions: {
47
+ ecmaVersion: 2022,
48
+ sourceType: "module",
49
+ parser: babelParser,
50
+ parserOptions: {
51
+ requireConfigFile: true,
52
+ babelOptions: {
53
+ configFile: "./babel.config.json"
54
+ }
55
+ },
56
+ globals: {
57
+ console: "readonly",
58
+ process: "readonly",
59
+ describe: "readonly",
60
+ it: "readonly",
61
+ expect: "readonly",
62
+ beforeEach: "readonly",
63
+ afterEach: "readonly",
64
+ jest: "readonly",
65
+ fail: "readonly"
66
+ }
67
+ },
68
+ rules: {
69
+ // In tests, decorated classes are used by the decorator system, not directly
70
+ "no-unused-vars": ["warn", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }]
71
+ }
72
+ }
73
+ ]);
package/index.d.ts ADDED
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Type definitions for decorator-dependency-injection
3
+ */
4
+
5
+ /**
6
+ * Context for registered instances in the container
7
+ */
8
+ export interface InstanceContext {
9
+ /** The type of registration */
10
+ type: 'singleton' | 'factory'
11
+ /** The current class constructor (may be a mock) */
12
+ clazz: new (...args: any[]) => any
13
+ /** The original class constructor if mocked */
14
+ originalClazz?: new (...args: any[]) => any
15
+ /** The cached singleton instance */
16
+ instance?: any
17
+ /** Whether to use proxy mocking */
18
+ proxy?: boolean
19
+ }
20
+
21
+ /**
22
+ * A dependency injection container that manages singleton and factory instances.
23
+ */
24
+ export declare class Container {
25
+ /**
26
+ * Register a class as a singleton.
27
+ */
28
+ registerSingleton(clazz: new (...args: any[]) => any, name?: string): void
29
+
30
+ /**
31
+ * Register a class as a factory.
32
+ */
33
+ registerFactory(clazz: new (...args: any[]) => any, name?: string): void
34
+
35
+ /**
36
+ * Get the context for a given class or name.
37
+ */
38
+ getContext(clazzOrName: string | (new (...args: any[]) => any)): InstanceContext
39
+
40
+ /**
41
+ * Check if a class or name is registered.
42
+ */
43
+ has(clazzOrName: string | (new (...args: any[]) => any)): boolean
44
+
45
+ /**
46
+ * Get or create an instance based on the context.
47
+ */
48
+ getInstance(instanceContext: InstanceContext, params: any[]): any
49
+
50
+ /**
51
+ * Register a mock for an existing class.
52
+ */
53
+ registerMock(
54
+ targetClazzOrName: string | (new (...args: any[]) => any),
55
+ mockClazz: new (...args: any[]) => any,
56
+ useProxy?: boolean
57
+ ): void
58
+
59
+ /**
60
+ * Reset a specific mock to its original class.
61
+ */
62
+ resetMock(clazzOrName: string | (new (...args: any[]) => any)): void
63
+
64
+ /**
65
+ * Reset all mocks to their original classes.
66
+ */
67
+ resetAllMocks(): void
68
+
69
+ /**
70
+ * Clear all registered instances and mocks.
71
+ */
72
+ clear(): void
73
+ }
74
+
75
+ /**
76
+ * Register a class as a singleton.
77
+ * @param name Optional name to register the singleton under
78
+ */
79
+ export declare function Singleton(name?: string): ClassDecorator
80
+
81
+ /**
82
+ * Register a class as a factory.
83
+ * @param name Optional name to register the factory under
84
+ */
85
+ export declare function Factory(name?: string): ClassDecorator
86
+
87
+ /**
88
+ * Inject a singleton or factory instance into a class field.
89
+ * @param clazzOrName The class or name to inject
90
+ * @param params Optional parameters to pass to the constructor
91
+ */
92
+ export declare function Inject<T>(
93
+ clazzOrName: string | (new (...args: any[]) => T),
94
+ ...params: any[]
95
+ ): PropertyDecorator
96
+
97
+ /**
98
+ * Inject a singleton or factory instance lazily into a class field.
99
+ * The instance is created on first access.
100
+ * @param clazzOrName The class or name to inject
101
+ * @param params Optional parameters to pass to the constructor
102
+ */
103
+ export declare function InjectLazy<T>(
104
+ clazzOrName: string | (new (...args: any[]) => T),
105
+ ...params: any[]
106
+ ): PropertyDecorator
107
+
108
+ /**
109
+ * Mark a class as a mock for another class.
110
+ * @param mockedClazzOrName The class or name to mock
111
+ * @param proxy If true, unmocked methods delegate to the original
112
+ */
113
+ export declare function Mock(
114
+ mockedClazzOrName: string | (new (...args: any[]) => any),
115
+ proxy?: boolean
116
+ ): ClassDecorator
117
+
118
+ /**
119
+ * Reset all mocks to their original classes.
120
+ */
121
+ export declare function resetMocks(): void
122
+
123
+ /**
124
+ * Reset a specific mock to its original class.
125
+ * @param clazzOrName The class or name to reset
126
+ */
127
+ export declare function resetMock(clazzOrName: string | (new (...args: any[]) => any)): void
128
+
129
+ /**
130
+ * Clear all registered instances and mocks from the container.
131
+ */
132
+ export declare function clearContainer(): void
133
+
134
+ /**
135
+ * Get the default container instance.
136
+ */
137
+ export declare function getContainer(): Container
138
+
139
+ /**
140
+ * Create a proxy that delegates to the mock first, then falls back to the original.
141
+ * This is an internal utility but exported for advanced use cases.
142
+ *
143
+ * @param mock The mock instance
144
+ * @param original The original instance to fall back to
145
+ */
146
+ export declare function createProxy<T extends object>(mock: T, original: T): T
package/index.js CHANGED
@@ -1,22 +1,22 @@
1
1
  /**
2
- * @typedef {Object} InstanceContext
3
- * @property {'singleton'|'factory'} type - The type of the instance.
4
- * @property {Function} clazz - The class constructor for the instance.
5
- * @property {Function} [originalClazz] - The original class if this is a mock.
6
- * @property {Object} [instance] - The singleton instance, if created.
7
- * @property {Object} [originalInstance] - The original instance if this is a mock.
8
- * @property {boolean} [proxy=false] - If true, the mock will proxy to the original class for undefined methods/properties.
2
+ * Decorator Dependency Injection
3
+ *
4
+ * A simple library for dependency injection using TC39 Stage 3 decorators.
5
+ *
6
+ * @module decorator-dependency-injection
9
7
  */
10
8
 
11
- /** @type {Map<string|Class, InstanceContext>} */
12
- const instances = new Map()
9
+ import {Container} from './src/Container.js'
10
+
11
+ /** @type {Container} The default global container */
12
+ const defaultContainer = new Container()
13
13
 
14
14
  /**
15
15
  * Register a class as a singleton. If a name is provided, it will be used as the key in the singleton map.
16
16
  * Singleton instances only ever have one instance created via the @Inject decorator.
17
17
  *
18
18
  * @param {string} [name] The name of the singleton. If not provided, the class will be used as the key.
19
- * @return {(function(Function, {kind: string}): void)}
19
+ * @returns {(function(Function, {kind: string}): void)}
20
20
  * @example @Singleton() class MySingleton {}
21
21
  * @example @Singleton('customName') class MySingleton {}
22
22
  * @throws {Error} If the injection target is not a class
@@ -31,11 +31,7 @@ export function Singleton(name) {
31
31
  if (typeof clazz !== 'function' || !clazz.prototype) {
32
32
  throw new Error('Target must be a class constructor')
33
33
  }
34
- const key = name ?? clazz
35
- if (instances.has(key)) {
36
- throw new Error('A different class is already registered under this name. This may be possibly a circular dependency. Try using @InjectLazy')
37
- }
38
- instances.set(key, {clazz, type: 'singleton'})
34
+ defaultContainer.registerSingleton(clazz, name)
39
35
  }
40
36
  }
41
37
 
@@ -44,7 +40,7 @@ export function Singleton(name) {
44
40
  * Factory instances are created via the @Inject decorator. Each call to the factory will create a new instance.
45
41
  *
46
42
  * @param {string} [name] The name of the factory. If not provided, the class will be used as the key.
47
- * @return {(function(Function, {kind: string}): void)}
43
+ * @returns {(function(Function, {kind: string}): void)}
48
44
  * @example @Factory() class MyFactory {}
49
45
  * @example @Factory('customName') class MyFactory {}
50
46
  * @throws {Error} If the injection target is not a class
@@ -59,11 +55,7 @@ export function Factory(name) {
59
55
  if (typeof clazz !== 'function' || !clazz.prototype) {
60
56
  throw new Error('Target must be a class constructor')
61
57
  }
62
- const key = name ?? clazz
63
- if (instances.has(key)) {
64
- throw new Error('A different class is already registered under this name, This may be possibly a circular dependency. Try using @InjectLazy')
65
- }
66
- instances.set(key, {clazz, type: 'factory'})
58
+ defaultContainer.registerFactory(clazz, name)
67
59
  }
68
60
  }
69
61
 
@@ -71,16 +63,16 @@ export function Factory(name) {
71
63
  * Inject a singleton or factory instance into a class field. You can also provide parameters to the constructor.
72
64
  * If the instance is a singleton, it will only be created once with the first set of parameters it encounters.
73
65
  *
74
- * @param {string|Class} clazzOrName The singleton or factory class or name
75
- * @param {*} params Parameters to pass to the constructor. Recommended to use only with factories.
76
- * @return {(function(*, {kind: string, name: string}): void)}
66
+ * @param {string|Function} clazzOrName The singleton or factory class or name
67
+ * @param {...*} params Parameters to pass to the constructor. Recommended to use only with factories.
68
+ * @returns {(function(*, {kind: string, name: string}): function(): Object)}
77
69
  * @example @Inject(MySingleton) mySingleton
78
70
  * @example @Inject("myCustomName") myFactory
79
71
  * @throws {Error} If the injection target is not a field
80
72
  * @throws {Error} If the injected field is assigned a value
81
73
  */
82
74
  export function Inject(clazzOrName, ...params) {
83
- return function (initialValue, context) {
75
+ return function (_, context) {
84
76
  if (context.kind !== 'field') {
85
77
  throw new Error('Invalid injection target')
86
78
  }
@@ -88,8 +80,8 @@ export function Inject(clazzOrName, ...params) {
88
80
  if (initialValue) {
89
81
  throw new Error('Cannot assign value to injected field')
90
82
  }
91
- const instanceContext = getContext(clazzOrName)
92
- return getInjectedInstance(instanceContext, params)
83
+ const instanceContext = defaultContainer.getContext(clazzOrName)
84
+ return defaultContainer.getInstance(instanceContext, params)
93
85
  }
94
86
  }
95
87
  }
@@ -97,9 +89,10 @@ export function Inject(clazzOrName, ...params) {
97
89
  /**
98
90
  * Inject a singleton or factory instance lazily into a class field. You can also provide parameters to the constructor.
99
91
  * If the instance is a singleton, it will only be created once with the first set of parameters it encounters.
100
- * @param {string|Class} clazzOrName The singleton or factory class or name
101
- * @param {*} params Parameters to pass to the constructor. Recommended to use only with factories.
102
- * @return {(function(*, {kind: string, name: string, addInitializer: Function}): void)}
92
+ *
93
+ * @param {string|Function} clazzOrName The singleton or factory class or name
94
+ * @param {...*} params Parameters to pass to the constructor. Recommended to use only with factories.
95
+ * @returns {(function(*, {kind: string, name: string, addInitializer: Function}): void)}
103
96
  * @example @InjectLazy(MySingleton) mySingleton
104
97
  * @example @InjectLazy("myCustomName") myFactory
105
98
  * @throws {Error} If the injection target is not a field
@@ -107,7 +100,7 @@ export function Inject(clazzOrName, ...params) {
107
100
  */
108
101
  export function InjectLazy(clazzOrName, ...params) {
109
102
  const cache = new WeakMap()
110
- return (initialValue, context) => {
103
+ return (_, context) => {
111
104
  if (context.kind !== 'field') {
112
105
  throw new Error('Invalid injection target')
113
106
  }
@@ -115,12 +108,15 @@ export function InjectLazy(clazzOrName, ...params) {
115
108
  Object.defineProperty(this, context.name, {
116
109
  get() {
117
110
  if (!cache.has(this)) {
118
- const instanceContext = getContext(clazzOrName)
119
- const value = getInjectedInstance(instanceContext, params)
111
+ const instanceContext = defaultContainer.getContext(clazzOrName)
112
+ const value = defaultContainer.getInstance(instanceContext, params)
120
113
  cache.set(this, value)
121
114
  }
122
115
  return cache.get(this)
123
116
  },
117
+ set() {
118
+ throw new Error(`Cannot assign value to lazy-injected field "${context.name}"`)
119
+ },
124
120
  configurable: true,
125
121
  enumerable: true
126
122
  })
@@ -128,30 +124,15 @@ export function InjectLazy(clazzOrName, ...params) {
128
124
  }
129
125
  }
130
126
 
131
- /**
132
- * Get a proxy for the mock instance. This allows the mock to call methods on the original class if they are not defined in the mock.
133
- * @param {Object} mock The mock instance
134
- * @param {Object} original The original class instance
135
- * @return {*|object} The proxy instance
136
- */
137
- function getProxy(mock, original) {
138
- return new Proxy(mock, {
139
- get(target, prop, receiver) {
140
- if (prop in target) {
141
- return Reflect.get(target, prop, receiver)
142
- }
143
- return Reflect.get(original, prop, receiver)
144
- }
145
- })
146
- }
147
-
148
127
  /**
149
128
  * Mark a class as a mock. This will replace the class with a mock instance when injected.
150
- * @param {string|Class} mockedClazzOrName The singleton or factory class or name to be mocked
151
- * @param {boolean} [proxy=false] If true, the mock will be a proxy to the original class. Any methods not defined in the mock will be called on the original class.
152
- * @return {(function(Function, {kind: string}): void)}
129
+ *
130
+ * @param {string|Function} mockedClazzOrName The singleton or factory class or name to be mocked
131
+ * @param {boolean} [proxy=false] If true, the mock will proxy to the original class.
132
+ * Any methods not defined in the mock will be called on the original class.
133
+ * @returns {(function(Function, {kind: string}): void)}
153
134
  * @example @Mock(MySingleton) class MyMock {}
154
- * @example @Mock("myCustomName") class MyMock {}
135
+ * @example @Mock("myCustomName", true) class MyMock {}
155
136
  * @throws {Error} If the injection target is not a class
156
137
  * @throws {Error} If the injection source is not found
157
138
  */
@@ -160,32 +141,7 @@ export function Mock(mockedClazzOrName, proxy = false) {
160
141
  if (context.kind !== 'class') {
161
142
  throw new Error('Invalid injection target')
162
143
  }
163
- const instanceContext = getContext(mockedClazzOrName)
164
- if (instanceContext.originalClazz) {
165
- throw new Error('Mock already defined, reset before mocking again')
166
- }
167
- instanceContext.originalClazz = instanceContext.clazz
168
- instanceContext.proxy = proxy
169
- instanceContext.clazz = clazz
170
- }
171
- }
172
-
173
- /**
174
- * Internal: Get the context for a given class or name.
175
- *
176
- * @param {string|Class} mockedClazzOrName - The class or name to look up.
177
- * @returns {InstanceContext}
178
- * @throws {Error} If the context is not found.
179
- */
180
- function getContext(mockedClazzOrName) {
181
- if (instances.has(mockedClazzOrName)) {
182
- return instances.get(mockedClazzOrName)
183
- } else {
184
- const available = Array.from(instances.keys()).map(k => typeof k === 'string' ? k : k.name).join(', ')
185
- throw new Error(
186
- `Cannot find injection source for "${mockedClazzOrName?.name || mockedClazzOrName}". ` +
187
- `Available: [${available}]`
188
- )
144
+ defaultContainer.registerMock(mockedClazzOrName, clazz, proxy)
189
145
  }
190
146
  }
191
147
 
@@ -193,64 +149,38 @@ function getContext(mockedClazzOrName) {
193
149
  * Reset all mocks to their original classes.
194
150
  */
195
151
  export function resetMocks() {
196
- for (const instanceContext of instances.values()) {
197
- restoreOriginal(instanceContext)
198
- }
152
+ defaultContainer.resetAllMocks()
199
153
  }
200
154
 
201
155
  /**
202
156
  * Reset a specific mock to its original class.
203
- * @param {string|Class} clazzOrName The singleton or factory class or name to reset
157
+ *
158
+ * @param {string|Function} clazzOrName The singleton or factory class or name to reset
204
159
  */
205
160
  export function resetMock(clazzOrName) {
206
- restoreOriginal(getContext(clazzOrName))
161
+ defaultContainer.resetMock(clazzOrName)
207
162
  }
208
163
 
209
164
  /**
210
- * Internal function to reset an instance context to its original.
211
- * @param {InstanceContext} instanceContext The instance context to reset
212
- * @private
165
+ * Clear all registered instances and mocks from the container.
166
+ * Useful for complete test isolation between test suites.
213
167
  */
214
- function restoreOriginal(instanceContext) {
215
- if (!instanceContext) {
216
- throw new Error('Cannot find injection source with the provided name')
217
- }
218
- if (instanceContext.originalClazz) {
219
- instanceContext.clazz = instanceContext.originalClazz
220
- delete instanceContext.instance
221
- delete instanceContext.originalClazz
222
- delete instanceContext.originalInstance
223
- }
168
+ export function clearContainer() {
169
+ defaultContainer.clear()
224
170
  }
225
171
 
226
172
  /**
227
- * Get the injected instance based on the context and parameters.
228
- * @param {InstanceContext} instanceContext The instance context
229
- * @param {Array} params The parameters to pass to the constructor
230
- * @return {Object} The injected instance
173
+ * Get the default container instance.
174
+ * Useful for advanced use cases or testing the container itself.
175
+ *
176
+ * @returns {Container} The default container
231
177
  */
232
- function getInjectedInstance(instanceContext, params) {
233
- if (instanceContext.type === 'singleton' && !instanceContext.originalClazz && instanceContext.instance) {
234
- return instanceContext.instance
235
- }
236
- let instance
237
- try {
238
- instance = new instanceContext.clazz(...params)
239
- } catch (err) {
240
- if (err instanceof RangeError) {
241
- throw new Error(
242
- `Circular dependency detected for ${instanceContext.clazz.name || instanceContext.clazz}. ` +
243
- `Use @InjectLazy to break the cycle.`
244
- )
245
- }
246
- throw err
247
- }
248
- if (instanceContext.proxy && instanceContext.originalClazz) {
249
- const originalInstance = new instanceContext.originalClazz(...params)
250
- instance = getProxy(instance, originalInstance)
251
- }
252
- if (instanceContext.type === 'singleton') {
253
- instanceContext.instance = instance
254
- }
255
- return instance
178
+ export function getContainer() {
179
+ return defaultContainer
256
180
  }
181
+
182
+ // Export Container class for advanced use cases (e.g., isolated containers)
183
+ export {Container}
184
+
185
+ // Export createProxy for advanced proxy use cases
186
+ export {createProxy} from './src/proxy.js'