@lunora/mail 0.0.0 → 1.0.0-alpha.2
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/LICENSE.md +105 -0
- package/README.md +117 -9
- package/__assets__/package-og.svg +14 -0
- package/dist/inbound/index.d.mts +193 -0
- package/dist/inbound/index.d.ts +193 -0
- package/dist/inbound/index.mjs +2 -0
- package/dist/index.d.mts +133 -0
- package/dist/index.d.ts +133 -0
- package/dist/index.mjs +7 -0
- package/dist/packem_shared/address-fkXxLKza.mjs +62 -0
- package/dist/packem_shared/capture-transport.d-ChnhdPO2.d.mts +117 -0
- package/dist/packem_shared/capture-transport.d-ChnhdPO2.d.ts +117 -0
- package/dist/packem_shared/consumeQueuedSend-BEKOdaxU.mjs +75 -0
- package/dist/packem_shared/createCaptureSink-DeihS4LH.mjs +63 -0
- package/dist/packem_shared/createCaptureTransport-Crz_8822.mjs +11 -0
- package/dist/packem_shared/createCloudflareTransport-yHOVEsZv.mjs +26 -0
- package/dist/packem_shared/createInboundEmailHandler-D0uCOrU-.mjs +83 -0
- package/dist/packem_shared/createMailer-oEKPAd4J.mjs +77 -0
- package/dist/packem_shared/createResendTransport-oNIorpzv.mjs +16 -0
- package/dist/packem_shared/parseInboundEmail-Bw9u_1oc.mjs +72 -0
- package/dist/packem_shared/provider-transport-C5CVbjRF.mjs +47 -0
- package/dist/packem_shared/renderEmail-hyS1bpVP.mjs +8 -0
- package/dist/packem_shared/shard-CJ-TvmfT.mjs +13 -0
- package/dist/packem_shared/shard.d-CL2Lmliv.d.mts +39 -0
- package/dist/packem_shared/shard.d-CL2Lmliv.d.ts +39 -0
- package/dist/testing.d.mts +49 -0
- package/dist/testing.d.ts +49 -0
- package/dist/testing.mjs +57 -0
- package/package.json +58 -17
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { D as DurableObjectJurisdiction, S as ShardNamespaceLike } from "../packem_shared/shard.d-CL2Lmliv.mjs";
|
|
2
|
+
export type { a as ShardStubLike } from "../packem_shared/shard.d-CL2Lmliv.mjs";
|
|
3
|
+
/** A raw RFC 822 message as accepted by the parser. */
|
|
4
|
+
type RawInboundEmail = ArrayBuffer | ReadableStream<Uint8Array> | string | Uint8Array;
|
|
5
|
+
/** One parsed attachment. `content` is preserved as the parser decoded it. */
|
|
6
|
+
interface InboundAttachment {
|
|
7
|
+
/** Raw attachment content (base64 string or binary buffer, per `encoding`). */
|
|
8
|
+
content: ArrayBuffer | string | Uint8Array;
|
|
9
|
+
/** `Content-Disposition` (`"attachment"`/`"inline"`), or `null` when absent. */
|
|
10
|
+
disposition: "attachment" | "inline" | null;
|
|
11
|
+
/** How `content` is encoded, when the parser reported it. */
|
|
12
|
+
encoding?: "base64" | "utf8";
|
|
13
|
+
/** Filename, or `null` when the part declared none. */
|
|
14
|
+
filename: string | null;
|
|
15
|
+
/** Declared MIME type (e.g. `image/png`). */
|
|
16
|
+
mimeType: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Sender-authentication verdicts pulled from the `Authentication-Results` header
|
|
20
|
+
* the receiving MX (e.g. Cloudflare Email Routing) stamped on the message.
|
|
21
|
+
*
|
|
22
|
+
* SECURITY: Cloudflare Email Routing authenticates only the *recipient* domain,
|
|
23
|
+
* **not** the sender. The envelope `from` and message content are trivially
|
|
24
|
+
* spoofable, so a downstream handler MUST NOT make trust/authorization decisions
|
|
25
|
+
* on `email.from` alone — gate on these verdicts (or your own policy) instead.
|
|
26
|
+
* Verdicts are best-effort: when the receiving MX did not stamp an
|
|
27
|
+
* `Authentication-Results` header, every field is `null` ("unknown").
|
|
28
|
+
*/
|
|
29
|
+
interface InboundAuthentication {
|
|
30
|
+
/** DKIM verdict (`"pass"`/`"fail"`/…), or `null` when not reported. */
|
|
31
|
+
dkim: string | null;
|
|
32
|
+
/** DMARC verdict (`"pass"`/`"fail"`/…), or `null` when not reported. */
|
|
33
|
+
dmarc: string | null;
|
|
34
|
+
/** SPF verdict (`"pass"`/`"fail"`/…), or `null` when not reported. */
|
|
35
|
+
spf: string | null;
|
|
36
|
+
}
|
|
37
|
+
/** Normalised, transport-agnostic view of a received message. */
|
|
38
|
+
interface InboundEmail {
|
|
39
|
+
/** Decoded attachments (empty array when none). */
|
|
40
|
+
attachments: InboundAttachment[];
|
|
41
|
+
/**
|
|
42
|
+
* Sender-authentication verdicts (DKIM/SPF/DMARC) parsed from the receiving
|
|
43
|
+
* MX's `Authentication-Results` header. SECURITY: see {@link InboundAuthentication}
|
|
44
|
+
* — `from` is spoofable; gate trust on these verdicts, not on `from`.
|
|
45
|
+
*/
|
|
46
|
+
authentication: InboundAuthentication;
|
|
47
|
+
/** Sender mailbox (`from`), CR/LF-checked. Empty string when the message omitted it. SECURITY: spoofable — do not trust for authorization. */
|
|
48
|
+
from: string;
|
|
49
|
+
/** Flattened `key → value` of the parsed headers (lowercase keys), each value CR/LF-checked. */
|
|
50
|
+
headers: Record<string, string>;
|
|
51
|
+
/** HTML body, when present. */
|
|
52
|
+
html?: string;
|
|
53
|
+
/** `In-Reply-To` header (threading), CR/LF-checked. */
|
|
54
|
+
inReplyTo?: string;
|
|
55
|
+
/** `Message-ID` of this message, CR/LF-checked. */
|
|
56
|
+
messageId?: string;
|
|
57
|
+
/** `References` header (threading), CR/LF-checked. */
|
|
58
|
+
references?: string;
|
|
59
|
+
/** Subject line, CR/LF-checked. */
|
|
60
|
+
subject?: string;
|
|
61
|
+
/** Plain-text body, when present. */
|
|
62
|
+
text?: string;
|
|
63
|
+
/** Recipient mailboxes (`to`), each CR/LF-checked. */
|
|
64
|
+
to: string[];
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Parse a raw RFC 822 message into a normalised {@link InboundEmail}. Accepts the
|
|
68
|
+
* shapes a Cloudflare Email Worker can hand off — `ReadableStream`, `ArrayBuffer`,
|
|
69
|
+
* `Uint8Array`, or a decoded string.
|
|
70
|
+
*/
|
|
71
|
+
declare const parseInboundEmail: (raw: RawInboundEmail) => Promise<InboundEmail>;
|
|
72
|
+
/**
|
|
73
|
+
* Structural projection of Cloudflare's `ForwardableEmailMessage` (verified
|
|
74
|
+
* against `@cloudflare/workers-types`' `ForwardableEmailMessage`). Only the
|
|
75
|
+
* members the handler touches are modelled, so the host can pass the real
|
|
76
|
+
* runtime object without `@lunora/mail` importing `cloudflare:email`.
|
|
77
|
+
*/
|
|
78
|
+
interface ForwardableEmailMessageLike {
|
|
79
|
+
/** Forward this message to a verified destination address. */
|
|
80
|
+
forward: (rcptTo: string, headers?: Headers) => Promise<unknown>;
|
|
81
|
+
/** Envelope `From`. */
|
|
82
|
+
readonly from: string;
|
|
83
|
+
/** Parsed top-level headers. */
|
|
84
|
+
readonly headers: Headers;
|
|
85
|
+
/** Stream of the raw RFC 822 message content. */
|
|
86
|
+
readonly raw: ReadableStream<Uint8Array>;
|
|
87
|
+
/** Reply to the sender with a new message. */
|
|
88
|
+
reply: (message: {
|
|
89
|
+
from: string;
|
|
90
|
+
raw: string;
|
|
91
|
+
to: string;
|
|
92
|
+
}) => Promise<unknown>;
|
|
93
|
+
/** Reject the message with a permanent SMTP error (Cloudflare bounces/retries). */
|
|
94
|
+
setReject: (reason: string) => void;
|
|
95
|
+
/** Envelope `To`. */
|
|
96
|
+
readonly to: string;
|
|
97
|
+
}
|
|
98
|
+
/** Context threaded to a `dispatch` callback alongside the parsed message. */
|
|
99
|
+
interface InboundDispatchContext<TEnv = Record<string, unknown>> {
|
|
100
|
+
/** Cloudflare's `ExecutionContext`, forwarded verbatim from the `email()` entry. */
|
|
101
|
+
ctx: unknown;
|
|
102
|
+
/** Worker `env` (bindings, vars, secrets) projected as a plain record. */
|
|
103
|
+
env: TEnv;
|
|
104
|
+
/** The originating message, for `setReject`/`forward`/`reply`. */
|
|
105
|
+
message: ForwardableEmailMessageLike;
|
|
106
|
+
}
|
|
107
|
+
/** Routes a parsed message into a Lunora function (or anywhere). */
|
|
108
|
+
type InboundDispatch<TEnv = Record<string, unknown>> = (email: InboundEmail, context: InboundDispatchContext<TEnv>) => Promise<void>;
|
|
109
|
+
/**
|
|
110
|
+
* Opt-in sender-verification gate. Runs after `parse` and before `dispatch` with
|
|
111
|
+
* the parsed message. Return `false` (or throw) to reject the message before it
|
|
112
|
+
* reaches the privileged dispatch — use it to enforce DKIM/SPF/DMARC via
|
|
113
|
+
* `email.authentication`, an allow-list, etc. Returning `true`/`undefined`
|
|
114
|
+
* proceeds.
|
|
115
|
+
*/
|
|
116
|
+
type InboundVerify<TEnv = Record<string, unknown>> = (email: InboundEmail, context: InboundDispatchContext<TEnv>) => Promise<boolean | void> | boolean | void;
|
|
117
|
+
/** Options for {@link createInboundEmailHandler}. */
|
|
118
|
+
interface InboundEmailHandlerOptions<TEnv = Record<string, unknown>> {
|
|
119
|
+
/** Routes the parsed message onward (e.g. {@link dispatchToLunoraFunction}). */
|
|
120
|
+
dispatch: InboundDispatch<TEnv>;
|
|
121
|
+
/**
|
|
122
|
+
* Called when `parse`/`verify`/`dispatch` throws. The default rejects the
|
|
123
|
+
* message via `message.setReject` so Cloudflare bounces/retries rather than
|
|
124
|
+
* silently dropping it. SECURITY: the reject reason is delivered to the
|
|
125
|
+
* (attacker-controlled) sender as a bounce, so the default reason is a fixed,
|
|
126
|
+
* generic string and the real error is logged server-side. Override to log,
|
|
127
|
+
* forward, or swallow — but never pass internal error text to `setReject`.
|
|
128
|
+
*/
|
|
129
|
+
onError?: (error: unknown, context: InboundDispatchContext<TEnv>) => Promise<void> | void;
|
|
130
|
+
/** Parses raw bytes into an {@link InboundEmail} (e.g. `parseInboundEmail`). */
|
|
131
|
+
parse: (raw: RawInboundEmail) => Promise<InboundEmail>;
|
|
132
|
+
/**
|
|
133
|
+
* Opt-in sender-authentication gate run before `dispatch`. SECURITY: inbound
|
|
134
|
+
* `from` is spoofable and dispatch is privileged — supply this (gating on
|
|
135
|
+
* `email.authentication`) when an inbound function makes any trust decision.
|
|
136
|
+
*/
|
|
137
|
+
verify?: InboundVerify<TEnv>;
|
|
138
|
+
}
|
|
139
|
+
/** The `email(message, env, ctx)` callback the factory returns. */
|
|
140
|
+
type InboundEmailHandler<TEnv = Record<string, unknown>> = (message: ForwardableEmailMessageLike, env: TEnv, context: unknown) => Promise<void>;
|
|
141
|
+
/**
|
|
142
|
+
* Build the `email(message, env, ctx)` handler. It (a) reads `message.raw`,
|
|
143
|
+
* (b) parses it via `parse`, (c) runs the optional `verify` gate, then
|
|
144
|
+
* (d) calls `dispatch(parsed, { message, env, ctx })`. Any throw (or a falsy
|
|
145
|
+
* `verify`) routes through `onError` (default: a generic `message.setReject`).
|
|
146
|
+
*/
|
|
147
|
+
declare const createInboundEmailHandler: <TEnv = Record<string, unknown>>(options: InboundEmailHandlerOptions<TEnv>) => InboundEmailHandler<TEnv>;
|
|
148
|
+
/** The `RpcEnvelope` shape the runtime's `/_lunora/rpc` path consumes. */
|
|
149
|
+
interface RpcEnvelope {
|
|
150
|
+
args: unknown;
|
|
151
|
+
functionPath: string;
|
|
152
|
+
shardKey?: string;
|
|
153
|
+
}
|
|
154
|
+
/** Options for {@link dispatchToLunoraFunction}. */
|
|
155
|
+
interface DispatchToLunoraFunctionOptions<TEnv = Record<string, unknown>> {
|
|
156
|
+
/**
|
|
157
|
+
* Admin bearer authorizing the shard RPC. Defaults to reading
|
|
158
|
+
* `env.LUNORA_ADMIN_TOKEN` at dispatch time.
|
|
159
|
+
*/
|
|
160
|
+
adminToken?: string;
|
|
161
|
+
/** `functionPath` of the target mutation/action (e.g. `"inbound:onEmail"`). */
|
|
162
|
+
functionPath: string;
|
|
163
|
+
/**
|
|
164
|
+
* Pin inbound dispatch to a Cloudflare data-residency jurisdiction. Pass the
|
|
165
|
+
* same value as the worker's `jurisdiction` so inbound mail routes to the
|
|
166
|
+
* jurisdiction-pinned shard. Omit for the un-pinned global namespace.
|
|
167
|
+
*/
|
|
168
|
+
jurisdiction?: DurableObjectJurisdiction;
|
|
169
|
+
/**
|
|
170
|
+
* Map the parsed message into the function's args. Defaults to passing the
|
|
171
|
+
* whole {@link InboundEmail} with binary attachment `content` base64-encoded
|
|
172
|
+
* (see {@link toJsonSafeEmail}) so it survives the JSON-serialised RPC body.
|
|
173
|
+
*/
|
|
174
|
+
resolveArgs?: (email: InboundEmail, context: InboundDispatchContext<TEnv>) => unknown;
|
|
175
|
+
/** The `SHARD` Durable Object namespace. */
|
|
176
|
+
shard: ShardNamespaceLike;
|
|
177
|
+
/** Shard the function runs on. Defaults to the runtime's default root shard. */
|
|
178
|
+
shardKey?: string;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Build a {@link InboundDispatch} that posts an {@link RpcEnvelope} to the root
|
|
182
|
+
* shard stub — the same admin-RPC-over-shard path the dev capture sink uses
|
|
183
|
+
* (`from-env.ts`) — routing the parsed message into a named Lunora
|
|
184
|
+
* mutation/action. Throws on a non-2xx RPC or a missing admin token so the
|
|
185
|
+
* handler's `onError` (default `setReject`) bounces the message.
|
|
186
|
+
*
|
|
187
|
+
* SECURITY: the RPC carries the admin bearer, so the target function runs with
|
|
188
|
+
* RLS bypassed over fully attacker-controlled, spoofable input — see the module
|
|
189
|
+
* docstring. Verify the sender (`verify` hook / `email.authentication`) before
|
|
190
|
+
* making any trust decision in the target function.
|
|
191
|
+
*/
|
|
192
|
+
declare const dispatchToLunoraFunction: <TEnv extends Record<string, unknown> = Record<string, unknown>>(options: DispatchToLunoraFunctionOptions<TEnv>) => InboundDispatch<TEnv>;
|
|
193
|
+
export { type DispatchToLunoraFunctionOptions, type ForwardableEmailMessageLike, type InboundAttachment, type InboundAuthentication, type InboundDispatch, type InboundDispatchContext, type InboundEmail, type InboundEmailHandler, type InboundEmailHandlerOptions, type InboundVerify, type RawInboundEmail, type RpcEnvelope, type ShardNamespaceLike, createInboundEmailHandler, dispatchToLunoraFunction, parseInboundEmail };
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { D as DurableObjectJurisdiction, S as ShardNamespaceLike } from "../packem_shared/shard.d-CL2Lmliv.js";
|
|
2
|
+
export type { a as ShardStubLike } from "../packem_shared/shard.d-CL2Lmliv.js";
|
|
3
|
+
/** A raw RFC 822 message as accepted by the parser. */
|
|
4
|
+
type RawInboundEmail = ArrayBuffer | ReadableStream<Uint8Array> | string | Uint8Array;
|
|
5
|
+
/** One parsed attachment. `content` is preserved as the parser decoded it. */
|
|
6
|
+
interface InboundAttachment {
|
|
7
|
+
/** Raw attachment content (base64 string or binary buffer, per `encoding`). */
|
|
8
|
+
content: ArrayBuffer | string | Uint8Array;
|
|
9
|
+
/** `Content-Disposition` (`"attachment"`/`"inline"`), or `null` when absent. */
|
|
10
|
+
disposition: "attachment" | "inline" | null;
|
|
11
|
+
/** How `content` is encoded, when the parser reported it. */
|
|
12
|
+
encoding?: "base64" | "utf8";
|
|
13
|
+
/** Filename, or `null` when the part declared none. */
|
|
14
|
+
filename: string | null;
|
|
15
|
+
/** Declared MIME type (e.g. `image/png`). */
|
|
16
|
+
mimeType: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Sender-authentication verdicts pulled from the `Authentication-Results` header
|
|
20
|
+
* the receiving MX (e.g. Cloudflare Email Routing) stamped on the message.
|
|
21
|
+
*
|
|
22
|
+
* SECURITY: Cloudflare Email Routing authenticates only the *recipient* domain,
|
|
23
|
+
* **not** the sender. The envelope `from` and message content are trivially
|
|
24
|
+
* spoofable, so a downstream handler MUST NOT make trust/authorization decisions
|
|
25
|
+
* on `email.from` alone — gate on these verdicts (or your own policy) instead.
|
|
26
|
+
* Verdicts are best-effort: when the receiving MX did not stamp an
|
|
27
|
+
* `Authentication-Results` header, every field is `null` ("unknown").
|
|
28
|
+
*/
|
|
29
|
+
interface InboundAuthentication {
|
|
30
|
+
/** DKIM verdict (`"pass"`/`"fail"`/…), or `null` when not reported. */
|
|
31
|
+
dkim: string | null;
|
|
32
|
+
/** DMARC verdict (`"pass"`/`"fail"`/…), or `null` when not reported. */
|
|
33
|
+
dmarc: string | null;
|
|
34
|
+
/** SPF verdict (`"pass"`/`"fail"`/…), or `null` when not reported. */
|
|
35
|
+
spf: string | null;
|
|
36
|
+
}
|
|
37
|
+
/** Normalised, transport-agnostic view of a received message. */
|
|
38
|
+
interface InboundEmail {
|
|
39
|
+
/** Decoded attachments (empty array when none). */
|
|
40
|
+
attachments: InboundAttachment[];
|
|
41
|
+
/**
|
|
42
|
+
* Sender-authentication verdicts (DKIM/SPF/DMARC) parsed from the receiving
|
|
43
|
+
* MX's `Authentication-Results` header. SECURITY: see {@link InboundAuthentication}
|
|
44
|
+
* — `from` is spoofable; gate trust on these verdicts, not on `from`.
|
|
45
|
+
*/
|
|
46
|
+
authentication: InboundAuthentication;
|
|
47
|
+
/** Sender mailbox (`from`), CR/LF-checked. Empty string when the message omitted it. SECURITY: spoofable — do not trust for authorization. */
|
|
48
|
+
from: string;
|
|
49
|
+
/** Flattened `key → value` of the parsed headers (lowercase keys), each value CR/LF-checked. */
|
|
50
|
+
headers: Record<string, string>;
|
|
51
|
+
/** HTML body, when present. */
|
|
52
|
+
html?: string;
|
|
53
|
+
/** `In-Reply-To` header (threading), CR/LF-checked. */
|
|
54
|
+
inReplyTo?: string;
|
|
55
|
+
/** `Message-ID` of this message, CR/LF-checked. */
|
|
56
|
+
messageId?: string;
|
|
57
|
+
/** `References` header (threading), CR/LF-checked. */
|
|
58
|
+
references?: string;
|
|
59
|
+
/** Subject line, CR/LF-checked. */
|
|
60
|
+
subject?: string;
|
|
61
|
+
/** Plain-text body, when present. */
|
|
62
|
+
text?: string;
|
|
63
|
+
/** Recipient mailboxes (`to`), each CR/LF-checked. */
|
|
64
|
+
to: string[];
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Parse a raw RFC 822 message into a normalised {@link InboundEmail}. Accepts the
|
|
68
|
+
* shapes a Cloudflare Email Worker can hand off — `ReadableStream`, `ArrayBuffer`,
|
|
69
|
+
* `Uint8Array`, or a decoded string.
|
|
70
|
+
*/
|
|
71
|
+
declare const parseInboundEmail: (raw: RawInboundEmail) => Promise<InboundEmail>;
|
|
72
|
+
/**
|
|
73
|
+
* Structural projection of Cloudflare's `ForwardableEmailMessage` (verified
|
|
74
|
+
* against `@cloudflare/workers-types`' `ForwardableEmailMessage`). Only the
|
|
75
|
+
* members the handler touches are modelled, so the host can pass the real
|
|
76
|
+
* runtime object without `@lunora/mail` importing `cloudflare:email`.
|
|
77
|
+
*/
|
|
78
|
+
interface ForwardableEmailMessageLike {
|
|
79
|
+
/** Forward this message to a verified destination address. */
|
|
80
|
+
forward: (rcptTo: string, headers?: Headers) => Promise<unknown>;
|
|
81
|
+
/** Envelope `From`. */
|
|
82
|
+
readonly from: string;
|
|
83
|
+
/** Parsed top-level headers. */
|
|
84
|
+
readonly headers: Headers;
|
|
85
|
+
/** Stream of the raw RFC 822 message content. */
|
|
86
|
+
readonly raw: ReadableStream<Uint8Array>;
|
|
87
|
+
/** Reply to the sender with a new message. */
|
|
88
|
+
reply: (message: {
|
|
89
|
+
from: string;
|
|
90
|
+
raw: string;
|
|
91
|
+
to: string;
|
|
92
|
+
}) => Promise<unknown>;
|
|
93
|
+
/** Reject the message with a permanent SMTP error (Cloudflare bounces/retries). */
|
|
94
|
+
setReject: (reason: string) => void;
|
|
95
|
+
/** Envelope `To`. */
|
|
96
|
+
readonly to: string;
|
|
97
|
+
}
|
|
98
|
+
/** Context threaded to a `dispatch` callback alongside the parsed message. */
|
|
99
|
+
interface InboundDispatchContext<TEnv = Record<string, unknown>> {
|
|
100
|
+
/** Cloudflare's `ExecutionContext`, forwarded verbatim from the `email()` entry. */
|
|
101
|
+
ctx: unknown;
|
|
102
|
+
/** Worker `env` (bindings, vars, secrets) projected as a plain record. */
|
|
103
|
+
env: TEnv;
|
|
104
|
+
/** The originating message, for `setReject`/`forward`/`reply`. */
|
|
105
|
+
message: ForwardableEmailMessageLike;
|
|
106
|
+
}
|
|
107
|
+
/** Routes a parsed message into a Lunora function (or anywhere). */
|
|
108
|
+
type InboundDispatch<TEnv = Record<string, unknown>> = (email: InboundEmail, context: InboundDispatchContext<TEnv>) => Promise<void>;
|
|
109
|
+
/**
|
|
110
|
+
* Opt-in sender-verification gate. Runs after `parse` and before `dispatch` with
|
|
111
|
+
* the parsed message. Return `false` (or throw) to reject the message before it
|
|
112
|
+
* reaches the privileged dispatch — use it to enforce DKIM/SPF/DMARC via
|
|
113
|
+
* `email.authentication`, an allow-list, etc. Returning `true`/`undefined`
|
|
114
|
+
* proceeds.
|
|
115
|
+
*/
|
|
116
|
+
type InboundVerify<TEnv = Record<string, unknown>> = (email: InboundEmail, context: InboundDispatchContext<TEnv>) => Promise<boolean | void> | boolean | void;
|
|
117
|
+
/** Options for {@link createInboundEmailHandler}. */
|
|
118
|
+
interface InboundEmailHandlerOptions<TEnv = Record<string, unknown>> {
|
|
119
|
+
/** Routes the parsed message onward (e.g. {@link dispatchToLunoraFunction}). */
|
|
120
|
+
dispatch: InboundDispatch<TEnv>;
|
|
121
|
+
/**
|
|
122
|
+
* Called when `parse`/`verify`/`dispatch` throws. The default rejects the
|
|
123
|
+
* message via `message.setReject` so Cloudflare bounces/retries rather than
|
|
124
|
+
* silently dropping it. SECURITY: the reject reason is delivered to the
|
|
125
|
+
* (attacker-controlled) sender as a bounce, so the default reason is a fixed,
|
|
126
|
+
* generic string and the real error is logged server-side. Override to log,
|
|
127
|
+
* forward, or swallow — but never pass internal error text to `setReject`.
|
|
128
|
+
*/
|
|
129
|
+
onError?: (error: unknown, context: InboundDispatchContext<TEnv>) => Promise<void> | void;
|
|
130
|
+
/** Parses raw bytes into an {@link InboundEmail} (e.g. `parseInboundEmail`). */
|
|
131
|
+
parse: (raw: RawInboundEmail) => Promise<InboundEmail>;
|
|
132
|
+
/**
|
|
133
|
+
* Opt-in sender-authentication gate run before `dispatch`. SECURITY: inbound
|
|
134
|
+
* `from` is spoofable and dispatch is privileged — supply this (gating on
|
|
135
|
+
* `email.authentication`) when an inbound function makes any trust decision.
|
|
136
|
+
*/
|
|
137
|
+
verify?: InboundVerify<TEnv>;
|
|
138
|
+
}
|
|
139
|
+
/** The `email(message, env, ctx)` callback the factory returns. */
|
|
140
|
+
type InboundEmailHandler<TEnv = Record<string, unknown>> = (message: ForwardableEmailMessageLike, env: TEnv, context: unknown) => Promise<void>;
|
|
141
|
+
/**
|
|
142
|
+
* Build the `email(message, env, ctx)` handler. It (a) reads `message.raw`,
|
|
143
|
+
* (b) parses it via `parse`, (c) runs the optional `verify` gate, then
|
|
144
|
+
* (d) calls `dispatch(parsed, { message, env, ctx })`. Any throw (or a falsy
|
|
145
|
+
* `verify`) routes through `onError` (default: a generic `message.setReject`).
|
|
146
|
+
*/
|
|
147
|
+
declare const createInboundEmailHandler: <TEnv = Record<string, unknown>>(options: InboundEmailHandlerOptions<TEnv>) => InboundEmailHandler<TEnv>;
|
|
148
|
+
/** The `RpcEnvelope` shape the runtime's `/_lunora/rpc` path consumes. */
|
|
149
|
+
interface RpcEnvelope {
|
|
150
|
+
args: unknown;
|
|
151
|
+
functionPath: string;
|
|
152
|
+
shardKey?: string;
|
|
153
|
+
}
|
|
154
|
+
/** Options for {@link dispatchToLunoraFunction}. */
|
|
155
|
+
interface DispatchToLunoraFunctionOptions<TEnv = Record<string, unknown>> {
|
|
156
|
+
/**
|
|
157
|
+
* Admin bearer authorizing the shard RPC. Defaults to reading
|
|
158
|
+
* `env.LUNORA_ADMIN_TOKEN` at dispatch time.
|
|
159
|
+
*/
|
|
160
|
+
adminToken?: string;
|
|
161
|
+
/** `functionPath` of the target mutation/action (e.g. `"inbound:onEmail"`). */
|
|
162
|
+
functionPath: string;
|
|
163
|
+
/**
|
|
164
|
+
* Pin inbound dispatch to a Cloudflare data-residency jurisdiction. Pass the
|
|
165
|
+
* same value as the worker's `jurisdiction` so inbound mail routes to the
|
|
166
|
+
* jurisdiction-pinned shard. Omit for the un-pinned global namespace.
|
|
167
|
+
*/
|
|
168
|
+
jurisdiction?: DurableObjectJurisdiction;
|
|
169
|
+
/**
|
|
170
|
+
* Map the parsed message into the function's args. Defaults to passing the
|
|
171
|
+
* whole {@link InboundEmail} with binary attachment `content` base64-encoded
|
|
172
|
+
* (see {@link toJsonSafeEmail}) so it survives the JSON-serialised RPC body.
|
|
173
|
+
*/
|
|
174
|
+
resolveArgs?: (email: InboundEmail, context: InboundDispatchContext<TEnv>) => unknown;
|
|
175
|
+
/** The `SHARD` Durable Object namespace. */
|
|
176
|
+
shard: ShardNamespaceLike;
|
|
177
|
+
/** Shard the function runs on. Defaults to the runtime's default root shard. */
|
|
178
|
+
shardKey?: string;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Build a {@link InboundDispatch} that posts an {@link RpcEnvelope} to the root
|
|
182
|
+
* shard stub — the same admin-RPC-over-shard path the dev capture sink uses
|
|
183
|
+
* (`from-env.ts`) — routing the parsed message into a named Lunora
|
|
184
|
+
* mutation/action. Throws on a non-2xx RPC or a missing admin token so the
|
|
185
|
+
* handler's `onError` (default `setReject`) bounces the message.
|
|
186
|
+
*
|
|
187
|
+
* SECURITY: the RPC carries the admin bearer, so the target function runs with
|
|
188
|
+
* RLS bypassed over fully attacker-controlled, spoofable input — see the module
|
|
189
|
+
* docstring. Verify the sender (`verify` hook / `email.authentication`) before
|
|
190
|
+
* making any trust decision in the target function.
|
|
191
|
+
*/
|
|
192
|
+
declare const dispatchToLunoraFunction: <TEnv extends Record<string, unknown> = Record<string, unknown>>(options: DispatchToLunoraFunctionOptions<TEnv>) => InboundDispatch<TEnv>;
|
|
193
|
+
export { type DispatchToLunoraFunctionOptions, type ForwardableEmailMessageLike, type InboundAttachment, type InboundAuthentication, type InboundDispatch, type InboundDispatchContext, type InboundEmail, type InboundEmailHandler, type InboundEmailHandlerOptions, type InboundVerify, type RawInboundEmail, type RpcEnvelope, type ShardNamespaceLike, createInboundEmailHandler, dispatchToLunoraFunction, parseInboundEmail };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { M as MailTransport, L as LunoraMailOptions, a as Mailer, b as MailboxSink, S as SendOptions } from "./packem_shared/capture-transport.d-ChnhdPO2.mjs";
|
|
2
|
+
export { type C as CapturedMail, type Q as QueueLike, type c as SendPayload, d as createCaptureTransport } from "./packem_shared/capture-transport.d-ChnhdPO2.mjs";
|
|
3
|
+
import { D as DurableObjectJurisdiction } from "./packem_shared/shard.d-CL2Lmliv.mjs";
|
|
4
|
+
import { ReactElement } from 'react';
|
|
5
|
+
/**
|
|
6
|
+
* Sends a raw RFC 822 message through a Worker's Email send binding. The Workers
|
|
7
|
+
* runtime owns the binding, so the caller supplies this thin callback — keeping
|
|
8
|
+
* `@lunora/mail` free of a `cloudflare:email` import and unit-testable. Wire it
|
|
9
|
+
* in your project as:
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* send: async (from, to, raw) => {
|
|
13
|
+
* const { EmailMessage } = await import("cloudflare:email");
|
|
14
|
+
* await env.SEND_EMAIL.send(new EmailMessage(from, to, raw));
|
|
15
|
+
* }
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
type CloudflareSend = (from: string, to: string, raw: string) => Promise<void>;
|
|
19
|
+
interface CloudflareTransportOptions {
|
|
20
|
+
/** Default sender used when a `SendOptions.from` isn't supplied. */
|
|
21
|
+
from: string;
|
|
22
|
+
/** RFC 822 send callback bound to the Worker's `send_email` binding. */
|
|
23
|
+
send: CloudflareSend;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Build the default Cloudflare Email Workers transport via `@visulima/email`.
|
|
27
|
+
* Cloudflare's `send_email` binding is **single-recipient** and only delivers to
|
|
28
|
+
* **verified Email Routing destination addresses**, and the underlying provider
|
|
29
|
+
* rejects any `cc`/`bcc` or a non-single `to` outright. So this enforces a single
|
|
30
|
+
* `to` recipient (throwing a clear error instead of silently dropping the rest)
|
|
31
|
+
* and rejects `cc`/`bcc` rather than forwarding them into a generic "send failed".
|
|
32
|
+
* Multi-recipient transactional mail must fan out one send per recipient at the
|
|
33
|
+
* call site. In dev the capture transport intercepts before this runs, so the
|
|
34
|
+
* verified-address constraint never bites the dev loop.
|
|
35
|
+
*/
|
|
36
|
+
declare const createCloudflareTransport: (options: CloudflareTransportOptions) => MailTransport;
|
|
37
|
+
declare const createMailer: (options: LunoraMailOptions) => Mailer;
|
|
38
|
+
/** A Worker `env` projected as a plain record (vars, secrets, and bindings are `unknown`-valued). */
|
|
39
|
+
type MailEnv = Record<string, unknown>;
|
|
40
|
+
/** Options for {@link createMailerFromEnv}. */
|
|
41
|
+
interface FromEnvOptions {
|
|
42
|
+
/** RFC 822 send callback bound to the Worker's `send_email` binding (Cloudflare default transport). */
|
|
43
|
+
cloudflareSend?: CloudflareSend;
|
|
44
|
+
/**
|
|
45
|
+
* Pin the captured-mail inbox shard to a Cloudflare data-residency
|
|
46
|
+
* jurisdiction. Pass the same value as the worker's `jurisdiction` so the
|
|
47
|
+
* dev inbox co-resides with app data. Omit for the un-pinned global namespace.
|
|
48
|
+
*/
|
|
49
|
+
jurisdiction?: DurableObjectJurisdiction;
|
|
50
|
+
/** Shard the captured-mail inbox lives on; override if your worker sets a custom `defaultShardKey`. */
|
|
51
|
+
rootShard?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Whether outbound mail should be captured (into the studio inbox) rather than
|
|
55
|
+
* delivered. Explicit `LUNORA_MAIL_CAPTURE` (`"1"`/`"true"` vs `"0"`/`"false"`)
|
|
56
|
+
* always wins; unset, capture is on only in a development environment. It does
|
|
57
|
+
* NOT fall back to "no SEND_EMAIL binding ⇒ capture" — a production deploy that
|
|
58
|
+
* forgot the binding must fail loudly on send, not silently swallow mail.
|
|
59
|
+
*/
|
|
60
|
+
declare const shouldCaptureMail: (env: MailEnv) => boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Build the {@link MailboxSink} that records a captured message into the studio's
|
|
63
|
+
* root-shard inbox via the reserved `recordMail` admin RPC — the same
|
|
64
|
+
* worker→root-shard path the runtime uses for auth events. Best-effort: without
|
|
65
|
+
* the `SHARD` binding or `LUNORA_ADMIN_TOKEN` it returns a sentinel id so a send
|
|
66
|
+
* never fails for lack of somewhere to record.
|
|
67
|
+
*/
|
|
68
|
+
declare const createCaptureSink: (env: MailEnv, rootShard?: string, jurisdiction?: DurableObjectJurisdiction) => MailboxSink;
|
|
69
|
+
/**
|
|
70
|
+
* Build a {@link Mailer} from a Worker `env`. In a dev environment every send is
|
|
71
|
+
* captured into the studio's Mail inbox; otherwise it delivers via the supplied
|
|
72
|
+
* `cloudflareSend` (the `SEND_EMAIL` binding) or, failing that, `RESEND_API_KEY`.
|
|
73
|
+
* Throws when neither a capture context nor a real transport is available, so a
|
|
74
|
+
* misconfigured production deploy fails loudly instead of silently dropping mail.
|
|
75
|
+
*
|
|
76
|
+
* `MAIL_FROM` is required (the default sender).
|
|
77
|
+
*/
|
|
78
|
+
declare const createMailerFromEnv: (env: MailEnv, options?: FromEnvOptions) => Mailer;
|
|
79
|
+
/** Serializable representation of a `SendOptions` payload — drops the `react` field. */
|
|
80
|
+
interface QueuedSend {
|
|
81
|
+
bcc?: string[];
|
|
82
|
+
cc?: string[];
|
|
83
|
+
from?: string;
|
|
84
|
+
headers?: Record<string, string>;
|
|
85
|
+
html?: string;
|
|
86
|
+
replyTo?: string;
|
|
87
|
+
subject: string;
|
|
88
|
+
text?: string;
|
|
89
|
+
to: string | string[];
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Narrow a `SendOptions` to its serializable `QueuedSend` shape by dropping the
|
|
93
|
+
* non-cloneable `react` field. React elements are not structured-cloneable, so
|
|
94
|
+
* the queue body must carry only the pre-rendered html/text and scalar fields.
|
|
95
|
+
*/
|
|
96
|
+
declare const toQueuedPayload: (options: SendOptions) => QueuedSend;
|
|
97
|
+
/**
|
|
98
|
+
* Helper used by Queue consumers: rehydrate a `QueuedSend` payload and forward
|
|
99
|
+
* to a configured `Mailer.send()`. Use this inside your Worker's `queue()`
|
|
100
|
+
* handler.
|
|
101
|
+
*
|
|
102
|
+
* ```ts
|
|
103
|
+
* export default {
|
|
104
|
+
* queue: async (batch, env) => {
|
|
105
|
+
* const mailer = createMailer({ apiKey: env.RESEND_API_KEY, from: "..." });
|
|
106
|
+
* for (const message of batch.messages) {
|
|
107
|
+
* await consumeQueuedSend(mailer, message.body);
|
|
108
|
+
* }
|
|
109
|
+
* },
|
|
110
|
+
* };
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
declare const consumeQueuedSend: (mailer: Mailer, payload: unknown) => Promise<{
|
|
114
|
+
id: string;
|
|
115
|
+
}>;
|
|
116
|
+
/**
|
|
117
|
+
* Render a React element to an HTML/text pair suitable for inlining into a
|
|
118
|
+
* provider payload. Wraps `@react-email/render` so we can swap to
|
|
119
|
+
* `@visulima/email`'s react-email template engine without touching callers.
|
|
120
|
+
*/
|
|
121
|
+
declare const renderEmail: (element: ReactElement) => Promise<{
|
|
122
|
+
html: string;
|
|
123
|
+
text: string;
|
|
124
|
+
}>;
|
|
125
|
+
/**
|
|
126
|
+
* Build a Resend-backed transport via `@visulima/email`. Wraps the provider's
|
|
127
|
+
* `sendEmail()` into the minimal `{ send(payload) -> { id } }` shape the rest of
|
|
128
|
+
* `@lunora/mail` consumes. Kept reachable as a named export so a project that
|
|
129
|
+
* prefers Resend over the Cloudflare default can pass
|
|
130
|
+
* `transport: createResendTransport(apiKey, from)` to `createMailer`.
|
|
131
|
+
*/
|
|
132
|
+
declare const createResendTransport: (apiKey: string, defaultFrom: string) => MailTransport;
|
|
133
|
+
export { type CloudflareSend, type CloudflareTransportOptions, type FromEnvOptions, type LunoraMailOptions, type MailEnv, type MailTransport, type MailboxSink, type Mailer, type QueuedSend, type SendOptions, consumeQueuedSend, createCaptureSink, createCloudflareTransport, createMailer, createMailerFromEnv, createResendTransport, renderEmail, shouldCaptureMail, toQueuedPayload };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { M as MailTransport, L as LunoraMailOptions, a as Mailer, b as MailboxSink, S as SendOptions } from "./packem_shared/capture-transport.d-ChnhdPO2.js";
|
|
2
|
+
export { type C as CapturedMail, type Q as QueueLike, type c as SendPayload, d as createCaptureTransport } from "./packem_shared/capture-transport.d-ChnhdPO2.js";
|
|
3
|
+
import { D as DurableObjectJurisdiction } from "./packem_shared/shard.d-CL2Lmliv.js";
|
|
4
|
+
import { ReactElement } from 'react';
|
|
5
|
+
/**
|
|
6
|
+
* Sends a raw RFC 822 message through a Worker's Email send binding. The Workers
|
|
7
|
+
* runtime owns the binding, so the caller supplies this thin callback — keeping
|
|
8
|
+
* `@lunora/mail` free of a `cloudflare:email` import and unit-testable. Wire it
|
|
9
|
+
* in your project as:
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* send: async (from, to, raw) => {
|
|
13
|
+
* const { EmailMessage } = await import("cloudflare:email");
|
|
14
|
+
* await env.SEND_EMAIL.send(new EmailMessage(from, to, raw));
|
|
15
|
+
* }
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
type CloudflareSend = (from: string, to: string, raw: string) => Promise<void>;
|
|
19
|
+
interface CloudflareTransportOptions {
|
|
20
|
+
/** Default sender used when a `SendOptions.from` isn't supplied. */
|
|
21
|
+
from: string;
|
|
22
|
+
/** RFC 822 send callback bound to the Worker's `send_email` binding. */
|
|
23
|
+
send: CloudflareSend;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Build the default Cloudflare Email Workers transport via `@visulima/email`.
|
|
27
|
+
* Cloudflare's `send_email` binding is **single-recipient** and only delivers to
|
|
28
|
+
* **verified Email Routing destination addresses**, and the underlying provider
|
|
29
|
+
* rejects any `cc`/`bcc` or a non-single `to` outright. So this enforces a single
|
|
30
|
+
* `to` recipient (throwing a clear error instead of silently dropping the rest)
|
|
31
|
+
* and rejects `cc`/`bcc` rather than forwarding them into a generic "send failed".
|
|
32
|
+
* Multi-recipient transactional mail must fan out one send per recipient at the
|
|
33
|
+
* call site. In dev the capture transport intercepts before this runs, so the
|
|
34
|
+
* verified-address constraint never bites the dev loop.
|
|
35
|
+
*/
|
|
36
|
+
declare const createCloudflareTransport: (options: CloudflareTransportOptions) => MailTransport;
|
|
37
|
+
declare const createMailer: (options: LunoraMailOptions) => Mailer;
|
|
38
|
+
/** A Worker `env` projected as a plain record (vars, secrets, and bindings are `unknown`-valued). */
|
|
39
|
+
type MailEnv = Record<string, unknown>;
|
|
40
|
+
/** Options for {@link createMailerFromEnv}. */
|
|
41
|
+
interface FromEnvOptions {
|
|
42
|
+
/** RFC 822 send callback bound to the Worker's `send_email` binding (Cloudflare default transport). */
|
|
43
|
+
cloudflareSend?: CloudflareSend;
|
|
44
|
+
/**
|
|
45
|
+
* Pin the captured-mail inbox shard to a Cloudflare data-residency
|
|
46
|
+
* jurisdiction. Pass the same value as the worker's `jurisdiction` so the
|
|
47
|
+
* dev inbox co-resides with app data. Omit for the un-pinned global namespace.
|
|
48
|
+
*/
|
|
49
|
+
jurisdiction?: DurableObjectJurisdiction;
|
|
50
|
+
/** Shard the captured-mail inbox lives on; override if your worker sets a custom `defaultShardKey`. */
|
|
51
|
+
rootShard?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Whether outbound mail should be captured (into the studio inbox) rather than
|
|
55
|
+
* delivered. Explicit `LUNORA_MAIL_CAPTURE` (`"1"`/`"true"` vs `"0"`/`"false"`)
|
|
56
|
+
* always wins; unset, capture is on only in a development environment. It does
|
|
57
|
+
* NOT fall back to "no SEND_EMAIL binding ⇒ capture" — a production deploy that
|
|
58
|
+
* forgot the binding must fail loudly on send, not silently swallow mail.
|
|
59
|
+
*/
|
|
60
|
+
declare const shouldCaptureMail: (env: MailEnv) => boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Build the {@link MailboxSink} that records a captured message into the studio's
|
|
63
|
+
* root-shard inbox via the reserved `recordMail` admin RPC — the same
|
|
64
|
+
* worker→root-shard path the runtime uses for auth events. Best-effort: without
|
|
65
|
+
* the `SHARD` binding or `LUNORA_ADMIN_TOKEN` it returns a sentinel id so a send
|
|
66
|
+
* never fails for lack of somewhere to record.
|
|
67
|
+
*/
|
|
68
|
+
declare const createCaptureSink: (env: MailEnv, rootShard?: string, jurisdiction?: DurableObjectJurisdiction) => MailboxSink;
|
|
69
|
+
/**
|
|
70
|
+
* Build a {@link Mailer} from a Worker `env`. In a dev environment every send is
|
|
71
|
+
* captured into the studio's Mail inbox; otherwise it delivers via the supplied
|
|
72
|
+
* `cloudflareSend` (the `SEND_EMAIL` binding) or, failing that, `RESEND_API_KEY`.
|
|
73
|
+
* Throws when neither a capture context nor a real transport is available, so a
|
|
74
|
+
* misconfigured production deploy fails loudly instead of silently dropping mail.
|
|
75
|
+
*
|
|
76
|
+
* `MAIL_FROM` is required (the default sender).
|
|
77
|
+
*/
|
|
78
|
+
declare const createMailerFromEnv: (env: MailEnv, options?: FromEnvOptions) => Mailer;
|
|
79
|
+
/** Serializable representation of a `SendOptions` payload — drops the `react` field. */
|
|
80
|
+
interface QueuedSend {
|
|
81
|
+
bcc?: string[];
|
|
82
|
+
cc?: string[];
|
|
83
|
+
from?: string;
|
|
84
|
+
headers?: Record<string, string>;
|
|
85
|
+
html?: string;
|
|
86
|
+
replyTo?: string;
|
|
87
|
+
subject: string;
|
|
88
|
+
text?: string;
|
|
89
|
+
to: string | string[];
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Narrow a `SendOptions` to its serializable `QueuedSend` shape by dropping the
|
|
93
|
+
* non-cloneable `react` field. React elements are not structured-cloneable, so
|
|
94
|
+
* the queue body must carry only the pre-rendered html/text and scalar fields.
|
|
95
|
+
*/
|
|
96
|
+
declare const toQueuedPayload: (options: SendOptions) => QueuedSend;
|
|
97
|
+
/**
|
|
98
|
+
* Helper used by Queue consumers: rehydrate a `QueuedSend` payload and forward
|
|
99
|
+
* to a configured `Mailer.send()`. Use this inside your Worker's `queue()`
|
|
100
|
+
* handler.
|
|
101
|
+
*
|
|
102
|
+
* ```ts
|
|
103
|
+
* export default {
|
|
104
|
+
* queue: async (batch, env) => {
|
|
105
|
+
* const mailer = createMailer({ apiKey: env.RESEND_API_KEY, from: "..." });
|
|
106
|
+
* for (const message of batch.messages) {
|
|
107
|
+
* await consumeQueuedSend(mailer, message.body);
|
|
108
|
+
* }
|
|
109
|
+
* },
|
|
110
|
+
* };
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
declare const consumeQueuedSend: (mailer: Mailer, payload: unknown) => Promise<{
|
|
114
|
+
id: string;
|
|
115
|
+
}>;
|
|
116
|
+
/**
|
|
117
|
+
* Render a React element to an HTML/text pair suitable for inlining into a
|
|
118
|
+
* provider payload. Wraps `@react-email/render` so we can swap to
|
|
119
|
+
* `@visulima/email`'s react-email template engine without touching callers.
|
|
120
|
+
*/
|
|
121
|
+
declare const renderEmail: (element: ReactElement) => Promise<{
|
|
122
|
+
html: string;
|
|
123
|
+
text: string;
|
|
124
|
+
}>;
|
|
125
|
+
/**
|
|
126
|
+
* Build a Resend-backed transport via `@visulima/email`. Wraps the provider's
|
|
127
|
+
* `sendEmail()` into the minimal `{ send(payload) -> { id } }` shape the rest of
|
|
128
|
+
* `@lunora/mail` consumes. Kept reachable as a named export so a project that
|
|
129
|
+
* prefers Resend over the Cloudflare default can pass
|
|
130
|
+
* `transport: createResendTransport(apiKey, from)` to `createMailer`.
|
|
131
|
+
*/
|
|
132
|
+
declare const createResendTransport: (apiKey: string, defaultFrom: string) => MailTransport;
|
|
133
|
+
export { type CloudflareSend, type CloudflareTransportOptions, type FromEnvOptions, type LunoraMailOptions, type MailEnv, type MailTransport, type MailboxSink, type Mailer, type QueuedSend, type SendOptions, consumeQueuedSend, createCaptureSink, createCloudflareTransport, createMailer, createMailerFromEnv, createResendTransport, renderEmail, shouldCaptureMail, toQueuedPayload };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { createCaptureTransport } from './packem_shared/createCaptureTransport-Crz_8822.mjs';
|
|
2
|
+
export { createCloudflareTransport } from './packem_shared/createCloudflareTransport-yHOVEsZv.mjs';
|
|
3
|
+
export { default as createMailer } from './packem_shared/createMailer-oEKPAd4J.mjs';
|
|
4
|
+
export { createCaptureSink, createMailerFromEnv, shouldCaptureMail } from './packem_shared/createCaptureSink-DeihS4LH.mjs';
|
|
5
|
+
export { consumeQueuedSend, toQueuedPayload } from './packem_shared/consumeQueuedSend-BEKOdaxU.mjs';
|
|
6
|
+
export { default as renderEmail } from './packem_shared/renderEmail-hyS1bpVP.mjs';
|
|
7
|
+
export { default as createResendTransport } from './packem_shared/createResendTransport-oNIorpzv.mjs';
|