@husky-di/module 1.2.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +512 -10
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,23 +1,525 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @husky-di/module
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`@husky-di/module` gives husky-di an ESM-like module system.
|
|
4
|
+
It builds on top of `@husky-di/core`, so groups of services can be declared, imported, exported, and validated like modules.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
If `core` answers "how are services registered and resolved?", `module` answers "how are those services grouped, exposed, and kept from colliding with each other?".
|
|
7
|
+
|
|
8
|
+
## Is This The Right Package?
|
|
9
|
+
|
|
10
|
+
This package is a good fit when:
|
|
11
|
+
|
|
12
|
+
- a single flat container is no longer enough
|
|
13
|
+
- you want clear declaration, import, and export boundaries
|
|
14
|
+
- you want conflicts and invalid exports to fail during module creation instead of later at runtime
|
|
15
|
+
|
|
16
|
+
If what you currently need is only:
|
|
17
|
+
|
|
18
|
+
- a low-level DI container:
|
|
19
|
+
see `../core/README.md`
|
|
20
|
+
- constructor injection through decorators:
|
|
21
|
+
pair it with `../decorator/README.md`
|
|
22
|
+
|
|
23
|
+
## What You Get
|
|
24
|
+
|
|
25
|
+
- `createModule()` to create modules
|
|
26
|
+
- `declarations` for local service declarations
|
|
27
|
+
- `imports` for importing exports from other modules
|
|
28
|
+
- `exports` for defining the public boundary of a module
|
|
29
|
+
- `withAliases()` for renaming imported service identifiers
|
|
30
|
+
- creation-time validation for duplicate declarations, import conflicts, invalid exports, circular dependencies, and more
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
6
33
|
|
|
7
34
|
```bash
|
|
8
|
-
pnpm
|
|
35
|
+
pnpm add @husky-di/core @husky-di/module
|
|
9
36
|
```
|
|
10
37
|
|
|
11
|
-
##
|
|
38
|
+
## Quick Start
|
|
12
39
|
|
|
13
|
-
|
|
40
|
+
The example below shows the basic idea: `CoreModule` exposes shared capabilities, `UserModule` imports what it needs, then exports its own service.
|
|
14
41
|
|
|
15
|
-
```
|
|
16
|
-
|
|
42
|
+
```typescript
|
|
43
|
+
import { createServiceIdentifier, resolve } from "@husky-di/core";
|
|
44
|
+
import { createModule } from "@husky-di/module";
|
|
45
|
+
|
|
46
|
+
interface Config {
|
|
47
|
+
apiBaseUrl: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface Logger {
|
|
51
|
+
log(message: string): void;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface UserService {
|
|
55
|
+
getUser(id: string): { id: string; name: string };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const IConfig = createServiceIdentifier<Config>("IConfig");
|
|
59
|
+
const ILogger = createServiceIdentifier<Logger>("ILogger");
|
|
60
|
+
const IUserService = createServiceIdentifier<UserService>("IUserService");
|
|
61
|
+
|
|
62
|
+
class ConsoleLogger implements Logger {
|
|
63
|
+
log(message: string) {
|
|
64
|
+
console.log(message);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
class DefaultUserService implements UserService {
|
|
69
|
+
private readonly config = resolve(IConfig);
|
|
70
|
+
private readonly logger = resolve(ILogger);
|
|
71
|
+
|
|
72
|
+
getUser(id: string) {
|
|
73
|
+
this.logger.log(`GET ${this.config.apiBaseUrl}/users/${id}`);
|
|
74
|
+
return { id, name: "Ada" };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const CoreModule = createModule({
|
|
79
|
+
name: "CoreModule",
|
|
80
|
+
declarations: [
|
|
81
|
+
{
|
|
82
|
+
serviceIdentifier: IConfig,
|
|
83
|
+
useValue: { apiBaseUrl: "https://api.example.com" },
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
serviceIdentifier: ILogger,
|
|
87
|
+
useClass: ConsoleLogger,
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
exports: [IConfig, ILogger],
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const UserModule = createModule({
|
|
94
|
+
name: "UserModule",
|
|
95
|
+
imports: [CoreModule],
|
|
96
|
+
declarations: [
|
|
97
|
+
{
|
|
98
|
+
serviceIdentifier: IUserService,
|
|
99
|
+
useClass: DefaultUserService,
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
exports: [IUserService],
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const userService = UserModule.resolve(IUserService);
|
|
106
|
+
console.log(userService.getUser("u-1"));
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
In this example:
|
|
110
|
+
|
|
111
|
+
- `CoreModule` exposes `IConfig` and `ILogger`
|
|
112
|
+
- `UserModule` imports the exported view of `CoreModule`
|
|
113
|
+
- `DefaultUserService` uses `resolve()` to access dependencies visible inside the module
|
|
114
|
+
- external callers can only resolve services listed in `UserModule.exports`
|
|
115
|
+
|
|
116
|
+
## Module Mental Model
|
|
117
|
+
|
|
118
|
+
A module has four main parts:
|
|
119
|
+
|
|
120
|
+
### `name`
|
|
121
|
+
|
|
122
|
+
The display name of the module, used for debugging, error messages, and circular dependency paths.
|
|
123
|
+
|
|
124
|
+
### `declarations`
|
|
125
|
+
|
|
126
|
+
Local service declarations.
|
|
127
|
+
Each declaration is essentially a `core.register()` configuration plus a `serviceIdentifier`.
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
const LoggerModule = createModule({
|
|
131
|
+
name: "LoggerModule",
|
|
132
|
+
declarations: [
|
|
133
|
+
{
|
|
134
|
+
serviceIdentifier: "logger",
|
|
135
|
+
useValue: { log: console.log },
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
exports: ["logger"],
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`declarations` supports the same provider strategies as `core`:
|
|
143
|
+
|
|
144
|
+
- `useClass`
|
|
145
|
+
- `useFactory`
|
|
146
|
+
- `useValue`
|
|
147
|
+
- `useAlias`
|
|
148
|
+
|
|
149
|
+
### `imports`
|
|
150
|
+
|
|
151
|
+
Import services exported by other modules.
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
const AppModule = createModule({
|
|
155
|
+
name: "AppModule",
|
|
156
|
+
imports: [LoggerModule],
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
What gets imported is not the entire internal state of the other module.
|
|
161
|
+
It is only that module's exported view.
|
|
162
|
+
|
|
163
|
+
### `exports`
|
|
164
|
+
|
|
165
|
+
Defines which service identifiers this module is willing to expose to the outside.
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
const LoggerModule = createModule({
|
|
169
|
+
name: "LoggerModule",
|
|
170
|
+
declarations: [
|
|
171
|
+
{
|
|
172
|
+
serviceIdentifier: "logger",
|
|
173
|
+
useValue: { log: console.log },
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
serviceIdentifier: "config",
|
|
177
|
+
useValue: { level: "info" },
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
exports: ["logger"],
|
|
181
|
+
});
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
External callers can resolve `"logger"`, but not `"config"`.
|
|
185
|
+
|
|
186
|
+
## Declaring Services
|
|
187
|
+
|
|
188
|
+
### `useClass`
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
const UserModule = createModule({
|
|
192
|
+
name: "UserModule",
|
|
193
|
+
declarations: [
|
|
194
|
+
{
|
|
195
|
+
serviceIdentifier: IUserService,
|
|
196
|
+
useClass: DefaultUserService,
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
exports: [IUserService],
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
If the class depends on services inside the module, it is usually best to use `resolve()` as you would in `core`, or to assemble the dependencies explicitly with `useFactory`.
|
|
204
|
+
|
|
205
|
+
### `useFactory`
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
const UserModule = createModule({
|
|
209
|
+
name: "UserModule",
|
|
210
|
+
imports: [CoreModule],
|
|
211
|
+
declarations: [
|
|
212
|
+
{
|
|
213
|
+
serviceIdentifier: IUserService,
|
|
214
|
+
useFactory: (container) => {
|
|
215
|
+
const config = container.resolve(IConfig);
|
|
216
|
+
const logger = container.resolve(ILogger);
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
getUser(id: string) {
|
|
220
|
+
logger.log(`GET ${config.apiBaseUrl}/users/${id}`);
|
|
221
|
+
return { id, name: "Ada" };
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
exports: [IUserService],
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### `useValue`
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
const ConfigModule = createModule({
|
|
235
|
+
name: "ConfigModule",
|
|
236
|
+
declarations: [
|
|
237
|
+
{
|
|
238
|
+
serviceIdentifier: IConfig,
|
|
239
|
+
useValue: { apiBaseUrl: "https://api.example.com" },
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
exports: [IConfig],
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### `useAlias`
|
|
247
|
+
|
|
248
|
+
You can also create aliases inside module-local declarations:
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
const LoggerModule = createModule({
|
|
252
|
+
name: "LoggerModule",
|
|
253
|
+
declarations: [
|
|
254
|
+
{ serviceIdentifier: ILogger, useClass: ConsoleLogger },
|
|
255
|
+
{ serviceIdentifier: "appLogger", useAlias: ILogger },
|
|
256
|
+
],
|
|
257
|
+
exports: ["appLogger"],
|
|
258
|
+
});
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Importing And Re-exporting
|
|
262
|
+
|
|
263
|
+
A module can import services and optionally export them again.
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
const SharedModule = createModule({
|
|
267
|
+
name: "SharedModule",
|
|
268
|
+
imports: [ConfigModule, LoggerModule],
|
|
269
|
+
exports: [IConfig, ILogger],
|
|
270
|
+
});
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
`SharedModule` has no local declarations here.
|
|
274
|
+
It simply re-exposes services exported by its imported modules.
|
|
275
|
+
|
|
276
|
+
## `withAliases()`: Renaming Imports
|
|
277
|
+
|
|
278
|
+
If multiple modules export the same identifier, or if you want a different local name in the current module, use `withAliases()`.
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
const CoreModule = createModule({
|
|
282
|
+
name: "CoreModule",
|
|
283
|
+
declarations: [
|
|
284
|
+
{ serviceIdentifier: "logger", useValue: { log: () => "core" } },
|
|
285
|
+
{ serviceIdentifier: "config", useValue: { env: "production" } },
|
|
286
|
+
],
|
|
287
|
+
exports: ["logger", "config"],
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
const AppModule = createModule({
|
|
291
|
+
name: "AppModule",
|
|
292
|
+
imports: [
|
|
293
|
+
CoreModule.withAliases([
|
|
294
|
+
{ serviceIdentifier: "logger", as: "appLogger" },
|
|
295
|
+
]),
|
|
296
|
+
],
|
|
297
|
+
exports: ["appLogger", "config"],
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
AppModule.resolve("appLogger");
|
|
301
|
+
AppModule.resolve("config");
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Important semantics:
|
|
305
|
+
|
|
306
|
+
- an alias renames, it does not filter
|
|
307
|
+
- an aliased service enters the current module scope under the new name
|
|
308
|
+
- exports that are not aliased still enter under their original names
|
|
309
|
+
- once a service has been aliased, the original imported name is no longer visible in the current module scope
|
|
310
|
+
|
|
311
|
+
So in the example above:
|
|
312
|
+
|
|
313
|
+
- `"appLogger"` is visible
|
|
314
|
+
- `"config"` is visible
|
|
315
|
+
- `"logger"` is not visible
|
|
316
|
+
|
|
317
|
+
## Why Export Boundaries Matter
|
|
318
|
+
|
|
319
|
+
One of the most important behaviors in `module` is strict export-boundary enforcement.
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
const DatabaseModule = createModule({
|
|
323
|
+
name: "DatabaseModule",
|
|
324
|
+
declarations: [
|
|
325
|
+
{ serviceIdentifier: "config", useValue: { host: "localhost" } },
|
|
326
|
+
{ serviceIdentifier: "database", useClass: DatabaseService },
|
|
327
|
+
],
|
|
328
|
+
exports: ["database"],
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
DatabaseModule.resolve("database"); // ok
|
|
332
|
+
DatabaseModule.resolve("config"); // throws
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Here, `"config"` is available internally, but it is not public API.
|
|
336
|
+
|
|
337
|
+
There are two layers to keep in mind:
|
|
338
|
+
|
|
339
|
+
- external callers using `module.resolve()` can only get services listed in `exports`
|
|
340
|
+
- the internal resolution flow can still access local declarations and imported internal dependencies
|
|
341
|
+
|
|
342
|
+
That gives you room to compose internal details without exposing them as public surface area.
|
|
343
|
+
|
|
344
|
+
## What A Module Instance Can Do
|
|
345
|
+
|
|
346
|
+
The object returned by `createModule()` is also a container facade with export-boundary protection.
|
|
347
|
+
|
|
348
|
+
You can call:
|
|
349
|
+
|
|
350
|
+
- `module.resolve()`
|
|
351
|
+
- `module.isRegistered()`
|
|
352
|
+
- `module.getServiceIdentifiers()`
|
|
353
|
+
- `module.use()`
|
|
354
|
+
- `module.unused()`
|
|
355
|
+
- `module.withAliases()`
|
|
356
|
+
|
|
357
|
+
You can also inspect:
|
|
358
|
+
|
|
359
|
+
- `module.container`
|
|
360
|
+
- `module.name`
|
|
361
|
+
- `module.displayName`
|
|
362
|
+
- `module.declarations`
|
|
363
|
+
- `module.imports`
|
|
364
|
+
- `module.exports`
|
|
365
|
+
|
|
366
|
+
## What Gets Validated At Creation Time
|
|
367
|
+
|
|
368
|
+
`module` tries to surface structural problems as early as possible.
|
|
369
|
+
|
|
370
|
+
### Duplicate Declarations
|
|
371
|
+
|
|
372
|
+
The same module cannot declare the same `serviceIdentifier` twice.
|
|
373
|
+
|
|
374
|
+
### Duplicate Module Imports
|
|
375
|
+
|
|
376
|
+
The same module instance cannot appear more than once in `imports`.
|
|
377
|
+
|
|
378
|
+
### Import Name Conflicts
|
|
379
|
+
|
|
380
|
+
If two imported modules export the same service and you do not resolve the conflict with aliases, module creation fails immediately.
|
|
381
|
+
|
|
382
|
+
### Local Declaration And Import Conflicts
|
|
383
|
+
|
|
384
|
+
A locally visible imported name cannot collide with one of the module's own declarations.
|
|
385
|
+
|
|
386
|
+
### Exporting Non-existent Services
|
|
387
|
+
|
|
388
|
+
Every item in `exports` must come from:
|
|
389
|
+
|
|
390
|
+
- a local declaration
|
|
391
|
+
- an imported module export
|
|
392
|
+
- a service that entered the current module scope through aliasing
|
|
393
|
+
|
|
394
|
+
### Circular Dependencies
|
|
395
|
+
|
|
396
|
+
The module import graph cannot contain cycles.
|
|
397
|
+
|
|
398
|
+
## How It Relates To `core` And `decorator`
|
|
399
|
+
|
|
400
|
+
`@husky-di/module` does not replace `core`.
|
|
401
|
+
It is built directly on top of it:
|
|
402
|
+
|
|
403
|
+
- provider semantics come from `core`
|
|
404
|
+
- container resolution and lifecycles come from `core`
|
|
405
|
+
- `resolve()` still comes from `core`
|
|
406
|
+
|
|
407
|
+
If you want to keep using constructor decorator injection inside module `useClass` declarations, you can pair it with `@husky-di/decorator` as well.
|
|
408
|
+
The two packages solve different problems:
|
|
409
|
+
|
|
410
|
+
- `module` owns boundaries, imports, and exports
|
|
411
|
+
- `decorator` owns constructor-parameter injection
|
|
412
|
+
|
|
413
|
+
## Complete Example
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
import { createServiceIdentifier, resolve } from "@husky-di/core";
|
|
417
|
+
import { createModule } from "@husky-di/module";
|
|
418
|
+
|
|
419
|
+
interface DatabaseConfig {
|
|
420
|
+
type: string;
|
|
421
|
+
host: string;
|
|
422
|
+
port: number;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
interface Database {
|
|
426
|
+
connect(): string;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
interface AuthService {
|
|
430
|
+
authenticate(): { authenticated: true; token: string };
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
interface App {
|
|
434
|
+
bootstrap(): string;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const IDatabaseConfig =
|
|
438
|
+
createServiceIdentifier<DatabaseConfig>("IDatabaseConfig");
|
|
439
|
+
const IDatabase = createServiceIdentifier<Database>("IDatabase");
|
|
440
|
+
const IAuthService = createServiceIdentifier<AuthService>("IAuthService");
|
|
441
|
+
const IApp = createServiceIdentifier<App>("IApp");
|
|
442
|
+
|
|
443
|
+
class DatabaseService implements Database {
|
|
444
|
+
private readonly config = resolve(IDatabaseConfig);
|
|
445
|
+
|
|
446
|
+
connect() {
|
|
447
|
+
return `Connected to ${this.config.type} at ${this.config.host}:${this.config.port}`;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
class DefaultAuthService implements AuthService {
|
|
452
|
+
authenticate() {
|
|
453
|
+
return { authenticated: true, token: "test-token" } as const;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
class AppService implements App {
|
|
458
|
+
private readonly database = resolve(IDatabase);
|
|
459
|
+
private readonly authService = resolve(IAuthService);
|
|
460
|
+
|
|
461
|
+
bootstrap() {
|
|
462
|
+
this.database.connect();
|
|
463
|
+
this.authService.authenticate();
|
|
464
|
+
return "Application bootstrapped successfully";
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const DatabaseModule = createModule({
|
|
469
|
+
name: "DatabaseModule",
|
|
470
|
+
declarations: [
|
|
471
|
+
{
|
|
472
|
+
serviceIdentifier: IDatabaseConfig,
|
|
473
|
+
useValue: {
|
|
474
|
+
type: "sqlite",
|
|
475
|
+
host: "localhost",
|
|
476
|
+
port: 3306,
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
serviceIdentifier: IDatabase,
|
|
481
|
+
useClass: DatabaseService,
|
|
482
|
+
},
|
|
483
|
+
],
|
|
484
|
+
exports: [IDatabase],
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
const AuthModule = createModule({
|
|
488
|
+
name: "AuthModule",
|
|
489
|
+
declarations: [
|
|
490
|
+
{
|
|
491
|
+
serviceIdentifier: IAuthService,
|
|
492
|
+
useClass: DefaultAuthService,
|
|
493
|
+
},
|
|
494
|
+
],
|
|
495
|
+
exports: [IAuthService],
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
const AppModule = createModule({
|
|
499
|
+
name: "AppModule",
|
|
500
|
+
imports: [DatabaseModule, AuthModule],
|
|
501
|
+
declarations: [
|
|
502
|
+
{
|
|
503
|
+
serviceIdentifier: IApp,
|
|
504
|
+
useClass: AppService,
|
|
505
|
+
},
|
|
506
|
+
],
|
|
507
|
+
exports: [IApp],
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
const app = AppModule.resolve(IApp);
|
|
511
|
+
console.log(app.bootstrap());
|
|
17
512
|
```
|
|
18
513
|
|
|
19
|
-
|
|
514
|
+
## Related Docs
|
|
515
|
+
|
|
516
|
+
- container and resolution model: `../core/README.md`
|
|
517
|
+
- module behavior specification: `./docs/SPECIFICATION.md`
|
|
518
|
+
- decorator support: `../decorator/README.md`
|
|
519
|
+
|
|
520
|
+
## Local Development
|
|
20
521
|
|
|
21
522
|
```bash
|
|
22
|
-
pnpm
|
|
523
|
+
pnpm build
|
|
524
|
+
pnpm test
|
|
23
525
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@husky-di/module",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"dist"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@husky-di/core": "1.
|
|
18
|
+
"@husky-di/core": "1.3.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@rslib/core": "^0.20.1",
|