@venizia/ignis-docs 0.2.0 → 0.2.1-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -14
- package/content/best-practices/api-usage-examples.md +39 -19
- package/content/best-practices/architectural-patterns.md +22 -11
- package/content/best-practices/architecture-decisions.md +29 -16
- package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
- package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
- package/content/best-practices/code-style-standards/control-flow.md +33 -1
- package/content/best-practices/code-style-standards/documentation.md +28 -8
- package/content/best-practices/code-style-standards/function-patterns.md +15 -4
- package/content/best-practices/code-style-standards/index.md +7 -2
- package/content/best-practices/code-style-standards/naming-conventions.md +26 -2
- package/content/best-practices/code-style-standards/route-definitions.md +9 -5
- package/content/best-practices/code-style-standards/tooling.md +14 -1
- package/content/best-practices/code-style-standards/type-safety.md +39 -6
- package/content/best-practices/common-pitfalls.md +59 -35
- package/content/best-practices/contribution-workflow.md +6 -2
- package/content/best-practices/data-modeling.md +78 -62
- package/content/best-practices/deployment-strategies.md +56 -19
- package/content/best-practices/error-handling.md +182 -93
- package/content/best-practices/index.md +6 -0
- package/content/best-practices/performance-optimization.md +26 -13
- package/content/best-practices/security-guidelines.md +35 -9
- package/content/best-practices/testing-strategies.md +7 -2
- package/content/best-practices/troubleshooting-tips.md +27 -17
- package/content/extensions/components/api-reference.md +107 -322
- package/content/extensions/components/authentication/api.md +454 -603
- package/content/extensions/components/authentication/errors.md +121 -498
- package/content/extensions/components/authentication/index.md +88 -801
- package/content/extensions/components/authentication/usage.md +207 -956
- package/content/extensions/components/authorization/api.md +736 -656
- package/content/extensions/components/authorization/errors.md +168 -206
- package/content/extensions/components/authorization/index.md +82 -797
- package/content/extensions/components/authorization/usage.md +194 -527
- package/content/extensions/components/health-check.md +71 -243
- package/content/extensions/components/mail/api.md +504 -287
- package/content/extensions/components/mail/errors.md +73 -61
- package/content/extensions/components/mail/index.md +96 -467
- package/content/extensions/components/mail/usage.md +130 -172
- package/content/extensions/components/request-tracker.md +66 -173
- package/content/extensions/components/socket-io/api.md +195 -13
- package/content/extensions/components/socket-io/errors.md +3 -3
- package/content/extensions/components/socket-io/index.md +50 -337
- package/content/extensions/components/socket-io/usage.md +143 -26
- package/content/extensions/components/static-asset/api.md +410 -142
- package/content/extensions/components/static-asset/errors.md +110 -53
- package/content/extensions/components/static-asset/index.md +79 -608
- package/content/extensions/components/static-asset/usage.md +180 -300
- package/content/extensions/components/websocket/api.md +275 -399
- package/content/extensions/components/websocket/errors.md +47 -56
- package/content/extensions/components/websocket/index.md +74 -407
- package/content/extensions/components/websocket/usage.md +110 -341
- package/content/extensions/helpers/cron/index.md +51 -160
- package/content/extensions/helpers/crypto/index.md +62 -483
- package/content/extensions/helpers/crypto/reference.md +456 -0
- package/content/extensions/helpers/env/index.md +60 -178
- package/content/extensions/helpers/error/index.md +221 -207
- package/content/extensions/helpers/inversion/index.md +65 -556
- package/content/extensions/helpers/inversion/reference.md +522 -0
- package/content/extensions/helpers/kafka/admin.md +20 -1
- package/content/extensions/helpers/kafka/compile-binary.md +41 -35
- package/content/extensions/helpers/kafka/consumer.md +54 -20
- package/content/extensions/helpers/kafka/examples.md +21 -16
- package/content/extensions/helpers/kafka/index.md +80 -610
- package/content/extensions/helpers/kafka/producer.md +134 -5
- package/content/extensions/helpers/kafka/schema-registry.md +45 -70
- package/content/extensions/helpers/logger/hf-logger.md +193 -0
- package/content/extensions/helpers/logger/index.md +64 -563
- package/content/extensions/helpers/logger/pino.md +85 -0
- package/content/extensions/helpers/logger/reference.md +746 -0
- package/content/extensions/helpers/network/api.md +241 -195
- package/content/extensions/helpers/network/index.md +72 -530
- package/content/extensions/helpers/queue/index.md +72 -900
- package/content/extensions/helpers/queue/reference.md +467 -0
- package/content/extensions/helpers/redis/index.md +73 -645
- package/content/extensions/helpers/redis/reference.md +727 -0
- package/content/extensions/helpers/secrets/index.md +66 -0
- package/content/extensions/helpers/socket-io/api.md +305 -203
- package/content/extensions/helpers/socket-io/index.md +66 -432
- package/content/extensions/helpers/storage/api.md +564 -462
- package/content/extensions/helpers/storage/index.md +77 -573
- package/content/extensions/helpers/types/index.md +66 -499
- package/content/extensions/helpers/types/reference.md +650 -0
- package/content/extensions/helpers/uid/index.md +58 -227
- package/content/extensions/helpers/websocket/api.md +329 -216
- package/content/extensions/helpers/websocket/index.md +65 -503
- package/content/extensions/helpers/worker-thread/index.md +58 -396
- package/content/extensions/helpers/worker-thread/reference.md +428 -0
- package/content/guides/core-concepts/persistent/models.md +1 -1
- package/content/guides/core-concepts/persistent/search-typesense.md +3 -3
- package/content/guides/core-concepts/persistent/transactions.md +1 -1
- package/content/guides/core-concepts/secrets-vault.md +177 -0
- package/content/guides/core-concepts/services.md +1 -1
- package/content/guides/migrations/redis-helpers-migration.md +1 -1
- package/content/guides/migrations/unified-connectors-migration.md +2 -2
- package/content/guides/tutorials/ecommerce-api.md +3 -8
- package/content/references/base/application.md +1 -1
- package/content/references/base/connectors.md +79 -136
- package/content/references/base/datasources-reference.md +599 -0
- package/content/references/base/datasources.md +84 -444
- package/content/references/base/dependency-injection.md +17 -39
- package/content/references/base/filter-system/application-usage.md +69 -121
- package/content/references/base/filter-system/array-operators.md +12 -0
- package/content/references/base/filter-system/comparison-operators.md +12 -0
- package/content/references/base/filter-system/default-filter.md +136 -348
- package/content/references/base/filter-system/fields-order-pagination.md +38 -16
- package/content/references/base/filter-system/index.md +106 -257
- package/content/references/base/filter-system/json-filtering.md +12 -2
- package/content/references/base/filter-system/list-operators.md +16 -2
- package/content/references/base/filter-system/logical-operators.md +13 -0
- package/content/references/base/filter-system/null-operators.md +13 -0
- package/content/references/base/filter-system/pattern-matching.md +12 -0
- package/content/references/base/filter-system/quick-reference.md +11 -2
- package/content/references/base/filter-system/range-operators.md +12 -0
- package/content/references/base/filter-system/tips.md +70 -133
- package/content/references/base/filter-system/use-cases.md +156 -233
- package/content/references/base/middlewares.md +35 -21
- package/content/references/base/models-reference.md +886 -0
- package/content/references/base/models.md +80 -1452
- package/content/references/base/repositories/advanced.md +156 -192
- package/content/references/base/repositories/index.md +77 -650
- package/content/references/base/repositories/mixins.md +22 -18
- package/content/references/base/repositories/relations.md +123 -171
- package/content/references/base/repositories/soft-deletable.md +58 -56
- package/content/references/base/secrets.md +263 -0
- package/content/references/base/services.md +2 -2
- package/content/references/configuration/environment-variables.md +48 -4
- package/content/references/configuration/index.md +49 -31
- package/content/references/quick-reference.md +3 -16
- package/content/references/utilities/crypto.md +35 -76
- package/content/references/utilities/date.md +33 -73
- package/content/references/utilities/index.md +1 -1
- package/content/references/utilities/jsx-reference.md +298 -0
- package/content/references/utilities/jsx.md +82 -525
- package/content/references/utilities/module.md +29 -62
- package/content/references/utilities/parse.md +34 -64
- package/content/references/utilities/performance.md +33 -58
- package/content/references/utilities/promise.md +28 -62
- package/content/references/utilities/request.md +57 -218
- package/content/references/utilities/schema.md +43 -137
- package/content/references/utilities/statuses-reference.md +361 -0
- package/content/references/utilities/statuses.md +63 -667
- package/package.json +8 -8
|
@@ -1,6 +1,107 @@
|
|
|
1
|
-
|
|
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/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)
|
|
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
|
-
|
|
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,285 @@
|
|
|
8
109
|
┌─────────────────────────────────────────────────┐
|
|
9
110
|
│ Your Application │
|
|
10
111
|
│ │
|
|
11
|
-
│
|
|
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
|
-
│
|
|
22
|
-
│ ├── initGenerators() │
|
|
123
|
+
│ ├── initGenerators() (transient bindings) │
|
|
23
124
|
│ │ ├── NumericCodeGenerator │
|
|
24
125
|
│ │ ├── RandomTokenGenerator │
|
|
25
126
|
│ │ └── DefaultVerificationDataGenerator │
|
|
26
127
|
│ │ │
|
|
27
|
-
│ ├── initProviders()
|
|
28
|
-
│ │ ├── MailTransportProvider
|
|
29
|
-
│ │ └── MailQueueExecutorProvider
|
|
128
|
+
│ ├── initProviders() (singleton bindings) │
|
|
129
|
+
│ │ ├── MailTransportProvider │
|
|
130
|
+
│ │ └── MailQueueExecutorProvider │
|
|
30
131
|
│ │ │
|
|
31
|
-
│ ├── initServices()
|
|
32
|
-
│ │ ├── MailService
|
|
33
|
-
│ │ └── TemplateEngineService
|
|
132
|
+
│ ├── initServices() (singleton bindings) │
|
|
133
|
+
│ │ ├── MailService │
|
|
134
|
+
│ │ └── TemplateEngineService │
|
|
34
135
|
│ │ │
|
|
35
136
|
│ └── createAndBindInstances() │
|
|
36
|
-
│ ├── Transport Instance
|
|
37
|
-
│ └── Queue Executor
|
|
137
|
+
│ ├── Transport Instance ◄── MAIL_OPTIONS │
|
|
138
|
+
│ └── Queue Executor ◄── QUEUE_CONFIG │
|
|
139
|
+
│ (or the direct │
|
|
140
|
+
│ default) │
|
|
38
141
|
└─────────────────────────────────────────────────┘
|
|
39
142
|
```
|
|
40
143
|
|
|
41
|
-
**
|
|
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
|
-
|
|
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/src/components/mail/component.ts)
|
|
49
147
|
|
|
50
|
-
|
|
148
|
+
## Binding keys
|
|
51
149
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
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
|
-
|
|
167
|
+
**Source:** [`common/keys.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/common/keys.ts)
|
|
60
168
|
|
|
61
|
-
|
|
169
|
+
## Configuration
|
|
62
170
|
|
|
63
|
-
|
|
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
|
-
|
|
173
|
+
A discriminated union on `provider`, extending `IBaseMailOptions`:
|
|
69
174
|
|
|
70
175
|
```typescript
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
+
}
|
|
185
|
+
|
|
186
|
+
interface IMailgunMailOptions extends IBaseMailOptions {
|
|
187
|
+
provider: 'mailgun';
|
|
188
|
+
config: TMailgunConfig; // AnyType & { domain: string } -- also requires username, key at runtime
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
interface ICustomMailOptions extends IBaseMailOptions {
|
|
192
|
+
provider: 'custom';
|
|
193
|
+
config: IMailTransport; // Must implement send() and verify()
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
interface IGenericMailOptions extends IBaseMailOptions {
|
|
197
|
+
provider: string;
|
|
198
|
+
config: Record<string, AnyType>;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
type TMailOptions =
|
|
202
|
+
| INodemailerMailOptions
|
|
203
|
+
| IMailgunMailOptions
|
|
204
|
+
| ICustomMailOptions
|
|
205
|
+
| IGenericMailOptions;
|
|
74
206
|
```
|
|
75
207
|
|
|
76
|
-
|
|
208
|
+
**Nodemailer (SMTP with basic auth):**
|
|
77
209
|
|
|
78
|
-
|
|
210
|
+
```typescript
|
|
211
|
+
{
|
|
212
|
+
provider: MailProviders.NODEMAILER,
|
|
213
|
+
from: 'noreply@example.com',
|
|
214
|
+
fromName: 'Example App',
|
|
215
|
+
config: {
|
|
216
|
+
host: 'smtp.gmail.com',
|
|
217
|
+
port: 465,
|
|
218
|
+
secure: true,
|
|
219
|
+
auth: { user: 'your-email@gmail.com', pass: 'your-app-password' },
|
|
220
|
+
},
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**Nodemailer (OAuth2):**
|
|
79
225
|
|
|
80
226
|
```typescript
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
227
|
+
{
|
|
228
|
+
provider: MailProviders.NODEMAILER,
|
|
229
|
+
from: applicationEnvironment.get<string>('APP_ENV_MAIL_FROM') ?? 'noreply@example.com',
|
|
230
|
+
fromName: applicationEnvironment.get<string>('APP_ENV_MAIL_FROM_NAME') ?? 'Example App',
|
|
231
|
+
config: {
|
|
232
|
+
host: applicationEnvironment.get<string>('APP_ENV_MAIL_HOST') ?? 'smtp.gmail.com',
|
|
233
|
+
port: +(applicationEnvironment.get<number>('APP_ENV_MAIL_PORT') ?? 465),
|
|
234
|
+
secure: toBoolean(applicationEnvironment.get<boolean>('APP_ENV_MAIL_SECURE') ?? true),
|
|
235
|
+
auth: {
|
|
236
|
+
type: 'oauth2',
|
|
237
|
+
user: applicationEnvironment.get<string>('APP_ENV_MAIL_USER'),
|
|
238
|
+
clientId: applicationEnvironment.get<string>('APP_ENV_MAIL_CLIENT_ID'),
|
|
239
|
+
clientSecret: applicationEnvironment.get<string>('APP_ENV_MAIL_CLIENT_SECRET'),
|
|
240
|
+
refreshToken: applicationEnvironment.get<string>('APP_ENV_MAIL_REFRESH_TOKEN'),
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
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`:
|
|
247
|
+
|
|
248
|
+
```
|
|
249
|
+
APP_ENV_MAIL_HOST=smtp.gmail.com
|
|
250
|
+
APP_ENV_MAIL_PORT=465
|
|
251
|
+
APP_ENV_MAIL_SECURE=true
|
|
252
|
+
APP_ENV_MAIL_USER=your-email@gmail.com
|
|
253
|
+
APP_ENV_MAIL_CLIENT_ID=your-oauth2-client-id
|
|
254
|
+
APP_ENV_MAIL_CLIENT_SECRET=your-oauth2-client-secret
|
|
255
|
+
APP_ENV_MAIL_REFRESH_TOKEN=your-oauth2-refresh-token
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
> [!TIP]
|
|
259
|
+
> 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.
|
|
260
|
+
|
|
261
|
+
**Mailgun:**
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
{
|
|
265
|
+
provider: MailProviders.MAILGUN,
|
|
266
|
+
from: 'noreply@example.com',
|
|
267
|
+
fromName: 'Example App',
|
|
268
|
+
config: {
|
|
269
|
+
username: 'api', // required -- mailgun.js client username
|
|
270
|
+
key: process.env.MAILGUN_API_KEY, // required -- Mailgun API key
|
|
271
|
+
domain: 'mg.example.com', // required
|
|
272
|
+
host: 'api.eu.mailgun.net', // optional -- EU region
|
|
273
|
+
},
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
> [!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.
|
|
279
|
+
|
|
280
|
+
**Custom transport:**
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
{
|
|
284
|
+
provider: MailProviders.CUSTOM,
|
|
285
|
+
from: 'noreply@example.com',
|
|
286
|
+
config: myTransportImplementingIMailTransport, // must have send() and verify()
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
**Generic provider (extensibility escape hatch):**
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
{
|
|
294
|
+
provider: 'sendgrid',
|
|
295
|
+
from: 'noreply@example.com',
|
|
296
|
+
config: { apiKey: process.env.SENDGRID_API_KEY },
|
|
85
297
|
}
|
|
86
298
|
```
|
|
87
299
|
|
|
88
|
-
|
|
300
|
+
> [!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.
|
|
89
302
|
|
|
90
|
-
|
|
303
|
+
### Queue executor options (`IMailQueueExecutorConfig`)
|
|
91
304
|
|
|
92
|
-
|
|
93
|
-
|
|
305
|
+
```typescript
|
|
306
|
+
interface IMailQueueExecutorConfig {
|
|
307
|
+
type: TConstValue<typeof MailQueueExecutorTypes>; // 'direct' | 'internal-queue' | 'bullmq'
|
|
308
|
+
internalQueue?: {
|
|
309
|
+
identifier: string;
|
|
310
|
+
};
|
|
311
|
+
bullmq?: {
|
|
312
|
+
redis: IRedisSingleHelperOptions;
|
|
313
|
+
queue: { identifier: string; name: string };
|
|
314
|
+
mode: TConstValue<typeof BullMQExecutorModes>; // REQUIRED -- no default
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
```
|
|
94
318
|
|
|
95
|
-
|
|
319
|
+
```typescript
|
|
320
|
+
// Direct (also the implicit default when MAIL_QUEUE_EXECUTOR_CONFIG is unbound)
|
|
321
|
+
{ type: 'direct' }
|
|
322
|
+
|
|
323
|
+
// Internal queue (in-memory)
|
|
324
|
+
{ type: 'internal-queue', internalQueue: { identifier: 'mail-internal-queue' } }
|
|
325
|
+
|
|
326
|
+
// BullMQ (Redis-backed)
|
|
327
|
+
{
|
|
328
|
+
type: 'bullmq',
|
|
329
|
+
bullmq: {
|
|
330
|
+
redis: { host: 'localhost', port: 6379, password: 'your-redis-password' },
|
|
331
|
+
queue: { identifier: 'mail-queue', name: 'mail-queue' },
|
|
332
|
+
mode: 'both', // 'queue-only' | 'worker-only' | 'both' -- required, no default
|
|
333
|
+
},
|
|
334
|
+
}
|
|
335
|
+
```
|
|
96
336
|
|
|
97
|
-
|
|
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).
|
|
98
339
|
|
|
99
|
-
|
|
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
|
|
340
|
+
**Source:** [`common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/common/types.ts)
|
|
104
341
|
|
|
105
|
-
|
|
342
|
+
### Constants
|
|
106
343
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
344
|
+
| Constant | Value | Description |
|
|
345
|
+
|----------|-------|-------------|
|
|
346
|
+
| `MailDefaults.BATCH_CONCURRENCY` | `5` | Default concurrent sends in `sendBatch()` |
|
|
347
|
+
| `MailDefaults.FALLBACK_FROM` | `'noreply@example.com'` | Used by `getDefaultFrom()` when `options.from` is unset |
|
|
348
|
+
| `MailExecutorErrors.PROCESSOR_NOT_SET` | `'Processor not set. Call setProcessor() first.'` | Thrown by all three queue executors |
|
|
349
|
+
| `MailQueueExecutorTypes.DIRECT` | `'direct'` | Immediate execution |
|
|
350
|
+
| `MailQueueExecutorTypes.INTERNAL_QUEUE` | `'internal-queue'` | In-memory queue |
|
|
351
|
+
| `MailQueueExecutorTypes.BULLMQ` | `'bullmq'` | Redis-backed queue |
|
|
352
|
+
| `BullMQExecutorModes.QUEUE_ONLY` | `'queue-only'` | Producer only (enqueue) |
|
|
353
|
+
| `BullMQExecutorModes.WORKER_ONLY` | `'worker-only'` | Consumer only (process) |
|
|
354
|
+
| `BullMQExecutorModes.BOTH` | `'both'` | Full duplex (produce + consume) |
|
|
112
355
|
|
|
113
|
-
|
|
356
|
+
`MailQueueExecutorTypes` and `BullMQExecutorModes` both carry a `SCHEME_SET`/`MODE_SET` and a static `isValid()`:
|
|
114
357
|
|
|
115
|
-
|
|
358
|
+
```typescript
|
|
359
|
+
MailQueueExecutorTypes.isValid('bullmq'); // true
|
|
360
|
+
MailQueueExecutorTypes.isValid('unknown'); // false
|
|
361
|
+
BullMQExecutorModes.isValid('both'); // true
|
|
362
|
+
BullMQExecutorModes.isValid('invalid'); // false
|
|
363
|
+
```
|
|
116
364
|
|
|
117
|
-
|
|
365
|
+
#### `MailErrorCodes`
|
|
366
|
+
|
|
367
|
+
Built through `MessageCode.build()`, so every value is lower-case (`ApplicationError` lower-cases whatever it is handed):
|
|
368
|
+
|
|
369
|
+
| Constant | Value | Description |
|
|
370
|
+
|----------|-------|-------------|
|
|
371
|
+
| `MailErrorCodes.INVALID_CONFIGURATION` | `'core.mail.invalid_configuration'` | Invalid or missing configuration (transport, template engine, subject, body) |
|
|
372
|
+
| `MailErrorCodes.SEND_FAILED` | `'core.mail.send_failed'` | Single email send failed |
|
|
373
|
+
| `MailErrorCodes.VERIFICATION_FAILED` | `'core.mail.verification_failed'` | Transport connection verification failed |
|
|
374
|
+
| `MailErrorCodes.INVALID_RECIPIENT` | `'core.mail.invalid_recipient'` | Missing or empty recipient address |
|
|
375
|
+
| `MailErrorCodes.BATCH_SEND_FAILED` | `'core.mail.batch_send_failed'` | Batch email operation failed |
|
|
376
|
+
| `MailErrorCodes.TEMPLATE_NOT_FOUND` | `'core.mail.template_not_found'` | Template name not found in registry |
|
|
377
|
+
|
|
378
|
+
**Source:** [`common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/common/constants.ts)
|
|
379
|
+
|
|
380
|
+
## `IMailService` interface
|
|
118
381
|
|
|
119
382
|
```typescript
|
|
120
383
|
interface IMailService {
|
|
121
|
-
// Send a single email
|
|
122
384
|
send(message: IMailMessage): Promise<IMailSendResult>;
|
|
123
385
|
|
|
124
|
-
// Send multiple emails with controlled concurrency
|
|
125
386
|
sendBatch(
|
|
126
387
|
messages: IMailMessage[],
|
|
127
388
|
options?: { concurrency?: number },
|
|
128
389
|
): Promise<IMailSendResult[]>;
|
|
129
390
|
|
|
130
|
-
// Send email using a registered template
|
|
131
391
|
sendTemplate(opts: {
|
|
132
392
|
templateName: string;
|
|
133
393
|
data: Record<string, any>;
|
|
@@ -135,100 +395,77 @@ interface IMailService {
|
|
|
135
395
|
options?: Partial<IMailMessage>;
|
|
136
396
|
}): Promise<IMailSendResult>;
|
|
137
397
|
|
|
138
|
-
// Verify transport connection
|
|
139
398
|
verify(): Promise<boolean>;
|
|
140
399
|
}
|
|
141
400
|
```
|
|
142
401
|
|
|
143
|
-
|
|
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`
|
|
402
|
+
**`send(message)`**
|
|
153
403
|
|
|
154
|
-
|
|
404
|
+
1. `validateMessage()` throws for missing `to`, missing `subject`, or missing both `text`/`html` (400, see below).
|
|
405
|
+
2. Merges `message.from` with `getDefaultFrom()` if unset.
|
|
406
|
+
3. Delegates to `transport.send()`.
|
|
407
|
+
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.
|
|
155
409
|
|
|
156
|
-
|
|
410
|
+
**`sendBatch(messages, options?)`**
|
|
157
411
|
|
|
158
|
-
|
|
412
|
+
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
413
|
|
|
160
|
-
|
|
414
|
+
**`sendTemplate(opts)`**
|
|
161
415
|
|
|
162
|
-
|
|
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).
|
|
416
|
+
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
417
|
|
|
168
418
|
**`verify()`**
|
|
169
419
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
### Protected Methods (MailService)
|
|
420
|
+
Delegates to `transport.verify()`. If the transport throws, wraps it as `MailErrorCodes.VERIFICATION_FAILED` (500).
|
|
173
421
|
|
|
174
|
-
|
|
422
|
+
### Protected methods (`MailService`)
|
|
175
423
|
|
|
176
|
-
|
|
424
|
+
**`validateMessage(message)`**
|
|
177
425
|
|
|
178
|
-
| Check | Error
|
|
426
|
+
| Check | Error code | Status | Message |
|
|
179
427
|
|-------|-----------|--------|---------|
|
|
180
|
-
| `to` is falsy or empty array | `INVALID_RECIPIENT` | 400 | `Recipient email address is required` |
|
|
428
|
+
| `to` is falsy or an empty array | `INVALID_RECIPIENT` | 400 | `Recipient email address is required` |
|
|
181
429
|
| `subject` is falsy | `INVALID_CONFIGURATION` | 400 | `Email subject is required` |
|
|
182
430
|
| Both `text` and `html` are falsy | `INVALID_CONFIGURATION` | 400 | `Email must have either text or html content` |
|
|
183
431
|
|
|
184
432
|
**`getDefaultFrom()`**
|
|
185
433
|
|
|
186
|
-
|
|
434
|
+
- If `options.from` is unset, returns `MailDefaults.FALLBACK_FROM` (`'noreply@example.com'`).
|
|
435
|
+
- Else if `options.fromName` is unset, returns `options.from` as-is.
|
|
436
|
+
- Else returns `"${fromName}" <${from}>`.
|
|
437
|
+
|
|
438
|
+
**Source:** [`services/mail.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/services/mail.service.ts)
|
|
187
439
|
|
|
188
|
-
## IMailMessage
|
|
440
|
+
## `IMailMessage` interface
|
|
189
441
|
|
|
190
442
|
```typescript
|
|
191
443
|
interface IMailMessage {
|
|
192
|
-
from?: string;
|
|
193
|
-
to: string | string[];
|
|
194
|
-
cc?: string | string[];
|
|
195
|
-
bcc?: string | string[];
|
|
196
|
-
replyTo?: string;
|
|
197
|
-
subject: string;
|
|
198
|
-
text?: string;
|
|
199
|
-
html?: string;
|
|
200
|
-
attachments?: IMailAttachment[];
|
|
201
|
-
headers?: Record<string, string>;
|
|
202
|
-
requireValidate?: boolean;
|
|
203
|
-
[key: string]: any;
|
|
444
|
+
from?: string; // Uses getDefaultFrom() if not provided
|
|
445
|
+
to: string | string[];
|
|
446
|
+
cc?: string | string[];
|
|
447
|
+
bcc?: string | string[];
|
|
448
|
+
replyTo?: string;
|
|
449
|
+
subject: string;
|
|
450
|
+
text?: string;
|
|
451
|
+
html?: string;
|
|
452
|
+
attachments?: IMailAttachment[];
|
|
453
|
+
headers?: Record<string, string>;
|
|
454
|
+
requireValidate?: boolean; // Passed through to sendTemplate()'s render() call
|
|
455
|
+
[key: string]: any; // Open-ended -- provider-specific fields pass through
|
|
204
456
|
}
|
|
205
457
|
```
|
|
206
458
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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:
|
|
459
|
+
| Field | Notes |
|
|
460
|
+
|-------|-------|
|
|
461
|
+
| `from` | Falls back to `getDefaultFrom()`. Can be overridden per message (multi-tenant scenarios). |
|
|
462
|
+
| `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. |
|
|
463
|
+
| `replyTo` | Nodemailer passes it directly; Mailgun maps it to `h:Reply-To`. |
|
|
464
|
+
| `subject` | Rendered through the template engine when set via `sendTemplate()`. |
|
|
465
|
+
| `text`, `html` | At least one is required (`validateMessage()`). |
|
|
466
|
+
| `attachments` | See `IMailAttachment` below. |
|
|
467
|
+
| `headers` | Nodemailer passes them directly; Mailgun prefixes every key with `h:`. |
|
|
468
|
+
| `requireValidate` | `true` makes template rendering throw on missing placeholders instead of preserving them as literal text. Defaults to `false`. |
|
|
232
469
|
|
|
233
470
|
```typescript
|
|
234
471
|
interface IMailAttachment {
|
|
@@ -241,28 +478,17 @@ interface IMailAttachment {
|
|
|
241
478
|
}
|
|
242
479
|
```
|
|
243
480
|
|
|
244
|
-
|
|
245
|
-
-
|
|
246
|
-
-
|
|
247
|
-
-
|
|
248
|
-
|
|
249
|
-
**`headers`**
|
|
250
|
-
|
|
251
|
-
Custom SMTP headers. For Nodemailer, passed directly. For Mailgun, each key is auto-prefixed with `h:`.
|
|
481
|
+
- File path: `{ filename: 'doc.pdf', path: '/path/to/doc.pdf' }`
|
|
482
|
+
- Buffer: `{ filename: 'data.txt', content: Buffer.from('...') }`
|
|
483
|
+
- Inline image: `{ filename: 'logo.png', path: '...', cid: 'logo' }`
|
|
484
|
+
- Mailgun maps each attachment to `{ filename, data: att.path ?? att.content ?? Buffer.from('') }`.
|
|
252
485
|
|
|
253
|
-
|
|
486
|
+
**Source:** [`common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/common/types.ts)
|
|
254
487
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
**`[key: string]: any`**
|
|
258
|
-
|
|
259
|
-
The interface is open-ended -- additional fields are accepted for provider-specific options.
|
|
260
|
-
|
|
261
|
-
## IMailTemplateEngine Interface
|
|
488
|
+
## `IMailTemplateEngine` interface
|
|
262
489
|
|
|
263
490
|
```typescript
|
|
264
491
|
interface IMailTemplateEngine {
|
|
265
|
-
// Render a template with data
|
|
266
492
|
render(opts: {
|
|
267
493
|
templateData?: string;
|
|
268
494
|
templateName?: string;
|
|
@@ -270,144 +496,173 @@ interface IMailTemplateEngine {
|
|
|
270
496
|
requireValidate?: boolean;
|
|
271
497
|
}): string;
|
|
272
498
|
|
|
273
|
-
// Register a new template
|
|
274
499
|
registerTemplate(opts: { name: string; content: string }): void;
|
|
275
500
|
|
|
276
|
-
// Validate template data
|
|
277
501
|
validateTemplateData(opts: { template: string; data: Record<string, any> }): {
|
|
278
502
|
isValid: boolean;
|
|
279
503
|
missingKeys: string[];
|
|
280
504
|
allKeys: string[];
|
|
281
505
|
};
|
|
282
506
|
|
|
283
|
-
// Get a registered template
|
|
284
507
|
getTemplate(name: string): ITemplate | undefined;
|
|
285
|
-
|
|
286
|
-
// List all registered templates
|
|
287
508
|
listTemplates(): ITemplate[];
|
|
288
|
-
|
|
289
|
-
// Check if template exists
|
|
290
509
|
hasTemplate(name: string): boolean;
|
|
291
|
-
|
|
292
|
-
// Remove a template
|
|
293
510
|
removeTemplate(name: string): boolean;
|
|
294
511
|
}
|
|
295
512
|
```
|
|
296
513
|
|
|
297
|
-
**
|
|
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.
|
|
298
515
|
|
|
299
|
-
|
|
516
|
+
| Method | Behavior |
|
|
517
|
+
|--------|----------|
|
|
518
|
+
| `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()`. |
|
|
519
|
+
| `registerTemplate(opts)` | `options` (on the class) accepts `subject`/`description` (`Partial<ITemplate>`). Overwrites an existing template with the same name. |
|
|
520
|
+
| `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`. |
|
|
521
|
+
| `getTemplate(name)` | Returns `undefined` if not registered. |
|
|
522
|
+
| `listTemplates()` | All templates, as an array of `ITemplate`. |
|
|
523
|
+
| `hasTemplate(name)` | Registry membership check. |
|
|
524
|
+
| `removeTemplate(name)` | Logs the removal, returns `true`/`false`. |
|
|
300
525
|
|
|
301
|
-
|
|
526
|
+
```typescript
|
|
527
|
+
interface ITemplate {
|
|
528
|
+
name: string;
|
|
529
|
+
content?: string;
|
|
530
|
+
render?: (data: Record<string, AnyType>) => string;
|
|
531
|
+
subject?: string;
|
|
532
|
+
description?: string;
|
|
533
|
+
}
|
|
534
|
+
```
|
|
302
535
|
|
|
303
|
-
|
|
536
|
+
The `render` field supports a custom per-template render function, though the built-in `TemplateEngineService` always uses `content` + `renderSimpleTemplate()` instead.
|
|
304
537
|
|
|
305
|
-
|
|
538
|
+
**Source:** [`services/template.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/services/template.service.ts)
|
|
306
539
|
|
|
307
|
-
|
|
540
|
+
## Transport layer
|
|
308
541
|
|
|
309
|
-
|
|
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
|
|
542
|
+
`MailTransportProvider.value()` returns a factory function that switches on `TMailOptions.provider`:
|
|
313
543
|
|
|
314
|
-
|
|
544
|
+
| Provider | Result |
|
|
545
|
+
|----------|--------|
|
|
546
|
+
| `'nodemailer'` | `NodemailerTransportHelper`, backed by `nodemailer` |
|
|
547
|
+
| `'mailgun'` | `MailgunTransportHelper`, backed by the Mailgun HTTP API (`mailgun.js`) |
|
|
548
|
+
| `'custom'` | The `config` value itself, validated to implement `IMailTransport` |
|
|
549
|
+
| Any other string | Throws `Unsupported mail provider: <provider>` (`INVALID_CONFIGURATION`, 500) |
|
|
315
550
|
|
|
316
|
-
|
|
551
|
+
Type narrowing uses three private guards, each checking `provider === X && 'config' in options`:
|
|
317
552
|
|
|
318
|
-
|
|
553
|
+
```typescript
|
|
554
|
+
private isNodemailerOptions(options: TMailOptions): options is INodemailerMailOptions
|
|
555
|
+
private isMailgunOptions(options: TMailOptions): options is IMailgunMailOptions
|
|
556
|
+
private isCustomOptions(options: TMailOptions): options is ICustomMailOptions
|
|
557
|
+
```
|
|
319
558
|
|
|
320
|
-
|
|
559
|
+
For `custom`, an additional `isMailTransport()` utility check reports specifically which of `send`/`verify` is missing.
|
|
321
560
|
|
|
322
|
-
|
|
561
|
+
```typescript
|
|
562
|
+
interface IMailTransport {
|
|
563
|
+
send(message: IMailMessage): Promise<IMailSendResult>;
|
|
564
|
+
verify(): Promise<boolean>;
|
|
565
|
+
close?(): Promise<void>;
|
|
566
|
+
}
|
|
567
|
+
```
|
|
323
568
|
|
|
324
|
-
|
|
569
|
+
### Peer dependency loading
|
|
325
570
|
|
|
326
|
-
|
|
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.
|
|
327
572
|
|
|
328
|
-
|
|
573
|
+
**Nodemailer (`NodemailerTransportHelper`, extends `BaseHelper`):**
|
|
329
574
|
|
|
330
|
-
|
|
575
|
+
- `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.
|
|
578
|
+
- `close()` calls `transporter.close()`.
|
|
331
579
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
description?: string;
|
|
339
|
-
}
|
|
340
|
-
```
|
|
580
|
+
**Mailgun (`MailgunTransportHelper`, extends `BaseHelper`):**
|
|
581
|
+
|
|
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.
|
|
341
586
|
|
|
342
|
-
|
|
587
|
+
**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.
|
|
343
588
|
|
|
344
|
-
|
|
589
|
+
**Source:** [`helpers/transporters/`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/helpers/transporters)
|
|
345
590
|
|
|
346
|
-
|
|
591
|
+
## Queue executor implementations
|
|
592
|
+
|
|
593
|
+
All three implement:
|
|
347
594
|
|
|
348
595
|
```typescript
|
|
349
|
-
interface
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
response?: any;
|
|
353
|
-
error?: string;
|
|
596
|
+
interface IMailQueueExecutor {
|
|
597
|
+
enqueueVerificationEmail(email: string, options?: IMailQueueOptions): Promise<IMailQueueResult>;
|
|
598
|
+
setProcessor(processor: (email: string) => Promise<IMailProcessorResult>): void;
|
|
354
599
|
}
|
|
355
600
|
```
|
|
356
601
|
|
|
357
|
-
|
|
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()`.
|
|
358
603
|
|
|
359
|
-
|
|
604
|
+
**`InternalQueueMailExecutorHelper`** (extends `BaseHelper`, constructor takes `IInternalQueueMailExecutorOpts`) -- wraps `SequentialQueueHelper<IQueueJobPayload>` from `@venizia/ignis-helpers`, `autoDispatch: true`.
|
|
605
|
+
|
|
606
|
+
- Job IDs: `job_<counter>_<timestamp>`.
|
|
607
|
+
- `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.
|
|
610
|
+
- No persistence across restarts.
|
|
611
|
+
|
|
612
|
+
**`BullMQMailExecutorHelper`** (extends `BaseHelper`, constructor takes `IBullMQMailExecutorOpts`) -- wraps `BullMQHelper` (`@venizia/ignis-helpers/bullmq`) and a `RedisSingleHelper` connection.
|
|
613
|
+
|
|
614
|
+
| Mode | Queue created | Workers created | Can enqueue | Can process |
|
|
615
|
+
|------|----------------|------------------|-------------|-------------|
|
|
616
|
+
| `'queue-only'` | Yes | No (`setProcessor()` skips worker creation, logs a warning) | Yes -- processor not required | No |
|
|
617
|
+
| `'worker-only'` | No | Yes | No (throws) | Yes |
|
|
618
|
+
| `'both'` | Yes | Yes | Yes -- processor required | Yes |
|
|
619
|
+
|
|
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.
|
|
360
624
|
|
|
361
625
|
```typescript
|
|
362
|
-
interface
|
|
363
|
-
|
|
364
|
-
|
|
626
|
+
interface IBullMQMailExecutorOpts {
|
|
627
|
+
redis: IRedisSingleHelperOptions; // from @venizia/ignis-helpers
|
|
628
|
+
queue: { identifier: string; name: string };
|
|
629
|
+
mode: TConstValue<typeof BullMQExecutorModes>; // REQUIRED, no default
|
|
365
630
|
}
|
|
366
631
|
```
|
|
367
632
|
|
|
368
|
-
|
|
633
|
+
**Source:** [`helpers/executors/`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/helpers/executors)
|
|
634
|
+
|
|
635
|
+
## Additional interfaces
|
|
369
636
|
|
|
370
637
|
```typescript
|
|
638
|
+
interface IMailSendResult {
|
|
639
|
+
success: boolean;
|
|
640
|
+
messageId?: string;
|
|
641
|
+
response?: any;
|
|
642
|
+
error?: string;
|
|
643
|
+
}
|
|
644
|
+
|
|
371
645
|
interface IMailQueueOptions {
|
|
372
646
|
priority?: number;
|
|
373
647
|
delay?: number;
|
|
374
648
|
attempts?: number;
|
|
375
|
-
backoff?: {
|
|
376
|
-
type: 'fixed' | 'exponential';
|
|
377
|
-
delay: number;
|
|
378
|
-
};
|
|
649
|
+
backoff?: { type: 'fixed' | 'exponential'; delay: number };
|
|
379
650
|
}
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
### IMailQueueResult
|
|
383
651
|
|
|
384
|
-
```typescript
|
|
385
652
|
interface IMailQueueResult {
|
|
386
653
|
jobId?: string;
|
|
387
|
-
queued: boolean;
|
|
654
|
+
queued: boolean; // false for direct execution, true for internal-queue and bullmq
|
|
388
655
|
message: string;
|
|
389
|
-
result?: IMailProcessorResult;
|
|
656
|
+
result?: IMailProcessorResult; // populated only for direct execution (synchronous processor)
|
|
390
657
|
}
|
|
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
658
|
|
|
395
|
-
### IMailProcessorResult
|
|
396
|
-
|
|
397
|
-
```typescript
|
|
398
659
|
interface IMailProcessorResult {
|
|
399
660
|
success: boolean;
|
|
400
661
|
message: string;
|
|
401
662
|
expiresInMinutes: number;
|
|
402
663
|
nextResendAt?: string;
|
|
403
664
|
}
|
|
404
|
-
```
|
|
405
665
|
|
|
406
|
-
The return type of the processor function registered via `setProcessor()`.
|
|
407
|
-
|
|
408
|
-
### IVerificationData
|
|
409
|
-
|
|
410
|
-
```typescript
|
|
411
666
|
interface IVerificationData {
|
|
412
667
|
verificationCode: string;
|
|
413
668
|
codeGeneratedAt: string;
|
|
@@ -418,88 +673,50 @@ interface IVerificationData {
|
|
|
418
673
|
tokenExpiresAt: string;
|
|
419
674
|
lastCodeSentAt: string;
|
|
420
675
|
}
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
### IVerificationGenerationOptions
|
|
424
676
|
|
|
425
|
-
```typescript
|
|
426
677
|
interface IVerificationGenerationOptions {
|
|
427
|
-
codeLength: number;
|
|
678
|
+
codeLength: number; // all four fields required, no defaults
|
|
428
679
|
tokenBytes: number;
|
|
429
680
|
codeExpiryMinutes: number;
|
|
430
681
|
tokenExpiryHours: number;
|
|
431
682
|
}
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
All fields are required. Passed to `DefaultVerificationDataGenerator.generateVerificationData()`.
|
|
435
683
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
mode: TConstValue<typeof BullMQExecutorModes>; // REQUIRED
|
|
684
|
+
interface IQueueJobPayload {
|
|
685
|
+
id: string;
|
|
686
|
+
email: string;
|
|
687
|
+
options?: IMailQueueOptions;
|
|
688
|
+
attempts: number;
|
|
689
|
+
scheduledAt: number;
|
|
443
690
|
}
|
|
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
|
-
|
|
460
|
-
Unlike the interface's synchronous `setProcessor()`, the BullMQ executor's version is `async` and accepts an optional second argument:
|
|
461
691
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
opts?: {
|
|
466
|
-
numberOfWorkers?: number; // default: 1
|
|
467
|
-
concurrencyPerWorker?: number; // default: 5
|
|
468
|
-
lockDuration?: number; // default: 30000 (ms)
|
|
469
|
-
},
|
|
470
|
-
): Promise<void>
|
|
692
|
+
interface IInternalQueueMailExecutorOpts {
|
|
693
|
+
identifier: string;
|
|
694
|
+
}
|
|
471
695
|
```
|
|
472
696
|
|
|
473
|
-
|
|
697
|
+
**Source:** [`common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/common/types.ts)
|
|
474
698
|
|
|
475
|
-
## Utility
|
|
476
|
-
|
|
477
|
-
### Type Utilities
|
|
699
|
+
## Utility functions
|
|
478
700
|
|
|
479
701
|
```typescript
|
|
480
|
-
//
|
|
702
|
+
// type.utility.ts
|
|
481
703
|
function isMailTransport(value: AnyType): value is IMailTransport;
|
|
482
|
-
|
|
483
|
-
// Check if a value is valid TMailOptions
|
|
484
704
|
function isValidMailOptions(options: AnyType): options is TMailOptions;
|
|
485
|
-
```
|
|
486
705
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
706
|
+
// verification.utility.ts
|
|
707
|
+
function getExpiryTime(minutes: number): Date; // Date `minutes` minutes from now
|
|
708
|
+
function getExpiryTimeInHours(hours: number): Date; // Date `hours` hours from now
|
|
709
|
+
```
|
|
490
710
|
|
|
491
|
-
|
|
711
|
+
- `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.
|
|
492
713
|
|
|
493
|
-
|
|
494
|
-
// Get a Date object `minutes` minutes in the future
|
|
495
|
-
function getExpiryTime(minutes: number): Date;
|
|
496
|
-
|
|
497
|
-
// Get a Date object `hours` hours in the future
|
|
498
|
-
function getExpiryTimeInHours(hours: number): Date;
|
|
499
|
-
```
|
|
714
|
+
**Source:** [`utilities/`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/components/mail/utilities)
|
|
500
715
|
|
|
501
|
-
## See
|
|
716
|
+
## See also
|
|
502
717
|
|
|
503
|
-
- [
|
|
504
|
-
- [Usage & Examples](./usage) --
|
|
505
|
-
- [Error Reference](./errors) --
|
|
718
|
+
- [Overview](./) -- quick start, imports, common configuration tasks
|
|
719
|
+
- [Usage & Examples](./usage) -- sending emails, templates, queue executors, verification generators
|
|
720
|
+
- [Error Reference](./errors) -- error codes and troubleshooting
|
|
721
|
+
- [Queue Helper](/extensions/helpers/queue/) -- `SequentialQueueHelper` and `BullMQHelper`
|
|
722
|
+
- [Redis Helper](/extensions/helpers/redis/) -- `RedisSingleHelper`, `IRedisSingleHelperOptions`
|