@venizia/ignis-docs 0.2.0 → 0.2.1-1

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 (174) hide show
  1. package/README.md +14 -14
  2. package/content/best-practices/api-usage-examples.md +39 -19
  3. package/content/best-practices/architectural-patterns.md +24 -13
  4. package/content/best-practices/architecture-decisions.md +29 -16
  5. package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
  6. package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
  7. package/content/best-practices/code-style-standards/control-flow.md +33 -1
  8. package/content/best-practices/code-style-standards/documentation.md +28 -8
  9. package/content/best-practices/code-style-standards/function-patterns.md +15 -4
  10. package/content/best-practices/code-style-standards/index.md +7 -2
  11. package/content/best-practices/code-style-standards/naming-conventions.md +27 -3
  12. package/content/best-practices/code-style-standards/route-definitions.md +9 -5
  13. package/content/best-practices/code-style-standards/tooling.md +14 -1
  14. package/content/best-practices/code-style-standards/type-safety.md +39 -6
  15. package/content/best-practices/common-pitfalls.md +59 -35
  16. package/content/best-practices/contribution-workflow.md +8 -4
  17. package/content/best-practices/data-modeling.md +78 -62
  18. package/content/best-practices/deployment-strategies.md +56 -19
  19. package/content/best-practices/error-handling.md +247 -153
  20. package/content/best-practices/index.md +6 -0
  21. package/content/best-practices/performance-optimization.md +26 -13
  22. package/content/best-practices/security-guidelines.md +35 -9
  23. package/content/best-practices/testing-strategies.md +7 -2
  24. package/content/best-practices/troubleshooting-tips.md +27 -17
  25. package/content/extensions/components/api-reference.md +109 -323
  26. package/content/extensions/components/authentication/api.md +489 -602
  27. package/content/extensions/components/authentication/errors.md +135 -498
  28. package/content/extensions/components/authentication/index.md +89 -801
  29. package/content/extensions/components/authentication/usage.md +231 -955
  30. package/content/extensions/components/authorization/api.md +991 -644
  31. package/content/extensions/components/authorization/errors.md +204 -208
  32. package/content/extensions/components/authorization/getting-started.md +227 -0
  33. package/content/extensions/components/authorization/index.md +88 -795
  34. package/content/extensions/components/authorization/usage.md +219 -528
  35. package/content/extensions/components/health-check.md +77 -243
  36. package/content/extensions/components/index.md +24 -90
  37. package/content/extensions/components/mail/api.md +553 -285
  38. package/content/extensions/components/mail/errors.md +76 -62
  39. package/content/extensions/components/mail/index.md +111 -463
  40. package/content/extensions/components/mail/usage.md +139 -173
  41. package/content/extensions/components/request-tracker.md +70 -173
  42. package/content/extensions/components/socket-io/api.md +462 -785
  43. package/content/extensions/components/socket-io/errors.md +49 -51
  44. package/content/extensions/components/socket-io/index.md +69 -372
  45. package/content/extensions/components/socket-io/usage.md +212 -105
  46. package/content/extensions/components/static-asset/api.md +461 -141
  47. package/content/extensions/components/static-asset/errors.md +121 -53
  48. package/content/extensions/components/static-asset/index.md +84 -606
  49. package/content/extensions/components/static-asset/usage.md +184 -299
  50. package/content/extensions/components/template/index.md +3 -3
  51. package/content/extensions/components/websocket/api.md +311 -404
  52. package/content/extensions/components/websocket/errors.md +47 -56
  53. package/content/extensions/components/websocket/index.md +75 -407
  54. package/content/extensions/components/websocket/usage.md +120 -338
  55. package/content/extensions/helpers/cron/index.md +52 -160
  56. package/content/extensions/helpers/crypto/index.md +67 -480
  57. package/content/extensions/helpers/crypto/reference.md +528 -0
  58. package/content/extensions/helpers/env/index.md +64 -178
  59. package/content/extensions/helpers/error/index.md +286 -196
  60. package/content/extensions/helpers/index.md +61 -47
  61. package/content/extensions/helpers/inversion/index.md +76 -550
  62. package/content/extensions/helpers/inversion/reference.md +530 -0
  63. package/content/extensions/helpers/kafka/admin.md +23 -3
  64. package/content/extensions/helpers/kafka/compile-binary.md +83 -52
  65. package/content/extensions/helpers/kafka/consumer.md +65 -28
  66. package/content/extensions/helpers/kafka/examples.md +46 -253
  67. package/content/extensions/helpers/kafka/index.md +55 -617
  68. package/content/extensions/helpers/kafka/producer.md +156 -22
  69. package/content/extensions/helpers/kafka/schema-registry.md +59 -79
  70. package/content/extensions/helpers/logger/hf-logger.md +220 -0
  71. package/content/extensions/helpers/logger/index.md +78 -552
  72. package/content/extensions/helpers/logger/pino.md +105 -0
  73. package/content/extensions/helpers/logger/reference.md +937 -0
  74. package/content/extensions/helpers/network/api.md +276 -195
  75. package/content/extensions/helpers/network/index.md +87 -524
  76. package/content/extensions/helpers/queue/index.md +80 -897
  77. package/content/extensions/helpers/queue/reference.md +494 -0
  78. package/content/extensions/helpers/redis/index.md +86 -640
  79. package/content/extensions/helpers/redis/reference.md +784 -0
  80. package/content/extensions/helpers/secrets/index.md +136 -0
  81. package/content/extensions/helpers/socket-io/api.md +325 -204
  82. package/content/extensions/helpers/socket-io/index.md +79 -429
  83. package/content/extensions/helpers/storage/api.md +584 -464
  84. package/content/extensions/helpers/storage/index.md +80 -573
  85. package/content/extensions/helpers/types/index.md +75 -495
  86. package/content/extensions/helpers/types/reference.md +689 -0
  87. package/content/extensions/helpers/uid/index.md +176 -189
  88. package/content/extensions/helpers/websocket/api.md +366 -214
  89. package/content/extensions/helpers/websocket/index.md +68 -503
  90. package/content/extensions/helpers/worker-thread/index.md +62 -396
  91. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  92. package/content/extensions/index.md +38 -39
  93. package/content/extensions/src-details/mcp-server.md +96 -548
  94. package/content/guides/core-concepts/persistent/datasources.md +2 -2
  95. package/content/guides/core-concepts/persistent/index.md +5 -1
  96. package/content/guides/core-concepts/persistent/models.md +1 -1
  97. package/content/guides/core-concepts/persistent/pglite.md +218 -0
  98. package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
  99. package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
  100. package/content/guides/core-concepts/persistent/search-typesense.md +28 -16
  101. package/content/guides/core-concepts/persistent/sqlite.md +296 -0
  102. package/content/guides/core-concepts/persistent/transactions.md +12 -11
  103. package/content/guides/core-concepts/secrets-vault.md +177 -0
  104. package/content/guides/core-concepts/services.md +1 -1
  105. package/content/guides/get-started/5-minute-quickstart.md +59 -206
  106. package/content/guides/get-started/philosophy.md +135 -670
  107. package/content/guides/get-started/setup.md +53 -74
  108. package/content/guides/migrations/redis-helpers-migration.md +5 -4
  109. package/content/guides/migrations/scoped-rbac-migration.md +5 -5
  110. package/content/guides/migrations/unified-connectors-migration.md +8 -7
  111. package/content/guides/tutorials/ecommerce-api.md +3 -8
  112. package/content/guides/tutorials/realtime-chat.md +1 -1
  113. package/content/references/base/application.md +64 -22
  114. package/content/references/base/components.md +3 -3
  115. package/content/references/base/connectors.md +79 -136
  116. package/content/references/base/controllers.md +12 -12
  117. package/content/references/base/datasources-reference.md +600 -0
  118. package/content/references/base/datasources.md +84 -444
  119. package/content/references/base/dependency-injection.md +25 -46
  120. package/content/references/base/filter-system/application-usage.md +95 -123
  121. package/content/references/base/filter-system/array-operators.md +33 -43
  122. package/content/references/base/filter-system/comparison-operators.md +55 -63
  123. package/content/references/base/filter-system/default-filter.md +144 -350
  124. package/content/references/base/filter-system/fields-order-pagination.md +116 -148
  125. package/content/references/base/filter-system/index.md +114 -253
  126. package/content/references/base/filter-system/json-filtering.md +52 -181
  127. package/content/references/base/filter-system/list-operators.md +28 -44
  128. package/content/references/base/filter-system/logical-operators.md +69 -114
  129. package/content/references/base/filter-system/null-operators.md +41 -98
  130. package/content/references/base/filter-system/pattern-matching.md +48 -51
  131. package/content/references/base/filter-system/quick-reference.md +93 -196
  132. package/content/references/base/filter-system/range-operators.md +22 -38
  133. package/content/references/base/filter-system/tips.md +70 -133
  134. package/content/references/base/filter-system/use-cases.md +173 -232
  135. package/content/references/base/grpc-controllers.md +53 -17
  136. package/content/references/base/index.md +5 -3
  137. package/content/references/base/middlewares.md +43 -28
  138. package/content/references/base/models-reference.md +886 -0
  139. package/content/references/base/models.md +81 -1452
  140. package/content/references/base/providers.md +8 -8
  141. package/content/references/base/repositories/advanced.md +281 -419
  142. package/content/references/base/repositories/index.md +84 -644
  143. package/content/references/base/repositories/mixins.md +25 -21
  144. package/content/references/base/repositories/relations.md +194 -375
  145. package/content/references/base/repositories/soft-deletable.md +67 -55
  146. package/content/references/base/secrets.md +267 -0
  147. package/content/references/base/services.md +8 -6
  148. package/content/references/configuration/environment-variables.md +73 -21
  149. package/content/references/configuration/index.md +51 -31
  150. package/content/references/index.md +1 -1
  151. package/content/references/quick-reference.md +3 -16
  152. package/content/references/utilities/crypto.md +35 -76
  153. package/content/references/utilities/date.md +33 -73
  154. package/content/references/utilities/duration.md +85 -0
  155. package/content/references/utilities/index.md +6 -2
  156. package/content/references/utilities/jsx-reference.md +298 -0
  157. package/content/references/utilities/jsx.md +82 -525
  158. package/content/references/utilities/module.md +81 -61
  159. package/content/references/utilities/parse.md +34 -64
  160. package/content/references/utilities/performance.md +33 -58
  161. package/content/references/utilities/promise.md +28 -62
  162. package/content/references/utilities/request.md +58 -218
  163. package/content/references/utilities/retry.md +139 -0
  164. package/content/references/utilities/schema.md +43 -137
  165. package/content/references/utilities/statuses-reference.md +361 -0
  166. package/content/references/utilities/statuses.md +63 -667
  167. package/dist/mcp-server/index.js +0 -0
  168. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  169. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
  170. package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
  171. package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
  172. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
  173. package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
  174. package/package.json +24 -23
@@ -1,616 +1,142 @@
1
- # Inversion (DI)
2
-
3
- Standalone IoC container with decorator-based injection, fluent binding API, and singleton/transient scoping -- the foundation layer for all IGNIS packages.
4
-
5
- ## Quick Reference
1
+ ---
2
+ title: Inversion (DI)
3
+ description: Standalone IoC container with decorator-based injection and a fluent binding API
4
+ difficulty: intermediate
5
+ ---
6
6
 
7
- | Item | Value |
8
- |------|-------|
9
- | **Package** | `@venizia/ignis-inversion` |
10
- | **Classes** | `Container`, `Binding`, `MetadataRegistry` |
11
- | **Decorators** | `@inject`, `@injectable` |
12
- | **Runtimes** | Both (Bun and Node.js) |
13
-
14
- #### Import Paths
15
-
16
- ```typescript
17
- import {
18
- Container,
19
- Binding,
20
- MetadataRegistry,
21
- metadataRegistry,
22
- inject,
23
- injectable,
24
- BindingKeys,
25
- BindingScopes,
26
- BindingValueTypes,
27
- MetadataKeys,
28
- BaseHelper,
29
- ApplicationError,
30
- getError,
31
- ErrorSchema,
32
- Logger,
33
- } from '@venizia/ignis-inversion';
34
-
35
- import type {
36
- TNullable,
37
- ValueOrPromise,
38
- ValueOf,
39
- TClass,
40
- TConstructor,
41
- TAbstractConstructor,
42
- TConstValue,
43
- TBindingScope,
44
- TBindingValueType,
45
- IProvider,
46
- IInjectMetadata,
47
- IPropertyMetadata,
48
- IInjectableMetadata,
49
- } from '@venizia/ignis-inversion';
50
- ```
7
+ # Inversion (DI)
51
8
 
52
- > [!NOTE]
53
- > The framework package `@venizia/ignis` re-exports DI-specific symbols (`Binding`, `BindingKeys`, `BindingScopes`, `BindingValueTypes`, `IProvider`, `isClass`, `isClassProvider`, `TBindingScope`, `TBindingValueType`, `IBindingTag`) from `@venizia/ignis-inversion` and adds higher-level helpers (`app.controller()`, `app.service()`, etc.). All types from inversion are also available via type-only re-exports.
9
+ `@venizia/ignis-inversion` is the standalone IoC container - decorator-based injection, a fluent binding API, and singleton/transient scoping - that every other IGNIS package builds on.
54
10
 
55
- ## Creating an Instance
11
+ ## In one example
56
12
 
57
- `Container` extends `BaseHelper`, providing a named scope for debugging context.
13
+ The smallest real use: bind a class with constructor injection, then resolve it through the container.
58
14
 
59
15
  ```typescript
60
- import { Container } from '@venizia/ignis-inversion';
61
-
62
- const container = new Container({ scope: 'MyApp' });
63
- ```
64
-
65
- The `scope` parameter is optional and defaults to `'Container'`. It is used for logging and error context only.
16
+ import { Container, inject, BindingScopes } from '@venizia/ignis-inversion';
66
17
 
67
- Basic binding example:
68
-
69
- ```typescript
70
- import { Container, BindingScopes } from '@venizia/ignis-inversion';
18
+ class UserService {
19
+ constructor(@inject({ key: 'config.appName' }) private appName: string) {}
20
+ }
71
21
 
72
22
  const container = new Container({ scope: 'MyApp' });
73
23
 
74
- // Bind a class (container instantiates with DI)
24
+ container.bind<string>({ key: 'config.appName' }).toValue('MyApp');
75
25
  container.bind<UserService>({ key: 'services.UserService' })
76
26
  .toClass(UserService)
77
27
  .setScope(BindingScopes.SINGLETON);
78
28
 
79
- // Resolve the dependency
80
29
  const userService = container.get<UserService>({ key: 'services.UserService' });
81
30
  ```
82
31
 
83
- ## Usage
84
-
85
- ### Binding Values
86
-
87
- Three resolver strategies are available via the fluent `Binding` API:
88
-
89
- ```typescript
90
- // Class -- container instantiates with DI
91
- container.bind<UserService>({ key: 'services.UserService' })
92
- .toClass(UserService);
93
-
94
- // Value -- return directly
95
- container.bind<string>({ key: 'APP_NAME' })
96
- .toValue('MyApp');
97
-
98
- // Provider -- factory function
99
- container.bind<DatabaseConnection>({ key: 'db.connection' })
100
- .toProvider((container) => {
101
- const config = container.get<Config>({ key: 'config.database' });
102
- return new DatabaseConnection(config);
103
- });
104
- ```
105
-
106
- #### Class-based Provider
32
+ A class needs no decorator to be bindable. The binding is always created explicitly with `container.bind()`, and its scope is set via `setScope()`.
107
33
 
108
- For complex creation logic, implement the `IProvider<T>` interface:
34
+ The framework layer (`@venizia/ignis`) creates these bindings for you - for controllers, services, and repositories - via `app.controller()` / `app.service()` / `@repository`.
109
35
 
110
- ```typescript
111
- import { IProvider, Container } from '@venizia/ignis-inversion';
36
+ ## How it works
112
37
 
113
- class DatabaseConnectionProvider implements IProvider<DatabaseConnection> {
114
- value(container: Container): DatabaseConnection {
115
- const config = container.get<Config>({ key: 'config.database' });
116
- return new DatabaseConnection(config);
117
- }
118
- }
38
+ - **A binding resolves one of three ways.** All `Binding` setters return `this`, so calls chain.
119
39
 
120
- container.bind<DatabaseConnection>({ key: 'db.connection' })
121
- .toProvider(DatabaseConnectionProvider);
122
- ```
123
-
124
- When `toProvider` receives a class with a `value()` method on its prototype, the container instantiates the class (with full DI support) and then calls `value(container)` to produce the final value.
40
+ | Resolver | Behavior |
41
+ |---|---|
42
+ | `toClass` | Container instantiates the class with full DI |
43
+ | `toValue` | Returns the value as-is |
44
+ | `toProvider` | Calls a factory function, or an `IProvider` class |
125
45
 
126
- #### Fluent Chaining
46
+ - **Instantiation is two-phase.** `container.resolve(cls)` and `container.instantiate(cls)` are the same method - `resolve` is an alias.
127
47
 
128
- All `Binding` setter methods return `this` for chaining:
48
+ | Phase | What happens |
49
+ |---|---|
50
+ | 1. Constructor injection | Reads `@inject` metadata by parameter index, passes resolved values as constructor args |
51
+ | 2. Property injection | Assigns each `@inject`-decorated property on the built instance |
129
52
 
130
- ```typescript
131
- container.bind<CacheService>({ key: 'services.CacheService' })
132
- .toClass(CacheService)
133
- .setScope(BindingScopes.SINGLETON)
134
- .setTags('infrastructure', 'cache');
135
- ```
53
+ - **Every constructor parameter must carry `@inject`.** The metadata array is index-keyed - an undecorated parameter leaves a hole the container has no way to fill. `instantiate()` refuses the class by name and parameter index rather than passing `undefined`.
54
+ - **Namespaces auto-tag bindings.** A key like `services.UserService` tags the binding `services` automatically. `setTags()` adds more. `findByTag()` queries by tag, with an `exclude` list.
55
+ - **Keys** can be a `string`, a `symbol`, or `{ namespace, key }` (built into a dotted string via `BindingKeys.build`).
136
56
 
137
- #### Static Factory
57
+ **Scopes**
138
58
 
139
- `Binding` also exposes a static factory for creating bindings outside a container:
59
+ | Scope | Constant | Behavior |
60
+ |-------|----------|----------|
61
+ | Transient | `BindingScopes.TRANSIENT` | New instance on every resolution (default) |
62
+ | Singleton | `BindingScopes.SINGLETON` | Cached on the `Binding` after first resolution |
140
63
 
141
- ```typescript
142
- import { Binding, BindingScopes } from '@venizia/ignis-inversion';
64
+ > [!IMPORTANT]
65
+ > Singleton caching lives on the `Binding` object, not the container. Rebinding a key creates a fresh `Binding` with its own cache. If you hold onto an old `Binding` reference, its cache stays independent of `container.clear()`/`reset()` calls made against the new `Binding` registered under the same key.
143
66
 
144
- const binding = Binding.bind<IHealthCheckOptions>({
145
- key: 'options.healthCheck',
146
- }).toValue({ restOptions: { path: '/health' } });
67
+ Property-injected classes only get their `@inject` properties populated when built through the container - `container.resolve()` or `instantiate()`. A plain `new MyClass()` leaves them `undefined`.
147
68
 
148
- // Register it on a container later
149
- container.set({ binding });
150
- ```
69
+ The [Full reference](/extensions/helpers/inversion/reference) covers `MetadataRegistry`, `gets()`, key formats, `IProvider`, and every error message in detail.
151
70
 
152
- ### Constructor Injection
71
+ ## Common tasks
153
72
 
154
- This is the recommended approach -- dependencies are explicit and available at instantiation.
73
+ ### Bind a class with constructor injection
155
74
 
156
75
  ```typescript
157
- import { inject, injectable, BindingScopes } from '@venizia/ignis-inversion';
158
-
159
- @injectable({ scope: BindingScopes.SINGLETON })
160
- class UserService {
76
+ class OrderService {
161
77
  constructor(
162
- @inject({ key: 'repositories.UserRepository' })
163
- private userRepository: UserRepository,
164
-
165
- @inject({ key: 'services.Logger', isOptional: true })
166
- private logger?: Logger,
78
+ @inject({ key: 'repositories.OrderRepository' }) private orderRepository: OrderRepository,
79
+ @inject({ key: 'services.Logger', isOptional: true }) private logger?: Logger,
167
80
  ) {}
168
81
  }
169
82
  ```
170
83
 
171
- The container reads `@inject` metadata during `instantiate()` by constructor index (the `Reflect`-stored array is already index-keyed - no sort is performed), resolves each dependency, and passes them as constructor arguments.
172
-
173
- > [!IMPORTANT]
174
- > Every constructor parameter of a container-instantiated class must carry `@inject`. Mixing decorated and undecorated parameters is refused: an undecorated parameter leaves a hole in the index-keyed metadata array, and there is no channel through which the container could supply it anyway. `instantiate()` throws `[ClassName] Constructor parameter N has no @inject | Every parameter of a container-instantiated class must be decorated - the container cannot supply an undecorated one`.
175
-
176
- ### Property Injection
84
+ ### Bind a value or a provider
177
85
 
178
86
  ```typescript
179
- import { inject, injectable } from '@venizia/ignis-inversion';
180
-
181
- @injectable({})
182
- class UserService {
183
- @inject({ key: 'repositories.UserRepository' })
184
- private userRepository: UserRepository;
87
+ container.bind<string>({ key: 'APP_NAME' }).toValue('MyApp');
185
88
 
186
- @inject({ key: 'services.Logger', isOptional: true })
187
- private logger?: Logger;
188
- }
89
+ container.bind<DatabaseConnection>({ key: 'db.connection' })
90
+ .toProvider((container) => {
91
+ const config = container.get<Config>({ key: 'config.database' });
92
+ return new DatabaseConnection(config);
93
+ });
189
94
  ```
190
95
 
191
- > [!WARNING]
192
- > Property-injected classes must be instantiated through the container (`container.resolve()` or `container.instantiate()`). Using `new MyClass()` directly will leave `@inject` properties as `undefined`.
193
-
194
- The instantiation algorithm is two-phase:
195
- 1. **Constructor injection** -- reads `@inject` metadata on the constructor by parameter index (no sort - the metadata is already index-keyed), resolves from container
196
- 2. **Property injection** -- reads property metadata, resolves and assigns each dependency to the instance
197
-
198
- ### Scopes (Singleton / Transient)
199
-
200
- | Scope | Constant | Behavior |
201
- |-------|----------|----------|
202
- | Transient | `BindingScopes.TRANSIENT` | New instance every resolution (default) |
203
- | Singleton | `BindingScopes.SINGLETON` | Cached after first resolution, reused thereafter |
96
+ ### Set the scope
204
97
 
205
98
  ```typescript
206
- import { BindingScopes } from '@venizia/ignis-inversion';
207
-
208
- // Singleton -- one instance shared across all resolutions
209
99
  container.bind({ key: 'services.CacheService' })
210
100
  .toClass(CacheService)
211
- .setScope(BindingScopes.SINGLETON);
212
-
213
- // Transient (default) -- new instance every time
214
- container.bind({ key: 'services.RequestHandler' })
215
- .toClass(RequestHandler)
216
- .setScope(BindingScopes.TRANSIENT);
217
- ```
218
-
219
- > [!IMPORTANT]
220
- > Singleton caching is per-`Binding` object, not per-Container. If you rebind the same key, the old `Binding` retains its cache independently.
221
-
222
- #### Cache Management
223
-
224
- ```typescript
225
- // Clear all singleton caches (bindings stay registered)
226
- container.clear();
227
-
228
- // Remove all bindings entirely (full reset)
229
- container.reset();
230
-
231
- // Clear cache for a single binding
232
- const binding = container.getBinding({ key: 'services.CacheService' });
233
- binding?.clearCache();
234
- ```
235
-
236
- ### Namespaces and Tags
237
-
238
- Bindings with namespaced keys (e.g., `services.UserService`) are automatically tagged with the namespace portion (`services`). You can also add custom tags manually.
239
-
240
- ```typescript
241
- container.bind({ key: 'workers.EmailWorker' })
242
- .toClass(EmailWorker)
243
- .setTags('background', 'email');
244
- // This binding now has tags: ['workers', 'background', 'email']
245
-
246
- // Find all bindings tagged 'services'
247
- const serviceBindings = container.findByTag({ tag: 'services' });
248
-
249
- // Exclude specific keys
250
- const filtered = container.findByTag({
251
- tag: 'services',
252
- exclude: ['services.InternalService'],
253
- });
254
- ```
255
-
256
- #### Building Namespaced Keys
257
-
258
- ```typescript
259
- import { BindingKeys } from '@venizia/ignis-inversion';
260
-
261
- BindingKeys.build({ namespace: 'services', key: 'UserService' });
262
- // => 'services.UserService'
263
-
264
- // The key parameter is required; an empty key throws an error
265
- BindingKeys.build({ namespace: '', key: 'UserService' });
266
- // => 'UserService'
267
- ```
268
-
269
- ### Key Formats
270
-
271
- The `get`, `getBinding`, and `gets` methods accept three key formats:
272
-
273
- ```typescript
274
- // String key
275
- container.get<UserService>({ key: 'services.UserService' });
276
-
277
- // Symbol key
278
- container.get<UserService>({ key: Symbol.for('services.UserService') });
279
-
280
- // Namespaced object (built via BindingKeys.build internally)
281
- container.get<UserService>({ key: { namespace: 'services', key: 'UserService' } });
282
- ```
283
-
284
- ### Optional Dependencies
285
-
286
- ```typescript
287
- // Returns undefined instead of throwing if not bound
288
- const maybeSvc = container.get<MyService>({
289
- key: 'services.Optional',
290
- isOptional: true,
291
- });
292
-
293
- // In decorators
294
- @inject({ key: 'services.Logger', isOptional: true })
295
- private logger?: Logger;
296
- ```
297
-
298
- ### Resolving Multiple Dependencies
299
-
300
- ```typescript
301
- const [svcA, svcB] = container.gets<[ServiceA, ServiceB]>({
302
- bindings: [
303
- { key: 'services.ServiceA' },
304
- { key: 'services.ServiceB', isOptional: true },
305
- ],
306
- });
307
- ```
308
-
309
- > [!NOTE]
310
- > `gets()` internally calls `get()` with `isOptional: true` for each entry. Unresolved bindings return `undefined` rather than throwing.
311
-
312
- ### Instantiate Without Binding
313
-
314
- ```typescript
315
- // Create an instance with full DI resolution but don't register it
316
- const instance = container.resolve<MyClass>(MyClass);
317
- // or equivalently:
318
- const instance2 = container.instantiate<MyClass>(MyClass);
319
- ```
320
-
321
- Both methods perform the same two-phase instantiation (constructor injection, then property injection). `resolve()` is an alias for `instantiate()`.
322
-
323
- ### Checking and Removing Bindings
324
-
325
- ```typescript
326
- // Check if a key is registered
327
- container.isBound({ key: 'services.UserService' }); // true or false
328
-
329
- // Remove a binding
330
- container.unbind({ key: 'services.UserService' }); // returns true if removed, false if not found
101
+ .setScope(BindingScopes.SINGLETON); // default is TRANSIENT if omitted
331
102
  ```
332
103
 
333
- ### MetadataRegistry
334
-
335
- The `MetadataRegistry` is a singleton that stores all decorator metadata using `reflect-metadata`. Both `@inject` and `@injectable` delegate to it. You typically will not interact with the registry directly.
336
-
337
- ```typescript
338
- import { MetadataKeys, metadataRegistry } from '@venizia/ignis-inversion';
339
-
340
- // Well-known metadata keys
341
- MetadataKeys.PROPERTIES // Symbol.for('ignis:properties')
342
- MetadataKeys.INJECT // Symbol.for('ignis:inject')
343
- MetadataKeys.INJECTABLE // Symbol.for('ignis:injectable')
344
-
345
- // Access via container
346
- const registry = container.getMetadataRegistry();
347
- ```
348
-
349
- The registry also supports generic metadata operations for storing arbitrary metadata on any object:
350
-
351
- ```typescript
352
- metadataRegistry.define({ target: myObj, key: 'custom:flag', value: true });
353
- metadataRegistry.get({ target: myObj, key: 'custom:flag' }); // true
354
- metadataRegistry.has({ target: myObj, key: 'custom:flag' }); // true
355
- metadataRegistry.delete({ target: myObj, key: 'custom:flag' }); // true
356
- ```
357
-
358
- ### @injectable Decorator
359
-
360
- Marks a class with DI metadata (scope and tags). Used by the framework layer to configure bindings automatically.
104
+ ### Inject into a property instead of the constructor
361
105
 
362
106
  ```typescript
363
- @injectable({
364
- scope: BindingScopes.SINGLETON,
365
- tags: { category: 'infrastructure' },
366
- })
367
- class CacheService {
368
- // ...
107
+ class UserService {
108
+ @inject({ key: 'repositories.UserRepository' })
109
+ private userRepository: UserRepository;
369
110
  }
370
111
  ```
371
112
 
372
- ### Utilities
113
+ ### Resolve an optional dependency
373
114
 
374
- #### ApplicationError and getError
375
-
376
- Error factory used internally and available for consumers:
115
+ `isOptional: true` returns `undefined` instead of throwing when the key is unbound - on the constructor and via `container.get()`. `gets()` resolves several keys at once, always treating each as optional.
377
116
 
378
117
  ```typescript
379
- import { ApplicationError, getError, ErrorSchema } from '@venizia/ignis-inversion';
380
-
381
- // Factory function
382
- throw getError({ message: 'Something failed', statusCode: 500, messageCode: 'ERR_INTERNAL' });
383
-
384
- // Direct construction (defaults to statusCode 400)
385
- throw new ApplicationError({ message: 'Not found', statusCode: 404 });
386
-
387
- // Zod schema for validation
388
- ErrorSchema.parse({ message: 'test', statusCode: 400 });
118
+ const maybeService = container.get<MyService>({ key: 'services.Optional', isOptional: true });
389
119
  ```
390
120
 
391
- #### Logger
392
-
393
- Lightweight console logger (debug output requires `process.env.DEBUG`):
121
+ ### Instantiate a class without registering it
394
122
 
395
123
  ```typescript
396
- import { Logger } from '@venizia/ignis-inversion';
397
-
398
- Logger.info('Server started on port %d', 3000);
399
- Logger.warn('Deprecation warning');
400
- Logger.error('Connection failed: %s', err.message);
401
- Logger.debug('Resolved binding: %s', key); // Only prints when DEBUG env var is set
124
+ const instance = container.resolve<MyClass>(MyClass); // full DI, not bound to a key
402
125
  ```
403
126
 
404
- ## API Summary
405
-
406
- ### Container
407
-
408
- | Method | Signature | Description |
409
- |--------|-----------|-------------|
410
- | `bind` | `bind<T>(opts: { key: string \| symbol }): Binding<T>` | Create and register a new binding |
411
- | `get` | `get<T>(opts: { key: string \| symbol \| { namespace, key }, isOptional?: boolean }): T` | Resolve a dependency by key; throws if not found and `isOptional` is `false` |
412
- | `gets` | `gets<T>(opts: { bindings: Array<{ key, isOptional? }> }): T[]` | Resolve multiple dependencies at once (all treated as optional) |
413
- | `getBinding` | `getBinding<T>(opts: { key: string \| symbol \| { namespace, key } }): Binding<T> \| undefined` | Retrieve the raw `Binding` without resolving |
414
- | `set` | `set<T>(opts: { binding: Binding<T> }): void` | Register an externally-created binding |
415
- | `isBound` | `isBound(opts: { key: string \| symbol }): boolean` | Check if a key is registered |
416
- | `unbind` | `unbind(opts: { key: string \| symbol }): boolean` | Remove a binding; returns `true` if removed |
417
- | `resolve` | `resolve<T>(cls: TClass<T>): T` | Alias for `instantiate` |
418
- | `instantiate` | `instantiate<T>(cls: TClass<T>): T` | Create instance with full DI (constructor + property injection) |
419
- | `findByTag` | `findByTag<T>(opts: { tag: string, exclude?: string[] \| Set<string> }): Binding<T>[]` | Find all bindings matching a tag, optionally excluding keys |
420
- | `clear` | `clear(): void` | Clear all singleton caches (bindings remain) |
421
- | `reset` | `reset(): void` | Remove all bindings entirely |
422
- | `getMetadataRegistry` | `getMetadataRegistry(): MetadataRegistry` | Access the shared MetadataRegistry singleton |
423
-
424
- ### Binding
425
-
426
- | Method | Signature | Description |
427
- |--------|-----------|-------------|
428
- | `toClass` | `toClass(value: TClass<T>): this` | Container instantiates the class with DI |
429
- | `toValue` | `toValue(value: T): this` | Return value directly |
430
- | `toProvider` | `toProvider(value: ((container) => T) \| TClass<IProvider<T>>): this` | Factory function or `IProvider` class |
431
- | `setScope` | `setScope(scope: TBindingScope): this` | Set to `'singleton'` or `'transient'` (default) |
432
- | `setTags` | `setTags(...tags: string[]): this` | Add string tags (namespace auto-tagged from key) |
433
- | `hasTag` | `hasTag(tag: string): boolean` | Check if binding has a specific tag |
434
- | `getTags` | `getTags(): string[]` | Get all tags as array |
435
- | `getScope` | `getScope(): TBindingScope` | Get current scope |
436
- | `getValue` | `getValue(container?: Container): T` | Resolve the bound value (respects scope caching) |
437
- | `getBindingMeta` | `getBindingMeta(opts: { type: TBindingValueType }): any` | Get raw resolver value; throws if type does not match |
438
- | `clearCache` | `clearCache(): void` | Clear singleton cache for this binding |
439
- | `bind` (static) | `static bind<T>(opts: { key: string }): Binding<T>` | Static factory to create a Binding outside a container |
440
-
441
- ### MetadataRegistry
442
-
443
- | Method | Signature | Description |
444
- |--------|-----------|-------------|
445
- | `define` | `define<Target, Value>(opts: { target: Target, key: string \| symbol, value: Value }): void` | Store arbitrary metadata on a target |
446
- | `get` | `get<Target, Value>(opts: { target: Target, key: string \| symbol }): Value \| undefined` | Retrieve metadata by key |
447
- | `has` | `has<Target>(opts: { target: Target, key: string \| symbol }): boolean` | Check if metadata exists |
448
- | `delete` | `delete<Target>(opts: { target: Target, key: string \| symbol }): boolean` | Remove metadata by key |
449
- | `getKeys` | `getKeys<Target>(opts: { target: Target }): (string \| symbol)[]` | List all metadata keys on a target |
450
- | `getMethodNames` | `getMethodNames<T>(opts: { target: TClass<T> }): string[]` | List non-constructor method names on a class prototype |
451
- | `clearMetadata` | `clearMetadata<T>(opts: { target: T }): void` | Remove all metadata from a target |
452
- | `setInjectMetadata` | `setInjectMetadata<T>(opts: { target: T, index: number, metadata: IInjectMetadata }): void` | Store constructor `@inject` metadata at parameter index |
453
- | `getInjectMetadata` | `getInjectMetadata<T>(opts: { target: T }): IInjectMetadata[] \| undefined` | Get all constructor injection metadata |
454
- | `setPropertyMetadata` | `setPropertyMetadata<T>(opts: { target: T, propertyName: string \| symbol, metadata: IPropertyMetadata }): void` | Store property `@inject` metadata |
455
- | `getPropertiesMetadata` | `getPropertiesMetadata<T>(opts: { target: T }): Map<string \| symbol, IPropertyMetadata> \| undefined` | Get all property injection metadata |
456
- | `getPropertyMetadata` | `getPropertyMetadata<T>(opts: { target: T, propertyName: string \| symbol }): IPropertyMetadata \| undefined` | Get single property injection metadata |
457
- | `setInjectableMetadata` | `setInjectableMetadata<T>(opts: { target: T, metadata: IInjectableMetadata }): void` | Store `@injectable` metadata |
458
- | `getInjectableMetadata` | `getInjectableMetadata<T>(opts: { target: T }): IInjectableMetadata \| undefined` | Get `@injectable` metadata |
459
-
460
- ### Decorators
461
-
462
- | Decorator | Signature | Description |
463
- |-----------|-----------|-------------|
464
- | `@inject` | `inject(opts: { key: string \| symbol, isOptional?: boolean, registry?: MetadataRegistry })` | Marks a constructor parameter or property for dependency injection |
465
- | `@injectable` | `injectable(metadata: { scope?: TBindingScope, tags?: Record<string, any> }, registry?: MetadataRegistry)` | Marks a class with DI metadata (scope and tags) |
466
-
467
- ### Constants
468
-
469
- | Constant | Values | Description |
470
- |----------|--------|-------------|
471
- | `BindingScopes.SINGLETON` | `'singleton'` | Cached after first resolution |
472
- | `BindingScopes.TRANSIENT` | `'transient'` | New instance each resolution |
473
- | `BindingValueTypes.CLASS` | `'class'` | Container instantiates with DI |
474
- | `BindingValueTypes.VALUE` | `'value'` | Direct value return |
475
- | `BindingValueTypes.PROVIDER` | `'provider'` | Factory function or IProvider class |
476
- | `MetadataKeys.PROPERTIES` | `Symbol.for('ignis:properties')` | Property injection metadata key |
477
- | `MetadataKeys.INJECT` | `Symbol.for('ignis:inject')` | Constructor injection metadata key |
478
- | `MetadataKeys.INJECTABLE` | `Symbol.for('ignis:injectable')` | Injectable class metadata key |
479
-
480
- ### Exported Types
481
-
482
- ```typescript
483
- type TNullable<T> = T | undefined | null;
484
- type ValueOrPromise<T> = T | Promise<T>;
485
- type ValueOf<T> = T[keyof T];
486
- type TConstructor<T> = new (...args: any[]) => T;
487
- type TAbstractConstructor<T> = abstract new (...args: any[]) => T;
488
- type TClass<T> = TConstructor<T> & { [property: string]: any };
489
- type TConstValue<T extends TClass<any>> = Extract<ValueOf<T>, string | number>;
490
- type TBindingScope = 'singleton' | 'transient';
491
- type TBindingValueType = 'class' | 'value' | 'provider';
492
-
493
- interface IProvider<T> {
494
- value(container: Container): T;
495
- }
496
-
497
- interface IInjectMetadata {
498
- key: string | symbol;
499
- index: number;
500
- isOptional?: boolean;
501
- }
502
-
503
- interface IPropertyMetadata {
504
- bindingKey: string | symbol;
505
- isOptional?: boolean;
506
- [key: string]: any;
507
- }
508
-
509
- interface IInjectableMetadata {
510
- scope?: TBindingScope;
511
- tags?: Record<string, any>;
512
- }
513
-
514
- // Type guards
515
- function isClass<T>(target: any): target is TClass<T>;
516
- function isClassProvider<T>(target: any): target is TClass<IProvider<T>>;
517
- ```
518
-
519
- ## Troubleshooting
520
-
521
- ### "Binding key: X is not bounded in context!"
522
-
523
- **Cause:** The dependency was never registered with the container, or the key string does not match exactly.
524
-
525
- **Fix:**
526
- 1. Verify the binding exists: `container.isBound({ key: 'services.UserService' })`.
527
- 2. Check for typos in the key passed to `@inject({ key: '...' })` vs the key used in `container.bind({ key: '...' })`.
528
- 3. If the dependency is optional, use `@inject({ key: '...', isOptional: true })` or `container.get({ key: '...', isOptional: true })`.
529
-
530
- ### "[getValue] Invalid context/container to instantiate class"
531
-
532
- **Cause:** A `Binding` configured with `toClass()` was resolved without a `Container` reference. This happens when calling `binding.getValue()` directly without passing a container.
533
-
534
- **Fix:** Always resolve class bindings through the container via `container.get({ key })` rather than calling `binding.getValue()` without arguments.
535
-
536
- ### "[getValue] Invalid context/container to get provider value"
537
-
538
- **Cause:** A `Binding` configured with `toProvider()` was resolved without a `Container` reference.
539
-
540
- **Fix:** Same as above -- resolve provider bindings through the container via `container.get({ key })`.
541
-
542
- ### "[getBindingMeta] Invalid resolver type"
543
-
544
- **Cause:** Called `getBindingMeta({ type })` with a type that does not match the binding's actual resolver type (e.g., asking for `'class'` on a value binding).
545
-
546
- **Fix:** Ensure the `type` parameter matches the binding's resolver. Check what was used: `toClass()` = `'class'`, `toValue()` = `'value'`, `toProvider()` = `'provider'`.
547
-
548
- ### "[getBinding] Invalid binding key type"
549
-
550
- **Cause:** The key passed to `getBinding()` is not a `string`, `symbol`, or `{ namespace, key }` object.
551
-
552
- **Fix:** Use one of the three supported key formats: a string, a symbol, or an object with `namespace` and `key` properties.
553
-
554
- ### "[BindingKeys][build] Invalid key to build"
555
-
556
- **Cause:** Called `BindingKeys.build()` with an empty `key` value.
557
-
558
- **Fix:** Provide a non-empty `key` string: `BindingKeys.build({ namespace: 'services', key: 'UserService' })`.
559
-
560
- ### "[ClassName] Constructor parameter N has no @inject"
561
-
562
- **Cause:** A container-instantiated class has a constructor with a mix of decorated and undecorated parameters. `@inject` stores its metadata at the parameter's index, so an undecorated parameter leaves a hole in that index-keyed array; there is no channel through which the container could supply it anyway. `instantiate()` refuses the shape by name and index rather than passing `undefined`.
563
-
564
- **Fix:** Decorate every constructor parameter of the class with `@inject`. There is no partial-injection escape hatch - if a value does not come from the container (e.g. a plain `scope: string`), pass it through a factory/provider instead of a bare constructor parameter, or have the subclass forward it via its own `@inject`-decorated parameter.
565
-
566
- ### "@inject decorator can only be used on class properties or constructor parameters"
567
-
568
- **Cause:** The `@inject` decorator was applied to something other than a class property or constructor parameter.
569
-
570
- **Fix:** Only use `@inject` on constructor parameters or class properties.
571
-
572
- ### "Property injection returns undefined"
573
-
574
- **Cause:** The class was instantiated with `new MyClass()` directly instead of going through the container.
575
-
576
- **Fix:** Always use `container.resolve(MyClass)` or `container.instantiate(MyClass)` to create instances. Only the container reads `@inject` metadata and populates injected properties.
577
-
578
- ### "getInjectMetadata returns undefined"
579
-
580
- **Cause:** `reflect-metadata` was not imported before decorators were evaluated, or `experimentalDecorators` / `emitDecoratorMetadata` are not enabled in `tsconfig.json`.
581
-
582
- **Fix:**
583
- 1. Ensure `import 'reflect-metadata'` is at the top of your entry point (or rely on `@venizia/ignis-inversion` which imports it automatically).
584
- 2. Verify your `tsconfig.json` includes:
585
- ```json
586
- {
587
- "compilerOptions": {
588
- "experimentalDecorators": true,
589
- "emitDecoratorMetadata": true
590
- }
591
- }
592
- ```
593
-
594
- ### "Singleton returns stale instance after rebinding"
595
-
596
- **Cause:** Singleton caching is per-`Binding` object. If you hold a direct reference to an old `Binding` (e.g., from `getBinding()`), its cache is independent of the container.
597
-
598
- **Fix:**
599
- 1. Always resolve via `container.get()` rather than caching `Binding` references.
600
- 2. Call `container.clear()` to clear all singleton caches without removing bindings.
601
- 3. Call `container.reset()` to remove all bindings entirely.
602
-
603
- ## See Also
604
-
605
- - **Guides:**
606
- - [Dependency Injection Guide](/guides/core-concepts/dependency-injection) - DI fundamentals
607
- - [Application](/guides/core-concepts/application/) - Application extends Container
127
+ ## See also
608
128
 
609
- - **Other Helpers:**
610
- - [Helpers Index](../index) - All available helpers
129
+ - [Full reference](/extensions/helpers/inversion/reference) - every method, `MetadataRegistry`, error message, and edge case
130
+ - [Dependency Injection Guide](/guides/core-concepts/dependency-injection) - DI fundamentals in the framework layer
131
+ - [Application](/guides/core-concepts/application/) - `Application` extends `Container`
132
+ - [Dependency Injection API](/references/base/dependency-injection) - the framework-layer DI reference
133
+ - [Helpers Overview](/extensions/helpers/) - all available helpers
134
+ - [Architectural Patterns](/best-practices/architectural-patterns) - DI patterns
611
135
 
612
- - **References:**
613
- - [Dependency Injection API](/references/base/dependency-injection) - Complete DI reference
136
+ **Files:**
614
137
 
615
- - **Best Practices:**
616
- - [Architectural Patterns](/best-practices/architectural-patterns) - DI patterns
138
+ - [`packages/inversion/src/modules/container/container.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/container/container.ts) - `Container`, two-phase `instantiate()`
139
+ - [`packages/inversion/src/modules/container/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/container/base.ts) - `BaseContainer`, binding storage
140
+ - [`packages/inversion/src/modules/binding/binding.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/binding/binding.ts) - `Binding`
141
+ - [`packages/inversion/src/modules/metadata/injectors.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/metadata/injectors.ts) - `@inject`
142
+ - [`packages/inversion/src/modules/registry/registry.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/registry/registry.ts) - `MetadataRegistry`