What happens
SendContext.meta is documented as "a shared mutable bag for middleware to leave notes in", and docs/architecture.md shows a middleware writing ctx.meta.durationMs. Nothing ever hands that context back to the caller, so whatever a middleware writes there is unreachable.
Reproduction
const email = createEmail({ driver: mock(), defaults, use: [
defineMiddleware("timing", (next) => async (msgs, ctx) => {
const r = await next(msgs, ctx)
ctx.meta.durationMs = 42
return r
}),
]})
const result = await email.send(msg)
Observed:
Result keys: ["data","error"]
data keys: ["id","driver","at"]
is ctx.meta reachable from the return value? NO — the bag is write-only
Why it matters
The documented use for meta — passing observability data out of the pipeline — cannot be done. A middleware can only get information out by closing over its own variable, which breaks as soon as two sends run concurrently, because the closure has no way to tell them apart. So the feature reads as supported and is not.
Where
src/core/types.ts:281 documents it; src/core/email.ts:225-232 creates the context and discards it; docs/architecture.md demonstrates the unreachable pattern.
Suggested fix
Either surface it — carry the per-send meta onto EmailResult (and onto EmailError, so a failure can be traced too) — or drop meta from the public type and rewrite the docs example. Surfacing it is the smaller change and the one the docs already promise.
What happens
SendContext.metais documented as "a shared mutable bag for middleware to leave notes in", anddocs/architecture.mdshows a middleware writingctx.meta.durationMs. Nothing ever hands that context back to the caller, so whatever a middleware writes there is unreachable.Reproduction
Observed:
Why it matters
The documented use for
meta— passing observability data out of the pipeline — cannot be done. A middleware can only get information out by closing over its own variable, which breaks as soon as two sends run concurrently, because the closure has no way to tell them apart. So the feature reads as supported and is not.Where
src/core/types.ts:281documents it;src/core/email.ts:225-232creates the context and discards it;docs/architecture.mddemonstrates the unreachable pattern.Suggested fix
Either surface it — carry the per-send
metaontoEmailResult(and ontoEmailError, so a failure can be traced too) — or dropmetafrom the public type and rewrite the docs example. Surfacing it is the smaller change and the one the docs already promise.