@decaf-ts/injectable-decorators 1.6.8 → 1.6.10
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 +147 -163
- package/dist/injectable-decorators.cjs +105 -91
- package/dist/injectable-decorators.esm.cjs +101 -88
- package/lib/Metadata.cjs +3 -0
- package/lib/Metadata.d.ts +8 -0
- package/lib/decorators.cjs +100 -86
- package/lib/decorators.d.ts +3 -9
- package/lib/esm/Metadata.d.ts +8 -0
- package/lib/esm/Metadata.js +2 -0
- package/lib/esm/decorators.d.ts +3 -9
- package/lib/esm/decorators.js +98 -86
- package/lib/esm/index.d.ts +1 -1
- package/lib/esm/index.js +4 -2
- package/lib/esm/overrides.d.ts +1 -0
- package/lib/esm/overrides.js +25 -0
- package/lib/index.cjs +4 -2
- package/lib/index.d.ts +1 -1
- package/lib/overrides.cjs +27 -0
- package/lib/overrides.d.ts +1 -0
- package/package.json +11 -3
package/README.md
CHANGED
|
@@ -81,232 +81,216 @@ Unlike more complex DI frameworks, this library doesn't require extensive config
|
|
|
81
81
|
|
|
82
82
|
## Basic Usage Examples
|
|
83
83
|
|
|
84
|
-
###
|
|
84
|
+
### 1) Mark a class as injectable and get it from the registry
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
Description: Define a class with @injectable() so it becomes available through the central registry. Creating with new returns the instance managed by the registry.
|
|
87
87
|
|
|
88
88
|
```typescript
|
|
89
|
-
import
|
|
89
|
+
import 'reflect-metadata';
|
|
90
|
+
import { injectable, Injectables } from 'injectable-decorators';
|
|
90
91
|
|
|
91
92
|
@injectable()
|
|
92
|
-
class
|
|
93
|
-
|
|
94
|
-
console.log(`[LOG]: ${message}`);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
error(message: string): void {
|
|
98
|
-
console.error(`[ERROR]: ${message}`);
|
|
99
|
-
}
|
|
93
|
+
class InitialObject {
|
|
94
|
+
doSomething() { return 5; }
|
|
100
95
|
}
|
|
101
96
|
|
|
102
|
-
|
|
103
|
-
|
|
97
|
+
const obj = new InitialObject();
|
|
98
|
+
const same = Injectables.get(InitialObject);
|
|
99
|
+
// obj and same refer to the same instance (singleton by default)
|
|
104
100
|
```
|
|
105
101
|
|
|
106
|
-
###
|
|
102
|
+
### 2) Inject a dependency into a property
|
|
107
103
|
|
|
108
|
-
|
|
104
|
+
Description: Use @inject() on a typed property. The instance is created lazily when the property is first accessed and cached thereafter.
|
|
109
105
|
|
|
110
106
|
```typescript
|
|
111
|
-
import
|
|
112
|
-
import {
|
|
107
|
+
import 'reflect-metadata';
|
|
108
|
+
import { injectable, inject, Injectables } from 'injectable-decorators';
|
|
113
109
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
private logger!: LoggerService;
|
|
110
|
+
@injectable()
|
|
111
|
+
class SomeService { value = 5; }
|
|
117
112
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
this.logger.log(`User ${username} created successfully`);
|
|
122
|
-
}
|
|
113
|
+
class Controller {
|
|
114
|
+
@inject()
|
|
115
|
+
service!: SomeService; // non-null assertion because it's set outside the constructor
|
|
123
116
|
}
|
|
124
117
|
|
|
125
|
-
|
|
126
|
-
//
|
|
118
|
+
const c = new Controller();
|
|
119
|
+
console.log(c.service.value); // 5
|
|
120
|
+
console.log(c.service === Injectables.get(SomeService)); // true
|
|
127
121
|
```
|
|
128
122
|
|
|
129
|
-
###
|
|
123
|
+
### 3) Use a custom category (string) for minification or upcasting
|
|
130
124
|
|
|
131
|
-
|
|
125
|
+
Description: Provide a stable name when class names may change (e.g., minification) or to upcast through a base type.
|
|
132
126
|
|
|
133
127
|
```typescript
|
|
134
|
-
import
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
128
|
+
import 'reflect-metadata';
|
|
129
|
+
import { injectable, inject, singleton } from 'injectable-decorators';
|
|
130
|
+
|
|
131
|
+
@singleton()
|
|
132
|
+
class AAA { a = 'aaa'; }
|
|
133
|
+
|
|
134
|
+
@injectable('AAA')
|
|
135
|
+
class BBB extends AAA { b = 'bbb'; }
|
|
143
136
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if (this.auth.authenticate(username, password)) {
|
|
150
|
-
console.log('Login successful');
|
|
151
|
-
} else {
|
|
152
|
-
console.log('Login failed');
|
|
153
|
-
}
|
|
154
|
-
}
|
|
137
|
+
const b = new BBB();
|
|
138
|
+
|
|
139
|
+
class Host {
|
|
140
|
+
@inject()
|
|
141
|
+
repo!: AAA; // resolves to the instance registered under category 'AAA'
|
|
155
142
|
}
|
|
143
|
+
|
|
144
|
+
const h = new Host();
|
|
145
|
+
console.log(h.repo === b); // true
|
|
156
146
|
```
|
|
157
147
|
|
|
158
|
-
###
|
|
148
|
+
### 4) Inject by explicit category (string)
|
|
159
149
|
|
|
160
|
-
|
|
150
|
+
Description: When a different string category was used at registration, pass that string to @inject.
|
|
161
151
|
|
|
162
152
|
```typescript
|
|
163
|
-
import
|
|
153
|
+
import 'reflect-metadata';
|
|
154
|
+
import { inject, singleton } from 'injectable-decorators';
|
|
164
155
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
156
|
+
class DDD { a = 'aaa'; }
|
|
157
|
+
|
|
158
|
+
@singleton('EEE')
|
|
159
|
+
class CCC extends DDD { b = 'bbb'; }
|
|
160
|
+
|
|
161
|
+
const instance = new CCC();
|
|
162
|
+
|
|
163
|
+
class Holder {
|
|
164
|
+
@inject('EEE')
|
|
165
|
+
repo!: CCC;
|
|
175
166
|
}
|
|
176
167
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
168
|
+
const h = new Holder();
|
|
169
|
+
console.log(h.repo === instance); // true
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### 5) Map one constructor to another and inject by constructor
|
|
173
|
+
|
|
174
|
+
Description: You can register an injectable using another constructor as the category, then inject it by that constructor.
|
|
182
175
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
176
|
+
```typescript
|
|
177
|
+
import 'reflect-metadata';
|
|
178
|
+
import { injectable, inject } from 'injectable-decorators';
|
|
179
|
+
|
|
180
|
+
class Token {}
|
|
186
181
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
182
|
+
@injectable(Token, { callback: (original) => original })
|
|
183
|
+
class Impl {
|
|
184
|
+
id = 1;
|
|
185
|
+
}
|
|
190
186
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
187
|
+
class UsesImpl {
|
|
188
|
+
@inject(Token)
|
|
189
|
+
object!: Impl; // injects the instance registered under Token (Impl instance)
|
|
194
190
|
}
|
|
191
|
+
|
|
192
|
+
const u = new UsesImpl();
|
|
193
|
+
console.log(u.object instanceof Impl); // true
|
|
195
194
|
```
|
|
196
195
|
|
|
197
|
-
###
|
|
196
|
+
### 6) Non-singleton injectables with @onDemand and passing constructor args
|
|
198
197
|
|
|
199
|
-
|
|
198
|
+
Description: Use @onDemand() so each injection produces a fresh instance. You can pass args for construction via @inject({ args }).
|
|
200
199
|
|
|
201
200
|
```typescript
|
|
202
|
-
import
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
Injectables.register(databaseConnection, 'DatabaseConnection');
|
|
211
|
-
|
|
212
|
-
// Retrieve the instance elsewhere in your code
|
|
213
|
-
class QueryService {
|
|
214
|
-
private db = Injectables.get<typeof databaseConnection>('DatabaseConnection');
|
|
215
|
-
|
|
216
|
-
async executeQuery(sql: string): Promise<any[]> {
|
|
217
|
-
if (!this.db) {
|
|
218
|
-
throw new Error('Database connection not available');
|
|
219
|
-
}
|
|
220
|
-
return this.db.query(sql);
|
|
221
|
-
}
|
|
201
|
+
import 'reflect-metadata';
|
|
202
|
+
import { onDemand, inject } from 'injectable-decorators';
|
|
203
|
+
|
|
204
|
+
@onDemand()
|
|
205
|
+
class FreshObject {
|
|
206
|
+
constructor(public a?: string, public b?: string) {}
|
|
222
207
|
}
|
|
208
|
+
|
|
209
|
+
class ParentA {
|
|
210
|
+
@inject()
|
|
211
|
+
fresh!: FreshObject; // new instance per parent
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
class ParentB {
|
|
215
|
+
@inject({ args: ['x', 'y'] })
|
|
216
|
+
fresh!: FreshObject; // passes constructor args to on-demand instance
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const p1 = new ParentA();
|
|
220
|
+
const p2 = new ParentA();
|
|
221
|
+
console.log(p1.fresh !== p2.fresh); // true
|
|
222
|
+
|
|
223
|
+
const p3 = new ParentB();
|
|
224
|
+
console.log([p3.fresh.a, p3.fresh.b]); // ['x','y']
|
|
223
225
|
```
|
|
224
226
|
|
|
225
|
-
###
|
|
227
|
+
### 7) Transform an injected value
|
|
226
228
|
|
|
227
|
-
|
|
229
|
+
Description: Modify the resolved instance before assignment using a transformer.
|
|
228
230
|
|
|
229
231
|
```typescript
|
|
230
|
-
import
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
get<T>(name: string, ...args: any[]): T | undefined {
|
|
241
|
-
console.log(`Getting injectable: ${name}`);
|
|
242
|
-
return this.defaultRegistry.get<T>(name, ...args);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
register<T>(constructor: any, ...args: any[]): void {
|
|
246
|
-
console.log(`Registering injectable: ${args[0] || constructor.name}`);
|
|
247
|
-
return this.defaultRegistry.register(constructor, ...args);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
build<T>(obj: Record<string, any>, ...args: any[]): T {
|
|
251
|
-
console.log(`Building injectable: ${obj.name}`);
|
|
252
|
-
return this.defaultRegistry.build<T>(obj, ...args);
|
|
253
|
-
}
|
|
232
|
+
import 'reflect-metadata';
|
|
233
|
+
import { injectable, inject } from 'injectable-decorators';
|
|
234
|
+
|
|
235
|
+
@injectable('SomeOtherObject')
|
|
236
|
+
class SomeOtherObject { value() { return 10; } }
|
|
237
|
+
|
|
238
|
+
class Controller {
|
|
239
|
+
@inject({ transformer: (obj: SomeOtherObject, c: Controller) => '1' })
|
|
240
|
+
repo!: SomeOtherObject | string;
|
|
254
241
|
}
|
|
255
242
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
const customRegistry = new LoggingRegistry(new InjectableRegistryImp());
|
|
259
|
-
Injectables.setRegistry(customRegistry);
|
|
243
|
+
const c = new Controller();
|
|
244
|
+
console.log(c.repo); // '1'
|
|
260
245
|
```
|
|
261
246
|
|
|
262
|
-
###
|
|
247
|
+
### 8) Registry operations: reset and swapping registry
|
|
263
248
|
|
|
264
|
-
|
|
249
|
+
Description: Reset clears all registrations. Swapping the registry replaces the storage, losing previous entries.
|
|
265
250
|
|
|
266
251
|
```typescript
|
|
267
|
-
import { Injectables } from 'injectable-decorators';
|
|
252
|
+
import { Injectables, InjectableRegistryImp } from 'injectable-decorators';
|
|
253
|
+
|
|
254
|
+
// ensure something is registered
|
|
255
|
+
Injectables.get('SomeOtherObject');
|
|
268
256
|
|
|
269
|
-
//
|
|
257
|
+
// swap to a fresh registry
|
|
258
|
+
Injectables.setRegistry(new InjectableRegistryImp());
|
|
259
|
+
console.log(Injectables.get('SomeOtherObject')); // undefined
|
|
260
|
+
|
|
261
|
+
// reset to a new empty default registry
|
|
270
262
|
Injectables.reset();
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### 9) Singleton vs onDemand convenience decorators
|
|
266
|
+
|
|
267
|
+
Description: Prefer @singleton() to force single instance, or @onDemand() for new instance per retrieval.
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
import { singleton, onDemand } from 'injectable-decorators';
|
|
271
|
+
|
|
272
|
+
@singleton()
|
|
273
|
+
class OneOnly {}
|
|
271
274
|
|
|
272
|
-
|
|
273
|
-
|
|
275
|
+
@onDemand()
|
|
276
|
+
class Many {}
|
|
274
277
|
```
|
|
275
278
|
|
|
276
|
-
###
|
|
279
|
+
### 10) Utility helpers and constants
|
|
277
280
|
|
|
278
|
-
|
|
281
|
+
Description: Generate reflection keys and understand default config.
|
|
279
282
|
|
|
280
283
|
```typescript
|
|
281
|
-
import {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
instance.setLogLevel('debug');
|
|
286
|
-
instance.enableConsoleOutput(true);
|
|
287
|
-
};
|
|
288
|
-
|
|
289
|
-
@injectable(undefined, false, setupLogger)
|
|
290
|
-
class LoggerService {
|
|
291
|
-
private logLevel: string = 'info';
|
|
292
|
-
private consoleOutput: boolean = false;
|
|
293
|
-
|
|
294
|
-
setLogLevel(level: string): void {
|
|
295
|
-
this.logLevel = level;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
enableConsoleOutput(enabled: boolean): void {
|
|
299
|
-
this.consoleOutput = enabled;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
log(message: string): void {
|
|
303
|
-
if (this.consoleOutput) {
|
|
304
|
-
console.log(`[${this.logLevel.toUpperCase()}]: ${message}`);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
284
|
+
import { getInjectKey } from 'injectable-decorators';
|
|
285
|
+
|
|
286
|
+
console.log(getInjectKey('injectable')); // "inject.db.injectable"
|
|
287
|
+
console.log(getInjectKey('inject')); // "inject.db.inject"
|
|
308
288
|
```
|
|
309
289
|
|
|
290
|
+
Notes:
|
|
291
|
+
- Always include `import 'reflect-metadata'` once in your app before using decorators.
|
|
292
|
+
- VERSION is exported as a string placeholder defined at build time.
|
|
293
|
+
|
|
310
294
|
|
|
311
295
|
### Related
|
|
312
296
|
|