@venizia/ignis-docs 0.2.1-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/content/best-practices/architectural-patterns.md +3 -3
- package/content/best-practices/code-style-standards/naming-conventions.md +1 -1
- package/content/best-practices/contribution-workflow.md +2 -2
- package/content/best-practices/error-handling.md +94 -89
- package/content/best-practices/security-guidelines.md +5 -5
- package/content/extensions/components/api-reference.md +22 -21
- package/content/extensions/components/authentication/api.md +64 -28
- package/content/extensions/components/authentication/errors.md +19 -5
- package/content/extensions/components/authentication/index.md +19 -18
- package/content/extensions/components/authentication/usage.md +55 -30
- package/content/extensions/components/authorization/api.md +351 -84
- package/content/extensions/components/authorization/errors.md +51 -17
- package/content/extensions/components/authorization/getting-started.md +227 -0
- package/content/extensions/components/authorization/index.md +24 -16
- package/content/extensions/components/authorization/usage.md +45 -21
- package/content/extensions/components/health-check.md +15 -9
- package/content/extensions/components/index.md +24 -90
- package/content/extensions/components/mail/api.md +105 -54
- package/content/extensions/components/mail/errors.md +12 -10
- package/content/extensions/components/mail/index.md +32 -13
- package/content/extensions/components/mail/usage.md +26 -18
- package/content/extensions/components/request-tracker.md +18 -14
- package/content/extensions/components/socket-io/api.md +377 -882
- package/content/extensions/components/socket-io/errors.md +49 -51
- package/content/extensions/components/socket-io/index.md +72 -88
- package/content/extensions/components/socket-io/usage.md +107 -117
- package/content/extensions/components/static-asset/api.md +83 -31
- package/content/extensions/components/static-asset/errors.md +18 -7
- package/content/extensions/components/static-asset/index.md +18 -11
- package/content/extensions/components/static-asset/usage.md +11 -6
- package/content/extensions/components/template/index.md +3 -3
- package/content/extensions/components/websocket/api.md +58 -27
- package/content/extensions/components/websocket/errors.md +3 -3
- package/content/extensions/components/websocket/index.md +8 -7
- package/content/extensions/components/websocket/usage.md +21 -8
- package/content/extensions/helpers/cron/index.md +8 -7
- package/content/extensions/helpers/crypto/index.md +16 -8
- package/content/extensions/helpers/crypto/reference.md +96 -24
- package/content/extensions/helpers/env/index.md +14 -10
- package/content/extensions/helpers/error/index.md +99 -23
- package/content/extensions/helpers/index.md +61 -47
- package/content/extensions/helpers/inversion/index.md +23 -6
- package/content/extensions/helpers/inversion/reference.md +30 -22
- package/content/extensions/helpers/kafka/admin.md +3 -2
- package/content/extensions/helpers/kafka/compile-binary.md +69 -44
- package/content/extensions/helpers/kafka/consumer.md +24 -21
- package/content/extensions/helpers/kafka/examples.md +22 -234
- package/content/extensions/helpers/kafka/index.md +30 -62
- package/content/extensions/helpers/kafka/producer.md +31 -26
- package/content/extensions/helpers/kafka/schema-registry.md +19 -14
- package/content/extensions/helpers/logger/hf-logger.md +49 -22
- package/content/extensions/helpers/logger/index.md +37 -12
- package/content/extensions/helpers/logger/pino.md +31 -11
- package/content/extensions/helpers/logger/reference.md +243 -52
- package/content/extensions/helpers/network/api.md +65 -30
- package/content/extensions/helpers/network/index.md +32 -11
- package/content/extensions/helpers/queue/index.md +17 -6
- package/content/extensions/helpers/queue/reference.md +52 -25
- package/content/extensions/helpers/redis/index.md +29 -11
- package/content/extensions/helpers/redis/reference.md +85 -28
- package/content/extensions/helpers/secrets/index.md +82 -12
- package/content/extensions/helpers/socket-io/api.md +40 -21
- package/content/extensions/helpers/socket-io/index.md +26 -10
- package/content/extensions/helpers/storage/api.md +42 -24
- package/content/extensions/helpers/storage/index.md +10 -7
- package/content/extensions/helpers/types/index.md +20 -7
- package/content/extensions/helpers/types/reference.md +53 -14
- package/content/extensions/helpers/uid/index.md +166 -10
- package/content/extensions/helpers/websocket/api.md +52 -13
- package/content/extensions/helpers/websocket/index.md +7 -4
- package/content/extensions/helpers/worker-thread/index.md +9 -5
- package/content/extensions/helpers/worker-thread/reference.md +20 -20
- 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/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 +26 -14
- package/content/guides/core-concepts/persistent/sqlite.md +296 -0
- package/content/guides/core-concepts/persistent/transactions.md +12 -11
- 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 +4 -3
- package/content/guides/migrations/scoped-rbac-migration.md +5 -5
- package/content/guides/migrations/unified-connectors-migration.md +7 -6
- package/content/guides/tutorials/realtime-chat.md +1 -1
- package/content/references/base/application.md +63 -21
- package/content/references/base/components.md +3 -3
- package/content/references/base/connectors.md +14 -14
- package/content/references/base/controllers.md +12 -12
- package/content/references/base/datasources-reference.md +32 -31
- package/content/references/base/datasources.md +6 -6
- package/content/references/base/dependency-injection.md +12 -11
- package/content/references/base/filter-system/application-usage.md +54 -30
- package/content/references/base/filter-system/array-operators.md +24 -46
- package/content/references/base/filter-system/comparison-operators.md +47 -67
- package/content/references/base/filter-system/default-filter.md +59 -53
- package/content/references/base/filter-system/fields-order-pagination.md +92 -146
- package/content/references/base/filter-system/index.md +25 -13
- package/content/references/base/filter-system/json-filtering.md +45 -184
- package/content/references/base/filter-system/list-operators.md +23 -53
- package/content/references/base/filter-system/logical-operators.md +63 -121
- package/content/references/base/filter-system/null-operators.md +34 -104
- package/content/references/base/filter-system/pattern-matching.md +40 -55
- package/content/references/base/filter-system/quick-reference.md +86 -198
- package/content/references/base/filter-system/range-operators.md +18 -46
- package/content/references/base/filter-system/tips.md +6 -6
- package/content/references/base/filter-system/use-cases.md +33 -15
- package/content/references/base/grpc-controllers.md +53 -17
- package/content/references/base/index.md +5 -3
- package/content/references/base/middlewares.md +11 -10
- package/content/references/base/models-reference.md +17 -17
- package/content/references/base/models.md +4 -3
- package/content/references/base/providers.md +8 -8
- package/content/references/base/repositories/advanced.md +224 -326
- package/content/references/base/repositories/index.md +19 -6
- package/content/references/base/repositories/mixins.md +5 -5
- package/content/references/base/repositories/relations.md +160 -293
- package/content/references/base/repositories/soft-deletable.md +16 -6
- package/content/references/base/secrets.md +17 -13
- package/content/references/base/services.md +6 -4
- package/content/references/configuration/environment-variables.md +31 -23
- package/content/references/configuration/index.md +6 -4
- package/content/references/index.md +1 -1
- package/content/references/utilities/duration.md +85 -0
- package/content/references/utilities/index.md +5 -1
- package/content/references/utilities/jsx-reference.md +11 -11
- package/content/references/utilities/jsx.md +2 -2
- package/content/references/utilities/module.md +78 -25
- package/content/references/utilities/request.md +2 -1
- package/content/references/utilities/retry.md +139 -0
- package/content/references/utilities/schema.md +2 -2
- package/content/references/utilities/statuses-reference.md +4 -4
- package/content/references/utilities/statuses.md +5 -5
- 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 +17 -16
|
@@ -117,7 +117,7 @@ REST controllers extend `BaseRestController`, while gRPC controllers extend `Bas
|
|
|
117
117
|
|
|
118
118
|
## 3. Component-Based Modularity
|
|
119
119
|
|
|
120
|
-
Components bundle a group of related, reusable, and pluggable features into self-contained modules. A single component can encapsulate multiple providers, services, controllers, and repositories
|
|
120
|
+
Components bundle a group of related, reusable, and pluggable features into self-contained modules. A single component can encapsulate multiple providers, services, controllers, and repositories. It functions as a mini-application that plugs into any IGNIS project.
|
|
121
121
|
|
|
122
122
|
**Built-in Components:**
|
|
123
123
|
- `AuthenticateComponent` - JWT authentication
|
|
@@ -140,7 +140,7 @@ export class Application extends BaseApplication {
|
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
```
|
|
143
|
-
This architecture keeps the main `Application` class clean and focused on high-level assembly
|
|
143
|
+
This architecture keeps the main `Application` class clean and focused on high-level assembly. The details of each feature stay neatly encapsulated within their respective components.
|
|
144
144
|
|
|
145
145
|
## 4. Custom Components
|
|
146
146
|
|
|
@@ -303,7 +303,7 @@ service<Base extends IService, Args extends AnyObject = any>(
|
|
|
303
303
|
}
|
|
304
304
|
```
|
|
305
305
|
|
|
306
|
-
Every registration method takes the same optional second argument
|
|
306
|
+
Every registration method takes the same optional second argument. `opts.binding` overrides the derived `{ namespace, key }` when you need to register two classes under one contract.
|
|
307
307
|
|
|
308
308
|
**Capability interfaces:**
|
|
309
309
|
|
|
@@ -115,7 +115,7 @@ interface IAuthService {
|
|
|
115
115
|
|
|
116
116
|
// Type aliases use 'T' prefix
|
|
117
117
|
type TSignInRequest = z.infer<typeof SignInRequestSchema>;
|
|
118
|
-
type
|
|
118
|
+
type TJWKSMode = TConstValue<typeof JWKSModes>;
|
|
119
119
|
|
|
120
120
|
// ❌ Never a bare string-literal union for an enumerable set - it has no runtime
|
|
121
121
|
// values to validate against. Use a const class + TConstValue instead.
|
|
@@ -161,8 +161,8 @@ make lint-all
|
|
|
161
161
|
# Build all packages (from root)
|
|
162
162
|
make build
|
|
163
163
|
|
|
164
|
-
# Run tests (from a package directory, e.g. packages/core or packages/boot)
|
|
165
|
-
cd packages/core && bun test
|
|
164
|
+
# Run tests (from a package directory, e.g. packages/core-server or packages/boot)
|
|
165
|
+
cd packages/core-server && bun test
|
|
166
166
|
```
|
|
167
167
|
|
|
168
168
|
> [!WARNING]
|
|
@@ -1,28 +1,37 @@
|
|
|
1
1
|
# Error Handling
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Error Handling Philosophy
|
|
6
|
-
|
|
7
|
-
| Principle | Description |
|
|
8
|
-
|-----------|-------------|
|
|
9
|
-
| **Fail Fast** | Detect and report errors as early as possible |
|
|
10
|
-
| **Don't Swallow** | Never catch errors without logging or re-throwing |
|
|
11
|
-
| **User-Friendly** | Return clear, actionable messages to clients |
|
|
12
|
-
| **Debuggable** | Include context for debugging in logs |
|
|
13
|
-
|
|
14
|
-
## 1. Using `getError` Helper
|
|
15
|
-
|
|
16
|
-
IGNIS provides `getError` for creating consistent, structured errors.
|
|
3
|
+
Every error in IGNIS ends up as one shape: `statusCode`, `message`, `normalized`, `requestId`, and `details`. `getError` builds that shape at the throw site. A global handler catches whatever you don't.
|
|
17
4
|
|
|
18
5
|
```typescript
|
|
19
6
|
import { getError, HTTP } from '@venizia/ignis-helpers';
|
|
20
7
|
|
|
21
|
-
// Basic error
|
|
22
8
|
throw getError({
|
|
23
9
|
statusCode: HTTP.ResultCodes.RS_4.NotFound,
|
|
24
10
|
message: 'User not found',
|
|
25
11
|
});
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Find what you need
|
|
15
|
+
|
|
16
|
+
| You want to | Go to |
|
|
17
|
+
|---|---|
|
|
18
|
+
| Throw an error with debugging context | [Throw a structured error](#throw-a-structured-error) |
|
|
19
|
+
| Add an i18n error a client can branch on | [Catalog a domain error](#catalog-a-domain-error) |
|
|
20
|
+
| Pick the right HTTP status code | [Choose the right status code](#choose-the-right-status-code) |
|
|
21
|
+
| Handle errors in a service, controller, or repository | [Handle errors by layer](#handle-errors-by-layer) |
|
|
22
|
+
| Customize the global error response | [Customize the global handler](#customize-the-global-handler) |
|
|
23
|
+
| Read the exact JSON a client receives | [The error response shape](#the-error-response-shape) |
|
|
24
|
+
| Log an error without losing the stack | [Log errors](#log-errors) |
|
|
25
|
+
| Handle a rejected promise or a fire-and-forget call | [Handle async errors](#handle-async-errors) |
|
|
26
|
+
| Roll back a transaction safely | [Roll back a transaction on failure](#roll-back-a-transaction-on-failure) |
|
|
27
|
+
| Parse an error response on the client | [Parse errors on the client](#parse-errors-on-the-client) |
|
|
28
|
+
|
|
29
|
+
## Throw a structured error
|
|
30
|
+
|
|
31
|
+
`getError` is the one function every throw in IGNIS goes through. Give it a status code and a message, and it returns an `ApplicationError` ready to throw.
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { getError, HTTP } from '@venizia/ignis-helpers';
|
|
26
35
|
|
|
27
36
|
// Error with context - any key the framework does not model lands in `extra`
|
|
28
37
|
throw getError({
|
|
@@ -48,14 +57,16 @@ throw getError({
|
|
|
48
57
|
});
|
|
49
58
|
```
|
|
50
59
|
|
|
60
|
+
Any key `getError` does not model rides into `extra`. That's how a throw site attaches context the framework knows nothing about.
|
|
61
|
+
|
|
51
62
|
> [!NOTE]
|
|
52
|
-
>
|
|
63
|
+
> The trade-off: a **misspelling** rides the same path. `getError({ message, statuscode: 503 })` compiles. `statusCode` stays `400`, and `503` sits in `extra.statuscode`. The framework can't tell your context from your typo - spell option keys carefully.
|
|
53
64
|
|
|
54
|
-
|
|
65
|
+
## Catalog a domain error
|
|
55
66
|
|
|
56
|
-
|
|
67
|
+
A free-form `getError` call like that is right for a failure with no i18n code: an invariant, a misconfiguration, a seed guard. Nobody translates `'[UserService][create] Database connection failed'`.
|
|
57
68
|
|
|
58
|
-
|
|
69
|
+
But a **domain** failure - one a client localizes and branches on - belongs in a catalog instead. Retyping its code and status at each throw is how two call sites end up raising `category.create.duplicate_name` and `category.duplicate_name` for the same thing.
|
|
59
70
|
|
|
60
71
|
```typescript
|
|
61
72
|
import { ErrorScopes, getError, HTTP } from '@venizia/ignis-helpers';
|
|
@@ -81,15 +92,15 @@ declare module '@venizia/ignis-helpers' {
|
|
|
81
92
|
throw getError({ error: UserErrors.CREATE_DUPLICATE_EMAIL, messageArgs: { email } });
|
|
82
93
|
```
|
|
83
94
|
|
|
84
|
-
Pass the definition as `error` -
|
|
95
|
+
Pass the definition as `error` - never spread it. `getError({ ...UserErrors.CREATE_DUPLICATE_EMAIL, messageArgs: { email } })` looks equivalent. It isn't.
|
|
85
96
|
|
|
86
|
-
|
|
97
|
+
Spreading skips the `error:` field entirely. `category` and `description` are fields only a definition models, so they fall into `extra` instead of staying structured. `statusCode` and `message` still resolve correctly, but only because their shapes happen to match what `getError` expects standalone - nothing catches the rest for you.
|
|
87
98
|
|
|
88
|
-
|
|
99
|
+
See the [Error helper reference](/extensions/helpers/error/) for the full surface.
|
|
89
100
|
|
|
90
|
-
|
|
101
|
+
## Choose the right status code
|
|
91
102
|
|
|
92
|
-
| Code | Constant | Use
|
|
103
|
+
| Code | Constant | Use when |
|
|
93
104
|
|------|----------|----------|
|
|
94
105
|
| 400 | `RS_4.BadRequest` | Invalid input format, missing required fields, database constraint violations (auto-handled) |
|
|
95
106
|
| 401 | `RS_4.Unauthorized` | Missing or invalid authentication |
|
|
@@ -102,13 +113,15 @@ Use the correct status code for each error type:
|
|
|
102
113
|
| 502 | `RS_5.BadGateway` | External service failed |
|
|
103
114
|
| 503 | `RS_5.ServiceUnavailable` | Service temporarily down |
|
|
104
115
|
|
|
105
|
-
:::tip Automatic
|
|
106
|
-
|
|
116
|
+
:::tip Automatic database error handling
|
|
117
|
+
The global error middleware converts constraint violations to HTTP 400 on its own, no try/catch needed - SQLSTATE class `22` (data exception), `23` (integrity constraint: unique, foreign key, not null, check, exclusion), and `44` (WITH CHECK OPTION violation). Transient conflicts (`40001` serialization failure, `40P01` deadlock) become HTTP 409 with a retryable message instead. Other classes (for example, class `42` undefined column) stay 500, and production responses are sanitized - see [Repository layer](#repository-layer).
|
|
107
118
|
:::
|
|
108
119
|
|
|
109
|
-
##
|
|
120
|
+
## Handle errors by layer
|
|
110
121
|
|
|
111
|
-
### Service
|
|
122
|
+
### Service layer
|
|
123
|
+
|
|
124
|
+
A service decides what's wrong and throws with `getError`. It doesn't catch what it can't handle.
|
|
112
125
|
|
|
113
126
|
```typescript
|
|
114
127
|
import { BaseService } from '@venizia/ignis';
|
|
@@ -160,9 +173,11 @@ export class UserService extends BaseService {
|
|
|
160
173
|
}
|
|
161
174
|
```
|
|
162
175
|
|
|
163
|
-
|
|
176
|
+
Notice `findOne` and `findById` return the record or `null` - there's no separate "not found" exception to catch. You check for `null` and throw.
|
|
177
|
+
|
|
178
|
+
### Controller layer
|
|
164
179
|
|
|
165
|
-
Controllers
|
|
180
|
+
Controllers stay thin. Call the service, return the response, and let the global handler catch what the service throws.
|
|
166
181
|
|
|
167
182
|
```typescript
|
|
168
183
|
import { BaseRestController, controller, get, post } from '@venizia/ignis';
|
|
@@ -192,9 +207,9 @@ export class UserController extends BaseRestController {
|
|
|
192
207
|
}
|
|
193
208
|
```
|
|
194
209
|
|
|
195
|
-
### Repository
|
|
210
|
+
### Repository layer
|
|
196
211
|
|
|
197
|
-
|
|
212
|
+
The global middleware already converts constraint violations for you (see [Choose the right status code](#choose-the-right-status-code)), so most repository methods need no try/catch at all. Skip it unless you want a message tailored to one specific constraint.
|
|
198
213
|
|
|
199
214
|
**Non-production** returns the full driver context for debugging:
|
|
200
215
|
|
|
@@ -217,7 +232,7 @@ Database errors in SQLSTATE classes `22` (data exception), `23` (integrity const
|
|
|
217
232
|
```
|
|
218
233
|
|
|
219
234
|
:::warning Production sanitizes database internals
|
|
220
|
-
In production the message is the **base message only
|
|
235
|
+
In production the message is the **base message only**. `Detail:` (which echoes row values like emails), `Table:`, and `Constraint:` are stripped, and `details.stack`/`details.cause` are omitted. Unexpected (non-client) database errors and connection failures return a generic `"Internal Server Error"`, so SQL, schema names, and connection host/port never leak. Use `requestId` and server logs to diagnose.
|
|
221
236
|
|
|
222
237
|
```json
|
|
223
238
|
{
|
|
@@ -230,13 +245,13 @@ In production the message is the **base message only** - `Detail:` (which echoes
|
|
|
230
245
|
```
|
|
231
246
|
:::
|
|
232
247
|
|
|
233
|
-
|
|
248
|
+
If you need a custom message for one constraint, catch it explicitly and re-throw everything else:
|
|
234
249
|
|
|
235
250
|
```typescript
|
|
236
|
-
import {
|
|
251
|
+
import { DefaultCRUDRepository, type TCount } from '@venizia/ignis';
|
|
237
252
|
import { getError, HTTP } from '@venizia/ignis-helpers';
|
|
238
253
|
|
|
239
|
-
export class UserRepository extends
|
|
254
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
|
|
240
255
|
async createWithCustomError(data: TCreateUser): Promise<TCount & { data: TUser }> {
|
|
241
256
|
try {
|
|
242
257
|
return await this.create({ data });
|
|
@@ -254,9 +269,9 @@ export class UserRepository extends DefaultRelationalRepository<typeof User.sche
|
|
|
254
269
|
}
|
|
255
270
|
```
|
|
256
271
|
|
|
257
|
-
##
|
|
272
|
+
## Customize the global handler
|
|
258
273
|
|
|
259
|
-
IGNIS wires a built-in handler by default
|
|
274
|
+
IGNIS wires a built-in handler by default. `AppErrorMiddleware` from `@venizia/ignis` is a class, registered as `new AppErrorMiddleware({ logger, rootKey }).value()`, and `value()` returns the Hono `ErrorHandler`. You rarely need to replace it. When you do, keep the same response contract:
|
|
260
275
|
|
|
261
276
|
```typescript
|
|
262
277
|
import { BaseApplication } from '@venizia/ignis';
|
|
@@ -309,9 +324,7 @@ export class Application extends BaseApplication {
|
|
|
309
324
|
}
|
|
310
325
|
```
|
|
311
326
|
|
|
312
|
-
##
|
|
313
|
-
|
|
314
|
-
All errors should follow a consistent format:
|
|
327
|
+
## The error response shape
|
|
315
328
|
|
|
316
329
|
```typescript
|
|
317
330
|
interface ErrorResponse {
|
|
@@ -334,12 +347,11 @@ interface ErrorResponse {
|
|
|
334
347
|
}
|
|
335
348
|
```
|
|
336
349
|
|
|
337
|
-
There
|
|
350
|
+
There's no top-level `messageCode` - read `normalized.code`. `extra` never mirrors `messageArgs`; the resolved interpolation values live at `normalized.args`.
|
|
338
351
|
|
|
339
|
-
**
|
|
352
|
+
**400 Bad Request:**
|
|
340
353
|
|
|
341
354
|
```json
|
|
342
|
-
// 400 Bad Request
|
|
343
355
|
{
|
|
344
356
|
"message": "Invalid request body",
|
|
345
357
|
"statusCode": 400,
|
|
@@ -347,10 +359,11 @@ There is no top-level `messageCode` - read `normalized.code`. `extra` never mirr
|
|
|
347
359
|
"requestId": "abc123",
|
|
348
360
|
"details": { "url": "http://localhost:3000/users", "path": "/users" }
|
|
349
361
|
}
|
|
362
|
+
```
|
|
350
363
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
364
|
+
**404 Not Found:** extra keys passed to `getError(...)` (for example `details`) surface under the top-level `extra`. The top-level `details` object is reserved for middleware context (url, path, stack, cause) - the two never share a key.
|
|
365
|
+
|
|
366
|
+
```json
|
|
354
367
|
{
|
|
355
368
|
"message": "User not found",
|
|
356
369
|
"statusCode": 404,
|
|
@@ -359,11 +372,11 @@ There is no top-level `messageCode` - read `normalized.code`. `extra` never mirr
|
|
|
359
372
|
"extra": { "details": { "id": "user-uuid" } },
|
|
360
373
|
"details": { "url": "http://localhost:3000/users/user-uuid", "path": "/users/:id" }
|
|
361
374
|
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
**422 Validation Error:** `message` and `normalized.code` come from the first failing issue - its `params.code` if the schema set one, otherwise the raw Zod code (for example `invalid_type`, `too_small`). `normalized.args` is always empty for a Zod issue; the full list of issues stays in `details.cause`.
|
|
362
378
|
|
|
363
|
-
|
|
364
|
-
// `message`/`normalized.code` come from the first failing issue - its `params.code` if the schema
|
|
365
|
-
// set one, otherwise the raw Zod code (e.g. `invalid_type`, `too_small`). `normalized.args` is
|
|
366
|
-
// always empty for a Zod issue; the full list of issues stays in `details.cause`.
|
|
379
|
+
```json
|
|
367
380
|
{
|
|
368
381
|
"message": "Invalid email format",
|
|
369
382
|
"statusCode": 422,
|
|
@@ -381,53 +394,44 @@ There is no top-level `messageCode` - read `normalized.code`. `extra` never mirr
|
|
|
381
394
|
]
|
|
382
395
|
}
|
|
383
396
|
}
|
|
384
|
-
|
|
385
|
-
// 500 Internal Error (production)
|
|
386
|
-
{
|
|
387
|
-
"message": "Internal server error",
|
|
388
|
-
"statusCode": 500,
|
|
389
|
-
"normalized": { "text": "Internal server error", "code": "core.system_error", "args": {} },
|
|
390
|
-
"requestId": "abc123",
|
|
391
|
-
"details": { "url": "http://localhost:3000/orders", "path": "/orders" }
|
|
392
|
-
}
|
|
393
397
|
```
|
|
394
398
|
|
|
395
|
-
##
|
|
399
|
+
## Log errors
|
|
396
400
|
|
|
397
|
-
### `%s`,
|
|
401
|
+
### Use `%s`, never `%j`, for an Error
|
|
398
402
|
|
|
399
403
|
`message` and `stack` are non-enumerable properties on a native `Error` (and on `ApplicationError`, which extends it). `%j` serializes via `JSON.stringify`, which only visits enumerable own properties - so `logger.error('... | error: %j', error)` silently drops both. Always use `%s` to log an `Error` instance; reserve `%j`/`%o` for plain data objects.
|
|
400
404
|
|
|
401
405
|
```typescript
|
|
402
|
-
//
|
|
406
|
+
// Good - %s prints message + stack
|
|
403
407
|
this.logger.error('[createOrder] Failed | error: %s', error);
|
|
404
408
|
|
|
405
|
-
//
|
|
409
|
+
// Bad - %j drops the stack
|
|
406
410
|
this.logger.error('[createOrder] Failed | error: %j', error);
|
|
407
411
|
```
|
|
408
412
|
|
|
409
413
|
> [!NOTE]
|
|
410
|
-
> An `ApplicationError` logged with `%j` does show its message text, because the text rides inside the enumerable `normalized.text`. That
|
|
414
|
+
> An `ApplicationError` logged with `%j` does show its message text, because the text rides inside the enumerable `normalized.text`. That's incidental, not a reprieve - the **stack** is still gone, which is the reason the rule exists. A plain `Error` under `%j` still logs little more than `{}`.
|
|
411
415
|
|
|
412
|
-
### What to
|
|
416
|
+
### What to log
|
|
413
417
|
|
|
414
418
|
```typescript
|
|
415
|
-
//
|
|
419
|
+
// Good - context for debugging. `%s` on the error itself carries message + stack
|
|
416
420
|
this.logger.error('[createOrder] Failed | userId: %s | orderId: %s | error: %s',
|
|
417
421
|
userId, orderId, error);
|
|
418
422
|
|
|
419
|
-
//
|
|
423
|
+
// Bad - no context, and `.message` throws the stack away
|
|
420
424
|
this.logger.error(error.message);
|
|
421
425
|
|
|
422
|
-
//
|
|
426
|
+
// Bad - sensitive data
|
|
423
427
|
this.logger.error('Login failed for user | password: %s', password);
|
|
424
428
|
```
|
|
425
429
|
|
|
426
|
-
### Log
|
|
430
|
+
### Log levels
|
|
427
431
|
|
|
428
|
-
`ILogger` has exactly five levels, each a direct method. `alert`, `http`, `verbose
|
|
432
|
+
`ILogger` has exactly five levels, each a direct method. `alert`, `http`, `verbose`, and `silly` don't exist.
|
|
429
433
|
|
|
430
|
-
| Level | Use
|
|
434
|
+
| Level | Use for |
|
|
431
435
|
|-------|---------|
|
|
432
436
|
| `emerg` | The process cannot continue - unrecoverable |
|
|
433
437
|
| `error` | Exceptions that need attention |
|
|
@@ -449,18 +453,20 @@ this.logger.info('[order] Created | orderId: %s | userId: %s', orderId, userId);
|
|
|
449
453
|
this.logger.debug('[query] Executing | sql: %s | params: %j', sql, params);
|
|
450
454
|
```
|
|
451
455
|
|
|
452
|
-
##
|
|
456
|
+
## Handle async errors
|
|
453
457
|
|
|
454
458
|
### Promises
|
|
455
459
|
|
|
460
|
+
`async`/`await` propagates errors on its own - most of the time, you write no error handling at all.
|
|
461
|
+
|
|
456
462
|
```typescript
|
|
457
|
-
//
|
|
463
|
+
// Good - errors propagate naturally with async/await
|
|
458
464
|
const processOrder = async (orderId: string) => {
|
|
459
465
|
const order = await orderRepository.findById({ id: orderId }); // Throws if fails
|
|
460
466
|
return paymentService.charge(order); // Throws if fails
|
|
461
467
|
};
|
|
462
468
|
|
|
463
|
-
//
|
|
469
|
+
// Good - explicit catch when you need to handle
|
|
464
470
|
const processOrderWithFallback = async (order: TOrder) => {
|
|
465
471
|
try {
|
|
466
472
|
return await paymentService.charge(order);
|
|
@@ -470,7 +476,7 @@ const processOrderWithFallback = async (order: TOrder) => {
|
|
|
470
476
|
}
|
|
471
477
|
};
|
|
472
478
|
|
|
473
|
-
//
|
|
479
|
+
// Bad - swallowing errors
|
|
474
480
|
const processOrderSilently = async () => {
|
|
475
481
|
try {
|
|
476
482
|
await dangerousOperation();
|
|
@@ -480,22 +486,24 @@ const processOrderSilently = async () => {
|
|
|
480
486
|
};
|
|
481
487
|
```
|
|
482
488
|
|
|
483
|
-
### Fire-and-
|
|
489
|
+
### Fire-and-forget
|
|
490
|
+
|
|
491
|
+
But a call you don't `await` needs its own `.catch()`, or a rejection crashes the process as an unhandled rejection.
|
|
484
492
|
|
|
485
493
|
```typescript
|
|
486
|
-
//
|
|
494
|
+
// Good - log errors from fire-and-forget operations
|
|
487
495
|
this.sendNotification(userId).catch(error => {
|
|
488
496
|
this.logger.error('[notify] Failed | userId: %s | error: %s', userId, error);
|
|
489
497
|
});
|
|
490
498
|
|
|
491
|
-
//
|
|
499
|
+
// Good - use void to indicate intentional fire-and-forget
|
|
492
500
|
void this.analytics.track('order_created', { orderId });
|
|
493
501
|
|
|
494
|
-
//
|
|
502
|
+
// Bad - unhandled promise rejection
|
|
495
503
|
this.sendNotification(userId); // If this rejects, crash!
|
|
496
504
|
```
|
|
497
505
|
|
|
498
|
-
##
|
|
506
|
+
## Roll back a transaction on failure
|
|
499
507
|
|
|
500
508
|
```typescript
|
|
501
509
|
import { getError, HTTP } from '@venizia/ignis-helpers';
|
|
@@ -525,13 +533,11 @@ const transferFunds = async (opts: { from: string; to: string; amount: number })
|
|
|
525
533
|
```
|
|
526
534
|
|
|
527
535
|
> [!NOTE]
|
|
528
|
-
> `rollback()` throws on failure, so it belongs in the `catch` exactly as
|
|
529
|
-
> `commit()` you already awaited outside one. A rollback that follows a **failed** commit is a
|
|
530
|
-
> deliberate no-op: the transaction is already torn down, so this canonical shape stays safe.
|
|
536
|
+
> `rollback()` throws on failure, so it belongs in the `catch` block exactly as shown here - never after a `commit()` you already awaited outside one. A rollback that follows a **failed** commit is a deliberate no-op: the transaction is already torn down, so this canonical shape stays safe.
|
|
531
537
|
|
|
532
|
-
##
|
|
538
|
+
## Parse errors on the client
|
|
533
539
|
|
|
534
|
-
|
|
540
|
+
Branch on `normalized.code` - it's always present. There's no top-level `messageCode`.
|
|
535
541
|
|
|
536
542
|
```typescript
|
|
537
543
|
// TypeScript client example
|
|
@@ -545,7 +551,6 @@ const createUser = async (data: TCreateUserRequest): Promise<TUser> => {
|
|
|
545
551
|
if (!response.ok) {
|
|
546
552
|
const error = await response.json();
|
|
547
553
|
|
|
548
|
-
// Branch on `normalized.code` - it is always present. There is no top-level `messageCode`.
|
|
549
554
|
switch (response.status) {
|
|
550
555
|
case 400: {
|
|
551
556
|
// Context passed to getError(...) arrives under `extra`, not `details`
|
|
@@ -579,7 +584,7 @@ const createUser = async (data: TCreateUserRequest): Promise<TUser> => {
|
|
|
579
584
|
};
|
|
580
585
|
```
|
|
581
586
|
|
|
582
|
-
##
|
|
587
|
+
## Checklist
|
|
583
588
|
|
|
584
589
|
| Category | Check |
|
|
585
590
|
|----------|-------|
|
|
@@ -592,7 +597,7 @@ const createUser = async (data: TCreateUserRequest): Promise<TUser> => {
|
|
|
592
597
|
| **Responses** | Consistent error format returned |
|
|
593
598
|
| **Security** | No sensitive data in error messages |
|
|
594
599
|
|
|
595
|
-
## See
|
|
600
|
+
## See also
|
|
596
601
|
|
|
597
602
|
- [Common Pitfalls](./common-pitfalls) - Error handling mistakes
|
|
598
603
|
- [Testing Strategies](./testing-strategies) - Testing error scenarios
|
|
@@ -25,12 +25,12 @@ bun -e "console.log(Buffer.from(crypto.getRandomValues(new Uint8Array(32))).toSt
|
|
|
25
25
|
|
|
26
26
|
### Redaction at Log Time
|
|
27
27
|
|
|
28
|
-
Never hard-coding a secret is not enough on its own
|
|
28
|
+
Never hard-coding a secret is not enough on its own. An options object holding one still ends up in a log line the moment it is passed to `logger.info('...: %s', opts)`. `@venizia/ignis-helpers` provides two primitives for exactly this:
|
|
29
29
|
|
|
30
30
|
| Function | Use For | Behavior |
|
|
31
31
|
|----------|---------|----------|
|
|
32
32
|
| `redactSecrets(value)` | Any object/array being logged | Recursively replaces every value whose key matches a secret-looking name with `'[REDACTED]'` |
|
|
33
|
-
| `redactUrlCredentials(url)` | A connection/broker URL string | Strips the password out of a URL's authority section
|
|
33
|
+
| `redactUrlCredentials(url)` | A connection/broker URL string | Strips the password out of a URL's authority section: `mqtts://user:hunter2@broker:8883` becomes `mqtts://user:[REDACTED]@broker:8883`. A value that doesn't parse as a URL, or one with no password, is returned unchanged |
|
|
34
34
|
|
|
35
35
|
```typescript
|
|
36
36
|
import { redactSecrets, redactUrlCredentials } from '@venizia/ignis-helpers';
|
|
@@ -46,9 +46,9 @@ this.logger.info('[connect] Broker: %s', redactUrlCredentials(brokerUrl));
|
|
|
46
46
|
- Vault wire keys: `client_token`, `secret_id`, `role_id`
|
|
47
47
|
- HTTP header spellings: `x-api-key`, `x-vault-token`, `cookie`, `set-cookie`, `proxy-authorization`, `www-authenticate`
|
|
48
48
|
|
|
49
|
-
It also handles the shapes naive redaction breaks on
|
|
49
|
+
It also handles the shapes naive redaction breaks on. An `Error` is reprojected so its non-enumerable `message`/`stack` survive. Cycles become `'[Circular]'`, and buffers/typed arrays are summarized as `[Binary N bytes]` instead of serialized.
|
|
50
50
|
|
|
51
|
-
These are the primitives the framework itself uses
|
|
51
|
+
These are the primitives the framework itself uses. Outbound HTTP request configs (`NodeFetcher`/`AxiosFetcher`, see [Network Helper](/extensions/helpers/network/)) and MQTT broker URLs (`MQTTClientHelper`, see [Queue Helper](/extensions/helpers/queue/)) are both redacted this way before they reach a log line.
|
|
52
52
|
|
|
53
53
|
> [!WARNING]
|
|
54
54
|
> `APP_ENV_LOGGER_DO_REDACT=false` turns both functions into the identity function. It is a local-debugging kill-switch and must never be set in production. The check is fail-closed - only the literal string `false` disables redaction - and is read per call, so it can be flipped at runtime.
|
|
@@ -495,7 +495,7 @@ Deliberate `getError` messages are always returned verbatim, in every environmen
|
|
|
495
495
|
- Admin actions
|
|
496
496
|
|
|
497
497
|
> [!NOTE]
|
|
498
|
-
> When logging a request or connection config that might carry credentials, wrap it in `redactSecrets()` (or `redactUrlCredentials()` for a URL) rather than logging it raw
|
|
498
|
+
> When logging a request or connection config that might carry credentials, wrap it in `redactSecrets()` (or `redactUrlCredentials()` for a URL) rather than logging it raw. See [Redaction at Log Time](#redaction-at-log-time) above. This is what the framework's own outbound HTTP and MQTT logging already does automatically.
|
|
499
499
|
|
|
500
500
|
## Security Checklist
|
|
501
501
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# API Reference
|
|
2
2
|
|
|
3
|
-
Automatic interactive API documentation generated from OpenAPI
|
|
3
|
+
Automatic interactive API documentation generated from your OpenAPI spec. A pluggable UI provider renders it - Scalar by default, or classic Swagger UI.
|
|
4
4
|
|
|
5
5
|
> [!NOTE] Renamed from SwaggerComponent
|
|
6
|
-
> Swagger UI is
|
|
6
|
+
> Swagger UI is one of the pluggable UI providers, not the only one, so the component carries a vendor-neutral name. The deprecated `Swagger*` aliases are **removed**: use `ApiReferenceComponent`, `IApiReferenceOptions` and `ApiReferenceBindingKeys.API_REFERENCE_OPTIONS`.
|
|
7
7
|
|
|
8
8
|
## Quick Reference
|
|
9
9
|
|
|
@@ -27,7 +27,7 @@ import type { IApiReferenceOptions, IUIProvider, IUIConfig, IGetProviderParams }
|
|
|
27
27
|
|
|
28
28
|
## In one example
|
|
29
29
|
|
|
30
|
-
Register the component -
|
|
30
|
+
Register the component - no configuration required. The docs UI comes up at `<app base path>/doc/explorer`, the raw spec at `<app base path>/doc/openapi.json`. With the `/api` base path used throughout the getting-started guide, that's `/api/doc/explorer`.
|
|
31
31
|
|
|
32
32
|
```typescript
|
|
33
33
|
// src/application.ts
|
|
@@ -79,13 +79,14 @@ export class HelloController extends BaseRestController {
|
|
|
79
79
|
|
|
80
80
|
## How it works
|
|
81
81
|
|
|
82
|
-
- **Options merge group by group.** `binding()` reads the bound `IApiReferenceOptions`, then shallow-merges `base`, `doc`, and `ui` each against their own defaults
|
|
83
|
-
- **`explorer.info` is always overwritten.** The component
|
|
84
|
-
- **`explorer.servers` fills in only when empty.** A supplied server entry is kept as-is
|
|
85
|
-
- **UI type resolution uses `??`, not `||`.** The source is `restOptions.ui.type ?? DocumentUITypes.SWAGGER
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
-
|
|
82
|
+
- **Options merge group by group.** `binding()` reads the bound `IApiReferenceOptions`, then shallow-merges `base`, `doc`, and `ui` each against their own defaults. Overriding `ui.type` alone still keeps `ui.path` and every `base`/`doc` field.
|
|
83
|
+
- **`explorer.info` is always overwritten.** The component reads your `package.json` via `application.getAppInfo()`. It replaces `explorer.info` with `{ title, version, description, contact }` - any `explorer.info` you bind is discarded. Edit `package.json` instead.
|
|
84
|
+
- **`explorer.servers` fills in only when empty.** A supplied server entry is kept as-is. Otherwise, the component builds one from `application.getServerAddress()` plus the base path.
|
|
85
|
+
- **UI type resolution uses `??`, not `||`.** The source is `restOptions.ui.type ?? DocumentUITypes.SWAGGER`. Only `null`/`undefined` falls back, and it falls back to `'swagger'` - not the configured default `'scalar'`.
|
|
86
|
+
- An explicit empty string is NOT repaired by this fallback: it fails `DocumentUITypes.isValid()` and the component throws `Invalid document UI Type` immediately.
|
|
87
|
+
- **UI libraries load lazily.** `SwaggerUIProvider`/`ScalarUIProvider` each `await import()` their rendering library inside `render()`. That happens on the first request to the docs UI, not at application startup. Only the configured provider's library is ever loaded.
|
|
88
|
+
- **`ScalarUIProvider` renames `title` to `pageTitle`.** Scalar's own render API takes `pageTitle`, not `title` - worth knowing if you inspect the rendered output or write a custom UI provider.
|
|
89
|
+
- **Security schemes are always registered.** JWT (`bearer`) and Basic security schemes are added to the OpenAPI registry unconditionally. Routes using `authenticate: { strategies: ['jwt'] }` or `['basic']` render the correct auth UI as a result.
|
|
89
90
|
|
|
90
91
|
## Common tasks
|
|
91
92
|
|
|
@@ -102,10 +103,10 @@ this.bind<IApiReferenceOptions>({
|
|
|
102
103
|
key: ApiReferenceBindingKeys.API_REFERENCE_OPTIONS,
|
|
103
104
|
}).toValue({ restOptions: { base: { path: '/api-docs' } } });
|
|
104
105
|
```
|
|
105
|
-
Result: UI at
|
|
106
|
+
Result: UI at `<app base path>/api-docs/explorer`, spec at `<app base path>/api-docs/openapi.json`. The group merge keeps `doc.path`/`ui.path` defaults.
|
|
106
107
|
|
|
107
108
|
### Set the info block shown in the UI
|
|
108
|
-
`explorer.info` always comes from `package.json
|
|
109
|
+
`explorer.info` always comes from `package.json`. Update `name`, `version`, `description`, and `author` there - binding `explorer.info` directly has no effect.
|
|
109
110
|
|
|
110
111
|
### Register a custom UI provider
|
|
111
112
|
`UIProviderFactory.register()` only understands `'swagger'`/`'scalar'`. Register a custom provider directly on the factory before `ApiReferenceComponent.binding()` runs:
|
|
@@ -154,7 +155,7 @@ export interface IApiReferenceOptions {
|
|
|
154
155
|
|-----|----------|------|----------|---------|
|
|
155
156
|
| `@app/api-reference/options` | `ApiReferenceBindingKeys.API_REFERENCE_OPTIONS` | `IApiReferenceOptions` | No | See Options table |
|
|
156
157
|
|
|
157
|
-
`SwaggerBindingKeys.SWAGGER_OPTIONS` is
|
|
158
|
+
`SwaggerBindingKeys.SWAGGER_OPTIONS` is removed. It was only ever an alias for the key above - there was never a separate binding under the literal `'@app/swagger/options'`, so nothing needs rebinding.
|
|
158
159
|
|
|
159
160
|
**Default value:**
|
|
160
161
|
```typescript
|
|
@@ -179,12 +180,12 @@ const DEFAULT_API_REFERENCE_OPTIONS: IApiReferenceOptions = {
|
|
|
179
180
|
> The `explorer.info` values above are never used at runtime - `binding()` unconditionally overwrites `explorer.info` from `package.json`. They exist only as structural defaults.
|
|
180
181
|
|
|
181
182
|
### API endpoints
|
|
182
|
-
| Method | Path (default) | Description |
|
|
183
|
+
| Method | Path (default, relative to app base) | Description |
|
|
183
184
|
|--------|-----------------|-------------|
|
|
184
185
|
| `GET` | `/doc/explorer` | Documentation UI (Scalar by default) |
|
|
185
186
|
| `GET` | `/doc/openapi.json` | Raw OpenAPI specification |
|
|
186
187
|
|
|
187
|
-
|
|
188
|
+
These paths are mounted under your application's own base path - `path.base` in `IApplicationConfigs`. With the `/api` base path from the getting-started guide, that's `GET /api/doc/explorer`. They also shift with `restOptions.base.path`, `restOptions.ui.path`, and `restOptions.doc.path`.
|
|
188
189
|
|
|
189
190
|
### UIProviderFactory
|
|
190
191
|
| Method | Signature | Description |
|
|
@@ -208,7 +209,7 @@ class DocumentUITypes {
|
|
|
208
209
|
}
|
|
209
210
|
```
|
|
210
211
|
|
|
211
|
-
`TDocumentUIType` is derived via `TConstValue`, which extracts the union of every `static readonly` string on `DocumentUITypes
|
|
212
|
+
`TDocumentUIType` is derived via `TConstValue`, which extracts the union of every `static readonly` string on `DocumentUITypes`. The type stays in sync with the constants automatically.
|
|
212
213
|
|
|
213
214
|
### Component lifecycle (`binding()`)
|
|
214
215
|
1. **Resolve options** - reads `ApiReferenceBindingKeys.API_REFERENCE_OPTIONS` with `isOptional: true`, then merges `base`/`doc`/`ui` each against `DEFAULT_API_REFERENCE_OPTIONS`
|
|
@@ -260,8 +261,8 @@ class DocumentUITypes {
|
|
|
260
261
|
|
|
261
262
|
**Files:**
|
|
262
263
|
|
|
263
|
-
- [`packages/core/src/components/api-reference/component.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/api-reference/component.ts) - `ApiReferenceComponent`
|
|
264
|
-
- [`packages/core/src/components/api-reference/ui-factory.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/api-reference/ui-factory.ts) - `UIProviderFactory`, `SwaggerUIProvider`, `ScalarUIProvider`
|
|
265
|
-
- [`packages/core/src/components/api-reference/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/api-reference/common/types.ts) - `IApiReferenceOptions`, `IUIProvider`, `IUIConfig`
|
|
266
|
-
- [`packages/core/src/components/api-reference/common/keys.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/api-reference/common/keys.ts) - `ApiReferenceBindingKeys`
|
|
267
|
-
- [`packages/core/src/components/api-reference/common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/api-reference/common/constants.ts) - `DocumentUITypes`
|
|
264
|
+
- [`packages/core-server/src/components/api-reference/component.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/api-reference/component.ts) - `ApiReferenceComponent`
|
|
265
|
+
- [`packages/core-server/src/components/api-reference/ui-factory.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/api-reference/ui-factory.ts) - `UIProviderFactory`, `SwaggerUIProvider`, `ScalarUIProvider`
|
|
266
|
+
- [`packages/core-server/src/components/api-reference/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/api-reference/common/types.ts) - `IApiReferenceOptions`, `IUIProvider`, `IUIConfig`
|
|
267
|
+
- [`packages/core-server/src/components/api-reference/common/keys.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/api-reference/common/keys.ts) - `ApiReferenceBindingKeys`
|
|
268
|
+
- [`packages/core-server/src/components/api-reference/common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/api-reference/common/constants.ts) - `DocumentUITypes`
|