@ultimat3/mail 16.0.0 → 17.0.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/CLAUDE.md +10 -0
- package/package.json +6 -6
- package/src/driver-resend.ts +12 -1
- package/src/driver-smtp.ts +11 -1
package/CLAUDE.md
CHANGED
|
@@ -37,6 +37,16 @@
|
|
|
37
37
|
- No hex outside `MAIL_TOKENS`. Base styling is inlined (clients strip `<style>`); dark mode is
|
|
38
38
|
one `prefers-color-scheme` block keyed on short `data-x` role codes.
|
|
39
39
|
- Never format a date without `options.tz`. The `Date:` header is UTC, stated as `+0000`.
|
|
40
|
+
- **`timeoutMs` is refused at CONSTRUCTION, where `poolSize` already is, and its floor is 1**
|
|
41
|
+
(`As of 2026-08-26`). `finiteCount('createSmtpDriver'|'createResendDriver', 'timeoutMs', …, 1)`.
|
|
42
|
+
Zero is not "no deadline" in either driver: the value goes straight to `AbortSignal.timeout` and
|
|
43
|
+
to `setTimeout(fn, timeoutMs)`, both of which fire on the next tick, so `0` and `NaN` are the same
|
|
44
|
+
failure — `setTimeout(fn, NaN)` IS `setTimeout(fn, 0)`. Resend's was the worse half: the signal is
|
|
45
|
+
constructed INSIDE the `try` that wraps the fetch, so `AbortSignal.timeout(NaN)`'s bare
|
|
46
|
+
`TypeError` was caught and reported as a RETRYABLE egress failure whose `fix:` said to curl the
|
|
47
|
+
endpoint, and the queue retried a config error to the dead-letter table. `bun run finite-bounds`
|
|
48
|
+
is the ratchet; the repair is recognised by the shape of the call.
|
|
49
|
+
|
|
40
50
|
- **Every "now" in this package comes from a `Clock`, `createMemoryDriver` included** (`As of
|
|
41
51
|
2026-08-22`). It stamped `at: new Date()`, and `SentMail.at` is what `outbox()`, `lastTo()` and
|
|
42
52
|
the `/_x` panel ORDER on — so the one fact a test most needs to state was the one it could only
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/mail",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "17.0.0",
|
|
4
4
|
"description": "Transactional email as data: one template renders HTML and text, sent through a job.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -31,10 +31,10 @@
|
|
|
31
31
|
"test": "bun test"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ultimat3/core": "
|
|
35
|
-
"@ultimat3/i18n": "
|
|
36
|
-
"@ultimat3/jobs": "
|
|
37
|
-
"@ultimat3/schema": "
|
|
38
|
-
"@ultimat3/time": "
|
|
34
|
+
"@ultimat3/core": "17.0.0",
|
|
35
|
+
"@ultimat3/i18n": "17.0.0",
|
|
36
|
+
"@ultimat3/jobs": "17.0.0",
|
|
37
|
+
"@ultimat3/schema": "17.0.0",
|
|
38
|
+
"@ultimat3/time": "17.0.0"
|
|
39
39
|
}
|
|
40
40
|
}
|
package/src/driver-resend.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import {
|
|
6
6
|
ConfigInvalidError,
|
|
7
7
|
EnvMissingError,
|
|
8
|
+
finiteCount,
|
|
8
9
|
// The status table is core's: this package's copy and `packages/cache/src/purge-http.ts`'s were
|
|
9
10
|
// byte-identical in two packages that cannot import each other, so one was always going to be
|
|
10
11
|
// edited alone. A throttle or a transient conflict can land unchanged; every other 4xx here is a
|
|
@@ -130,7 +131,17 @@ export function createResendDriver(options: ResendDriverOptions): MailDriver {
|
|
|
130
131
|
const apiKey = requireApiKey(options.apiKey);
|
|
131
132
|
const from = requireFrom(options.from);
|
|
132
133
|
const baseUrl = options.baseUrl ?? RESEND_BASE_URL;
|
|
133
|
-
|
|
134
|
+
// Refused here, where `apiKey` and `from` already are, and not on the first send:
|
|
135
|
+
// `AbortSignal.timeout(NaN)` raises a bare TypeError INSIDE the try below, so a misconfigured
|
|
136
|
+
// deadline was reported as a retryable egress failure whose fix said to curl the endpoint — and
|
|
137
|
+
// the queue retried it to the dead-letter table for a value no network could change. `0` is not
|
|
138
|
+
// "no deadline" either: the signal aborts on the next tick, before a byte leaves the host.
|
|
139
|
+
const timeoutMs = finiteCount(
|
|
140
|
+
'createResendDriver',
|
|
141
|
+
'timeoutMs',
|
|
142
|
+
options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
143
|
+
1,
|
|
144
|
+
);
|
|
134
145
|
// A bare reference to `globalThis.fetch` risks "Illegal invocation" on some hosts; closing
|
|
135
146
|
// over the call keeps it detached from any receiver, in production and in tests alike.
|
|
136
147
|
const doFetch: MailFetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
package/src/driver-smtp.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import {
|
|
6
6
|
type Clock,
|
|
7
7
|
ConfigInvalidError,
|
|
8
|
+
finiteCount,
|
|
8
9
|
isUltimateError,
|
|
9
10
|
nanoid,
|
|
10
11
|
renderThrowable,
|
|
@@ -139,7 +140,16 @@ export function createSmtpDriver(options: SmtpDriverOptions): MailDriver {
|
|
|
139
140
|
}
|
|
140
141
|
const clock = options.clock ?? systemClock;
|
|
141
142
|
const connect = options.connect ?? bunSmtpStream;
|
|
142
|
-
|
|
143
|
+
// Refused where `poolSize` already is. The deadline reaches `setTimeout(fn, timeoutMs)` in the
|
|
144
|
+
// conversation and in the socket, and `setTimeout(fn, NaN)` is `setTimeout(fn, 0)` — so a
|
|
145
|
+
// non-finite deadline does not disable itself, it fires on the next tick and every send fails
|
|
146
|
+
// "the server sent nothing for NaNms". `0` is the same failure spelled deliberately.
|
|
147
|
+
const timeoutMs = finiteCount(
|
|
148
|
+
'createSmtpDriver',
|
|
149
|
+
'timeoutMs',
|
|
150
|
+
options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
151
|
+
1,
|
|
152
|
+
);
|
|
143
153
|
const limit = createLimiter(resolvePoolSize(options.poolSize));
|
|
144
154
|
const session: SmtpSessionOptions = {
|
|
145
155
|
clientName: options.clientName ?? addressDomain(options.from),
|