@venizia/ignis-docs 0.2.0 → 0.2.1-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/README.md +14 -14
  2. package/content/best-practices/api-usage-examples.md +39 -19
  3. package/content/best-practices/architectural-patterns.md +24 -13
  4. package/content/best-practices/architecture-decisions.md +29 -16
  5. package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
  6. package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
  7. package/content/best-practices/code-style-standards/control-flow.md +33 -1
  8. package/content/best-practices/code-style-standards/documentation.md +28 -8
  9. package/content/best-practices/code-style-standards/function-patterns.md +15 -4
  10. package/content/best-practices/code-style-standards/index.md +7 -2
  11. package/content/best-practices/code-style-standards/naming-conventions.md +27 -3
  12. package/content/best-practices/code-style-standards/route-definitions.md +9 -5
  13. package/content/best-practices/code-style-standards/tooling.md +14 -1
  14. package/content/best-practices/code-style-standards/type-safety.md +39 -6
  15. package/content/best-practices/common-pitfalls.md +59 -35
  16. package/content/best-practices/contribution-workflow.md +8 -4
  17. package/content/best-practices/data-modeling.md +78 -62
  18. package/content/best-practices/deployment-strategies.md +56 -19
  19. package/content/best-practices/error-handling.md +247 -153
  20. package/content/best-practices/index.md +6 -0
  21. package/content/best-practices/performance-optimization.md +26 -13
  22. package/content/best-practices/security-guidelines.md +35 -9
  23. package/content/best-practices/testing-strategies.md +7 -2
  24. package/content/best-practices/troubleshooting-tips.md +27 -17
  25. package/content/extensions/components/api-reference.md +109 -323
  26. package/content/extensions/components/authentication/api.md +489 -602
  27. package/content/extensions/components/authentication/errors.md +135 -498
  28. package/content/extensions/components/authentication/index.md +89 -801
  29. package/content/extensions/components/authentication/usage.md +231 -955
  30. package/content/extensions/components/authorization/api.md +991 -644
  31. package/content/extensions/components/authorization/errors.md +204 -208
  32. package/content/extensions/components/authorization/getting-started.md +227 -0
  33. package/content/extensions/components/authorization/index.md +88 -795
  34. package/content/extensions/components/authorization/usage.md +219 -528
  35. package/content/extensions/components/health-check.md +77 -243
  36. package/content/extensions/components/index.md +24 -90
  37. package/content/extensions/components/mail/api.md +553 -285
  38. package/content/extensions/components/mail/errors.md +76 -62
  39. package/content/extensions/components/mail/index.md +111 -463
  40. package/content/extensions/components/mail/usage.md +139 -173
  41. package/content/extensions/components/request-tracker.md +70 -173
  42. package/content/extensions/components/socket-io/api.md +462 -785
  43. package/content/extensions/components/socket-io/errors.md +49 -51
  44. package/content/extensions/components/socket-io/index.md +69 -372
  45. package/content/extensions/components/socket-io/usage.md +212 -105
  46. package/content/extensions/components/static-asset/api.md +461 -141
  47. package/content/extensions/components/static-asset/errors.md +121 -53
  48. package/content/extensions/components/static-asset/index.md +84 -606
  49. package/content/extensions/components/static-asset/usage.md +184 -299
  50. package/content/extensions/components/template/index.md +3 -3
  51. package/content/extensions/components/websocket/api.md +311 -404
  52. package/content/extensions/components/websocket/errors.md +47 -56
  53. package/content/extensions/components/websocket/index.md +75 -407
  54. package/content/extensions/components/websocket/usage.md +120 -338
  55. package/content/extensions/helpers/cron/index.md +52 -160
  56. package/content/extensions/helpers/crypto/index.md +67 -480
  57. package/content/extensions/helpers/crypto/reference.md +528 -0
  58. package/content/extensions/helpers/env/index.md +64 -178
  59. package/content/extensions/helpers/error/index.md +286 -196
  60. package/content/extensions/helpers/index.md +61 -47
  61. package/content/extensions/helpers/inversion/index.md +76 -550
  62. package/content/extensions/helpers/inversion/reference.md +530 -0
  63. package/content/extensions/helpers/kafka/admin.md +23 -3
  64. package/content/extensions/helpers/kafka/compile-binary.md +83 -52
  65. package/content/extensions/helpers/kafka/consumer.md +65 -28
  66. package/content/extensions/helpers/kafka/examples.md +46 -253
  67. package/content/extensions/helpers/kafka/index.md +55 -617
  68. package/content/extensions/helpers/kafka/producer.md +156 -22
  69. package/content/extensions/helpers/kafka/schema-registry.md +59 -79
  70. package/content/extensions/helpers/logger/hf-logger.md +220 -0
  71. package/content/extensions/helpers/logger/index.md +78 -552
  72. package/content/extensions/helpers/logger/pino.md +105 -0
  73. package/content/extensions/helpers/logger/reference.md +937 -0
  74. package/content/extensions/helpers/network/api.md +276 -195
  75. package/content/extensions/helpers/network/index.md +87 -524
  76. package/content/extensions/helpers/queue/index.md +80 -897
  77. package/content/extensions/helpers/queue/reference.md +494 -0
  78. package/content/extensions/helpers/redis/index.md +86 -640
  79. package/content/extensions/helpers/redis/reference.md +784 -0
  80. package/content/extensions/helpers/secrets/index.md +136 -0
  81. package/content/extensions/helpers/socket-io/api.md +325 -204
  82. package/content/extensions/helpers/socket-io/index.md +79 -429
  83. package/content/extensions/helpers/storage/api.md +584 -464
  84. package/content/extensions/helpers/storage/index.md +80 -573
  85. package/content/extensions/helpers/types/index.md +75 -495
  86. package/content/extensions/helpers/types/reference.md +689 -0
  87. package/content/extensions/helpers/uid/index.md +176 -189
  88. package/content/extensions/helpers/websocket/api.md +366 -214
  89. package/content/extensions/helpers/websocket/index.md +68 -503
  90. package/content/extensions/helpers/worker-thread/index.md +62 -396
  91. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  92. package/content/extensions/index.md +38 -39
  93. package/content/extensions/src-details/mcp-server.md +96 -548
  94. package/content/guides/core-concepts/persistent/datasources.md +2 -2
  95. package/content/guides/core-concepts/persistent/index.md +5 -1
  96. package/content/guides/core-concepts/persistent/models.md +1 -1
  97. package/content/guides/core-concepts/persistent/pglite.md +218 -0
  98. package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
  99. package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
  100. package/content/guides/core-concepts/persistent/search-typesense.md +28 -16
  101. package/content/guides/core-concepts/persistent/sqlite.md +296 -0
  102. package/content/guides/core-concepts/persistent/transactions.md +12 -11
  103. package/content/guides/core-concepts/secrets-vault.md +177 -0
  104. package/content/guides/core-concepts/services.md +1 -1
  105. package/content/guides/get-started/5-minute-quickstart.md +59 -206
  106. package/content/guides/get-started/philosophy.md +135 -670
  107. package/content/guides/get-started/setup.md +53 -74
  108. package/content/guides/migrations/redis-helpers-migration.md +5 -4
  109. package/content/guides/migrations/scoped-rbac-migration.md +5 -5
  110. package/content/guides/migrations/unified-connectors-migration.md +8 -7
  111. package/content/guides/tutorials/ecommerce-api.md +3 -8
  112. package/content/guides/tutorials/realtime-chat.md +1 -1
  113. package/content/references/base/application.md +64 -22
  114. package/content/references/base/components.md +3 -3
  115. package/content/references/base/connectors.md +79 -136
  116. package/content/references/base/controllers.md +12 -12
  117. package/content/references/base/datasources-reference.md +600 -0
  118. package/content/references/base/datasources.md +84 -444
  119. package/content/references/base/dependency-injection.md +25 -46
  120. package/content/references/base/filter-system/application-usage.md +95 -123
  121. package/content/references/base/filter-system/array-operators.md +33 -43
  122. package/content/references/base/filter-system/comparison-operators.md +55 -63
  123. package/content/references/base/filter-system/default-filter.md +144 -350
  124. package/content/references/base/filter-system/fields-order-pagination.md +116 -148
  125. package/content/references/base/filter-system/index.md +114 -253
  126. package/content/references/base/filter-system/json-filtering.md +52 -181
  127. package/content/references/base/filter-system/list-operators.md +28 -44
  128. package/content/references/base/filter-system/logical-operators.md +69 -114
  129. package/content/references/base/filter-system/null-operators.md +41 -98
  130. package/content/references/base/filter-system/pattern-matching.md +48 -51
  131. package/content/references/base/filter-system/quick-reference.md +93 -196
  132. package/content/references/base/filter-system/range-operators.md +22 -38
  133. package/content/references/base/filter-system/tips.md +70 -133
  134. package/content/references/base/filter-system/use-cases.md +173 -232
  135. package/content/references/base/grpc-controllers.md +53 -17
  136. package/content/references/base/index.md +5 -3
  137. package/content/references/base/middlewares.md +43 -28
  138. package/content/references/base/models-reference.md +886 -0
  139. package/content/references/base/models.md +81 -1452
  140. package/content/references/base/providers.md +8 -8
  141. package/content/references/base/repositories/advanced.md +281 -419
  142. package/content/references/base/repositories/index.md +84 -644
  143. package/content/references/base/repositories/mixins.md +25 -21
  144. package/content/references/base/repositories/relations.md +194 -375
  145. package/content/references/base/repositories/soft-deletable.md +67 -55
  146. package/content/references/base/secrets.md +267 -0
  147. package/content/references/base/services.md +8 -6
  148. package/content/references/configuration/environment-variables.md +73 -21
  149. package/content/references/configuration/index.md +51 -31
  150. package/content/references/index.md +1 -1
  151. package/content/references/quick-reference.md +3 -16
  152. package/content/references/utilities/crypto.md +35 -76
  153. package/content/references/utilities/date.md +33 -73
  154. package/content/references/utilities/duration.md +85 -0
  155. package/content/references/utilities/index.md +6 -2
  156. package/content/references/utilities/jsx-reference.md +298 -0
  157. package/content/references/utilities/jsx.md +82 -525
  158. package/content/references/utilities/module.md +81 -61
  159. package/content/references/utilities/parse.md +34 -64
  160. package/content/references/utilities/performance.md +33 -58
  161. package/content/references/utilities/promise.md +28 -62
  162. package/content/references/utilities/request.md +58 -218
  163. package/content/references/utilities/retry.md +139 -0
  164. package/content/references/utilities/schema.md +43 -137
  165. package/content/references/utilities/statuses-reference.md +361 -0
  166. package/content/references/utilities/statuses.md +63 -667
  167. package/dist/mcp-server/index.js +0 -0
  168. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  169. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
  170. package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
  171. package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
  172. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
  173. package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
  174. package/package.json +24 -23
@@ -1,6 +1,107 @@
1
- # Mail -- API Reference
1
+ ---
2
+ title: Mail Component - Full Reference
3
+ description: Binding keys, configuration options, interfaces, and internal implementation of the Mail component
4
+ difficulty: intermediate
5
+ ---
6
+
7
+ # Mail Component Reference
8
+
9
+ Every binding key, configuration variant, interface, and internal mechanism of `MailComponent`. For the task-oriented walkthrough, see [Usage & Examples](./usage).
10
+
11
+ **Files:**
12
+
13
+ - [`packages/core-server/src/components/mail/component.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/component.ts)
14
+ - [`packages/core-server/src/components/mail/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/common/types.ts)
15
+ - [`packages/core-server/src/components/mail/common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/common/constants.ts)
16
+ - [`packages/core-server/src/components/mail/common/keys.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/common/keys.ts)
17
+ - [`packages/core-server/src/components/mail/services/mail.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/services/mail.service.ts)
18
+ - [`packages/core-server/src/components/mail/services/template.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/services/template.service.ts)
19
+ - [`packages/core-server/src/components/mail/services/generator.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/services/generator.service.ts)
20
+ - [`packages/core-server/src/components/mail/providers/mail-transporter.provider.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/providers/mail-transporter.provider.ts)
21
+ - [`packages/core-server/src/components/mail/providers/mail-queue-executor.provider.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/providers/mail-queue-executor.provider.ts)
22
+ - [`packages/core-server/src/components/mail/helpers/transporters/nodemail-transporter.helper.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/helpers/transporters/nodemail-transporter.helper.ts)
23
+ - [`packages/core-server/src/components/mail/helpers/transporters/mailgun-transporter.helper.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/helpers/transporters/mailgun-transporter.helper.ts)
24
+ - [`packages/core-server/src/components/mail/helpers/executors/direct-executor.helper.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/helpers/executors/direct-executor.helper.ts)
25
+ - [`packages/core-server/src/components/mail/helpers/executors/internal-queue-executor.helper.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/helpers/executors/internal-queue-executor.helper.ts)
26
+ - [`packages/core-server/src/components/mail/helpers/executors/bull-mq-executor.helper.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/helpers/executors/bull-mq-executor.helper.ts)
27
+ - [`packages/core-server/src/components/mail/utilities/type.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/utilities/type.utility.ts)
28
+ - [`packages/core-server/src/components/mail/utilities/verification.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/utilities/verification.utility.ts)
29
+
30
+ ## Quick reference
31
+
32
+ | Item | Value |
33
+ |------|-------|
34
+ | Package | `@venizia/ignis` |
35
+ | Subpath | `@venizia/ignis/mail` |
36
+ | Component class | `MailComponent` |
37
+ | Runtimes | Both (Bun, Node.js) |
38
+
39
+ | Component | Purpose |
40
+ |-----------|---------|
41
+ | `MailComponent` | Registers mail services, transporters, generators, and the queue executor |
42
+ | `MailService` | `send()`, `sendBatch()`, `sendTemplate()`, `verify()` |
43
+ | `TemplateEngineService` | <code v-pre>{{variable}}</code> substitution engine, in-memory template registry |
44
+ | `NodemailerTransportHelper` | SMTP transport via `nodemailer` |
45
+ | `MailgunTransportHelper` | Mailgun HTTP API transport via `mailgun.js` |
46
+ | `DirectMailExecutorHelper` | Runs the processor immediately, no queue |
47
+ | `InternalQueueMailExecutorHelper` | In-memory queue (`SequentialQueueHelper`) |
48
+ | `BullMQMailExecutorHelper` | Redis-backed queue, distributed workers |
49
+ | `MailTransportProvider` | Factory: `TMailOptions` -> `IMailTransport` |
50
+ | `MailQueueExecutorProvider` | Factory: `IMailQueueExecutorConfig` -> `IMailQueueExecutor` |
51
+ | `NumericCodeGenerator` | Cryptographically random numeric codes |
52
+ | `RandomTokenGenerator` | Cryptographically random base64url tokens |
53
+ | `DefaultVerificationDataGenerator` | Composes both generators into an `IVerificationData` |
54
+
55
+ ## Import paths
2
56
 
3
- > Architecture, interfaces, and internal implementation details of the Mail component.
57
+ ```typescript
58
+ import {
59
+ MailComponent,
60
+ MailKeys,
61
+ MailProviders,
62
+ MailErrorCodes,
63
+ MailDefaults,
64
+ MailExecutorErrors,
65
+ MailQueueExecutorTypes,
66
+ BullMQExecutorModes,
67
+ MailService,
68
+ TemplateEngineService,
69
+ NumericCodeGenerator,
70
+ RandomTokenGenerator,
71
+ DefaultVerificationDataGenerator,
72
+ MailTransportProvider,
73
+ MailQueueExecutorProvider,
74
+ } from '@venizia/ignis/mail';
75
+
76
+ import type {
77
+ TMailOptions,
78
+ IBaseMailOptions,
79
+ INodemailerMailOptions,
80
+ IMailgunMailOptions,
81
+ ICustomMailOptions,
82
+ IGenericMailOptions,
83
+ IMailService,
84
+ IMailTemplateEngine,
85
+ IMailMessage,
86
+ IMailSendResult,
87
+ IMailTransport,
88
+ IMailAttachment,
89
+ IMailQueueExecutor,
90
+ IMailQueueExecutorConfig,
91
+ IMailQueueOptions,
92
+ IMailQueueResult,
93
+ IMailProcessorResult,
94
+ ITemplate,
95
+ IVerificationCodeGenerator,
96
+ IVerificationTokenGenerator,
97
+ IVerificationDataGenerator,
98
+ IVerificationData,
99
+ IVerificationGenerationOptions,
100
+ TMailProvider,
101
+ TNodemailerConfig,
102
+ TMailgunConfig,
103
+ } from '@venizia/ignis/mail';
104
+ ```
4
105
 
5
106
  ## Architecture
6
107
 
@@ -8,126 +109,292 @@
8
109
  ┌─────────────────────────────────────────────────┐
9
110
  │ Your Application │
10
111
  │ │
11
- NodemailerComponent (wrapper)
12
- │ ├── binds MailKeys.MAIL_OPTIONS
112
+ preConfigure()
113
+ │ ├── binds MailKeys.MAIL_OPTIONS (required)
13
114
  │ ├── binds MailKeys.MAIL_QUEUE_EXECUTOR_CONFIG │
115
+ │ │ (optional -- defaults to `direct`) │
14
116
  │ └── registers MailComponent │
15
117
  └───────────────────────┬─────────────────────────┘
16
118
 
17
119
 
18
120
  ┌─────────────────────────────────────────────────┐
19
- │ MailComponent
121
+ │ MailComponent.binding()
20
122
  │ │
21
- binding()
22
- │ ├── initGenerators() │
123
+ ├── initGenerators() (transient bindings)
23
124
  │ │ ├── NumericCodeGenerator │
24
125
  │ │ ├── RandomTokenGenerator │
25
126
  │ │ └── DefaultVerificationDataGenerator │
26
127
  │ │ │
27
- │ ├── initProviders()
28
- │ │ ├── MailTransportProvider (singleton)
29
- │ │ └── MailQueueExecutorProvider (singleton)
128
+ │ ├── initProviders() (singleton bindings)
129
+ │ │ ├── MailTransportProvider
130
+ │ │ └── MailQueueExecutorProvider
30
131
  │ │ │
31
- │ ├── initServices()
32
- │ │ ├── MailService (singleton)
33
- │ │ └── TemplateEngineService (singleton)
132
+ │ ├── initServices() (singleton bindings)
133
+ │ │ ├── MailService
134
+ │ │ └── TemplateEngineService
34
135
  │ │ │
35
136
  │ └── createAndBindInstances() │
36
- │ ├── Transport Instance ◄── MAIL_OPTIONS │
37
- │ └── Queue Executor ◄── QUEUE_CONFIG
137
+ │ ├── Transport Instance ◄── MAIL_OPTIONS │
138
+ │ └── Queue Executor ◄── QUEUE_CONFIG
139
+ │ (or the direct │
140
+ │ default) │
38
141
  └─────────────────────────────────────────────────┘
39
142
  ```
40
143
 
41
- **Architecture Components:**
144
+ `MailService` and the queue executor are **independent** consumers of the transport/config. `IMailQueueExecutor` never calls `MailService`. See [How it works](./#how-it-works) on the Overview for that distinction.
42
145
 
43
- - **`MailComponent`** -- Initializes and registers all mail services, transporters, and queue executors. Extends `BaseComponent`. Validates that `MailKeys.MAIL_OPTIONS` is bound before proceeding
44
- - **`MailService`** -- Extends `BaseService`. Provides `send()`, `sendBatch()`, `sendTemplate()`, and `verify()`. Injects transport instance and template engine. Validates messages before sending
45
- - **`TemplateEngineService`** -- Extends `BaseService`. Manages email templates with simple <code v-pre>{{variable}}</code> substitution. Stores templates in an in-memory `Map`
46
- - **`MailTransportProvider`** -- Extends `BaseProvider`. Factory that creates transport instances with type-guard methods (`isNodemailerOptions`, `isMailgunOptions`, `isCustomOptions`) and throws `MailErrorCodes` on invalid configs
47
- - **`MailQueueExecutorProvider`** -- Extends `BaseProvider`. Factory that creates queue executor instances. Throws for missing sub-configs (e.g., `config.internalQueue` or `config.bullmq`)
48
- - **Verification Generators** -- `NumericCodeGenerator`, `RandomTokenGenerator`, `DefaultVerificationDataGenerator`. Generate verification codes, tokens, and data for email verification flows
146
+ **Source:** [`component.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/component.ts)
49
147
 
50
- **Tech Stack:**
148
+ ## Binding keys
51
149
 
52
- - **Nodemailer** -- SMTP-based email sending (peer dependency: `nodemailer`)
53
- - **Mailgun** -- Mailgun API client (peer dependency: `mailgun.js`)
54
- - **BullMQ** (optional) -- Redis-backed queue for distributed processing (peer dependency: `bullmq`)
55
- - **Handlebars-style Templates** -- Simple <code v-pre>{{variable}}</code> syntax for email templates (no external dependency)
150
+ | Key | Constant | Type | Required | Default |
151
+ |-----|----------|------|----------|---------|
152
+ | `@app/components/mail/options` | `MailKeys.MAIL_OPTIONS` | `TMailOptions` | **Yes** | -- |
153
+ | `@app/components/mail/queue/executor-config` | `MailKeys.MAIL_QUEUE_EXECUTOR_CONFIG` | `IMailQueueExecutorConfig` | No | `{ type: MailQueueExecutorTypes.DIRECT }` |
154
+ | `@app/components/mail/service` | `MailKeys.MAIL_SERVICE` | `IMailService` | No | `MailService` (singleton) |
155
+ | `@app/components/mail/services/template-engine` | `MailKeys.MAIL_TEMPLATE_ENGINE` | `IMailTemplateEngine` | No | `TemplateEngineService` (singleton) |
156
+ | `@app/components/mail/transport-provider` | `MailKeys.MAIL_TRANSPORT_PROVIDER` | `TGetMailTransportFn` | No | `MailTransportProvider` (singleton) |
157
+ | `@app/components/mail/transport-instance` | `MailKeys.MAIL_TRANSPORT_INSTANCE` | `IMailTransport` | No | Created by the component |
158
+ | `@app/components/mail/queue-executor-provider` | `MailKeys.MAIL_QUEUE_EXECUTOR_PROVIDER` | `TGetMailQueueExecutorFn` | No | `MailQueueExecutorProvider` (singleton) |
159
+ | `@app/components/mail/queue-executor-instance` | `MailKeys.MAIL_QUEUE_EXECUTOR_INSTANCE` | `IMailQueueExecutor` | No | Created by the component |
160
+ | `@app/components/mail/verification/code-generator` | `MailKeys.MAIL_VERIFICATION_CODE_GENERATOR` | `IVerificationCodeGenerator` | No | `NumericCodeGenerator` (transient) |
161
+ | `@app/components/mail/verification/token-generator` | `MailKeys.MAIL_VERIFICATION_TOKEN_GENERATOR` | `IVerificationTokenGenerator` | No | `RandomTokenGenerator` (transient) |
162
+ | `@app/components/mail/verification/data-generator` | `MailKeys.MAIL_VERIFICATION_DATA_GENERATOR` | `IVerificationDataGenerator` | No | `DefaultVerificationDataGenerator` (transient) |
56
163
 
57
- ## Transport Layer
164
+ > [!IMPORTANT]
165
+ > `MailKeys.MAIL_OPTIONS` is the only binding `MailComponent` requires. It throws `Mail options not configured` in `binding()` if the key is not bound. `MailKeys.MAIL_QUEUE_EXECUTOR_CONFIG` is read with `isOptional: true` -- when it is not bound, `createAndBindInstances()` falls back to `{ type: MailQueueExecutorTypes.DIRECT }` rather than failing startup.
58
166
 
59
- ### Transport Layer Implementation
167
+ **Source:** [`common/keys.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/common/keys.ts)
60
168
 
61
- The `MailTransportProvider` extends `BaseProvider` and returns a factory function from its `value()` method. It creates the appropriate transport based on the `provider` field in `MailKeys.MAIL_OPTIONS`:
169
+ ## Configuration
62
170
 
63
- - **`'nodemailer'`** -- Creates a `NodemailerTransportHelper` backed by `nodemailer` with SMTP or OAuth2 auth
64
- - **`'mailgun'`** -- Creates a `MailgunTransportHelper` using the Mailgun HTTP API
65
- - **`'custom'`** -- Expects the `config` value to implement `IMailTransport` directly (must have `send()` and `verify()`)
66
- - **Any other string** -- Falls through to `default` and throws `Unsupported mail provider: <provider>` with `MailErrorCodes.INVALID_CONFIGURATION`
171
+ ### Transport options (`TMailOptions`)
67
172
 
68
- The provider uses three private type-guard methods to narrow the union type before creating transports:
173
+ A discriminated union on `provider`, extending `IBaseMailOptions`:
69
174
 
70
175
  ```typescript
71
- private isNodemailerOptions(options: TMailOptions): options is INodemailerMailOptions
72
- private isMailgunOptions(options: TMailOptions): options is IMailgunMailOptions
73
- private isCustomOptions(options: TMailOptions): options is ICustomMailOptions
176
+ interface IBaseMailOptions {
177
+ from?: string;
178
+ fromName?: string;
179
+ }
180
+
181
+ interface INodemailerMailOptions extends IBaseMailOptions {
182
+ provider: 'nodemailer';
183
+ config: TNodemailerConfig; // SMTPTransport | SMTPTransport.Options | string
184
+ module?: TNodemailerModule; // the peer itself; see "Peer dependency loading"
185
+ }
186
+
187
+ interface IMailgunMailOptions extends IBaseMailOptions {
188
+ provider: 'mailgun';
189
+ config: TMailgunConfig; // AnyType & { domain: string } -- also requires username, key at runtime
190
+ module?: TMailgunModule; // the peer itself; see "Peer dependency loading"
191
+ }
192
+
193
+ interface ICustomMailOptions extends IBaseMailOptions {
194
+ provider: 'custom';
195
+ config: IMailTransport; // Must implement send() and verify()
196
+ }
197
+
198
+ interface IGenericMailOptions extends IBaseMailOptions {
199
+ provider: string;
200
+ config: Record<string, AnyType>;
201
+ }
202
+
203
+ type TMailOptions =
204
+ | INodemailerMailOptions
205
+ | IMailgunMailOptions
206
+ | ICustomMailOptions
207
+ | IGenericMailOptions;
74
208
  ```
75
209
 
76
- For custom transports, an additional `isMailTransport()` utility validates that the config object has `send()` and `verify()` methods, reporting specific missing methods in the error message.
210
+ **Nodemailer (SMTP with basic auth):**
211
+
212
+ ```typescript
213
+ {
214
+ provider: MailProviders.NODEMAILER,
215
+ from: 'noreply@example.com',
216
+ fromName: 'Example App',
217
+ config: {
218
+ host: 'smtp.gmail.com',
219
+ port: 465,
220
+ secure: true,
221
+ auth: { user: 'your-email@gmail.com', pass: 'your-app-password' },
222
+ },
223
+ }
224
+ ```
77
225
 
78
- Both built-in transports implement `IMailTransport`:
226
+ **Nodemailer (OAuth2):**
79
227
 
80
228
  ```typescript
81
- interface IMailTransport {
82
- send(message: IMailMessage): Promise<IMailSendResult>;
83
- verify(): Promise<boolean>;
84
- close?(): Promise<void>;
229
+ {
230
+ provider: MailProviders.NODEMAILER,
231
+ from: applicationEnvironment.get<string>('APP_ENV_MAIL_FROM') ?? 'noreply@example.com',
232
+ fromName: applicationEnvironment.get<string>('APP_ENV_MAIL_FROM_NAME') ?? 'Example App',
233
+ config: {
234
+ host: applicationEnvironment.get<string>('APP_ENV_MAIL_HOST') ?? 'smtp.gmail.com',
235
+ port: +(applicationEnvironment.get<number>('APP_ENV_MAIL_PORT') ?? 465),
236
+ secure: toBoolean(applicationEnvironment.get<boolean>('APP_ENV_MAIL_SECURE') ?? true),
237
+ auth: {
238
+ type: 'oauth2',
239
+ user: applicationEnvironment.get<string>('APP_ENV_MAIL_USER'),
240
+ clientId: applicationEnvironment.get<string>('APP_ENV_MAIL_CLIENT_ID'),
241
+ clientSecret: applicationEnvironment.get<string>('APP_ENV_MAIL_CLIENT_SECRET'),
242
+ refreshToken: applicationEnvironment.get<string>('APP_ENV_MAIL_REFRESH_TOKEN'),
243
+ },
244
+ },
245
+ }
246
+ ```
247
+
248
+ `APP_ENV_MAIL_*` is an app-level convention, not a framework-defined env var. `applicationEnvironment.get()` reads whatever variables your wrapper component chooses to look up. A matching `.env`:
249
+
250
+ ```
251
+ APP_ENV_MAIL_HOST=smtp.gmail.com
252
+ APP_ENV_MAIL_PORT=465
253
+ APP_ENV_MAIL_SECURE=true
254
+ APP_ENV_MAIL_USER=your-email@gmail.com
255
+ APP_ENV_MAIL_CLIENT_ID=your-oauth2-client-id
256
+ APP_ENV_MAIL_CLIENT_SECRET=your-oauth2-client-secret
257
+ APP_ENV_MAIL_REFRESH_TOKEN=your-oauth2-refresh-token
258
+ ```
259
+
260
+ > [!TIP]
261
+ > For Gmail OAuth2, follow [Google's OAuth2 setup guide](https://developers.google.com/gmail/api/auth/web-server) to obtain the client ID, secret, and refresh token.
262
+
263
+ **Mailgun:**
264
+
265
+ ```typescript
266
+ {
267
+ provider: MailProviders.MAILGUN,
268
+ from: 'noreply@example.com',
269
+ fromName: 'Example App',
270
+ config: {
271
+ username: 'api', // required -- mailgun.js client username
272
+ key: process.env.MAILGUN_API_KEY, // required -- Mailgun API key
273
+ domain: 'mg.example.com', // required
274
+ host: 'api.eu.mailgun.net', // optional -- EU region
275
+ },
276
+ }
277
+ ```
278
+
279
+ > [!IMPORTANT]
280
+ > `MailgunTransportHelper` validates `username`, `key`, and `domain` on construction. It throws `Invalid Mailgun configuration | Missing required keys: <keys>` if any is missing. This check runs even though `TMailgunConfig`'s only *typed* requirement is `domain` - `username` and `key` are checked at runtime, not by the type.
281
+
282
+ **Custom transport:**
283
+
284
+ ```typescript
285
+ {
286
+ provider: MailProviders.CUSTOM,
287
+ from: 'noreply@example.com',
288
+ config: myTransportImplementingIMailTransport, // must have send() and verify()
289
+ }
290
+ ```
291
+
292
+ **Generic provider (extensibility escape hatch):**
293
+
294
+ ```typescript
295
+ {
296
+ provider: 'sendgrid',
297
+ from: 'noreply@example.com',
298
+ config: { apiKey: process.env.SENDGRID_API_KEY },
299
+ }
300
+ ```
301
+
302
+ > [!WARNING]
303
+ > `IGenericMailOptions` falls through to the `default` case in `MailTransportProvider` and throws `Unsupported mail provider: <provider>`. The framework's built-in provider does not handle it. `IGenericMailOptions` exists only so you can bind a **custom** `MailTransportProvider` that recognizes the provider string.
304
+
305
+ ### Queue executor options (`IMailQueueExecutorConfig`)
306
+
307
+ ```typescript
308
+ interface IMailQueueExecutorConfig {
309
+ type: TConstValue<typeof MailQueueExecutorTypes>; // 'direct' | 'internal-queue' | 'bullmq'
310
+ internalQueue?: {
311
+ identifier: string;
312
+ };
313
+ bullmq?: {
314
+ redis: IRedisSingleHelperOptions;
315
+ queue: { identifier: string; name: string };
316
+ mode: TConstValue<typeof BullMQExecutorModes>; // REQUIRED -- no default
317
+ };
85
318
  }
86
319
  ```
87
320
 
88
- ### Module Validation
321
+ ```typescript
322
+ // Direct (also the implicit default when MAIL_QUEUE_EXECUTOR_CONFIG is unbound)
323
+ { type: 'direct' }
324
+
325
+ // Internal queue (in-memory)
326
+ { type: 'internal-queue', internalQueue: { identifier: 'mail-internal-queue' } }
327
+
328
+ // BullMQ (Redis-backed)
329
+ {
330
+ type: 'bullmq',
331
+ bullmq: {
332
+ redis: { host: 'localhost', port: 6379, password: 'your-redis-password' },
333
+ queue: { identifier: 'mail-queue', name: 'mail-queue' },
334
+ mode: 'both', // 'queue-only' | 'worker-only' | 'both' -- required, no default
335
+ },
336
+ }
337
+ ```
89
338
 
90
- Both `NodemailerTransportHelper` and `MailgunTransportHelper` call `validateModule()` in their `configure()` method before importing the peer dependency. This checks that the required npm module is installed:
339
+ **Choosing an executor:**
91
340
 
92
- - `NodemailerTransportHelper` requires `nodemailer`
93
- - `MailgunTransportHelper` requires `mailgun.js`
341
+ | Executor | Use when | Why |
342
+ |---|---|---|
343
+ | `direct` | Development or low-volume apps | No queueing overhead |
344
+ | `internal-queue` | Single-instance apps, moderate volume | In-memory, with retry |
345
+ | `bullmq` | Distributed or high-volume systems | Redis-backed, configurable concurrency/priority/backoff |
94
346
 
95
- If the module is not installed, `validateModule()` throws an error identifying the missing peer dependency.
347
+ **Source:** [`common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/common/types.ts)
96
348
 
97
- **Nodemailer Transport:**
349
+ ### Constants
98
350
 
99
- The `NodemailerTransportHelper` extends `BaseHelper` and wraps Nodemailer's SMTP transport. In `configure()`, it calls `require('nodemailer')` and creates a transporter. Key behaviors:
100
- - `send()` maps `IMailMessage` fields to Nodemailer's mail options, joining array recipients with `, `
101
- - `send()` catches transport errors and returns `{ success: false, error: ... }` instead of throwing
102
- - `verify()` delegates to Nodemailer's built-in `transporter.verify()` SMTP handshake
103
- - `close()` calls `transporter.close()` to release the connection
351
+ | Constant | Value | Description |
352
+ |----------|-------|-------------|
353
+ | `MailDefaults.BATCH_CONCURRENCY` | `5` | Default concurrent sends in `sendBatch()` |
354
+ | `MailDefaults.FALLBACK_FROM` | `'noreply@example.com'` | Used by `getDefaultFrom()` when `options.from` is unset |
355
+ | `MailExecutorErrors.PROCESSOR_NOT_SET` | `'Processor not set. Call setProcessor() first.'` | Thrown by all three queue executors |
356
+ | `MailQueueExecutorTypes.DIRECT` | `'direct'` | Immediate execution |
357
+ | `MailQueueExecutorTypes.INTERNAL_QUEUE` | `'internal-queue'` | In-memory queue |
358
+ | `MailQueueExecutorTypes.BULLMQ` | `'bullmq'` | Redis-backed queue |
359
+ | `BullMQExecutorModes.QUEUE_ONLY` | `'queue-only'` | Producer only (enqueue) |
360
+ | `BullMQExecutorModes.WORKER_ONLY` | `'worker-only'` | Consumer only (process) |
361
+ | `BullMQExecutorModes.BOTH` | `'both'` | Full duplex (produce + consume) |
104
362
 
105
- **Mailgun Transport:**
363
+ `MailQueueExecutorTypes` and `BullMQExecutorModes` both carry a `SCHEME_SET`/`MODE_SET` and a static `isValid()`:
364
+
365
+ ```typescript
366
+ MailQueueExecutorTypes.isValid('bullmq'); // true
367
+ MailQueueExecutorTypes.isValid('unknown'); // false
368
+ BullMQExecutorModes.isValid('both'); // true
369
+ BullMQExecutorModes.isValid('invalid'); // false
370
+ ```
106
371
 
107
- The `MailgunTransportHelper` extends `BaseHelper` and uses the Mailgun REST API via `mailgun.js`. In `configure()`, it creates a `Mailgun` client using `FormData`. Key behaviors:
108
- - `send()` converts `IMailMessage` to Mailgun's format: `to` becomes an array, `replyTo` becomes `h:Reply-To`, all custom headers are prefixed with `h:`
109
- - Attachments are mapped to `{ filename, data }` objects where `data` is `path ?? content ?? Buffer.from('')`
110
- - `verify()` sends a test email to `verify@<domain>` with `o:testmode: 'yes'` flag to check API credentials without actually sending
111
- - No `close()` method (HTTP API is stateless)
372
+ #### `MailErrorCodes`
112
373
 
113
- **Custom Transport:**
374
+ Built through `MessageCode.build()`, so every value is lower-case (`ApplicationError` lower-cases whatever it is handed):
114
375
 
115
- You can provide your own transport implementation by setting `provider: MailProviders.CUSTOM` and passing an object that implements `IMailTransport` as the `config` value. The provider validates that `send()` and `verify()` are functions, reporting specific missing methods. This is useful for integrating with services like SendGrid, AWS SES, or custom SMTP relays.
376
+ | Constant | Value | Description |
377
+ |----------|-------|-------------|
378
+ | `MailErrorCodes.INVALID_CONFIGURATION` | `'core.mail.invalid_configuration'` | Invalid or missing configuration (transport, template engine, subject, body) |
379
+ | `MailErrorCodes.SEND_FAILED` | `'core.mail.send_failed'` | Single email send failed |
380
+ | `MailErrorCodes.VERIFICATION_FAILED` | `'core.mail.verification_failed'` | Transport connection verification failed |
381
+ | `MailErrorCodes.INVALID_RECIPIENT` | `'core.mail.invalid_recipient'` | Missing or empty recipient address |
382
+ | `MailErrorCodes.BATCH_SEND_FAILED` | `'core.mail.batch_send_failed'` | Batch email operation failed |
383
+ | `MailErrorCodes.TEMPLATE_NOT_FOUND` | `'core.mail.template_not_found'` | Template name not found in registry |
116
384
 
117
- ## IMailService Interface
385
+ **Source:** [`common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/common/constants.ts)
386
+
387
+ ## `IMailService` interface
118
388
 
119
389
  ```typescript
120
390
  interface IMailService {
121
- // Send a single email
122
391
  send(message: IMailMessage): Promise<IMailSendResult>;
123
392
 
124
- // Send multiple emails with controlled concurrency
125
393
  sendBatch(
126
394
  messages: IMailMessage[],
127
395
  options?: { concurrency?: number },
128
396
  ): Promise<IMailSendResult[]>;
129
397
 
130
- // Send email using a registered template
131
398
  sendTemplate(opts: {
132
399
  templateName: string;
133
400
  data: Record<string, any>;
@@ -135,100 +402,78 @@ interface IMailService {
135
402
  options?: Partial<IMailMessage>;
136
403
  }): Promise<IMailSendResult>;
137
404
 
138
- // Verify transport connection
139
405
  verify(): Promise<boolean>;
140
406
  }
141
407
  ```
142
408
 
143
- **Method Details:**
144
-
145
- **`send(message: IMailMessage)`**
146
-
147
- Sends a single email using the configured transport. Internally:
148
- 1. Calls `validateMessage()` which throws for missing `to`, `subject`, or both `text`/`html` (see error reference below)
149
- 2. Merges `message.from` with the default from address (via `getDefaultFrom()`)
150
- 3. Delegates to `transport.send()`
151
- 4. Returns a result object with `success`, `messageId`, and optional `error` fields
152
- 5. If the transport throws, catches the error and re-throws with `MailErrorCodes.SEND_FAILED`
409
+ **`send(message)`**
153
410
 
154
- **`sendBatch(messages: IMailMessage[], options?: { concurrency?: number })`**
411
+ 1. `validateMessage()` throws for missing `to`, missing `subject`, or missing both `text`/`html` (400, see below).
412
+ 2. Merges `message.from` with `getDefaultFrom()` if unset.
413
+ 3. Delegates to `transport.send()`.
414
+ 4. Returns `{ success, messageId, error? }`.
415
+ 5. If the transport throws, and the error is **not** already an `ApplicationError`, it is caught and re-thrown as `MailErrorCodes.SEND_FAILED` (500).
416
+ 6. An `ApplicationError` thrown earlier in the same `try` block - for example, from `validateMessage()` - is re-thrown as-is, unchanged.
155
417
 
156
- Sends multiple emails with controlled concurrency using `executePromiseWithLimit()`. Default concurrency is `MailDefaults.BATCH_CONCURRENCY` (5). Each message is sent via `send()` individually. If an individual `send()` throws, it is caught and converted to `{ success: false, error: '...' }` so the batch continues. If the entire batch operation fails, throws with `MailErrorCodes.BATCH_SEND_FAILED`.
418
+ **`sendBatch(messages, options?)`**
157
419
 
158
- **`sendTemplate(opts: { templateName, data, recipients, options? })`**
420
+ Sends every message via `send()` with concurrency bounded by `executePromiseWithLimit()`, default `MailDefaults.BATCH_CONCURRENCY` (`5`). A `send()` that throws is caught per-message and downgraded to `{ success: false, error }`; a failure in the batch operation itself throws `MailErrorCodes.BATCH_SEND_FAILED`.
159
421
 
160
- Renders a registered template with the provided data and sends the email. The subject is resolved through a priority chain:
422
+ **`sendTemplate(opts)`**
161
423
 
162
- 1. `options.subject` -- explicit override from the caller
163
- 2. Template subject rendered through the template engine (if `templateData.subject` is defined)
164
- 3. `'No Subject'` -- fallback if neither is provided
165
-
166
- Throws `MailErrorCodes.INVALID_CONFIGURATION` if the template engine is not configured. Re-throws any other errors (including `TEMPLATE_NOT_FOUND` from the template engine).
424
+ Renders a registered template and sends it. Subject resolution order: `options.subject` -> the template's own `subject` (rendered through the same engine) -> `'No Subject'`. Throws `MailErrorCodes.INVALID_CONFIGURATION` ("Template engine not configured") if `templateEngine` was not injected. Re-throws any other error unchanged, including `TEMPLATE_NOT_FOUND` from the template engine.
167
425
 
168
426
  **`verify()`**
169
427
 
170
- Verifies the transport connection without sending an email. Delegates to `transport.verify()`. If the transport throws, catches and re-throws with `MailErrorCodes.VERIFICATION_FAILED`.
171
-
172
- ### Protected Methods (MailService)
428
+ Delegates to `transport.verify()`. If the transport throws, wraps it as `MailErrorCodes.VERIFICATION_FAILED` (500).
173
429
 
174
- **`validateMessage(message: IMailMessage)`**
430
+ ### Protected methods (`MailService`)
175
431
 
176
- Pre-transport validation that throws immediately for invalid messages:
432
+ **`validateMessage(message)`**
177
433
 
178
- | Check | Error Code | Status | Message |
434
+ | Check | Error code | Status | Message |
179
435
  |-------|-----------|--------|---------|
180
- | `to` is falsy or empty array | `INVALID_RECIPIENT` | 400 | `Recipient email address is required` |
436
+ | `to` is falsy or an empty array | `INVALID_RECIPIENT` | 400 | `Recipient email address is required` |
181
437
  | `subject` is falsy | `INVALID_CONFIGURATION` | 400 | `Email subject is required` |
182
438
  | Both `text` and `html` are falsy | `INVALID_CONFIGURATION` | 400 | `Email must have either text or html content` |
183
439
 
184
440
  **`getDefaultFrom()`**
185
441
 
186
- Constructs the default "from" address. If `options.fromName` is set, returns `"fromName" <from>`. Otherwise returns `options.from ?? 'noreply@example.com'`.
442
+ - If `options.from` is unset, returns `MailDefaults.FALLBACK_FROM` (`'noreply@example.com'`).
443
+ - Else if `options.fromName` is unset, returns `options.from` as-is.
444
+ - Else returns `"${fromName}" <${from}>`.
445
+
446
+ **Source:** [`services/mail.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/services/mail.service.ts)
187
447
 
188
- ## IMailMessage Interface
448
+ ## `IMailMessage` interface
189
449
 
190
450
  ```typescript
191
451
  interface IMailMessage {
192
- from?: string; // Sender email (uses default if not provided)
193
- to: string | string[]; // Recipient(s)
194
- cc?: string | string[]; // CC recipient(s)
195
- bcc?: string | string[]; // BCC recipient(s)
196
- replyTo?: string; // Reply-to address
197
- subject: string; // Email subject
198
- text?: string; // Plain text content
199
- html?: string; // HTML content
200
- attachments?: IMailAttachment[]; // File attachments
201
- headers?: Record<string, string>; // Custom headers
202
- requireValidate?: boolean; // Validate template data
203
- [key: string]: any; // Additional arbitrary fields
452
+ from?: string; // Uses getDefaultFrom() if not provided
453
+ to: string | string[];
454
+ cc?: string | string[];
455
+ bcc?: string | string[];
456
+ replyTo?: string;
457
+ subject: string;
458
+ text?: string;
459
+ html?: string;
460
+ attachments?: IMailAttachment[];
461
+ headers?: Record<string, string>;
462
+ requireValidate?: boolean; // Passed through to sendTemplate()'s render() call
463
+ [key: string]: any; // Open-ended -- provider-specific fields pass through
204
464
  }
205
465
  ```
206
466
 
207
- **Field Details:**
208
-
209
- **`from`**
210
-
211
- Sender email address. If not provided, uses the default `from` value from `MailKeys.MAIL_OPTIONS`. When `fromName` is configured, the default from is formatted as `"Display Name" <email@example.com>`. Can be overridden per-message for multi-tenant scenarios.
212
-
213
- **`to`, `cc`, `bcc`**
214
-
215
- Recipient addresses. Can be a single string or an array of strings. Format can be either plain email (`user@example.com`) or display name + email (`John Doe <john@example.com>`). For Nodemailer, arrays are joined with `, `. For Mailgun, arrays are passed as-is.
216
-
217
- **`replyTo`**
218
-
219
- Reply-to address if different from the sender. Useful for no-reply addresses that route replies to a support inbox. Mailgun maps this to `h:Reply-To`.
220
-
221
- **`subject`**
222
-
223
- Email subject line. Supports template variables when used with `sendTemplate()` (the subject is rendered through the same template engine).
224
-
225
- **`text`, `html`**
226
-
227
- Plain text and HTML versions of the email body. At least one must be provided (validated by `validateMessage()`). Most email clients prefer HTML but fall back to text if HTML is not available. Best practice is to provide both.
228
-
229
- **`attachments`**
230
-
231
- Array of `IMailAttachment` objects:
467
+ | Field | Notes |
468
+ |-------|-------|
469
+ | `from` | Falls back to `getDefaultFrom()`. Can be overridden per message (multi-tenant scenarios). |
470
+ | `to`, `cc`, `bcc` | Single string or array. Nodemailer joins arrays with `', '`; Mailgun passes `to` as-is (always coerced to an array), `cc`/`bcc` pass through unmodified. |
471
+ | `replyTo` | Nodemailer passes it directly; Mailgun maps it to `h:Reply-To`. |
472
+ | `subject` | Rendered through the template engine when set via `sendTemplate()`. |
473
+ | `text`, `html` | At least one is required (`validateMessage()`). |
474
+ | `attachments` | See `IMailAttachment` below. |
475
+ | `headers` | Nodemailer passes them directly; Mailgun prefixes every key with `h:`. |
476
+ | `requireValidate` | `true` makes template rendering throw on missing placeholders instead of preserving them as literal text. Defaults to `false`. |
232
477
 
233
478
  ```typescript
234
479
  interface IMailAttachment {
@@ -241,28 +486,17 @@ interface IMailAttachment {
241
486
  }
242
487
  ```
243
488
 
244
- Examples:
245
- - A file path: `{ filename: 'doc.pdf', path: '/path/to/doc.pdf' }`
246
- - A buffer: `{ filename: 'data.txt', content: Buffer.from('...') }`
247
- - An inline image: `{ filename: 'logo.png', path: '...', cid: 'logo' }`
489
+ - File path: `{ filename: 'doc.pdf', path: '/path/to/doc.pdf' }`
490
+ - Buffer: `{ filename: 'data.txt', content: Buffer.from('...') }`
491
+ - Inline image: `{ filename: 'logo.png', path: '...', cid: 'logo' }`
492
+ - Mailgun maps each attachment to `{ filename, data: att.path ?? att.content ?? Buffer.from('') }`.
248
493
 
249
- **`headers`**
494
+ **Source:** [`common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/common/types.ts)
250
495
 
251
- Custom SMTP headers. For Nodemailer, passed directly. For Mailgun, each key is auto-prefixed with `h:`.
252
-
253
- **`requireValidate`**
254
-
255
- When `true`, template rendering will throw an error if any <code v-pre>{{variable}}</code> placeholders are missing from the data object. Defaults to `false` (missing variables are preserved as their original placeholder text, not replaced with empty strings).
256
-
257
- **`[key: string]: any`**
258
-
259
- The interface is open-ended -- additional fields are accepted for provider-specific options.
260
-
261
- ## IMailTemplateEngine Interface
496
+ ## `IMailTemplateEngine` interface
262
497
 
263
498
  ```typescript
264
499
  interface IMailTemplateEngine {
265
- // Render a template with data
266
500
  render(opts: {
267
501
  templateData?: string;
268
502
  templateName?: string;
@@ -270,93 +504,118 @@ interface IMailTemplateEngine {
270
504
  requireValidate?: boolean;
271
505
  }): string;
272
506
 
273
- // Register a new template
274
507
  registerTemplate(opts: { name: string; content: string }): void;
275
508
 
276
- // Validate template data
277
509
  validateTemplateData(opts: { template: string; data: Record<string, any> }): {
278
510
  isValid: boolean;
279
511
  missingKeys: string[];
280
512
  allKeys: string[];
281
513
  };
282
514
 
283
- // Get a registered template
284
515
  getTemplate(name: string): ITemplate | undefined;
285
-
286
- // List all registered templates
287
516
  listTemplates(): ITemplate[];
288
-
289
- // Check if template exists
290
517
  hasTemplate(name: string): boolean;
291
-
292
- // Remove a template
293
518
  removeTemplate(name: string): boolean;
294
519
  }
295
520
  ```
296
521
 
297
- **Method Details:**
522
+ `TemplateEngineService` also exposes `clearTemplates(): void`. It resets the registry, but it is **not** part of the `IMailTemplateEngine` interface - you can only reach it when you hold the concrete class, not the interface type.
298
523
 
299
- **`render(opts: { templateData?, templateName?, data, requireValidate? })`**
524
+ | Method | Behavior |
525
+ |--------|----------|
526
+ | `render(opts)` | Renders by `templateName` (registry lookup) or raw `templateData`. Throws if neither is given. Throws `TEMPLATE_NOT_FOUND` (404) if `templateName` is not registered. Delegates to `renderSimpleTemplate()`. |
527
+ | `registerTemplate(opts)` | `options` (on the class) accepts `subject`/`description` (`Partial<ITemplate>`). Overwrites an existing template with the same name. |
528
+ | `validateTemplateData(opts)` | Extracts every unique <code v-pre>{{key}}</code> via `/\{\{(\s*[\w.]+\s*)\}\}/g`, deduplicates, resolves nested dot-notation values. Returns `isValid`, `missingKeys`, `allKeys`. |
529
+ | `getTemplate(name)` | Returns `undefined` if not registered. |
530
+ | `listTemplates()` | All templates, as an array of `ITemplate`. |
531
+ | `hasTemplate(name)` | Registry membership check. |
532
+ | `removeTemplate(name)` | Logs the removal, returns `true`/`false`. |
300
533
 
301
- Renders a template by name (from registry) or by raw template string (`templateData`). At least one of `templateData` or `templateName` must be provided -- throws if neither is given. If `templateName` is used, looks up the template and throws `TEMPLATE_NOT_FOUND` if not registered. Delegates to `renderSimpleTemplate()` which replaces all <code v-pre>{{variable}}</code> placeholders with values from the `data` object. If `requireValidate` is `true`, throws `INVALID_CONFIGURATION` if any placeholders are missing from the data.
534
+ ```typescript
535
+ interface ITemplate {
536
+ name: string;
537
+ content?: string;
538
+ render?: (data: Record<string, AnyType>) => string;
539
+ subject?: string;
540
+ description?: string;
541
+ }
542
+ ```
302
543
 
303
- **`registerTemplate(opts: { name, content, options? })`**
544
+ The `render` field supports a custom per-template render function, though the built-in `TemplateEngineService` always uses `content` + `renderSimpleTemplate()` instead.
304
545
 
305
- Registers a new template in the in-memory registry. The `options` parameter (on the class implementation) can include `subject` and `description` via `Partial<ITemplate>`. Overwrites any existing template with the same name.
546
+ **Source:** [`services/template.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/services/template.service.ts)
306
547
 
307
- **`validateTemplateData(opts: { template, data })`**
548
+ ## Transport layer
308
549
 
309
- Extracts all <code v-pre>{{variable}}</code> placeholders from the template string using the regex `/\{\{(\s*[\w.]+\s*)\}\}/g`. Deduplicates keys. For each unique key, resolves nested values via dot notation. Returns:
310
- - `isValid` -- `true` if all placeholders have non-null, non-undefined values
311
- - `missingKeys` -- Array of placeholder names that are missing or null/undefined in the data
312
- - `allKeys` -- Array of all unique placeholder names found in the template
550
+ `MailTransportProvider.value()` returns a factory function that switches on `TMailOptions.provider`:
313
551
 
314
- **`getTemplate(name: string)`**
552
+ | Provider | Result |
553
+ |----------|--------|
554
+ | `'nodemailer'` | `NodemailerTransportHelper`, backed by `nodemailer` |
555
+ | `'mailgun'` | `MailgunTransportHelper`, backed by the Mailgun HTTP API (`mailgun.js`) |
556
+ | `'custom'` | The `config` value itself, validated to implement `IMailTransport` |
557
+ | Any other string | Throws `Unsupported mail provider: <provider>` (`INVALID_CONFIGURATION`, 500) |
315
558
 
316
- Retrieves a registered template by name. Returns `undefined` if the template does not exist.
559
+ Type narrowing uses three private guards, each checking `provider === X && 'config' in options`:
317
560
 
318
- **`listTemplates()`**
561
+ ```typescript
562
+ private isNodemailerOptions(options: TMailOptions): options is INodemailerMailOptions
563
+ private isMailgunOptions(options: TMailOptions): options is IMailgunMailOptions
564
+ private isCustomOptions(options: TMailOptions): options is ICustomMailOptions
565
+ ```
319
566
 
320
- Returns an array of all registered templates (values from the internal `Map`).
567
+ For `custom`, an additional `isMailTransport()` utility check reports specifically which of `send`/`verify` is missing.
321
568
 
322
- **`hasTemplate(name: string)`**
569
+ ```typescript
570
+ interface IMailTransport {
571
+ send(message: IMailMessage): Promise<IMailSendResult>;
572
+ verify(): Promise<boolean>;
573
+ close?(): Promise<void>;
574
+ }
575
+ ```
323
576
 
324
- Checks if a template with the given name exists in the registry.
577
+ ### Peer dependency loading
325
578
 
326
- **`removeTemplate(name: string)`**
579
+ Pass the peer yourself through `module`, or let the transport find it. With `module` set, the transport uses it as-is; without it, the transport falls back to `ModuleUtility.loadSync({ module })` from the client-factory seam `configure()` calls. A missing package then throws the framework's install hint - `[ModuleUtility.loadSync] nodemailer is required. Please install 'nodemailer'` - not Node's raw `Cannot find module`.
327
580
 
328
- Removes a template from the registry. Logs the removal. Returns `true` if the template was found and removed, `false` otherwise.
581
+ That fallback keeps the specifier invisible to `Bun.build`. Importing `@venizia/ignis/mail` for the Nodemailer transport no longer drags `mailgun.js` into your bundle, and the reverse holds too.
329
582
 
330
- ### ITemplate Interface
583
+ **A compiled application must pass `module`.** A `bun build --compile` binary ships without `node_modules`, so the runtime lookup has nothing to resolve against and the component throws that install hint at boot - with the peer sitting in `package.json`, which is no help because nothing put it inside the binary. The static import is what embeds it:
331
584
 
332
585
  ```typescript
333
- interface ITemplate {
334
- name: string;
335
- content?: string;
336
- render?: (data: Record<string, AnyType>) => string;
337
- subject?: string;
338
- description?: string;
339
- }
586
+ import * as nodemailer from 'nodemailer';
587
+
588
+ this.bind({ key: MailKeys.MAIL_OPTIONS }).toValue({
589
+ provider: MailProviders.NODEMAILER,
590
+ config: { host, port, secure, auth },
591
+ module: nodemailer,
592
+ });
340
593
  ```
341
594
 
342
- The `render` function on the interface supports custom render implementations, though the built-in `TemplateEngineService` uses `content` + `renderSimpleTemplate()` instead.
595
+ `module` is typed as the shape the transport calls (`createTransport` for Nodemailer, a constructor for Mailgun), so handing over the wrong thing is a compile error rather than a boot crash. Prefer it over [`ModuleUtility.register`](/references/utilities/module#compiled-binaries): the dependency arrives where it is used and cannot be defeated by binding order. Every IGNIS component that reaches an optional peer now takes one of these options - the [table in the module reference](/references/utilities/module#compiled-binaries) lists them.
343
596
 
344
- ## Additional Interfaces
597
+ **Nodemailer (`NodemailerTransportHelper`, extends `BaseHelper`):**
345
598
 
346
- ### IMailSendResult
599
+ - `configure()` builds the transporter via `nodemailer.createTransport(config)`.
600
+ - `send()` maps `IMailMessage` fields to Nodemailer's mail options, joining array recipients with `', '`. It catches transport errors and returns `{ success: false, error }` - it never throws.
601
+ - `verify()` delegates to `transporter.verify()` (SMTP handshake). It catches errors and returns `false` - it never throws.
602
+ - `close()` calls `transporter.close()`.
347
603
 
348
- ```typescript
349
- interface IMailSendResult {
350
- success: boolean;
351
- messageId?: string;
352
- response?: any;
353
- error?: string;
354
- }
355
- ```
604
+ **Mailgun (`MailgunTransportHelper`, extends `BaseHelper`):**
605
+
606
+ - `configure()` calls `validateConfig()` **before** building the client. `username`, `key`, and `domain` must all be present in `config`, or it throws `Invalid Mailgun configuration | Missing required keys: <keys>` (`INVALID_CONFIGURATION`, 500). This check runs even though `TMailgunConfig`'s type only requires `domain`.
607
+ - `send()` converts `IMailMessage` to Mailgun's format: `to` is coerced to an array, `replyTo` becomes `h:Reply-To`, and every custom header is prefixed `h:`. Attachments map to `{ filename, data: path ?? content ?? Buffer.from('') }`.
608
+ - It catches send errors and returns `{ success: false, error }` - it never throws.
609
+ - `verify()` sends a test message to `verify@<domain>` with `o:testmode: 'yes'`, since Mailgun has no dedicated verify endpoint. It catches errors and returns `false` - it never throws.
610
+ - No `close()` - the HTTP API is stateless.
356
611
 
357
- Returned by `send()` and individual entries in the `sendBatch()` result array. Transport-level errors populate `error` with the message string.
612
+ **Custom transport:** set `provider: MailProviders.CUSTOM` and pass an object implementing `IMailTransport` as `config`. Useful for SendGrid, AWS SES, or a custom SMTP relay that the framework does not ship a helper for.
358
613
 
359
- ### IMailQueueExecutor
614
+ **Source:** [`helpers/transporters/`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/helpers/transporters)
615
+
616
+ ## Queue executor implementations
617
+
618
+ All three implement:
360
619
 
361
620
  ```typescript
362
621
  interface IMailQueueExecutor {
@@ -365,49 +624,96 @@ interface IMailQueueExecutor {
365
624
  }
366
625
  ```
367
626
 
368
- ### IMailQueueOptions
627
+ **`DirectMailExecutorHelper`** extends `BaseHelper`. It calls the processor immediately, with no queue, and returns `{ queued: false, message: 'Email sent immediately (no queue)', result }`. If `enqueueVerificationEmail()` runs before `setProcessor()`, it throws `MailExecutorErrors.PROCESSOR_NOT_SET`.
628
+
629
+ **`InternalQueueMailExecutorHelper`** extends `BaseHelper` (constructor takes `IInternalQueueMailExecutorOpts`). It wraps `SequentialQueueHelper<IQueueJobPayload>` from `@venizia/ignis-helpers`, with `autoDispatch: true`.
630
+
631
+ - Job IDs: `job_<counter>_<timestamp>`.
632
+ - `options.delay > 0` schedules the enqueue itself via `setTimeout`, tracked in an internal `delayedJobs: Map<string, NodeJS.Timeout>`.
633
+ - Retry: on a thrown error, or `{ success: false }` from the processor, it retries up to `options.attempts ?? 3` times using `calculateBackoff()`:
634
+
635
+ | `backoff` config | Delay |
636
+ |---|---|
637
+ | Not set | `1000ms` fixed |
638
+ | `type: 'fixed'` | the raw `delay` |
639
+ | `type: 'exponential'` | `delay * 2 ** (attempt - 1)` |
640
+
641
+ - `close()` clears every pending delayed/retry `setTimeout`. Without that cleanup, a live timer keeps the event loop open past shutdown.
642
+ - No persistence across restarts.
643
+
644
+ **`BullMQMailExecutorHelper`** extends `BaseHelper` (constructor takes `IBullMQMailExecutorOpts`). It wraps `BullMQHelper` (`@venizia/ignis-helpers/bullmq`) and a `RedisSingleHelper` connection.
645
+
646
+ | Mode | Queue created | Workers created | Can enqueue | Can process |
647
+ |------|----------------|------------------|-------------|-------------|
648
+ | `'queue-only'` | Yes | No (`setProcessor()` skips worker creation, logs a warning) | Yes - processor not required | No |
649
+ | `'worker-only'` | No | Yes | No (throws) | Yes |
650
+ | `'both'` | Yes | Yes | Yes - processor required | Yes |
651
+
652
+ `setProcessor(processor, opts?)` is `async`. It clears all existing workers, then creates `opts?.numberOfWorkers ?? 1` workers, each with `concurrencyPerWorker ?? 5` and `lockDuration ?? 30000`ms. In `queue-only` mode it stores the processor and returns without creating any worker.
653
+
654
+ `enqueueVerificationEmail()` throws in three cases:
655
+
656
+ | Condition | Throws |
657
+ |---|---|
658
+ | Mode is `worker-only` | `Cannot enqueue jobs in worker-only mode...` |
659
+ | Queue was never created (should not happen in a queue-enabled mode) | `Queue helper not initialized...` |
660
+ | No processor set, and `mode !== 'queue-only'` | `PROCESSOR_NOT_SET` |
661
+
662
+ Its enqueue options: `attempts: options?.attempts ?? 3`, `backoff: { type: options?.backoff?.type ?? 'exponential', delay: options?.backoff?.delay ?? 1000 }`, `removeOnComplete: true`, `removeOnFail: false`.
663
+
664
+ **Dynamic workers** - manage the worker pool without a restart:
665
+
666
+ | Method | Behavior |
667
+ |---|---|
668
+ | `addWorker(opts)` | Requires a processor set first. Takes a unique identifier, default concurrency `5`, default lock duration `30000`ms |
669
+ | `removeWorker(index)` | Closes the worker, then splices it out. Returns `false` if `index` is out of range |
670
+ | `clearWorkers()` | Closes and empties every worker. Called internally by `setProcessor()` |
671
+ | `getWorkerCount()` | Returns the current worker count |
672
+ | `getMode()` | Returns the executor's `BullMQExecutorModes` value |
673
+
674
+ `close()` tears down workers, then the queue, then the Redis connection. Every step runs even if an earlier one failed, and every failure is collected into one thrown error at the end.
369
675
 
370
676
  ```typescript
677
+ interface IBullMQMailExecutorOpts {
678
+ redis: IRedisSingleHelperOptions; // from @venizia/ignis-helpers
679
+ queue: { identifier: string; name: string };
680
+ mode: TConstValue<typeof BullMQExecutorModes>; // REQUIRED, no default
681
+ }
682
+ ```
683
+
684
+ **Source:** [`helpers/executors/`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/helpers/executors)
685
+
686
+ ## Additional interfaces
687
+
688
+ ```typescript
689
+ interface IMailSendResult {
690
+ success: boolean;
691
+ messageId?: string;
692
+ response?: any;
693
+ error?: string;
694
+ }
695
+
371
696
  interface IMailQueueOptions {
372
697
  priority?: number;
373
698
  delay?: number;
374
699
  attempts?: number;
375
- backoff?: {
376
- type: 'fixed' | 'exponential';
377
- delay: number;
378
- };
700
+ backoff?: { type: 'fixed' | 'exponential'; delay: number };
379
701
  }
380
- ```
381
-
382
- ### IMailQueueResult
383
702
 
384
- ```typescript
385
703
  interface IMailQueueResult {
386
704
  jobId?: string;
387
- queued: boolean;
705
+ queued: boolean; // false for direct execution, true for internal-queue and bullmq
388
706
  message: string;
389
- result?: IMailProcessorResult;
707
+ result?: IMailProcessorResult; // populated only for direct execution (synchronous processor)
390
708
  }
391
- ```
392
-
393
- Returned by `enqueueVerificationEmail()`. The `queued` field is `false` for direct execution, `true` for internal queue and BullMQ. The `result` field is populated only for direct execution (since the processor runs synchronously).
394
709
 
395
- ### IMailProcessorResult
396
-
397
- ```typescript
398
710
  interface IMailProcessorResult {
399
711
  success: boolean;
400
712
  message: string;
401
713
  expiresInMinutes: number;
402
714
  nextResendAt?: string;
403
715
  }
404
- ```
405
716
 
406
- The return type of the processor function registered via `setProcessor()`.
407
-
408
- ### IVerificationData
409
-
410
- ```typescript
411
717
  interface IVerificationData {
412
718
  verificationCode: string;
413
719
  codeGeneratedAt: string;
@@ -418,88 +724,50 @@ interface IVerificationData {
418
724
  tokenExpiresAt: string;
419
725
  lastCodeSentAt: string;
420
726
  }
421
- ```
422
-
423
- ### IVerificationGenerationOptions
424
727
 
425
- ```typescript
426
728
  interface IVerificationGenerationOptions {
427
- codeLength: number;
729
+ codeLength: number; // all four fields required, no defaults
428
730
  tokenBytes: number;
429
731
  codeExpiryMinutes: number;
430
732
  tokenExpiryHours: number;
431
733
  }
432
- ```
433
-
434
- All fields are required. Passed to `DefaultVerificationDataGenerator.generateVerificationData()`.
435
-
436
- ### IBullMQMailExecutorOpts
437
734
 
438
- ```typescript
439
- interface IBullMQMailExecutorOpts {
440
- redis: IRedisHelperOptions;
441
- queue: { identifier: string; name: string };
442
- mode: TConstValue<typeof BullMQExecutorModes>; // REQUIRED
735
+ interface IQueueJobPayload {
736
+ id: string;
737
+ email: string;
738
+ options?: IMailQueueOptions;
739
+ attempts: number;
740
+ scheduledAt: number;
443
741
  }
444
- ```
445
-
446
- The `mode` field is **required** -- there is no default value. This is the config object for `IMailQueueExecutorConfig.bullmq`.
447
-
448
- ### BullMQ Dynamic Worker Management Methods
449
-
450
- Dynamic worker management methods (on `BullMQMailExecutorHelper`, not on the interface):
451
-
452
- - **`addWorker(opts)`** -- Adds a new BullMQ worker with configurable concurrency (default 5) and lock duration (default 30000ms). Requires `setProcessor()` to have been called first. Each worker gets a unique identifier
453
- - **`removeWorker(index)`** -- Removes a worker by its array index. Calls `worker.close()` before removal. Returns `false` if the index is out of range
454
- - **`clearWorkers()`** -- Closes all workers and empties the worker array. Called internally by `setProcessor()` before creating new workers
455
- - **`getWorkerCount()`** -- Returns the current number of active workers
456
- - **`getMode()`** -- Returns the current executor mode
457
-
458
- **Extended `setProcessor()` signature (BullMQ only):**
459
742
 
460
- Unlike the interface's synchronous `setProcessor()`, the BullMQ executor's version is `async` and accepts an optional second argument:
461
-
462
- ```typescript
463
- async setProcessor(
464
- processor: (email: string) => Promise<IMailProcessorResult>,
465
- opts?: {
466
- numberOfWorkers?: number; // default: 1
467
- concurrencyPerWorker?: number; // default: 5
468
- lockDuration?: number; // default: 30000 (ms)
469
- },
470
- ): Promise<void>
743
+ interface IInternalQueueMailExecutorOpts {
744
+ identifier: string;
745
+ }
471
746
  ```
472
747
 
473
- It clears all existing workers before creating new ones. In `queue-only` mode, it stores the processor but skips worker creation entirely (logs a warning).
474
-
475
- ## Utility Functions
748
+ **Source:** [`common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/common/types.ts)
476
749
 
477
- ### Type Utilities
750
+ ## Utility functions
478
751
 
479
752
  ```typescript
480
- // Check if a value implements IMailTransport
753
+ // type.utility.ts
481
754
  function isMailTransport(value: AnyType): value is IMailTransport;
482
-
483
- // Check if a value is valid TMailOptions
484
755
  function isValidMailOptions(options: AnyType): options is TMailOptions;
485
- ```
486
-
487
- `isMailTransport()` checks for `send` and `verify` as functions, and `close` as either a function or undefined.
488
-
489
- `isValidMailOptions()` checks for a string `provider` field and a truthy `config` field.
490
756
 
491
- ### Verification Utilities
757
+ // verification.utility.ts
758
+ function getExpiryTime(minutes: number): Date; // Date `minutes` minutes from now
759
+ function getExpiryTimeInHours(hours: number): Date; // Date `hours` hours from now
760
+ ```
492
761
 
493
- ```typescript
494
- // Get a Date object `minutes` minutes in the future
495
- function getExpiryTime(minutes: number): Date;
762
+ - `isMailTransport()` checks that `send` and `verify` are functions, and that `close` is either a function or `undefined`.
763
+ - `isValidMailOptions()` checks that `provider` is a string and `config` is truthy. It does not validate the shape of `config` against the specific provider.
496
764
 
497
- // Get a Date object `hours` hours in the future
498
- function getExpiryTimeInHours(hours: number): Date;
499
- ```
765
+ **Source:** [`utilities/`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/utilities)
500
766
 
501
- ## See Also
767
+ ## See also
502
768
 
503
- - [Setup & Configuration](./) -- Quick reference, setup steps, configuration options, and binding keys
504
- - [Usage & Examples](./usage) -- Sending emails, templates, queue executors, and verification
505
- - [Error Reference](./errors) -- Error codes and troubleshooting
769
+ - [Overview](./) -- quick start, imports, common configuration tasks
770
+ - [Usage & Examples](./usage) -- sending emails, templates, queue executors, verification generators
771
+ - [Error Reference](./errors) -- error codes and troubleshooting
772
+ - [Queue Helper](/extensions/helpers/queue/) -- `SequentialQueueHelper` and `BullMQHelper`
773
+ - [Redis Helper](/extensions/helpers/redis/) -- `RedisSingleHelper`, `IRedisSingleHelperOptions`