decorator-dependency-injection 1.0.3 → 1.0.5
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 +307 -16
- package/eslint.config.js +77 -0
- package/index.d.ts +227 -0
- package/index.js +207 -143
- package/package.json +46 -7
- package/src/Container.js +211 -0
- package/src/proxy.js +42 -0
- package/.github/workflows/release.yml +0 -129
- package/babel.config.json +0 -6
- package/test/injection.test.js +0 -309
- package/test/injectionLazy.test.js +0 -249
- package/test/mock.test.js +0 -295
- package/test/proxy.test.js +0 -130
package/index.js
CHANGED
|
@@ -1,22 +1,50 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* @
|
|
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
|
-
|
|
12
|
-
|
|
9
|
+
import {Container} from './src/Container.js'
|
|
10
|
+
|
|
11
|
+
/** @type {Container} The default global container */
|
|
12
|
+
const defaultContainer = new Container()
|
|
13
|
+
|
|
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
|
+
*/
|
|
22
|
+
function createLazyAccessor(cache, getValue, name) {
|
|
23
|
+
return {
|
|
24
|
+
init(initialValue) {
|
|
25
|
+
if (initialValue) {
|
|
26
|
+
throw new Error(`Cannot assign value to injected accessor "${name}"`)
|
|
27
|
+
}
|
|
28
|
+
return undefined
|
|
29
|
+
},
|
|
30
|
+
get() {
|
|
31
|
+
if (!cache.has(this)) {
|
|
32
|
+
cache.set(this, getValue())
|
|
33
|
+
}
|
|
34
|
+
return cache.get(this)
|
|
35
|
+
},
|
|
36
|
+
set() {
|
|
37
|
+
throw new Error(`Cannot assign value to injected accessor "${name}"`)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
13
41
|
|
|
14
42
|
/**
|
|
15
43
|
* Register a class as a singleton. If a name is provided, it will be used as the key in the singleton map.
|
|
16
44
|
* Singleton instances only ever have one instance created via the @Inject decorator.
|
|
17
45
|
*
|
|
18
46
|
* @param {string} [name] The name of the singleton. If not provided, the class will be used as the key.
|
|
19
|
-
* @
|
|
47
|
+
* @returns {(function(Function, {kind: string}): void)}
|
|
20
48
|
* @example @Singleton() class MySingleton {}
|
|
21
49
|
* @example @Singleton('customName') class MySingleton {}
|
|
22
50
|
* @throws {Error} If the injection target is not a class
|
|
@@ -31,11 +59,7 @@ export function Singleton(name) {
|
|
|
31
59
|
if (typeof clazz !== 'function' || !clazz.prototype) {
|
|
32
60
|
throw new Error('Target must be a class constructor')
|
|
33
61
|
}
|
|
34
|
-
|
|
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'})
|
|
62
|
+
defaultContainer.registerSingleton(clazz, name)
|
|
39
63
|
}
|
|
40
64
|
}
|
|
41
65
|
|
|
@@ -44,7 +68,7 @@ export function Singleton(name) {
|
|
|
44
68
|
* Factory instances are created via the @Inject decorator. Each call to the factory will create a new instance.
|
|
45
69
|
*
|
|
46
70
|
* @param {string} [name] The name of the factory. If not provided, the class will be used as the key.
|
|
47
|
-
* @
|
|
71
|
+
* @returns {(function(Function, {kind: string}): void)}
|
|
48
72
|
* @example @Factory() class MyFactory {}
|
|
49
73
|
* @example @Factory('customName') class MyFactory {}
|
|
50
74
|
* @throws {Error} If the injection target is not a class
|
|
@@ -59,11 +83,7 @@ export function Factory(name) {
|
|
|
59
83
|
if (typeof clazz !== 'function' || !clazz.prototype) {
|
|
60
84
|
throw new Error('Target must be a class constructor')
|
|
61
85
|
}
|
|
62
|
-
|
|
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'})
|
|
86
|
+
defaultContainer.registerFactory(clazz, name)
|
|
67
87
|
}
|
|
68
88
|
}
|
|
69
89
|
|
|
@@ -71,87 +91,131 @@ export function Factory(name) {
|
|
|
71
91
|
* Inject a singleton or factory instance into a class field. You can also provide parameters to the constructor.
|
|
72
92
|
* If the instance is a singleton, it will only be created once with the first set of parameters it encounters.
|
|
73
93
|
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
94
|
+
* Supports:
|
|
95
|
+
* - Public fields: @Inject(MyClass) myField
|
|
96
|
+
* - Private fields: @Inject(MyClass) #myField
|
|
97
|
+
* - Accessors: @Inject(MyClass) accessor myField
|
|
98
|
+
* - Private accessors: @Inject(MyClass) accessor #myField
|
|
99
|
+
*
|
|
100
|
+
* @param {string|Function} clazzOrName The singleton or factory class or name
|
|
101
|
+
* @param {...*} params Parameters to pass to the constructor. Recommended to use only with factories.
|
|
102
|
+
* @returns {(function(*, {kind: string, name: string}): function(): Object)}
|
|
77
103
|
* @example @Inject(MySingleton) mySingleton
|
|
78
104
|
* @example @Inject("myCustomName") myFactory
|
|
79
|
-
* @
|
|
105
|
+
* @example @Inject(MyService) #privateService
|
|
106
|
+
* @example @Inject(MyService) accessor myService
|
|
107
|
+
* @throws {Error} If the injection target is not a field or accessor
|
|
80
108
|
* @throws {Error} If the injected field is assigned a value
|
|
81
109
|
*/
|
|
82
110
|
export function Inject(clazzOrName, ...params) {
|
|
83
|
-
return function (
|
|
84
|
-
|
|
85
|
-
|
|
111
|
+
return function (_, context) {
|
|
112
|
+
const getValue = () => {
|
|
113
|
+
const instanceContext = defaultContainer.getContext(clazzOrName)
|
|
114
|
+
return defaultContainer.getInstance(instanceContext, params)
|
|
86
115
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
116
|
+
|
|
117
|
+
if (context.kind === 'field') {
|
|
118
|
+
return function (initialValue) {
|
|
119
|
+
if (initialValue) {
|
|
120
|
+
throw new Error(`Cannot assign value to injected field "${context.name}"`)
|
|
121
|
+
}
|
|
122
|
+
return getValue()
|
|
90
123
|
}
|
|
91
|
-
const instanceContext = getContext(clazzOrName)
|
|
92
|
-
return getInjectedInstance(instanceContext, params)
|
|
93
124
|
}
|
|
125
|
+
|
|
126
|
+
if (context.kind === 'accessor') {
|
|
127
|
+
const cache = new WeakMap()
|
|
128
|
+
return createLazyAccessor(cache, getValue, context.name)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
throw new Error('Invalid injection target: @Inject can only be used on fields or accessors')
|
|
94
132
|
}
|
|
95
133
|
}
|
|
96
134
|
|
|
97
135
|
/**
|
|
98
136
|
* Inject a singleton or factory instance lazily into a class field. You can also provide parameters to the constructor.
|
|
99
137
|
* If the instance is a singleton, it will only be created once with the first set of parameters it encounters.
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
138
|
+
*
|
|
139
|
+
* The lazy injection defers instantiation until the field is first accessed. This is useful for:
|
|
140
|
+
* - Breaking circular dependencies
|
|
141
|
+
* - Deferring expensive initializations
|
|
142
|
+
*
|
|
143
|
+
* Supports:
|
|
144
|
+
* - Public fields: @InjectLazy(MyClass) myField
|
|
145
|
+
* - Private fields: @InjectLazy(MyClass) #myField
|
|
146
|
+
* - Accessors: @InjectLazy(MyClass) accessor myField
|
|
147
|
+
* - Private accessors: @InjectLazy(MyClass) accessor #myField
|
|
148
|
+
*
|
|
149
|
+
* Note: For private fields, the lazy behavior is achieved through the field initializer
|
|
150
|
+
* returning a getter-based proxy. For accessors, it's achieved through the accessor's
|
|
151
|
+
* get/set methods directly.
|
|
152
|
+
*
|
|
153
|
+
* @param {string|Function} clazzOrName The singleton or factory class or name
|
|
154
|
+
* @param {...*} params Parameters to pass to the constructor. Recommended to use only with factories.
|
|
155
|
+
* @returns {(function(*, {kind: string, name: string, addInitializer: Function}): void)}
|
|
103
156
|
* @example @InjectLazy(MySingleton) mySingleton
|
|
104
157
|
* @example @InjectLazy("myCustomName") myFactory
|
|
105
|
-
* @
|
|
158
|
+
* @example @InjectLazy(MyService) #privateService
|
|
159
|
+
* @throws {Error} If the injection target is not a field or accessor
|
|
106
160
|
* @throws {Error} If the injected field is assigned a value
|
|
107
161
|
*/
|
|
108
162
|
export function InjectLazy(clazzOrName, ...params) {
|
|
109
163
|
const cache = new WeakMap()
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
context.addInitializer(function () {
|
|
115
|
-
Object.defineProperty(this, context.name, {
|
|
116
|
-
get() {
|
|
117
|
-
if (!cache.has(this)) {
|
|
118
|
-
const instanceContext = getContext(clazzOrName)
|
|
119
|
-
const value = getInjectedInstance(instanceContext, params)
|
|
120
|
-
cache.set(this, value)
|
|
121
|
-
}
|
|
122
|
-
return cache.get(this)
|
|
123
|
-
},
|
|
124
|
-
configurable: true,
|
|
125
|
-
enumerable: true
|
|
126
|
-
})
|
|
127
|
-
})
|
|
164
|
+
|
|
165
|
+
const getValue = () => {
|
|
166
|
+
const instanceContext = defaultContainer.getContext(clazzOrName)
|
|
167
|
+
return defaultContainer.getInstance(instanceContext, params)
|
|
128
168
|
}
|
|
129
|
-
}
|
|
130
169
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
170
|
+
return (_, context) => {
|
|
171
|
+
if (context.kind === 'field') {
|
|
172
|
+
// For private fields, we cannot use Object.defineProperty to create a lazy getter.
|
|
173
|
+
// Instead, we eagerly create the value. For true lazy behavior, use accessor syntax.
|
|
174
|
+
if (context.private) {
|
|
175
|
+
return function (initialValue) {
|
|
176
|
+
if (initialValue) {
|
|
177
|
+
throw new Error(`Cannot assign value to lazy-injected field "${context.name}"`)
|
|
178
|
+
}
|
|
179
|
+
return getValue()
|
|
180
|
+
}
|
|
142
181
|
}
|
|
143
|
-
|
|
182
|
+
|
|
183
|
+
// For public fields, use Object.defineProperty for true lazy behavior
|
|
184
|
+
context.addInitializer(function () {
|
|
185
|
+
Object.defineProperty(this, context.name, {
|
|
186
|
+
get() {
|
|
187
|
+
if (!cache.has(this)) {
|
|
188
|
+
cache.set(this, getValue())
|
|
189
|
+
}
|
|
190
|
+
return cache.get(this)
|
|
191
|
+
},
|
|
192
|
+
set() {
|
|
193
|
+
throw new Error(`Cannot assign value to lazy-injected field "${context.name}"`)
|
|
194
|
+
},
|
|
195
|
+
configurable: true,
|
|
196
|
+
enumerable: true
|
|
197
|
+
})
|
|
198
|
+
})
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (context.kind === 'accessor') {
|
|
203
|
+
return createLazyAccessor(cache, getValue, context.name)
|
|
144
204
|
}
|
|
145
|
-
|
|
205
|
+
|
|
206
|
+
throw new Error('Invalid injection target: @InjectLazy can only be used on fields or accessors')
|
|
207
|
+
}
|
|
146
208
|
}
|
|
147
209
|
|
|
148
210
|
/**
|
|
149
211
|
* Mark a class as a mock. This will replace the class with a mock instance when injected.
|
|
150
|
-
*
|
|
151
|
-
* @param {
|
|
152
|
-
* @
|
|
212
|
+
*
|
|
213
|
+
* @param {string|Function} mockedClazzOrName The singleton or factory class or name to be mocked
|
|
214
|
+
* @param {boolean} [proxy=false] If true, the mock will proxy to the original class.
|
|
215
|
+
* Any methods not defined in the mock will be called on the original class.
|
|
216
|
+
* @returns {(function(Function, {kind: string}): void)}
|
|
153
217
|
* @example @Mock(MySingleton) class MyMock {}
|
|
154
|
-
* @example @Mock("myCustomName") class MyMock {}
|
|
218
|
+
* @example @Mock("myCustomName", true) class MyMock {}
|
|
155
219
|
* @throws {Error} If the injection target is not a class
|
|
156
220
|
* @throws {Error} If the injection source is not found
|
|
157
221
|
*/
|
|
@@ -160,97 +224,97 @@ export function Mock(mockedClazzOrName, proxy = false) {
|
|
|
160
224
|
if (context.kind !== 'class') {
|
|
161
225
|
throw new Error('Invalid injection target')
|
|
162
226
|
}
|
|
163
|
-
|
|
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
|
|
227
|
+
defaultContainer.registerMock(mockedClazzOrName, clazz, proxy)
|
|
170
228
|
}
|
|
171
229
|
}
|
|
172
230
|
|
|
173
231
|
/**
|
|
174
|
-
*
|
|
232
|
+
* Reset all mocks to their original classes.
|
|
233
|
+
*/
|
|
234
|
+
export function resetMocks() {
|
|
235
|
+
defaultContainer.resetAllMocks()
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Reset a specific mock to its original class.
|
|
175
240
|
*
|
|
176
|
-
* @param {string|
|
|
177
|
-
* @returns {InstanceContext}
|
|
178
|
-
* @throws {Error} If the context is not found.
|
|
241
|
+
* @param {string|Function} clazzOrName The singleton or factory class or name to reset
|
|
179
242
|
*/
|
|
180
|
-
function
|
|
181
|
-
|
|
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
|
-
)
|
|
189
|
-
}
|
|
243
|
+
export function resetMock(clazzOrName) {
|
|
244
|
+
defaultContainer.resetMock(clazzOrName)
|
|
190
245
|
}
|
|
191
246
|
|
|
192
247
|
/**
|
|
193
|
-
*
|
|
248
|
+
* Clear all registered instances and mocks from the container.
|
|
249
|
+
* Useful for complete test isolation between test suites.
|
|
194
250
|
*/
|
|
195
|
-
export function
|
|
196
|
-
|
|
197
|
-
restoreOriginal(instanceContext)
|
|
198
|
-
}
|
|
251
|
+
export function clearContainer() {
|
|
252
|
+
defaultContainer.clear()
|
|
199
253
|
}
|
|
200
254
|
|
|
201
255
|
/**
|
|
202
|
-
*
|
|
203
|
-
*
|
|
256
|
+
* Get the default container instance.
|
|
257
|
+
* Useful for advanced use cases or testing the container itself.
|
|
258
|
+
*
|
|
259
|
+
* @returns {Container} The default container
|
|
204
260
|
*/
|
|
205
|
-
export function
|
|
206
|
-
|
|
261
|
+
export function getContainer() {
|
|
262
|
+
return defaultContainer
|
|
207
263
|
}
|
|
208
264
|
|
|
209
265
|
/**
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
266
|
+
* Enable or disable debug logging for dependency injection.
|
|
267
|
+
* When enabled, logs when instances are registered, created, and mocked.
|
|
268
|
+
*
|
|
269
|
+
* @param {boolean} enabled Whether to enable debug mode
|
|
270
|
+
* @example
|
|
271
|
+
* setDebug(true)
|
|
272
|
+
* // [DI] Registered singleton: UserService
|
|
273
|
+
* // [DI] Creating singleton: UserService
|
|
213
274
|
*/
|
|
214
|
-
function
|
|
215
|
-
|
|
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
|
-
}
|
|
275
|
+
export function setDebug(enabled) {
|
|
276
|
+
defaultContainer.setDebug(enabled)
|
|
224
277
|
}
|
|
225
278
|
|
|
226
279
|
/**
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
* @
|
|
280
|
+
* Check if a class or name is registered in the default container.
|
|
281
|
+
* Useful for validation before injection.
|
|
282
|
+
*
|
|
283
|
+
* @param {string|Function} clazzOrName The class or name to check
|
|
284
|
+
* @returns {boolean} true if registered, false otherwise
|
|
285
|
+
* @example
|
|
286
|
+
* if (!isRegistered(MyService)) {
|
|
287
|
+
* console.warn('MyService not registered!')
|
|
288
|
+
* }
|
|
231
289
|
*/
|
|
232
|
-
function
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
290
|
+
export function isRegistered(clazzOrName) {
|
|
291
|
+
return defaultContainer.has(clazzOrName)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Validate that all provided injection tokens are registered.
|
|
296
|
+
* Throws an error with details about missing registrations.
|
|
297
|
+
* Useful for fail-fast validation at application startup.
|
|
298
|
+
*
|
|
299
|
+
* @param {...(string|Function)} tokens Classes or names to validate
|
|
300
|
+
* @throws {Error} If any token is not registered
|
|
301
|
+
* @example
|
|
302
|
+
* // At app startup:
|
|
303
|
+
* validateRegistrations(UserService, AuthService, 'databaseConnection')
|
|
304
|
+
*/
|
|
305
|
+
export function validateRegistrations(...tokens) {
|
|
306
|
+
const missing = tokens.filter(token => !defaultContainer.has(token))
|
|
307
|
+
if (missing.length > 0) {
|
|
308
|
+
const names = missing.map(t => typeof t === 'string' ? t : t.name).join(', ')
|
|
309
|
+
throw new Error(
|
|
310
|
+
`Missing registrations: [${names}]. ` +
|
|
311
|
+
`Ensure these classes are decorated with @Singleton() or @Factory() before use.`
|
|
312
|
+
)
|
|
254
313
|
}
|
|
255
|
-
return instance
|
|
256
314
|
}
|
|
315
|
+
|
|
316
|
+
// Export Container class for advanced use cases (e.g., isolated containers)
|
|
317
|
+
export {Container}
|
|
318
|
+
|
|
319
|
+
// Export createProxy for advanced proxy use cases
|
|
320
|
+
export {createProxy} from './src/proxy.js'
|
package/package.json
CHANGED
|
@@ -1,34 +1,73 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "decorator-dependency-injection",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "A simple library for dependency injection using decorators",
|
|
5
5
|
"author": "Ravi Gairola <mallox@pyxzl.net>",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/mallocator/decorator-dependency-injection.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/mallocator/decorator-dependency-injection/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/mallocator/decorator-dependency-injection#readme",
|
|
7
15
|
"keywords": [
|
|
8
16
|
"dependency-injection",
|
|
9
17
|
"di",
|
|
10
18
|
"decorators",
|
|
11
|
-
"mocking"
|
|
19
|
+
"mocking",
|
|
20
|
+
"singleton",
|
|
21
|
+
"factory"
|
|
12
22
|
],
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=18.0.0"
|
|
25
|
+
},
|
|
13
26
|
"main": "index.js",
|
|
27
|
+
"types": "index.d.ts",
|
|
14
28
|
"type": "module",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./index.d.ts",
|
|
32
|
+
"import": "./index.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
15
35
|
"scripts": {
|
|
16
36
|
"start": "babel-node index.js",
|
|
17
|
-
"test": "jest"
|
|
37
|
+
"test": "jest --no-watchman",
|
|
38
|
+
"test:coverage": "jest --no-watchman --coverage",
|
|
39
|
+
"typecheck": "tsc test/types.check.ts --noEmit --esModuleInterop --skipLibCheck",
|
|
40
|
+
"lint": "eslint .",
|
|
41
|
+
"lint:fix": "eslint . --fix"
|
|
18
42
|
},
|
|
19
43
|
"jest": {
|
|
20
44
|
"transform": {
|
|
21
45
|
"^.+\\.js$": "babel-jest"
|
|
22
|
-
}
|
|
46
|
+
},
|
|
47
|
+
"coverageThreshold": {
|
|
48
|
+
"global": {
|
|
49
|
+
"branches": 80,
|
|
50
|
+
"functions": 80,
|
|
51
|
+
"lines": 80,
|
|
52
|
+
"statements": 80
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"collectCoverageFrom": [
|
|
56
|
+
"index.js",
|
|
57
|
+
"src/**/*.js"
|
|
58
|
+
]
|
|
23
59
|
},
|
|
24
60
|
"devDependencies": {
|
|
25
61
|
"@babel/cli": "^7.26.4",
|
|
26
62
|
"@babel/core": "^7.26.9",
|
|
63
|
+
"@babel/eslint-parser": "^7.28.6",
|
|
27
64
|
"@babel/plugin-proposal-decorators": "^7.25.9",
|
|
28
|
-
"@babel/polyfill": "^7.12.1",
|
|
29
65
|
"@babel/preset-env": "^7.26.9",
|
|
30
66
|
"@babel/register": "^7.25.9",
|
|
67
|
+
"@eslint/js": "^9.39.2",
|
|
31
68
|
"@types/jest": "^29.5.14",
|
|
32
|
-
"
|
|
69
|
+
"eslint": "^9.39.2",
|
|
70
|
+
"jest": "^29.7.0",
|
|
71
|
+
"typescript": "^5.9.3"
|
|
33
72
|
}
|
|
34
|
-
}
|
|
73
|
+
}
|