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