@holo-js/mail 0.1.3

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/dist/index.mjs ADDED
@@ -0,0 +1,1321 @@
1
+ import {
2
+ attachContent,
3
+ attachFromPath,
4
+ attachFromStorage,
5
+ createAttachmentMetadata,
6
+ createAttachmentResolutionPlan,
7
+ createAttachmentResolutionPlans,
8
+ defineMail,
9
+ inferMimeTypeFromName,
10
+ isAttachmentQueueSafe,
11
+ isMailDefinition,
12
+ mailInternals,
13
+ mergeMailDefinitionInputs,
14
+ normalizeMailDefinition,
15
+ resolveAttachmentDefinition,
16
+ resolveNormalizedAttachment
17
+ } from "./chunk-UB6ANQ2F.mjs";
18
+
19
+ // src/runtime.ts
20
+ import { randomUUID } from "crypto";
21
+ import { mkdir, writeFile } from "fs/promises";
22
+ import { join } from "path";
23
+ import {
24
+ holoMailDefaults,
25
+ normalizeAppEnv
26
+ } from "@holo-js/config";
27
+
28
+ // src/registry.ts
29
+ function normalizeDriverName(value) {
30
+ const normalized = value.trim();
31
+ if (!normalized) {
32
+ throw new Error("[@holo-js/mail] Mail driver names must be non-empty strings.");
33
+ }
34
+ return normalized;
35
+ }
36
+ function getRegistry() {
37
+ const runtime = globalThis;
38
+ runtime.__holoMailDriverRegistry__ ??= /* @__PURE__ */ new Map();
39
+ return runtime.__holoMailDriverRegistry__;
40
+ }
41
+ function registerMailDriver(name, driver, options = {}) {
42
+ const normalizedName = normalizeDriverName(name);
43
+ if (typeof driver?.send !== "function") {
44
+ throw new Error(`[@holo-js/mail] Mail driver "${normalizedName}" must define send().`);
45
+ }
46
+ const registry = getRegistry();
47
+ if (registry.has(normalizedName) && options.replaceExisting !== true) {
48
+ throw new Error(`[@holo-js/mail] Mail driver "${normalizedName}" is already registered.`);
49
+ }
50
+ registry.set(normalizedName, Object.freeze({
51
+ name: normalizedName,
52
+ driver
53
+ }));
54
+ }
55
+ function getRegisteredMailDriver(name) {
56
+ return getRegistry().get(normalizeDriverName(name));
57
+ }
58
+ function listRegisteredMailDrivers() {
59
+ return Object.freeze([...getRegistry().values()]);
60
+ }
61
+ function resetMailDriverRegistry() {
62
+ getRegistry().clear();
63
+ }
64
+ var mailRegistryInternals = {
65
+ getRegistry,
66
+ normalizeDriverName
67
+ };
68
+
69
+ // src/runtime.ts
70
+ var HOLO_MAIL_DELIVER_JOB = "holo.mail.deliver";
71
+ var MailError = class extends Error {
72
+ code;
73
+ constructor(message, code = "MAIL_ERROR", options) {
74
+ super(message, options);
75
+ this.name = "MailError";
76
+ this.code = code;
77
+ }
78
+ };
79
+ var MailPreviewDisabledError = class extends MailError {
80
+ policy;
81
+ constructor(policy) {
82
+ super(
83
+ `[@holo-js/mail] Mail preview is disabled for the "${policy.environment}" environment.`,
84
+ "MAIL_PREVIEW_DISABLED"
85
+ );
86
+ this.name = "MailPreviewDisabledError";
87
+ this.policy = policy;
88
+ }
89
+ };
90
+ var MailPreviewFormatUnavailableError = class extends MailError {
91
+ format;
92
+ constructor(format) {
93
+ super(
94
+ `[@holo-js/mail] Mail ${format} preview is unavailable for this message.`,
95
+ "MAIL_PREVIEW_FORMAT_UNAVAILABLE"
96
+ );
97
+ this.name = "MailPreviewFormatUnavailableError";
98
+ this.format = format;
99
+ }
100
+ };
101
+ var MailSendError = class extends MailError {
102
+ messageId;
103
+ mailer;
104
+ driver;
105
+ constructor(details, options) {
106
+ super(
107
+ details.message ?? `[@holo-js/mail] Mail delivery failed for mailer "${details.mailer}" using driver "${details.driver}".`,
108
+ "MAIL_SEND_FAILED",
109
+ options
110
+ );
111
+ this.name = "MailSendError";
112
+ this.messageId = details.messageId;
113
+ this.mailer = details.mailer;
114
+ this.driver = details.driver;
115
+ }
116
+ };
117
+ function getRuntimeState() {
118
+ const runtime = globalThis;
119
+ runtime.__holoMailRuntime__ ??= {};
120
+ return runtime.__holoMailRuntime__;
121
+ }
122
+ function getRuntimeBindings() {
123
+ return getRuntimeState().bindings ?? {};
124
+ }
125
+ function dynamicImport(specifier) {
126
+ return import(
127
+ /* webpackIgnore: true */
128
+ specifier
129
+ );
130
+ }
131
+ async function loadQueueModule() {
132
+ const override = getRuntimeState().loadQueueModule;
133
+ if (override) {
134
+ try {
135
+ return await override();
136
+ } catch (error) {
137
+ if (error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") {
138
+ throw new MailError(
139
+ "[@holo-js/mail] Queued or delayed mail delivery requires @holo-js/queue to be installed.",
140
+ "MAIL_QUEUE_MODULE_MISSING"
141
+ );
142
+ }
143
+ throw error;
144
+ }
145
+ }
146
+ try {
147
+ return await dynamicImport("@holo-js/queue");
148
+ } catch (error) {
149
+ if (error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") {
150
+ throw new MailError(
151
+ "[@holo-js/mail] Queued or delayed mail delivery requires @holo-js/queue to be installed.",
152
+ "MAIL_QUEUE_MODULE_MISSING"
153
+ );
154
+ }
155
+ throw error;
156
+ }
157
+ }
158
+ async function loadDbModule() {
159
+ const override = getRuntimeState().loadDbModule;
160
+ if (override) {
161
+ try {
162
+ return await override();
163
+ } catch (error) {
164
+ if (error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") {
165
+ return null;
166
+ }
167
+ throw error;
168
+ }
169
+ }
170
+ try {
171
+ return await dynamicImport("@holo-js/db");
172
+ } catch (error) {
173
+ if (error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") {
174
+ return null;
175
+ }
176
+ throw error;
177
+ }
178
+ }
179
+ function resolveNodemailerModule(module) {
180
+ if (module && typeof module === "object" && "createTransport" in module && typeof module.createTransport === "function") {
181
+ return module;
182
+ }
183
+ if (module && typeof module === "object" && "default" in module && module.default && typeof module.default?.createTransport === "function") {
184
+ return module.default;
185
+ }
186
+ throw new MailError(
187
+ "[@holo-js/mail] Nodemailer could not be loaded for SMTP delivery.",
188
+ "MAIL_SMTP_MODULE_INVALID"
189
+ );
190
+ }
191
+ async function loadNodemailerModule() {
192
+ const override = getRuntimeState().loadNodemailerModule;
193
+ if (override) {
194
+ try {
195
+ return resolveNodemailerModule(await override());
196
+ } catch (error) {
197
+ if (error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") {
198
+ throw new MailError(
199
+ "[@holo-js/mail] SMTP delivery requires nodemailer to be installed.",
200
+ "MAIL_SMTP_MODULE_MISSING",
201
+ { cause: error }
202
+ );
203
+ }
204
+ throw error;
205
+ }
206
+ }
207
+ try {
208
+ return resolveNodemailerModule(await dynamicImport("nodemailer"));
209
+ } catch (error) {
210
+ if (error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") {
211
+ throw new MailError(
212
+ "[@holo-js/mail] SMTP delivery requires nodemailer to be installed.",
213
+ "MAIL_SMTP_MODULE_MISSING",
214
+ { cause: error }
215
+ );
216
+ }
217
+ throw error;
218
+ }
219
+ }
220
+ function resolveStorageModule(module) {
221
+ if (module && typeof module === "object" && "Storage" in module && module.Storage && typeof module.Storage?.disk === "function") {
222
+ return module;
223
+ }
224
+ throw new MailError(
225
+ "[@holo-js/mail] The storage runtime could not be loaded for storage-backed attachments.",
226
+ "MAIL_STORAGE_MODULE_INVALID"
227
+ );
228
+ }
229
+ async function loadStorageModule() {
230
+ const override = getRuntimeState().loadStorageModule;
231
+ if (override) {
232
+ try {
233
+ return resolveStorageModule(await override());
234
+ } catch (error) {
235
+ if (error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") {
236
+ throw new MailError(
237
+ "[@holo-js/mail] Storage-backed attachments require @holo-js/storage to be installed.",
238
+ "MAIL_STORAGE_MODULE_MISSING",
239
+ { cause: error }
240
+ );
241
+ }
242
+ throw error;
243
+ }
244
+ }
245
+ try {
246
+ return resolveStorageModule(await dynamicImport("@holo-js/storage"));
247
+ } catch (error) {
248
+ if (error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") {
249
+ throw new MailError(
250
+ "[@holo-js/mail] Storage-backed attachments require @holo-js/storage to be installed.",
251
+ "MAIL_STORAGE_MODULE_MISSING",
252
+ { cause: error }
253
+ );
254
+ }
255
+ throw error;
256
+ }
257
+ }
258
+ function getFakeSentState() {
259
+ const state = getRuntimeState();
260
+ state.fakeSent ??= [];
261
+ return state.fakeSent;
262
+ }
263
+ function getPreviewArtifactState() {
264
+ const state = getRuntimeState();
265
+ state.previewArtifacts ??= [];
266
+ return state.previewArtifacts;
267
+ }
268
+ function normalizeExecutionString(value, label) {
269
+ const normalized = value.trim();
270
+ if (!normalized) {
271
+ throw new Error(`[@holo-js/mail] ${label} must be a non-empty string.`);
272
+ }
273
+ return normalized;
274
+ }
275
+ function normalizeExecutionDelay(value) {
276
+ if (typeof value === "number") {
277
+ if (!Number.isFinite(value) || value < 0) {
278
+ throw new Error("[@holo-js/mail] Mail delays must be finite numbers greater than or equal to 0.");
279
+ }
280
+ return value;
281
+ }
282
+ if (!(value instanceof Date) || Number.isNaN(value.getTime())) {
283
+ throw new Error("[@holo-js/mail] Mail delays must use valid Date instances.");
284
+ }
285
+ return value;
286
+ }
287
+ function getResolvedConfig() {
288
+ return getRuntimeBindings().config ?? holoMailDefaults;
289
+ }
290
+ function resolveCurrentEnvironment() {
291
+ return normalizeAppEnv(process.env.APP_ENV ?? process.env.NODE_ENV);
292
+ }
293
+ function createPreviewPolicy(config = getResolvedConfig()) {
294
+ return Object.freeze({
295
+ environment: resolveCurrentEnvironment(),
296
+ allowedEnvironments: config.preview.allowedEnvironments
297
+ });
298
+ }
299
+ function assertPreviewEnabled(config = getResolvedConfig()) {
300
+ const policy = createPreviewPolicy(config);
301
+ if (!policy.allowedEnvironments.includes(policy.environment)) {
302
+ throw new MailPreviewDisabledError(policy);
303
+ }
304
+ }
305
+ function createPreviewHtml(preview) {
306
+ const header = [
307
+ `<h1>${escapeHtml(preview.subject)}</h1>`,
308
+ `<p><strong>From:</strong> ${formatAddress(preview.from)}</p>`,
309
+ `<p><strong>Reply-To:</strong> ${formatAddress(preview.replyTo)}</p>`,
310
+ `<p><strong>To:</strong> ${preview.to.map(formatAddress).join(", ")}</p>`,
311
+ preview.cc.length > 0 ? `<p><strong>Cc:</strong> ${preview.cc.map(formatAddress).join(", ")}</p>` : "",
312
+ preview.bcc.length > 0 ? `<p><strong>Bcc:</strong> ${preview.bcc.map(formatAddress).join(", ")}</p>` : ""
313
+ ].filter(Boolean).join("");
314
+ const body = preview.html ? preview.html : preview.text ? `<pre>${escapeHtml(preview.text)}</pre>` : "<p>No rendered content is available.</p>";
315
+ return `<!doctype html><html><head><meta charset="utf-8"><title>${escapeHtml(preview.subject)}</title></head><body>${header}${body}</body></html>`;
316
+ }
317
+ function escapeHtml(value) {
318
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
319
+ }
320
+ function formatAddress(address) {
321
+ return address.name ? `${escapeHtml(address.name)} &lt;${escapeHtml(address.email)}&gt;` : escapeHtml(address.email);
322
+ }
323
+ function renderPreviewResponse(preview, format) {
324
+ if (format === "json") {
325
+ return new Response(JSON.stringify(preview, null, 2), {
326
+ status: 200,
327
+ headers: {
328
+ "content-type": "application/json; charset=utf-8"
329
+ }
330
+ });
331
+ }
332
+ if (format === "text") {
333
+ if (typeof preview.text !== "string") {
334
+ throw new MailPreviewFormatUnavailableError(format);
335
+ }
336
+ return new Response(preview.text, {
337
+ status: 200,
338
+ headers: {
339
+ "content-type": "text/plain; charset=utf-8"
340
+ }
341
+ });
342
+ }
343
+ return new Response(createPreviewHtml(preview), {
344
+ status: 200,
345
+ headers: {
346
+ "content-type": "text/html; charset=utf-8"
347
+ }
348
+ });
349
+ }
350
+ function getMailerConfig(mailer, config = getResolvedConfig()) {
351
+ const mailerConfig = config.mailers[mailer];
352
+ if (!mailerConfig) {
353
+ throw new MailError(
354
+ `[@holo-js/mail] Mailer "${mailer}" is not configured. Available mailers: ${Object.keys(config.mailers).join(", ")}`,
355
+ "MAILER_NOT_CONFIGURED"
356
+ );
357
+ }
358
+ return mailerConfig;
359
+ }
360
+ function isPreviewMailerConfig(config) {
361
+ return config.driver === "preview" && typeof config.path === "string";
362
+ }
363
+ function isSmtpMailerConfig(config) {
364
+ return config.driver === "smtp" && typeof config.host === "string" && typeof config.port === "number" && typeof config.secure === "boolean";
365
+ }
366
+ function isQueueRequested(mail2, mailer, options, config = getResolvedConfig()) {
367
+ const mailerConfig = getMailerConfig(mailer, config);
368
+ const mailQueue = mail2.queue;
369
+ if (typeof options.connection === "string" || typeof options.queue === "string" || typeof options.delay !== "undefined") {
370
+ return true;
371
+ }
372
+ if (mailQueue === true) {
373
+ return true;
374
+ }
375
+ if (mailQueue === false) {
376
+ return false;
377
+ }
378
+ if (typeof mailQueue === "object" && mailQueue !== null) {
379
+ if (typeof mailQueue.connection === "string" || typeof mailQueue.queue === "string") {
380
+ return true;
381
+ }
382
+ if (typeof mailQueue.queued === "boolean") {
383
+ return mailQueue.queued;
384
+ }
385
+ }
386
+ return mailerConfig.queue.queued === true || typeof mailerConfig.queue.connection === "string" || typeof mailerConfig.queue.queue === "string" || config.queue.queued === true || typeof config.queue.connection === "string" || typeof config.queue.queue === "string";
387
+ }
388
+ function resolveQueuePlan(mail2, options, mailer, config = getResolvedConfig()) {
389
+ const mailerConfig = getMailerConfig(mailer, config);
390
+ const mailQueue = typeof mail2.queue === "object" && mail2.queue !== null ? mail2.queue : void 0;
391
+ const queued = isQueueRequested(mail2, mailer, options, config);
392
+ return Object.freeze({
393
+ queued,
394
+ connection: queued ? options.connection ?? mailQueue?.connection ?? mailerConfig.queue.connection ?? config.queue.connection : void 0,
395
+ queue: queued ? options.queue ?? mailQueue?.queue ?? mailerConfig.queue.queue ?? config.queue.queue : void 0,
396
+ delay: queued ? options.delay ?? mail2.delay : void 0,
397
+ afterCommit: options.afterCommit ?? mailQueue?.afterCommit ?? mailerConfig.queue.afterCommit ?? config.queue.afterCommit
398
+ });
399
+ }
400
+ async function prepareMailSend(mail2, queued) {
401
+ const plans = createAttachmentResolutionPlans(mail2.attachments, { queued });
402
+ const attachments = Object.freeze(await Promise.all(plans.map((plan) => plan.resolve())));
403
+ return Object.freeze({
404
+ attachments
405
+ });
406
+ }
407
+ function createResolvedMail(preview, attachments) {
408
+ return Object.freeze({
409
+ from: preview.from,
410
+ replyTo: preview.replyTo,
411
+ to: preview.to,
412
+ cc: preview.cc,
413
+ bcc: preview.bcc,
414
+ subject: preview.subject,
415
+ ...typeof preview.html === "string" ? { html: preview.html } : {},
416
+ ...typeof preview.text === "string" ? { text: preview.text } : {},
417
+ attachments,
418
+ headers: preview.headers,
419
+ tags: preview.tags,
420
+ ...preview.metadata ? { metadata: preview.metadata } : {},
421
+ ...preview.priority ? { priority: preview.priority } : {}
422
+ });
423
+ }
424
+ function createSendContext(messageId, resolvedDriver, queued, deferred) {
425
+ return Object.freeze({
426
+ messageId,
427
+ mailer: resolvedDriver.mailer,
428
+ driver: resolvedDriver.driver,
429
+ queued,
430
+ ...typeof deferred === "boolean" ? { deferred } : {}
431
+ });
432
+ }
433
+ function freezeSendResult(result) {
434
+ return Object.freeze({
435
+ ...result,
436
+ ...result.provider ? { provider: Object.freeze({ ...result.provider }) } : {}
437
+ });
438
+ }
439
+ function createBaseSendResult(context, overrides = {}) {
440
+ return freezeSendResult({
441
+ messageId: context.messageId,
442
+ mailer: context.mailer,
443
+ driver: context.driver,
444
+ queued: context.queued,
445
+ ...typeof context.deferred === "boolean" ? { deferred: context.deferred } : {},
446
+ ...overrides
447
+ });
448
+ }
449
+ function normalizeDriverResult(result, context) {
450
+ if (!result) {
451
+ return createBaseSendResult(context);
452
+ }
453
+ return freezeSendResult({
454
+ messageId: result.messageId ?? context.messageId,
455
+ mailer: result.mailer ?? context.mailer,
456
+ driver: result.driver ?? context.driver,
457
+ queued: typeof result.queued === "boolean" ? result.queued : context.queued,
458
+ ...typeof result.deferred === "boolean" ? { deferred: result.deferred } : {},
459
+ ...typeof result.providerMessageId === "string" ? { providerMessageId: result.providerMessageId } : {},
460
+ ...result.provider ? { provider: result.provider } : {}
461
+ });
462
+ }
463
+ function createMailRecord(context, mail2, result) {
464
+ return Object.freeze({
465
+ messageId: context.messageId,
466
+ createdAt: /* @__PURE__ */ new Date(),
467
+ mail: mail2,
468
+ context,
469
+ result
470
+ });
471
+ }
472
+ async function persistPreviewArtifact(artifact) {
473
+ const mailer = getMailerConfig(artifact.context.mailer);
474
+ if (!isPreviewMailerConfig(mailer)) {
475
+ return;
476
+ }
477
+ await mkdir(mailer.path, { recursive: true });
478
+ await writeFile(
479
+ join(mailer.path, `${artifact.messageId}.json`),
480
+ `${JSON.stringify(artifact, null, 2)}
481
+ `,
482
+ "utf8"
483
+ );
484
+ }
485
+ function createSmtpAddress(address) {
486
+ return Object.freeze({
487
+ address: address.email,
488
+ ...typeof address.name === "string" ? { name: address.name } : {}
489
+ });
490
+ }
491
+ function createSmtpHeaders(mail2) {
492
+ const headers = { ...mail2.headers };
493
+ if (mail2.tags.length > 0) {
494
+ headers["X-Holo-Tags"] = mail2.tags.join(", ");
495
+ }
496
+ if (mail2.metadata) {
497
+ headers["X-Holo-Metadata"] = JSON.stringify(mail2.metadata);
498
+ }
499
+ if (mail2.priority === "high") {
500
+ headers["X-Priority"] = "1";
501
+ headers.Importance = "high";
502
+ headers.Priority = "urgent";
503
+ } else if (mail2.priority === "low") {
504
+ headers["X-Priority"] = "5";
505
+ headers.Importance = "low";
506
+ headers.Priority = "non-urgent";
507
+ } else if (mail2.priority === "normal") {
508
+ headers["X-Priority"] = "3";
509
+ headers.Importance = "normal";
510
+ headers.Priority = "normal";
511
+ }
512
+ return Object.keys(headers).length > 0 ? Object.freeze(headers) : void 0;
513
+ }
514
+ async function createSmtpAttachment(attachment) {
515
+ const base = {
516
+ filename: attachment.name,
517
+ ...typeof attachment.contentType === "string" ? { contentType: attachment.contentType } : {},
518
+ contentDisposition: attachment.disposition,
519
+ ...typeof attachment.contentId === "string" ? { cid: attachment.contentId } : {}
520
+ };
521
+ if (typeof attachment.path === "string") {
522
+ return Object.freeze({
523
+ ...base,
524
+ path: attachment.path
525
+ });
526
+ }
527
+ if (attachment.storage) {
528
+ const storageModule = await loadStorageModule();
529
+ const disk = storageModule.Storage.disk(attachment.storage.disk);
530
+ if (disk.driver !== "s3") {
531
+ return Object.freeze({
532
+ ...base,
533
+ path: disk.path(attachment.storage.path)
534
+ });
535
+ }
536
+ const bytes = await disk.getBytes(attachment.storage.path);
537
+ if (!bytes) {
538
+ throw new MailError(
539
+ `[@holo-js/mail] Storage attachment "${attachment.storage.path}" could not be read for SMTP delivery.`,
540
+ "MAIL_STORAGE_ATTACHMENT_MISSING"
541
+ );
542
+ }
543
+ return Object.freeze({
544
+ ...base,
545
+ content: bytes
546
+ });
547
+ }
548
+ if (typeof attachment.content !== "undefined") {
549
+ return Object.freeze({
550
+ ...base,
551
+ content: attachment.content
552
+ });
553
+ }
554
+ throw new MailError(
555
+ `[@holo-js/mail] Attachment "${attachment.name}" could not be translated for SMTP delivery.`,
556
+ "MAIL_SMTP_ATTACHMENT_INVALID"
557
+ );
558
+ }
559
+ async function createSmtpMessage(mail2, context) {
560
+ const attachments = mail2.attachments.length > 0 ? Object.freeze(await Promise.all(mail2.attachments.map(createSmtpAttachment))) : void 0;
561
+ const headers = createSmtpHeaders(mail2);
562
+ return Object.freeze({
563
+ messageId: context.messageId,
564
+ from: createSmtpAddress(mail2.from),
565
+ replyTo: createSmtpAddress(mail2.replyTo),
566
+ to: Object.freeze(mail2.to.map(createSmtpAddress)),
567
+ ...mail2.cc.length > 0 ? { cc: Object.freeze(mail2.cc.map(createSmtpAddress)) } : {},
568
+ ...mail2.bcc.length > 0 ? { bcc: Object.freeze(mail2.bcc.map(createSmtpAddress)) } : {},
569
+ subject: mail2.subject,
570
+ ...typeof mail2.html === "string" ? { html: mail2.html } : {},
571
+ ...typeof mail2.text === "string" ? { text: mail2.text } : {},
572
+ ...attachments ? { attachments } : {},
573
+ ...headers ? { headers } : {}
574
+ });
575
+ }
576
+ async function sendViaSmtp(mail2, context) {
577
+ const nodemailer = await loadNodemailerModule();
578
+ const mailerConfig = getMailerConfig(context.mailer);
579
+ if (!isSmtpMailerConfig(mailerConfig)) {
580
+ throw new MailError(
581
+ `[@holo-js/mail] Mailer "${context.mailer}" is not configured for SMTP delivery.`,
582
+ "MAIL_SMTP_MAILER_INVALID"
583
+ );
584
+ }
585
+ const transporter = nodemailer.createTransport({
586
+ host: mailerConfig.host,
587
+ port: mailerConfig.port,
588
+ secure: mailerConfig.secure,
589
+ ...typeof mailerConfig.user === "string" ? {
590
+ auth: {
591
+ user: mailerConfig.user,
592
+ ...typeof mailerConfig.password === "string" ? { pass: mailerConfig.password } : {}
593
+ }
594
+ } : {}
595
+ });
596
+ const smtpMessage = await createSmtpMessage(mail2, context);
597
+ const result = await transporter.sendMail(smtpMessage);
598
+ return createBaseSendResult(context, {
599
+ ...typeof result.messageId === "string" ? { providerMessageId: result.messageId } : {},
600
+ ...typeof result.response === "string" ? {
601
+ provider: {
602
+ response: result.response
603
+ }
604
+ } : {}
605
+ });
606
+ }
607
+ var builtInDrivers = Object.freeze({
608
+ preview: Object.freeze({
609
+ async send(mail2, context) {
610
+ const result = createBaseSendResult(context);
611
+ const artifact = createMailRecord(context, mail2, result);
612
+ getPreviewArtifactState().push(artifact);
613
+ await persistPreviewArtifact(artifact);
614
+ return result;
615
+ }
616
+ }),
617
+ fake: Object.freeze({
618
+ send(mail2, context) {
619
+ const result = createBaseSendResult(context);
620
+ getFakeSentState().push(createMailRecord(context, mail2, result));
621
+ return result;
622
+ }
623
+ }),
624
+ log: Object.freeze({
625
+ send(mail2, context) {
626
+ const mailerConfig = getMailerConfig(context.mailer);
627
+ const verbose = mailerConfig.driver === "log" ? mailerConfig.logBodies : false;
628
+ const payload = {
629
+ messageId: context.messageId,
630
+ mailer: context.mailer,
631
+ driver: context.driver,
632
+ to: mail2.to.map((address) => address.email),
633
+ subject: mail2.subject,
634
+ hasHtml: typeof mail2.html === "string",
635
+ hasText: typeof mail2.text === "string",
636
+ attachments: mail2.attachments.map((attachment) => ({
637
+ name: attachment.name,
638
+ source: attachment.source,
639
+ disposition: attachment.disposition
640
+ })),
641
+ ...mail2.tags.length > 0 ? { tags: [...mail2.tags] } : {},
642
+ ...mail2.priority ? { priority: mail2.priority } : {},
643
+ ...verbose ? {
644
+ html: mail2.html,
645
+ text: mail2.text
646
+ } : {}
647
+ };
648
+ console.warn("[@holo-js/mail] Logged mail send", payload);
649
+ return createBaseSendResult(context);
650
+ }
651
+ }),
652
+ smtp: Object.freeze({
653
+ async send(mail2, context) {
654
+ return await sendViaSmtp(mail2, context);
655
+ }
656
+ })
657
+ });
658
+ function resolveDriver(mail2, options, config) {
659
+ const selectedMailer = typeof options.mailer === "string" ? options.mailer : resolveMailerName(mail2, config);
660
+ const mailerConfig = getMailerConfig(selectedMailer, config);
661
+ const driverName = mailerConfig.driver;
662
+ const builtIn = builtInDrivers[driverName];
663
+ if (builtIn) {
664
+ return Object.freeze({
665
+ mailer: selectedMailer,
666
+ driver: driverName,
667
+ implementation: builtIn
668
+ });
669
+ }
670
+ const registered = getRegisteredMailDriver(driverName);
671
+ if (!registered) {
672
+ throw new MailError(
673
+ `[@holo-js/mail] Mail driver "${driverName}" is not registered.`,
674
+ "MAIL_DRIVER_NOT_REGISTERED"
675
+ );
676
+ }
677
+ return Object.freeze({
678
+ mailer: selectedMailer,
679
+ driver: driverName,
680
+ implementation: registered.driver
681
+ });
682
+ }
683
+ function resolveDriverByName(mailer, driver) {
684
+ const builtIn = builtInDrivers[driver];
685
+ if (builtIn) {
686
+ return Object.freeze({
687
+ mailer,
688
+ driver,
689
+ implementation: builtIn
690
+ });
691
+ }
692
+ const registered = getRegisteredMailDriver(driver);
693
+ if (!registered) {
694
+ throw new MailError(
695
+ `[@holo-js/mail] Mail driver "${driver}" is not registered.`,
696
+ "MAIL_DRIVER_NOT_REGISTERED"
697
+ );
698
+ }
699
+ return Object.freeze({
700
+ mailer,
701
+ driver,
702
+ implementation: registered.driver
703
+ });
704
+ }
705
+ function serializeQueuedAttachment(attachment) {
706
+ return Object.freeze({
707
+ source: attachment.source,
708
+ name: attachment.name,
709
+ ...typeof attachment.contentType === "string" ? { contentType: attachment.contentType } : {},
710
+ disposition: attachment.disposition,
711
+ ...typeof attachment.contentId === "string" ? { contentId: attachment.contentId } : {},
712
+ ...typeof attachment.path === "string" ? { path: attachment.path } : {},
713
+ ...attachment.storage ? { storage: attachment.storage } : {},
714
+ ...typeof attachment.content === "string" ? { content: attachment.content } : attachment.content instanceof Uint8Array ? {
715
+ content: Object.freeze({
716
+ encoding: "base64",
717
+ value: Buffer.from(attachment.content).toString("base64")
718
+ })
719
+ } : {}
720
+ });
721
+ }
722
+ function deserializeQueuedAttachment(attachment) {
723
+ return Object.freeze({
724
+ source: attachment.source,
725
+ name: attachment.name,
726
+ ...typeof attachment.contentType === "string" ? { contentType: attachment.contentType } : {},
727
+ disposition: attachment.disposition,
728
+ ...typeof attachment.contentId === "string" ? { contentId: attachment.contentId } : {},
729
+ ...typeof attachment.path === "string" ? { path: attachment.path } : {},
730
+ ...attachment.storage ? { storage: attachment.storage } : {},
731
+ ...typeof attachment.content === "string" ? { content: attachment.content } : attachment.content ? { content: new Uint8Array(Buffer.from(attachment.content.value, "base64")) } : {}
732
+ });
733
+ }
734
+ function createQueuedMailPayload(mail2, context) {
735
+ return Object.freeze({
736
+ messageId: context.messageId,
737
+ mailer: context.mailer,
738
+ driver: context.driver,
739
+ queued: true,
740
+ ...typeof context.deferred === "boolean" ? { deferred: context.deferred } : {},
741
+ mail: Object.freeze({
742
+ from: mail2.from,
743
+ replyTo: mail2.replyTo,
744
+ to: mail2.to,
745
+ cc: mail2.cc,
746
+ bcc: mail2.bcc,
747
+ subject: mail2.subject,
748
+ ...typeof mail2.html === "string" ? { html: mail2.html } : {},
749
+ ...typeof mail2.text === "string" ? { text: mail2.text } : {},
750
+ attachments: Object.freeze(mail2.attachments.map(serializeQueuedAttachment)),
751
+ headers: mail2.headers,
752
+ tags: mail2.tags,
753
+ ...mail2.metadata ? { metadata: mail2.metadata } : {},
754
+ ...mail2.priority ? { priority: mail2.priority } : {}
755
+ })
756
+ });
757
+ }
758
+ function createResolvedMailFromQueuedPayload(payload) {
759
+ return Object.freeze({
760
+ from: payload.mail.from,
761
+ replyTo: payload.mail.replyTo,
762
+ to: payload.mail.to,
763
+ cc: payload.mail.cc,
764
+ bcc: payload.mail.bcc,
765
+ subject: payload.mail.subject,
766
+ ...typeof payload.mail.html === "string" ? { html: payload.mail.html } : {},
767
+ ...typeof payload.mail.text === "string" ? { text: payload.mail.text } : {},
768
+ attachments: Object.freeze(payload.mail.attachments.map(deserializeQueuedAttachment)),
769
+ headers: payload.mail.headers,
770
+ tags: payload.mail.tags,
771
+ ...payload.mail.metadata ? { metadata: payload.mail.metadata } : {},
772
+ ...payload.mail.priority ? { priority: payload.mail.priority } : {}
773
+ });
774
+ }
775
+ async function deliverResolvedMail(mail2, resolvedDriver, context) {
776
+ try {
777
+ const result = await resolvedDriver.implementation.send(mail2, context);
778
+ return normalizeDriverResult(result, context);
779
+ } catch (error) {
780
+ throw new MailSendError({
781
+ messageId: context.messageId,
782
+ mailer: context.mailer,
783
+ driver: context.driver
784
+ }, {
785
+ cause: error
786
+ });
787
+ }
788
+ }
789
+ async function runQueuedMailDelivery(payload) {
790
+ const resolvedDriver = resolveDriverByName(payload.mailer, payload.driver);
791
+ const context = Object.freeze({
792
+ messageId: payload.messageId,
793
+ mailer: payload.mailer,
794
+ driver: payload.driver,
795
+ queued: true,
796
+ ...typeof payload.deferred === "boolean" ? { deferred: payload.deferred } : {}
797
+ });
798
+ return await deliverResolvedMail(
799
+ createResolvedMailFromQueuedPayload(payload),
800
+ resolvedDriver,
801
+ context
802
+ );
803
+ }
804
+ async function ensureMailQueueJobRegistered(queueModule) {
805
+ const resolvedQueueModule = queueModule ?? await loadQueueModule();
806
+ if (resolvedQueueModule.getRegisteredQueueJob(HOLO_MAIL_DELIVER_JOB)) {
807
+ return resolvedQueueModule;
808
+ }
809
+ resolvedQueueModule.registerQueueJob(
810
+ resolvedQueueModule.defineJob({
811
+ async handle(payload) {
812
+ return await runQueuedMailDelivery(payload);
813
+ }
814
+ }),
815
+ { name: HOLO_MAIL_DELIVER_JOB }
816
+ );
817
+ return resolvedQueueModule;
818
+ }
819
+ async function dispatchQueuedMail(mail2, context, plan) {
820
+ const queueModule = await ensureMailQueueJobRegistered();
821
+ let pending = queueModule.dispatch(
822
+ HOLO_MAIL_DELIVER_JOB,
823
+ createQueuedMailPayload(mail2, context)
824
+ );
825
+ if (typeof plan.connection !== "undefined") {
826
+ pending = pending.onConnection(plan.connection);
827
+ }
828
+ if (typeof plan.queue !== "undefined") {
829
+ pending = pending.onQueue(plan.queue);
830
+ }
831
+ if (typeof plan.delay !== "undefined") {
832
+ pending = pending.delay(plan.delay);
833
+ }
834
+ await pending.dispatch();
835
+ return createBaseSendResult(context);
836
+ }
837
+ async function deferSendUntilCommit(context, callback) {
838
+ const dbModule = await loadDbModule();
839
+ const active = dbModule?.connectionAsyncContext.getActive()?.connection;
840
+ if (!active || active.getScope().kind === "root") {
841
+ return null;
842
+ }
843
+ active.afterCommit(async () => {
844
+ await callback();
845
+ });
846
+ return createBaseSendResult(context);
847
+ }
848
+ async function renderView(input) {
849
+ const renderer = getRuntimeBindings().renderView;
850
+ if (!renderer) {
851
+ throw new MailError(`[@holo-js/mail] Mail view rendering requires a renderView runtime binding for "${input.view}".`, "MAIL_VIEW_RENDERER_MISSING");
852
+ }
853
+ const html = await renderer(input);
854
+ if (typeof html !== "string" || !html.trim()) {
855
+ throw new MailError(`[@holo-js/mail] Mail view "${input.view}" must render a non-empty HTML string.`, "MAIL_VIEW_RENDER_FAILED");
856
+ }
857
+ return html;
858
+ }
859
+ function stripMarkdownSyntax(markdown) {
860
+ return markdown.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1 ($2)").replace(/`([^`]+)`/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/^#{1,6}\s+/gm, "").replace(/^[-*]\s+/gm, "").trim();
861
+ }
862
+ function renderMarkdownInline(markdown) {
863
+ return escapeHtml(markdown).replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>').replace(/`([^`]+)`/g, "<code>$1</code>").replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>").replace(/\*([^*]+)\*/g, "<em>$1</em>");
864
+ }
865
+ function renderMarkdown(markdown) {
866
+ const blocks = markdown.trim().split(/\n\s*\n/g).map((block) => block.trim()).filter(Boolean);
867
+ return blocks.map((block) => {
868
+ const lines = block.split("\n").map((line) => line.trim()).filter(Boolean);
869
+ if (lines.length === 0) {
870
+ return "";
871
+ }
872
+ if (lines.every((line) => /^[-*]\s+/.test(line))) {
873
+ return `<ul>${lines.map((line) => `<li>${renderMarkdownInline(line.replace(/^[-*]\s+/, ""))}</li>`).join("")}</ul>`;
874
+ }
875
+ const heading = lines.length === 1 ? lines[0].match(/^(#{1,6})\s+(.+)$/) : null;
876
+ if (heading) {
877
+ const [, markers, title] = heading;
878
+ const level = markers.length;
879
+ return `<h${level}>${renderMarkdownInline(title)}</h${level}>`;
880
+ }
881
+ return `<p>${lines.map(renderMarkdownInline).join("<br />")}</p>`;
882
+ }).join("\n");
883
+ }
884
+ function resolveSourceKind(mail2) {
885
+ if (mail2.render) {
886
+ return "render";
887
+ }
888
+ if (typeof mail2.markdown === "string") {
889
+ return "markdown";
890
+ }
891
+ if (typeof mail2.html === "string") {
892
+ return "html";
893
+ }
894
+ return "text";
895
+ }
896
+ function resolveMailerName(mail2, config) {
897
+ return mail2.mailer ?? config.default;
898
+ }
899
+ function resolveEnvelope(mail2) {
900
+ const config = getResolvedConfig();
901
+ const mailer = resolveMailerName(mail2, config);
902
+ const mailerConfig = getMailerConfig(mailer, config);
903
+ const from = mail2.from ?? mailerConfig.from ?? config.from;
904
+ if (!from) {
905
+ throw new MailError("[@holo-js/mail] Mail preview requires a resolvable from address.", "MAIL_FROM_MISSING");
906
+ }
907
+ const replyTo = mail2.replyTo ?? mailerConfig.replyTo ?? config.replyTo ?? from;
908
+ return Object.freeze({
909
+ config,
910
+ mailer,
911
+ from,
912
+ replyTo,
913
+ to: mail2.to,
914
+ cc: mail2.cc,
915
+ bcc: mail2.bcc
916
+ });
917
+ }
918
+ function createMarkdownWrapperProps(mail2, envelope, html, text) {
919
+ const serializeAddress = (address) => Object.freeze({
920
+ email: address.email,
921
+ ...address.name ? { name: address.name } : {}
922
+ });
923
+ return Object.freeze({
924
+ subject: mail2.subject,
925
+ html,
926
+ markdown: mail2.markdown,
927
+ text,
928
+ from: serializeAddress(envelope.from),
929
+ replyTo: serializeAddress(envelope.replyTo),
930
+ to: Object.freeze(envelope.to.map(serializeAddress)),
931
+ cc: Object.freeze(envelope.cc.map(serializeAddress)),
932
+ bcc: Object.freeze(envelope.bcc.map(serializeAddress)),
933
+ headers: Object.freeze({ ...mail2.headers }),
934
+ tags: Object.freeze([...mail2.tags]),
935
+ ...mail2.metadata ? { metadata: mail2.metadata } : {},
936
+ ...mail2.priority ? { priority: mail2.priority } : {}
937
+ });
938
+ }
939
+ async function renderContent(mail2, envelope) {
940
+ if (mail2.render) {
941
+ const html = await renderView(mail2.render);
942
+ return Object.freeze({
943
+ kind: "render",
944
+ html,
945
+ ...typeof mail2.text === "string" ? { text: mail2.text } : {},
946
+ source: Object.freeze({
947
+ kind: "render",
948
+ render: mail2.render
949
+ })
950
+ });
951
+ }
952
+ if (typeof mail2.markdown === "string") {
953
+ const baseHtml = renderMarkdown(mail2.markdown);
954
+ const text = mail2.text ?? stripMarkdownSyntax(mail2.markdown);
955
+ const wrapper = mail2.markdownWrapper ?? envelope.config.markdown.wrapper;
956
+ const html = wrapper ? await renderView({
957
+ view: wrapper,
958
+ props: createMarkdownWrapperProps(mail2, envelope, baseHtml, text)
959
+ }) : baseHtml;
960
+ return Object.freeze({
961
+ kind: "markdown",
962
+ html,
963
+ text,
964
+ source: Object.freeze({
965
+ kind: "markdown",
966
+ markdown: mail2.markdown
967
+ })
968
+ });
969
+ }
970
+ if (typeof mail2.html === "string") {
971
+ return Object.freeze({
972
+ kind: "html",
973
+ html: mail2.html,
974
+ ...typeof mail2.text === "string" ? { text: mail2.text } : {},
975
+ source: Object.freeze({
976
+ kind: "html",
977
+ rawHtml: mail2.html
978
+ })
979
+ });
980
+ }
981
+ return Object.freeze({
982
+ kind: "text",
983
+ text: mail2.text,
984
+ source: Object.freeze({
985
+ kind: "text"
986
+ })
987
+ });
988
+ }
989
+ async function computePreview(mail2) {
990
+ const envelope = resolveEnvelope(mail2);
991
+ const rendered = await renderContent(mail2, envelope);
992
+ const preview = Object.freeze({
993
+ from: envelope.from,
994
+ replyTo: envelope.replyTo,
995
+ to: envelope.to,
996
+ cc: envelope.cc,
997
+ bcc: envelope.bcc,
998
+ subject: mail2.subject,
999
+ ...typeof rendered.html === "string" ? { html: rendered.html } : {},
1000
+ ...typeof rendered.text === "string" ? { text: rendered.text } : {},
1001
+ attachments: Object.freeze(mail2.attachments.map((attachment) => createAttachmentMetadata(attachment))),
1002
+ headers: mail2.headers,
1003
+ tags: mail2.tags,
1004
+ ...mail2.metadata ? { metadata: mail2.metadata } : {},
1005
+ ...mail2.priority ? { priority: mail2.priority } : {},
1006
+ source: rendered.source
1007
+ });
1008
+ return Object.freeze({
1009
+ ...envelope,
1010
+ preview
1011
+ });
1012
+ }
1013
+ var PendingSend = class {
1014
+ #mail;
1015
+ #options;
1016
+ #promise;
1017
+ constructor(mail2, options = {}) {
1018
+ this.#mail = mail2;
1019
+ this.#options = options;
1020
+ }
1021
+ using(name) {
1022
+ this.#options.mailer = normalizeExecutionString(name, "Mail mailer");
1023
+ return this;
1024
+ }
1025
+ onConnection(name) {
1026
+ this.#options.connection = normalizeExecutionString(name, "Mail queue connection");
1027
+ return this;
1028
+ }
1029
+ onQueue(name) {
1030
+ this.#options.queue = normalizeExecutionString(name, "Mail queue name");
1031
+ return this;
1032
+ }
1033
+ delay(value) {
1034
+ this.#options.delay = normalizeExecutionDelay(value);
1035
+ return this;
1036
+ }
1037
+ afterCommit() {
1038
+ this.#options.afterCommit = true;
1039
+ return this;
1040
+ }
1041
+ then(onfulfilled, onrejected) {
1042
+ return this.execute().then(onfulfilled, onrejected);
1043
+ }
1044
+ catch(onrejected) {
1045
+ return this.execute().catch(onrejected);
1046
+ }
1047
+ finally(onfinally) {
1048
+ return this.execute().finally(onfinally);
1049
+ }
1050
+ execute() {
1051
+ if (!this.#promise) {
1052
+ this.#promise = this.#execute();
1053
+ }
1054
+ return this.#promise;
1055
+ }
1056
+ async #execute(execution = {}) {
1057
+ const options = Object.freeze({ ...this.#options });
1058
+ const config = getResolvedConfig();
1059
+ const resolvedDriver = resolveDriver(this.#mail, options, config);
1060
+ const plan = resolveQueuePlan(this.#mail, options, resolvedDriver.mailer, config);
1061
+ const messageId = randomUUID();
1062
+ const prepared = await prepareMailSend(this.#mail, plan.queued);
1063
+ const input = Object.freeze({
1064
+ mail: this.#mail,
1065
+ attachments: prepared.attachments,
1066
+ options
1067
+ });
1068
+ const sendOverride = getRuntimeBindings().send;
1069
+ if (!plan.queued && plan.afterCommit !== true && sendOverride) {
1070
+ const context = createSendContext(messageId, resolvedDriver, false);
1071
+ return normalizeDriverResult(await sendOverride(input), context);
1072
+ }
1073
+ const { preview } = await computePreview(this.#mail);
1074
+ const resolvedMail = createResolvedMail(preview, prepared.attachments);
1075
+ const executionContext = createSendContext(
1076
+ messageId,
1077
+ resolvedDriver,
1078
+ plan.queued,
1079
+ plan.afterCommit ? true : void 0
1080
+ );
1081
+ if (execution.allowAfterCommitDeferral !== false && plan.afterCommit) {
1082
+ const deferredResult = await deferSendUntilCommit(executionContext, async () => {
1083
+ if (plan.queued) {
1084
+ return await dispatchQueuedMail(resolvedMail, executionContext, plan);
1085
+ }
1086
+ return await deliverResolvedMail(resolvedMail, resolvedDriver, executionContext);
1087
+ });
1088
+ if (deferredResult) {
1089
+ return deferredResult;
1090
+ }
1091
+ }
1092
+ if (plan.queued) {
1093
+ return await dispatchQueuedMail(
1094
+ resolvedMail,
1095
+ createSendContext(messageId, resolvedDriver, true),
1096
+ plan
1097
+ );
1098
+ }
1099
+ return await deliverResolvedMail(
1100
+ resolvedMail,
1101
+ resolvedDriver,
1102
+ createSendContext(messageId, resolvedDriver, false)
1103
+ );
1104
+ }
1105
+ };
1106
+ function listFakeSentMails() {
1107
+ return Object.freeze([...getFakeSentState()]);
1108
+ }
1109
+ function resetFakeSentMails() {
1110
+ getFakeSentState().length = 0;
1111
+ }
1112
+ function listPreviewMailArtifacts() {
1113
+ return Object.freeze([...getPreviewArtifactState()]);
1114
+ }
1115
+ function resetPreviewMailArtifacts() {
1116
+ getPreviewArtifactState().length = 0;
1117
+ }
1118
+ function resolveMailInput(mail2, overrides) {
1119
+ if (!overrides || Object.keys(overrides).length === 0) {
1120
+ return normalizeMailDefinition(mail2);
1121
+ }
1122
+ return normalizeMailDefinition(mergeMailDefinitionInputs(mail2, overrides));
1123
+ }
1124
+ function configureMailRuntime(bindings) {
1125
+ getRuntimeState().bindings = bindings;
1126
+ }
1127
+ function getMailRuntimeBindings() {
1128
+ return getRuntimeBindings();
1129
+ }
1130
+ function resetMailRuntime() {
1131
+ const state = getRuntimeState();
1132
+ state.bindings = void 0;
1133
+ state.fakeSent = void 0;
1134
+ state.previewArtifacts = void 0;
1135
+ state.loadQueueModule = void 0;
1136
+ state.loadDbModule = void 0;
1137
+ state.loadNodemailerModule = void 0;
1138
+ state.loadStorageModule = void 0;
1139
+ }
1140
+ function sendMail(mail2, overrides) {
1141
+ return new PendingSend(resolveMailInput(mail2, overrides));
1142
+ }
1143
+ async function previewMail(mail2, overrides) {
1144
+ const normalizedMail = resolveMailInput(mail2, overrides);
1145
+ const previewHandler = getRuntimeBindings().preview;
1146
+ if (previewHandler) {
1147
+ return Promise.resolve(previewHandler({
1148
+ mail: normalizedMail
1149
+ }));
1150
+ }
1151
+ return (await computePreview(normalizedMail)).preview;
1152
+ }
1153
+ async function renderMailPreview(mail2, overrides, format = "html") {
1154
+ const normalizedMail = resolveMailInput(mail2, overrides);
1155
+ try {
1156
+ assertPreviewEnabled();
1157
+ const explicitRenderer = getRuntimeBindings().renderPreview;
1158
+ if (explicitRenderer) {
1159
+ return Promise.resolve(explicitRenderer({
1160
+ mail: normalizedMail,
1161
+ format
1162
+ }));
1163
+ }
1164
+ const preview = await previewMail(normalizedMail);
1165
+ return renderPreviewResponse(preview, format);
1166
+ } catch (error) {
1167
+ if (error instanceof MailPreviewDisabledError) {
1168
+ return new Response(error.message, {
1169
+ status: 403,
1170
+ headers: {
1171
+ "content-type": "text/plain; charset=utf-8"
1172
+ }
1173
+ });
1174
+ }
1175
+ if (error instanceof MailPreviewFormatUnavailableError) {
1176
+ return new Response(error.message, {
1177
+ status: 422,
1178
+ headers: {
1179
+ "content-type": "text/plain; charset=utf-8"
1180
+ }
1181
+ });
1182
+ }
1183
+ throw error;
1184
+ }
1185
+ }
1186
+ function getMailRuntime() {
1187
+ return Object.freeze({
1188
+ sendMail,
1189
+ previewMail,
1190
+ renderMailPreview
1191
+ });
1192
+ }
1193
+ var mailRuntimeInternals = {
1194
+ HOLO_MAIL_DELIVER_JOB,
1195
+ MailSendError,
1196
+ MailError,
1197
+ MailPreviewDisabledError,
1198
+ MailPreviewFormatUnavailableError,
1199
+ PendingSend,
1200
+ assertPreviewEnabled,
1201
+ builtInDrivers,
1202
+ createBaseSendResult,
1203
+ createMailRecord,
1204
+ createQueuedMailPayload,
1205
+ createResolvedMail,
1206
+ createSmtpAddress,
1207
+ createSmtpAttachment,
1208
+ createSmtpHeaders,
1209
+ createSmtpMessage,
1210
+ createSendContext,
1211
+ createResolvedMailFromQueuedPayload,
1212
+ computePreview,
1213
+ createMarkdownWrapperProps,
1214
+ createPreviewHtml,
1215
+ createPreviewPolicy,
1216
+ deferSendUntilCommit,
1217
+ deliverResolvedMail,
1218
+ deserializeQueuedAttachment,
1219
+ dispatchQueuedMail,
1220
+ dynamicImport,
1221
+ ensureMailQueueJobRegistered,
1222
+ escapeHtml,
1223
+ formatAddress,
1224
+ freezeSendResult,
1225
+ getFakeSentState,
1226
+ getMailerConfig,
1227
+ getPreviewArtifactState,
1228
+ getResolvedConfig,
1229
+ getRuntimeBindings,
1230
+ getRuntimeState,
1231
+ loadDbModule,
1232
+ loadNodemailerModule,
1233
+ loadQueueModule,
1234
+ loadStorageModule,
1235
+ isQueueRequested,
1236
+ normalizeExecutionDelay,
1237
+ normalizeExecutionString,
1238
+ normalizeDriverResult,
1239
+ persistPreviewArtifact,
1240
+ prepareMailSend,
1241
+ renderContent,
1242
+ renderMarkdown,
1243
+ renderMarkdownInline,
1244
+ renderPreviewResponse,
1245
+ renderView,
1246
+ resolveDriver,
1247
+ resolveDriverByName,
1248
+ resolveCurrentEnvironment,
1249
+ resolveEnvelope,
1250
+ resolveQueuePlan,
1251
+ resolveMailInput,
1252
+ resolveMailerName,
1253
+ resolveNodemailerModule,
1254
+ resolveSourceKind,
1255
+ resolveStorageModule,
1256
+ runQueuedMailDelivery,
1257
+ sendViaSmtp,
1258
+ serializeQueuedAttachment,
1259
+ setDbModuleLoader(loader) {
1260
+ getRuntimeState().loadDbModule = loader;
1261
+ },
1262
+ setNodemailerModuleLoader(loader) {
1263
+ getRuntimeState().loadNodemailerModule = loader;
1264
+ },
1265
+ setQueueModuleLoader(loader) {
1266
+ getRuntimeState().loadQueueModule = loader;
1267
+ },
1268
+ setStorageModuleLoader(loader) {
1269
+ getRuntimeState().loadStorageModule = loader;
1270
+ },
1271
+ stripMarkdownSyntax
1272
+ };
1273
+
1274
+ // src/index.ts
1275
+ import { defineMailConfig } from "@holo-js/config";
1276
+ var mail = Object.freeze({
1277
+ previewMail,
1278
+ renderMailPreview,
1279
+ sendMail
1280
+ });
1281
+ var src_default = mail;
1282
+ export {
1283
+ MailError,
1284
+ MailPreviewDisabledError,
1285
+ MailPreviewFormatUnavailableError,
1286
+ MailSendError,
1287
+ attachContent,
1288
+ attachFromPath,
1289
+ attachFromStorage,
1290
+ configureMailRuntime,
1291
+ createAttachmentMetadata,
1292
+ createAttachmentResolutionPlan,
1293
+ createAttachmentResolutionPlans,
1294
+ src_default as default,
1295
+ defineMail,
1296
+ defineMailConfig,
1297
+ getMailRuntime,
1298
+ getMailRuntimeBindings,
1299
+ getRegisteredMailDriver,
1300
+ inferMimeTypeFromName,
1301
+ isAttachmentQueueSafe,
1302
+ isMailDefinition,
1303
+ listFakeSentMails,
1304
+ listPreviewMailArtifacts,
1305
+ listRegisteredMailDrivers,
1306
+ mailInternals,
1307
+ mailRegistryInternals,
1308
+ mailRuntimeInternals,
1309
+ mergeMailDefinitionInputs,
1310
+ normalizeMailDefinition,
1311
+ previewMail,
1312
+ registerMailDriver,
1313
+ renderMailPreview,
1314
+ resetFakeSentMails,
1315
+ resetMailDriverRegistry,
1316
+ resetMailRuntime,
1317
+ resetPreviewMailArtifacts,
1318
+ resolveAttachmentDefinition,
1319
+ resolveNormalizedAttachment,
1320
+ sendMail
1321
+ };