@enshou/di 0.1.2 → 0.2.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/dist/index.d.ts +21 -4
- package/dist/index.js +32 -5
- package/package.json +1 -1
- package/README.md +0 -131
package/dist/index.d.ts
CHANGED
|
@@ -9,15 +9,32 @@ declare function createToken<T>(description: string): Token<T>;
|
|
|
9
9
|
//#endregion
|
|
10
10
|
//#region src/container.d.ts
|
|
11
11
|
type Scope = "singleton" | "transient";
|
|
12
|
+
type ProviderToken<T> = Token<T> | string | Class<T>;
|
|
13
|
+
interface ClassProvider<T> {
|
|
14
|
+
provide: ProviderToken<T>;
|
|
15
|
+
useClass: Class<T>;
|
|
16
|
+
scope?: Scope;
|
|
17
|
+
}
|
|
18
|
+
interface ValueProvider<T> {
|
|
19
|
+
provide: ProviderToken<T>;
|
|
20
|
+
useValue: T;
|
|
21
|
+
}
|
|
22
|
+
interface FactoryProvider<T> {
|
|
23
|
+
provide: ProviderToken<T>;
|
|
24
|
+
useFactory: (container: Container) => T;
|
|
25
|
+
scope?: Scope;
|
|
26
|
+
}
|
|
27
|
+
type Provider<T> = ClassProvider<T> | ValueProvider<T> | FactoryProvider<T>;
|
|
12
28
|
declare class Container {
|
|
13
29
|
private readonly providers;
|
|
14
30
|
private readonly singletonCache;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
31
|
+
register<T>(provider: Provider<T>): void;
|
|
32
|
+
registerValue(token: ProviderToken<unknown>, value: unknown): void;
|
|
33
|
+
registerClass(token: ProviderToken<unknown>, value: Class<any>, scope?: Scope): void;
|
|
34
|
+
resolve<T>(token: ProviderToken<T>): T;
|
|
18
35
|
}
|
|
19
36
|
//#endregion
|
|
20
37
|
//#region src/inject.d.ts
|
|
21
38
|
declare function Inject(tokens: Array<Token<any> | string | Class<any>>): (target: any, _context?: ClassDecoratorContext) => void;
|
|
22
39
|
//#endregion
|
|
23
|
-
export { Container, Inject, type Scope, type Token, createToken };
|
|
40
|
+
export { type ClassProvider, Container, type FactoryProvider, Inject, type Provider, type ProviderToken, type Scope, type Token, type ValueProvider, createToken };
|
package/dist/index.js
CHANGED
|
@@ -10,12 +10,35 @@ function Inject(tokens) {
|
|
|
10
10
|
var Container = class {
|
|
11
11
|
providers = /* @__PURE__ */ new Map();
|
|
12
12
|
singletonCache = /* @__PURE__ */ new Map();
|
|
13
|
+
register(provider) {
|
|
14
|
+
this.singletonCache.delete(provider.provide);
|
|
15
|
+
if ("useValue" in provider) {
|
|
16
|
+
this.singletonCache.set(provider.provide, provider.useValue);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if ("useFactory" in provider) {
|
|
20
|
+
this.providers.set(provider.provide, {
|
|
21
|
+
kind: "factory",
|
|
22
|
+
useFactory: provider.useFactory,
|
|
23
|
+
scope: provider.scope ?? "singleton"
|
|
24
|
+
});
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
this.providers.set(provider.provide, {
|
|
28
|
+
kind: "class",
|
|
29
|
+
useClass: provider.useClass,
|
|
30
|
+
scope: provider.scope ?? "singleton"
|
|
31
|
+
});
|
|
32
|
+
}
|
|
13
33
|
registerValue(token, value) {
|
|
14
|
-
this.
|
|
34
|
+
this.register({
|
|
35
|
+
provide: token,
|
|
36
|
+
useValue: value
|
|
37
|
+
});
|
|
15
38
|
}
|
|
16
39
|
registerClass(token, value, scope = "singleton") {
|
|
17
|
-
this.
|
|
18
|
-
|
|
40
|
+
this.register({
|
|
41
|
+
provide: token,
|
|
19
42
|
useClass: value,
|
|
20
43
|
scope
|
|
21
44
|
});
|
|
@@ -24,8 +47,12 @@ var Container = class {
|
|
|
24
47
|
if (this.singletonCache.has(token)) return this.singletonCache.get(token);
|
|
25
48
|
const provider = this.providers.get(token);
|
|
26
49
|
if (!provider) throw Error(`No provider for ${String(token)}`);
|
|
27
|
-
|
|
28
|
-
|
|
50
|
+
let value;
|
|
51
|
+
if (provider.kind === "factory") value = provider.useFactory(this);
|
|
52
|
+
else {
|
|
53
|
+
const deps = (provider.useClass[INJECTS_KEY] ?? []).map(this.resolve.bind(this));
|
|
54
|
+
value = new provider.useClass(...deps);
|
|
55
|
+
}
|
|
29
56
|
if (provider.scope === "singleton") this.singletonCache.set(token, value);
|
|
30
57
|
return value;
|
|
31
58
|
}
|
package/package.json
CHANGED
package/README.md
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
# @enshou/di
|
|
2
|
-
|
|
3
|
-
A small dependency injection container for TypeScript.
|
|
4
|
-
|
|
5
|
-
It gives you a typed token system, a simple container, and an explicit
|
|
6
|
-
`@Inject(...)` decorator for constructor dependencies.
|
|
7
|
-
|
|
8
|
-
## Quick example
|
|
9
|
-
|
|
10
|
-
```ts
|
|
11
|
-
import { Container, Inject, createToken } from '@enshou/di'
|
|
12
|
-
|
|
13
|
-
type AppConfig = {
|
|
14
|
-
apiUrl: string
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const CONFIG = createToken<AppConfig>('config')
|
|
18
|
-
const LOGGER = createToken<Logger>('logger')
|
|
19
|
-
const API = createToken<ApiClient>('api')
|
|
20
|
-
|
|
21
|
-
class Logger {
|
|
22
|
-
log(message: string) {
|
|
23
|
-
console.log(message)
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
@Inject([CONFIG, LOGGER])
|
|
28
|
-
class ApiClient {
|
|
29
|
-
constructor(
|
|
30
|
-
private readonly config: AppConfig,
|
|
31
|
-
private readonly logger: Logger,
|
|
32
|
-
) {}
|
|
33
|
-
|
|
34
|
-
ping() {
|
|
35
|
-
this.logger.log(`GET ${this.config.apiUrl}/ping`)
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const container = new Container()
|
|
40
|
-
|
|
41
|
-
container.registerValue(CONFIG, {
|
|
42
|
-
apiUrl: 'https://example.dev',
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
container.registerClass(LOGGER, Logger)
|
|
46
|
-
container.registerClass(API, ApiClient)
|
|
47
|
-
|
|
48
|
-
const api = container.resolve(API)
|
|
49
|
-
api.ping()
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
## API
|
|
53
|
-
|
|
54
|
-
### `createToken<T>(description)`
|
|
55
|
-
|
|
56
|
-
Creates a unique, typed token.
|
|
57
|
-
|
|
58
|
-
```ts
|
|
59
|
-
const USER_REPOSITORY = createToken<UserRepository>('user-repository')
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### `new Container()`
|
|
63
|
-
|
|
64
|
-
Creates a new dependency container.
|
|
65
|
-
|
|
66
|
-
### `container.registerValue(token, value)`
|
|
67
|
-
|
|
68
|
-
Registers a prebuilt value.
|
|
69
|
-
|
|
70
|
-
Values are stored in the singleton cache, so resolving the same token always
|
|
71
|
-
returns the same instance.
|
|
72
|
-
|
|
73
|
-
```ts
|
|
74
|
-
container.registerValue(CONFIG, { apiUrl: 'https://example.dev' })
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### `container.registerClass(token, Class, scope?)`
|
|
78
|
-
|
|
79
|
-
Registers a class provider for a token.
|
|
80
|
-
|
|
81
|
-
- `singleton` by default: the instance is created once and reused
|
|
82
|
-
- `transient`: a new instance is created on every `resolve`
|
|
83
|
-
|
|
84
|
-
```ts
|
|
85
|
-
container.registerClass(LOGGER, Logger, 'singleton')
|
|
86
|
-
container.registerClass(API, ApiClient, 'transient')
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### `container.resolve(token)`
|
|
90
|
-
|
|
91
|
-
Resolves a dependency by token.
|
|
92
|
-
|
|
93
|
-
Throws if no provider has been registered for that token.
|
|
94
|
-
|
|
95
|
-
```ts
|
|
96
|
-
const logger = container.resolve(LOGGER)
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### `@Inject(tokens)`
|
|
100
|
-
|
|
101
|
-
Declares the constructor dependencies for a class.
|
|
102
|
-
|
|
103
|
-
The order of tokens must match the order of constructor parameters.
|
|
104
|
-
|
|
105
|
-
```ts
|
|
106
|
-
@Inject([CONFIG, LOGGER])
|
|
107
|
-
class ApiClient {
|
|
108
|
-
constructor(config: AppConfig, logger: Logger) {}
|
|
109
|
-
}
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
## Container behavior
|
|
113
|
-
|
|
114
|
-
- `registerValue` always stores the value in the singleton cache
|
|
115
|
-
- class dependencies are resolved recursively through `resolve`
|
|
116
|
-
- singleton instances are created lazily on first resolution
|
|
117
|
-
- transient instances are never cached
|
|
118
|
-
|
|
119
|
-
## Limitations
|
|
120
|
-
|
|
121
|
-
- dependencies are declared explicitly with `@Inject(...)`; constructor types are not read automatically
|
|
122
|
-
- the container only supports class providers and value providers
|
|
123
|
-
- circular dependencies are not handled specially
|
|
124
|
-
- tokens are compared by identity, so you must use the same token instance for registration and resolution
|
|
125
|
-
|
|
126
|
-
## Exports
|
|
127
|
-
|
|
128
|
-
```ts
|
|
129
|
-
import { Container, Inject, createToken } from '@enshou/di'
|
|
130
|
-
import type { Scope, Token } from '@enshou/di'
|
|
131
|
-
```
|