@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.
Files changed (61) hide show
  1. package/.github/workflows/ci.yml +6 -5
  2. package/.github/workflows/docs.yml +59 -0
  3. package/CHANGELOG.md +27 -0
  4. package/README.md +74 -740
  5. package/dist/{GraphValidator-G0F4QiLk.cjs → GraphValidator-CV4VoJl0.cjs} +18 -10
  6. package/dist/{GraphValidator-C8ldJtNp.mjs → GraphValidator-DXqqkNdS.mjs} +18 -10
  7. package/dist/cli/index.cjs +16 -1
  8. package/dist/cli/index.mjs +16 -1
  9. package/dist/index.d.cts +31 -5
  10. package/dist/index.d.mts +31 -5
  11. package/dist/lsp/index.cjs +1 -1
  12. package/dist/lsp/index.mjs +1 -1
  13. package/dist/unplugin/index.cjs +33 -9
  14. package/dist/unplugin/index.d.cts +7 -5
  15. package/dist/unplugin/index.d.mts +7 -5
  16. package/dist/unplugin/index.mjs +33 -9
  17. package/docs/.vitepress/config.ts +109 -0
  18. package/docs/.vitepress/theme/custom.css +150 -0
  19. package/docs/.vitepress/theme/index.ts +17 -0
  20. package/docs/api/configuration.md +274 -0
  21. package/docs/api/functions.md +291 -0
  22. package/docs/api/types.md +158 -0
  23. package/docs/guide/basic-usage.md +267 -0
  24. package/docs/guide/cli.md +174 -0
  25. package/docs/guide/generated-code.md +284 -0
  26. package/docs/guide/getting-started.md +171 -0
  27. package/docs/guide/ide-plugin.md +203 -0
  28. package/docs/guide/injection-types.md +287 -0
  29. package/docs/guide/legacy-migration.md +333 -0
  30. package/docs/guide/lifecycle.md +223 -0
  31. package/docs/guide/parent-container.md +321 -0
  32. package/docs/guide/scoped-injections.md +271 -0
  33. package/docs/guide/what-is-neo-syringe.md +162 -0
  34. package/docs/guide/why-neo-syringe.md +219 -0
  35. package/docs/index.md +138 -0
  36. package/docs/public/logo.png +0 -0
  37. package/package.json +15 -12
  38. package/src/analyzer/Analyzer.ts +20 -10
  39. package/src/analyzer/types.ts +55 -49
  40. package/src/cli/index.ts +15 -0
  41. package/src/generator/Generator.ts +24 -2
  42. package/src/generator/GraphValidator.ts +6 -2
  43. package/src/types.ts +30 -4
  44. package/src/unplugin/index.ts +13 -41
  45. package/tests/analyzer/Analyzer.test.ts +4 -4
  46. package/tests/analyzer/AnalyzerDeclarative.test.ts +1 -1
  47. package/tests/analyzer/Factory.test.ts +2 -2
  48. package/tests/analyzer/Scoped.test.ts +434 -0
  49. package/tests/cli/cli.test.ts +91 -0
  50. package/tests/e2e/container-integration.test.ts +21 -21
  51. package/tests/e2e/generated-code.test.ts +7 -7
  52. package/tests/e2e/scoped.test.ts +370 -0
  53. package/tests/e2e/snapshots.test.ts +2 -2
  54. package/tests/e2e/standalone.test.ts +2 -2
  55. package/tests/generator/ExternalGenerator.test.ts +1 -1
  56. package/tests/generator/FactoryGenerator.test.ts +6 -6
  57. package/tests/generator/Generator.test.ts +2 -2
  58. package/tests/generator/GeneratorDeclarative.test.ts +1 -1
  59. package/tests/generator/GraphValidator.test.ts +1 -1
  60. package/tsconfig.json +2 -1
  61. package/typedoc.json +0 -5
@@ -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) => {
@@ -74,7 +74,7 @@ describe('E2E - Container Integration', () => {
74
74
  const container = new NeoContainer();
75
75
  container.registerFactory(SimpleService, () => new SimpleService());
76
76
 
77
- const service = container.resolve(SimpleService);
77
+ const service = container.resolve<any>(SimpleService);
78
78
  expect(service.getValue()).toBe(42);
79
79
  });
80
80
 
@@ -96,7 +96,7 @@ describe('E2E - Container Integration', () => {
96
96
  container.registerFactory(Logger, () => new Logger());
97
97
  container.registerFactory(UserService, (c) => new UserService(c.resolve(Logger)));
98
98
 
99
- const userService = container.resolve(UserService);
99
+ const userService = container.resolve<any>(UserService);
100
100
  expect(userService.greet('World')).toBe('Hello World');
101
101
  });
102
102
  });
@@ -111,8 +111,8 @@ describe('E2E - Container Integration', () => {
111
111
  const container = new NeoContainer();
112
112
  container.registerFactory(Counter, () => new Counter(), 'singleton');
113
113
 
114
- const c1 = container.resolve(Counter);
115
- const c2 = container.resolve(Counter);
114
+ const c1 = container.resolve<any>(Counter);
115
+ const c2 = container.resolve<any>(Counter);
116
116
 
117
117
  expect(c1).toBe(c2);
118
118
  expect(c1.increment()).toBe(1);
@@ -131,8 +131,8 @@ describe('E2E - Container Integration', () => {
131
131
  const container = new NeoContainer();
132
132
  container.registerFactory(Transient, () => new Transient(), 'transient');
133
133
 
134
- const t1 = container.resolve(Transient);
135
- const t2 = container.resolve(Transient);
134
+ const t1 = container.resolve<any>(Transient);
135
+ const t2 = container.resolve<any>(Transient);
136
136
 
137
137
  expect(t1).not.toBe(t2);
138
138
  expect(t1.id).not.toBe(t2.id);
@@ -163,11 +163,11 @@ describe('E2E - Container Integration', () => {
163
163
  container.registerFactory(UserRepo, (c) => new UserRepo(c.resolve(Database)));
164
164
  container.registerFactory(UserService, (c) => new UserService(c.resolve(UserRepo), c.resolve(Logger)));
165
165
 
166
- const userService = container.resolve(UserService);
166
+ const userService = container.resolve<any>(UserService);
167
167
  expect(userService.getDbUrl()).toBe('postgres://localhost');
168
168
 
169
169
  // Verify singleton behavior
170
- const logger1 = container.resolve(Logger);
170
+ const logger1 = container.resolve<any>(Logger);
171
171
  const logger2 = userService.logger;
172
172
  expect(logger1).toBe(logger2);
173
173
  });
@@ -181,7 +181,7 @@ describe('E2E - Container Integration', () => {
181
181
  const container = new NeoContainer(undefined, undefined, 'MyApp');
182
182
  container.registerFactory(Registered, () => new Registered());
183
183
 
184
- expect(() => container.resolve(NotRegistered)).toThrow(/MyApp.*not found/);
184
+ expect(() => container.resolve<any>(NotRegistered)).toThrow(/MyApp.*not found/);
185
185
  });
186
186
  });
187
187
 
@@ -198,7 +198,7 @@ describe('E2E - Container Integration', () => {
198
198
  container.registerFactory(Right, (c) => new Right(c.resolve(Shared)));
199
199
  container.registerFactory(Top, (c) => new Top(c.resolve(Left), c.resolve(Right)));
200
200
 
201
- const top = container.resolve(Top);
201
+ const top = container.resolve<any>(Top);
202
202
 
203
203
  // Both Left and Right should share the same Shared instance
204
204
  expect(top.left.shared).toBe(top.right.shared);
@@ -217,7 +217,7 @@ describe('E2E - Container Integration', () => {
217
217
  const child = new NeoContainer(parent, undefined, 'Child');
218
218
  child.registerFactory(ChildService, (c) => new ChildService(c.resolve(SharedService)));
219
219
 
220
- const service = child.resolve(ChildService);
220
+ const service = child.resolve<any>(ChildService);
221
221
  expect(service.shared.value).toBe('shared');
222
222
  });
223
223
 
@@ -230,7 +230,7 @@ describe('E2E - Container Integration', () => {
230
230
  const child = new NeoContainer(parent);
231
231
  child.registerFactory(Service, () => new Service('child'));
232
232
 
233
- expect(child.resolve(Service).value).toBe('child');
233
+ expect(child.resolve<any>(Service).value).toBe('child');
234
234
  });
235
235
  });
236
236
 
@@ -248,7 +248,7 @@ describe('E2E - Container Integration', () => {
248
248
 
249
249
  const container = new NeoContainer(undefined, [legacyContainer], 'App');
250
250
 
251
- const service = container.resolve(LegacyService);
251
+ const service = container.resolve<any>(LegacyService);
252
252
  expect(service.fromLegacy).toBe(true);
253
253
  });
254
254
 
@@ -262,7 +262,7 @@ describe('E2E - Container Integration', () => {
262
262
  const container = new NeoContainer(undefined, [legacyContainer]);
263
263
  container.registerFactory(Service, () => new Service('local'));
264
264
 
265
- expect(container.resolve(Service).source).toBe('local');
265
+ expect(container.resolve<any>(Service).source).toBe('local');
266
266
  });
267
267
  });
268
268
 
@@ -291,7 +291,7 @@ describe('E2E - Container Integration', () => {
291
291
  container.registerFactory(ILOGGER, () => new ConsoleLogger());
292
292
  container.registerFactory(UserService, (c) => new UserService(c.resolve(ILOGGER)));
293
293
 
294
- const service = container.resolve(UserService);
294
+ const service = container.resolve<any>(UserService);
295
295
  expect(service.logger.log('hi')).toBe('hi');
296
296
  });
297
297
  });
@@ -305,11 +305,11 @@ describe('E2E - Container Integration', () => {
305
305
 
306
306
  const API_URL_TOKEN = 'ApiUrl';
307
307
  container.registerFactory(API_URL_TOKEN, (c) => {
308
- const config = c.resolve(Config);
308
+ const config = c.resolve<Config>(Config);
309
309
  return `https://api.${config.env}.example.com`;
310
310
  });
311
311
 
312
- expect(container.resolve(API_URL_TOKEN)).toBe('https://api.test.example.com');
312
+ expect(container.resolve<any>(API_URL_TOKEN)).toBe('https://api.test.example.com');
313
313
  });
314
314
  });
315
315
 
@@ -324,7 +324,7 @@ describe('E2E - Container Integration', () => {
324
324
 
325
325
  const start = Date.now();
326
326
  for (let i = 0; i < 100; i++) {
327
- container.resolve(HeavyService);
327
+ container.resolve<any>(HeavyService);
328
328
  }
329
329
  const duration = Date.now() - start;
330
330
 
@@ -348,7 +348,7 @@ describe('E2E - Container Integration', () => {
348
348
  }
349
349
 
350
350
  const start = Date.now();
351
- container.resolve(prevClass);
351
+ container.resolve<any>(prevClass);
352
352
  const duration = Date.now() - start;
353
353
 
354
354
  expect(duration).toBeLessThan(100); // 100ms max for 20 levels