decorator-dependency-injection 1.0.6 → 1.0.7
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 +318 -386
- package/index.d.ts +173 -13
- package/index.js +246 -21
- package/package.json +18 -5
- package/src/Container.js +248 -26
- package/src/proxy.js +15 -15
package/index.d.ts
CHANGED
|
@@ -29,10 +29,43 @@ export interface InstanceContext {
|
|
|
29
29
|
proxy?: boolean
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Registration info returned by list()
|
|
34
|
+
*/
|
|
35
|
+
export interface RegistrationInfo {
|
|
36
|
+
/** The registration key (class or string name) */
|
|
37
|
+
key: string | Constructor
|
|
38
|
+
/** Human-readable name */
|
|
39
|
+
name: string
|
|
40
|
+
/** Registration type */
|
|
41
|
+
type: 'singleton' | 'factory'
|
|
42
|
+
/** Whether this registration is mocked */
|
|
43
|
+
isMocked: boolean
|
|
44
|
+
/** Whether a cached instance exists */
|
|
45
|
+
hasInstance: boolean
|
|
46
|
+
}
|
|
47
|
+
|
|
32
48
|
/**
|
|
33
49
|
* A dependency injection container that manages singleton and factory instances.
|
|
34
50
|
*/
|
|
35
51
|
export declare class Container {
|
|
52
|
+
/**
|
|
53
|
+
* Custom string tag for better debugging.
|
|
54
|
+
* Shows as [object Container] in console.
|
|
55
|
+
*/
|
|
56
|
+
readonly [Symbol.toStringTag]: 'Container'
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Make the container iterable.
|
|
60
|
+
* Yields registration info for each registered class.
|
|
61
|
+
*/
|
|
62
|
+
[Symbol.iterator](): IterableIterator<RegistrationInfo>
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get the number of registrations in the container.
|
|
66
|
+
*/
|
|
67
|
+
readonly size: number
|
|
68
|
+
|
|
36
69
|
/**
|
|
37
70
|
* Enable or disable debug logging.
|
|
38
71
|
* When enabled, logs when instances are created.
|
|
@@ -60,6 +93,22 @@ export declare class Container {
|
|
|
60
93
|
*/
|
|
61
94
|
has<T>(clazzOrName: InjectionToken<T>): boolean
|
|
62
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Check if a class or name has a mock registered.
|
|
98
|
+
*/
|
|
99
|
+
isMocked<T>(clazzOrName: InjectionToken<T>): boolean
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Unregister a class or name from the container.
|
|
103
|
+
* @returns true if the registration was removed, false if it wasn't registered
|
|
104
|
+
*/
|
|
105
|
+
unregister<T>(clazzOrName: InjectionToken<T>): boolean
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* List all registrations in the container.
|
|
109
|
+
*/
|
|
110
|
+
list(): RegistrationInfo[]
|
|
111
|
+
|
|
63
112
|
/**
|
|
64
113
|
* Resolve and return an instance by class or name.
|
|
65
114
|
* This allows non-decorator code to retrieve instances from the container.
|
|
@@ -74,26 +123,49 @@ export declare class Container {
|
|
|
74
123
|
/**
|
|
75
124
|
* Register a mock for an existing class.
|
|
76
125
|
*/
|
|
77
|
-
registerMock<T>(
|
|
78
|
-
targetClazzOrName: InjectionToken<T>,
|
|
79
|
-
mockClazz: Constructor<T>,
|
|
80
|
-
useProxy?: boolean
|
|
81
|
-
): void
|
|
126
|
+
registerMock<T>(targetClazzOrName: InjectionToken<T>, mockClazz: Constructor<Partial<T>>, useProxy?: boolean): void
|
|
82
127
|
|
|
83
128
|
/**
|
|
84
|
-
*
|
|
129
|
+
* Get the mock instance for a mocked class.
|
|
130
|
+
* @throws Error if the class is not mocked
|
|
131
|
+
*/
|
|
132
|
+
getMockInstance<T>(clazzOrName: InjectionToken<T>, ...params: any[]): T
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Remove a specific mock and restore the original class.
|
|
136
|
+
* This completely removes the mock - it does NOT clear mock call history.
|
|
137
|
+
*/
|
|
138
|
+
removeMock<T>(clazzOrName: InjectionToken<T>): void
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Remove all mocks and restore original classes.
|
|
142
|
+
* This completely removes all mocks - it does NOT clear mock call history.
|
|
143
|
+
*/
|
|
144
|
+
removeAllMocks(): void
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* @deprecated Use removeMock() instead. This will be removed in a future version.
|
|
148
|
+
* WARNING: This removes the mock, it does NOT clear mock call history.
|
|
85
149
|
*/
|
|
86
150
|
resetMock<T>(clazzOrName: InjectionToken<T>): void
|
|
87
151
|
|
|
88
152
|
/**
|
|
89
|
-
*
|
|
153
|
+
* @deprecated Use removeAllMocks() instead. This will be removed in a future version.
|
|
154
|
+
* WARNING: This removes all mocks, it does NOT clear mock call history.
|
|
90
155
|
*/
|
|
91
156
|
resetAllMocks(): void
|
|
92
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Reset singleton instances without removing registrations.
|
|
160
|
+
* Mock registrations are preserved by default.
|
|
161
|
+
*/
|
|
162
|
+
resetSingletons(options?: { preserveMocks?: boolean }): void
|
|
163
|
+
|
|
93
164
|
/**
|
|
94
165
|
* Clear all registered instances and mocks.
|
|
166
|
+
* @param options.preserveRegistrations If true, keeps all registrations but clears cached instances.
|
|
95
167
|
*/
|
|
96
|
-
clear(): void
|
|
168
|
+
clear(options?: { preserveRegistrations?: boolean }): void
|
|
97
169
|
}
|
|
98
170
|
|
|
99
171
|
/**
|
|
@@ -170,8 +242,33 @@ export declare function InjectLazy<T>(
|
|
|
170
242
|
|
|
171
243
|
/**
|
|
172
244
|
* Mark a class as a mock for another class.
|
|
245
|
+
* The mock class can implement only the methods you need (Partial<T>).
|
|
246
|
+
*
|
|
173
247
|
* @param mockedClazzOrName The class or name to mock
|
|
174
|
-
* @param proxy If true, unmocked methods delegate to the original
|
|
248
|
+
* @param proxy If true, unmocked methods delegate to the original implementation
|
|
249
|
+
*
|
|
250
|
+
* @example Basic mocking
|
|
251
|
+
* ```ts
|
|
252
|
+
* @Mock(UserService)
|
|
253
|
+
* class MockUserService {
|
|
254
|
+
* // Only implement methods you need to mock
|
|
255
|
+
* getUser() { return { id: 1, name: 'Test' } }
|
|
256
|
+
* }
|
|
257
|
+
* ```
|
|
258
|
+
*
|
|
259
|
+
* @example With hoisted mock functions (Vitest/Jest)
|
|
260
|
+
* ```ts
|
|
261
|
+
* const mockGetUser = vi.hoisted(() => vi.fn())
|
|
262
|
+
*
|
|
263
|
+
* @Mock(UserService)
|
|
264
|
+
* class MockUserService {
|
|
265
|
+
* getUser = mockGetUser
|
|
266
|
+
* }
|
|
267
|
+
*
|
|
268
|
+
* beforeEach(() => {
|
|
269
|
+
* mockGetUser.mockClear() // Clear call history, not removeMock()
|
|
270
|
+
* })
|
|
271
|
+
* ```
|
|
175
272
|
*/
|
|
176
273
|
export declare function Mock<T>(
|
|
177
274
|
mockedClazzOrName: InjectionToken<T>,
|
|
@@ -179,20 +276,46 @@ export declare function Mock<T>(
|
|
|
179
276
|
): ClassDecorator
|
|
180
277
|
|
|
181
278
|
/**
|
|
182
|
-
*
|
|
279
|
+
* Remove all mocks and restore original classes.
|
|
280
|
+
* This completely removes all mocks - it does NOT clear mock call history.
|
|
281
|
+
*/
|
|
282
|
+
export declare function removeAllMocks(): void
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Remove a specific mock and restore the original class.
|
|
286
|
+
* This completely removes the mock - it does NOT clear mock call history.
|
|
287
|
+
* @param clazzOrName The class or name to restore
|
|
288
|
+
*/
|
|
289
|
+
export declare function removeMock<T>(clazzOrName: InjectionToken<T>): void
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* @deprecated Use removeAllMocks() instead. This will be removed in a future version.
|
|
293
|
+
* WARNING: This removes all mocks, it does NOT clear mock call history.
|
|
183
294
|
*/
|
|
184
295
|
export declare function resetMocks(): void
|
|
185
296
|
|
|
186
297
|
/**
|
|
187
|
-
*
|
|
188
|
-
*
|
|
298
|
+
* @deprecated Use removeMock() instead. This will be removed in a future version.
|
|
299
|
+
* WARNING: This removes the mock, it does NOT clear mock call history.
|
|
300
|
+
* @param clazzOrName The class or name to restore
|
|
189
301
|
*/
|
|
190
302
|
export declare function resetMock<T>(clazzOrName: InjectionToken<T>): void
|
|
191
303
|
|
|
304
|
+
/**
|
|
305
|
+
* Reset singleton instances without removing registrations.
|
|
306
|
+
* Mock registrations are preserved by default.
|
|
307
|
+
*
|
|
308
|
+
* Ideal for test isolation where you want fresh instances but keep mocks.
|
|
309
|
+
*
|
|
310
|
+
* @param options.preserveMocks If true (default), keeps mock registrations
|
|
311
|
+
*/
|
|
312
|
+
export declare function resetSingletons(options?: { preserveMocks?: boolean }): void
|
|
313
|
+
|
|
192
314
|
/**
|
|
193
315
|
* Clear all registered instances and mocks from the container.
|
|
316
|
+
* @param options.preserveRegistrations If true, keeps all registrations but clears cached instances.
|
|
194
317
|
*/
|
|
195
|
-
export declare function clearContainer(): void
|
|
318
|
+
export declare function clearContainer(options?: { preserveRegistrations?: boolean }): void
|
|
196
319
|
|
|
197
320
|
/**
|
|
198
321
|
* Get the default container instance.
|
|
@@ -214,6 +337,43 @@ export declare function setDebug(enabled: boolean): void
|
|
|
214
337
|
*/
|
|
215
338
|
export declare function isRegistered<T>(clazzOrName: InjectionToken<T>): boolean
|
|
216
339
|
|
|
340
|
+
/**
|
|
341
|
+
* Check if a class or name has a mock registered.
|
|
342
|
+
* @param clazzOrName The class or name to check
|
|
343
|
+
* @returns true if mocked, false otherwise
|
|
344
|
+
*/
|
|
345
|
+
export declare function isMocked<T>(clazzOrName: InjectionToken<T>): boolean
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Get the mock instance for a mocked class.
|
|
349
|
+
* Useful for configuring mock behavior dynamically in tests.
|
|
350
|
+
*
|
|
351
|
+
* @param clazzOrName The original class or name that was mocked
|
|
352
|
+
* @param params Parameters to pass to the constructor
|
|
353
|
+
* @returns The mock instance
|
|
354
|
+
* @throws Error if the class is not mocked
|
|
355
|
+
*
|
|
356
|
+
* @example
|
|
357
|
+
* ```ts
|
|
358
|
+
* const mock = getMockInstance(UserService)
|
|
359
|
+
* mock.getUser.mockReturnValue({ id: 1 })
|
|
360
|
+
* ```
|
|
361
|
+
*/
|
|
362
|
+
export declare function getMockInstance<T>(clazzOrName: InjectionToken<T>, ...params: any[]): T
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Unregister a class or name from the container.
|
|
366
|
+
* @param clazzOrName The class or name to unregister
|
|
367
|
+
* @returns true if the registration was removed, false if it wasn't registered
|
|
368
|
+
*/
|
|
369
|
+
export declare function unregister<T>(clazzOrName: InjectionToken<T>): boolean
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* List all registrations in the container.
|
|
373
|
+
* Useful for debugging and introspection.
|
|
374
|
+
*/
|
|
375
|
+
export declare function listRegistrations(): RegistrationInfo[]
|
|
376
|
+
|
|
217
377
|
/**
|
|
218
378
|
* Validate that all provided injection tokens are registered.
|
|
219
379
|
* Throws an error with details about missing registrations.
|
package/index.js
CHANGED
|
@@ -28,10 +28,7 @@ function createLazyAccessor(cache, getValue, name) {
|
|
|
28
28
|
return undefined
|
|
29
29
|
},
|
|
30
30
|
get() {
|
|
31
|
-
|
|
32
|
-
cache.set(this, getValue())
|
|
33
|
-
}
|
|
34
|
-
return cache.get(this)
|
|
31
|
+
return cache.get(this) ?? (cache.set(this, getValue()), cache.get(this))
|
|
35
32
|
},
|
|
36
33
|
set() {
|
|
37
34
|
throw new Error(`Cannot assign value to injected accessor "${name}"`)
|
|
@@ -52,7 +49,7 @@ function createLazyAccessor(cache, getValue, name) {
|
|
|
52
49
|
* @throws {Error} If the target is not a class constructor
|
|
53
50
|
*/
|
|
54
51
|
export function Singleton(name) {
|
|
55
|
-
return
|
|
52
|
+
return (clazz, context) => {
|
|
56
53
|
if (context.kind !== 'class') {
|
|
57
54
|
throw new Error('Invalid injection target')
|
|
58
55
|
}
|
|
@@ -76,7 +73,7 @@ export function Singleton(name) {
|
|
|
76
73
|
* @throws {Error} If the target is not a class constructor
|
|
77
74
|
*/
|
|
78
75
|
export function Factory(name) {
|
|
79
|
-
return
|
|
76
|
+
return (clazz, context) => {
|
|
80
77
|
if (context.kind !== 'class') {
|
|
81
78
|
throw new Error('Invalid injection target')
|
|
82
79
|
}
|
|
@@ -108,14 +105,14 @@ export function Factory(name) {
|
|
|
108
105
|
* @throws {Error} If the injected field is assigned a value
|
|
109
106
|
*/
|
|
110
107
|
export function Inject(clazzOrName, ...params) {
|
|
111
|
-
return
|
|
108
|
+
return (_, context) => {
|
|
112
109
|
const getValue = () => {
|
|
113
110
|
const instanceContext = defaultContainer.getContext(clazzOrName)
|
|
114
111
|
return defaultContainer.getInstance(instanceContext, params)
|
|
115
112
|
}
|
|
116
113
|
|
|
117
114
|
if (context.kind === 'field') {
|
|
118
|
-
return
|
|
115
|
+
return (initialValue) => {
|
|
119
116
|
if (initialValue) {
|
|
120
117
|
throw new Error(`Cannot assign value to injected field "${context.name}"`)
|
|
121
118
|
}
|
|
@@ -172,7 +169,7 @@ export function InjectLazy(clazzOrName, ...params) {
|
|
|
172
169
|
// For private fields, we cannot use Object.defineProperty to create a lazy getter.
|
|
173
170
|
// Instead, we eagerly create the value. For true lazy behavior, use accessor syntax.
|
|
174
171
|
if (context.private) {
|
|
175
|
-
return
|
|
172
|
+
return (initialValue) => {
|
|
176
173
|
if (initialValue) {
|
|
177
174
|
throw new Error(`Cannot assign value to lazy-injected field "${context.name}"`)
|
|
178
175
|
}
|
|
@@ -208,19 +205,70 @@ export function InjectLazy(clazzOrName, ...params) {
|
|
|
208
205
|
}
|
|
209
206
|
|
|
210
207
|
/**
|
|
211
|
-
* Mark a class as a mock. This will replace the class with
|
|
208
|
+
* Mark a class as a mock. This will replace the original class with the mock when injected.
|
|
209
|
+
* The mock registration persists until explicitly removed with removeMock() or removeAllMocks().
|
|
212
210
|
*
|
|
213
211
|
* @param {string|Function} mockedClazzOrName The singleton or factory class or name to be mocked
|
|
214
212
|
* @param {boolean} [proxy=false] If true, the mock will proxy to the original class.
|
|
215
213
|
* Any methods not defined in the mock will be called on the original class.
|
|
216
214
|
* @returns {(function(Function, {kind: string}): void)}
|
|
217
|
-
*
|
|
218
|
-
* @example
|
|
215
|
+
*
|
|
216
|
+
* @example Basic mocking
|
|
217
|
+
* ```js
|
|
218
|
+
* @Mock(MySingleton)
|
|
219
|
+
* class MockedSingleton {
|
|
220
|
+
* doSomething() { return 'mocked result' }
|
|
221
|
+
* }
|
|
222
|
+
* ```
|
|
223
|
+
*
|
|
224
|
+
* @example Proxy mocking (partial mock)
|
|
225
|
+
* ```js
|
|
226
|
+
* // Only override specific methods, others delegate to original
|
|
227
|
+
* @Mock(MySingleton, true)
|
|
228
|
+
* class PartialMock {
|
|
229
|
+
* onlyThisMethod() { return 'mocked' }
|
|
230
|
+
* // All other methods call the original implementation
|
|
231
|
+
* }
|
|
232
|
+
* ```
|
|
233
|
+
*
|
|
234
|
+
* @example Testing pattern with hoisted mock functions
|
|
235
|
+
* ```js
|
|
236
|
+
* // Hoist mock functions for per-test configuration
|
|
237
|
+
* const mockMethod = vi.hoisted(() => vi.fn())
|
|
238
|
+
*
|
|
239
|
+
* @Mock(MyService)
|
|
240
|
+
* class MockMyService {
|
|
241
|
+
* doSomething = mockMethod
|
|
242
|
+
* }
|
|
243
|
+
*
|
|
244
|
+
* beforeEach(() => {
|
|
245
|
+
* // Clear call history - NOT removeMock() which removes the mock entirely
|
|
246
|
+
* mockMethod.mockClear()
|
|
247
|
+
* })
|
|
248
|
+
*
|
|
249
|
+
* it('should call the service', () => {
|
|
250
|
+
* mockMethod.mockReturnValue('test value')
|
|
251
|
+
* // ... your test
|
|
252
|
+
* expect(mockMethod).toHaveBeenCalled()
|
|
253
|
+
* })
|
|
254
|
+
* ```
|
|
255
|
+
*
|
|
256
|
+
* @example Cleanup in afterEach
|
|
257
|
+
* ```js
|
|
258
|
+
* afterEach(() => {
|
|
259
|
+
* // Option 1: Remove all mocks and restore originals
|
|
260
|
+
* removeAllMocks()
|
|
261
|
+
*
|
|
262
|
+
* // Option 2: Just clear singleton instances, keep mocks registered
|
|
263
|
+
* resetSingletons()
|
|
264
|
+
* })
|
|
265
|
+
* ```
|
|
266
|
+
*
|
|
219
267
|
* @throws {Error} If the injection target is not a class
|
|
220
268
|
* @throws {Error} If the injection source is not found
|
|
221
269
|
*/
|
|
222
270
|
export function Mock(mockedClazzOrName, proxy = false) {
|
|
223
|
-
return
|
|
271
|
+
return (clazz, context) => {
|
|
224
272
|
if (context.kind !== 'class') {
|
|
225
273
|
throw new Error('Invalid injection target')
|
|
226
274
|
}
|
|
@@ -229,27 +277,118 @@ export function Mock(mockedClazzOrName, proxy = false) {
|
|
|
229
277
|
}
|
|
230
278
|
|
|
231
279
|
/**
|
|
232
|
-
*
|
|
280
|
+
* Remove all mocks and restore original classes.
|
|
281
|
+
* This completely removes all mocks - it does NOT clear mock call history.
|
|
282
|
+
*
|
|
283
|
+
* If you want to clear call history on mock functions without removing the mock,
|
|
284
|
+
* call mockClear() on your mock functions instead.
|
|
285
|
+
*
|
|
286
|
+
* @example
|
|
287
|
+
* ```js
|
|
288
|
+
* afterEach(() => {
|
|
289
|
+
* removeAllMocks() // Restores original classes
|
|
290
|
+
* })
|
|
291
|
+
* ```
|
|
292
|
+
*/
|
|
293
|
+
export function removeAllMocks() {
|
|
294
|
+
defaultContainer.removeAllMocks()
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Remove a specific mock and restore the original class.
|
|
299
|
+
* This completely removes the mock - it does NOT clear mock call history.
|
|
300
|
+
*
|
|
301
|
+
* @param {string|Function} clazzOrName The singleton or factory class or name to restore
|
|
302
|
+
*
|
|
303
|
+
* @example
|
|
304
|
+
* ```js
|
|
305
|
+
* removeMock(UserService) // Restores original UserService
|
|
306
|
+
* ```
|
|
307
|
+
*/
|
|
308
|
+
export function removeMock(clazzOrName) {
|
|
309
|
+
defaultContainer.removeMock(clazzOrName)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* @deprecated Use removeAllMocks() instead. This will be removed in a future version.
|
|
314
|
+
*
|
|
315
|
+
* WARNING: Despite the name, this does NOT reset mock call history like mockClear().
|
|
316
|
+
* It completely removes all mocks and restores the original classes.
|
|
233
317
|
*/
|
|
234
318
|
export function resetMocks() {
|
|
235
|
-
|
|
319
|
+
console.warn(
|
|
320
|
+
'[DI] resetMocks() is deprecated. Use removeAllMocks() instead. ' +
|
|
321
|
+
'Note: This removes mocks entirely, NOT clearing call history.'
|
|
322
|
+
)
|
|
323
|
+
defaultContainer.removeAllMocks()
|
|
236
324
|
}
|
|
237
325
|
|
|
238
326
|
/**
|
|
239
|
-
*
|
|
327
|
+
* @deprecated Use removeMock() instead. This will be removed in a future version.
|
|
328
|
+
*
|
|
329
|
+
* WARNING: Despite the name, this does NOT reset mock call history like mockClear().
|
|
330
|
+
* It completely removes the mock and restores the original class.
|
|
240
331
|
*
|
|
241
|
-
* @param {string|Function} clazzOrName The singleton or factory class or name to
|
|
332
|
+
* @param {string|Function} clazzOrName The singleton or factory class or name to restore
|
|
242
333
|
*/
|
|
243
334
|
export function resetMock(clazzOrName) {
|
|
244
|
-
|
|
335
|
+
console.warn(
|
|
336
|
+
'[DI] resetMock() is deprecated. Use removeMock() instead. ' +
|
|
337
|
+
'Note: This removes the mock entirely, NOT clearing call history.'
|
|
338
|
+
)
|
|
339
|
+
defaultContainer.removeMock(clazzOrName)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Reset singleton instances without removing registrations.
|
|
344
|
+
* This clears cached singleton instances so they will be recreated on next resolve.
|
|
345
|
+
* Mock registrations are preserved by default.
|
|
346
|
+
*
|
|
347
|
+
* This is ideal for test isolation where you want:
|
|
348
|
+
* - Fresh singleton instances per test
|
|
349
|
+
* - Mock registrations to remain intact
|
|
350
|
+
*
|
|
351
|
+
* @param {Object} [options] Options for resetting
|
|
352
|
+
* @param {boolean} [options.preserveMocks=true] If true, keeps mock registrations.
|
|
353
|
+
* If false, also removes mocks.
|
|
354
|
+
*
|
|
355
|
+
* @example
|
|
356
|
+
* ```js
|
|
357
|
+
* beforeEach(() => {
|
|
358
|
+
* // Each test gets fresh singleton instances
|
|
359
|
+
* // but mocks remain registered
|
|
360
|
+
* resetSingletons()
|
|
361
|
+
* })
|
|
362
|
+
* ```
|
|
363
|
+
*/
|
|
364
|
+
export function resetSingletons(options) {
|
|
365
|
+
defaultContainer.resetSingletons(options)
|
|
245
366
|
}
|
|
246
367
|
|
|
247
368
|
/**
|
|
248
369
|
* Clear all registered instances and mocks from the container.
|
|
249
|
-
*
|
|
370
|
+
*
|
|
371
|
+
* By default, this removes ALL registrations including @Singleton and @Factory classes.
|
|
372
|
+
* For just clearing singleton instances without removing any registrations,
|
|
373
|
+
* use resetSingletons() instead.
|
|
374
|
+
*
|
|
375
|
+
* @param {Object} [options] Options for clearing
|
|
376
|
+
* @param {boolean} [options.preserveRegistrations=false] If true, keeps all registrations but clears cached instances.
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* ```js
|
|
380
|
+
* // Complete reset - removes everything
|
|
381
|
+
* clearContainer()
|
|
382
|
+
*
|
|
383
|
+
* // Clear cached instances but keep registrations (including mocks)
|
|
384
|
+
* clearContainer({ preserveRegistrations: true })
|
|
385
|
+
*
|
|
386
|
+
* // Just clear singleton instances (preferred for test isolation)
|
|
387
|
+
* resetSingletons()
|
|
388
|
+
* ```
|
|
250
389
|
*/
|
|
251
|
-
export function clearContainer() {
|
|
252
|
-
defaultContainer.clear()
|
|
390
|
+
export function clearContainer(options) {
|
|
391
|
+
defaultContainer.clear(options)
|
|
253
392
|
}
|
|
254
393
|
|
|
255
394
|
/**
|
|
@@ -340,6 +479,92 @@ export function resolve(clazzOrName, ...params) {
|
|
|
340
479
|
return defaultContainer.resolve(clazzOrName, ...params)
|
|
341
480
|
}
|
|
342
481
|
|
|
482
|
+
/**
|
|
483
|
+
* Get the mock instance for a mocked class.
|
|
484
|
+
* This is useful when you need to access or configure mock behavior dynamically in tests.
|
|
485
|
+
*
|
|
486
|
+
* Unlike resolve(), this explicitly checks that the class is mocked and provides
|
|
487
|
+
* better error messages if it's not.
|
|
488
|
+
*
|
|
489
|
+
* @template T
|
|
490
|
+
* @param {string|Function} clazzOrName The original class or name that was mocked
|
|
491
|
+
* @param {...*} params Parameters to pass to the constructor
|
|
492
|
+
* @returns {T} The mock instance
|
|
493
|
+
* @throws {Error} If the class is not registered
|
|
494
|
+
* @throws {Error} If the class is not mocked
|
|
495
|
+
*
|
|
496
|
+
* @example
|
|
497
|
+
* ```js
|
|
498
|
+
* @Mock(UserService)
|
|
499
|
+
* class MockUserService {
|
|
500
|
+
* getUser = vi.fn()
|
|
501
|
+
* }
|
|
502
|
+
*
|
|
503
|
+
* it('should get user', () => {
|
|
504
|
+
* const mock = getMockInstance(UserService)
|
|
505
|
+
* mock.getUser.mockReturnValue({ id: 1, name: 'Test' })
|
|
506
|
+
*
|
|
507
|
+
* // ... test code that uses UserService
|
|
508
|
+
*
|
|
509
|
+
* expect(mock.getUser).toHaveBeenCalledWith(1)
|
|
510
|
+
* })
|
|
511
|
+
* ```
|
|
512
|
+
*/
|
|
513
|
+
export function getMockInstance(clazzOrName, ...params) {
|
|
514
|
+
return defaultContainer.getMockInstance(clazzOrName, ...params)
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Check if a class or name has a mock registered.
|
|
519
|
+
* Useful for conditional test logic or debugging.
|
|
520
|
+
*
|
|
521
|
+
* @param {string|Function} clazzOrName The class or name to check
|
|
522
|
+
* @returns {boolean} true if a mock is registered, false otherwise
|
|
523
|
+
*
|
|
524
|
+
* @example
|
|
525
|
+
* ```js
|
|
526
|
+
* if (isMocked(UserService)) {
|
|
527
|
+
* console.log('UserService is mocked')
|
|
528
|
+
* }
|
|
529
|
+
* ```
|
|
530
|
+
*/
|
|
531
|
+
export function isMocked(clazzOrName) {
|
|
532
|
+
return defaultContainer.isMocked(clazzOrName)
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Unregister a class or name from the container.
|
|
537
|
+
* This removes the registration entirely, including any mock.
|
|
538
|
+
*
|
|
539
|
+
* @param {string|Function} clazzOrName The class or name to unregister
|
|
540
|
+
* @returns {boolean} true if the registration was removed, false if it wasn't registered
|
|
541
|
+
*
|
|
542
|
+
* @example
|
|
543
|
+
* ```js
|
|
544
|
+
* unregister(MyService) // Returns true if was registered
|
|
545
|
+
* ```
|
|
546
|
+
*/
|
|
547
|
+
export function unregister(clazzOrName) {
|
|
548
|
+
return defaultContainer.unregister(clazzOrName)
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* List all registrations in the container.
|
|
553
|
+
* Useful for debugging and introspection.
|
|
554
|
+
*
|
|
555
|
+
* @returns {Array<{key: string|Function, name: string, type: 'singleton'|'factory', isMocked: boolean, hasInstance: boolean}>}
|
|
556
|
+
*
|
|
557
|
+
* @example
|
|
558
|
+
* ```js
|
|
559
|
+
* listRegistrations().forEach(reg => {
|
|
560
|
+
* console.log(`${reg.name}: ${reg.type}, mocked: ${reg.isMocked}`)
|
|
561
|
+
* })
|
|
562
|
+
* ```
|
|
563
|
+
*/
|
|
564
|
+
export function listRegistrations() {
|
|
565
|
+
return defaultContainer.list()
|
|
566
|
+
}
|
|
567
|
+
|
|
343
568
|
// Export Container class for advanced use cases (e.g., isolated containers)
|
|
344
569
|
export {Container}
|
|
345
570
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "decorator-dependency-injection",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.7",
|
|
4
|
+
"description": "Lightweight dependency injection (DI) library using native TC39 Stage 3 decorators. Zero dependencies, built-in mocking, TypeScript support.",
|
|
5
5
|
"author": "Ravi Gairola <mallox@pyxzl.net>",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
@@ -16,12 +16,25 @@
|
|
|
16
16
|
"dependency-injection",
|
|
17
17
|
"di",
|
|
18
18
|
"decorators",
|
|
19
|
-
"
|
|
19
|
+
"tc39-decorators",
|
|
20
|
+
"stage-3-decorators",
|
|
21
|
+
"inject",
|
|
22
|
+
"injectable",
|
|
20
23
|
"singleton",
|
|
21
|
-
"factory"
|
|
24
|
+
"factory",
|
|
25
|
+
"ioc",
|
|
26
|
+
"inversion-of-control",
|
|
27
|
+
"container",
|
|
28
|
+
"mocking",
|
|
29
|
+
"mock",
|
|
30
|
+
"testing",
|
|
31
|
+
"jest",
|
|
32
|
+
"vitest",
|
|
33
|
+
"typescript",
|
|
34
|
+
"service-locator"
|
|
22
35
|
],
|
|
23
36
|
"engines": {
|
|
24
|
-
"node": ">=
|
|
37
|
+
"node": ">=20.0.0"
|
|
25
38
|
},
|
|
26
39
|
"main": "index.js",
|
|
27
40
|
"types": "index.d.ts",
|