@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.
- package/README.md +14 -14
- package/content/best-practices/api-usage-examples.md +39 -19
- package/content/best-practices/architectural-patterns.md +24 -13
- package/content/best-practices/architecture-decisions.md +29 -16
- package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
- package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
- package/content/best-practices/code-style-standards/control-flow.md +33 -1
- package/content/best-practices/code-style-standards/documentation.md +28 -8
- package/content/best-practices/code-style-standards/function-patterns.md +15 -4
- package/content/best-practices/code-style-standards/index.md +7 -2
- package/content/best-practices/code-style-standards/naming-conventions.md +27 -3
- package/content/best-practices/code-style-standards/route-definitions.md +9 -5
- package/content/best-practices/code-style-standards/tooling.md +14 -1
- package/content/best-practices/code-style-standards/type-safety.md +39 -6
- package/content/best-practices/common-pitfalls.md +59 -35
- package/content/best-practices/contribution-workflow.md +8 -4
- package/content/best-practices/data-modeling.md +78 -62
- package/content/best-practices/deployment-strategies.md +56 -19
- package/content/best-practices/error-handling.md +247 -153
- package/content/best-practices/index.md +6 -0
- package/content/best-practices/performance-optimization.md +26 -13
- package/content/best-practices/security-guidelines.md +35 -9
- package/content/best-practices/testing-strategies.md +7 -2
- package/content/best-practices/troubleshooting-tips.md +27 -17
- package/content/extensions/components/api-reference.md +109 -323
- package/content/extensions/components/authentication/api.md +489 -602
- package/content/extensions/components/authentication/errors.md +135 -498
- package/content/extensions/components/authentication/index.md +89 -801
- package/content/extensions/components/authentication/usage.md +231 -955
- package/content/extensions/components/authorization/api.md +991 -644
- package/content/extensions/components/authorization/errors.md +204 -208
- package/content/extensions/components/authorization/getting-started.md +227 -0
- package/content/extensions/components/authorization/index.md +88 -795
- package/content/extensions/components/authorization/usage.md +219 -528
- package/content/extensions/components/health-check.md +77 -243
- package/content/extensions/components/index.md +24 -90
- package/content/extensions/components/mail/api.md +553 -285
- package/content/extensions/components/mail/errors.md +76 -62
- package/content/extensions/components/mail/index.md +111 -463
- package/content/extensions/components/mail/usage.md +139 -173
- package/content/extensions/components/request-tracker.md +70 -173
- package/content/extensions/components/socket-io/api.md +462 -785
- package/content/extensions/components/socket-io/errors.md +49 -51
- package/content/extensions/components/socket-io/index.md +69 -372
- package/content/extensions/components/socket-io/usage.md +212 -105
- package/content/extensions/components/static-asset/api.md +461 -141
- package/content/extensions/components/static-asset/errors.md +121 -53
- package/content/extensions/components/static-asset/index.md +84 -606
- package/content/extensions/components/static-asset/usage.md +184 -299
- package/content/extensions/components/template/index.md +3 -3
- package/content/extensions/components/websocket/api.md +311 -404
- package/content/extensions/components/websocket/errors.md +47 -56
- package/content/extensions/components/websocket/index.md +75 -407
- package/content/extensions/components/websocket/usage.md +120 -338
- package/content/extensions/helpers/cron/index.md +52 -160
- package/content/extensions/helpers/crypto/index.md +67 -480
- package/content/extensions/helpers/crypto/reference.md +528 -0
- package/content/extensions/helpers/env/index.md +64 -178
- package/content/extensions/helpers/error/index.md +286 -196
- package/content/extensions/helpers/index.md +61 -47
- package/content/extensions/helpers/inversion/index.md +76 -550
- package/content/extensions/helpers/inversion/reference.md +530 -0
- package/content/extensions/helpers/kafka/admin.md +23 -3
- package/content/extensions/helpers/kafka/compile-binary.md +83 -52
- package/content/extensions/helpers/kafka/consumer.md +65 -28
- package/content/extensions/helpers/kafka/examples.md +46 -253
- package/content/extensions/helpers/kafka/index.md +55 -617
- package/content/extensions/helpers/kafka/producer.md +156 -22
- package/content/extensions/helpers/kafka/schema-registry.md +59 -79
- package/content/extensions/helpers/logger/hf-logger.md +220 -0
- package/content/extensions/helpers/logger/index.md +78 -552
- package/content/extensions/helpers/logger/pino.md +105 -0
- package/content/extensions/helpers/logger/reference.md +937 -0
- package/content/extensions/helpers/network/api.md +276 -195
- package/content/extensions/helpers/network/index.md +87 -524
- package/content/extensions/helpers/queue/index.md +80 -897
- package/content/extensions/helpers/queue/reference.md +494 -0
- package/content/extensions/helpers/redis/index.md +86 -640
- package/content/extensions/helpers/redis/reference.md +784 -0
- package/content/extensions/helpers/secrets/index.md +136 -0
- package/content/extensions/helpers/socket-io/api.md +325 -204
- package/content/extensions/helpers/socket-io/index.md +79 -429
- package/content/extensions/helpers/storage/api.md +584 -464
- package/content/extensions/helpers/storage/index.md +80 -573
- package/content/extensions/helpers/types/index.md +75 -495
- package/content/extensions/helpers/types/reference.md +689 -0
- package/content/extensions/helpers/uid/index.md +176 -189
- package/content/extensions/helpers/websocket/api.md +366 -214
- package/content/extensions/helpers/websocket/index.md +68 -503
- package/content/extensions/helpers/worker-thread/index.md +62 -396
- package/content/extensions/helpers/worker-thread/reference.md +428 -0
- package/content/extensions/index.md +38 -39
- package/content/extensions/src-details/mcp-server.md +96 -548
- package/content/guides/core-concepts/persistent/datasources.md +2 -2
- package/content/guides/core-concepts/persistent/index.md +5 -1
- package/content/guides/core-concepts/persistent/models.md +1 -1
- package/content/guides/core-concepts/persistent/pglite.md +218 -0
- package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
- package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
- package/content/guides/core-concepts/persistent/search-typesense.md +28 -16
- package/content/guides/core-concepts/persistent/sqlite.md +296 -0
- package/content/guides/core-concepts/persistent/transactions.md +12 -11
- package/content/guides/core-concepts/secrets-vault.md +177 -0
- package/content/guides/core-concepts/services.md +1 -1
- package/content/guides/get-started/5-minute-quickstart.md +59 -206
- package/content/guides/get-started/philosophy.md +135 -670
- package/content/guides/get-started/setup.md +53 -74
- package/content/guides/migrations/redis-helpers-migration.md +5 -4
- package/content/guides/migrations/scoped-rbac-migration.md +5 -5
- package/content/guides/migrations/unified-connectors-migration.md +8 -7
- package/content/guides/tutorials/ecommerce-api.md +3 -8
- package/content/guides/tutorials/realtime-chat.md +1 -1
- package/content/references/base/application.md +64 -22
- package/content/references/base/components.md +3 -3
- package/content/references/base/connectors.md +79 -136
- package/content/references/base/controllers.md +12 -12
- package/content/references/base/datasources-reference.md +600 -0
- package/content/references/base/datasources.md +84 -444
- package/content/references/base/dependency-injection.md +25 -46
- package/content/references/base/filter-system/application-usage.md +95 -123
- package/content/references/base/filter-system/array-operators.md +33 -43
- package/content/references/base/filter-system/comparison-operators.md +55 -63
- package/content/references/base/filter-system/default-filter.md +144 -350
- package/content/references/base/filter-system/fields-order-pagination.md +116 -148
- package/content/references/base/filter-system/index.md +114 -253
- package/content/references/base/filter-system/json-filtering.md +52 -181
- package/content/references/base/filter-system/list-operators.md +28 -44
- package/content/references/base/filter-system/logical-operators.md +69 -114
- package/content/references/base/filter-system/null-operators.md +41 -98
- package/content/references/base/filter-system/pattern-matching.md +48 -51
- package/content/references/base/filter-system/quick-reference.md +93 -196
- package/content/references/base/filter-system/range-operators.md +22 -38
- package/content/references/base/filter-system/tips.md +70 -133
- package/content/references/base/filter-system/use-cases.md +173 -232
- package/content/references/base/grpc-controllers.md +53 -17
- package/content/references/base/index.md +5 -3
- package/content/references/base/middlewares.md +43 -28
- package/content/references/base/models-reference.md +886 -0
- package/content/references/base/models.md +81 -1452
- package/content/references/base/providers.md +8 -8
- package/content/references/base/repositories/advanced.md +281 -419
- package/content/references/base/repositories/index.md +84 -644
- package/content/references/base/repositories/mixins.md +25 -21
- package/content/references/base/repositories/relations.md +194 -375
- package/content/references/base/repositories/soft-deletable.md +67 -55
- package/content/references/base/secrets.md +267 -0
- package/content/references/base/services.md +8 -6
- package/content/references/configuration/environment-variables.md +73 -21
- package/content/references/configuration/index.md +51 -31
- package/content/references/index.md +1 -1
- package/content/references/quick-reference.md +3 -16
- package/content/references/utilities/crypto.md +35 -76
- package/content/references/utilities/date.md +33 -73
- package/content/references/utilities/duration.md +85 -0
- package/content/references/utilities/index.md +6 -2
- package/content/references/utilities/jsx-reference.md +298 -0
- package/content/references/utilities/jsx.md +82 -525
- package/content/references/utilities/module.md +81 -61
- package/content/references/utilities/parse.md +34 -64
- package/content/references/utilities/performance.md +33 -58
- package/content/references/utilities/promise.md +28 -62
- package/content/references/utilities/request.md +58 -218
- package/content/references/utilities/retry.md +139 -0
- package/content/references/utilities/schema.md +43 -137
- package/content/references/utilities/statuses-reference.md +361 -0
- package/content/references/utilities/statuses.md +63 -667
- package/dist/mcp-server/index.js +0 -0
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
- package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
- package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
- package/package.json +24 -23
|
@@ -0,0 +1,530 @@
|
|
|
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 these DI symbols from `@venizia/ignis-inversion`, types included: `Binding`, `BindingKeys`, `BindingScopes`, `BindingValueTypes`, `IProvider`, `isClass`, `isClassProvider`, `TBindingScope`, `TBindingValueType`, `IBindingTag`. It also adds higher-level helpers of its own (`app.controller()`, `app.service()`, etc.).
|
|
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.
|
|
143
|
+
|
|
144
|
+
Otherwise, for each `[propertyKey, metadata]` entry, resolves `this.get({ key: metadata.bindingKey, isOptional: metadata.isOptional ?? false })` and assigns it to `instance[propertyKey]`.
|
|
145
|
+
|
|
146
|
+
`@inject({ key, isOptional: true })` on a **property** behaves exactly like on a constructor parameter: an unbound key resolves to `undefined` instead of throwing. A required property (`isOptional` omitted or `false`) still throws when its key is unbound.
|
|
147
|
+
|
|
148
|
+
### Key formats
|
|
149
|
+
|
|
150
|
+
`get`, `getBinding`, and `gets` accept three key shapes:
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
container.get<UserService>({ key: 'services.UserService' });
|
|
154
|
+
container.get<UserService>({ key: Symbol.for('services.UserService') });
|
|
155
|
+
container.get<UserService>({ key: { namespace: 'services', key: 'UserService' } });
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`getBinding` throws `[getBinding] Invalid binding key type | ...` if `opts.key` is not a `string`, `symbol`, or `{ namespace, key }` object.
|
|
159
|
+
|
|
160
|
+
### `gets()` behavior
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
const [svcA, svcB] = container.gets<[ServiceA, ServiceB]>({
|
|
164
|
+
bindings: [
|
|
165
|
+
{ key: 'services.ServiceA' },
|
|
166
|
+
{ key: 'services.ServiceB', isOptional: true },
|
|
167
|
+
],
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Internally maps each entry through `this.get({ ...opt, isOptional: true })`. Regardless of what `isOptional` was set on the entry, `gets()` always resolves with `isOptional: true` - anything unbound returns `undefined` instead of throwing.
|
|
172
|
+
|
|
173
|
+
## Binding
|
|
174
|
+
|
|
175
|
+
`Source ->` [`binding/binding.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/binding/binding.ts)
|
|
176
|
+
|
|
177
|
+
| Method | Signature | Description |
|
|
178
|
+
|--------|-----------|--------------|
|
|
179
|
+
| `toClass` | `toClass(value: TClass<T>): this` | Container instantiates `value` with full DI when resolved |
|
|
180
|
+
| `toValue` | `toValue(value: T): this` | Returns `value` directly, no instantiation |
|
|
181
|
+
| `toProvider` | `toProvider(value: ((container) => T) \| TClass<IProvider<T>>): this` | Factory function, or a class whose prototype has a `value()` method |
|
|
182
|
+
| `setScope` | `setScope(scope: TBindingScope): this` | `'singleton'` or `'transient'` (default) |
|
|
183
|
+
| `setTags` | `setTags(...tags: string[]): this` | Adds tags to the internal `Set<string>` |
|
|
184
|
+
| `hasTag` | `hasTag(tag: string): boolean` | Check for a specific tag |
|
|
185
|
+
| `getTags` | `getTags(): string[]` | All tags as an array |
|
|
186
|
+
| `getScope` | `getScope(): TBindingScope` | Current scope |
|
|
187
|
+
| `getValue` | `getValue(container?: IContainer): T` | Resolve the bound value, respecting scope caching |
|
|
188
|
+
| `getBindingMeta` | `getBindingMeta(opts: { type: TBindingValueType }): TBindingResolverValue<T>` | Raw resolver value; throws if `type` does not match the actual resolver |
|
|
189
|
+
| `clearCache` | `clearCache(): void` | Clears this binding's singleton cache (no-op if nothing cached) |
|
|
190
|
+
| `bind` (static) | `static bind<T>(opts: { key: string }): Binding<T>` | Create a `Binding` outside a container |
|
|
191
|
+
|
|
192
|
+
### Constructor and namespace auto-tagging
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
constructor(opts: { key: string })
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
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.
|
|
199
|
+
|
|
200
|
+
### `getValue()` resolution by type
|
|
201
|
+
|
|
202
|
+
| Resolver type | Behavior | Throws when |
|
|
203
|
+
|----------------|----------|--------------|
|
|
204
|
+
| `VALUE` | Returns the stored value directly | Never |
|
|
205
|
+
| `PROVIDER` (plain function) | Calls `provider(container)` | No `container` argument was passed - `[getValue] Invalid context/container to get provider value` |
|
|
206
|
+
| `PROVIDER` (class, matched via `isClassProvider`) | `container.instantiate()`s the class, then calls `.value(container)` on the instance | Same as above |
|
|
207
|
+
| `CLASS` | `container.instantiate(this.resolver.value)` | No `container` argument was passed - `[getValue] Invalid context/container to instantiate class` |
|
|
208
|
+
|
|
209
|
+
`isClassProvider` matches a class whose prototype has a `value()` method - see [Class-based provider](#class-based-provider) below.
|
|
210
|
+
|
|
211
|
+
If `bindScope` is `SINGLETON`, the resolved instance is cached on `this.cached`. Every subsequent call returns it directly without re-invoking the resolver - caching is per-`Binding` instance, not per-container.
|
|
212
|
+
|
|
213
|
+
### Class-based provider
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
import { IProvider, Container } from '@venizia/ignis-inversion';
|
|
217
|
+
|
|
218
|
+
class DatabaseConnectionProvider implements IProvider<DatabaseConnection> {
|
|
219
|
+
value(container: Container): DatabaseConnection {
|
|
220
|
+
const config = container.get<Config>({ key: 'config.database' });
|
|
221
|
+
return new DatabaseConnection(config);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
container.bind<DatabaseConnection>({ key: 'db.connection' }).toProvider(DatabaseConnectionProvider);
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
`isClassProvider` detects this shape at runtime: `typeof target === 'function' && target.prototype && typeof target.prototype.value === 'function'`.
|
|
229
|
+
|
|
230
|
+
### Static factory
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
import { Binding, BindingScopes } from '@venizia/ignis-inversion';
|
|
234
|
+
|
|
235
|
+
const binding = Binding.bind<IHealthCheckOptions>({ key: 'options.healthCheck' })
|
|
236
|
+
.toValue({ restOptions: { path: '/health' } });
|
|
237
|
+
|
|
238
|
+
container.set({ binding }); // registers under binding.key
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## MetadataRegistry
|
|
242
|
+
|
|
243
|
+
`Source ->` [`registry/registry.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/registry/registry.ts)
|
|
244
|
+
|
|
245
|
+
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.
|
|
246
|
+
|
|
247
|
+
| Method | Signature | Description |
|
|
248
|
+
|--------|-----------|--------------|
|
|
249
|
+
| `define` | `define<Target, Value>(opts: { target, key: TBindingKey, value: Value }): void` | Store arbitrary metadata on a target |
|
|
250
|
+
| `get` | `get<Target, Value>(opts: { target, key: TBindingKey }): Value \| undefined` | Retrieve metadata by key |
|
|
251
|
+
| `has` | `has<Target>(opts: { target, key: TBindingKey }): boolean` | Check if metadata exists |
|
|
252
|
+
| `delete` | `delete<Target>(opts: { target, key: TBindingKey }): boolean` | Remove metadata by key |
|
|
253
|
+
| `getKeys` | `getKeys<Target>(opts: { target }): TBindingKey[]` | List all metadata keys (`string`/`symbol` only) on a target |
|
|
254
|
+
| `getMethodNames` | `getMethodNames<T>(opts: { target: TClass<T> }): string[]` | Non-constructor function-valued own property names on the prototype |
|
|
255
|
+
| `clearMetadata` | `clearMetadata<T>(opts: { target }): void` | Delete every metadata key on a target |
|
|
256
|
+
| `setInjectMetadata` | `setInjectMetadata<T>(opts: { target, index: number, metadata: IInjectMetadata }): void` | Write constructor `@inject` metadata at parameter `index` into the `MetadataKeys.INJECT` array |
|
|
257
|
+
| `getInjectMetadata` | `getInjectMetadata<T>(opts: { target }): IInjectMetadata[] \| undefined` | Read the constructor injection array |
|
|
258
|
+
| `setPropertyMetadata` | `setPropertyMetadata<T>(opts: { target, propertyName, metadata: IPropertyMetadata }): void` | Write property `@inject` metadata into a `Map` keyed by property name, stored on `target.constructor` |
|
|
259
|
+
| `getPropertiesMetadata` | `getPropertiesMetadata<T>(opts: { target }): Map<string \| symbol, IPropertyMetadata> \| undefined` | Read the full property metadata map |
|
|
260
|
+
| `getPropertyMetadata` | `getPropertyMetadata<T>(opts: { target, propertyName }): IPropertyMetadata \| undefined` | Read metadata for one property |
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
import { MetadataKeys, metadataRegistry } from '@venizia/ignis-inversion';
|
|
264
|
+
|
|
265
|
+
MetadataKeys.PROPERTIES; // Symbol.for('ignis:properties')
|
|
266
|
+
MetadataKeys.INJECT; // Symbol.for('ignis:inject')
|
|
267
|
+
|
|
268
|
+
metadataRegistry.define({ target: myObj, key: 'custom:flag', value: true });
|
|
269
|
+
metadataRegistry.get({ target: myObj, key: 'custom:flag' }); // true
|
|
270
|
+
metadataRegistry.has({ target: myObj, key: 'custom:flag' }); // true
|
|
271
|
+
metadataRegistry.delete({ target: myObj, key: 'custom:flag' }); // true
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### MetadataRegistry types
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
interface IInjectMetadata {
|
|
278
|
+
key: TBindingKey;
|
|
279
|
+
index: number;
|
|
280
|
+
isOptional?: boolean;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
interface IPropertyMetadata {
|
|
284
|
+
bindingKey: TBindingKey;
|
|
285
|
+
isOptional?: boolean;
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Decorators
|
|
290
|
+
|
|
291
|
+
`Source ->` [`metadata/injectors.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/metadata/injectors.ts)
|
|
292
|
+
|
|
293
|
+
### `@inject`
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
inject(opts: { key: TBindingKey; isOptional?: boolean; registry?: MetadataRegistry }): PropertyDecorator | ParameterDecorator
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Dispatches on how the decorator was invoked:
|
|
300
|
+
|
|
301
|
+
| Applied to | Detection | Stored via |
|
|
302
|
+
|------------|-----------|------------|
|
|
303
|
+
| Constructor parameter | `parameterIndex` is a `number` | `registry.setInjectMetadata({ target, index: parameterIndex, metadata: { key, index: parameterIndex, isOptional } })` |
|
|
304
|
+
| Class property | `propertyName !== undefined` | `registry.setPropertyMetadata({ target, propertyName, metadata: { bindingKey: key, isOptional } })` |
|
|
305
|
+
| Anything else | neither condition matches | Throws `@inject decorator can only be used on class properties or constructor parameters` |
|
|
306
|
+
|
|
307
|
+
`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`).
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
class UserService {
|
|
311
|
+
constructor(
|
|
312
|
+
@inject({ key: 'repositories.UserRepository' }) private userRepository: UserRepository,
|
|
313
|
+
@inject({ key: 'services.Logger', isOptional: true }) private logger?: Logger,
|
|
314
|
+
) {}
|
|
315
|
+
|
|
316
|
+
@inject({ key: 'config.retryCount' })
|
|
317
|
+
private retryCount: number;
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Namespaces and Tags
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
import { BindingKeys } from '@venizia/ignis-inversion';
|
|
325
|
+
|
|
326
|
+
BindingKeys.build({ namespace: 'services', key: 'UserService' });
|
|
327
|
+
// => 'services.UserService'
|
|
328
|
+
|
|
329
|
+
BindingKeys.build({ namespace: '', key: 'UserService' });
|
|
330
|
+
// => 'UserService' (empty namespace segment is dropped)
|
|
331
|
+
|
|
332
|
+
BindingKeys.build({ namespace: 'services', key: '' });
|
|
333
|
+
// throws: [BindingKeys][build] Invalid key to build | key:
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
`key` is required and must be non-empty. `namespace` is optional - it's silently omitted from the joined string when empty.
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
container.bind({ key: 'workers.EmailWorker' }).toClass(EmailWorker).setTags('background', 'email');
|
|
340
|
+
// tags: ['workers', 'background', 'email']
|
|
341
|
+
|
|
342
|
+
const serviceBindings = container.findByTag({ tag: 'services' });
|
|
343
|
+
const filtered = container.findByTag({ tag: 'services', exclude: ['services.InternalService'] });
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
## Utilities
|
|
347
|
+
|
|
348
|
+
### ApplicationError and getError
|
|
349
|
+
|
|
350
|
+
`Source ->` [`modules/error/app-error.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/modules/error/app-error.ts)
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
class ApplicationError extends Error {
|
|
354
|
+
statusCode: number;
|
|
355
|
+
normalized: { text: string; code: string; args: Record<string, unknown> };
|
|
356
|
+
extra?: Record<string, unknown>;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
getError(opts: TError): ApplicationError; // factory function
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
`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`.
|
|
363
|
+
|
|
364
|
+
`messageCode`/`messageArgs` are lowest precedence - `message.code`/`message.args`, or a catalogued definition's own, win when both are present. There is no flat `error.messageCode`. `extra` never mirrors `messageArgs`. `normalized.args` is always populated (`{}` when empty).
|
|
365
|
+
|
|
366
|
+
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.
|
|
367
|
+
|
|
368
|
+
`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 - inversion ships to browsers.
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
throw getError({ message: 'Something failed', statusCode: 500, messageCode: 'ERR_INTERNAL' });
|
|
372
|
+
throw new ApplicationError({ message: 'Not found', statusCode: 404 });
|
|
373
|
+
throw new ApplicationError({ message: { text: 'Not found', code: 'core.user.not_found' }, statusCode: 404 });
|
|
374
|
+
|
|
375
|
+
// The code and args are read off `normalized`, never off the error directly.
|
|
376
|
+
error.normalized.code; // 'err_internal'
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
See the [Error reference](/extensions/helpers/error/) for the full input shape, precedence rules, and the catalogued (`TErrorDefinition`) pattern.
|
|
380
|
+
|
|
381
|
+
### Logger
|
|
382
|
+
|
|
383
|
+
`Source ->` [`common/logger.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/common/logger.ts)
|
|
384
|
+
|
|
385
|
+
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.
|
|
386
|
+
|
|
387
|
+
```typescript
|
|
388
|
+
Logger.info('Server started on port %d', 3000); // console.log('[INFO] ...')
|
|
389
|
+
Logger.warn('Deprecation warning'); // console.warn('[WARN] ...')
|
|
390
|
+
Logger.error('Connection failed: %s', err.message); // console.error('[ERROR] ...')
|
|
391
|
+
Logger.debug('Resolved binding: %s', key); // console.log('[DEBUG] ...') only if process.env.DEBUG is set
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### Type guards and shared types
|
|
395
|
+
|
|
396
|
+
`Source ->` [`common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/inversion/src/common/types.ts)
|
|
397
|
+
|
|
398
|
+
```typescript
|
|
399
|
+
type TNullable<T> = T | undefined | null;
|
|
400
|
+
type ValueOrPromise<T> = T | Promise<T>;
|
|
401
|
+
type ValueOf<T> = T[keyof T];
|
|
402
|
+
type TConstructor<T> = new (...args: any[]) => T;
|
|
403
|
+
type TAbstractConstructor<T> = abstract new (...args: any[]) => T;
|
|
404
|
+
type TClass<T> = TConstructor<T> & { [property: string]: any };
|
|
405
|
+
type TConstValue<T extends TClass<any>> = Extract<ValueOf<T>, string | number>;
|
|
406
|
+
type TBindingKey = string | symbol;
|
|
407
|
+
|
|
408
|
+
interface IBindingTag {
|
|
409
|
+
[name: string]: any;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function isClass<T>(target: any): target is TClass<T>;
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
`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.
|
|
416
|
+
|
|
417
|
+
```typescript
|
|
418
|
+
interface IProvider<T> {
|
|
419
|
+
value(container: Container): T;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function isClassProvider<T>(target: any): target is TClass<IProvider<T>>;
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
## Constants
|
|
426
|
+
|
|
427
|
+
| Constant | Values | Description |
|
|
428
|
+
|----------|--------|--------------|
|
|
429
|
+
| `BindingScopes.SINGLETON` | `'singleton'` | Cached after first resolution |
|
|
430
|
+
| `BindingScopes.TRANSIENT` | `'transient'` | New instance each resolution |
|
|
431
|
+
| `BindingValueTypes.CLASS` | `'class'` | Container instantiates with DI |
|
|
432
|
+
| `BindingValueTypes.VALUE` | `'value'` | Direct value return |
|
|
433
|
+
| `BindingValueTypes.PROVIDER` | `'provider'` | Factory function or `IProvider` class |
|
|
434
|
+
| `MetadataKeys.PROPERTIES` | `Symbol.for('ignis:properties')` | Property injection metadata key |
|
|
435
|
+
| `MetadataKeys.INJECT` | `Symbol.for('ignis:inject')` | Constructor injection metadata key |
|
|
436
|
+
|
|
437
|
+
## Troubleshooting
|
|
438
|
+
|
|
439
|
+
### "Binding key: X is not bounded in context!"
|
|
440
|
+
|
|
441
|
+
**Cause:** The dependency was never registered with the container, or the key does not match exactly.
|
|
442
|
+
|
|
443
|
+
**Fix:**
|
|
444
|
+
1. Verify the binding exists: `container.isBound({ key: 'services.UserService' })`.
|
|
445
|
+
2. Check for typos between `@inject({ key: '...' })` and the key used in `container.bind({ key: '...' })`.
|
|
446
|
+
3. If the dependency is genuinely optional, add `isOptional: true` to the `@inject` call - constructor parameter or property, both work - or use `container.get({ key: '...', isOptional: true })`.
|
|
447
|
+
|
|
448
|
+
### "[getValue] Invalid context/container to instantiate class"
|
|
449
|
+
|
|
450
|
+
**Cause:** A `Binding` configured with `toClass()` was resolved by calling `binding.getValue()` directly, without a `Container` argument.
|
|
451
|
+
|
|
452
|
+
**Fix:** Resolve class bindings through the container - `container.get({ key })` - rather than calling `binding.getValue()` with no arguments.
|
|
453
|
+
|
|
454
|
+
### "[getValue] Invalid context/container to get provider value"
|
|
455
|
+
|
|
456
|
+
**Cause:** A `Binding` configured with `toProvider()` was resolved without a `Container` argument.
|
|
457
|
+
|
|
458
|
+
**Fix:** Same as above - always resolve through `container.get({ key })`.
|
|
459
|
+
|
|
460
|
+
### "[getBindingMeta] Invalid resolver type"
|
|
461
|
+
|
|
462
|
+
**Cause:** `getBindingMeta({ type })` was called with a type that does not match the binding's actual resolver (e.g. `'class'` on a value binding).
|
|
463
|
+
|
|
464
|
+
**Fix:** Match `type` to how the binding was created: `toClass()` -> `'class'`, `toValue()` -> `'value'`, `toProvider()` -> `'provider'`.
|
|
465
|
+
|
|
466
|
+
### "[getBinding] Invalid binding key type"
|
|
467
|
+
|
|
468
|
+
**Cause:** The key passed to `getBinding()` is not a `string`, `symbol`, or `{ namespace, key }` object.
|
|
469
|
+
|
|
470
|
+
**Fix:** Use one of the three supported key formats.
|
|
471
|
+
|
|
472
|
+
### "[BindingKeys][build] Invalid key to build"
|
|
473
|
+
|
|
474
|
+
**Cause:** `BindingKeys.build()` was called with an empty `key`.
|
|
475
|
+
|
|
476
|
+
**Fix:** Provide a non-empty `key`: `BindingKeys.build({ namespace: 'services', key: 'UserService' })`.
|
|
477
|
+
|
|
478
|
+
### "[ClassName] Constructor parameter N has no @inject"
|
|
479
|
+
|
|
480
|
+
**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.
|
|
481
|
+
|
|
482
|
+
**Fix:** Decorate every constructor parameter with `@inject`. There is no partial-injection escape hatch. If a value doesn't come from the container (e.g. a plain `scope: string`), choose one:
|
|
483
|
+
|
|
484
|
+
- Pass it through a factory/provider instead of a bare constructor parameter.
|
|
485
|
+
- Have the subclass forward it via its own `@inject`-decorated parameter.
|
|
486
|
+
|
|
487
|
+
### "@inject decorator can only be used on class properties or constructor parameters"
|
|
488
|
+
|
|
489
|
+
**Cause:** `@inject` was applied to something other than a class property or constructor parameter.
|
|
490
|
+
|
|
491
|
+
**Fix:** Only use `@inject` on constructor parameters or class properties.
|
|
492
|
+
|
|
493
|
+
### Property injection never runs
|
|
494
|
+
|
|
495
|
+
**Cause:** The class was instantiated with `new MyClass()` directly instead of through the container. Only `container.resolve()`/`instantiate()` reads `@inject` metadata and populates properties. A plain `new` leaves them at whatever their field initializer set - `undefined` if none.
|
|
496
|
+
|
|
497
|
+
**Fix:** Always use `container.resolve(MyClass)` or `container.instantiate(MyClass)` to create instances that use property injection.
|
|
498
|
+
|
|
499
|
+
### "getInjectMetadata returns undefined"
|
|
500
|
+
|
|
501
|
+
**Cause:** `reflect-metadata` was not imported before decorators were evaluated, or `experimentalDecorators`/`emitDecoratorMetadata` are not enabled in `tsconfig.json`.
|
|
502
|
+
|
|
503
|
+
**Fix:**
|
|
504
|
+
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.
|
|
505
|
+
2. Verify `tsconfig.json` includes:
|
|
506
|
+
```json
|
|
507
|
+
{
|
|
508
|
+
"compilerOptions": {
|
|
509
|
+
"experimentalDecorators": true,
|
|
510
|
+
"emitDecoratorMetadata": true
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
### Singleton returns a stale instance after rebinding
|
|
516
|
+
|
|
517
|
+
**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.
|
|
518
|
+
|
|
519
|
+
**Fix:**
|
|
520
|
+
1. Always resolve via `container.get()` rather than caching `Binding` references.
|
|
521
|
+
2. Call `container.clear()` to clear every binding's singleton cache without removing registrations.
|
|
522
|
+
3. Call `container.reset()` to remove all bindings entirely.
|
|
523
|
+
|
|
524
|
+
## See also
|
|
525
|
+
|
|
526
|
+
- [Inversion overview](/extensions/helpers/inversion/) - introduction and the most common tasks
|
|
527
|
+
- [Dependency Injection Guide](/guides/core-concepts/dependency-injection) - DI fundamentals in the framework layer
|
|
528
|
+
- [Application](/guides/core-concepts/application/) - `Application` extends `Container`
|
|
529
|
+
- [Dependency Injection API](/references/base/dependency-injection) - the framework-layer DI reference
|
|
530
|
+
- [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.
|
|
@@ -7,7 +13,7 @@ class KafkaAdminHelper extends BaseKafkaHelper<Admin>
|
|
|
7
13
|
```
|
|
8
14
|
|
|
9
15
|
> [!NOTE]
|
|
10
|
-
> `KafkaAdminHelper` has **no generic type parameters**
|
|
16
|
+
> `KafkaAdminHelper` has **no generic type parameters** - the Admin client does not deal with serialized messages.
|
|
11
17
|
|
|
12
18
|
## Helper API
|
|
13
19
|
|
|
@@ -18,6 +24,7 @@ class KafkaAdminHelper extends BaseKafkaHelper<Admin>
|
|
|
18
24
|
| `isHealthy()` | `(): boolean` | `true` when broker connected |
|
|
19
25
|
| `isReady()` | `(): boolean` | Same as `isHealthy()` |
|
|
20
26
|
| `getHealthStatus()` | `(): TKafkaHealthStatus` | `'connected'` \| `'disconnected'` \| `'unknown'` |
|
|
27
|
+
| `getConnectedBrokerCount()` | `(): number` | Number of currently connected brokers |
|
|
21
28
|
| `close(opts?)` | `(opts?: { isForce?: boolean }): Promise<void>` | Close the admin connection (default: graceful) |
|
|
22
29
|
|
|
23
30
|
## IKafkaAdminOptions
|
|
@@ -31,7 +38,7 @@ interface IKafkaAdminOptions extends IKafkaConnectionOptions {
|
|
|
31
38
|
}
|
|
32
39
|
```
|
|
33
40
|
|
|
34
|
-
Plus
|
|
41
|
+
Plus the shared [Connection & Authentication](./producer#connection--authentication) options (`bootstrapBrokers`, `clientId`, `retries`, `sasl`, `tls`, ...), documented once on the Producer page.
|
|
35
42
|
|
|
36
43
|
## Basic Example
|
|
37
44
|
|
|
@@ -65,7 +72,7 @@ await helper.close({ isForce: true });
|
|
|
65
72
|
|
|
66
73
|
## Graceful Shutdown
|
|
67
74
|
|
|
68
|
-
`close()` uses the base `closeClient()`
|
|
75
|
+
`close()` uses the base `closeClient()`, wrapped in a graceful timeout. `closeClient()` calls `this.client.close()` directly. If the graceful close exceeds `shutdownTimeout` (default 30s), it automatically force-closes. After `close()`, `healthStatus` becomes `'disconnected'`.
|
|
69
76
|
|
|
70
77
|
```typescript
|
|
71
78
|
// Graceful (recommended)
|
|
@@ -188,3 +195,16 @@ await admin.alterConsumerGroupOffsets({
|
|
|
188
195
|
| Method | Signature | Description |
|
|
189
196
|
|--------|-----------|-------------|
|
|
190
197
|
| `describeLogDirs(opts)` | `(opts: { topics }): Promise<BrokerLogDirDescription[]>` | Describe broker log directories |
|
|
198
|
+
|
|
199
|
+
## See also
|
|
200
|
+
|
|
201
|
+
- [Kafka Overview](./) - the four helpers, shared health/close API, and the compile-binary caveat
|
|
202
|
+
- [Producer](./producer) - the shared Connection & Authentication options
|
|
203
|
+
- [Consumer](./consumer) - `groupInstanceId`, rebalance behavior, and lag monitoring for the groups this page manages
|
|
204
|
+
- [Examples & Troubleshooting](./examples) - a full topic-setup script and IoC wiring
|
|
205
|
+
|
|
206
|
+
**Files:**
|
|
207
|
+
|
|
208
|
+
- [`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`
|
|
209
|
+
- [`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
|
|
210
|
+
- [`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`
|