@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 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": "16.0.0",
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": "16.0.0",
35
- "@ultimat3/i18n": "16.0.0",
36
- "@ultimat3/jobs": "16.0.0",
37
- "@ultimat3/schema": "16.0.0",
38
- "@ultimat3/time": "16.0.0"
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
  }
@@ -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
- const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
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));
@@ -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
- const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
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),