decorator-dependency-injection 1.0.7 → 1.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.
- package/README.md +87 -19
- package/docs/FRAMEWORK_INTEGRATION.md +808 -0
- package/eslint.config.js +4 -1
- package/index.d.ts +6 -355
- package/index.js +46 -378
- package/package.json +6 -1
- package/src/Container.js +48 -201
- package/src/integrations/middleware.d.ts +40 -0
- package/src/integrations/middleware.js +171 -0
package/index.js
CHANGED
|
@@ -1,34 +1,21 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Decorator Dependency Injection
|
|
3
|
-
*
|
|
4
|
-
* A simple library for dependency injection using TC39 Stage 3 decorators.
|
|
5
|
-
*
|
|
6
|
-
* @module decorator-dependency-injection
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
1
|
import {Container} from './src/Container.js'
|
|
10
2
|
|
|
11
|
-
/** @type {Container}
|
|
3
|
+
/** @type {Container} */
|
|
12
4
|
const defaultContainer = new Container()
|
|
13
5
|
|
|
14
|
-
/**
|
|
15
|
-
* Creates a lazy accessor descriptor with WeakMap-based caching.
|
|
16
|
-
* @param {WeakMap} cache - WeakMap for per-instance caching
|
|
17
|
-
* @param {Function} getValue - Factory function to create the value
|
|
18
|
-
* @param {string} name - The accessor name for error messages
|
|
19
|
-
* @returns {{init: Function, get: Function, set: Function}} Accessor descriptor
|
|
20
|
-
* @private
|
|
21
|
-
*/
|
|
6
|
+
/** @private */
|
|
22
7
|
function createLazyAccessor(cache, getValue, name) {
|
|
23
8
|
return {
|
|
24
9
|
init(initialValue) {
|
|
25
|
-
if (initialValue) {
|
|
10
|
+
if (initialValue !== undefined) {
|
|
26
11
|
throw new Error(`Cannot assign value to injected accessor "${name}"`)
|
|
27
12
|
}
|
|
28
|
-
return undefined
|
|
29
13
|
},
|
|
30
14
|
get() {
|
|
31
|
-
|
|
15
|
+
if (!cache.has(this)) {
|
|
16
|
+
cache.set(this, getValue())
|
|
17
|
+
}
|
|
18
|
+
return cache.get(this)
|
|
32
19
|
},
|
|
33
20
|
set() {
|
|
34
21
|
throw new Error(`Cannot assign value to injected accessor "${name}"`)
|
|
@@ -36,18 +23,7 @@ function createLazyAccessor(cache, getValue, name) {
|
|
|
36
23
|
}
|
|
37
24
|
}
|
|
38
25
|
|
|
39
|
-
/**
|
|
40
|
-
* Register a class as a singleton. If a name is provided, it will be used as the key in the singleton map.
|
|
41
|
-
* Singleton instances only ever have one instance created via the @Inject decorator.
|
|
42
|
-
*
|
|
43
|
-
* @param {string} [name] The name of the singleton. If not provided, the class will be used as the key.
|
|
44
|
-
* @returns {(function(Function, {kind: string}): void)}
|
|
45
|
-
* @example @Singleton() class MySingleton {}
|
|
46
|
-
* @example @Singleton('customName') class MySingleton {}
|
|
47
|
-
* @throws {Error} If the injection target is not a class
|
|
48
|
-
* @throws {Error} If a singleton or factory with the same name is already defined
|
|
49
|
-
* @throws {Error} If the target is not a class constructor
|
|
50
|
-
*/
|
|
26
|
+
/** @param {string} [name] */
|
|
51
27
|
export function Singleton(name) {
|
|
52
28
|
return (clazz, context) => {
|
|
53
29
|
if (context.kind !== 'class') {
|
|
@@ -60,18 +36,7 @@ export function Singleton(name) {
|
|
|
60
36
|
}
|
|
61
37
|
}
|
|
62
38
|
|
|
63
|
-
/**
|
|
64
|
-
* Register a class as a factory. If a name is provided, it will be used as the key in the factory map.
|
|
65
|
-
* Factory instances are created via the @Inject decorator. Each call to the factory will create a new instance.
|
|
66
|
-
*
|
|
67
|
-
* @param {string} [name] The name of the factory. If not provided, the class will be used as the key.
|
|
68
|
-
* @returns {(function(Function, {kind: string}): void)}
|
|
69
|
-
* @example @Factory() class MyFactory {}
|
|
70
|
-
* @example @Factory('customName') class MyFactory {}
|
|
71
|
-
* @throws {Error} If the injection target is not a class
|
|
72
|
-
* @throws {Error} If a factory or singleton with the same name is already defined
|
|
73
|
-
* @throws {Error} If the target is not a class constructor
|
|
74
|
-
*/
|
|
39
|
+
/** @param {string} [name] */
|
|
75
40
|
export function Factory(name) {
|
|
76
41
|
return (clazz, context) => {
|
|
77
42
|
if (context.kind !== 'class') {
|
|
@@ -85,24 +50,8 @@ export function Factory(name) {
|
|
|
85
50
|
}
|
|
86
51
|
|
|
87
52
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* Supports:
|
|
92
|
-
* - Public fields: @Inject(MyClass) myField
|
|
93
|
-
* - Private fields: @Inject(MyClass) #myField
|
|
94
|
-
* - Accessors: @Inject(MyClass) accessor myField
|
|
95
|
-
* - Private accessors: @Inject(MyClass) accessor #myField
|
|
96
|
-
*
|
|
97
|
-
* @param {string|Function} clazzOrName The singleton or factory class or name
|
|
98
|
-
* @param {...*} params Parameters to pass to the constructor. Recommended to use only with factories.
|
|
99
|
-
* @returns {(function(*, {kind: string, name: string}): function(): Object)}
|
|
100
|
-
* @example @Inject(MySingleton) mySingleton
|
|
101
|
-
* @example @Inject("myCustomName") myFactory
|
|
102
|
-
* @example @Inject(MyService) #privateService
|
|
103
|
-
* @example @Inject(MyService) accessor myService
|
|
104
|
-
* @throws {Error} If the injection target is not a field or accessor
|
|
105
|
-
* @throws {Error} If the injected field is assigned a value
|
|
53
|
+
* @param {string|Function} clazzOrName
|
|
54
|
+
* @param {...*} params
|
|
106
55
|
*/
|
|
107
56
|
export function Inject(clazzOrName, ...params) {
|
|
108
57
|
return (_, context) => {
|
|
@@ -113,7 +62,7 @@ export function Inject(clazzOrName, ...params) {
|
|
|
113
62
|
|
|
114
63
|
if (context.kind === 'field') {
|
|
115
64
|
return (initialValue) => {
|
|
116
|
-
if (initialValue) {
|
|
65
|
+
if (initialValue !== undefined) {
|
|
117
66
|
throw new Error(`Cannot assign value to injected field "${context.name}"`)
|
|
118
67
|
}
|
|
119
68
|
return getValue()
|
|
@@ -130,31 +79,9 @@ export function Inject(clazzOrName, ...params) {
|
|
|
130
79
|
}
|
|
131
80
|
|
|
132
81
|
/**
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* The lazy injection defers instantiation until the field is first accessed. This is useful for:
|
|
137
|
-
* - Breaking circular dependencies
|
|
138
|
-
* - Deferring expensive initializations
|
|
139
|
-
*
|
|
140
|
-
* Supports:
|
|
141
|
-
* - Public fields: @InjectLazy(MyClass) myField
|
|
142
|
-
* - Private fields: @InjectLazy(MyClass) #myField
|
|
143
|
-
* - Accessors: @InjectLazy(MyClass) accessor myField
|
|
144
|
-
* - Private accessors: @InjectLazy(MyClass) accessor #myField
|
|
145
|
-
*
|
|
146
|
-
* Note: For private fields, the lazy behavior is achieved through the field initializer
|
|
147
|
-
* returning a getter-based proxy. For accessors, it's achieved through the accessor's
|
|
148
|
-
* get/set methods directly.
|
|
149
|
-
*
|
|
150
|
-
* @param {string|Function} clazzOrName The singleton or factory class or name
|
|
151
|
-
* @param {...*} params Parameters to pass to the constructor. Recommended to use only with factories.
|
|
152
|
-
* @returns {(function(*, {kind: string, name: string, addInitializer: Function}): void)}
|
|
153
|
-
* @example @InjectLazy(MySingleton) mySingleton
|
|
154
|
-
* @example @InjectLazy("myCustomName") myFactory
|
|
155
|
-
* @example @InjectLazy(MyService) #privateService
|
|
156
|
-
* @throws {Error} If the injection target is not a field or accessor
|
|
157
|
-
* @throws {Error} If the injected field is assigned a value
|
|
82
|
+
* Defers instantiation until first access. For private fields, use accessor syntax for true lazy behavior.
|
|
83
|
+
* @param {string|Function} clazzOrName
|
|
84
|
+
* @param {...*} params
|
|
158
85
|
*/
|
|
159
86
|
export function InjectLazy(clazzOrName, ...params) {
|
|
160
87
|
const cache = new WeakMap()
|
|
@@ -170,7 +97,7 @@ export function InjectLazy(clazzOrName, ...params) {
|
|
|
170
97
|
// Instead, we eagerly create the value. For true lazy behavior, use accessor syntax.
|
|
171
98
|
if (context.private) {
|
|
172
99
|
return (initialValue) => {
|
|
173
|
-
if (initialValue) {
|
|
100
|
+
if (initialValue !== undefined) {
|
|
174
101
|
throw new Error(`Cannot assign value to lazy-injected field "${context.name}"`)
|
|
175
102
|
}
|
|
176
103
|
return getValue()
|
|
@@ -205,67 +132,8 @@ export function InjectLazy(clazzOrName, ...params) {
|
|
|
205
132
|
}
|
|
206
133
|
|
|
207
134
|
/**
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
* @param {string|Function} mockedClazzOrName The singleton or factory class or name to be mocked
|
|
212
|
-
* @param {boolean} [proxy=false] If true, the mock will proxy to the original class.
|
|
213
|
-
* Any methods not defined in the mock will be called on the original class.
|
|
214
|
-
* @returns {(function(Function, {kind: string}): void)}
|
|
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
|
-
*
|
|
267
|
-
* @throws {Error} If the injection target is not a class
|
|
268
|
-
* @throws {Error} If the injection source is not found
|
|
135
|
+
* @param {string|Function} mockedClazzOrName
|
|
136
|
+
* @param {boolean} [proxy=false] If true, unmocked methods delegate to the original
|
|
269
137
|
*/
|
|
270
138
|
export function Mock(mockedClazzOrName, proxy = false) {
|
|
271
139
|
return (clazz, context) => {
|
|
@@ -276,297 +144,97 @@ export function Mock(mockedClazzOrName, proxy = false) {
|
|
|
276
144
|
}
|
|
277
145
|
}
|
|
278
146
|
|
|
279
|
-
/**
|
|
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
|
-
*/
|
|
147
|
+
/** Remove all mocks and restore originals. Does NOT clear mock call history. */
|
|
293
148
|
export function removeAllMocks() {
|
|
294
149
|
defaultContainer.removeAllMocks()
|
|
295
150
|
}
|
|
296
151
|
|
|
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
|
-
*/
|
|
152
|
+
/** @param {string|Function} clazzOrName */
|
|
308
153
|
export function removeMock(clazzOrName) {
|
|
309
154
|
defaultContainer.removeMock(clazzOrName)
|
|
310
155
|
}
|
|
311
156
|
|
|
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.
|
|
317
|
-
*/
|
|
318
|
-
export function resetMocks() {
|
|
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()
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
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.
|
|
331
|
-
*
|
|
332
|
-
* @param {string|Function} clazzOrName The singleton or factory class or name to restore
|
|
333
|
-
*/
|
|
334
|
-
export function resetMock(clazzOrName) {
|
|
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
|
-
*/
|
|
157
|
+
/** @param {{preserveMocks?: boolean}} [options] */
|
|
364
158
|
export function resetSingletons(options) {
|
|
365
159
|
defaultContainer.resetSingletons(options)
|
|
366
160
|
}
|
|
367
161
|
|
|
368
|
-
/**
|
|
369
|
-
* Clear all registered instances and mocks from the container.
|
|
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
|
-
* ```
|
|
389
|
-
*/
|
|
162
|
+
/** @param {{preserveRegistrations?: boolean}} [options] */
|
|
390
163
|
export function clearContainer(options) {
|
|
391
164
|
defaultContainer.clear(options)
|
|
392
165
|
}
|
|
393
166
|
|
|
394
|
-
/**
|
|
395
|
-
* Get the default container instance.
|
|
396
|
-
* Useful for advanced use cases or testing the container itself.
|
|
397
|
-
*
|
|
398
|
-
* @returns {Container} The default container
|
|
399
|
-
*/
|
|
167
|
+
/** @returns {Container} */
|
|
400
168
|
export function getContainer() {
|
|
401
169
|
return defaultContainer
|
|
402
170
|
}
|
|
403
171
|
|
|
404
|
-
/**
|
|
405
|
-
* Enable or disable debug logging for dependency injection.
|
|
406
|
-
* When enabled, logs when instances are registered, created, and mocked.
|
|
407
|
-
*
|
|
408
|
-
* @param {boolean} enabled Whether to enable debug mode
|
|
409
|
-
* @example
|
|
410
|
-
* setDebug(true)
|
|
411
|
-
* // [DI] Registered singleton: UserService
|
|
412
|
-
* // [DI] Creating singleton: UserService
|
|
413
|
-
*/
|
|
172
|
+
/** @param {boolean} enabled */
|
|
414
173
|
export function setDebug(enabled) {
|
|
415
174
|
defaultContainer.setDebug(enabled)
|
|
416
175
|
}
|
|
417
176
|
|
|
418
177
|
/**
|
|
419
|
-
*
|
|
420
|
-
*
|
|
421
|
-
*
|
|
422
|
-
* @param {string|Function} clazzOrName The class or name to check
|
|
423
|
-
* @returns {boolean} true if registered, false otherwise
|
|
424
|
-
* @example
|
|
425
|
-
* if (!isRegistered(MyService)) {
|
|
426
|
-
* console.warn('MyService not registered!')
|
|
427
|
-
* }
|
|
178
|
+
* @param {string|Function} clazzOrName
|
|
179
|
+
* @returns {boolean}
|
|
428
180
|
*/
|
|
429
181
|
export function isRegistered(clazzOrName) {
|
|
430
182
|
return defaultContainer.has(clazzOrName)
|
|
431
183
|
}
|
|
432
184
|
|
|
433
|
-
/**
|
|
434
|
-
* Validate that all provided injection tokens are registered.
|
|
435
|
-
* Throws an error with details about missing registrations.
|
|
436
|
-
* Useful for fail-fast validation at application startup.
|
|
437
|
-
*
|
|
438
|
-
* @param {...(string|Function)} tokens Classes or names to validate
|
|
439
|
-
* @throws {Error} If any token is not registered
|
|
440
|
-
* @example
|
|
441
|
-
* // At app startup:
|
|
442
|
-
* validateRegistrations(UserService, AuthService, 'databaseConnection')
|
|
443
|
-
*/
|
|
185
|
+
/** @param {...(string|Function)} tokens */
|
|
444
186
|
export function validateRegistrations(...tokens) {
|
|
445
187
|
const missing = tokens.filter(token => !defaultContainer.has(token))
|
|
446
|
-
if (missing.length
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
)
|
|
452
|
-
|
|
188
|
+
if (missing.length === 0) return
|
|
189
|
+
|
|
190
|
+
const names = missing.map(t => t?.name ?? t).join(', ')
|
|
191
|
+
throw new Error(
|
|
192
|
+
`Missing registrations: [${names}]. ` +
|
|
193
|
+
`Ensure these classes are decorated with @Singleton() or @Factory() before use.`
|
|
194
|
+
)
|
|
453
195
|
}
|
|
454
196
|
|
|
455
197
|
/**
|
|
456
|
-
* Resolve and return an instance by class or name.
|
|
457
|
-
* This allows non-decorator code (plain functions, modules, etc.) to retrieve
|
|
458
|
-
* instances from the DI container.
|
|
459
|
-
*
|
|
460
198
|
* @template T
|
|
461
|
-
* @param {string|Function} clazzOrName
|
|
462
|
-
* @param {...*} params
|
|
463
|
-
* @returns {T}
|
|
464
|
-
* @throws {Error} If the class or name is not registered
|
|
465
|
-
* @example
|
|
466
|
-
* // In a plain function:
|
|
467
|
-
* function handleRequest(req) {
|
|
468
|
-
* const userService = resolve(UserService)
|
|
469
|
-
* return userService.getUser(req.userId)
|
|
470
|
-
* }
|
|
471
|
-
* @example
|
|
472
|
-
* // With a named registration:
|
|
473
|
-
* const db = resolve('database')
|
|
474
|
-
* @example
|
|
475
|
-
* // With factory parameters:
|
|
476
|
-
* const logger = resolve(Logger, 'my-module')
|
|
199
|
+
* @param {string|Function} clazzOrName
|
|
200
|
+
* @param {...*} params
|
|
201
|
+
* @returns {T}
|
|
477
202
|
*/
|
|
478
203
|
export function resolve(clazzOrName, ...params) {
|
|
479
204
|
return defaultContainer.resolve(clazzOrName, ...params)
|
|
480
205
|
}
|
|
481
206
|
|
|
482
207
|
/**
|
|
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
208
|
* @template T
|
|
490
|
-
* @param {string|Function} clazzOrName
|
|
491
|
-
* @param {...*} params
|
|
492
|
-
* @returns {T}
|
|
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
|
-
* ```
|
|
209
|
+
* @param {string|Function} clazzOrName
|
|
210
|
+
* @param {...*} params
|
|
211
|
+
* @returns {T}
|
|
512
212
|
*/
|
|
513
213
|
export function getMockInstance(clazzOrName, ...params) {
|
|
514
214
|
return defaultContainer.getMockInstance(clazzOrName, ...params)
|
|
515
215
|
}
|
|
516
216
|
|
|
517
217
|
/**
|
|
518
|
-
*
|
|
519
|
-
*
|
|
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
|
-
* ```
|
|
218
|
+
* @param {string|Function} clazzOrName
|
|
219
|
+
* @returns {boolean}
|
|
530
220
|
*/
|
|
531
221
|
export function isMocked(clazzOrName) {
|
|
532
222
|
return defaultContainer.isMocked(clazzOrName)
|
|
533
223
|
}
|
|
534
224
|
|
|
535
225
|
/**
|
|
536
|
-
*
|
|
537
|
-
*
|
|
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
|
-
* ```
|
|
226
|
+
* @param {string|Function} clazzOrName
|
|
227
|
+
* @returns {boolean}
|
|
546
228
|
*/
|
|
547
229
|
export function unregister(clazzOrName) {
|
|
548
230
|
return defaultContainer.unregister(clazzOrName)
|
|
549
231
|
}
|
|
550
232
|
|
|
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
|
-
*/
|
|
233
|
+
/** @returns {Array<{key: string|Function, name: string, type: 'singleton'|'factory', isMocked: boolean, hasInstance: boolean}>} */
|
|
564
234
|
export function listRegistrations() {
|
|
565
235
|
return defaultContainer.list()
|
|
566
236
|
}
|
|
567
237
|
|
|
568
|
-
// Export Container class for advanced use cases (e.g., isolated containers)
|
|
569
238
|
export {Container}
|
|
570
|
-
|
|
571
|
-
// Export createProxy for advanced proxy use cases
|
|
239
|
+
export {defaultContainer}
|
|
572
240
|
export {createProxy} from './src/proxy.js'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "decorator-dependency-injection",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
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",
|
|
@@ -39,10 +39,15 @@
|
|
|
39
39
|
"main": "index.js",
|
|
40
40
|
"types": "index.d.ts",
|
|
41
41
|
"type": "module",
|
|
42
|
+
"sideEffects": false,
|
|
42
43
|
"exports": {
|
|
43
44
|
".": {
|
|
44
45
|
"types": "./index.d.ts",
|
|
45
46
|
"import": "./index.js"
|
|
47
|
+
},
|
|
48
|
+
"./middleware": {
|
|
49
|
+
"types": "./src/integrations/middleware.d.ts",
|
|
50
|
+
"import": "./src/integrations/middleware.js"
|
|
46
51
|
}
|
|
47
52
|
},
|
|
48
53
|
"scripts": {
|