@venizia/ignis-docs 0.2.0 → 0.2.1-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 (142) 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 +22 -11
  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 +26 -2
  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 +6 -2
  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 +182 -93
  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 +107 -322
  26. package/content/extensions/components/authentication/api.md +454 -603
  27. package/content/extensions/components/authentication/errors.md +121 -498
  28. package/content/extensions/components/authentication/index.md +88 -801
  29. package/content/extensions/components/authentication/usage.md +207 -956
  30. package/content/extensions/components/authorization/api.md +736 -656
  31. package/content/extensions/components/authorization/errors.md +168 -206
  32. package/content/extensions/components/authorization/index.md +82 -797
  33. package/content/extensions/components/authorization/usage.md +194 -527
  34. package/content/extensions/components/health-check.md +71 -243
  35. package/content/extensions/components/mail/api.md +504 -287
  36. package/content/extensions/components/mail/errors.md +73 -61
  37. package/content/extensions/components/mail/index.md +96 -467
  38. package/content/extensions/components/mail/usage.md +130 -172
  39. package/content/extensions/components/request-tracker.md +66 -173
  40. package/content/extensions/components/socket-io/api.md +195 -13
  41. package/content/extensions/components/socket-io/errors.md +3 -3
  42. package/content/extensions/components/socket-io/index.md +50 -337
  43. package/content/extensions/components/socket-io/usage.md +143 -26
  44. package/content/extensions/components/static-asset/api.md +410 -142
  45. package/content/extensions/components/static-asset/errors.md +110 -53
  46. package/content/extensions/components/static-asset/index.md +79 -608
  47. package/content/extensions/components/static-asset/usage.md +180 -300
  48. package/content/extensions/components/websocket/api.md +275 -399
  49. package/content/extensions/components/websocket/errors.md +47 -56
  50. package/content/extensions/components/websocket/index.md +74 -407
  51. package/content/extensions/components/websocket/usage.md +110 -341
  52. package/content/extensions/helpers/cron/index.md +51 -160
  53. package/content/extensions/helpers/crypto/index.md +62 -483
  54. package/content/extensions/helpers/crypto/reference.md +456 -0
  55. package/content/extensions/helpers/env/index.md +60 -178
  56. package/content/extensions/helpers/error/index.md +221 -207
  57. package/content/extensions/helpers/inversion/index.md +65 -556
  58. package/content/extensions/helpers/inversion/reference.md +522 -0
  59. package/content/extensions/helpers/kafka/admin.md +20 -1
  60. package/content/extensions/helpers/kafka/compile-binary.md +41 -35
  61. package/content/extensions/helpers/kafka/consumer.md +54 -20
  62. package/content/extensions/helpers/kafka/examples.md +21 -16
  63. package/content/extensions/helpers/kafka/index.md +80 -610
  64. package/content/extensions/helpers/kafka/producer.md +134 -5
  65. package/content/extensions/helpers/kafka/schema-registry.md +45 -70
  66. package/content/extensions/helpers/logger/hf-logger.md +193 -0
  67. package/content/extensions/helpers/logger/index.md +64 -563
  68. package/content/extensions/helpers/logger/pino.md +85 -0
  69. package/content/extensions/helpers/logger/reference.md +746 -0
  70. package/content/extensions/helpers/network/api.md +241 -195
  71. package/content/extensions/helpers/network/index.md +72 -530
  72. package/content/extensions/helpers/queue/index.md +72 -900
  73. package/content/extensions/helpers/queue/reference.md +467 -0
  74. package/content/extensions/helpers/redis/index.md +73 -645
  75. package/content/extensions/helpers/redis/reference.md +727 -0
  76. package/content/extensions/helpers/secrets/index.md +66 -0
  77. package/content/extensions/helpers/socket-io/api.md +305 -203
  78. package/content/extensions/helpers/socket-io/index.md +66 -432
  79. package/content/extensions/helpers/storage/api.md +564 -462
  80. package/content/extensions/helpers/storage/index.md +77 -573
  81. package/content/extensions/helpers/types/index.md +66 -499
  82. package/content/extensions/helpers/types/reference.md +650 -0
  83. package/content/extensions/helpers/uid/index.md +58 -227
  84. package/content/extensions/helpers/websocket/api.md +329 -216
  85. package/content/extensions/helpers/websocket/index.md +65 -503
  86. package/content/extensions/helpers/worker-thread/index.md +58 -396
  87. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  88. package/content/guides/core-concepts/persistent/models.md +1 -1
  89. package/content/guides/core-concepts/persistent/search-typesense.md +3 -3
  90. package/content/guides/core-concepts/persistent/transactions.md +1 -1
  91. package/content/guides/core-concepts/secrets-vault.md +177 -0
  92. package/content/guides/core-concepts/services.md +1 -1
  93. package/content/guides/migrations/redis-helpers-migration.md +1 -1
  94. package/content/guides/migrations/unified-connectors-migration.md +2 -2
  95. package/content/guides/tutorials/ecommerce-api.md +3 -8
  96. package/content/references/base/application.md +1 -1
  97. package/content/references/base/connectors.md +79 -136
  98. package/content/references/base/datasources-reference.md +599 -0
  99. package/content/references/base/datasources.md +84 -444
  100. package/content/references/base/dependency-injection.md +17 -39
  101. package/content/references/base/filter-system/application-usage.md +69 -121
  102. package/content/references/base/filter-system/array-operators.md +12 -0
  103. package/content/references/base/filter-system/comparison-operators.md +12 -0
  104. package/content/references/base/filter-system/default-filter.md +136 -348
  105. package/content/references/base/filter-system/fields-order-pagination.md +38 -16
  106. package/content/references/base/filter-system/index.md +106 -257
  107. package/content/references/base/filter-system/json-filtering.md +12 -2
  108. package/content/references/base/filter-system/list-operators.md +16 -2
  109. package/content/references/base/filter-system/logical-operators.md +13 -0
  110. package/content/references/base/filter-system/null-operators.md +13 -0
  111. package/content/references/base/filter-system/pattern-matching.md +12 -0
  112. package/content/references/base/filter-system/quick-reference.md +11 -2
  113. package/content/references/base/filter-system/range-operators.md +12 -0
  114. package/content/references/base/filter-system/tips.md +70 -133
  115. package/content/references/base/filter-system/use-cases.md +156 -233
  116. package/content/references/base/middlewares.md +35 -21
  117. package/content/references/base/models-reference.md +886 -0
  118. package/content/references/base/models.md +80 -1452
  119. package/content/references/base/repositories/advanced.md +156 -192
  120. package/content/references/base/repositories/index.md +77 -650
  121. package/content/references/base/repositories/mixins.md +22 -18
  122. package/content/references/base/repositories/relations.md +123 -171
  123. package/content/references/base/repositories/soft-deletable.md +58 -56
  124. package/content/references/base/secrets.md +263 -0
  125. package/content/references/base/services.md +2 -2
  126. package/content/references/configuration/environment-variables.md +48 -4
  127. package/content/references/configuration/index.md +49 -31
  128. package/content/references/quick-reference.md +3 -16
  129. package/content/references/utilities/crypto.md +35 -76
  130. package/content/references/utilities/date.md +33 -73
  131. package/content/references/utilities/index.md +1 -1
  132. package/content/references/utilities/jsx-reference.md +298 -0
  133. package/content/references/utilities/jsx.md +82 -525
  134. package/content/references/utilities/module.md +29 -62
  135. package/content/references/utilities/parse.md +34 -64
  136. package/content/references/utilities/performance.md +33 -58
  137. package/content/references/utilities/promise.md +28 -62
  138. package/content/references/utilities/request.md +57 -218
  139. package/content/references/utilities/schema.md +43 -137
  140. package/content/references/utilities/statuses-reference.md +361 -0
  141. package/content/references/utilities/statuses.md +63 -667
  142. package/package.json +8 -8
@@ -0,0 +1,522 @@
1
+ ---
2
+ title: Inversion (DI) - Full Reference
3
+ description: Complete reference for Container, Binding, MetadataRegistry, decorators, and every error message
4
+ difficulty: intermediate
5
+ ---
6
+
7
+ # Inversion (DI) - Full Reference
8
+
9
+ Exhaustive reference for `Container`, `Binding`, `MetadataRegistry`, the `@inject` decorator, and every type, constant, and error message in `@venizia/ignis-inversion`. For a readable introduction and the most common tasks, start with the [Inversion overview](/extensions/helpers/inversion/).
10
+
11
+ **Files:**
12
+
13
+ - [`packages/inversion/src/modules/container/container.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/container/container.ts) - `Container`
14
+ - [`packages/inversion/src/modules/container/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/container/base.ts) - `BaseContainer`
15
+ - [`packages/inversion/src/modules/container/abstract.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/container/abstract.ts) - `AbstractContainer`
16
+ - [`packages/inversion/src/modules/binding/binding.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/binding/binding.ts) - `Binding`
17
+ - [`packages/inversion/src/modules/binding/common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/binding/common/constants.ts) - `BindingScopes`, `BindingValueTypes`, `BindingKeys`
18
+ - [`packages/inversion/src/modules/binding/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/binding/common/types.ts) - `IBinding`, `IProvider`, `isClassProvider`
19
+ - [`packages/inversion/src/modules/metadata/injectors.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/metadata/injectors.ts) - `@inject`
20
+ - [`packages/inversion/src/modules/metadata/common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/metadata/common/constants.ts) - `MetadataKeys`
21
+ - [`packages/inversion/src/modules/registry/registry.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/registry/registry.ts) - `MetadataRegistry`, `metadataRegistry`
22
+ - [`packages/inversion/src/modules/registry/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/registry/common/types.ts) - `IInjectMetadata`, `IPropertyMetadata`
23
+ - [`packages/inversion/src/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/common/types.ts) - `TNullable`, `ValueOrPromise`, `TClass`, `TConstValue`, `isClass`
24
+ - [`packages/inversion/src/modules/error/app-error.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/error/app-error.ts) - `ApplicationError`, `getError`, `isApplicationError`
25
+ - [`packages/inversion/src/modules/error/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/error/types.ts) - `TError`, `TErrorDefinition`, `TErrorNormalized`, `IErrorKeyRegistry`
26
+ - [`packages/inversion/src/common/logger.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/common/logger.ts) - `Logger`
27
+
28
+ ## Quick Reference
29
+
30
+ | Item | Value |
31
+ |------|-------|
32
+ | Package | `@venizia/ignis-inversion` |
33
+ | Classes | `Container`, `BaseContainer`, `AbstractContainer`, `Binding`, `MetadataRegistry` |
34
+ | Decorators | `@inject` |
35
+ | Runtimes | Both Bun and Node.js |
36
+ | Build | Dual CJS + ESM |
37
+
38
+ ### Import paths
39
+
40
+ ```typescript
41
+ import {
42
+ Container,
43
+ Binding,
44
+ MetadataRegistry,
45
+ metadataRegistry,
46
+ inject,
47
+ BindingKeys,
48
+ BindingScopes,
49
+ BindingValueTypes,
50
+ MetadataKeys,
51
+ BaseHelper,
52
+ ApplicationError,
53
+ getError,
54
+ isApplicationError,
55
+ MessageCode,
56
+ ErrorScopes,
57
+ Logger,
58
+ isClass,
59
+ isClassProvider,
60
+ } from '@venizia/ignis-inversion';
61
+
62
+ import type {
63
+ TNullable,
64
+ ValueOrPromise,
65
+ ValueOf,
66
+ TClass,
67
+ TConstructor,
68
+ TAbstractConstructor,
69
+ TConstValue,
70
+ TBindingKey,
71
+ TBindingScope,
72
+ TBindingValueType,
73
+ IProvider,
74
+ IBinding,
75
+ IContainer,
76
+ IInjectMetadata,
77
+ IPropertyMetadata,
78
+ IBindingTag,
79
+ } from '@venizia/ignis-inversion';
80
+ ```
81
+
82
+ > [!NOTE]
83
+ > 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 are also available via type-only re-exports.
84
+
85
+ ## Class Hierarchy
86
+
87
+ `Source ->` [`container/abstract.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/container/abstract.ts), [`container/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/container/base.ts), [`container/container.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/container/container.ts)
88
+
89
+ ```
90
+ AbstractContainer extends BaseHelper implements IContainer # contract only - every member abstract
91
+ └── BaseContainer # storage: bind/lookup/tags/lifecycle, `instantiate` still abstract
92
+ └── Container # instantiate() = two-phase decorator injection
93
+ ```
94
+
95
+ `AbstractContainer` exists so a container implementation that shares nothing with the shipped storage can start there; one that only wants to vary resolution extends `BaseContainer` instead. `binding/` and `container/` only talk to each other through `IContainer`/`IBinding` - there is no import cycle between the two folders.
96
+
97
+ ## Creating a Container
98
+
99
+ ```typescript
100
+ import { Container } from '@venizia/ignis-inversion';
101
+
102
+ const container = new Container({ scope: 'MyApp' });
103
+ ```
104
+
105
+ `scope` is optional and defaults to `'Container'` (or `'BaseContainer'`/`'AbstractContainer'` for those classes). It is passed through to `BaseHelper` and used for logging/error context only - it has no effect on binding resolution.
106
+
107
+ ## Container
108
+
109
+ `Source ->` [`container/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/container/base.ts), [`container/container.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/container/container.ts)
110
+
111
+ | Method | Signature | Description |
112
+ |--------|-----------|--------------|
113
+ | `bind` | `bind<T>(opts: { key: TBindingKey }): Binding<T>` | Create a new `Binding`, register it under `String(key)`, and return it |
114
+ | `get` | `get<T>(opts: { key: TBindingKey \| { namespace, key }, isOptional?: boolean }): T \| undefined` | Resolve a dependency by key. Throws if not found and `isOptional` is falsy |
115
+ | `gets` | `gets<T>(opts: { bindings: Array<{ key, isOptional? }> }): T[]` | Resolve multiple dependencies; every entry is internally re-issued with `isOptional: true` regardless of what was passed |
116
+ | `getBinding` | `getBinding<T>(opts: { key: TBindingKey \| { namespace, key } }): Binding<T> \| undefined` | Retrieve the raw `Binding` without resolving it |
117
+ | `set` | `set<T>(opts: { binding: Binding<T> }): void` | Register an externally created `Binding` under its own `.key` |
118
+ | `isBound` | `isBound(opts: { key: TBindingKey }): boolean` | Check whether a key is registered |
119
+ | `unbind` | `unbind(opts: { key: TBindingKey }): boolean` | Remove a binding; returns `true` if one was removed |
120
+ | `resolve` | `resolve<T>(cls: TClass<T>): T` | Alias for `instantiate` |
121
+ | `instantiate` | `instantiate<T>(cls: TClass<T>): T` | Build an instance with full DI (constructor + property injection), see below |
122
+ | `findByTag` | `findByTag<T>(opts: { tag: string, exclude?: string[] \| Set<string> }): Binding<T>[]` | Find every binding with a matching tag, optionally excluding keys |
123
+ | `clear` | `clear(): void` | Clear every binding's singleton cache; bindings themselves remain registered |
124
+ | `reset` | `reset(): void` | Remove all bindings entirely |
125
+ | `getMetadataRegistry` | `getMetadataRegistry(): MetadataRegistry` | Return the shared `metadataRegistry` singleton |
126
+
127
+ All keys passed to `bind`, `isBound`, and `unbind` are normalized with `String(key)` before being used as the `Map` key - a `Symbol` key resolves to its `.toString()` form (`'Symbol(...)'`), so a `symbol` and the equivalent string are distinct entries.
128
+
129
+ ### `instantiate()` - two-phase algorithm
130
+
131
+ ```typescript
132
+ override instantiate<T>(cls: TClass<T>): T
133
+ ```
134
+
135
+ **Phase 1 - constructor injection.** Reads `registry.getInjectMetadata({ target: cls })`, which returns the index-keyed `IInjectMetadata[]` array built by `@inject`. For every index in that array:
136
+
137
+ - If the slot is empty (an undecorated parameter left a hole), 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`.
138
+ - Otherwise resolves `this.get({ key: meta.key, isOptional: meta.isOptional ?? false })` and places it at `args[meta.index]`.
139
+
140
+ The array is already index-keyed (`setInjectMetadata` writes to `injects[index]`) - there is no sort step. Once all arguments are resolved, `new cls(...args)` builds the instance.
141
+
142
+ **Phase 2 - property injection.** Reads `registry.getPropertiesMetadata({ target: instance })`. If there is none, returns the instance as-is. Otherwise, for each `[propertyKey, metadata]` entry, resolves `this.get({ key: metadata.bindingKey, isOptional: metadata.optional ?? false })` and assigns it to `instance[propertyKey]`.
143
+
144
+ > [!WARNING]
145
+ > Property injection's optional check reads `metadata.optional`, but `@inject` writes `IPropertyMetadata.isOptional` (see [`IPropertyMetadata`](#metadataregistry-types) below) - `metadata.optional` is always `undefined` at runtime, so `metadata.optional ?? false` always evaluates to `false`. In practice, `@inject({ key, isOptional: true })` on a **property** has no effect: an unbound property dependency still throws. This is a verified source-level discrepancy in `container.ts`, not a documented API - `isOptional` on a **constructor parameter** (`meta.isOptional`, phase 1) works correctly. If you need an optional dependency, use `@inject` on a constructor parameter, or resolve it manually with `container.get({ key, isOptional: true })` inside the constructor.
146
+
147
+ ### Key formats
148
+
149
+ `get`, `getBinding`, and `gets` accept three key shapes:
150
+
151
+ ```typescript
152
+ container.get<UserService>({ key: 'services.UserService' });
153
+ container.get<UserService>({ key: Symbol.for('services.UserService') });
154
+ container.get<UserService>({ key: { namespace: 'services', key: 'UserService' } });
155
+ ```
156
+
157
+ `getBinding` throws `[getBinding] Invalid binding key type | ...` if `opts.key` is not a `string`, `symbol`, or `{ namespace, key }` object.
158
+
159
+ ### `gets()` behavior
160
+
161
+ ```typescript
162
+ const [svcA, svcB] = container.gets<[ServiceA, ServiceB]>({
163
+ bindings: [
164
+ { key: 'services.ServiceA' },
165
+ { key: 'services.ServiceB', isOptional: true },
166
+ ],
167
+ });
168
+ ```
169
+
170
+ Internally maps each entry through `this.get({ ...opt, isOptional: true })` - regardless of what `isOptional` was set to on the entry, `gets()` always resolves with `isOptional: true` and returns `undefined` for anything unbound rather than throwing.
171
+
172
+ ## Binding
173
+
174
+ `Source ->` [`binding/binding.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/binding/binding.ts)
175
+
176
+ | Method | Signature | Description |
177
+ |--------|-----------|--------------|
178
+ | `toClass` | `toClass(value: TClass<T>): this` | Container instantiates `value` with full DI when resolved |
179
+ | `toValue` | `toValue(value: T): this` | Returns `value` directly, no instantiation |
180
+ | `toProvider` | `toProvider(value: ((container) => T) \| TClass<IProvider<T>>): this` | Factory function, or a class whose prototype has a `value()` method |
181
+ | `setScope` | `setScope(scope: TBindingScope): this` | `'singleton'` or `'transient'` (default) |
182
+ | `setTags` | `setTags(...tags: string[]): this` | Adds tags to the internal `Set<string>` |
183
+ | `hasTag` | `hasTag(tag: string): boolean` | Check for a specific tag |
184
+ | `getTags` | `getTags(): string[]` | All tags as an array |
185
+ | `getScope` | `getScope(): TBindingScope` | Current scope |
186
+ | `getValue` | `getValue(container?: IContainer): T` | Resolve the bound value, respecting scope caching |
187
+ | `getBindingMeta` | `getBindingMeta(opts: { type: TBindingValueType }): TBindingResolverValue<T>` | Raw resolver value; throws if `type` does not match the actual resolver |
188
+ | `clearCache` | `clearCache(): void` | Clears this binding's singleton cache (no-op if nothing cached) |
189
+ | `bind` (static) | `static bind<T>(opts: { key: string }): Binding<T>` | Create a `Binding` outside a container |
190
+
191
+ ### Constructor and namespace auto-tagging
192
+
193
+ ```typescript
194
+ constructor(opts: { key: string })
195
+ ```
196
+
197
+ Splits `key` on `.`; if there is more than one segment, the first segment is auto-added as a tag via `setTags()`. `'services.UserService'` auto-tags `'services'`; a key with no `.` gets no automatic tag.
198
+
199
+ ### `getValue()` resolution by type
200
+
201
+ | Resolver type | Behavior | Throws when |
202
+ |----------------|----------|--------------|
203
+ | `VALUE` | Returns the stored value directly | Never |
204
+ | `PROVIDER` | If the stored value is a plain function, calls `provider(container)`. If it is a class matching `isClassProvider` (prototype has a `value()` method), the container `instantiate()`s the class first, then calls `.value(container)` on the instance | No `container` argument was passed - `[getValue] Invalid context/container to get provider value` |
205
+ | `CLASS` | `container.instantiate(this.resolver.value)` | No `container` argument was passed - `[getValue] Invalid context/container to instantiate class` |
206
+
207
+ If `bindScope` is `SINGLETON`, the resolved instance is cached on `this.cached` and returned directly on every subsequent call without re-invoking the resolver - caching is per-`Binding` instance, not per-container.
208
+
209
+ ### Class-based provider
210
+
211
+ ```typescript
212
+ import { IProvider, Container } from '@venizia/ignis-inversion';
213
+
214
+ class DatabaseConnectionProvider implements IProvider<DatabaseConnection> {
215
+ value(container: Container): DatabaseConnection {
216
+ const config = container.get<Config>({ key: 'config.database' });
217
+ return new DatabaseConnection(config);
218
+ }
219
+ }
220
+
221
+ container.bind<DatabaseConnection>({ key: 'db.connection' }).toProvider(DatabaseConnectionProvider);
222
+ ```
223
+
224
+ `isClassProvider` detects this shape at runtime: `typeof target === 'function' && target.prototype && typeof target.prototype.value === 'function'`.
225
+
226
+ ### Static factory
227
+
228
+ ```typescript
229
+ import { Binding, BindingScopes } from '@venizia/ignis-inversion';
230
+
231
+ const binding = Binding.bind<IHealthCheckOptions>({ key: 'options.healthCheck' })
232
+ .toValue({ restOptions: { path: '/health' } });
233
+
234
+ container.set({ binding }); // registers under binding.key
235
+ ```
236
+
237
+ ## MetadataRegistry
238
+
239
+ `Source ->` [`registry/registry.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/registry/registry.ts)
240
+
241
+ Singleton (`metadataRegistry`) backing `@inject`, built entirely on `reflect-metadata`'s `Reflect.defineMetadata`/`getMetadata`/`hasMetadata`/`deleteMetadata`. You typically interact with it only through `container.getMetadataRegistry()` or the decorators.
242
+
243
+ | Method | Signature | Description |
244
+ |--------|-----------|--------------|
245
+ | `define` | `define<Target, Value>(opts: { target, key: TBindingKey, value: Value }): void` | Store arbitrary metadata on a target |
246
+ | `get` | `get<Target, Value>(opts: { target, key: TBindingKey }): Value \| undefined` | Retrieve metadata by key |
247
+ | `has` | `has<Target>(opts: { target, key: TBindingKey }): boolean` | Check if metadata exists |
248
+ | `delete` | `delete<Target>(opts: { target, key: TBindingKey }): boolean` | Remove metadata by key |
249
+ | `getKeys` | `getKeys<Target>(opts: { target }): TBindingKey[]` | List all metadata keys (`string`/`symbol` only) on a target |
250
+ | `getMethodNames` | `getMethodNames<T>(opts: { target: TClass<T> }): string[]` | Non-constructor function-valued own property names on the prototype |
251
+ | `clearMetadata` | `clearMetadata<T>(opts: { target }): void` | Delete every metadata key on a target |
252
+ | `setInjectMetadata` | `setInjectMetadata<T>(opts: { target, index: number, metadata: IInjectMetadata }): void` | Write constructor `@inject` metadata at parameter `index` into the `MetadataKeys.INJECT` array |
253
+ | `getInjectMetadata` | `getInjectMetadata<T>(opts: { target }): IInjectMetadata[] \| undefined` | Read the constructor injection array |
254
+ | `setPropertyMetadata` | `setPropertyMetadata<T>(opts: { target, propertyName, metadata: IPropertyMetadata }): void` | Write property `@inject` metadata into a `Map` keyed by property name, stored on `target.constructor` |
255
+ | `getPropertiesMetadata` | `getPropertiesMetadata<T>(opts: { target }): Map<string \| symbol, IPropertyMetadata> \| undefined` | Read the full property metadata map |
256
+ | `getPropertyMetadata` | `getPropertyMetadata<T>(opts: { target, propertyName }): IPropertyMetadata \| undefined` | Read metadata for one property |
257
+
258
+ ```typescript
259
+ import { MetadataKeys, metadataRegistry } from '@venizia/ignis-inversion';
260
+
261
+ MetadataKeys.PROPERTIES; // Symbol.for('ignis:properties')
262
+ MetadataKeys.INJECT; // Symbol.for('ignis:inject')
263
+
264
+ metadataRegistry.define({ target: myObj, key: 'custom:flag', value: true });
265
+ metadataRegistry.get({ target: myObj, key: 'custom:flag' }); // true
266
+ metadataRegistry.has({ target: myObj, key: 'custom:flag' }); // true
267
+ metadataRegistry.delete({ target: myObj, key: 'custom:flag' }); // true
268
+ ```
269
+
270
+ ### MetadataRegistry types
271
+
272
+ ```typescript
273
+ interface IInjectMetadata {
274
+ key: TBindingKey;
275
+ index: number;
276
+ isOptional?: boolean;
277
+ }
278
+
279
+ interface IPropertyMetadata {
280
+ bindingKey: TBindingKey;
281
+ isOptional?: boolean;
282
+ [key: string]: any;
283
+ }
284
+ ```
285
+
286
+ ## Decorators
287
+
288
+ `Source ->` [`metadata/injectors.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/metadata/injectors.ts)
289
+
290
+ ### `@inject`
291
+
292
+ ```typescript
293
+ inject(opts: { key: TBindingKey; isOptional?: boolean; registry?: MetadataRegistry }): PropertyDecorator | ParameterDecorator
294
+ ```
295
+
296
+ Dispatches on how the decorator was invoked:
297
+
298
+ | Applied to | Detection | Stored via |
299
+ |------------|-----------|------------|
300
+ | Constructor parameter | `parameterIndex` is a `number` | `registry.setInjectMetadata({ target, index: parameterIndex, metadata: { key, index: parameterIndex, isOptional } })` |
301
+ | Class property | `propertyName !== undefined` | `registry.setPropertyMetadata({ target, propertyName, metadata: { bindingKey: key, isOptional } })` |
302
+ | Anything else | neither condition matches | Throws `@inject decorator can only be used on class properties or constructor parameters` |
303
+
304
+ `isOptional` defaults to `false` in both branches. Pass a custom `registry` to target a non-default `MetadataRegistry` instance (rare - almost always omitted, using the shared `metadataRegistry`).
305
+
306
+ ```typescript
307
+ class UserService {
308
+ constructor(
309
+ @inject({ key: 'repositories.UserRepository' }) private userRepository: UserRepository,
310
+ @inject({ key: 'services.Logger', isOptional: true }) private logger?: Logger,
311
+ ) {}
312
+
313
+ @inject({ key: 'config.retryCount' })
314
+ private retryCount: number;
315
+ }
316
+ ```
317
+
318
+ ## Namespaces and Tags
319
+
320
+ ```typescript
321
+ import { BindingKeys } from '@venizia/ignis-inversion';
322
+
323
+ BindingKeys.build({ namespace: 'services', key: 'UserService' });
324
+ // => 'services.UserService'
325
+
326
+ BindingKeys.build({ namespace: '', key: 'UserService' });
327
+ // => 'UserService' (empty namespace segment is dropped)
328
+
329
+ BindingKeys.build({ namespace: 'services', key: '' });
330
+ // throws: [BindingKeys][build] Invalid key to build | key:
331
+ ```
332
+
333
+ `key` is required and must be non-empty; `namespace` is optional and silently omitted from the joined string when empty.
334
+
335
+ ```typescript
336
+ container.bind({ key: 'workers.EmailWorker' }).toClass(EmailWorker).setTags('background', 'email');
337
+ // tags: ['workers', 'background', 'email']
338
+
339
+ const serviceBindings = container.findByTag({ tag: 'services' });
340
+ const filtered = container.findByTag({ tag: 'services', exclude: ['services.InternalService'] });
341
+ ```
342
+
343
+ ## Utilities
344
+
345
+ ### ApplicationError and getError
346
+
347
+ `Source ->` [`modules/error/app-error.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/error/app-error.ts)
348
+
349
+ ```typescript
350
+ class ApplicationError extends Error {
351
+ statusCode: number;
352
+ normalized: { text: string; code: string; args: Record<string, unknown> };
353
+ extra?: Record<string, unknown>;
354
+ }
355
+
356
+ getError(opts: TError): ApplicationError; // factory function
357
+ ```
358
+
359
+ `opts.message` accepts two shapes: the historical string (paired with sibling `messageCode?`/`messageArgs?`), or an object mirroring `normalized` - `{ text, code?, args? }`. Both resolve to the same `normalized`; `messageCode`/`messageArgs` are lowest precedence, so `message.code`/`message.args` (or a catalogued definition's own `message.code`/`message.args`) win when both are present. There is no flat `error.messageCode`, and `extra` never mirrors `messageArgs`. `normalized.args` is always populated (`{}` when empty).
360
+
361
+ The catalogued form (`{ error: TErrorDefinition }`) takes `message` as a **partial** override - `{ message: { args } }` amends just the args and keeps the definition's `text`/`code`. `error` is refused on the free-form branch (`error?: never`) - wrap a caught failure with `cause` instead.
362
+
363
+ `ApplicationError`'s constructor defaults `statusCode` to `400` when omitted, and moves any property it does not model into `this.extra`. The error RESPONSE schema (`ErrorSchema`, for OpenAPI) lives in `@venizia/ignis-helpers`, not here - it needs `@hono/zod-openapi`, which inversion must not depend on because it ships to browsers.
364
+
365
+ ```typescript
366
+ throw getError({ message: 'Something failed', statusCode: 500, messageCode: 'ERR_INTERNAL' });
367
+ throw new ApplicationError({ message: 'Not found', statusCode: 404 });
368
+ throw new ApplicationError({ message: { text: 'Not found', code: 'core.user.not_found' }, statusCode: 404 });
369
+
370
+ // The code and args are read off `normalized`, never off the error directly.
371
+ error.normalized.code; // 'err_internal'
372
+ ```
373
+
374
+ See the [Error reference](/extensions/helpers/error/) for the full input shape, precedence rules, and the catalogued (`TErrorDefinition`) pattern.
375
+
376
+ ### Logger
377
+
378
+ `Source ->` [`common/logger.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/common/logger.ts)
379
+
380
+ A minimal `console`-backed static logger, independent of `@venizia/ignis-helpers`' `Logger`/`LoggerFactory` - this one exists so the inversion package has zero dependency on the logging package.
381
+
382
+ ```typescript
383
+ Logger.info('Server started on port %d', 3000); // console.log('[INFO] ...')
384
+ Logger.warn('Deprecation warning'); // console.warn('[WARN] ...')
385
+ Logger.error('Connection failed: %s', err.message); // console.error('[ERROR] ...')
386
+ Logger.debug('Resolved binding: %s', key); // console.log('[DEBUG] ...') only if process.env.DEBUG is set
387
+ ```
388
+
389
+ ### Type guards and shared types
390
+
391
+ `Source ->` [`common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/common/types.ts)
392
+
393
+ ```typescript
394
+ type TNullable<T> = T | undefined | null;
395
+ type ValueOrPromise<T> = T | Promise<T>;
396
+ type ValueOf<T> = T[keyof T];
397
+ type TConstructor<T> = new (...args: any[]) => T;
398
+ type TAbstractConstructor<T> = abstract new (...args: any[]) => T;
399
+ type TClass<T> = TConstructor<T> & { [property: string]: any };
400
+ type TConstValue<T extends TClass<any>> = Extract<ValueOf<T>, string | number>;
401
+ type TBindingKey = string | symbol;
402
+
403
+ interface IBindingTag {
404
+ [name: string]: any;
405
+ }
406
+
407
+ function isClass<T>(target: any): target is TClass<T>;
408
+ ```
409
+
410
+ `isClass` tests `typeof target === 'function' && target.prototype !== undefined` plus a regex match on the function's stringified source (`/^class[\s{]/`) - it relies on the class being emitted as an ES2024 `class`, not transpiled down to an ES5 constructor function.
411
+
412
+ ```typescript
413
+ interface IProvider<T> {
414
+ value(container: Container): T;
415
+ }
416
+
417
+ function isClassProvider<T>(target: any): target is TClass<IProvider<T>>;
418
+ ```
419
+
420
+ ## Constants
421
+
422
+ | Constant | Values | Description |
423
+ |----------|--------|--------------|
424
+ | `BindingScopes.SINGLETON` | `'singleton'` | Cached after first resolution |
425
+ | `BindingScopes.TRANSIENT` | `'transient'` | New instance each resolution |
426
+ | `BindingValueTypes.CLASS` | `'class'` | Container instantiates with DI |
427
+ | `BindingValueTypes.VALUE` | `'value'` | Direct value return |
428
+ | `BindingValueTypes.PROVIDER` | `'provider'` | Factory function or `IProvider` class |
429
+ | `MetadataKeys.PROPERTIES` | `Symbol.for('ignis:properties')` | Property injection metadata key |
430
+ | `MetadataKeys.INJECT` | `Symbol.for('ignis:inject')` | Constructor injection metadata key |
431
+
432
+ ## Troubleshooting
433
+
434
+ ### "Binding key: X is not bounded in context!"
435
+
436
+ **Cause:** The dependency was never registered with the container, or the key does not match exactly.
437
+
438
+ **Fix:**
439
+ 1. Verify the binding exists: `container.isBound({ key: 'services.UserService' })`.
440
+ 2. Check for typos between `@inject({ key: '...' })` and the key used in `container.bind({ key: '...' })`.
441
+ 3. If the dependency is genuinely optional, use `@inject({ key: '...', isOptional: true })` on a **constructor parameter** (not a property - see the warning above), or `container.get({ key: '...', isOptional: true })`.
442
+
443
+ ### "[getValue] Invalid context/container to instantiate class"
444
+
445
+ **Cause:** A `Binding` configured with `toClass()` was resolved by calling `binding.getValue()` directly, without a `Container` argument.
446
+
447
+ **Fix:** Resolve class bindings through the container - `container.get({ key })` - rather than calling `binding.getValue()` with no arguments.
448
+
449
+ ### "[getValue] Invalid context/container to get provider value"
450
+
451
+ **Cause:** A `Binding` configured with `toProvider()` was resolved without a `Container` argument.
452
+
453
+ **Fix:** Same as above - always resolve through `container.get({ key })`.
454
+
455
+ ### "[getBindingMeta] Invalid resolver type"
456
+
457
+ **Cause:** `getBindingMeta({ type })` was called with a type that does not match the binding's actual resolver (e.g. `'class'` on a value binding).
458
+
459
+ **Fix:** Match `type` to how the binding was created: `toClass()` -> `'class'`, `toValue()` -> `'value'`, `toProvider()` -> `'provider'`.
460
+
461
+ ### "[getBinding] Invalid binding key type"
462
+
463
+ **Cause:** The key passed to `getBinding()` is not a `string`, `symbol`, or `{ namespace, key }` object.
464
+
465
+ **Fix:** Use one of the three supported key formats.
466
+
467
+ ### "[BindingKeys][build] Invalid key to build"
468
+
469
+ **Cause:** `BindingKeys.build()` was called with an empty `key`.
470
+
471
+ **Fix:** Provide a non-empty `key`: `BindingKeys.build({ namespace: 'services', key: 'UserService' })`.
472
+
473
+ ### "[ClassName] Constructor parameter N has no @inject"
474
+
475
+ **Cause:** A container-instantiated class has a constructor mixing decorated and undecorated parameters. `@inject` stores metadata at the parameter's index, so an undecorated parameter leaves a hole in that array; there is no channel through which the container could supply it anyway.
476
+
477
+ **Fix:** Decorate every constructor parameter 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.
478
+
479
+ ### "@inject decorator can only be used on class properties or constructor parameters"
480
+
481
+ **Cause:** `@inject` was applied to something other than a class property or constructor parameter.
482
+
483
+ **Fix:** Only use `@inject` on constructor parameters or class properties.
484
+
485
+ ### Property injection returns `undefined` even without `isOptional`
486
+
487
+ **Cause:** Either (a) the class was instantiated with `new MyClass()` directly instead of through the container - only the container reads `@inject` metadata and populates properties - or (b) the binding key really is unbound, and the `isOptional: true` you set on the property has no effect (see the phase-2 warning above, `metadata.optional` vs `metadata.isOptional`).
488
+
489
+ **Fix:** Always use `container.resolve(MyClass)` or `container.instantiate(MyClass)` to create instances. If the dependency may legitimately be absent, inject it on the constructor instead of a property, or resolve it manually inside the constructor with `container.get({ key, isOptional: true })`.
490
+
491
+ ### "getInjectMetadata returns undefined"
492
+
493
+ **Cause:** `reflect-metadata` was not imported before decorators were evaluated, or `experimentalDecorators`/`emitDecoratorMetadata` are not enabled in `tsconfig.json`.
494
+
495
+ **Fix:**
496
+ 1. Ensure `import 'reflect-metadata'` runs before any decorated class is evaluated - `@venizia/ignis-inversion`'s entry point already does this, so importing anything from the package is enough.
497
+ 2. Verify `tsconfig.json` includes:
498
+ ```json
499
+ {
500
+ "compilerOptions": {
501
+ "experimentalDecorators": true,
502
+ "emitDecoratorMetadata": true
503
+ }
504
+ }
505
+ ```
506
+
507
+ ### Singleton returns a stale instance after rebinding
508
+
509
+ **Cause:** Singleton caching is per-`Binding` object. Holding a direct reference to an old `Binding` (e.g. from `getBinding()`) keeps its cache independent of any new binding registered under the same key.
510
+
511
+ **Fix:**
512
+ 1. Always resolve via `container.get()` rather than caching `Binding` references.
513
+ 2. Call `container.clear()` to clear every binding's singleton cache without removing registrations.
514
+ 3. Call `container.reset()` to remove all bindings entirely.
515
+
516
+ ## See also
517
+
518
+ - [Inversion overview](/extensions/helpers/inversion/) - introduction and the most common tasks
519
+ - [Dependency Injection Guide](/guides/core-concepts/dependency-injection) - DI fundamentals in the framework layer
520
+ - [Application](/guides/core-concepts/application/) - `Application` extends `Container`
521
+ - [Dependency Injection API](/references/base/dependency-injection) - the framework-layer DI reference
522
+ - [Architectural Patterns](/best-practices/architectural-patterns) - DI patterns
@@ -1,3 +1,9 @@
1
+ ---
2
+ title: Kafka Admin
3
+ description: KafkaAdminHelper - topic, group, offset, ACL, and quota management
4
+ difficulty: intermediate
5
+ ---
6
+
1
7
  # Admin
2
8
 
3
9
  The `KafkaAdminHelper` wraps `@platformatic/kafka`'s `Admin` with health tracking, graceful shutdown, and broker event callbacks. Use `getAdmin()` to access the full Admin API directly.
@@ -31,7 +37,7 @@ interface IKafkaAdminOptions extends IKafkaConnectionOptions {
31
37
  }
32
38
  ```
33
39
 
34
- Plus all [Connection Options](./#connection-options).
40
+ Plus the shared [Connection & Authentication](./producer#connection--authentication) options (`bootstrapBrokers`, `clientId`, `retries`, `sasl`, `tls`, ...), documented once on the Producer page.
35
41
 
36
42
  ## Basic Example
37
43
 
@@ -188,3 +194,16 @@ await admin.alterConsumerGroupOffsets({
188
194
  | Method | Signature | Description |
189
195
  |--------|-----------|-------------|
190
196
  | `describeLogDirs(opts)` | `(opts: { topics }): Promise<BrokerLogDirDescription[]>` | Describe broker log directories |
197
+
198
+ ## See also
199
+
200
+ - [Kafka Overview](./) - the four helpers, shared health/close API, and the compile-binary caveat
201
+ - [Producer](./producer) - the shared Connection & Authentication options
202
+ - [Consumer](./consumer) - `groupInstanceId`, rebalance behavior, and lag monitoring for the groups this page manages
203
+ - [Examples & Troubleshooting](./examples) - a full topic-setup script and IoC wiring
204
+
205
+ **Files:**
206
+
207
+ - [`packages/helpers/src/modules/queue/kafka/admin.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/queue/kafka/admin.ts) - `KafkaAdminHelper`
208
+ - [`packages/helpers/src/modules/queue/kafka/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/queue/kafka/base.ts) - `BaseKafkaHelper`, shared health tracking and shutdown
209
+ - [`packages/helpers/src/modules/queue/kafka/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/queue/kafka/common/types.ts) - `IKafkaAdminOptions`