@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,203 @@
|
|
|
1
|
+
# IDE Plugin
|
|
2
|
+
|
|
3
|
+
Get real-time error detection in your editor.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The Neo-Syringe LSP plugin integrates with TypeScript's language service to provide:
|
|
8
|
+
|
|
9
|
+
- 🔴 **Circular dependency detection** - Instant feedback when you create cycles
|
|
10
|
+
- 🔴 **Missing binding detection** - Errors when dependencies aren't registered
|
|
11
|
+
- 🔴 **Duplicate registration detection** - Warnings for conflicts
|
|
12
|
+
- 💡 **Suggestions** - Helpful tips to fix issues
|
|
13
|
+
|
|
14
|
+
## Setup
|
|
15
|
+
|
|
16
|
+
### Step 1: Add to `tsconfig.json`
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"compilerOptions": {
|
|
21
|
+
"plugins": [
|
|
22
|
+
{ "name": "@djodjonx/neo-syringe/lsp" }
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Step 2: Use Workspace TypeScript
|
|
29
|
+
|
|
30
|
+
The plugin only works with the workspace TypeScript version.
|
|
31
|
+
|
|
32
|
+
#### VS Code
|
|
33
|
+
|
|
34
|
+
1. Open Command Palette: `Ctrl+Shift+P` (or `Cmd+Shift+P` on Mac)
|
|
35
|
+
2. Search for: **"TypeScript: Select TypeScript Version"**
|
|
36
|
+
3. Select: **"Use Workspace Version"**
|
|
37
|
+
|
|
38
|
+
#### JetBrains IDEs (WebStorm, IntelliJ)
|
|
39
|
+
|
|
40
|
+
1. Go to **Settings** → **Languages & Frameworks** → **TypeScript**
|
|
41
|
+
2. Set TypeScript version to **"Project"** or point to `node_modules/typescript`
|
|
42
|
+
|
|
43
|
+
#### Neovim (with nvim-lspconfig)
|
|
44
|
+
|
|
45
|
+
```lua
|
|
46
|
+
require('lspconfig').tsserver.setup {
|
|
47
|
+
init_options = {
|
|
48
|
+
plugins = {
|
|
49
|
+
{
|
|
50
|
+
name = "@djodjonx/neo-syringe/lsp",
|
|
51
|
+
location = "./node_modules/@djodjonx/neo-syringe/dist/lsp"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Step 3: Restart TypeScript Server
|
|
59
|
+
|
|
60
|
+
After configuration, restart the TypeScript server:
|
|
61
|
+
|
|
62
|
+
- **VS Code**: `Ctrl+Shift+P` → "TypeScript: Restart TS Server"
|
|
63
|
+
- **WebStorm**: File → Invalidate Caches / Restart
|
|
64
|
+
|
|
65
|
+
## Detected Errors
|
|
66
|
+
|
|
67
|
+
### Circular Dependency
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
class A {
|
|
71
|
+
constructor(private b: B) {}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
class B {
|
|
75
|
+
constructor(private a: A) {} // 🔴 Cycle!
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const container = defineBuilderConfig({
|
|
79
|
+
injections: [
|
|
80
|
+
{ token: A },
|
|
81
|
+
{ token: B }
|
|
82
|
+
]
|
|
83
|
+
});
|
|
84
|
+
// Error: [Neo-Syringe] Circular dependency detected: A -> B -> A
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Missing Binding
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
interface ILogger {
|
|
91
|
+
log(msg: string): void;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
class UserService {
|
|
95
|
+
constructor(private logger: ILogger) {}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export const container = defineBuilderConfig({
|
|
99
|
+
injections: [
|
|
100
|
+
{ token: UserService } // 🔴 ILogger not registered!
|
|
101
|
+
]
|
|
102
|
+
});
|
|
103
|
+
// Error: [Neo-Syringe] Missing binding: 'UserService' depends on 'ILogger',
|
|
104
|
+
// but no provider registered.
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Duplicate Registration
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
const parent = defineBuilderConfig({
|
|
111
|
+
injections: [
|
|
112
|
+
{ token: useInterface<ILogger>(), provider: ConsoleLogger }
|
|
113
|
+
]
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
export const child = defineBuilderConfig({
|
|
117
|
+
useContainer: parent,
|
|
118
|
+
injections: [
|
|
119
|
+
{ token: useInterface<ILogger>(), provider: FileLogger } // 🔴 Duplicate!
|
|
120
|
+
]
|
|
121
|
+
});
|
|
122
|
+
// Error: [Neo-Syringe] Duplicate registration: 'ILogger' is already registered
|
|
123
|
+
// in the parent container. Use 'scoped: true' to override intentionally.
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Screenshots
|
|
127
|
+
|
|
128
|
+
### Error in Editor
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
132
|
+
│ container.ts │
|
|
133
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
134
|
+
│ │
|
|
135
|
+
│ export const container = defineBuilderConfig({ │
|
|
136
|
+
│ injections: [ │
|
|
137
|
+
│ { token: UserService } │
|
|
138
|
+
│ ~~~~~~~~~~~ │
|
|
139
|
+
│ ▲ │
|
|
140
|
+
│ │ │
|
|
141
|
+
│ ┌────────────┴───────────────────────────────────────────────┐ │
|
|
142
|
+
│ │ 🔴 [Neo-Syringe] Missing binding: 'UserService' depends │ │
|
|
143
|
+
│ │ on 'ILogger', but no provider registered. │ │
|
|
144
|
+
│ └────────────────────────────────────────────────────────────┘ │
|
|
145
|
+
│ │
|
|
146
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Quick Fix Available
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
┌────────────────────────────────────────────┐
|
|
153
|
+
│ Quick Fix │
|
|
154
|
+
├────────────────────────────────────────────┤
|
|
155
|
+
│ 💡 Add missing injection for ILogger │
|
|
156
|
+
│ 💡 Mark as optional dependency │
|
|
157
|
+
│ 💡 Ignore this error │
|
|
158
|
+
└────────────────────────────────────────────┘
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Troubleshooting
|
|
162
|
+
|
|
163
|
+
### Plugin Not Working
|
|
164
|
+
|
|
165
|
+
1. **Check TypeScript version**: Must use workspace version
|
|
166
|
+
2. **Check tsconfig.json**: Plugin must be in `compilerOptions.plugins`
|
|
167
|
+
3. **Restart TS Server**: Changes require restart
|
|
168
|
+
4. **Check node_modules**: Package must be installed
|
|
169
|
+
|
|
170
|
+
### Errors Not Showing
|
|
171
|
+
|
|
172
|
+
1. **Save the file**: Some editors need file save to trigger
|
|
173
|
+
2. **Check file extension**: Only `.ts` and `.tsx` files
|
|
174
|
+
3. **Check if file has `defineBuilderConfig`**: Plugin only analyzes DI files
|
|
175
|
+
|
|
176
|
+
### False Positives
|
|
177
|
+
|
|
178
|
+
If the plugin reports errors that shouldn't exist:
|
|
179
|
+
|
|
180
|
+
1. Check that all imports are correct
|
|
181
|
+
2. Ensure interface names match
|
|
182
|
+
3. Try restarting the TypeScript server
|
|
183
|
+
|
|
184
|
+
## Configuration
|
|
185
|
+
|
|
186
|
+
Currently, the plugin uses default settings. Future versions may support:
|
|
187
|
+
|
|
188
|
+
```json
|
|
189
|
+
{
|
|
190
|
+
"compilerOptions": {
|
|
191
|
+
"plugins": [
|
|
192
|
+
{
|
|
193
|
+
"name": "@djodjonx/neo-syringe/lsp",
|
|
194
|
+
"options": {
|
|
195
|
+
"strictMode": true,
|
|
196
|
+
"warnOnUnusedProviders": true
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
]
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# Injection Types
|
|
2
|
+
|
|
3
|
+
All the ways to define and inject dependencies in Neo-Syringe.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
| Type | Syntax | Use Case |
|
|
8
|
+
|------|--------|----------|
|
|
9
|
+
| **Class** | `{ token: MyClass }` | Simple autowiring |
|
|
10
|
+
| **Interface** | `{ token: useInterface<IFoo>(), provider: Foo }` | Abstraction |
|
|
11
|
+
| **Explicit** | `{ token: MyClass, provider: OtherClass }` | Override default |
|
|
12
|
+
| **Factory** | `{ token: X, provider: (c) => ... }` | Dynamic creation |
|
|
13
|
+
| **Property** | `{ token: useProperty(Class, 'param') }` | Primitives |
|
|
14
|
+
|
|
15
|
+
## Class Token
|
|
16
|
+
|
|
17
|
+
Register a class directly. Dependencies are resolved automatically from constructor parameters.
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
class Repository {
|
|
21
|
+
findAll() { return []; }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class Service {
|
|
25
|
+
constructor(private repo: Repository) {}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const container = defineBuilderConfig({
|
|
29
|
+
injections: [
|
|
30
|
+
{ token: Repository },
|
|
31
|
+
{ token: Service } // Repository injected automatically
|
|
32
|
+
]
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Interface Token
|
|
37
|
+
|
|
38
|
+
Bind an interface to a concrete implementation using `useInterface<T>()`.
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { useInterface } from '@djodjonx/neo-syringe';
|
|
42
|
+
|
|
43
|
+
interface ICache {
|
|
44
|
+
get(key: string): any;
|
|
45
|
+
set(key: string, value: any): void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
class RedisCache implements ICache {
|
|
49
|
+
get(key: string) { /* ... */ }
|
|
50
|
+
set(key: string, value: any) { /* ... */ }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class MemoryCache implements ICache {
|
|
54
|
+
private store = new Map();
|
|
55
|
+
get(key: string) { return this.store.get(key); }
|
|
56
|
+
set(key: string, value: any) { this.store.set(key, value); }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Production
|
|
60
|
+
const prodContainer = defineBuilderConfig({
|
|
61
|
+
injections: [
|
|
62
|
+
{ token: useInterface<ICache>(), provider: RedisCache }
|
|
63
|
+
]
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Development
|
|
67
|
+
const devContainer = defineBuilderConfig({
|
|
68
|
+
injections: [
|
|
69
|
+
{ token: useInterface<ICache>(), provider: MemoryCache }
|
|
70
|
+
]
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### How It Works
|
|
75
|
+
|
|
76
|
+
At compile-time, `useInterface<ICache>()` is replaced with a unique string ID:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
// Before (your code)
|
|
80
|
+
{ token: useInterface<ICache>(), provider: RedisCache }
|
|
81
|
+
|
|
82
|
+
// After (generated)
|
|
83
|
+
{ token: "ICache", provider: RedisCache }
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Explicit Provider
|
|
87
|
+
|
|
88
|
+
Override which class is used when resolving a token:
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
class UserService {
|
|
92
|
+
validate(user: User) { /* production logic */ }
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
class TestUserService extends UserService {
|
|
96
|
+
validate(user: User) { return true; } // Skip validation in tests
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Test container
|
|
100
|
+
const testContainer = defineBuilderConfig({
|
|
101
|
+
injections: [
|
|
102
|
+
{ token: UserService, provider: TestUserService }
|
|
103
|
+
]
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const service = testContainer.resolve(UserService);
|
|
107
|
+
// service instanceof TestUserService === true
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Factory Provider
|
|
111
|
+
|
|
112
|
+
Use factory functions when you need:
|
|
113
|
+
- Dynamic configuration
|
|
114
|
+
- Container access for conditional resolution
|
|
115
|
+
- External resource initialization
|
|
116
|
+
|
|
117
|
+
### Arrow Function (Auto-detected)
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
{
|
|
121
|
+
token: useInterface<IConfig>(),
|
|
122
|
+
provider: () => ({
|
|
123
|
+
apiUrl: process.env.API_URL,
|
|
124
|
+
environment: process.env.NODE_ENV
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Factory with Container Access
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
{
|
|
133
|
+
token: useInterface<IHttpClient>(),
|
|
134
|
+
provider: (container) => {
|
|
135
|
+
const config = container.resolve(useInterface<IConfig>());
|
|
136
|
+
const logger = container.resolve(useInterface<ILogger>());
|
|
137
|
+
|
|
138
|
+
return new HttpClient({
|
|
139
|
+
baseUrl: config.apiUrl,
|
|
140
|
+
logger,
|
|
141
|
+
timeout: config.timeout
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Explicit Factory Flag
|
|
148
|
+
|
|
149
|
+
For regular functions that aren't arrow functions:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
function createDatabaseConnection(container: Container) {
|
|
153
|
+
const config = container.resolve(useInterface<IDbConfig>());
|
|
154
|
+
return new DatabaseConnection(config.connectionString);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
{
|
|
158
|
+
token: useInterface<IDatabase>(),
|
|
159
|
+
provider: createDatabaseConnection,
|
|
160
|
+
useFactory: true // Required for non-arrow functions
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Property Token
|
|
165
|
+
|
|
166
|
+
Inject primitive values (string, number, boolean) into class constructors.
|
|
167
|
+
|
|
168
|
+
### The Problem
|
|
169
|
+
|
|
170
|
+
How do you inject configuration values without polluting your classes?
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
// ❌ Coupled to DI
|
|
174
|
+
class ApiService {
|
|
175
|
+
constructor(@inject('API_URL') private apiUrl: string) {}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ❌ Coupled to environment
|
|
179
|
+
class ApiService {
|
|
180
|
+
private apiUrl = process.env.API_URL;
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### The Solution
|
|
185
|
+
|
|
186
|
+
Use `useProperty<T>(Class, 'paramName')`:
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
import { useProperty } from '@djodjonx/neo-syringe';
|
|
190
|
+
|
|
191
|
+
// ✅ Pure class
|
|
192
|
+
class ApiService {
|
|
193
|
+
constructor(
|
|
194
|
+
private apiUrl: string,
|
|
195
|
+
private timeout: number,
|
|
196
|
+
private retryCount: number
|
|
197
|
+
) {}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Property tokens
|
|
201
|
+
const apiUrl = useProperty<string>(ApiService, 'apiUrl');
|
|
202
|
+
const timeout = useProperty<number>(ApiService, 'timeout');
|
|
203
|
+
const retryCount = useProperty<number>(ApiService, 'retryCount');
|
|
204
|
+
|
|
205
|
+
export const container = defineBuilderConfig({
|
|
206
|
+
injections: [
|
|
207
|
+
{ token: apiUrl, provider: () => 'https://api.example.com' },
|
|
208
|
+
{ token: timeout, provider: () => 5000 },
|
|
209
|
+
{ token: retryCount, provider: () => 3 },
|
|
210
|
+
{ token: ApiService } // All primitives injected!
|
|
211
|
+
]
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Scoped to Class
|
|
216
|
+
|
|
217
|
+
Property tokens are scoped to their class, avoiding collisions:
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
const serviceAUrl = useProperty<string>(ServiceA, 'url');
|
|
221
|
+
const serviceBUrl = useProperty<string>(ServiceB, 'url');
|
|
222
|
+
|
|
223
|
+
// serviceAUrl !== serviceBUrl (different tokens!)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Validated by LSP
|
|
227
|
+
|
|
228
|
+
The IDE plugin validates property names:
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
const invalid = useProperty<string>(ApiService, 'invalidParam');
|
|
232
|
+
// 🔴 Error: Parameter 'invalidParam' does not exist in ApiService constructor
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Combined Example
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
import { defineBuilderConfig, useInterface, useProperty } from '@djodjonx/neo-syringe';
|
|
239
|
+
|
|
240
|
+
// Interfaces
|
|
241
|
+
interface ILogger { log(msg: string): void; }
|
|
242
|
+
interface IHttpClient { get(url: string): Promise<any>; }
|
|
243
|
+
|
|
244
|
+
// Implementations
|
|
245
|
+
class ConsoleLogger implements ILogger {
|
|
246
|
+
log(msg: string) { console.log(msg); }
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Pure service class
|
|
250
|
+
class ApiService {
|
|
251
|
+
constructor(
|
|
252
|
+
private logger: ILogger,
|
|
253
|
+
private http: IHttpClient,
|
|
254
|
+
private baseUrl: string,
|
|
255
|
+
private timeout: number
|
|
256
|
+
) {}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Tokens
|
|
260
|
+
const baseUrl = useProperty<string>(ApiService, 'baseUrl');
|
|
261
|
+
const timeout = useProperty<number>(ApiService, 'timeout');
|
|
262
|
+
|
|
263
|
+
export const container = defineBuilderConfig({
|
|
264
|
+
name: 'AppContainer',
|
|
265
|
+
injections: [
|
|
266
|
+
// Interface bindings
|
|
267
|
+
{ token: useInterface<ILogger>(), provider: ConsoleLogger },
|
|
268
|
+
|
|
269
|
+
// Factory for complex initialization
|
|
270
|
+
{
|
|
271
|
+
token: useInterface<IHttpClient>(),
|
|
272
|
+
provider: (container) => {
|
|
273
|
+
const logger = container.resolve(useInterface<ILogger>());
|
|
274
|
+
return new HttpClient(logger);
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
|
|
278
|
+
// Primitive values
|
|
279
|
+
{ token: baseUrl, provider: () => process.env.API_URL ?? 'http://localhost' },
|
|
280
|
+
{ token: timeout, provider: () => 5000 },
|
|
281
|
+
|
|
282
|
+
// Service with mixed dependencies
|
|
283
|
+
{ token: ApiService }
|
|
284
|
+
]
|
|
285
|
+
});
|
|
286
|
+
```
|
|
287
|
+
|