@wtfalch/mailer 0.2.0 → 0.4.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 +9 -17
- package/dist/header-safe.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/layout.d.ts +1 -6
- package/dist/layout.js +2 -12
- package/dist/templates/account.js +2 -1
- package/dist/templates/invitation.js +2 -1
- package/dist/transport.d.ts +8 -61
- package/dist/transport.js +6 -57
- package/dist/types.d.ts +4 -6
- package/dist/types.js +1 -1
- package/package.json +4 -10
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @wtfalch/mailer
|
|
2
2
|
|
|
3
3
|
One `send(template, to, data)` call for the estate's transactional email,
|
|
4
|
-
over
|
|
4
|
+
over Lettermint, with a dev transport that writes to disk instead of sending.
|
|
5
5
|
|
|
6
6
|
## Install
|
|
7
7
|
|
|
@@ -28,9 +28,9 @@ await send({
|
|
|
28
28
|
brand: { name: 'Acme' }, // optional
|
|
29
29
|
},
|
|
30
30
|
from: {
|
|
31
|
-
fromAddress: 'no-reply@acme.
|
|
31
|
+
fromAddress: 'no-reply@acme.example.com',
|
|
32
32
|
fromName: 'Acme', // optional
|
|
33
|
-
replyTo: 'support@acme.
|
|
33
|
+
replyTo: 'support@acme.example.com', // optional
|
|
34
34
|
locale: 'nb', // optional, defaults to 'en' -- picks which language's copy renders
|
|
35
35
|
},
|
|
36
36
|
attachments: [ // optional
|
|
@@ -45,9 +45,9 @@ await send({
|
|
|
45
45
|
missing transport fails loudly instead of silently no-op'ing or making a
|
|
46
46
|
live call with no key.
|
|
47
47
|
|
|
48
|
-
A held key: however you resolve `apiKey` for `
|
|
48
|
+
A held key: however you resolve `apiKey` for `createLettermintTransport`,
|
|
49
49
|
mailer never fetches or stores it. In the estate, the caller resolves the
|
|
50
|
-
org's
|
|
50
|
+
org's Lettermint key as a held key from `@wtfalch/keys` and passes it in on
|
|
51
51
|
every call.
|
|
52
52
|
|
|
53
53
|
### Locale
|
|
@@ -60,22 +60,14 @@ already uses (`WORDS[sender.locale] ?? WORDS.en`).
|
|
|
60
60
|
|
|
61
61
|
### Transports
|
|
62
62
|
|
|
63
|
-
A held key: however you resolve `apiKey` for
|
|
63
|
+
A held key: however you resolve `apiKey` for the transport below, mailer
|
|
64
64
|
never fetches or stores it. In the estate, the caller resolves the org's
|
|
65
|
-
|
|
65
|
+
Lettermint key as a held key from `@wtfalch/keys` and passes it
|
|
66
66
|
in on every call.
|
|
67
67
|
|
|
68
68
|
- `createDevTransport({ dir })` -- writes each message to
|
|
69
69
|
`<dir>/<timestamp>-<id>.json` (default `dir`: `.mailer/dev`). Never
|
|
70
70
|
opens a network connection.
|
|
71
|
-
- `createResendTransport({ apiKey })` -- sends over Resend. Needs either
|
|
72
|
-
`apiKey` (a plain string -- resolve it yourself, e.g. from
|
|
73
|
-
`@wtfalch/keys`) or `client`, a minimal structural `ResendLike` for
|
|
74
|
-
injecting a fake in tests. `resend` is an optional peer dependency
|
|
75
|
-
(`^4.1.0`) -- install it yourself if you use this transport with `apiKey`;
|
|
76
|
-
not needed if you always inject `client`, or if you only use
|
|
77
|
-
`createLettermintTransport` below. Attachments passed to `send()` are
|
|
78
|
-
forwarded as Resend's own `{ filename, content, content_type }` shape.
|
|
79
71
|
- `createLettermintTransport({ apiKey })` -- sends over Lettermint's
|
|
80
72
|
Sending API (EU-hosted). Needs either `apiKey` (the org's Lettermint
|
|
81
73
|
Project API token, `lm_...` -- resolve it yourself, e.g. from
|
|
@@ -126,7 +118,7 @@ await send({
|
|
|
126
118
|
template: 'verify',
|
|
127
119
|
to: 'person@example.com',
|
|
128
120
|
data: { productName: 'Acme', url: 'https://app.example.com/verify/abc', code: '482913' },
|
|
129
|
-
from: { fromAddress: 'no-reply@acme.
|
|
121
|
+
from: { fromAddress: 'no-reply@acme.example.com', locale: 'nb' },
|
|
130
122
|
});
|
|
131
123
|
```
|
|
132
124
|
|
|
@@ -142,5 +134,5 @@ From `packages/mailer`:
|
|
|
142
134
|
pnpm test
|
|
143
135
|
```
|
|
144
136
|
|
|
145
|
-
Tests run against the dev transport and a fake `
|
|
137
|
+
Tests run against the dev transport and a fake `LettermintLike` client --
|
|
146
138
|
never a live key, never the network.
|
package/dist/header-safe.js
CHANGED
|
@@ -7,7 +7,7 @@ function isControlCharCode(code) {
|
|
|
7
7
|
* True if `value` contains a CR, LF or any other control character. Checked
|
|
8
8
|
* by character code rather than a regex, since a header-bound field is
|
|
9
9
|
* built in part from caller-supplied strings (an inviter's name, an org's
|
|
10
|
-
* name) and this package must not depend on
|
|
10
|
+
* name) and this package must not depend on the mail provider's own defence against
|
|
11
11
|
* header injection to reject them (see issue #3).
|
|
12
12
|
*/
|
|
13
13
|
function hasControlCharacter(value) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type { Attachment, Brand, Locale, MailMessage, SenderIdentity, SendResult, } from './types.js';
|
|
2
2
|
export { DEFAULT_LOCALE, MailerConfigError } from './types.js';
|
|
3
|
-
export type { DevTransportOptions, LettermintAttachment, LettermintFetchLike, LettermintLike, LettermintSendPayload, LettermintTransportOptions, OutgoingMail,
|
|
4
|
-
export { createDevTransport, createLettermintTransport,
|
|
3
|
+
export type { DevTransportOptions, LettermintAttachment, LettermintFetchLike, LettermintLike, LettermintSendPayload, LettermintTransportOptions, OutgoingMail, Transport, } from './transport.js';
|
|
4
|
+
export { createDevTransport, createLettermintTransport, defaultTransport, LettermintDomainRefusedError, } from './transport.js';
|
|
5
5
|
export type { SendOptions } from './send.js';
|
|
6
6
|
export { send } from './send.js';
|
|
7
7
|
export type { TemplateData, TemplateName } from './templates/index.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { DEFAULT_LOCALE, MailerConfigError } from './types.js';
|
|
2
|
-
export { createDevTransport, createLettermintTransport,
|
|
2
|
+
export { createDevTransport, createLettermintTransport, defaultTransport, LettermintDomainRefusedError, } from './transport.js';
|
|
3
3
|
export { send } from './send.js';
|
|
4
4
|
export { templates } from './templates/index.js';
|
|
5
5
|
export { renderInvitation } from './templates/invitation.js';
|
package/dist/layout.d.ts
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
import type { Brand, MailMessage } from './types.js';
|
|
2
|
-
/**
|
|
3
|
-
* The one rule. Ampersand first, or it double-escapes everything after it.
|
|
4
|
-
* Same rule and reason as app-template's `src/lib/marketing/render.ts`.
|
|
5
|
-
*/
|
|
6
|
-
export declare function escapeHtml(value: string): string;
|
|
7
2
|
export interface LayoutOptions {
|
|
8
3
|
readonly subject: string;
|
|
9
4
|
/** Already-escaped HTML for the message body. */
|
|
@@ -28,7 +23,7 @@ export interface LayoutOptions {
|
|
|
28
23
|
* given, except for `brand.name`, which it escapes itself.
|
|
29
24
|
*
|
|
30
25
|
* `subject` is checked for CR/LF and other control characters before it is
|
|
31
|
-
* returned, since it is not HTML-escaped and reaches
|
|
26
|
+
* returned, since it is not HTML-escaped and reaches the mail provider as a header
|
|
32
27
|
* value -- see `header-safe.ts`.
|
|
33
28
|
*/
|
|
34
29
|
export declare function layout(options: LayoutOptions): MailMessage;
|
package/dist/layout.js
CHANGED
|
@@ -1,15 +1,5 @@
|
|
|
1
|
+
import { escapeHtml } from '@wtfalch/utils/html';
|
|
1
2
|
import { assertHeaderSafe } from './header-safe.js';
|
|
2
|
-
/**
|
|
3
|
-
* The one rule. Ampersand first, or it double-escapes everything after it.
|
|
4
|
-
* Same rule and reason as app-template's `src/lib/marketing/render.ts`.
|
|
5
|
-
*/
|
|
6
|
-
export function escapeHtml(value) {
|
|
7
|
-
return value
|
|
8
|
-
.replace(/&/g, '&')
|
|
9
|
-
.replace(/</g, '<')
|
|
10
|
-
.replace(/>/g, '>')
|
|
11
|
-
.replace(/"/g, '"');
|
|
12
|
-
}
|
|
13
3
|
const BODY_STYLE = 'margin:0;padding:24px;background:#f4f6f8;font-family:-apple-system,Segoe UI,Helvetica,Arial,sans-serif;color:#15181d;line-height:1.6';
|
|
14
4
|
const CARD_STYLE = 'max-width:600px;margin:0 auto;background:#ffffff;padding:28px;border-radius:4px';
|
|
15
5
|
const BRAND_STYLE = 'margin:0 0 20px;font-size:13px;color:#545b66;text-transform:uppercase;letter-spacing:0.04em';
|
|
@@ -30,7 +20,7 @@ const BRAND_STYLE = 'margin:0 0 20px;font-size:13px;color:#545b66;text-transform
|
|
|
30
20
|
* given, except for `brand.name`, which it escapes itself.
|
|
31
21
|
*
|
|
32
22
|
* `subject` is checked for CR/LF and other control characters before it is
|
|
33
|
-
* returned, since it is not HTML-escaped and reaches
|
|
23
|
+
* returned, since it is not HTML-escaped and reaches the mail provider as a header
|
|
34
24
|
* value -- see `header-safe.ts`.
|
|
35
25
|
*/
|
|
36
26
|
export function layout(options) {
|
package/dist/transport.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type Attachment, type MailMessage, type SendResult } from './types.js';
|
|
|
2
2
|
/** A rendered message, addressed and ready for a transport. */
|
|
3
3
|
export interface OutgoingMail extends MailMessage {
|
|
4
4
|
readonly to: string;
|
|
5
|
-
/** Already formatted, e.g. `"Acme <no-reply@acme.
|
|
5
|
+
/** Already formatted, e.g. `"Acme <no-reply@acme.example.com>"` or a bare address. */
|
|
6
6
|
readonly from: string;
|
|
7
7
|
readonly replyTo?: string;
|
|
8
8
|
readonly attachments?: readonly Attachment[];
|
|
@@ -16,63 +16,10 @@ export interface DevTransportOptions {
|
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
18
|
* Writes every message to `<dir>/<timestamp>-<id>.json` instead of sending
|
|
19
|
-
* it. Never opens a network connection
|
|
20
|
-
*
|
|
21
|
-
* nothing that sends mail could be tested without a live key.
|
|
19
|
+
* it. Never opens a network connection, so nothing that sends mail needs
|
|
20
|
+
* a live key to be tested.
|
|
22
21
|
*/
|
|
23
22
|
export declare function createDevTransport(options?: DevTransportOptions): Transport;
|
|
24
|
-
/**
|
|
25
|
-
* The minimal shape this package calls on a Resend client: `emails.send`
|
|
26
|
-
* only, with the fields mailer actually sets. The real `Resend` class
|
|
27
|
-
* satisfies this structurally, and so does a fake with no network access,
|
|
28
|
-
* which is what this package's own tests inject (never a real key, never a
|
|
29
|
-
* real send -- see `transport.test.ts`).
|
|
30
|
-
*/
|
|
31
|
-
export interface ResendLike {
|
|
32
|
-
emails: {
|
|
33
|
-
send(payload: {
|
|
34
|
-
from: string;
|
|
35
|
-
to: string;
|
|
36
|
-
subject: string;
|
|
37
|
-
html: string;
|
|
38
|
-
text: string;
|
|
39
|
-
reply_to?: string;
|
|
40
|
-
/** Resend's own field names: `content` is base64 (or a Buffer), `content_type` is
|
|
41
|
-
* optional and derived from `filename` when absent --
|
|
42
|
-
* https://resend.com/docs/api-reference/emails/send-email. */
|
|
43
|
-
attachments?: Array<{
|
|
44
|
-
filename: string;
|
|
45
|
-
content: string;
|
|
46
|
-
content_type?: string;
|
|
47
|
-
}>;
|
|
48
|
-
}): Promise<{
|
|
49
|
-
data: {
|
|
50
|
-
id: string;
|
|
51
|
-
} | null;
|
|
52
|
-
error: {
|
|
53
|
-
message: string;
|
|
54
|
-
} | null;
|
|
55
|
-
}>;
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
export interface ResendTransportOptions {
|
|
59
|
-
/** The Resend API key. The caller resolves this from `@wtfalch/keys`; mailer never fetches or stores it. */
|
|
60
|
-
readonly apiKey?: string;
|
|
61
|
-
/** A client to use instead of constructing one from `apiKey` -- how tests inject a fake. */
|
|
62
|
-
readonly client?: ResendLike;
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Sends over Resend. The one place in the estate that imports the `resend`
|
|
66
|
-
* package (README: "This package is the one place that knows about
|
|
67
|
-
* Resend") -- and, since [0006](../docs/adr/0006-lettermint-transport-resend-optional-peer.md),
|
|
68
|
-
* it imports it lazily: `resend` is now an optional peer, not a runtime
|
|
69
|
-
* dependency, so a consumer that only ever passes `client` (every test in
|
|
70
|
-
* this repo, and any caller that only sends over Lettermint) never needs
|
|
71
|
-
* the package installed at all. The `import('resend')` below runs at most
|
|
72
|
-
* once per transport, on the first `send()` call, only when no `client` was
|
|
73
|
-
* given.
|
|
74
|
-
*/
|
|
75
|
-
export declare function createResendTransport(options: ResendTransportOptions): Transport;
|
|
76
23
|
/**
|
|
77
24
|
* The minimal shape this package calls on `fetch` to speak Lettermint's
|
|
78
25
|
* Sending API directly -- the same no-SDK, injectable-`fetch` posture
|
|
@@ -93,7 +40,7 @@ export interface LettermintAttachment {
|
|
|
93
40
|
readonly filename: string;
|
|
94
41
|
/**
|
|
95
42
|
* Snake-case, matching the Sending API's own wire field exactly --
|
|
96
|
-
*
|
|
43
|
+
* `LettermintSendPayload` has no translation step
|
|
97
44
|
* before `JSON.stringify` (`createLettermintClient`'s `send`), so this
|
|
98
45
|
* interface's own field names ARE the request body's JSON keys.
|
|
99
46
|
*/
|
|
@@ -121,7 +68,7 @@ export interface LettermintSendPayload {
|
|
|
121
68
|
}
|
|
122
69
|
/**
|
|
123
70
|
* The minimal shape this package calls on a Lettermint client: `send`
|
|
124
|
-
* only,
|
|
71
|
+
* only, a structural interface so a test can inject a fake. The
|
|
125
72
|
* default client (built from `apiKey`) satisfies this over plain `fetch`;
|
|
126
73
|
* a fake with no network access is what this package's own tests inject.
|
|
127
74
|
*/
|
|
@@ -159,8 +106,8 @@ export declare class LettermintDomainRefusedError extends Error {
|
|
|
159
106
|
constructor(message: string);
|
|
160
107
|
}
|
|
161
108
|
/**
|
|
162
|
-
* Sends over Lettermint (EU, GDPR
|
|
163
|
-
*
|
|
109
|
+
* Sends over Lettermint (EU, GDPR; see
|
|
110
|
+
* [0006](../docs/adr/0006-lettermint-transport.md)),
|
|
164
111
|
* via plain `fetch` against Lettermint's Sending API. Base URL, auth header
|
|
165
112
|
* and request/response shapes:
|
|
166
113
|
* https://lettermint.co/docs/api-reference/sending/0.0.1/lettermint-sending-openapi.json
|
|
@@ -177,7 +124,7 @@ export declare function createLettermintTransport(options: LettermintTransportOp
|
|
|
177
124
|
/**
|
|
178
125
|
* `dev`, unconditionally, outside `NODE_ENV=production`. In production
|
|
179
126
|
* there is no default: `send()` must be given an explicit transport,
|
|
180
|
-
* because mailer never holds a
|
|
127
|
+
* because mailer never holds a mail API key of its own to fall back to.
|
|
181
128
|
* Calling `send()` in production with no transport is a configuration bug,
|
|
182
129
|
* not a silent no-op and not an accidental live send.
|
|
183
130
|
*/
|
package/dist/transport.js
CHANGED
|
@@ -4,9 +4,8 @@ import { join } from 'node:path';
|
|
|
4
4
|
import { MailerConfigError } from './types.js';
|
|
5
5
|
/**
|
|
6
6
|
* Writes every message to `<dir>/<timestamp>-<id>.json` instead of sending
|
|
7
|
-
* it. Never opens a network connection
|
|
8
|
-
*
|
|
9
|
-
* nothing that sends mail could be tested without a live key.
|
|
7
|
+
* it. Never opens a network connection, so nothing that sends mail needs
|
|
8
|
+
* a live key to be tested.
|
|
10
9
|
*/
|
|
11
10
|
export function createDevTransport(options = {}) {
|
|
12
11
|
const dir = options.dir ?? '.mailer/dev';
|
|
@@ -20,56 +19,6 @@ export function createDevTransport(options = {}) {
|
|
|
20
19
|
},
|
|
21
20
|
};
|
|
22
21
|
}
|
|
23
|
-
/**
|
|
24
|
-
* Sends over Resend. The one place in the estate that imports the `resend`
|
|
25
|
-
* package (README: "This package is the one place that knows about
|
|
26
|
-
* Resend") -- and, since [0006](../docs/adr/0006-lettermint-transport-resend-optional-peer.md),
|
|
27
|
-
* it imports it lazily: `resend` is now an optional peer, not a runtime
|
|
28
|
-
* dependency, so a consumer that only ever passes `client` (every test in
|
|
29
|
-
* this repo, and any caller that only sends over Lettermint) never needs
|
|
30
|
-
* the package installed at all. The `import('resend')` below runs at most
|
|
31
|
-
* once per transport, on the first `send()` call, only when no `client` was
|
|
32
|
-
* given.
|
|
33
|
-
*/
|
|
34
|
-
export function createResendTransport(options) {
|
|
35
|
-
if (!options.client && !options.apiKey) {
|
|
36
|
-
throw new MailerConfigError('createResendTransport needs either "apiKey" or "client".');
|
|
37
|
-
}
|
|
38
|
-
let resolvedClient;
|
|
39
|
-
function resolveClient() {
|
|
40
|
-
if (options.client)
|
|
41
|
-
return Promise.resolve(options.client);
|
|
42
|
-
resolvedClient ??= import('resend').then(({ Resend }) => new Resend(options.apiKey));
|
|
43
|
-
return resolvedClient;
|
|
44
|
-
}
|
|
45
|
-
return {
|
|
46
|
-
async send(message) {
|
|
47
|
-
const client = await resolveClient();
|
|
48
|
-
const { data, error } = await client.emails.send({
|
|
49
|
-
from: message.from,
|
|
50
|
-
to: message.to,
|
|
51
|
-
subject: message.subject,
|
|
52
|
-
html: message.html,
|
|
53
|
-
text: message.text,
|
|
54
|
-
...(message.replyTo ? { reply_to: message.replyTo } : {}),
|
|
55
|
-
...(message.attachments
|
|
56
|
-
? {
|
|
57
|
-
attachments: message.attachments.map((attachment) => ({
|
|
58
|
-
filename: attachment.filename,
|
|
59
|
-
content: attachment.content,
|
|
60
|
-
...(attachment.contentType ? { content_type: attachment.contentType } : {}),
|
|
61
|
-
})),
|
|
62
|
-
}
|
|
63
|
-
: {}),
|
|
64
|
-
});
|
|
65
|
-
if (error)
|
|
66
|
-
throw new Error(`mailer: resend refused the message: ${error.message}`);
|
|
67
|
-
if (!data)
|
|
68
|
-
throw new Error('mailer: resend returned no data and no error');
|
|
69
|
-
return { id: data.id, transport: 'resend' };
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
22
|
/**
|
|
74
23
|
* Thrown when Lettermint refuses to send because the `from` domain is not
|
|
75
24
|
* one the sending project may use -- either unverified, or verified but
|
|
@@ -128,8 +77,8 @@ function createLettermintClient(apiKey, fetchImpl) {
|
|
|
128
77
|
};
|
|
129
78
|
}
|
|
130
79
|
/**
|
|
131
|
-
* Sends over Lettermint (EU, GDPR
|
|
132
|
-
*
|
|
80
|
+
* Sends over Lettermint (EU, GDPR; see
|
|
81
|
+
* [0006](../docs/adr/0006-lettermint-transport.md)),
|
|
133
82
|
* via plain `fetch` against Lettermint's Sending API. Base URL, auth header
|
|
134
83
|
* and request/response shapes:
|
|
135
84
|
* https://lettermint.co/docs/api-reference/sending/0.0.1/lettermint-sending-openapi.json
|
|
@@ -174,13 +123,13 @@ export function createLettermintTransport(options) {
|
|
|
174
123
|
/**
|
|
175
124
|
* `dev`, unconditionally, outside `NODE_ENV=production`. In production
|
|
176
125
|
* there is no default: `send()` must be given an explicit transport,
|
|
177
|
-
* because mailer never holds a
|
|
126
|
+
* because mailer never holds a mail API key of its own to fall back to.
|
|
178
127
|
* Calling `send()` in production with no transport is a configuration bug,
|
|
179
128
|
* not a silent no-op and not an accidental live send.
|
|
180
129
|
*/
|
|
181
130
|
export function defaultTransport() {
|
|
182
131
|
if (process.env.NODE_ENV === 'production') {
|
|
183
|
-
throw new MailerConfigError('send() needs an explicit transport in production: pass { transport:
|
|
132
|
+
throw new MailerConfigError('send() needs an explicit transport in production: pass { transport: createLettermintTransport({ apiKey }) }.');
|
|
184
133
|
}
|
|
185
134
|
return createDevTransport();
|
|
186
135
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -42,10 +42,8 @@ export interface SenderIdentity {
|
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
44
44
|
* One file attached to a send. `content` is base64-encoded bytes -- the
|
|
45
|
-
* convention
|
|
46
|
-
*
|
|
47
|
-
* attached file, passed as a buffer or Base64 string"; Lettermint's Sending
|
|
48
|
-
* API OpenAPI spec, `attachments[].content`, and its official SDKs, which
|
|
45
|
+
* convention the Lettermint Sending API uses (its
|
|
46
|
+
* OpenAPI spec, `attachments[].content`, and its official SDKs, which
|
|
49
47
|
* all base64-encode before setting it).
|
|
50
48
|
*/
|
|
51
49
|
export interface Attachment {
|
|
@@ -62,11 +60,11 @@ export interface MailMessage {
|
|
|
62
60
|
/** What a transport returns once it has accepted a message. */
|
|
63
61
|
export interface SendResult {
|
|
64
62
|
readonly id: string;
|
|
65
|
-
readonly transport: '
|
|
63
|
+
readonly transport: 'dev' | 'lettermint';
|
|
66
64
|
}
|
|
67
65
|
/**
|
|
68
66
|
* `send()` has no usable default (no transport given, and either
|
|
69
|
-
* production with no explicit transport, or a
|
|
67
|
+
* production with no explicit transport, or a transport built with
|
|
70
68
|
* neither `apiKey` nor `client`). Thrown rather than a silent no-op or an
|
|
71
69
|
* accidental network call.
|
|
72
70
|
*/
|
package/dist/types.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
export const DEFAULT_LOCALE = 'en';
|
|
3
3
|
/**
|
|
4
4
|
* `send()` has no usable default (no transport given, and either
|
|
5
|
-
* production with no explicit transport, or a
|
|
5
|
+
* production with no explicit transport, or a transport built with
|
|
6
6
|
* neither `apiKey` nor `client`). Thrown rather than a silent no-op or an
|
|
7
7
|
* accidental network call.
|
|
8
8
|
*/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wtfalch/mailer",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "One send(template, to, data) call for the estate's transactional email, over
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "One send(template, to, data) call for the estate's transactional email, over Lettermint, with a dev transport that writes to disk instead of sending.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/wtfalch/mailer",
|
|
@@ -30,17 +30,11 @@
|
|
|
30
30
|
"test": "vitest run",
|
|
31
31
|
"prepack": "pnpm build"
|
|
32
32
|
},
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
},
|
|
36
|
-
"peerDependenciesMeta": {
|
|
37
|
-
"resend": {
|
|
38
|
-
"optional": true
|
|
39
|
-
}
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@wtfalch/utils": "0.1.0"
|
|
40
35
|
},
|
|
41
36
|
"devDependencies": {
|
|
42
37
|
"@types/node": "^22",
|
|
43
|
-
"resend": "^4.1.0",
|
|
44
38
|
"typescript": "^5.9.0",
|
|
45
39
|
"vitest": "^4.1.6"
|
|
46
40
|
}
|