@t4h.framework/smtp 0.7.5 → 0.7.7
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/.ai/skills/framework-smtp/SKILL.md +56 -246
- package/CHANGELOG.md +31 -0
- package/README.md +26 -83
- package/dist/activities/SmtpSendEmailActivity.d.ts.map +1 -1
- package/dist/activities/SmtpSendEmailActivity.js +8 -11
- package/dist/activities/SmtpSendEmailActivity.js.map +1 -1
- package/package.json +4 -2
|
@@ -1,282 +1,92 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: framework-smtp
|
|
3
3
|
description: >-
|
|
4
|
-
Guides
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
and
|
|
8
|
-
|
|
9
|
-
or
|
|
4
|
+
Guides the use of @t4h.framework/smtp to send email from a T4H Framework
|
|
5
|
+
workflow: the Smtp client and its send() method, the default from, to/cc/bcc
|
|
6
|
+
recipients, HTML bodies, Binary attachments, SmtpSendEmailOutput.messageId,
|
|
7
|
+
and how to test with TestWorkflowEnvironment and mockClaim of
|
|
8
|
+
SmtpSendEmailClaim. Use when a workflow must send an email, a notification
|
|
9
|
+
or a report with an attachment, or when someone asks for nodemailer, an SMTP
|
|
10
|
+
transport or an smtp:// connection string in a workflow.
|
|
10
11
|
---
|
|
11
12
|
|
|
12
13
|
# @t4h.framework/smtp
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
## Send an email
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
1. Declare the connection string as a `Secret` in the `EnvDesign` in
|
|
18
|
+
`src/constants/environments.ts`. The URL (`smtp://user:pass@host:587`)
|
|
19
|
+
carries the password.
|
|
20
|
+
2. Create one `Smtp` client and call `send` in the workflow body.
|
|
17
21
|
|
|
18
22
|
```typescript
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
SmtpSendEmailActivity,
|
|
22
|
-
SmtpSendEmailClaim,
|
|
23
|
-
SmtpSendEmailOutput,
|
|
24
|
-
type SmtpOptions,
|
|
25
|
-
type SmtpSendEmailActivityOptions,
|
|
26
|
-
type SmtpSendEmailActivityInput,
|
|
27
|
-
type SmtpSendEmailActivityAttachment,
|
|
28
|
-
type ISmtpSendEmailOutput,
|
|
29
|
-
} from '@t4h.framework/smtp'
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Node `>=22`. See [README.md](../../../README.md) for a short overview (note: README may say `sendEmail` on the claim; the actual method is **`send`**).
|
|
33
|
-
|
|
34
|
-
---
|
|
35
|
-
|
|
36
|
-
## Architecture
|
|
37
|
-
|
|
38
|
-
`Workflow` → `Smtp.send` (`History.bind(SmtpSendEmailActivity)`) → `SmtpSendEmailActivity.toInput` / `run` → `SmtpSendEmailClaim.send` → `SmtpSendEmailOutput`.
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
┌─────────────┐ send() ┌────────────────────────┐ run() ┌─────────────────────┐
|
|
42
|
-
│ Smtp │ ──────────────► │ SmtpSendEmailActivity │ ────────────► │ SmtpSendEmailClaim │
|
|
43
|
-
│ (client) │ reconciler │ (SyncActivity) │ Claim.smtp │ (runtime impl) │
|
|
44
|
-
└─────────────┘ └────────────────────────┘ └─────────────────────┘
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
Never call nodemailer or raw SMTP sockets directly in workflow code if the step must be replayable — use `Smtp` / `SmtpSendEmailActivity`.
|
|
48
|
-
|
|
49
|
-
---
|
|
50
|
-
|
|
51
|
-
## SmtpSendEmailClaim (required for delivery)
|
|
52
|
-
|
|
53
|
-
Abstract dependency; the **runtime** must register a concrete implementation:
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
56
|
-
{ provide: SmtpSendEmailClaim, value: new MySmtpSendEmailClaimImpl() }
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
Contract (from source):
|
|
60
|
-
|
|
61
|
-
```typescript
|
|
62
|
-
abstract class SmtpSendEmailClaim {
|
|
63
|
-
abstract send(
|
|
64
|
-
input: SmtpSendEmailActivityInput,
|
|
65
|
-
): ISmtpSendEmailOutput | Promise<ISmtpSendEmailOutput>
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
type ISmtpSendEmailOutput = { messageId: string }
|
|
69
|
-
```
|
|
23
|
+
// src/constants/environments.ts
|
|
24
|
+
import { EnvDesign, Secret } from '@t4h.framework/core'
|
|
70
25
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
---
|
|
74
|
-
|
|
75
|
-
## Send options and payload
|
|
76
|
-
|
|
77
|
-
The exported `SmtpSendEmailActivity` — driven by `smtp.send` or directly via
|
|
78
|
-
`History.reconciler(SmtpSendEmailActivity, smtp, content)` — is replay-safe and
|
|
79
|
-
delegates to `SmtpSendEmailClaim.send`, returning a `SmtpSendEmailOutput`. Its
|
|
80
|
-
input shapes:
|
|
81
|
-
|
|
82
|
-
### SmtpSendEmailActivityOptions (what `smtp.send` accepts)
|
|
83
|
-
|
|
84
|
-
| Field | Type | Required | Notes |
|
|
85
|
-
|-------|------|----------|-------|
|
|
86
|
-
| `to` | `string \| readonly string[]` | yes | Normalized to `readonly string[]` in input |
|
|
87
|
-
| `subject` | `string` | yes | |
|
|
88
|
-
| `html` | `string` | yes | HTML body |
|
|
89
|
-
| `from` | `string?` | no | Overrides `smtp.options.from` |
|
|
90
|
-
| `cc` | `string \| readonly string[]?` | no | String → single-element array |
|
|
91
|
-
| `bcc` | `string \| readonly string[]?` | no | String → single-element array |
|
|
92
|
-
| `attachments` | `readonly SmtpSendEmailActivityAttachment[]?` | no | Passed through unchanged |
|
|
93
|
-
|
|
94
|
-
### SmtpSendEmailActivityInput (claim / activity payload)
|
|
95
|
-
|
|
96
|
-
| Field | Type | Notes |
|
|
97
|
-
|-------|------|-------|
|
|
98
|
-
| `connectionString` | `Secret` | From `smtp.connectionString`; the host reads the value when it sends |
|
|
99
|
-
| `from` | `string?` | `content.from ?? smtp.options.from` |
|
|
100
|
-
| `to` | `readonly string[]` | Always an array after `toInput` |
|
|
101
|
-
| `cc` | `readonly string[]?` | Omitted if not set |
|
|
102
|
-
| `bcc` | `readonly string[]?` | Omitted if not set |
|
|
103
|
-
| `subject` | `string` | |
|
|
104
|
-
| `html` | `string` | |
|
|
105
|
-
| `attachments` | `readonly SmtpSendEmailActivityAttachment[]?` | |
|
|
106
|
-
|
|
107
|
-
### Attachments
|
|
108
|
-
|
|
109
|
-
```typescript
|
|
110
|
-
import type { Binary } from '@t4h.framework/core'
|
|
111
|
-
|
|
112
|
-
type SmtpSendEmailActivityAttachment = {
|
|
113
|
-
filename: string
|
|
114
|
-
content: Binary
|
|
115
|
-
}
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
Use framework `Binary` instances (e.g. from an HTTP `response.body`).
|
|
119
|
-
|
|
120
|
-
---
|
|
121
|
-
|
|
122
|
-
## Smtp client
|
|
123
|
-
|
|
124
|
-
High-level API: holds `connectionString` (a `Secret` from the `EnvDesign`, since the URL `smtp://user:pass@host:587` carries the password — a string is a `tsc` error) and optional default `from`, extends `SerializableClass` for workflow persistence.
|
|
125
|
-
|
|
126
|
-
```typescript
|
|
127
|
-
const smtp = new Smtp(environments.SMTP_URL, {
|
|
128
|
-
from: 'noreply@example.com',
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
const result = await smtp.send({
|
|
132
|
-
to: 'recipient@example.com',
|
|
133
|
-
subject: 'Hello',
|
|
134
|
-
html: '<p>Hello world</p>',
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
result.messageId // string — via SmtpSendEmailOutput
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
- `send(options)` returns a promise of `SmtpSendEmailOutput` when history/reconciler completes (same pattern as `http` client).
|
|
141
|
-
- Default `from`: set on `Smtp` constructor options; per-send `from` in `send()` wins over `smtp.options.from`.
|
|
142
|
-
- `toSerializable()` / `fromSerializable()` restore `connectionString` and `options` (see `Smtp.spec.ts`).
|
|
143
|
-
|
|
144
|
-
### Multiple recipients, cc, bcc
|
|
145
|
-
|
|
146
|
-
```typescript
|
|
147
|
-
await smtp.send({
|
|
148
|
-
to: ['alice@example.com', 'bob@example.com'],
|
|
149
|
-
cc: 'manager@example.com',
|
|
150
|
-
bcc: ['audit@example.com'],
|
|
151
|
-
subject: 'Team Update',
|
|
152
|
-
html: '<p>Weekly report.</p>',
|
|
153
|
-
})
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
### Using the exported activity directly via `History.reconciler`
|
|
157
|
-
|
|
158
|
-
Prefer `smtp.send` in workflows. When you need the exported activity directly,
|
|
159
|
-
drive the **shipped** `SmtpSendEmailActivity` through `History.reconciler` — a
|
|
160
|
-
ready, replay-safe helper. Never define or extend your own activity:
|
|
161
|
-
|
|
162
|
-
```typescript
|
|
163
|
-
import { History } from '@t4h.framework/core'
|
|
164
|
-
import { Smtp, SmtpSendEmailActivity } from '@t4h.framework/smtp'
|
|
165
|
-
|
|
166
|
-
const smtp = new Smtp(environments.SMTP_URL) // Secret
|
|
167
|
-
const result = await History.reconciler(SmtpSendEmailActivity, smtp, {
|
|
168
|
-
to: 'user@example.com',
|
|
169
|
-
subject: 'Subject',
|
|
170
|
-
html: '<p>Body</p>',
|
|
26
|
+
export const environments = EnvDesign({
|
|
27
|
+
SMTP_URL: { type: Secret },
|
|
171
28
|
})
|
|
172
29
|
```
|
|
173
30
|
|
|
174
|
-
Runs inside a workflow (the runtime, or `TestWorkflowEnvironment` in tests, provides the reconciler context).
|
|
175
|
-
|
|
176
|
-
---
|
|
177
|
-
|
|
178
|
-
## SmtpSendEmailOutput
|
|
179
|
-
|
|
180
31
|
```typescript
|
|
181
|
-
class SmtpSendEmailOutput {
|
|
182
|
-
constructor(public readonly raw: ISmtpSendEmailOutput)
|
|
183
|
-
get messageId(): string
|
|
184
|
-
}
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
Activity and `Smtp.send` surface this type, not the raw `{ messageId }` object.
|
|
188
|
-
|
|
189
|
-
---
|
|
190
|
-
|
|
191
|
-
## Workflow example
|
|
192
|
-
|
|
193
|
-
```typescript
|
|
194
|
-
import { App, EnvDesign, Secret, Workflow } from '@t4h.framework/core'
|
|
195
|
-
import { http } from '@t4h.framework/http'
|
|
196
32
|
import { Smtp } from '@t4h.framework/smtp'
|
|
197
|
-
import Type from 'typebox'
|
|
198
|
-
|
|
199
|
-
const env = EnvDesign({
|
|
200
|
-
SMTP_CONNECTION_STRING: { type: Secret }, // carries the password
|
|
201
|
-
SMTP_FROM: { type: String },
|
|
202
|
-
})
|
|
203
33
|
|
|
204
|
-
|
|
34
|
+
import { environments } from '../constants/environments.js'
|
|
205
35
|
|
|
206
|
-
|
|
207
|
-
id: 'email',
|
|
208
|
-
workflows: [
|
|
209
|
-
new Workflow({ id: 'send-email', schema: Type.String() }, async input => {
|
|
210
|
-
const response = await http.get('https://picsum.photos/300/200')
|
|
36
|
+
const smtp = new Smtp(environments.SMTP_URL, { from: 'noreply@example.com' })
|
|
211
37
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
attachments: [{ filename: 'image.png', content: response.body.binary }],
|
|
218
|
-
})
|
|
219
|
-
}),
|
|
220
|
-
],
|
|
38
|
+
const result = await smtp.send({
|
|
39
|
+
to: input.email,
|
|
40
|
+
subject: 'Report',
|
|
41
|
+
html: '<p>See attached.</p>',
|
|
42
|
+
attachments: [{ filename: 'report.pdf', content: reportBinary }],
|
|
221
43
|
})
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
Connection strings are opaque to the package; dev implementation expects URL form with credentials in the authority (e.g. `smtp://user:pass@host:587`, `smtps://...`).
|
|
225
|
-
|
|
226
|
-
---
|
|
227
44
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
Run package tests: `yarn test` in `packages/smtp` (Vitest).
|
|
45
|
+
result.messageId
|
|
46
|
+
```
|
|
231
47
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
48
|
+
## Traps
|
|
49
|
+
|
|
50
|
+
- The method is `send`, on the client and on `SmtpSendEmailClaim`. There is no
|
|
51
|
+
`sendEmail`.
|
|
52
|
+
- `new Smtp(...)` takes a `Secret`. A string literal or a `String` env entry is
|
|
53
|
+
a type error.
|
|
54
|
+
- The body field is `html`. There is no `text` field.
|
|
55
|
+
- An attachment `content` is a `Binary` (from `sftp.get`, an HTTP body, `write`
|
|
56
|
+
of `@t4h.framework/fs`). Never build a `Binary` by hand; see the
|
|
57
|
+
`framework-fs` skill.
|
|
58
|
+
- A `from` in `send` wins over the client default. Without both, `from` is
|
|
59
|
+
`undefined` and the claim decides.
|
|
60
|
+
- `@t4h.framework/core/no-io-in-workflow` rejects `nodemailer` in a workflow.
|
|
61
|
+
Do not call an SMTP library; use `Smtp`.
|
|
62
|
+
- Do not `new` or `extend` `SmtpSendEmailActivity`
|
|
63
|
+
(`@t4h.framework/core/no-framework-activity-in-app`). Use `smtp.send`.
|
|
64
|
+
- The runtime must provide `SmtpSendEmailClaim`.
|
|
65
|
+
|
|
66
|
+
## Test
|
|
67
|
+
|
|
68
|
+
Mock `SmtpSendEmailClaim` with its `send` method. The mock receives the
|
|
69
|
+
normalized input: `to`, `cc` and `bcc` are always arrays.
|
|
236
70
|
|
|
237
71
|
```typescript
|
|
238
72
|
import { TestWorkflowEnvironment, mockClaim } from '@t4h.framework/core/testing'
|
|
239
73
|
import { SmtpSendEmailClaim } from '@t4h.framework/smtp'
|
|
240
74
|
|
|
241
|
-
const
|
|
75
|
+
const send = vi.fn().mockResolvedValue({ messageId: 'msg-123' })
|
|
242
76
|
|
|
243
77
|
const env = TestWorkflowEnvironment.create({ now: 0 })
|
|
244
78
|
|
|
245
79
|
await env.execute(workflow, input, {
|
|
246
|
-
claims: [mockClaim(SmtpSendEmailClaim, { send
|
|
247
|
-
env: {
|
|
80
|
+
claims: [mockClaim(SmtpSendEmailClaim, { send })],
|
|
81
|
+
env: { SMTP_URL: 'smtp://user:pass@smtp.example.com:587' },
|
|
248
82
|
})
|
|
249
83
|
|
|
250
|
-
|
|
251
|
-
expect(mockSend.mock.calls[0][0]).toMatchObject({
|
|
252
|
-
to: ['recipient@example.com'],
|
|
253
|
-
subject: 'Test',
|
|
254
|
-
html: '<p>Hello</p>',
|
|
255
|
-
})
|
|
84
|
+
expect(send.mock.calls[0][0]).toMatchObject({ to: ['recipient@example.com'] })
|
|
256
85
|
```
|
|
257
86
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
## Implementing SmtpSendEmailClaim
|
|
261
|
-
|
|
262
|
-
1. Subclass `SmtpSendEmailClaim` and implement **`send(input: SmtpSendEmailActivityInput)`**.
|
|
263
|
-
2. Read `connectionString`, `from`, `to`, `cc`, `bcc`, `subject`, `html`, `attachments` from `input` (arrays already normalized).
|
|
264
|
-
3. Return `{ messageId: string }` (or a Promise thereof). `messageId` is what workflows expose via `SmtpSendEmailOutput.messageId`.
|
|
265
|
-
4. For attachments, consume `Binary` via `.stream()`, `.contentType`, etc.
|
|
266
|
-
5. Register: `{ provide: SmtpSendEmailClaim, value: new MyClaim() }`.
|
|
267
|
-
|
|
268
|
-
---
|
|
269
|
-
|
|
270
|
-
## Checklist for agents
|
|
271
|
-
|
|
272
|
-
1. In workflows, use **`Smtp` + `smtp.send()`** (or `History.reconciler` with the exported `SmtpSendEmailActivity`) — never define or extend your own activity.
|
|
273
|
-
2. Ensure runtime provides **`SmtpSendEmailClaim`** (or a mock in tests).
|
|
274
|
-
3. Use **`html`** for the message body; required fields on send options are **`to`**, **`subject`**, **`html`**.
|
|
275
|
-
4. Set default **`from`** on `new Smtp(connectionString, { from })` or per-send; per-send wins.
|
|
276
|
-
5. Pass **`Binary`** for attachment `content`, not raw `Buffer` in activity options.
|
|
277
|
-
6. Expect **`SmtpSendEmailOutput`** from `send`, not a plain object — use `.messageId`.
|
|
278
|
-
7. Test workflows with **`TestWorkflowEnvironment`** + **`mockClaim(SmtpSendEmailClaim, { send })`**; assert on the mock's `mock.calls`.
|
|
279
|
-
8. Do not document or import APIs not exported from `@t4h.framework/smtp`.
|
|
280
|
-
9. For core workflow/reconciler concepts, use the **framework-core** skill (`@t4h.framework/core`).
|
|
87
|
+
## Reference
|
|
281
88
|
|
|
282
|
-
|
|
89
|
+
- Guide: https://tech4humans-brasil.github.io/framework/smtp/
|
|
90
|
+
- Secrets: https://tech4humans-brasil.github.io/framework/core/environment
|
|
91
|
+
- Workflow testing: https://tech4humans-brasil.github.io/framework/core/testing
|
|
92
|
+
- Types: `node_modules/@t4h.framework/smtp/dist/smtp.d.ts`
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# @t4h.framework/smtp
|
|
2
2
|
|
|
3
|
+
## 0.7.7
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#257](https://github.com/tech4humans-brasil/framework/pull/257) [`586bfb4`](https://github.com/tech4humans-brasil/framework/commit/586bfb4663f4a478527ee0c8db360603f034d615) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - The smtp README is now short and links to the site. The docs page now has the testing and runtime sections, and the README no longer names a `sendEmail` claim method: the method is `send`.
|
|
8
|
+
|
|
9
|
+
## 0.7.6
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#241](https://github.com/tech4humans-brasil/framework/pull/241) [`f6b8454`](https://github.com/tech4humans-brasil/framework/commit/f6b845484d22e7c8e7240fe2e113f1cdea7c8ce3) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - Adds seven rules to `@t4h.framework/core/eslint` and turns them on in `configs.recommended`:
|
|
14
|
+
- `no-io-in-workflow` reports `fetch` and calls to network, file, database and mail modules in a workflow and in the `constructor`, `toInput` and `toOutput` of an activity. It also reports `zlib` and `stream` calls there, because they work on the content in memory on each replay.
|
|
15
|
+
- `no-binary-subclass` reports a class that extends `Binary`.
|
|
16
|
+
- `no-framework-activity-in-app` reports `new` and `extends` on an activity of a framework package other than the core.
|
|
17
|
+
- `no-testing-import-in-source` reports an import of a `@t4h.framework/<package>/testing` entry out of a test file.
|
|
18
|
+
- `require-workflow-id` reports a `Workflow` with no `id`.
|
|
19
|
+
- `no-secret-to-string` reports a `Secret` that becomes a string.
|
|
20
|
+
- `no-literal-secret-in-authorization` reports a string literal in the `secret` or `clientSecret` field of an `Authorization`.
|
|
21
|
+
|
|
22
|
+
The package skills now name these rules in place of the text that the rules enforce. The docs and skills give each `Workflow` an `id`, read the HMAC secret from `EnvDesign`, and do not document a custom HTTP activity.
|
|
23
|
+
|
|
24
|
+
- [#243](https://github.com/tech4humans-brasil/framework/pull/243) [`0ffd663`](https://github.com/tech4humans-brasil/framework/commit/0ffd663ce8369edd7b54206cb3216568ad1de471) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - The README examples read each value from `environments`, an `EnvDesign` in `src/constants/environments.ts`, and pass each credential as a `Secret`. They no longer use `process.env`, the raw `env` from the core or a literal in a `Secret` field. The jwks README describes the real context fields (`signature`, `rawPayload`) and says that a token that `jwt.verify` rejects throws.
|
|
25
|
+
|
|
26
|
+
- [#240](https://github.com/tech4humans-brasil/framework/pull/240) [`0606115`](https://github.com/tech4humans-brasil/framework/commit/0606115e2e0baa994e9f0ed3559ee54ecac88e44) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - The skills do not repeat a rule that a lint rule, `tsc` or a runtime error already enforces. Each such rule is now one line that names the gate. The examples that broke a gate (`process.env`, `as any`, an inline secret, an `EnvDesign` in a workflow file, `ref.output` before `wait()`) are correct now.
|
|
27
|
+
|
|
28
|
+
- [#228](https://github.com/tech4humans-brasil/framework/pull/228) [`d29e1ab`](https://github.com/tech4humans-brasil/framework/commit/d29e1ab29a039b13c3e4b075472bd615566ae75e) Thanks [@gusteycamargo](https://github.com/gusteycamargo)! - Uses exact types and `unknown` in place of `any`, and checks external data with guards.
|
|
29
|
+
- harness: the fix-issues prompt lists each sensor as `- checkTypes: PASS`. Before, it printed `- 0: [object Object]`.
|
|
30
|
+
- hmac: an algorithm name such as `constructor` throws `Unsupported algorithm`.
|
|
31
|
+
- oidc: a discovery response with no `introspection_endpoint`, or a null introspection body, throws a clear error.
|
|
32
|
+
- `HttpRequestActivity.toOutput` takes `HttpRequestActivityOutput` in place of `any`.
|
|
33
|
+
|
|
3
34
|
## 0.7.5
|
|
4
35
|
|
|
5
36
|
## 0.7.4
|
package/README.md
CHANGED
|
@@ -1,106 +1,49 @@
|
|
|
1
1
|
# @t4h.framework/smtp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Sends email from a T4H Framework workflow. Each send is a durable step, so a
|
|
4
|
+
replay does not send the message again.
|
|
4
5
|
|
|
5
|
-
##
|
|
6
|
+
## Install
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Abstract dependency for sending emails via SMTP. The runtime must provide a concrete implementation.
|
|
10
|
-
|
|
11
|
-
```typescript
|
|
12
|
-
import { SmtpSendEmailClaim } from '@t4h.framework/smtp'
|
|
13
|
-
|
|
14
|
-
// the runtime provides this:
|
|
15
|
-
// { provide: SmtpSendEmailClaim, value: new MySmtpSendEmailClaimImpl() }
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
### Interface
|
|
19
|
-
|
|
20
|
-
```typescript
|
|
21
|
-
abstract class SmtpSendEmailClaim {
|
|
22
|
-
public abstract sendEmail(
|
|
23
|
-
input: SmtpSendEmailActivityInput,
|
|
24
|
-
): ISmtpSendEmailOutput | Promise<ISmtpSendEmailOutput>
|
|
25
|
-
}
|
|
8
|
+
```bash
|
|
9
|
+
npm install @t4h.framework/smtp
|
|
26
10
|
```
|
|
27
11
|
|
|
28
|
-
|
|
12
|
+
Peer dependency: `@t4h.framework/core`.
|
|
29
13
|
|
|
30
|
-
|
|
14
|
+
## Usage
|
|
31
15
|
|
|
32
|
-
|
|
16
|
+
The connection string carries the password, so declare it with
|
|
17
|
+
`type: Secret` in the `EnvDesign` in `src/constants/environments.ts`:
|
|
33
18
|
|
|
34
19
|
```typescript
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
## Smtp
|
|
39
|
-
|
|
40
|
-
High-level SMTP client that wraps `SmtpSendEmailActivity` behind a simple API. Instantiated with a connection string and optional defaults.
|
|
41
|
-
|
|
42
|
-
### Basic usage
|
|
43
|
-
|
|
44
|
-
```typescript
|
|
45
|
-
import { Smtp } from '@t4h.framework/smtp'
|
|
46
|
-
|
|
47
|
-
const smtp = new Smtp('smtp://user:pass@smtp.example.com:587')
|
|
20
|
+
// src/constants/environments.ts
|
|
21
|
+
import { EnvDesign, Secret } from '@t4h.framework/core'
|
|
48
22
|
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
to: 'recipient@example.com',
|
|
52
|
-
subject: 'Hello',
|
|
53
|
-
html: '<p>Hello world</p>',
|
|
23
|
+
export const environments = EnvDesign({
|
|
24
|
+
SMTP_URL: { type: Secret },
|
|
54
25
|
})
|
|
55
|
-
|
|
56
|
-
result.messageId // string
|
|
57
26
|
```
|
|
58
27
|
|
|
59
|
-
### Default sender
|
|
60
|
-
|
|
61
28
|
```typescript
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
const result = await smtp.send({
|
|
67
|
-
to: 'recipient@example.com',
|
|
68
|
-
subject: 'Hello',
|
|
69
|
-
html: '<p>Hello world</p>',
|
|
70
|
-
})
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### Multiple recipients, cc, and bcc
|
|
29
|
+
import { Workflow } from '@t4h.framework/core'
|
|
30
|
+
import { Smtp } from '@t4h.framework/smtp'
|
|
74
31
|
|
|
75
|
-
|
|
76
|
-
const result = await smtp.send({
|
|
77
|
-
to: ['alice@example.com', 'bob@example.com'],
|
|
78
|
-
cc: 'manager@example.com',
|
|
79
|
-
bcc: ['audit@example.com'],
|
|
80
|
-
subject: 'Team Update',
|
|
81
|
-
html: '<p>Weekly report attached.</p>',
|
|
82
|
-
})
|
|
83
|
-
```
|
|
32
|
+
import { environments } from './constants/environments.js'
|
|
84
33
|
|
|
85
|
-
|
|
34
|
+
const smtp = new Smtp(environments.SMTP_URL, { from: 'noreply@example.com' })
|
|
86
35
|
|
|
87
|
-
|
|
88
|
-
|
|
36
|
+
const notify = new Workflow({ id: 'notify' }, async () => {
|
|
37
|
+
const result = await smtp.send({
|
|
38
|
+
to: 'recipient@example.com',
|
|
39
|
+
subject: 'Hello',
|
|
40
|
+
html: '<p>Hello world</p>',
|
|
41
|
+
})
|
|
89
42
|
|
|
90
|
-
|
|
91
|
-
to: 'recipient@example.com',
|
|
92
|
-
subject: 'Report',
|
|
93
|
-
html: '<p>See attached.</p>',
|
|
94
|
-
attachments: [attachment],
|
|
43
|
+
return { messageId: result.messageId }
|
|
95
44
|
})
|
|
96
45
|
```
|
|
97
46
|
|
|
98
|
-
##
|
|
47
|
+
## Documentation
|
|
99
48
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
```typescript
|
|
103
|
-
const result = await smtp.send({ ... })
|
|
104
|
-
|
|
105
|
-
result.messageId // string — the message ID returned by the SMTP server
|
|
106
|
-
```
|
|
49
|
+
https://tech4humans-brasil.github.io/framework/smtp/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SmtpSendEmailActivity.d.ts","sourceRoot":"","sources":["../../src/activities/SmtpSendEmailActivity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAS,YAAY,EAAE,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAE9E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAA;AAE7C,OAAO,EACL,mBAAmB,EACnB,KAAK,oBAAoB,EAC1B,MAAM,kCAAkC,CAAA;AAEzC,MAAM,MAAM,+BAA+B,GAAG;IAC5C,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,4BAA4B,GAAG;IACzC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,EAAE,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAA;IAC9B,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAA;IAC/B,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAA;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,SAAS,+BAA+B,EAAE,CAAA;CACzD,CAAA;AAED,MAAM,MAAM,0BAA0B,GAAG;IACvC,gBAAgB,EAAE,MAAM,CAAA;IACxB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,SAAS,MAAM,EAAE,CAAA;IACrB,EAAE,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACtB,GAAG,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,WAAW,CAAC,EAAE,SAAS,+BAA+B,EAAE,CAAA;CACzD,CAAA;
|
|
1
|
+
{"version":3,"file":"SmtpSendEmailActivity.d.ts","sourceRoot":"","sources":["../../src/activities/SmtpSendEmailActivity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAS,YAAY,EAAE,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAE9E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAA;AAE7C,OAAO,EACL,mBAAmB,EACnB,KAAK,oBAAoB,EAC1B,MAAM,kCAAkC,CAAA;AAEzC,MAAM,MAAM,+BAA+B,GAAG;IAC5C,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,4BAA4B,GAAG;IACzC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,EAAE,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAA;IAC9B,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAA;IAC/B,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAA;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,SAAS,+BAA+B,EAAE,CAAA;CACzD,CAAA;AAED,MAAM,MAAM,0BAA0B,GAAG;IACvC,gBAAgB,EAAE,MAAM,CAAA;IACxB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,SAAS,MAAM,EAAE,CAAA;IACrB,EAAE,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACtB,GAAG,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,WAAW,CAAC,EAAE,SAAS,+BAA+B,EAAE,CAAA;CACzD,CAAA;AAUD,QAAA,MAAM,+BAA+B,eAEpC,CAAA;AAED,qBAAa,qBAAsB,SAAQ,YAAY,CACrD,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,4BAA4B,CAAC,EAC5D,0BAA0B,EAC1B,oBAAoB,EACpB,mBAAmB,CACpB;IACC,OAAO,CAAC,QAAQ,CAAC,CAAC,+BAA+B,CAAC,CAEhD;IAEK,OAAO,CACZ,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,4BAA4B,GACpC,0BAA0B;IAqBtB,GAAG,CAAC,OAAO,EAAE,0BAA0B;IAIvC,QAAQ,CAAC,MAAM,EAAE,oBAAoB;CAG7C"}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { Binary, Claim, SyncActivity } from '@t4h.framework/core';
|
|
2
2
|
import { SmtpSendEmailClaim } from '../models/SmtpSendEmailClaim.js';
|
|
3
3
|
import { SmtpSendEmailOutput, } from '../models/SmtpSendEmailOutput.js';
|
|
4
|
+
function toOptionalRecipients(recipients) {
|
|
5
|
+
if (!recipients)
|
|
6
|
+
return undefined;
|
|
7
|
+
return typeof recipients === 'string' ? [recipients] : recipients;
|
|
8
|
+
}
|
|
4
9
|
const SMTP_SEND_EMAIL_ACTIVITY_CLAIMS = Symbol('smtp-send-email-activity-claims');
|
|
5
10
|
export class SmtpSendEmailActivity extends SyncActivity {
|
|
6
11
|
[SMTP_SEND_EMAIL_ACTIVITY_CLAIMS] = new Claim({
|
|
@@ -8,17 +13,9 @@ export class SmtpSendEmailActivity extends SyncActivity {
|
|
|
8
13
|
});
|
|
9
14
|
toInput(smtp, content) {
|
|
10
15
|
const from = content.from ?? smtp.options.from;
|
|
11
|
-
const to =
|
|
12
|
-
const cc = content.cc
|
|
13
|
-
|
|
14
|
-
? content.cc
|
|
15
|
-
: [content.cc]
|
|
16
|
-
: undefined;
|
|
17
|
-
const bcc = content.bcc
|
|
18
|
-
? Array.isArray(content.bcc)
|
|
19
|
-
? content.bcc
|
|
20
|
-
: [content.bcc]
|
|
21
|
-
: undefined;
|
|
16
|
+
const to = typeof content.to === 'string' ? [content.to] : content.to;
|
|
17
|
+
const cc = toOptionalRecipients(content.cc);
|
|
18
|
+
const bcc = toOptionalRecipients(content.bcc);
|
|
22
19
|
return {
|
|
23
20
|
connectionString: smtp.connectionString,
|
|
24
21
|
from,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SmtpSendEmailActivity.js","sourceRoot":"","sources":["../../src/activities/SmtpSendEmailActivity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAe,MAAM,qBAAqB,CAAA;AAG9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAA;AACpE,OAAO,EACL,mBAAmB,GAEpB,MAAM,kCAAkC,CAAA;AA4BzC,MAAM,+BAA+B,GAAG,MAAM,CAC5C,iCAAiC,CAClC,CAAA;AAED,MAAM,OAAO,qBAAsB,SAAQ,YAK1C;IACkB,CAAC,+BAA+B,CAAC,GAAG,IAAI,KAAK,CAAC;QAC7D,IAAI,EAAE,kBAAkB;KACzB,CAAC,CAAA;IAEK,OAAO,CACZ,IAAU,EACV,OAAqC;QAErC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAA;QAE9C,MAAM,EAAE,GAAG,
|
|
1
|
+
{"version":3,"file":"SmtpSendEmailActivity.js","sourceRoot":"","sources":["../../src/activities/SmtpSendEmailActivity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAe,MAAM,qBAAqB,CAAA;AAG9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAA;AACpE,OAAO,EACL,mBAAmB,GAEpB,MAAM,kCAAkC,CAAA;AA4BzC,SAAS,oBAAoB,CAC3B,UAAkD;IAElD,IAAI,CAAC,UAAU;QAAE,OAAO,SAAS,CAAA;IAEjC,OAAO,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAA;AACnE,CAAC;AAED,MAAM,+BAA+B,GAAG,MAAM,CAC5C,iCAAiC,CAClC,CAAA;AAED,MAAM,OAAO,qBAAsB,SAAQ,YAK1C;IACkB,CAAC,+BAA+B,CAAC,GAAG,IAAI,KAAK,CAAC;QAC7D,IAAI,EAAE,kBAAkB;KACzB,CAAC,CAAA;IAEK,OAAO,CACZ,IAAU,EACV,OAAqC;QAErC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAA;QAE9C,MAAM,EAAE,GAAG,OAAO,OAAO,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAA;QAErE,MAAM,EAAE,GAAG,oBAAoB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QAE3C,MAAM,GAAG,GAAG,oBAAoB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAE7C,OAAO;YACL,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;YACvC,IAAI;YACJ,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,EAAE;YACF,EAAE;YACF,GAAG;YACH,WAAW,EAAE,OAAO,CAAC,WAAW;SACjC,CAAA;IACH,CAAC;IAEM,GAAG,CAAC,OAAmC;QAC5C,OAAO,IAAI,CAAC,+BAA+B,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACjE,CAAC;IAEM,QAAQ,CAAC,MAA4B;QAC1C,OAAO,IAAI,mBAAmB,CAAC,MAAM,CAAC,CAAA;IACxC,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@t4h.framework/smtp",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.7",
|
|
4
4
|
"description": "SMTP module for the T4H Framework",
|
|
5
5
|
"homepage": "https://github.com/tech4humans-brasil/framework/tree/main/packages/smtp",
|
|
6
6
|
"bugs": "https://github.com/tech4humans-brasil/framework/issues",
|
|
@@ -26,12 +26,14 @@
|
|
|
26
26
|
"scripts": {
|
|
27
27
|
"build": "tsc --project tsconfig.build.json",
|
|
28
28
|
"check-types": "tsc --noEmit",
|
|
29
|
+
"check-package": "publint --strict && attw --pack . --profile esm-only",
|
|
30
|
+
"lint": "eslint . --max-warnings 0",
|
|
29
31
|
"prepublishOnly": "yarn build",
|
|
30
32
|
"test": "vitest run",
|
|
31
33
|
"test:watch": "vitest"
|
|
32
34
|
},
|
|
33
35
|
"devDependencies": {
|
|
34
|
-
"@t4h.framework/core": "^0.
|
|
36
|
+
"@t4h.framework/core": "^0.21.0",
|
|
35
37
|
"typescript": "^5.9.3",
|
|
36
38
|
"vitest": "^4.0.18"
|
|
37
39
|
},
|