@djodjonx/neo-syringe 1.1.5 → 1.2.2
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/.github/workflows/ci.yml +6 -5
- package/.github/workflows/docs.yml +59 -0
- package/CHANGELOG.md +27 -0
- package/README.md +74 -740
- package/dist/{GraphValidator-G0F4QiLk.cjs → GraphValidator-CV4VoJl0.cjs} +18 -10
- package/dist/{GraphValidator-C8ldJtNp.mjs → GraphValidator-DXqqkNdS.mjs} +18 -10
- package/dist/cli/index.cjs +16 -1
- package/dist/cli/index.mjs +16 -1
- package/dist/index.d.cts +31 -5
- package/dist/index.d.mts +31 -5
- package/dist/lsp/index.cjs +1 -1
- package/dist/lsp/index.mjs +1 -1
- package/dist/unplugin/index.cjs +33 -9
- package/dist/unplugin/index.d.cts +7 -5
- package/dist/unplugin/index.d.mts +7 -5
- package/dist/unplugin/index.mjs +33 -9
- package/docs/.vitepress/config.ts +109 -0
- package/docs/.vitepress/theme/custom.css +150 -0
- package/docs/.vitepress/theme/index.ts +17 -0
- package/docs/api/configuration.md +274 -0
- package/docs/api/functions.md +291 -0
- package/docs/api/types.md +158 -0
- package/docs/guide/basic-usage.md +267 -0
- package/docs/guide/cli.md +174 -0
- package/docs/guide/generated-code.md +284 -0
- package/docs/guide/getting-started.md +171 -0
- package/docs/guide/ide-plugin.md +203 -0
- package/docs/guide/injection-types.md +287 -0
- package/docs/guide/legacy-migration.md +333 -0
- package/docs/guide/lifecycle.md +223 -0
- package/docs/guide/parent-container.md +321 -0
- package/docs/guide/scoped-injections.md +271 -0
- package/docs/guide/what-is-neo-syringe.md +162 -0
- package/docs/guide/why-neo-syringe.md +219 -0
- package/docs/index.md +138 -0
- package/docs/public/logo.png +0 -0
- package/package.json +15 -12
- package/src/analyzer/Analyzer.ts +20 -10
- package/src/analyzer/types.ts +55 -49
- package/src/cli/index.ts +15 -0
- package/src/generator/Generator.ts +24 -2
- package/src/generator/GraphValidator.ts +6 -2
- package/src/types.ts +30 -4
- package/src/unplugin/index.ts +13 -41
- package/tests/analyzer/Analyzer.test.ts +4 -4
- package/tests/analyzer/AnalyzerDeclarative.test.ts +1 -1
- package/tests/analyzer/Factory.test.ts +2 -2
- package/tests/analyzer/Scoped.test.ts +434 -0
- package/tests/cli/cli.test.ts +91 -0
- package/tests/e2e/container-integration.test.ts +21 -21
- package/tests/e2e/generated-code.test.ts +7 -7
- package/tests/e2e/scoped.test.ts +370 -0
- package/tests/e2e/snapshots.test.ts +2 -2
- package/tests/e2e/standalone.test.ts +2 -2
- package/tests/generator/ExternalGenerator.test.ts +1 -1
- package/tests/generator/FactoryGenerator.test.ts +6 -6
- package/tests/generator/Generator.test.ts +2 -2
- package/tests/generator/GeneratorDeclarative.test.ts +1 -1
- package/tests/generator/GraphValidator.test.ts +1 -1
- package/tsconfig.json +2 -1
- package/typedoc.json +0 -5
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# Functions
|
|
2
|
+
|
|
3
|
+
API functions exported by Neo-Syringe.
|
|
4
|
+
|
|
5
|
+
## defineBuilderConfig
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
function defineBuilderConfig(config: BuilderConfig): Container
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Define a container configuration. At runtime without the build plugin, this throws an error. At build time, it's replaced with the generated container.
|
|
12
|
+
|
|
13
|
+
### Parameters
|
|
14
|
+
|
|
15
|
+
| Name | Type | Description |
|
|
16
|
+
|------|------|-------------|
|
|
17
|
+
| `config` | `BuilderConfig` | Container configuration |
|
|
18
|
+
|
|
19
|
+
### Returns
|
|
20
|
+
|
|
21
|
+
`Container` - The generated container (after build)
|
|
22
|
+
|
|
23
|
+
### Example
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { defineBuilderConfig, useInterface } from '@djodjonx/neo-syringe';
|
|
27
|
+
|
|
28
|
+
export const container = defineBuilderConfig({
|
|
29
|
+
name: 'AppContainer',
|
|
30
|
+
injections: [
|
|
31
|
+
{ token: useInterface<ILogger>(), provider: ConsoleLogger },
|
|
32
|
+
{ token: UserService }
|
|
33
|
+
]
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## definePartialConfig
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
function definePartialConfig(config: PartialConfig): PartialConfig
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Define a reusable partial configuration that can be extended.
|
|
46
|
+
|
|
47
|
+
### Parameters
|
|
48
|
+
|
|
49
|
+
| Name | Type | Description |
|
|
50
|
+
|------|------|-------------|
|
|
51
|
+
| `config` | `PartialConfig` | Partial configuration |
|
|
52
|
+
|
|
53
|
+
### Returns
|
|
54
|
+
|
|
55
|
+
`PartialConfig` - The partial configuration
|
|
56
|
+
|
|
57
|
+
### Example
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { definePartialConfig, useInterface } from '@djodjonx/neo-syringe';
|
|
61
|
+
|
|
62
|
+
export const loggingPartial = definePartialConfig({
|
|
63
|
+
injections: [
|
|
64
|
+
{ token: useInterface<ILogger>(), provider: ConsoleLogger }
|
|
65
|
+
]
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Use in main config
|
|
69
|
+
export const container = defineBuilderConfig({
|
|
70
|
+
extends: [loggingPartial],
|
|
71
|
+
injections: [
|
|
72
|
+
{ token: UserService }
|
|
73
|
+
]
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## useInterface
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
function useInterface<T>(): InterfaceToken<T>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Create a token for an interface. At compile time, this generates a unique string ID.
|
|
86
|
+
|
|
87
|
+
### Type Parameters
|
|
88
|
+
|
|
89
|
+
| Name | Description |
|
|
90
|
+
|------|-------------|
|
|
91
|
+
| `T` | The interface type |
|
|
92
|
+
|
|
93
|
+
### Returns
|
|
94
|
+
|
|
95
|
+
`InterfaceToken<T>` - A token representing the interface
|
|
96
|
+
|
|
97
|
+
### Example
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { useInterface } from '@djodjonx/neo-syringe';
|
|
101
|
+
|
|
102
|
+
interface ILogger {
|
|
103
|
+
log(msg: string): void;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
interface IDatabase {
|
|
107
|
+
query(sql: string): any[];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// In configuration
|
|
111
|
+
{
|
|
112
|
+
injections: [
|
|
113
|
+
{ token: useInterface<ILogger>(), provider: ConsoleLogger },
|
|
114
|
+
{ token: useInterface<IDatabase>(), provider: PostgresDatabase }
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Resolution
|
|
119
|
+
const logger = container.resolve(useInterface<ILogger>());
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## useProperty
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
function useProperty<T, C = unknown>(
|
|
128
|
+
targetClass: Constructor<C>,
|
|
129
|
+
paramName: string
|
|
130
|
+
): PropertyToken<T, C>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Create a token for a primitive constructor parameter.
|
|
134
|
+
|
|
135
|
+
### Type Parameters
|
|
136
|
+
|
|
137
|
+
| Name | Description |
|
|
138
|
+
|------|-------------|
|
|
139
|
+
| `T` | The primitive type (string, number, boolean) |
|
|
140
|
+
| `C` | The class type |
|
|
141
|
+
|
|
142
|
+
### Parameters
|
|
143
|
+
|
|
144
|
+
| Name | Type | Description |
|
|
145
|
+
|------|------|-------------|
|
|
146
|
+
| `targetClass` | `Constructor<C>` | The class that has this parameter |
|
|
147
|
+
| `paramName` | `string` | The parameter name |
|
|
148
|
+
|
|
149
|
+
### Returns
|
|
150
|
+
|
|
151
|
+
`PropertyToken<T, C>` - A token for the primitive
|
|
152
|
+
|
|
153
|
+
### Example
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { useProperty } from '@djodjonx/neo-syringe';
|
|
157
|
+
|
|
158
|
+
class ApiService {
|
|
159
|
+
constructor(
|
|
160
|
+
private apiUrl: string,
|
|
161
|
+
private timeout: number
|
|
162
|
+
) {}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const apiUrl = useProperty<string>(ApiService, 'apiUrl');
|
|
166
|
+
const timeout = useProperty<number>(ApiService, 'timeout');
|
|
167
|
+
|
|
168
|
+
// In configuration
|
|
169
|
+
{
|
|
170
|
+
injections: [
|
|
171
|
+
{ token: apiUrl, provider: () => 'https://api.example.com' },
|
|
172
|
+
{ token: timeout, provider: () => 5000 },
|
|
173
|
+
{ token: ApiService }
|
|
174
|
+
]
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## declareContainerTokens
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
function declareContainerTokens<T>(container: any): any
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Declare tokens provided by a legacy container for type-safety and validation.
|
|
187
|
+
|
|
188
|
+
### Type Parameters
|
|
189
|
+
|
|
190
|
+
| Name | Description |
|
|
191
|
+
|------|-------------|
|
|
192
|
+
| `T` | Object type mapping token names to types |
|
|
193
|
+
|
|
194
|
+
### Parameters
|
|
195
|
+
|
|
196
|
+
| Name | Type | Description |
|
|
197
|
+
|------|------|-------------|
|
|
198
|
+
| `container` | `any` | The legacy container instance |
|
|
199
|
+
|
|
200
|
+
### Returns
|
|
201
|
+
|
|
202
|
+
The container with declared types
|
|
203
|
+
|
|
204
|
+
### Example
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
import { declareContainerTokens } from '@djodjonx/neo-syringe';
|
|
208
|
+
import { container as tsyringeContainer } from 'tsyringe';
|
|
209
|
+
|
|
210
|
+
// Declare what tsyringe provides
|
|
211
|
+
const legacy = declareContainerTokens<{
|
|
212
|
+
AuthService: AuthService;
|
|
213
|
+
UserRepository: UserRepository;
|
|
214
|
+
}>(tsyringeContainer);
|
|
215
|
+
|
|
216
|
+
// Use in configuration
|
|
217
|
+
export const container = defineBuilderConfig({
|
|
218
|
+
useContainer: legacy,
|
|
219
|
+
injections: [
|
|
220
|
+
{ token: NewService } // Can depend on AuthService, UserRepository
|
|
221
|
+
]
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Container.resolve
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
resolve<T>(token: Token<T>): T
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Resolve a service from the container.
|
|
234
|
+
|
|
235
|
+
### Type Parameters
|
|
236
|
+
|
|
237
|
+
| Name | Description |
|
|
238
|
+
|------|-------------|
|
|
239
|
+
| `T` | The service type |
|
|
240
|
+
|
|
241
|
+
### Parameters
|
|
242
|
+
|
|
243
|
+
| Name | Type | Description |
|
|
244
|
+
|------|------|-------------|
|
|
245
|
+
| `token` | `Token<T>` | Class, interface token, or property token |
|
|
246
|
+
|
|
247
|
+
### Returns
|
|
248
|
+
|
|
249
|
+
`T` - The resolved instance
|
|
250
|
+
|
|
251
|
+
### Throws
|
|
252
|
+
|
|
253
|
+
`Error` - If the service is not found
|
|
254
|
+
|
|
255
|
+
### Example
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
// By class
|
|
259
|
+
const userService = container.resolve(UserService);
|
|
260
|
+
|
|
261
|
+
// By interface
|
|
262
|
+
const logger = container.resolve(useInterface<ILogger>());
|
|
263
|
+
|
|
264
|
+
// By property (unusual, but possible)
|
|
265
|
+
const apiUrl = container.resolve(useProperty<string>(ApiService, 'apiUrl'));
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## Container.createChildContainer
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
createChildContainer(): Container
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Create a child container that inherits from this one.
|
|
277
|
+
|
|
278
|
+
### Returns
|
|
279
|
+
|
|
280
|
+
`Container` - A new child container
|
|
281
|
+
|
|
282
|
+
### Example
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
const parent = container;
|
|
286
|
+
const child = parent.createChildContainer();
|
|
287
|
+
|
|
288
|
+
// Child can resolve parent's services
|
|
289
|
+
const logger = child.resolve(useInterface<ILogger>());
|
|
290
|
+
```
|
|
291
|
+
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Types
|
|
2
|
+
|
|
3
|
+
Type definitions for Neo-Syringe.
|
|
4
|
+
|
|
5
|
+
## Lifecycle
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
type Lifecycle = 'singleton' | 'transient';
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Defines how instances are managed:
|
|
12
|
+
|
|
13
|
+
| Value | Description |
|
|
14
|
+
|-------|-------------|
|
|
15
|
+
| `singleton` | One instance per container (default) |
|
|
16
|
+
| `transient` | New instance on every `resolve()` |
|
|
17
|
+
|
|
18
|
+
## Constructor
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
type Constructor<T = unknown> = new (...args: unknown[]) => T;
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Represents any class constructor.
|
|
25
|
+
|
|
26
|
+
## Token
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
type Token<T = any> = Constructor<T> | InterfaceToken<T> | PropertyToken<T, any>;
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
A token that can be resolved by the container:
|
|
33
|
+
|
|
34
|
+
- **Constructor**: A class reference (e.g., `UserService`)
|
|
35
|
+
- **InterfaceToken**: Created by `useInterface<T>()`
|
|
36
|
+
- **PropertyToken**: Created by `useProperty<T>(Class, 'param')`
|
|
37
|
+
|
|
38
|
+
## InterfaceToken
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
type InterfaceToken<T> = {
|
|
42
|
+
__brand: 'InterfaceToken';
|
|
43
|
+
__type: T;
|
|
44
|
+
};
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
A compile-time token for interfaces. Created by `useInterface<T>()`.
|
|
48
|
+
|
|
49
|
+
::: warning Internal Type
|
|
50
|
+
You don't create this directly. Use `useInterface<T>()` instead.
|
|
51
|
+
:::
|
|
52
|
+
|
|
53
|
+
## PropertyToken
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
type PropertyToken<T, C = unknown> = {
|
|
57
|
+
__brand: 'PropertyToken';
|
|
58
|
+
__type: T;
|
|
59
|
+
__class: C;
|
|
60
|
+
__name: string;
|
|
61
|
+
};
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
A token for primitive values bound to a specific class parameter. Created by `useProperty<T>(Class, 'param')`.
|
|
65
|
+
|
|
66
|
+
## Provider
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
type Provider<T> = Constructor<T> | Factory<T>;
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
What creates instances:
|
|
73
|
+
|
|
74
|
+
- **Constructor**: A class to instantiate
|
|
75
|
+
- **Factory**: A function that creates the instance
|
|
76
|
+
|
|
77
|
+
## Factory
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
type Factory<T> = (container: Container) => T;
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
A function that receives the container and returns an instance.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
const myFactory: Factory<IConfig> = (container) => ({
|
|
87
|
+
apiUrl: process.env.API_URL,
|
|
88
|
+
logger: container.resolve(useInterface<ILogger>())
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Injection
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
interface Injection<T = any> {
|
|
96
|
+
token: Token<T>;
|
|
97
|
+
provider?: Provider<T>;
|
|
98
|
+
useFactory?: boolean;
|
|
99
|
+
lifecycle?: Lifecycle;
|
|
100
|
+
scoped?: boolean;
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
A single injection definition:
|
|
105
|
+
|
|
106
|
+
| Property | Type | Description |
|
|
107
|
+
|----------|------|-------------|
|
|
108
|
+
| `token` | `Token<T>` | **Required**. What to register |
|
|
109
|
+
| `provider` | `Provider<T>` | Optional. What provides the instance |
|
|
110
|
+
| `useFactory` | `boolean` | Explicit factory flag |
|
|
111
|
+
| `lifecycle` | `Lifecycle` | `'singleton'` (default) or `'transient'` |
|
|
112
|
+
| `scoped` | `boolean` | If `true`, resolve locally (override parent) |
|
|
113
|
+
|
|
114
|
+
## PartialConfig
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
interface PartialConfig {
|
|
118
|
+
injections?: Injection[];
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
A reusable configuration block.
|
|
123
|
+
|
|
124
|
+
## BuilderConfig
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
interface BuilderConfig extends PartialConfig {
|
|
128
|
+
name?: string;
|
|
129
|
+
extends?: PartialConfig[];
|
|
130
|
+
useContainer?: any;
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Main configuration for a container:
|
|
135
|
+
|
|
136
|
+
| Property | Type | Description |
|
|
137
|
+
|----------|------|-------------|
|
|
138
|
+
| `name` | `string` | Container name (for debugging) |
|
|
139
|
+
| `injections` | `Injection[]` | List of injections |
|
|
140
|
+
| `extends` | `PartialConfig[]` | Inherit from partials |
|
|
141
|
+
| `useContainer` | `any` | Parent container |
|
|
142
|
+
|
|
143
|
+
## Container
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
interface Container {
|
|
147
|
+
resolve<T>(token: Token<T>): T;
|
|
148
|
+
createChildContainer(): Container;
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The generated container interface:
|
|
153
|
+
|
|
154
|
+
| Method | Description |
|
|
155
|
+
|--------|-------------|
|
|
156
|
+
| `resolve<T>(token)` | Resolve a service by token |
|
|
157
|
+
| `createChildContainer()` | Create a child container |
|
|
158
|
+
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# Basic Usage
|
|
2
|
+
|
|
3
|
+
Learn the fundamental concepts and injection patterns in Neo-Syringe.
|
|
4
|
+
|
|
5
|
+
## Container Configuration
|
|
6
|
+
|
|
7
|
+
All configuration is done through `defineBuilderConfig`:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { defineBuilderConfig } from '@djodjonx/neo-syringe';
|
|
11
|
+
|
|
12
|
+
export const container = defineBuilderConfig({
|
|
13
|
+
name: 'MyContainer', // Optional: container name for debugging
|
|
14
|
+
injections: [ // Required: list of injections
|
|
15
|
+
// ... your injections
|
|
16
|
+
],
|
|
17
|
+
extends: [], // Optional: inherit from partials
|
|
18
|
+
useContainer: undefined // Optional: parent container
|
|
19
|
+
});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Injection Types
|
|
23
|
+
|
|
24
|
+
### Class Token (Autowire)
|
|
25
|
+
|
|
26
|
+
The simplest form - register a class and let Neo-Syringe resolve its dependencies:
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
class UserRepository {
|
|
30
|
+
findAll() { return []; }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
class UserService {
|
|
34
|
+
constructor(private repo: UserRepository) {}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const container = defineBuilderConfig({
|
|
38
|
+
injections: [
|
|
39
|
+
{ token: UserRepository },
|
|
40
|
+
{ token: UserService } // Dependencies auto-resolved
|
|
41
|
+
]
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Interface Token
|
|
46
|
+
|
|
47
|
+
Use `useInterface<T>()` to bind interfaces to implementations:
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { useInterface } from '@djodjonx/neo-syringe';
|
|
51
|
+
|
|
52
|
+
interface ILogger {
|
|
53
|
+
log(msg: string): void;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
class ConsoleLogger implements ILogger {
|
|
57
|
+
log(msg: string) { console.log(msg); }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
class FileLogger implements ILogger {
|
|
61
|
+
log(msg: string) { /* write to file */ }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const container = defineBuilderConfig({
|
|
65
|
+
injections: [
|
|
66
|
+
{ token: useInterface<ILogger>(), provider: ConsoleLogger }
|
|
67
|
+
]
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Resolve by interface
|
|
71
|
+
const logger = container.resolve(useInterface<ILogger>());
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
::: tip Automatic ID Generation
|
|
75
|
+
`useInterface<ILogger>()` generates a unique string ID at compile-time. You don't need to manage Symbols manually!
|
|
76
|
+
:::
|
|
77
|
+
|
|
78
|
+
### Explicit Provider
|
|
79
|
+
|
|
80
|
+
Override the default implementation:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
class UserService {
|
|
84
|
+
// ...
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
class MockUserService extends UserService {
|
|
88
|
+
// Test implementation
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export const container = defineBuilderConfig({
|
|
92
|
+
injections: [
|
|
93
|
+
{ token: UserService, provider: MockUserService }
|
|
94
|
+
]
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Factory Provider
|
|
99
|
+
|
|
100
|
+
Use factory functions for dynamic instantiation:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
// Arrow functions are auto-detected as factories
|
|
104
|
+
{
|
|
105
|
+
token: useInterface<IConfig>(),
|
|
106
|
+
provider: (container) => ({
|
|
107
|
+
apiUrl: process.env.API_URL ?? 'http://localhost',
|
|
108
|
+
timeout: 5000
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Factory with dependencies
|
|
113
|
+
{
|
|
114
|
+
token: useInterface<IService>(),
|
|
115
|
+
provider: (container) => {
|
|
116
|
+
const logger = container.resolve(useInterface<ILogger>());
|
|
117
|
+
const config = container.resolve(useInterface<IConfig>());
|
|
118
|
+
return new MyService(logger, config);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Explicit factory flag (for non-arrow functions)
|
|
123
|
+
{
|
|
124
|
+
token: useInterface<IDatabase>(),
|
|
125
|
+
provider: createDatabaseConnection,
|
|
126
|
+
useFactory: true
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Property Token
|
|
131
|
+
|
|
132
|
+
Inject primitive values (string, number, boolean) while keeping classes pure:
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
import { useProperty } from '@djodjonx/neo-syringe';
|
|
136
|
+
|
|
137
|
+
// Pure class - no DI imports!
|
|
138
|
+
class ApiService {
|
|
139
|
+
constructor(
|
|
140
|
+
private apiUrl: string,
|
|
141
|
+
private timeout: number,
|
|
142
|
+
private debug: boolean
|
|
143
|
+
) {}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Define property tokens
|
|
147
|
+
const apiUrl = useProperty<string>(ApiService, 'apiUrl');
|
|
148
|
+
const timeout = useProperty<number>(ApiService, 'timeout');
|
|
149
|
+
const debug = useProperty<boolean>(ApiService, 'debug');
|
|
150
|
+
|
|
151
|
+
export const container = defineBuilderConfig({
|
|
152
|
+
injections: [
|
|
153
|
+
{ token: apiUrl, provider: () => process.env.API_URL ?? 'http://localhost' },
|
|
154
|
+
{ token: timeout, provider: () => 5000 },
|
|
155
|
+
{ token: debug, provider: () => process.env.NODE_ENV === 'development' },
|
|
156
|
+
{ token: ApiService } // Primitives auto-wired!
|
|
157
|
+
]
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
::: info Type Safety
|
|
162
|
+
`useProperty(ApiService, 'apiUrl')` creates a unique token scoped to that specific class parameter. This means `useProperty(ServiceA, 'url')` ≠ `useProperty(ServiceB, 'url')`.
|
|
163
|
+
:::
|
|
164
|
+
|
|
165
|
+
## Resolving Services
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
import { container } from './container';
|
|
169
|
+
import { UserService } from './services/user.service';
|
|
170
|
+
import { useInterface } from '@djodjonx/neo-syringe';
|
|
171
|
+
import type { ILogger } from './services/logger';
|
|
172
|
+
|
|
173
|
+
// Resolve by class
|
|
174
|
+
const userService = container.resolve(UserService);
|
|
175
|
+
|
|
176
|
+
// Resolve by interface
|
|
177
|
+
const logger = container.resolve(useInterface<ILogger>());
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Partials (Modular Configuration)
|
|
181
|
+
|
|
182
|
+
Split configuration into reusable modules:
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
// logging.partial.ts
|
|
186
|
+
import { definePartialConfig, useInterface } from '@djodjonx/neo-syringe';
|
|
187
|
+
|
|
188
|
+
export const loggingConfig = definePartialConfig({
|
|
189
|
+
injections: [
|
|
190
|
+
{ token: useInterface<ILogger>(), provider: ConsoleLogger }
|
|
191
|
+
]
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// database.partial.ts
|
|
195
|
+
export const databaseConfig = definePartialConfig({
|
|
196
|
+
injections: [
|
|
197
|
+
{ token: useInterface<IDatabase>(), provider: PostgresDatabase }
|
|
198
|
+
]
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// container.ts
|
|
202
|
+
export const container = defineBuilderConfig({
|
|
203
|
+
extends: [loggingConfig, databaseConfig], // Inherit injections
|
|
204
|
+
injections: [
|
|
205
|
+
{ token: UserService },
|
|
206
|
+
{ token: OrderService }
|
|
207
|
+
]
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Complete Example
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
// interfaces.ts
|
|
215
|
+
export interface ILogger {
|
|
216
|
+
log(msg: string): void;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export interface IUserRepository {
|
|
220
|
+
findById(id: string): User | null;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// implementations.ts
|
|
224
|
+
export class ConsoleLogger implements ILogger {
|
|
225
|
+
log(msg: string) { console.log(`[LOG] ${msg}`); }
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export class InMemoryUserRepository implements IUserRepository {
|
|
229
|
+
private users = new Map<string, User>();
|
|
230
|
+
|
|
231
|
+
findById(id: string) {
|
|
232
|
+
return this.users.get(id) ?? null;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// services.ts
|
|
237
|
+
export class UserService {
|
|
238
|
+
constructor(
|
|
239
|
+
private logger: ILogger,
|
|
240
|
+
private repo: IUserRepository
|
|
241
|
+
) {}
|
|
242
|
+
|
|
243
|
+
getUser(id: string) {
|
|
244
|
+
this.logger.log(`Fetching user ${id}`);
|
|
245
|
+
return this.repo.findById(id);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// container.ts
|
|
250
|
+
import { defineBuilderConfig, useInterface } from '@djodjonx/neo-syringe';
|
|
251
|
+
|
|
252
|
+
export const container = defineBuilderConfig({
|
|
253
|
+
name: 'AppContainer',
|
|
254
|
+
injections: [
|
|
255
|
+
{ token: useInterface<ILogger>(), provider: ConsoleLogger },
|
|
256
|
+
{ token: useInterface<IUserRepository>(), provider: InMemoryUserRepository },
|
|
257
|
+
{ token: UserService }
|
|
258
|
+
]
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// main.ts
|
|
262
|
+
import { container } from './container';
|
|
263
|
+
|
|
264
|
+
const userService = container.resolve(UserService);
|
|
265
|
+
const user = userService.getUser('123');
|
|
266
|
+
```
|
|
267
|
+
|