@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.
Files changed (145) hide show
  1. package/content/best-practices/architectural-patterns.md +3 -3
  2. package/content/best-practices/code-style-standards/naming-conventions.md +1 -1
  3. package/content/best-practices/contribution-workflow.md +2 -2
  4. package/content/best-practices/error-handling.md +94 -89
  5. package/content/best-practices/security-guidelines.md +5 -5
  6. package/content/extensions/components/api-reference.md +22 -21
  7. package/content/extensions/components/authentication/api.md +64 -28
  8. package/content/extensions/components/authentication/errors.md +19 -5
  9. package/content/extensions/components/authentication/index.md +19 -18
  10. package/content/extensions/components/authentication/usage.md +55 -30
  11. package/content/extensions/components/authorization/api.md +351 -84
  12. package/content/extensions/components/authorization/errors.md +51 -17
  13. package/content/extensions/components/authorization/getting-started.md +227 -0
  14. package/content/extensions/components/authorization/index.md +24 -16
  15. package/content/extensions/components/authorization/usage.md +45 -21
  16. package/content/extensions/components/health-check.md +15 -9
  17. package/content/extensions/components/index.md +24 -90
  18. package/content/extensions/components/mail/api.md +105 -54
  19. package/content/extensions/components/mail/errors.md +12 -10
  20. package/content/extensions/components/mail/index.md +32 -13
  21. package/content/extensions/components/mail/usage.md +26 -18
  22. package/content/extensions/components/request-tracker.md +18 -14
  23. package/content/extensions/components/socket-io/api.md +377 -882
  24. package/content/extensions/components/socket-io/errors.md +49 -51
  25. package/content/extensions/components/socket-io/index.md +72 -88
  26. package/content/extensions/components/socket-io/usage.md +107 -117
  27. package/content/extensions/components/static-asset/api.md +83 -31
  28. package/content/extensions/components/static-asset/errors.md +18 -7
  29. package/content/extensions/components/static-asset/index.md +18 -11
  30. package/content/extensions/components/static-asset/usage.md +11 -6
  31. package/content/extensions/components/template/index.md +3 -3
  32. package/content/extensions/components/websocket/api.md +58 -27
  33. package/content/extensions/components/websocket/errors.md +3 -3
  34. package/content/extensions/components/websocket/index.md +8 -7
  35. package/content/extensions/components/websocket/usage.md +21 -8
  36. package/content/extensions/helpers/cron/index.md +8 -7
  37. package/content/extensions/helpers/crypto/index.md +16 -8
  38. package/content/extensions/helpers/crypto/reference.md +96 -24
  39. package/content/extensions/helpers/env/index.md +14 -10
  40. package/content/extensions/helpers/error/index.md +99 -23
  41. package/content/extensions/helpers/index.md +61 -47
  42. package/content/extensions/helpers/inversion/index.md +23 -6
  43. package/content/extensions/helpers/inversion/reference.md +30 -22
  44. package/content/extensions/helpers/kafka/admin.md +3 -2
  45. package/content/extensions/helpers/kafka/compile-binary.md +69 -44
  46. package/content/extensions/helpers/kafka/consumer.md +24 -21
  47. package/content/extensions/helpers/kafka/examples.md +22 -234
  48. package/content/extensions/helpers/kafka/index.md +30 -62
  49. package/content/extensions/helpers/kafka/producer.md +31 -26
  50. package/content/extensions/helpers/kafka/schema-registry.md +19 -14
  51. package/content/extensions/helpers/logger/hf-logger.md +49 -22
  52. package/content/extensions/helpers/logger/index.md +37 -12
  53. package/content/extensions/helpers/logger/pino.md +31 -11
  54. package/content/extensions/helpers/logger/reference.md +243 -52
  55. package/content/extensions/helpers/network/api.md +65 -30
  56. package/content/extensions/helpers/network/index.md +32 -11
  57. package/content/extensions/helpers/queue/index.md +17 -6
  58. package/content/extensions/helpers/queue/reference.md +52 -25
  59. package/content/extensions/helpers/redis/index.md +29 -11
  60. package/content/extensions/helpers/redis/reference.md +85 -28
  61. package/content/extensions/helpers/secrets/index.md +82 -12
  62. package/content/extensions/helpers/socket-io/api.md +40 -21
  63. package/content/extensions/helpers/socket-io/index.md +26 -10
  64. package/content/extensions/helpers/storage/api.md +42 -24
  65. package/content/extensions/helpers/storage/index.md +10 -7
  66. package/content/extensions/helpers/types/index.md +20 -7
  67. package/content/extensions/helpers/types/reference.md +53 -14
  68. package/content/extensions/helpers/uid/index.md +166 -10
  69. package/content/extensions/helpers/websocket/api.md +52 -13
  70. package/content/extensions/helpers/websocket/index.md +7 -4
  71. package/content/extensions/helpers/worker-thread/index.md +9 -5
  72. package/content/extensions/helpers/worker-thread/reference.md +20 -20
  73. package/content/extensions/index.md +38 -39
  74. package/content/extensions/src-details/mcp-server.md +96 -548
  75. package/content/guides/core-concepts/persistent/datasources.md +2 -2
  76. package/content/guides/core-concepts/persistent/index.md +5 -1
  77. package/content/guides/core-concepts/persistent/pglite.md +218 -0
  78. package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
  79. package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
  80. package/content/guides/core-concepts/persistent/search-typesense.md +26 -14
  81. package/content/guides/core-concepts/persistent/sqlite.md +296 -0
  82. package/content/guides/core-concepts/persistent/transactions.md +12 -11
  83. package/content/guides/get-started/5-minute-quickstart.md +59 -206
  84. package/content/guides/get-started/philosophy.md +135 -670
  85. package/content/guides/get-started/setup.md +53 -74
  86. package/content/guides/migrations/redis-helpers-migration.md +4 -3
  87. package/content/guides/migrations/scoped-rbac-migration.md +5 -5
  88. package/content/guides/migrations/unified-connectors-migration.md +7 -6
  89. package/content/guides/tutorials/realtime-chat.md +1 -1
  90. package/content/references/base/application.md +63 -21
  91. package/content/references/base/components.md +3 -3
  92. package/content/references/base/connectors.md +14 -14
  93. package/content/references/base/controllers.md +12 -12
  94. package/content/references/base/datasources-reference.md +32 -31
  95. package/content/references/base/datasources.md +6 -6
  96. package/content/references/base/dependency-injection.md +12 -11
  97. package/content/references/base/filter-system/application-usage.md +54 -30
  98. package/content/references/base/filter-system/array-operators.md +24 -46
  99. package/content/references/base/filter-system/comparison-operators.md +47 -67
  100. package/content/references/base/filter-system/default-filter.md +59 -53
  101. package/content/references/base/filter-system/fields-order-pagination.md +92 -146
  102. package/content/references/base/filter-system/index.md +25 -13
  103. package/content/references/base/filter-system/json-filtering.md +45 -184
  104. package/content/references/base/filter-system/list-operators.md +23 -53
  105. package/content/references/base/filter-system/logical-operators.md +63 -121
  106. package/content/references/base/filter-system/null-operators.md +34 -104
  107. package/content/references/base/filter-system/pattern-matching.md +40 -55
  108. package/content/references/base/filter-system/quick-reference.md +86 -198
  109. package/content/references/base/filter-system/range-operators.md +18 -46
  110. package/content/references/base/filter-system/tips.md +6 -6
  111. package/content/references/base/filter-system/use-cases.md +33 -15
  112. package/content/references/base/grpc-controllers.md +53 -17
  113. package/content/references/base/index.md +5 -3
  114. package/content/references/base/middlewares.md +11 -10
  115. package/content/references/base/models-reference.md +17 -17
  116. package/content/references/base/models.md +4 -3
  117. package/content/references/base/providers.md +8 -8
  118. package/content/references/base/repositories/advanced.md +224 -326
  119. package/content/references/base/repositories/index.md +19 -6
  120. package/content/references/base/repositories/mixins.md +5 -5
  121. package/content/references/base/repositories/relations.md +160 -293
  122. package/content/references/base/repositories/soft-deletable.md +16 -6
  123. package/content/references/base/secrets.md +17 -13
  124. package/content/references/base/services.md +6 -4
  125. package/content/references/configuration/environment-variables.md +31 -23
  126. package/content/references/configuration/index.md +6 -4
  127. package/content/references/index.md +1 -1
  128. package/content/references/utilities/duration.md +85 -0
  129. package/content/references/utilities/index.md +5 -1
  130. package/content/references/utilities/jsx-reference.md +11 -11
  131. package/content/references/utilities/jsx.md +2 -2
  132. package/content/references/utilities/module.md +78 -25
  133. package/content/references/utilities/request.md +2 -1
  134. package/content/references/utilities/retry.md +139 -0
  135. package/content/references/utilities/schema.md +2 -2
  136. package/content/references/utilities/statuses-reference.md +4 -4
  137. package/content/references/utilities/statuses.md +5 -5
  138. package/dist/mcp-server/index.js +0 -0
  139. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  140. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
  141. package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
  142. package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
  143. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
  144. package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
  145. package/package.json +17 -16
@@ -10,22 +10,22 @@ Every binding key, configuration variant, interface, and internal mechanism of `
10
10
 
11
11
  **Files:**
12
12
 
13
- - [`packages/core/src/components/mail/component.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/component.ts)
14
- - [`packages/core/src/components/mail/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/common/types.ts)
15
- - [`packages/core/src/components/mail/common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/common/constants.ts)
16
- - [`packages/core/src/components/mail/common/keys.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/common/keys.ts)
17
- - [`packages/core/src/components/mail/services/mail.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/services/mail.service.ts)
18
- - [`packages/core/src/components/mail/services/template.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/services/template.service.ts)
19
- - [`packages/core/src/components/mail/services/generator.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/services/generator.service.ts)
20
- - [`packages/core/src/components/mail/providers/mail-transporter.provider.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/providers/mail-transporter.provider.ts)
21
- - [`packages/core/src/components/mail/providers/mail-queue-executor.provider.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/providers/mail-queue-executor.provider.ts)
22
- - [`packages/core/src/components/mail/helpers/transporters/nodemail-transporter.helper.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/helpers/transporters/nodemail-transporter.helper.ts)
23
- - [`packages/core/src/components/mail/helpers/transporters/mailgun-transporter.helper.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/helpers/transporters/mailgun-transporter.helper.ts)
24
- - [`packages/core/src/components/mail/helpers/executors/direct-executor.helper.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/helpers/executors/direct-executor.helper.ts)
25
- - [`packages/core/src/components/mail/helpers/executors/internal-queue-executor.helper.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/helpers/executors/internal-queue-executor.helper.ts)
26
- - [`packages/core/src/components/mail/helpers/executors/bull-mq-executor.helper.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/helpers/executors/bull-mq-executor.helper.ts)
27
- - [`packages/core/src/components/mail/utilities/type.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/utilities/type.utility.ts)
28
- - [`packages/core/src/components/mail/utilities/verification.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/utilities/verification.utility.ts)
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
29
 
30
30
  ## Quick reference
31
31
 
@@ -141,9 +141,9 @@ import type {
141
141
  └─────────────────────────────────────────────────┘
142
142
  ```
143
143
 
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.
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.
145
145
 
146
- **Source:** [`component.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/component.ts)
146
+ **Source:** [`component.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/component.ts)
147
147
 
148
148
  ## Binding keys
149
149
 
@@ -164,7 +164,7 @@ import type {
164
164
  > [!IMPORTANT]
165
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.
166
166
 
167
- **Source:** [`common/keys.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/common/keys.ts)
167
+ **Source:** [`common/keys.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/common/keys.ts)
168
168
 
169
169
  ## Configuration
170
170
 
@@ -181,11 +181,13 @@ interface IBaseMailOptions {
181
181
  interface INodemailerMailOptions extends IBaseMailOptions {
182
182
  provider: 'nodemailer';
183
183
  config: TNodemailerConfig; // SMTPTransport | SMTPTransport.Options | string
184
+ module?: TNodemailerModule; // the peer itself; see "Peer dependency loading"
184
185
  }
185
186
 
186
187
  interface IMailgunMailOptions extends IBaseMailOptions {
187
188
  provider: 'mailgun';
188
189
  config: TMailgunConfig; // AnyType & { domain: string } -- also requires username, key at runtime
190
+ module?: TMailgunModule; // the peer itself; see "Peer dependency loading"
189
191
  }
190
192
 
191
193
  interface ICustomMailOptions extends IBaseMailOptions {
@@ -243,7 +245,7 @@ type TMailOptions =
243
245
  }
244
246
  ```
245
247
 
246
- `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`:
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`:
247
249
 
248
250
  ```
249
251
  APP_ENV_MAIL_HOST=smtp.gmail.com
@@ -275,7 +277,7 @@ APP_ENV_MAIL_REFRESH_TOKEN=your-oauth2-refresh-token
275
277
  ```
276
278
 
277
279
  > [!IMPORTANT]
278
- > `MailgunTransportHelper` validates `username`, `key`, and `domain` on construction and throws `Invalid Mailgun configuration | Missing required keys: <keys>` if any is missing -- this happens even though `TMailgunConfig`'s only *typed* requirement is `domain`. `username` and `key` are checked at runtime, not by the type.
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.
279
281
 
280
282
  **Custom transport:**
281
283
 
@@ -298,7 +300,7 @@ APP_ENV_MAIL_REFRESH_TOKEN=your-oauth2-refresh-token
298
300
  ```
299
301
 
300
302
  > [!WARNING]
301
- > `IGenericMailOptions` falls through to the `default` case in `MailTransportProvider` and throws `Unsupported mail provider: <provider>` -- it exists only for you to bind a **custom** `MailTransportProvider` that recognizes the provider string. It is not handled by the framework's built-in provider.
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.
302
304
 
303
305
  ### Queue executor options (`IMailQueueExecutorConfig`)
304
306
 
@@ -334,10 +336,15 @@ interface IMailQueueExecutorConfig {
334
336
  }
335
337
  ```
336
338
 
337
- > [!NOTE]
338
- > Choosing an executor: **`direct`** for development or low-volume apps (no queueing overhead); **`internal-queue`** for single-instance apps with moderate volume (in-memory, with retry); **`bullmq`** for distributed or high-volume systems (Redis-backed, configurable concurrency/priority/backoff).
339
+ **Choosing an executor:**
339
340
 
340
- **Source:** [`common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/common/types.ts)
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 |
346
+
347
+ **Source:** [`common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/common/types.ts)
341
348
 
342
349
  ### Constants
343
350
 
@@ -375,7 +382,7 @@ Built through `MessageCode.build()`, so every value is lower-case (`ApplicationE
375
382
  | `MailErrorCodes.BATCH_SEND_FAILED` | `'core.mail.batch_send_failed'` | Batch email operation failed |
376
383
  | `MailErrorCodes.TEMPLATE_NOT_FOUND` | `'core.mail.template_not_found'` | Template name not found in registry |
377
384
 
378
- **Source:** [`common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/common/constants.ts)
385
+ **Source:** [`common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/common/constants.ts)
379
386
 
380
387
  ## `IMailService` interface
381
388
 
@@ -405,7 +412,8 @@ interface IMailService {
405
412
  2. Merges `message.from` with `getDefaultFrom()` if unset.
406
413
  3. Delegates to `transport.send()`.
407
414
  4. Returns `{ success, messageId, error? }`.
408
- 5. If the transport throws and the error is **not** already an `ApplicationError`, it is caught and re-thrown as `MailErrorCodes.SEND_FAILED` (500). An `ApplicationError` thrown earlier in the same `try` block (e.g. from `validateMessage()`) is re-thrown as-is, unchanged.
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.
409
417
 
410
418
  **`sendBatch(messages, options?)`**
411
419
 
@@ -435,7 +443,7 @@ Delegates to `transport.verify()`. If the transport throws, wraps it as `MailErr
435
443
  - Else if `options.fromName` is unset, returns `options.from` as-is.
436
444
  - Else returns `"${fromName}" <${from}>`.
437
445
 
438
- **Source:** [`services/mail.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/services/mail.service.ts)
446
+ **Source:** [`services/mail.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/services/mail.service.ts)
439
447
 
440
448
  ## `IMailMessage` interface
441
449
 
@@ -483,7 +491,7 @@ interface IMailAttachment {
483
491
  - Inline image: `{ filename: 'logo.png', path: '...', cid: 'logo' }`
484
492
  - Mailgun maps each attachment to `{ filename, data: att.path ?? att.content ?? Buffer.from('') }`.
485
493
 
486
- **Source:** [`common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/common/types.ts)
494
+ **Source:** [`common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/common/types.ts)
487
495
 
488
496
  ## `IMailTemplateEngine` interface
489
497
 
@@ -511,7 +519,7 @@ interface IMailTemplateEngine {
511
519
  }
512
520
  ```
513
521
 
514
- `TemplateEngineService` also exposes `clearTemplates(): void` -- it resets the registry but is **not** part of the `IMailTemplateEngine` interface, so it is only reachable when you hold the concrete class, not the interface type.
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.
515
523
 
516
524
  | Method | Behavior |
517
525
  |--------|----------|
@@ -535,7 +543,7 @@ interface ITemplate {
535
543
 
536
544
  The `render` field supports a custom per-template render function, though the built-in `TemplateEngineService` always uses `content` + `renderSimpleTemplate()` instead.
537
545
 
538
- **Source:** [`services/template.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/services/template.service.ts)
546
+ **Source:** [`services/template.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/services/template.service.ts)
539
547
 
540
548
  ## Transport layer
541
549
 
@@ -568,25 +576,42 @@ interface IMailTransport {
568
576
 
569
577
  ### Peer dependency loading
570
578
 
571
- Neither transport calls a shared `validateModule()` helper. `configure()` calls `require('nodemailer')` / `require('mailgun.js')` directly -- if the package is not installed, Node's own `Cannot find module '<name>'` error propagates uncaught from the constructor, not a framework-formatted message.
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`.
580
+
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.
582
+
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:
584
+
585
+ ```typescript
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
+ });
593
+ ```
594
+
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.
572
596
 
573
597
  **Nodemailer (`NodemailerTransportHelper`, extends `BaseHelper`):**
574
598
 
575
599
  - `configure()` builds the transporter via `nodemailer.createTransport(config)`.
576
- - `send()` maps `IMailMessage` fields to Nodemailer's mail options, joining array recipients with `', '`. Catches transport errors and returns `{ success: false, error }` -- never throws.
577
- - `verify()` delegates to `transporter.verify()` (SMTP handshake). Catches errors and returns `false` -- never throws.
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.
578
602
  - `close()` calls `transporter.close()`.
579
603
 
580
604
  **Mailgun (`MailgunTransportHelper`, extends `BaseHelper`):**
581
605
 
582
- - `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`.
583
- - `send()` converts `IMailMessage` to Mailgun's format: `to` is coerced to an array, `replyTo` becomes `h:Reply-To`, every custom header is prefixed `h:`. Attachments map to `{ filename, data: path ?? content ?? Buffer.from('') }`. Catches send errors and returns `{ success: false, error }` -- never throws.
584
- - `verify()` sends a test message to `verify@<domain>` with `o:testmode: 'yes'` (Mailgun has no dedicated verify endpoint). Catches errors and returns `false` -- never throws.
585
- - No `close()` -- the HTTP API is stateless.
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.
586
611
 
587
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.
588
613
 
589
- **Source:** [`helpers/transporters/`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/helpers/transporters)
614
+ **Source:** [`helpers/transporters/`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/helpers/transporters)
590
615
 
591
616
  ## Queue executor implementations
592
617
 
@@ -599,28 +624,54 @@ interface IMailQueueExecutor {
599
624
  }
600
625
  ```
601
626
 
602
- **`DirectMailExecutorHelper`** (extends `BaseHelper`) -- calls the processor immediately, no queue. Returns `{ queued: false, message: 'Email sent immediately (no queue)', result }`. Throws `MailExecutorErrors.PROCESSOR_NOT_SET` if `enqueueVerificationEmail()` runs before `setProcessor()`.
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`.
603
628
 
604
- **`InternalQueueMailExecutorHelper`** (extends `BaseHelper`, constructor takes `IInternalQueueMailExecutorOpts`) -- wraps `SequentialQueueHelper<IQueueJobPayload>` from `@venizia/ignis-helpers`, `autoDispatch: true`.
629
+ **`InternalQueueMailExecutorHelper`** extends `BaseHelper` (constructor takes `IInternalQueueMailExecutorOpts`). It wraps `SequentialQueueHelper<IQueueJobPayload>` from `@venizia/ignis-helpers`, with `autoDispatch: true`.
605
630
 
606
631
  - Job IDs: `job_<counter>_<timestamp>`.
607
632
  - `options.delay > 0` schedules the enqueue itself via `setTimeout`, tracked in an internal `delayedJobs: Map<string, NodeJS.Timeout>`.
608
- - Retry: on a thrown error or `{ success: false }` from the processor, retries up to `options.attempts ?? 3` times. `calculateBackoff()`: no `backoff` config -> `1000ms` fixed; `type: 'fixed'` -> the raw `delay`; `type: 'exponential'` -> `delay * 2 ** (attempt - 1)`.
609
- - `close()` clears every pending delayed/retry `setTimeout` -- without it, a live timer keeps the event loop open past shutdown.
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.
610
642
  - No persistence across restarts.
611
643
 
612
- **`BullMQMailExecutorHelper`** (extends `BaseHelper`, constructor takes `IBullMQMailExecutorOpts`) -- wraps `BullMQHelper` (`@venizia/ignis-helpers/bullmq`) and a `RedisSingleHelper` connection.
644
+ **`BullMQMailExecutorHelper`** extends `BaseHelper` (constructor takes `IBullMQMailExecutorOpts`). It wraps `BullMQHelper` (`@venizia/ignis-helpers/bullmq`) and a `RedisSingleHelper` connection.
613
645
 
614
646
  | Mode | Queue created | Workers created | Can enqueue | Can process |
615
647
  |------|----------------|------------------|-------------|-------------|
616
- | `'queue-only'` | Yes | No (`setProcessor()` skips worker creation, logs a warning) | Yes -- processor not required | No |
648
+ | `'queue-only'` | Yes | No (`setProcessor()` skips worker creation, logs a warning) | Yes - processor not required | No |
617
649
  | `'worker-only'` | No | Yes | No (throws) | Yes |
618
- | `'both'` | Yes | Yes | Yes -- processor required | 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 |
619
673
 
620
- - `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.
621
- - `enqueueVerificationEmail()` throws `Cannot enqueue jobs in worker-only mode...` in `worker-only` mode; throws `Queue helper not initialized...` if the queue was never created (should not happen in a queue-enabled mode); throws `PROCESSOR_NOT_SET` only when `mode !== 'queue-only'` and no processor is set. Enqueue options: `attempts: options?.attempts ?? 3`, `backoff: { type: options?.backoff?.type ?? 'exponential', delay: options?.backoff?.delay ?? 1000 }`, `removeOnComplete: true`, `removeOnFail: false`.
622
- - **Dynamic workers:** `addWorker(opts)` (requires a processor, unique identifier, default concurrency `5`, default lock duration `30000`ms), `removeWorker(index)` (closes then splices, returns `false` if out of range), `clearWorkers()` (closes and empties, called internally by `setProcessor()`), `getWorkerCount()`, `getMode()`.
623
- - `close()` tears down workers, then the queue, then the Redis connection -- every step runs even if an earlier one failed, and all failures are collected into one thrown error at the end.
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.
624
675
 
625
676
  ```typescript
626
677
  interface IBullMQMailExecutorOpts {
@@ -630,7 +681,7 @@ interface IBullMQMailExecutorOpts {
630
681
  }
631
682
  ```
632
683
 
633
- **Source:** [`helpers/executors/`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/helpers/executors)
684
+ **Source:** [`helpers/executors/`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/helpers/executors)
634
685
 
635
686
  ## Additional interfaces
636
687
 
@@ -694,7 +745,7 @@ interface IInternalQueueMailExecutorOpts {
694
745
  }
695
746
  ```
696
747
 
697
- **Source:** [`common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/common/types.ts)
748
+ **Source:** [`common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/common/types.ts)
698
749
 
699
750
  ## Utility functions
700
751
 
@@ -709,9 +760,9 @@ function getExpiryTimeInHours(hours: number): Date; // Date `hours` hours fr
709
760
  ```
710
761
 
711
762
  - `isMailTransport()` checks that `send` and `verify` are functions, and that `close` is either a function or `undefined`.
712
- - `isValidMailOptions()` checks that `provider` is a string and `config` is truthy -- it does not validate the shape of `config` against the specific provider.
763
+ - `isValidMailOptions()` checks that `provider` is a string and `config` is truthy. It does not validate the shape of `config` against the specific provider.
713
764
 
714
- **Source:** [`utilities/`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/utilities)
765
+ **Source:** [`utilities/`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/utilities)
715
766
 
716
767
  ## See also
717
768
 
@@ -1,10 +1,12 @@
1
- # Mail -- Error Reference & Troubleshooting
1
+ # Mail - Error Reference & Troubleshooting
2
2
 
3
3
  > Complete error code reference and troubleshooting guide for the Mail component.
4
4
 
5
5
  ## Error reference
6
6
 
7
- All errors are created via `getError()`. Only errors that pass `statusCode`/`messageCode` carry an `ApplicationError` identity with those fields -- the rows marked `--` throw a plain error with just a `message`. Read an error's code at `error.normalized.code`; there is no flat `error.messageCode`.
7
+ All errors are created via `getError()`. Only errors that carry a status and a code reach you as an `ApplicationError` with those fields; the rows marked `--` throw a plain error with only a `message`. Read an error's code at `error.normalized.code` - there is no flat `error.messageCode`.
8
+
9
+ The 4xx rows below come from the `MailErrors` catalog, which holds the status and the code together so two throw sites cannot disagree. The 5xx rows stay codeless by design - a delivery failure is not something a caller can act on.
8
10
 
9
11
  ### `MailService` errors
10
12
 
@@ -19,7 +21,7 @@ All errors are created via `getError()`. Only errors that pass `statusCode`/`mes
19
21
  | Transport throws during `verify()` | 500 | `core.mail.verification_failed` | `Mail transport verification failed: <error>` |
20
22
 
21
23
  > [!NOTE]
22
- > "Transport throws during `send()`/`verify()`" only fires for a **custom** transport. The built-in `NodemailerTransportHelper` and `MailgunTransportHelper` never throw from `send()` or `verify()` -- they catch internally and return `{ success: false, error }` (or `false` for `verify()`). A 400 validation error raised by `validateMessage()` is re-thrown unchanged, not wrapped as `SEND_FAILED`.
24
+ > "Transport throws during `send()`/`verify()`" only fires for a **custom** transport. The built-in `NodemailerTransportHelper` and `MailgunTransportHelper` never throw from `send()` or `verify()`. They catch internally and return `{ success: false, error }` (or `false` for `verify()`). A 400 validation error raised by `validateMessage()` is re-thrown unchanged, not wrapped as `SEND_FAILED`.
23
25
 
24
26
  ### `MailComponent` errors
25
27
 
@@ -43,7 +45,7 @@ All errors are created via `getError()`. Only errors that pass `statusCode`/`mes
43
45
  |-----------|--------|-----------|---------|
44
46
  | `config` is missing `username`, `key`, or `domain` | 500 | `core.mail.invalid_configuration` | `Invalid Mailgun configuration \| Missing required keys: <keys>` |
45
47
 
46
- This check runs on `configure()`, one layer deeper than `MailTransportProvider`'s type guard -- a `config` object that passes the provider's guard (has *a* `config` property) can still fail this one if it is missing the specific keys Mailgun's client needs.
48
+ This check runs on `configure()`, one layer deeper than `MailTransportProvider`'s type guard. A `config` object that passes the provider's guard - it merely has *a* `config` property - can still fail this one if it is missing the specific keys Mailgun's client needs.
47
49
 
48
50
  ### `MailQueueExecutorProvider` errors
49
51
 
@@ -99,7 +101,7 @@ config: { username: 'api', key: process.env.MAILGUN_API_KEY, domain: 'mg.example
99
101
 
100
102
  ### `core.mail.template_not_found` from `sendTemplate()`
101
103
 
102
- - **Cause.** The template name was never registered via `templateEngine.registerTemplate()` -- often because a database-backed sync runs after the first `sendTemplate()` call, not before it.
104
+ - **Cause.** The template name was never registered via `templateEngine.registerTemplate()`. This often happens because a database-backed sync runs after the first `sendTemplate()` call, not before it.
103
105
  - **Fix.** Register the template first:
104
106
 
105
107
  ```typescript
@@ -111,7 +113,7 @@ this.templateEngine.registerTemplate({
111
113
 
112
114
  ### Emails silently fail with `success: false`
113
115
 
114
- - **Cause.** The transport connection is misconfigured (wrong credentials, blocked port, expired OAuth2 token). `MailService.send()` never throws for a built-in transport's connection failure -- it returns `{ success: false, error }`.
116
+ - **Cause.** The transport connection is misconfigured: wrong credentials, a blocked port, or an expired OAuth2 token. `MailService.send()` never throws for a built-in transport's connection failure - it returns `{ success: false, error }`.
115
117
  - **Fix.** Check `result.error` for the underlying transport error, and verify the connection at startup:
116
118
 
117
119
  ```typescript
@@ -139,7 +141,7 @@ if (!isConnected) {
139
141
 
140
142
  ### Template placeholders show up literally in the output
141
143
 
142
- - **Cause.** The `data` object passed to `render()`/`sendTemplate()` is missing a <code v-pre>{{key}}</code> the template uses. Without `requireValidate: true`, the engine preserves the original placeholder text -- it does **not** replace a missing value with an empty string.
144
+ - **Cause.** The `data` object passed to `render()`/`sendTemplate()` is missing a <code v-pre>{{key}}</code> the template uses. Without `requireValidate: true`, the engine preserves the original placeholder text. It does **not** replace a missing value with an empty string.
143
145
  - **Fix.** Check what is missing before rendering, or fail loudly instead:
144
146
 
145
147
  ```typescript
@@ -159,7 +161,7 @@ const html = this.templateEngine.render({
159
161
 
160
162
  ### "Processor not set. Call setProcessor() first."
161
163
 
162
- - **Cause.** `enqueueVerificationEmail()` was called before `setProcessor()`. Direct and Internal Queue always require this; BullMQ requires it too, except in `'queue-only'` mode, where enqueueing does not need a processor.
164
+ - **Cause.** `enqueueVerificationEmail()` was called before `setProcessor()`. Direct and Internal Queue always require a processor first. BullMQ requires one too, except in `'queue-only'` mode, where enqueueing does not need a processor.
163
165
  - **Fix.** Register a processor before enqueuing:
164
166
 
165
167
  ```typescript
@@ -176,10 +178,10 @@ executor.setProcessor(async (email: string) => {
176
178
 
177
179
  ### Startup logs and credentials
178
180
 
179
- `MailComponent.createAndBindInstances()` logs only `mailOptions.provider` and `queueExecutorConfig.type` at `info` level -- by design, it never logs the full config object, so SMTP passwords, OAuth2 secrets, Mailgun API keys, and Redis passwords are never written to the log by the component itself.
181
+ `MailComponent.createAndBindInstances()` logs only `mailOptions.provider` and `queueExecutorConfig.type`, at `info` level. It never logs the full config object, by design. That keeps SMTP passwords, OAuth2 secrets, Mailgun API keys, and Redis passwords out of the log - at least through the component itself.
180
182
 
181
183
  > [!WARNING]
182
- > That guarantee is scoped to `MailComponent`'s own logging. If your wrapper component (or any other code) logs the `TMailOptions`/`IMailQueueExecutorConfig` object directly -- for example `logger.info('%j', mailOptions)` while debugging -- you reintroduce the leak yourself. Log individual safe fields instead of the whole object.
184
+ > That guarantee is scoped to `MailComponent`'s own logging. If your wrapper component or any other code logs the `TMailOptions`/`IMailQueueExecutorConfig` object directly - for example, `logger.info('%j', mailOptions)` while debugging - you reintroduce the leak yourself. Log individual safe fields instead of the whole object.
183
185
 
184
186
  ## See also
185
187
 
@@ -6,7 +6,7 @@ difficulty: intermediate
6
6
 
7
7
  # Mail Component
8
8
 
9
- `MailComponent` wires a pluggable email transport (Nodemailer, Mailgun, or your own) into `MailService`, adding template rendering, batch sending, and an independent queue executor for verification-code/token flows.
9
+ `MailComponent` wires a pluggable email transport (Nodemailer, Mailgun, or your own) into `MailService`. It adds template rendering, batch sending, and an independent queue executor for verification-code/token flows.
10
10
 
11
11
  ## In one example
12
12
 
@@ -54,12 +54,31 @@ export class UserService extends BaseService {
54
54
 
55
55
  ## How it works
56
56
 
57
- - **One required binding.** `MailComponent.binding()` throws `Mail options not configured` if `MailKeys.MAIL_OPTIONS` is not bound before registration. Every other binding -- queue executor config, verification generators -- is optional with a working default.
58
- - **Transport is a discriminated union.** `TMailOptions.provider` selects `NodemailerTransportHelper`, `MailgunTransportHelper`, or a `custom` object you supply that implements `IMailTransport` (`send()` + `verify()`). `MailTransportProvider` is the factory that switches on it and throws for an unsupported provider string.
59
- - **`MailService` is the one sending entry point.** `send()`, `sendBatch()`, `sendTemplate()`, and `verify()` all validate first, then call the transport directly and synchronously, then normalize failures into `MailErrorCodes`. A validation error (400) passes through unchanged; only a throwing transport gets wrapped as `SEND_FAILED` (500) -- the built-in Nodemailer and Mailgun transports never throw, they return `{ success: false, error }` instead.
60
- - **The queue executor is a separate subsystem, not a mail queue.** `IMailQueueExecutor` (`direct` / `internal-queue` / `bullmq`) only exposes `enqueueVerificationEmail()` and `setProcessor()` -- it never touches `MailService`. You must call `setProcessor()` with your own function (typically one that calls `mailService.send()` internally) before `enqueueVerificationEmail()` does anything.
61
- - **Templates are a simple substitution engine.** `TemplateEngineService` stores templates in an in-memory `Map` and replaces <code v-pre>{{variable}}</code> placeholders (dot-notation for nested values). A missing value is logged and left as the literal placeholder text, not blanked out.
62
- - **Startup logging never leaks credentials.** `MailComponent` logs only `mailOptions.provider` and `queueExecutorConfig.type` -- never the SMTP password, OAuth2 secret, API key, or Redis password nested inside them.
57
+ - **One required binding.** `MailComponent.binding()` throws `Mail options not configured` if `MailKeys.MAIL_OPTIONS` is not bound before registration. Every other binding is optional, with a working default: queue executor config, verification generators, and more.
58
+
59
+ - **Transport is a discriminated union.** `TMailOptions.provider` picks the transport class:
60
+
61
+ | `provider` | Class |
62
+ |---|---|
63
+ | `nodemailer` | `NodemailerTransportHelper` |
64
+ | `mailgun` | `MailgunTransportHelper` |
65
+ | `custom` | Your own object, implementing `IMailTransport` (`send()` + `verify()`) |
66
+
67
+ `MailTransportProvider` is the factory that switches on the provider. It throws for an unsupported provider string.
68
+
69
+ - **`MailService` is the one sending entry point.** `send()`, `sendBatch()`, `sendTemplate()`, and `verify()` all validate first, then call the transport directly, then normalize failures into `MailErrorCodes`:
70
+
71
+ | Case | Result |
72
+ |---|---|
73
+ | Validation error | Passes through unchanged as `400` |
74
+ | Transport throws | Wrapped as `SEND_FAILED` (`500`) |
75
+ | Nodemailer / Mailgun transport | Never throws - returns `{ success: false, error }` instead |
76
+
77
+ - **The queue executor is a separate subsystem, not a mail queue.** `IMailQueueExecutor` (`direct` / `internal-queue` / `bullmq`) exposes only `enqueueVerificationEmail()` and `setProcessor()`, and never touches `MailService`. Call `setProcessor()` before `enqueueVerificationEmail()`, with your own function - typically one wrapping `mailService.send()`.
78
+
79
+ - **Templates are a simple substitution engine.** `TemplateEngineService` stores templates in an in-memory `Map`. It replaces <code v-pre>{{variable}}</code> placeholders, with dot-notation for nested values. A missing value is logged and left as the literal placeholder text, not blanked out.
80
+
81
+ - **Startup logging never leaks credentials.** `MailComponent` logs only `mailOptions.provider` and `queueExecutorConfig.type`. It never logs the SMTP password, OAuth2 secret, API key, or Redis password nested inside them.
63
82
 
64
83
  ## Common tasks
65
84
 
@@ -155,9 +174,9 @@ const data = this.verificationGenerator.generateVerificationData({
155
174
 
156
175
  **Files:**
157
176
 
158
- - [`packages/core/src/components/mail/component.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/component.ts) -- `MailComponent`
159
- - [`packages/core/src/components/mail/services/mail.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/services/mail.service.ts) -- `MailService`
160
- - [`packages/core/src/components/mail/services/template.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/services/template.service.ts) -- `TemplateEngineService`
161
- - [`packages/core/src/components/mail/services/generator.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/services/generator.service.ts) -- verification generators
162
- - [`packages/core/src/components/mail/common/keys.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/common/keys.ts) -- `MailKeys`
163
- - [`packages/core/src/components/mail/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/common/types.ts) -- `TMailOptions`, `IMailMessage`, and every mail interface
177
+ - [`packages/core-server/src/components/mail/component.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/component.ts) -- `MailComponent`
178
+ - [`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) -- `MailService`
179
+ - [`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) -- `TemplateEngineService`
180
+ - [`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) -- verification generators
181
+ - [`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) -- `MailKeys`
182
+ - [`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) -- `TMailOptions`, `IMailMessage`, and every mail interface