@djodjonx/neo-syringe 1.1.5 → 1.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/src/types.ts CHANGED
@@ -5,11 +5,11 @@
5
5
  export type Constructor<T = unknown> = new (...args: unknown[]) => T;
6
6
 
7
7
  /**
8
- * Defines the lifecycle scope of a service.
8
+ * Defines the lifecycle of a service.
9
9
  * - `singleton`: One instance per container.
10
10
  * - `transient`: A new instance every time it is resolved.
11
11
  */
12
- export type Scope = 'singleton' | 'transient';
12
+ export type Lifecycle = 'singleton' | 'transient';
13
13
 
14
14
  /**
15
15
  * The dependency injection container interface.
@@ -86,7 +86,33 @@ export interface Injection<T = any> {
86
86
  * Required when provider is a function, not a class.
87
87
  */
88
88
  useFactory?: boolean;
89
- scope?: Scope;
89
+ /**
90
+ * Lifecycle of the service.
91
+ * - `singleton`: One instance per container (default).
92
+ * - `transient`: A new instance every time it is resolved.
93
+ */
94
+ lifecycle?: Lifecycle;
95
+ /**
96
+ * If true, this injection is scoped to this container only.
97
+ * Allows overriding a token from a parent container without causing a duplicate error.
98
+ * The local instance will be used instead of delegating to the parent.
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * const parent = defineBuilderConfig({
103
+ * injections: [{ token: useInterface<ILogger>(), provider: ConsoleLogger }]
104
+ * });
105
+ *
106
+ * const child = defineBuilderConfig({
107
+ * useContainer: parent,
108
+ * injections: [
109
+ * // Override parent's ILogger with a local FileLogger
110
+ * { token: useInterface<ILogger>(), provider: FileLogger, scoped: true }
111
+ * ]
112
+ * });
113
+ * ```
114
+ */
115
+ scoped?: boolean;
90
116
  }
91
117
 
92
118
  /**
@@ -49,8 +49,8 @@ describe('Analyzer', () => {
49
49
  class B {}
50
50
  export const container = defineBuilderConfig({
51
51
  injections: [
52
- { token: A, scope: 'transient' },
53
- { token: B, scope: 'singleton' }
52
+ { token: A, lifecycle: 'transient' },
53
+ { token: B, lifecycle: 'singleton' }
54
54
  ]
55
55
  });
56
56
  `;
@@ -69,7 +69,7 @@ describe('Analyzer', () => {
69
69
  const analyzer = new Analyzer(program);
70
70
  const graph = analyzer.extract();
71
71
 
72
- expect(graph.nodes.get('A')?.service.scope).toBe('transient');
73
- expect(graph.nodes.get('B')?.service.scope).toBe('singleton');
72
+ expect(graph.nodes.get('A')?.service.lifecycle).toBe('transient');
73
+ expect(graph.nodes.get('B')?.service.lifecycle).toBe('singleton');
74
74
  });
75
75
  });
@@ -151,7 +151,7 @@ describe('Analyzer - Factory Support', () => {
151
151
  {
152
152
  token: useInterface<IRequest>(),
153
153
  provider: () => ({ id: ++counter }),
154
- scope: 'transient'
154
+ lifecycle: 'transient'
155
155
  }
156
156
  ]
157
157
  });
@@ -163,7 +163,7 @@ describe('Analyzer - Factory Support', () => {
163
163
 
164
164
  const node = findNodeByName(graph, 'IRequest');
165
165
  expect(node?.service.isFactory).toBe(true);
166
- expect(node?.service.scope).toBe('transient');
166
+ expect(node?.service.lifecycle).toBe('transient');
167
167
  });
168
168
 
169
169
  it('should not treat class as factory', () => {
@@ -0,0 +1,434 @@
1
+ /**
2
+ * Tests for scoped: true functionality
3
+ *
4
+ * Allows overriding a token from a parent container
5
+ * without causing a duplicate registration error.
6
+ */
7
+ import { describe, it, expect } from 'vitest';
8
+ import * as ts from 'typescript';
9
+ import { Analyzer } from '../../src/analyzer/Analyzer';
10
+ import { GraphValidator } from '../../src/generator/GraphValidator';
11
+ import { Generator } from '../../src/generator/Generator';
12
+
13
+ describe('Scoped Injection', () => {
14
+ const createProgram = (fileName: string, fileContent: string) => {
15
+ const compilerHost = ts.createCompilerHost({});
16
+ const originalGetSourceFile = compilerHost.getSourceFile;
17
+
18
+ compilerHost.getSourceFile = (name, languageVersion) => {
19
+ if (name === fileName) {
20
+ return ts.createSourceFile(fileName, fileContent, languageVersion);
21
+ }
22
+ return originalGetSourceFile(name, languageVersion);
23
+ };
24
+
25
+ return ts.createProgram([fileName], {}, compilerHost);
26
+ };
27
+
28
+ describe('Analyzer - scoped detection', () => {
29
+ it('should detect scoped: true in injection', () => {
30
+ const program = createProgram('scoped.ts', `
31
+ function defineBuilderConfig(config: any) { return config; }
32
+ function useInterface<T>(): any { return null; }
33
+
34
+ interface ILogger {}
35
+ class FileLogger implements ILogger {}
36
+
37
+ export const c = defineBuilderConfig({
38
+ injections: [
39
+ { token: useInterface<ILogger>(), provider: FileLogger, scoped: true }
40
+ ]
41
+ });
42
+ `);
43
+
44
+ const analyzer = new Analyzer(program);
45
+ const graph = analyzer.extract();
46
+
47
+ // Find node containing ILogger (may have filename prefix)
48
+ const node = Array.from(graph.nodes.values()).find(n =>
49
+ n.service.tokenId.includes('ILogger')
50
+ );
51
+ expect(node).toBeDefined();
52
+ expect(node!.service.isScoped).toBe(true);
53
+ });
54
+
55
+ it('should detect scoped: false (default) when not specified', () => {
56
+ const program = createProgram('not-scoped.ts', `
57
+ function defineBuilderConfig(config: any) { return config; }
58
+ function useInterface<T>(): any { return null; }
59
+
60
+ interface ILogger {}
61
+ class ConsoleLogger implements ILogger {}
62
+
63
+ export const c = defineBuilderConfig({
64
+ injections: [
65
+ { token: useInterface<ILogger>(), provider: ConsoleLogger }
66
+ ]
67
+ });
68
+ `);
69
+
70
+ const analyzer = new Analyzer(program);
71
+ const graph = analyzer.extract();
72
+
73
+ // Find node containing ILogger (may have filename prefix)
74
+ const node = Array.from(graph.nodes.values()).find(n =>
75
+ n.service.tokenId.includes('ILogger')
76
+ );
77
+ expect(node).toBeDefined();
78
+ expect(node!.service.isScoped).toBeFalsy();
79
+ });
80
+
81
+ it('should allow duplicate registration when scoped: true in same container', () => {
82
+ const program = createProgram('scoped-same.ts', `
83
+ function defineBuilderConfig(config: any) { return config; }
84
+ function useInterface<T>(): any { return null; }
85
+
86
+ interface ILogger {}
87
+ class FileLogger implements ILogger {}
88
+
89
+ export const c = defineBuilderConfig({
90
+ injections: [
91
+ { token: useInterface<ILogger>(), provider: FileLogger, scoped: true },
92
+ { token: useInterface<ILogger>(), provider: FileLogger, scoped: true }
93
+ ]
94
+ });
95
+ `);
96
+
97
+ const analyzer = new Analyzer(program);
98
+ // Should not throw - second scoped: true overrides the first
99
+ expect(() => analyzer.extract()).not.toThrow();
100
+ });
101
+ });
102
+
103
+ describe('GraphValidator - scoped with parent', () => {
104
+ it('should throw duplicate error without scoped: true', () => {
105
+ const program = createProgram('duplicate.ts', `
106
+ function defineBuilderConfig(config: any) { return config; }
107
+ function useInterface<T>(): any { return null; }
108
+
109
+ interface ILogger {}
110
+ class ConsoleLogger implements ILogger {}
111
+ class FileLogger implements ILogger {}
112
+
113
+ const parent = defineBuilderConfig({
114
+ injections: [{ token: useInterface<ILogger>(), provider: ConsoleLogger }]
115
+ });
116
+
117
+ export const c = defineBuilderConfig({
118
+ useContainer: parent,
119
+ injections: [
120
+ { token: useInterface<ILogger>(), provider: FileLogger }
121
+ ]
122
+ });
123
+ `);
124
+
125
+ const analyzer = new Analyzer(program);
126
+ const graph = analyzer.extract();
127
+ const validator = new GraphValidator();
128
+
129
+ expect(() => validator.validate(graph)).toThrow(/Duplicate registration.*ILogger.*parent/);
130
+ });
131
+
132
+ it('should NOT throw duplicate error with scoped: true', () => {
133
+ const program = createProgram('scoped-override.ts', `
134
+ function defineBuilderConfig(config: any) { return config; }
135
+ function useInterface<T>(): any { return null; }
136
+
137
+ interface ILogger {}
138
+ class ConsoleLogger implements ILogger {}
139
+ class FileLogger implements ILogger {}
140
+
141
+ const parent = defineBuilderConfig({
142
+ injections: [{ token: useInterface<ILogger>(), provider: ConsoleLogger }]
143
+ });
144
+
145
+ export const c = defineBuilderConfig({
146
+ useContainer: parent,
147
+ injections: [
148
+ { token: useInterface<ILogger>(), provider: FileLogger, scoped: true }
149
+ ]
150
+ });
151
+ `);
152
+
153
+ const analyzer = new Analyzer(program);
154
+ const graph = analyzer.extract();
155
+ const validator = new GraphValidator();
156
+
157
+ expect(() => validator.validate(graph)).not.toThrow();
158
+ });
159
+
160
+ it('should suggest scoped: true in error message', () => {
161
+ const program = createProgram('suggest-scoped.ts', `
162
+ function defineBuilderConfig(config: any) { return config; }
163
+ function useInterface<T>(): any { return null; }
164
+
165
+ interface ICache {}
166
+ class RedisCache implements ICache {}
167
+ class MemoryCache implements ICache {}
168
+
169
+ const parent = defineBuilderConfig({
170
+ injections: [{ token: useInterface<ICache>(), provider: RedisCache }]
171
+ });
172
+
173
+ export const c = defineBuilderConfig({
174
+ useContainer: parent,
175
+ injections: [
176
+ { token: useInterface<ICache>(), provider: MemoryCache }
177
+ ]
178
+ });
179
+ `);
180
+
181
+ const analyzer = new Analyzer(program);
182
+ const graph = analyzer.extract();
183
+ const validator = new GraphValidator();
184
+
185
+ try {
186
+ validator.validate(graph);
187
+ expect.fail('Should have thrown');
188
+ } catch (e: any) {
189
+ expect(e.message).toContain('scoped: true');
190
+ }
191
+ });
192
+ });
193
+
194
+ describe('Generator - scoped code generation', () => {
195
+ it('should generate local factory for scoped token', () => {
196
+ const program = createProgram('gen-scoped.ts', `
197
+ function defineBuilderConfig(config: any) { return config; }
198
+ function useInterface<T>(): any { return null; }
199
+
200
+ interface ILogger {}
201
+ class ConsoleLogger implements ILogger {}
202
+ class FileLogger implements ILogger {}
203
+
204
+ const parent = defineBuilderConfig({
205
+ injections: [{ token: useInterface<ILogger>(), provider: ConsoleLogger }]
206
+ });
207
+
208
+ export const c = defineBuilderConfig({
209
+ name: 'ChildContainer',
210
+ useContainer: parent,
211
+ injections: [
212
+ { token: useInterface<ILogger>(), provider: FileLogger, scoped: true }
213
+ ]
214
+ });
215
+ `);
216
+
217
+ const analyzer = new Analyzer(program);
218
+ const graph = analyzer.extract();
219
+
220
+ // Skip validation for this test (it would throw duplicate without scoped handling in validator)
221
+ // The generator should include the scoped token factory
222
+ const generator = new Generator(graph);
223
+ const code = generator.generate();
224
+
225
+ // Should have a factory for ILogger (locally) - may have filename prefix
226
+ expect(code).toMatch(/create_.*ILogger/);
227
+ expect(code).toContain('FileLogger');
228
+ });
229
+
230
+ it('should resolve scoped token locally before parent', () => {
231
+ const program = createProgram('resolve-local.ts', `
232
+ function defineBuilderConfig(config: any) { return config; }
233
+ function useInterface<T>(): any { return null; }
234
+
235
+ interface ILogger {}
236
+ class ConsoleLogger implements ILogger {}
237
+ class FileLogger implements ILogger {}
238
+
239
+ const parent = defineBuilderConfig({
240
+ injections: [{ token: useInterface<ILogger>(), provider: ConsoleLogger }]
241
+ });
242
+
243
+ class UserService {
244
+ constructor(logger: ILogger) {}
245
+ }
246
+
247
+ export const c = defineBuilderConfig({
248
+ name: 'ChildContainer',
249
+ useContainer: parent,
250
+ injections: [
251
+ { token: useInterface<ILogger>(), provider: FileLogger, scoped: true },
252
+ { token: UserService }
253
+ ]
254
+ });
255
+ `);
256
+
257
+ const analyzer = new Analyzer(program);
258
+ const graph = analyzer.extract();
259
+ const generator = new Generator(graph);
260
+ const code = generator.generate();
261
+
262
+ // UserService should resolve ILogger from local container
263
+ expect(code).toContain('create_UserService');
264
+ expect(code).toContain('container.resolve');
265
+ });
266
+ });
267
+
268
+ describe('Scoped with different scopes (singleton/transient)', () => {
269
+ it('should allow scoped + transient', () => {
270
+ const program = createProgram('scoped-transient.ts', `
271
+ function defineBuilderConfig(config: any) { return config; }
272
+ function useInterface<T>(): any { return null; }
273
+
274
+ interface ILogger {}
275
+ class ConsoleLogger implements ILogger {}
276
+ class FileLogger implements ILogger {}
277
+
278
+ const parent = defineBuilderConfig({
279
+ injections: [{ token: useInterface<ILogger>(), provider: ConsoleLogger, lifecycle: 'singleton' }]
280
+ });
281
+
282
+ export const c = defineBuilderConfig({
283
+ useContainer: parent,
284
+ injections: [
285
+ {
286
+ token: useInterface<ILogger>(),
287
+ provider: FileLogger,
288
+ lifecycle: 'transient', // Different scope than parent!
289
+ scoped: true
290
+ }
291
+ ]
292
+ });
293
+ `);
294
+
295
+ const analyzer = new Analyzer(program);
296
+ const graph = analyzer.extract();
297
+
298
+ // Find node containing ILogger (may have filename prefix)
299
+ const node = Array.from(graph.nodes.values()).find(n =>
300
+ n.service.tokenId.includes('ILogger')
301
+ );
302
+ expect(node).toBeDefined();
303
+ expect(node!.service.lifecycle).toBe('transient');
304
+ expect(node!.service.isScoped).toBe(true);
305
+ });
306
+
307
+ it('should generate transient code for scoped transient token', () => {
308
+ const program = createProgram('gen-scoped-transient.ts', `
309
+ function defineBuilderConfig(config: any) { return config; }
310
+ function useInterface<T>(): any { return null; }
311
+
312
+ interface IRequest {}
313
+ class RequestImpl implements IRequest {}
314
+
315
+ const parent = defineBuilderConfig({
316
+ injections: [{ token: useInterface<IRequest>(), provider: RequestImpl, lifecycle: 'singleton' }]
317
+ });
318
+
319
+ export const c = defineBuilderConfig({
320
+ name: 'RequestScope',
321
+ useContainer: parent,
322
+ injections: [
323
+ {
324
+ token: useInterface<IRequest>(),
325
+ provider: RequestImpl,
326
+ lifecycle: 'transient',
327
+ scoped: true
328
+ }
329
+ ]
330
+ });
331
+ `);
332
+
333
+ const analyzer = new Analyzer(program);
334
+ const graph = analyzer.extract();
335
+ const generator = new Generator(graph);
336
+ const code = generator.generate();
337
+
338
+ // Transient should NOT use instances cache
339
+ expect(code).toMatch(/create_.*IRequest/);
340
+ // The transient resolution should return directly without caching
341
+ expect(code).toMatch(/if \(token === ".*IRequest"\) \{\s*return create_/);
342
+ });
343
+ });
344
+
345
+ describe('Complex scenarios', () => {
346
+ it('should work with multi-level hierarchy', () => {
347
+ const program = createProgram('multi-level.ts', `
348
+ function defineBuilderConfig(config: any) { return config; }
349
+ function useInterface<T>(): any { return null; }
350
+
351
+ interface ILogger {}
352
+ class ConsoleLogger implements ILogger {}
353
+ class FileLogger implements ILogger {}
354
+ class MockLogger implements ILogger {}
355
+
356
+ // Level 1: Infrastructure
357
+ const infrastructure = defineBuilderConfig({
358
+ name: 'Infrastructure',
359
+ injections: [{ token: useInterface<ILogger>(), provider: ConsoleLogger }]
360
+ });
361
+
362
+ // Level 2: Domain (uses parent's logger)
363
+ const domain = defineBuilderConfig({
364
+ name: 'Domain',
365
+ useContainer: infrastructure,
366
+ injections: []
367
+ });
368
+
369
+ // Level 3: Test (overrides logger)
370
+ export const testModule = defineBuilderConfig({
371
+ name: 'TestModule',
372
+ useContainer: domain,
373
+ injections: [
374
+ { token: useInterface<ILogger>(), provider: MockLogger, scoped: true }
375
+ ]
376
+ });
377
+ `);
378
+
379
+ const analyzer = new Analyzer(program);
380
+ const graph = analyzer.extract();
381
+ const validator = new GraphValidator();
382
+
383
+ expect(() => validator.validate(graph)).not.toThrow();
384
+
385
+ // Find the ILogger node (may have filename prefix)
386
+ const loggerNode = Array.from(graph.nodes.values()).find(n =>
387
+ n.service.tokenId.includes('ILogger')
388
+ );
389
+ expect(loggerNode?.service.isScoped).toBe(true);
390
+ });
391
+
392
+ it('should work with factory + scoped', () => {
393
+ const program = createProgram('factory-scoped.ts', `
394
+ function defineBuilderConfig(config: any) { return config; }
395
+ function useInterface<T>(): any { return null; }
396
+
397
+ interface IConfig { env: string; }
398
+
399
+ const parent = defineBuilderConfig({
400
+ injections: [{
401
+ token: useInterface<IConfig>(),
402
+ provider: () => ({ env: 'production' })
403
+ }]
404
+ });
405
+
406
+ export const c = defineBuilderConfig({
407
+ useContainer: parent,
408
+ injections: [
409
+ {
410
+ token: useInterface<IConfig>(),
411
+ provider: () => ({ env: 'test' }),
412
+ scoped: true
413
+ }
414
+ ]
415
+ });
416
+ `);
417
+
418
+ const analyzer = new Analyzer(program);
419
+ const graph = analyzer.extract();
420
+ const validator = new GraphValidator();
421
+
422
+ expect(() => validator.validate(graph)).not.toThrow();
423
+
424
+ // Find the node by checking all nodes for IConfig
425
+ const configNode = Array.from(graph.nodes.values()).find(n =>
426
+ n.service.tokenId.includes('IConfig')
427
+ );
428
+ expect(configNode).toBeDefined();
429
+ expect(configNode!.service.isScoped).toBe(true);
430
+ expect(configNode!.service.isFactory).toBe(true);
431
+ });
432
+ });
433
+ });
434
+
@@ -225,5 +225,96 @@ describe('CLI - Validation Logic', () => {
225
225
  expect(() => validator.validate(graph)).toThrow(/Circular/);
226
226
  });
227
227
  });
228
+
229
+ describe('Scoped Injections', () => {
230
+ it('should pass validation with scoped: true override', () => {
231
+ const program = createProgram('scoped-valid.ts', `
232
+ function defineBuilderConfig(config: any) { return config; }
233
+ function useInterface<T>(): any { return null; }
234
+
235
+ interface ILogger {}
236
+ class ConsoleLogger implements ILogger {}
237
+ class FileLogger implements ILogger {}
238
+
239
+ const parent = defineBuilderConfig({
240
+ injections: [{ token: useInterface<ILogger>(), provider: ConsoleLogger }]
241
+ });
242
+
243
+ export const c = defineBuilderConfig({
244
+ useContainer: parent,
245
+ injections: [
246
+ { token: useInterface<ILogger>(), provider: FileLogger, scoped: true }
247
+ ]
248
+ });
249
+ `);
250
+
251
+ const analyzer = new Analyzer(program);
252
+ const graph = analyzer.extract();
253
+ const validator = new GraphValidator();
254
+
255
+ expect(() => validator.validate(graph)).not.toThrow();
256
+ });
257
+
258
+ it('should fail validation without scoped: true', () => {
259
+ const program = createProgram('scoped-invalid.ts', `
260
+ function defineBuilderConfig(config: any) { return config; }
261
+ function useInterface<T>(): any { return null; }
262
+
263
+ interface ILogger {}
264
+ class ConsoleLogger implements ILogger {}
265
+ class FileLogger implements ILogger {}
266
+
267
+ const parent = defineBuilderConfig({
268
+ injections: [{ token: useInterface<ILogger>(), provider: ConsoleLogger }]
269
+ });
270
+
271
+ export const c = defineBuilderConfig({
272
+ useContainer: parent,
273
+ injections: [
274
+ { token: useInterface<ILogger>(), provider: FileLogger }
275
+ ]
276
+ });
277
+ `);
278
+
279
+ const analyzer = new Analyzer(program);
280
+ const graph = analyzer.extract();
281
+ const validator = new GraphValidator();
282
+
283
+ expect(() => validator.validate(graph)).toThrow(/Duplicate registration.*ILogger/);
284
+ });
285
+
286
+ it('should suggest scoped: true in error message', () => {
287
+ const program = createProgram('scoped-suggest.ts', `
288
+ function defineBuilderConfig(config: any) { return config; }
289
+ function useInterface<T>(): any { return null; }
290
+
291
+ interface ICache {}
292
+ class RedisCache implements ICache {}
293
+ class MemoryCache implements ICache {}
294
+
295
+ const parent = defineBuilderConfig({
296
+ injections: [{ token: useInterface<ICache>(), provider: RedisCache }]
297
+ });
298
+
299
+ export const c = defineBuilderConfig({
300
+ useContainer: parent,
301
+ injections: [
302
+ { token: useInterface<ICache>(), provider: MemoryCache }
303
+ ]
304
+ });
305
+ `);
306
+
307
+ const analyzer = new Analyzer(program);
308
+ const graph = analyzer.extract();
309
+ const validator = new GraphValidator();
310
+
311
+ try {
312
+ validator.validate(graph);
313
+ expect.fail('Should have thrown');
314
+ } catch (e: any) {
315
+ expect(e.message).toContain("scoped: true");
316
+ }
317
+ });
318
+ });
228
319
  });
229
320
 
@@ -22,8 +22,8 @@ describe('E2E - Container Integration', () => {
22
22
 
23
23
  private factories = new Map<any, (container: NeoContainer) => any>();
24
24
 
25
- registerFactory(token: any, factory: (container: NeoContainer) => any, scope: 'singleton' | 'transient' = 'singleton') {
26
- if (scope === 'transient') {
25
+ registerFactory(token: any, factory: (container: NeoContainer) => any, lifecycle: 'singleton' | 'transient' = 'singleton') {
26
+ if (lifecycle === 'transient') {
27
27
  this.factories.set(token, (c) => factory(c));
28
28
  } else {
29
29
  this.factories.set(token, (c) => {
@@ -202,7 +202,7 @@ it('should resolve class dependencies on interfaces', () => {
202
202
  function defineBuilderConfig(config: any) { return config; }
203
203
  class SingletonService {}
204
204
  export const c = defineBuilderConfig({
205
- injections: [{ token: SingletonService, scope: 'singleton' }]
205
+ injections: [{ token: SingletonService, lifecycle: 'singleton' }]
206
206
  });
207
207
  `);
208
208
 
@@ -217,7 +217,7 @@ it('should resolve class dependencies on interfaces', () => {
217
217
  function defineBuilderConfig(config: any) { return config; }
218
218
  class TransientService {}
219
219
  export const c = defineBuilderConfig({
220
- injections: [{ token: TransientService, scope: 'transient' }]
220
+ injections: [{ token: TransientService, lifecycle: 'transient' }]
221
221
  });
222
222
  `);
223
223
 
@@ -652,10 +652,10 @@ it('should generate PropertyToken resolution', () => {
652
652
 
653
653
  export const c = defineBuilderConfig({
654
654
  injections: [
655
- { token: SingletonA, scope: 'singleton' },
655
+ { token: SingletonA, lifecycle: 'singleton' },
656
656
  { token: SingletonB }, // Default singleton
657
- { token: TransientA, scope: 'transient' },
658
- { token: TransientB, scope: 'transient' }
657
+ { token: TransientA, lifecycle: 'transient' },
658
+ { token: TransientB, lifecycle: 'transient' }
659
659
  ]
660
660
  });
661
661
  `);