@navios/di 0.4.2 → 0.5.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.
Files changed (120) hide show
  1. package/README.md +211 -1
  2. package/coverage/clover.xml +1912 -1277
  3. package/coverage/coverage-final.json +37 -28
  4. package/coverage/docs/examples/basic-usage.mts.html +1 -1
  5. package/coverage/docs/examples/factory-pattern.mts.html +1 -1
  6. package/coverage/docs/examples/index.html +1 -1
  7. package/coverage/docs/examples/injection-tokens.mts.html +1 -1
  8. package/coverage/docs/examples/request-scope-example.mts.html +1 -1
  9. package/coverage/docs/examples/service-lifecycle.mts.html +1 -1
  10. package/coverage/index.html +71 -41
  11. package/coverage/lib/_tsup-dts-rollup.d.mts.html +682 -43
  12. package/coverage/lib/index.d.mts.html +7 -4
  13. package/coverage/lib/index.html +5 -5
  14. package/coverage/lib/testing/index.d.mts.html +91 -0
  15. package/coverage/lib/testing/index.html +116 -0
  16. package/coverage/src/base-instance-holder-manager.mts.html +589 -0
  17. package/coverage/src/container.mts.html +257 -74
  18. package/coverage/src/decorators/factory.decorator.mts.html +1 -1
  19. package/coverage/src/decorators/index.html +1 -1
  20. package/coverage/src/decorators/index.mts.html +1 -1
  21. package/coverage/src/decorators/injectable.decorator.mts.html +20 -20
  22. package/coverage/src/enums/index.html +1 -1
  23. package/coverage/src/enums/index.mts.html +1 -1
  24. package/coverage/src/enums/injectable-scope.enum.mts.html +1 -1
  25. package/coverage/src/enums/injectable-type.enum.mts.html +1 -1
  26. package/coverage/src/errors/di-error.mts.html +292 -0
  27. package/coverage/src/errors/errors.enum.mts.html +30 -21
  28. package/coverage/src/errors/factory-not-found.mts.html +31 -22
  29. package/coverage/src/errors/factory-token-not-resolved.mts.html +29 -26
  30. package/coverage/src/errors/index.html +56 -41
  31. package/coverage/src/errors/index.mts.html +15 -9
  32. package/coverage/src/errors/instance-destroying.mts.html +31 -22
  33. package/coverage/src/errors/instance-expired.mts.html +31 -22
  34. package/coverage/src/errors/instance-not-found.mts.html +31 -22
  35. package/coverage/src/errors/unknown-error.mts.html +31 -43
  36. package/coverage/src/event-emitter.mts.html +14 -14
  37. package/coverage/src/factory-context.mts.html +1 -1
  38. package/coverage/src/index.html +121 -46
  39. package/coverage/src/index.mts.html +7 -4
  40. package/coverage/src/injection-token.mts.html +28 -28
  41. package/coverage/src/injector.mts.html +1 -1
  42. package/coverage/src/instance-resolver.mts.html +1762 -0
  43. package/coverage/src/interfaces/factory.interface.mts.html +1 -1
  44. package/coverage/src/interfaces/index.html +1 -1
  45. package/coverage/src/interfaces/index.mts.html +1 -1
  46. package/coverage/src/interfaces/on-service-destroy.interface.mts.html +1 -1
  47. package/coverage/src/interfaces/on-service-init.interface.mts.html +1 -1
  48. package/coverage/src/registry.mts.html +28 -28
  49. package/coverage/src/request-context-holder.mts.html +183 -102
  50. package/coverage/src/request-context-manager.mts.html +532 -0
  51. package/coverage/src/service-instantiator.mts.html +49 -49
  52. package/coverage/src/service-invalidator.mts.html +1372 -0
  53. package/coverage/src/service-locator-event-bus.mts.html +48 -48
  54. package/coverage/src/service-locator-instance-holder.mts.html +2 -14
  55. package/coverage/src/service-locator-manager.mts.html +71 -335
  56. package/coverage/src/service-locator.mts.html +240 -2328
  57. package/coverage/src/symbols/index.html +1 -1
  58. package/coverage/src/symbols/index.mts.html +1 -1
  59. package/coverage/src/symbols/injectable-token.mts.html +1 -1
  60. package/coverage/src/testing/index.html +131 -0
  61. package/coverage/src/testing/index.mts.html +88 -0
  62. package/coverage/src/testing/test-container.mts.html +445 -0
  63. package/coverage/src/token-processor.mts.html +607 -0
  64. package/coverage/src/utils/defer.mts.html +28 -214
  65. package/coverage/src/utils/get-injectable-token.mts.html +7 -7
  66. package/coverage/src/utils/get-injectors.mts.html +99 -99
  67. package/coverage/src/utils/index.html +15 -15
  68. package/coverage/src/utils/index.mts.html +4 -7
  69. package/coverage/src/utils/types.mts.html +1 -1
  70. package/docs/injectable.md +51 -11
  71. package/docs/scopes.md +63 -29
  72. package/lib/_tsup-dts-rollup.d.mts +376 -213
  73. package/lib/_tsup-dts-rollup.d.ts +376 -213
  74. package/lib/{chunk-3NLYPYBY.mjs → chunk-44F3LXW5.mjs} +1021 -605
  75. package/lib/chunk-44F3LXW5.mjs.map +1 -0
  76. package/lib/index.d.mts +6 -4
  77. package/lib/index.d.ts +6 -4
  78. package/lib/index.js +1192 -776
  79. package/lib/index.js.map +1 -1
  80. package/lib/index.mjs +2 -2
  81. package/lib/testing/index.js +1258 -840
  82. package/lib/testing/index.js.map +1 -1
  83. package/lib/testing/index.mjs +1 -1
  84. package/package.json +1 -1
  85. package/src/__tests__/container.spec.mts +47 -13
  86. package/src/__tests__/errors.spec.mts +53 -27
  87. package/src/__tests__/injectable.spec.mts +73 -0
  88. package/src/__tests__/request-scope.spec.mts +0 -2
  89. package/src/__tests__/service-locator-manager.spec.mts +12 -82
  90. package/src/__tests__/service-locator.spec.mts +1009 -1
  91. package/src/__type-tests__/inject.spec-d.mts +30 -7
  92. package/src/__type-tests__/injectable.spec-d.mts +76 -37
  93. package/src/base-instance-holder-manager.mts +2 -9
  94. package/src/container.mts +61 -9
  95. package/src/decorators/injectable.decorator.mts +29 -5
  96. package/src/errors/di-error.mts +69 -0
  97. package/src/errors/index.mts +9 -7
  98. package/src/injection-token.mts +1 -0
  99. package/src/injector.mts +2 -0
  100. package/src/instance-resolver.mts +559 -0
  101. package/src/request-context-holder.mts +0 -2
  102. package/src/request-context-manager.mts +149 -0
  103. package/src/service-invalidator.mts +429 -0
  104. package/src/service-locator-instance-holder.mts +0 -4
  105. package/src/service-locator-manager.mts +10 -40
  106. package/src/service-locator.mts +86 -782
  107. package/src/token-processor.mts +174 -0
  108. package/src/utils/get-injectors.mts +161 -24
  109. package/src/utils/index.mts +0 -1
  110. package/src/utils/types.mts +12 -8
  111. package/lib/chunk-3NLYPYBY.mjs.map +0 -1
  112. package/src/__tests__/defer.spec.mts +0 -166
  113. package/src/errors/errors.enum.mts +0 -8
  114. package/src/errors/factory-not-found.mts +0 -8
  115. package/src/errors/factory-token-not-resolved.mts +0 -10
  116. package/src/errors/instance-destroying.mts +0 -8
  117. package/src/errors/instance-expired.mts +0 -8
  118. package/src/errors/instance-not-found.mts +0 -8
  119. package/src/errors/unknown-error.mts +0 -15
  120. package/src/utils/defer.mts +0 -73
package/README.md CHANGED
@@ -88,6 +88,9 @@ The `@Injectable` decorator marks a class as injectable:
88
88
  ```typescript
89
89
  import { Injectable, InjectableScope } from '@navios/di'
90
90
 
91
+ // With schema (for constructor arguments)
92
+ import { z } from 'zod'
93
+
91
94
  // Singleton (default)
92
95
  @Injectable()
93
96
  class SingletonService {}
@@ -99,6 +102,16 @@ class TransientService {}
99
102
  // With custom injection token
100
103
  @Injectable({ token: MyToken })
101
104
  class TokenizedService {}
105
+
106
+ const configSchema = z.object({
107
+ host: z.string(),
108
+ port: z.number(),
109
+ })
110
+
111
+ @Injectable({ schema: configSchema })
112
+ class DatabaseConfig {
113
+ constructor(public readonly config: z.output<typeof configSchema>) {}
114
+ }
102
115
  ```
103
116
 
104
117
  ### Injection Methods
@@ -302,6 +315,201 @@ const FactoryConfig = InjectionToken.factory(CONFIG_TOKEN, async () => {
302
315
  const config = await container.get(FactoryConfig)
303
316
  ```
304
317
 
318
+ ### Injectable with Schema
319
+
320
+ Instead of creating an injection token with a schema, you can directly provide a schema to the `@Injectable` decorator. This is a more concise way to define services that require constructor arguments with validation.
321
+
322
+ #### Basic Schema Usage
323
+
324
+ ```typescript
325
+ import { getInjectableToken, Injectable, InjectionToken } from '@navios/di'
326
+
327
+ import { z } from 'zod'
328
+
329
+ const databaseConfigSchema = z.object({
330
+ host: z.string(),
331
+ port: z.number(),
332
+ username: z.string(),
333
+ password: z.string(),
334
+ })
335
+
336
+ @Injectable({ schema: databaseConfigSchema })
337
+ class DatabaseConfig {
338
+ constructor(public readonly config: z.output<typeof databaseConfigSchema>) {}
339
+
340
+ getConnectionString() {
341
+ return `${this.config.host}:${this.config.port}`
342
+ }
343
+ }
344
+
345
+ // Usage with bound token
346
+ const container = new Container()
347
+ const config = await container.get(DatabaseConfig, {
348
+ host: 'localhost',
349
+ port: 5432,
350
+ username: 'admin',
351
+ password: 'secret',
352
+ })
353
+ console.log(config.getConnectionString()) // "localhost:5432"
354
+ ```
355
+
356
+ #### Schema with Different Scopes
357
+
358
+ ```typescript
359
+ // Singleton with schema (default)
360
+ @Injectable({ schema: apiConfigSchema })
361
+ class ApiConfig {
362
+ constructor(public readonly config: z.output<typeof apiConfigSchema>) {}
363
+ }
364
+
365
+ // Transient with schema
366
+ @Injectable({
367
+ schema: loggerConfigSchema,
368
+ scope: InjectableScope.Transient,
369
+ })
370
+ class Logger {
371
+ constructor(public readonly config: z.output<typeof loggerConfigSchema>) {}
372
+ }
373
+
374
+ // Request-scoped with schema
375
+ @Injectable({
376
+ schema: userContextSchema,
377
+ scope: InjectableScope.Request,
378
+ })
379
+ class UserContext {
380
+ constructor(public readonly context: z.output<typeof userContextSchema>) {}
381
+ }
382
+ ```
383
+
384
+ #### Using Schema-based Services as Dependencies
385
+
386
+ ```typescript
387
+ const dbConfigSchema = z.object({
388
+ connectionString: z.string(),
389
+ })
390
+
391
+ @Injectable({ schema: dbConfigSchema })
392
+ class DatabaseConfig {
393
+ constructor(public readonly config: z.output<typeof dbConfigSchema>) {}
394
+ }
395
+
396
+ @Injectable()
397
+ class DatabaseService {
398
+ // Inject with bound token
399
+ private dbConfig = inject(DatabaseConfig, {
400
+ connectionString: 'postgres://localhost:5432/myapp',
401
+ })
402
+
403
+ connect() {
404
+ return `Connecting to ${this.dbConfig.config.connectionString}`
405
+ }
406
+ }
407
+
408
+ // Or with async injection
409
+ @Injectable()
410
+ class AsyncDatabaseService {
411
+ private dbConfig = asyncInject(DatabaseConfig, {
412
+ connectionString: 'postgres://localhost:5432/myapp',
413
+ })
414
+
415
+ async connect() {
416
+ const config = await this.dbConfig
417
+ return `Connecting to ${config.config.connectionString}`
418
+ }
419
+ }
420
+ ```
421
+
422
+ #### Complex Nested Schemas
423
+
424
+ ```typescript
425
+ const appConfigSchema = z.object({
426
+ database: z.object({
427
+ host: z.string(),
428
+ port: z.number(),
429
+ credentials: z.object({
430
+ username: z.string(),
431
+ password: z.string(),
432
+ }),
433
+ }),
434
+ cache: z.object({
435
+ enabled: z.boolean(),
436
+ ttl: z.number(),
437
+ }),
438
+ api: z.object({
439
+ baseUrl: z.string(),
440
+ timeout: z.number(),
441
+ }),
442
+ })
443
+
444
+ @Injectable({ schema: appConfigSchema })
445
+ class AppConfig {
446
+ constructor(public readonly config: z.output<typeof appConfigSchema>) {}
447
+
448
+ getDatabaseConfig() {
449
+ return this.config.database
450
+ }
451
+
452
+ getCacheConfig() {
453
+ return this.config.cache
454
+ }
455
+
456
+ getApiConfig() {
457
+ return this.config.api
458
+ }
459
+ }
460
+
461
+ // Usage
462
+ const container = new Container()
463
+ const config = await container.get(AppConfig, {
464
+ database: {
465
+ host: 'db.example.com',
466
+ port: 5432,
467
+ credentials: {
468
+ username: 'admin',
469
+ password: 'secret',
470
+ },
471
+ },
472
+ cache: {
473
+ enabled: true,
474
+ ttl: 300,
475
+ },
476
+ api: {
477
+ baseUrl: 'https://api.example.com',
478
+ timeout: 5000,
479
+ },
480
+ })
481
+ ```
482
+
483
+ #### Schema vs Token with Schema
484
+
485
+ There are two ways to use schemas with `@Injectable`:
486
+
487
+ **Option 1: Direct Schema (Recommended for simplicity)**
488
+
489
+ ```typescript
490
+ @Injectable({ schema: configSchema })
491
+ class ConfigService {
492
+ constructor(public readonly config: z.output<typeof configSchema>) {}
493
+ }
494
+ ```
495
+
496
+ **Option 2: Token with Schema (Use when you need to share the token)**
497
+
498
+ ```typescript
499
+ const CONFIG_TOKEN = InjectionToken.create('CONFIG', configSchema)
500
+
501
+ @Injectable({ token: CONFIG_TOKEN })
502
+ class ConfigService {
503
+ constructor(public readonly config: z.output<typeof configSchema>) {}
504
+ }
505
+ ```
506
+
507
+ The direct schema approach is simpler and creates the injection token automatically. Use the token approach when you need to:
508
+
509
+ - Share the same token across multiple classes
510
+ - Reference the token in multiple places
511
+ - Have more control over the token name
512
+
305
513
  ## Advanced Usage
306
514
 
307
515
  ### Custom Registry
@@ -350,9 +558,11 @@ const newService = await container.get(MyService)
350
558
 
351
559
  - `@Injectable(options?: InjectableOptions)` - Mark class as injectable
352
560
  - Options:
353
- - `scope?: InjectableScope` - Service scope (Singleton | Transient)
561
+ - `scope?: InjectableScope` - Service scope (Singleton | Transient | Request)
354
562
  - `token?: InjectionToken` - Custom injection token
563
+ - `schema?: ZodSchema` - Zod schema for constructor arguments (alternative to token)
355
564
  - `registry?: Registry` - Custom registry
565
+ - Note: Cannot use both `token` and `schema` options together
356
566
 
357
567
  ### Factory Decorator
358
568