@holo-js/mail 0.2.5 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,4 @@
1
1
  // src/contracts-types.ts
2
- import { defineMailConfig } from "@holo-js/config";
3
2
  var HOLO_MAIL_DEFINITION_MARKER = /* @__PURE__ */ Symbol.for("holo-js.mail.definition");
4
3
  var BUILT_IN_MAIL_DRIVERS = ["preview", "log", "fake", "smtp"];
5
4
  var MAIL_PRIORITY_VALUES = ["high", "normal", "low"];
@@ -505,7 +504,6 @@ function attachContent(content, options) {
505
504
  }
506
505
 
507
506
  export {
508
- defineMailConfig,
509
507
  HOLO_MAIL_DEFINITION_MARKER,
510
508
  BUILT_IN_MAIL_DRIVERS,
511
509
  MAIL_PRIORITY_VALUES,
@@ -0,0 +1,219 @@
1
+ // src/config.ts
2
+ import { registerConfigNormalizer } from "@holo-js/config/registry";
3
+ function normalizeMailEnvironment(value, fallback = "development") {
4
+ return value === "development" || value === "production" || value === "test" ? value : fallback;
5
+ }
6
+ var DEFAULT_MAILER_NAME = "preview";
7
+ var DEFAULT_MAIL_PREVIEW_PATH = ".holo-js/runtime/mail-preview";
8
+ var DEFAULT_SMTP_HOST = "127.0.0.1";
9
+ var DEFAULT_SMTP_PORT = 1025;
10
+ var DEFAULT_MAIL_QUEUE_CONFIG = Object.freeze({
11
+ queued: false,
12
+ connection: void 0,
13
+ queue: void 0,
14
+ afterCommit: false
15
+ });
16
+ var holoMailDefaults = Object.freeze({
17
+ default: DEFAULT_MAILER_NAME,
18
+ from: void 0,
19
+ replyTo: void 0,
20
+ queue: DEFAULT_MAIL_QUEUE_CONFIG,
21
+ preview: Object.freeze({
22
+ allowedEnvironments: Object.freeze(["development"])
23
+ }),
24
+ markdown: Object.freeze({
25
+ wrapper: void 0
26
+ }),
27
+ mailers: Object.freeze({
28
+ preview: Object.freeze({
29
+ name: "preview",
30
+ driver: "preview",
31
+ from: void 0,
32
+ replyTo: void 0,
33
+ queue: DEFAULT_MAIL_QUEUE_CONFIG,
34
+ path: DEFAULT_MAIL_PREVIEW_PATH
35
+ }),
36
+ log: Object.freeze({
37
+ name: "log",
38
+ driver: "log",
39
+ from: void 0,
40
+ replyTo: void 0,
41
+ queue: DEFAULT_MAIL_QUEUE_CONFIG,
42
+ logBodies: false
43
+ }),
44
+ fake: Object.freeze({
45
+ name: "fake",
46
+ driver: "fake",
47
+ from: void 0,
48
+ replyTo: void 0,
49
+ queue: DEFAULT_MAIL_QUEUE_CONFIG
50
+ }),
51
+ smtp: Object.freeze({
52
+ name: "smtp",
53
+ driver: "smtp",
54
+ from: void 0,
55
+ replyTo: void 0,
56
+ queue: DEFAULT_MAIL_QUEUE_CONFIG,
57
+ host: DEFAULT_SMTP_HOST,
58
+ port: DEFAULT_SMTP_PORT,
59
+ secure: false
60
+ })
61
+ })
62
+ });
63
+ function normalizeOptionalMailString(value, label) {
64
+ if (typeof value === "undefined") {
65
+ return void 0;
66
+ }
67
+ const normalized = value.trim();
68
+ if (!normalized) {
69
+ throw new Error(`[Holo Mail] ${label} must be a non-empty string when provided.`);
70
+ }
71
+ return normalized;
72
+ }
73
+ function isValidMailAddress(email) {
74
+ if (email.includes(" ")) {
75
+ return false;
76
+ }
77
+ const parts = email.split("@");
78
+ return parts.length === 2 && parts[0].length > 0 && parts[1].length > 0;
79
+ }
80
+ function normalizeMailAddress(address, label) {
81
+ if (!address) {
82
+ return void 0;
83
+ }
84
+ const email = normalizeOptionalMailString(address.email, `${label} email`)?.toLowerCase();
85
+ if (!email || !isValidMailAddress(email)) {
86
+ throw new Error(`[Holo Mail] ${label} email must be a valid email address.`);
87
+ }
88
+ const name = normalizeOptionalMailString(address.name, `${label} name`);
89
+ return Object.freeze({
90
+ email,
91
+ ...name ? { name } : {}
92
+ });
93
+ }
94
+ function normalizeMailQueueConfig(queue, fallback = holoMailDefaults.queue) {
95
+ return Object.freeze({
96
+ queued: queue?.queued ?? fallback.queued,
97
+ connection: normalizeOptionalMailString(queue?.connection, "Mail queue connection") ?? fallback.connection,
98
+ queue: normalizeOptionalMailString(queue?.queue, "Mail queue name") ?? fallback.queue,
99
+ afterCommit: queue?.afterCommit ?? fallback.afterCommit
100
+ });
101
+ }
102
+ function normalizeMailPreviewEnvironments(environments) {
103
+ if (typeof environments === "undefined") {
104
+ return holoMailDefaults.preview.allowedEnvironments;
105
+ }
106
+ const normalized = /* @__PURE__ */ new Set();
107
+ for (const environment of environments) {
108
+ if (environment !== "development" && environment !== "production" && environment !== "test") {
109
+ throw new Error("[Holo Mail] Mail preview environments must be development, production, or test.");
110
+ }
111
+ normalized.add(environment);
112
+ }
113
+ return Object.freeze([...normalized]);
114
+ }
115
+ function normalizeMailMailerConfig(name, config, fallback) {
116
+ const normalizedName = normalizeOptionalMailString(name, "Mail mailer name");
117
+ const driver = normalizeOptionalMailString(config.driver, `Mail mailer "${name}" driver`);
118
+ if (!normalizedName || !driver) {
119
+ throw new Error("[Holo Mail] Mailers must define a name and driver.");
120
+ }
121
+ const base = {
122
+ name: normalizedName,
123
+ driver,
124
+ from: normalizeMailAddress(config.from, `Mail mailer "${name}" from`) ?? fallback?.from,
125
+ replyTo: normalizeMailAddress(config.replyTo, `Mail mailer "${name}" replyTo`) ?? fallback?.replyTo,
126
+ queue: normalizeMailQueueConfig(config.queue, fallback?.queue ?? holoMailDefaults.queue)
127
+ };
128
+ if (driver === "preview") {
129
+ const previewFallback = fallback?.driver === "preview" ? fallback : void 0;
130
+ return Object.freeze({
131
+ ...base,
132
+ driver: "preview",
133
+ path: normalizeOptionalMailString(config.path, `Mail mailer "${name}" preview path`) ?? previewFallback?.path ?? DEFAULT_MAIL_PREVIEW_PATH
134
+ });
135
+ }
136
+ if (driver === "log") {
137
+ const logFallback = fallback?.driver === "log" ? fallback : void 0;
138
+ return Object.freeze({
139
+ ...base,
140
+ driver: "log",
141
+ logBodies: config.logBodies ?? logFallback?.logBodies ?? false
142
+ });
143
+ }
144
+ if (driver === "fake") {
145
+ return Object.freeze({
146
+ ...base,
147
+ driver: "fake"
148
+ });
149
+ }
150
+ if (driver === "smtp") {
151
+ const smtpFallback = fallback?.driver === "smtp" ? fallback : void 0;
152
+ const rawPort = config.port;
153
+ const normalizedPort = typeof rawPort === "number" ? rawPort : typeof rawPort === "string" && rawPort.trim() ? Number(rawPort.trim()) : smtpFallback?.port ?? DEFAULT_SMTP_PORT;
154
+ if (!Number.isFinite(normalizedPort) || normalizedPort <= 0) {
155
+ throw new Error(`[Holo Mail] Mail mailer "${name}" SMTP port must be a positive number.`);
156
+ }
157
+ return Object.freeze({
158
+ ...base,
159
+ driver: "smtp",
160
+ host: normalizeOptionalMailString(config.host, `Mail mailer "${name}" SMTP host`) ?? smtpFallback?.host ?? DEFAULT_SMTP_HOST,
161
+ port: normalizedPort,
162
+ secure: config.secure ?? smtpFallback?.secure ?? false,
163
+ user: normalizeOptionalMailString(config.user, `Mail mailer "${name}" SMTP user`) ?? smtpFallback?.user ?? void 0,
164
+ password: normalizeOptionalMailString(config.password, `Mail mailer "${name}" SMTP password`) ?? smtpFallback?.password ?? void 0
165
+ });
166
+ }
167
+ const customFields = Object.fromEntries(
168
+ Object.entries(config).filter(([key]) => key !== "driver" && key !== "from" && key !== "replyTo" && key !== "queue")
169
+ );
170
+ return Object.freeze({
171
+ ...base,
172
+ ...customFields
173
+ });
174
+ }
175
+ function normalizeMailConfig(config = {}) {
176
+ const mergedMailers = {
177
+ ...holoMailDefaults.mailers
178
+ };
179
+ for (const [name, mailer] of Object.entries(config.mailers ?? {})) {
180
+ mergedMailers[name] = normalizeMailMailerConfig(name, mailer, mergedMailers[name]);
181
+ }
182
+ const defaultMailer = normalizeOptionalMailString(config.default, "Default mailer") ?? holoMailDefaults.default;
183
+ if (!mergedMailers[defaultMailer]) {
184
+ throw new Error(
185
+ `[Holo Mail] default mailer "${defaultMailer}" is not configured. Available mailers: ${Object.keys(mergedMailers).join(", ")}`
186
+ );
187
+ }
188
+ return Object.freeze({
189
+ default: defaultMailer,
190
+ from: normalizeMailAddress(config.from, "Mail from") ?? holoMailDefaults.from,
191
+ replyTo: normalizeMailAddress(config.replyTo, "Mail replyTo") ?? holoMailDefaults.replyTo,
192
+ queue: normalizeMailQueueConfig(config.queue),
193
+ preview: Object.freeze({
194
+ allowedEnvironments: normalizeMailPreviewEnvironments(config.preview?.allowedEnvironments)
195
+ }),
196
+ markdown: Object.freeze({
197
+ wrapper: normalizeOptionalMailString(config.markdown?.wrapper, "Mail markdown wrapper") ?? holoMailDefaults.markdown.wrapper
198
+ }),
199
+ mailers: Object.freeze(mergedMailers)
200
+ });
201
+ }
202
+ function defineMailConfig(config) {
203
+ return Object.freeze({ ...config });
204
+ }
205
+ registerConfigNormalizer({
206
+ name: "mail",
207
+ normalize: normalizeMailConfig
208
+ });
209
+
210
+ export {
211
+ normalizeMailEnvironment,
212
+ DEFAULT_MAILER_NAME,
213
+ DEFAULT_MAIL_PREVIEW_PATH,
214
+ DEFAULT_SMTP_HOST,
215
+ DEFAULT_SMTP_PORT,
216
+ holoMailDefaults,
217
+ normalizeMailConfig,
218
+ defineMailConfig
219
+ };
@@ -0,0 +1,119 @@
1
+ type HoloAppEnv = 'development' | 'production' | 'test';
2
+ declare function normalizeMailEnvironment(value: string | undefined, fallback?: HoloAppEnv): HoloAppEnv;
3
+ interface HoloMailAddressConfig {
4
+ readonly email: string;
5
+ readonly name?: string;
6
+ }
7
+ interface HoloMailQueueConfig {
8
+ readonly queued?: boolean;
9
+ readonly connection?: string;
10
+ readonly queue?: string;
11
+ readonly afterCommit?: boolean;
12
+ }
13
+ interface HoloMailPreviewConfig {
14
+ readonly allowedEnvironments?: readonly HoloAppEnv[];
15
+ }
16
+ interface HoloMailMarkdownConfig {
17
+ readonly wrapper?: string;
18
+ }
19
+ interface BaseMailDriverConfig {
20
+ readonly driver: string;
21
+ readonly from?: HoloMailAddressConfig;
22
+ readonly replyTo?: HoloMailAddressConfig;
23
+ readonly queue?: HoloMailQueueConfig;
24
+ }
25
+ interface PreviewMailDriverConfig extends BaseMailDriverConfig {
26
+ readonly driver: 'preview';
27
+ readonly path?: string;
28
+ }
29
+ interface LogMailDriverConfig extends BaseMailDriverConfig {
30
+ readonly driver: 'log';
31
+ readonly logBodies?: boolean;
32
+ }
33
+ interface FakeMailDriverConfig extends BaseMailDriverConfig {
34
+ readonly driver: 'fake';
35
+ }
36
+ interface SmtpMailDriverConfig extends BaseMailDriverConfig {
37
+ readonly driver: 'smtp';
38
+ readonly host?: string;
39
+ readonly port?: number | string;
40
+ readonly secure?: boolean;
41
+ readonly user?: string;
42
+ readonly password?: string;
43
+ }
44
+ type HoloMailMailerConfig = PreviewMailDriverConfig | LogMailDriverConfig | FakeMailDriverConfig | SmtpMailDriverConfig | (BaseMailDriverConfig & Record<string, unknown>);
45
+ interface HoloMailConfig {
46
+ readonly default?: string;
47
+ readonly from?: HoloMailAddressConfig;
48
+ readonly replyTo?: HoloMailAddressConfig;
49
+ readonly queue?: HoloMailQueueConfig;
50
+ readonly preview?: HoloMailPreviewConfig;
51
+ readonly markdown?: HoloMailMarkdownConfig;
52
+ readonly mailers?: Readonly<Record<string, HoloMailMailerConfig>>;
53
+ }
54
+ interface NormalizedHoloMailAddressConfig {
55
+ readonly email: string;
56
+ readonly name?: string;
57
+ }
58
+ interface NormalizedHoloMailQueueConfig {
59
+ readonly queued: boolean;
60
+ readonly connection?: string;
61
+ readonly queue?: string;
62
+ readonly afterCommit: boolean;
63
+ }
64
+ interface NormalizedHoloMailPreviewConfig {
65
+ readonly allowedEnvironments: readonly HoloAppEnv[];
66
+ }
67
+ interface NormalizedHoloMailMarkdownConfig {
68
+ readonly wrapper?: string;
69
+ }
70
+ interface NormalizedBaseMailDriverConfig {
71
+ readonly name: string;
72
+ readonly driver: string;
73
+ readonly from?: NormalizedHoloMailAddressConfig;
74
+ readonly replyTo?: NormalizedHoloMailAddressConfig;
75
+ readonly queue: NormalizedHoloMailQueueConfig;
76
+ }
77
+ interface NormalizedPreviewMailDriverConfig extends NormalizedBaseMailDriverConfig {
78
+ readonly driver: 'preview';
79
+ readonly path: string;
80
+ }
81
+ interface NormalizedLogMailDriverConfig extends NormalizedBaseMailDriverConfig {
82
+ readonly driver: 'log';
83
+ readonly logBodies: boolean;
84
+ }
85
+ interface NormalizedFakeMailDriverConfig extends NormalizedBaseMailDriverConfig {
86
+ readonly driver: 'fake';
87
+ }
88
+ interface NormalizedSmtpMailDriverConfig extends NormalizedBaseMailDriverConfig {
89
+ readonly driver: 'smtp';
90
+ readonly host: string;
91
+ readonly port: number;
92
+ readonly secure: boolean;
93
+ readonly user?: string;
94
+ readonly password?: string;
95
+ }
96
+ type NormalizedHoloMailMailerConfig = NormalizedPreviewMailDriverConfig | NormalizedLogMailDriverConfig | NormalizedFakeMailDriverConfig | NormalizedSmtpMailDriverConfig | NormalizedBaseMailDriverConfig;
97
+ interface NormalizedHoloMailConfig {
98
+ readonly default: string;
99
+ readonly from?: NormalizedHoloMailAddressConfig;
100
+ readonly replyTo?: NormalizedHoloMailAddressConfig;
101
+ readonly queue: NormalizedHoloMailQueueConfig;
102
+ readonly preview: NormalizedHoloMailPreviewConfig;
103
+ readonly markdown: NormalizedHoloMailMarkdownConfig;
104
+ readonly mailers: Readonly<Record<string, NormalizedHoloMailMailerConfig>>;
105
+ }
106
+ declare const DEFAULT_MAILER_NAME = "preview";
107
+ declare const DEFAULT_MAIL_PREVIEW_PATH = ".holo-js/runtime/mail-preview";
108
+ declare const DEFAULT_SMTP_HOST = "127.0.0.1";
109
+ declare const DEFAULT_SMTP_PORT = 1025;
110
+ declare const holoMailDefaults: Readonly<NormalizedHoloMailConfig>;
111
+ declare function normalizeMailConfig(config?: HoloMailConfig): NormalizedHoloMailConfig;
112
+ declare function defineMailConfig<TConfig extends HoloMailConfig>(config: TConfig): Readonly<TConfig>;
113
+ declare module '@holo-js/config' {
114
+ interface HoloConfigRegistry {
115
+ mail: NormalizedHoloMailConfig;
116
+ }
117
+ }
118
+
119
+ export { type BaseMailDriverConfig, DEFAULT_MAILER_NAME, DEFAULT_MAIL_PREVIEW_PATH, DEFAULT_SMTP_HOST, DEFAULT_SMTP_PORT, type FakeMailDriverConfig, type HoloAppEnv, type HoloMailAddressConfig, type HoloMailConfig, type HoloMailMailerConfig, type HoloMailMarkdownConfig, type HoloMailPreviewConfig, type HoloMailQueueConfig, type LogMailDriverConfig, type NormalizedBaseMailDriverConfig, type NormalizedFakeMailDriverConfig, type NormalizedHoloMailAddressConfig, type NormalizedHoloMailConfig, type NormalizedHoloMailMailerConfig, type NormalizedHoloMailMarkdownConfig, type NormalizedHoloMailPreviewConfig, type NormalizedHoloMailQueueConfig, type NormalizedLogMailDriverConfig, type NormalizedPreviewMailDriverConfig, type NormalizedSmtpMailDriverConfig, type PreviewMailDriverConfig, type SmtpMailDriverConfig, defineMailConfig, holoMailDefaults, normalizeMailConfig, normalizeMailEnvironment };
@@ -0,0 +1,20 @@
1
+ import {
2
+ DEFAULT_MAILER_NAME,
3
+ DEFAULT_MAIL_PREVIEW_PATH,
4
+ DEFAULT_SMTP_HOST,
5
+ DEFAULT_SMTP_PORT,
6
+ defineMailConfig,
7
+ holoMailDefaults,
8
+ normalizeMailConfig,
9
+ normalizeMailEnvironment
10
+ } from "./chunk-YPNVXZL6.mjs";
11
+ export {
12
+ DEFAULT_MAILER_NAME,
13
+ DEFAULT_MAIL_PREVIEW_PATH,
14
+ DEFAULT_SMTP_HOST,
15
+ DEFAULT_SMTP_PORT,
16
+ defineMailConfig,
17
+ holoMailDefaults,
18
+ normalizeMailConfig,
19
+ normalizeMailEnvironment
20
+ };
@@ -1,5 +1,5 @@
1
- import { HoloAppEnv, NormalizedHoloMailConfig } from '@holo-js/config';
2
- export { defineMailConfig } from '@holo-js/config';
1
+ import { HoloAppEnv, NormalizedHoloMailConfig } from './config.js';
2
+ export { defineMailConfig } from './config.js';
3
3
 
4
4
  declare const HOLO_MAIL_DEFINITION_MARKER: unique symbol;
5
5
  declare const BUILT_IN_MAIL_DRIVERS: readonly ["preview", "log", "fake", "smtp"];
@@ -10,7 +10,6 @@ import {
10
10
  createAttachmentResolutionPlan,
11
11
  createAttachmentResolutionPlans,
12
12
  defineMail,
13
- defineMailConfig,
14
13
  inferMimeTypeFromName,
15
14
  isAttachmentQueueSafe,
16
15
  isMailDefinition,
@@ -18,7 +17,10 @@ import {
18
17
  normalizeMailDefinition,
19
18
  resolveAttachmentDefinition,
20
19
  resolveNormalizedAttachment
21
- } from "./chunk-BQCGZOVF.mjs";
20
+ } from "./chunk-7CZAQPEH.mjs";
21
+ import {
22
+ defineMailConfig
23
+ } from "./chunk-YPNVXZL6.mjs";
22
24
  export {
23
25
  BUILT_IN_MAIL_DRIVERS,
24
26
  HOLO_MAIL_DEFINITION_MARKER,
package/dist/index.d.ts CHANGED
@@ -1,21 +1,7 @@
1
- import { ResolvedMail, MailDriverExecutionContext, MailSendResult, MailPreviewPolicy, MailPreviewFormat, MailRuntimeBindings, MailDefinition, MailDefinitionInput, MailOverrideInput, MailPreviewResult, PendingMailSend, RegisteredMailDriver, MailDriver, RegisterMailDriverOptions } from './contracts.js';
1
+ export { HoloMailConfig, NormalizedHoloMailConfig, defineMailConfig, holoMailDefaults, normalizeMailConfig } from './config.js';
2
+ import { MailPreviewPolicy, MailPreviewFormat, ResolvedMail, MailDriverExecutionContext, MailSendResult, MailRuntimeBindings, MailDefinition, MailDefinitionInput, MailOverrideInput, MailPreviewResult, PendingMailSend, RegisteredMailDriver, MailDriver, RegisterMailDriverOptions } from './contracts.js';
2
3
  export { BuiltInMailDriverRegistry, HoloMailDriverRegistry, MailAddress, MailAddressInput, MailAttachmentBase, MailAttachmentContent, MailAttachmentDisposition, MailAttachmentHelperOptions, MailAttachmentInput, MailAttachmentMetadata, MailAttachmentResolutionContext, MailAttachmentResolutionPlan, MailContentAttachment, MailContentAttachmentHelperOptions, MailContentSourceKind, MailDelayValue, MailDriverName, MailJsonObject, MailJsonValue, MailPathAttachment, MailPreviewInput, MailPriority, MailQueueOptions, MailRecipientsInput, MailRenderPreviewInput, MailRenderSource, MailResolvableAttachment, MailResolvedAttachmentPayload, MailSendInput, MailSendOptions, MailStorageAttachment, MailStorageAttachmentHelperOptions, MailViewRenderInput, MailViewRenderer, ResolvedMailAttachment, attachContent, attachFromPath, attachFromStorage, createAttachmentMetadata, createAttachmentResolutionPlan, createAttachmentResolutionPlans, defineMail, inferMimeTypeFromName, isAttachmentQueueSafe, isMailDefinition, mergeMailDefinitionInputs, normalizeMailDefinition, resolveAttachmentDefinition, resolveNormalizedAttachment } from './contracts.js';
3
- export { HoloMailConfig, NormalizedHoloMailConfig, defineMailConfig } from '@holo-js/config';
4
4
 
5
- interface FakeSentMail {
6
- readonly messageId: string;
7
- readonly createdAt: Date;
8
- readonly mail: Readonly<ResolvedMail>;
9
- readonly context: Readonly<MailDriverExecutionContext>;
10
- readonly result: Readonly<MailSendResult>;
11
- }
12
- interface MailPreviewArtifact {
13
- readonly messageId: string;
14
- readonly createdAt: Date;
15
- readonly mail: Readonly<ResolvedMail>;
16
- readonly context: Readonly<MailDriverExecutionContext>;
17
- readonly result: Readonly<MailSendResult>;
18
- }
19
5
  declare class MailError extends Error {
20
6
  readonly code: string;
21
7
  constructor(message: string, code?: string, options?: ErrorOptions);
@@ -39,6 +25,21 @@ declare class MailSendError extends MailError {
39
25
  readonly message?: string;
40
26
  }, options?: ErrorOptions);
41
27
  }
28
+
29
+ interface FakeSentMail {
30
+ readonly messageId: string;
31
+ readonly createdAt: Date;
32
+ readonly mail: Readonly<ResolvedMail>;
33
+ readonly context: Readonly<MailDriverExecutionContext>;
34
+ readonly result: Readonly<MailSendResult>;
35
+ }
36
+ interface MailPreviewArtifact {
37
+ readonly messageId: string;
38
+ readonly createdAt: Date;
39
+ readonly mail: Readonly<ResolvedMail>;
40
+ readonly context: Readonly<MailDriverExecutionContext>;
41
+ readonly result: Readonly<MailSendResult>;
42
+ }
42
43
  interface MailRuntimeFacade {
43
44
  sendMail(mail: MailDefinition | MailDefinitionInput, overrides?: MailOverrideInput): PendingMailSend<MailSendResult>;
44
45
  previewMail(mail: MailDefinition | MailDefinitionInput, overrides?: MailOverrideInput): Promise<MailPreviewResult>;
@@ -67,10 +68,13 @@ declare const mailRegistryInternals: {
67
68
  normalizeDriverName: typeof normalizeDriverName;
68
69
  };
69
70
 
71
+ declare function loadMailPluginDrivers(projectRoot?: string, pluginNames?: readonly string[]): Promise<void>;
72
+ declare function resetMailPluginDrivers(): void;
73
+
70
74
  declare const mail: Readonly<{
71
75
  previewMail: typeof previewMail;
72
76
  renderMailPreview: typeof renderMailPreview;
73
77
  sendMail: typeof sendMail;
74
78
  }>;
75
79
 
76
- export { type FakeSentMail, MailDefinition, MailDefinitionInput, MailDriver, MailDriverExecutionContext, MailError, MailOverrideInput, type MailPreviewArtifact, MailPreviewDisabledError, MailPreviewFormat, MailPreviewFormatUnavailableError, MailPreviewPolicy, MailPreviewResult, MailRuntimeBindings, MailSendError, MailSendResult, PendingMailSend, RegisterMailDriverOptions, RegisteredMailDriver, ResolvedMail, configureMailRuntime, mail as default, getMailRuntime, getMailRuntimeBindings, getRegisteredMailDriver, listFakeSentMails, listPreviewMailArtifacts, listRegisteredMailDrivers, mailRegistryInternals, previewMail, registerMailDriver, renderMailPreview, resetFakeSentMails, resetMailDriverRegistry, resetMailRuntime, resetPreviewMailArtifacts, sendMail };
80
+ export { type FakeSentMail, MailDefinition, MailDefinitionInput, MailDriver, MailDriverExecutionContext, MailError, MailOverrideInput, type MailPreviewArtifact, MailPreviewDisabledError, MailPreviewFormat, MailPreviewFormatUnavailableError, MailPreviewPolicy, MailPreviewResult, MailRuntimeBindings, MailSendError, MailSendResult, PendingMailSend, RegisterMailDriverOptions, RegisteredMailDriver, ResolvedMail, configureMailRuntime, mail as default, getMailRuntime, getMailRuntimeBindings, getRegisteredMailDriver, listFakeSentMails, listPreviewMailArtifacts, listRegisteredMailDrivers, loadMailPluginDrivers, mailRegistryInternals, previewMail, registerMailDriver, renderMailPreview, resetFakeSentMails, resetMailDriverRegistry, resetMailPluginDrivers, resetMailRuntime, resetPreviewMailArtifacts, sendMail };
package/dist/index.mjs CHANGED
@@ -13,17 +13,58 @@ import {
13
13
  normalizeMailDefinition,
14
14
  resolveAttachmentDefinition,
15
15
  resolveNormalizedAttachment
16
- } from "./chunk-BQCGZOVF.mjs";
16
+ } from "./chunk-7CZAQPEH.mjs";
17
+ import {
18
+ defineMailConfig,
19
+ holoMailDefaults,
20
+ normalizeMailConfig,
21
+ normalizeMailEnvironment
22
+ } from "./chunk-YPNVXZL6.mjs";
17
23
 
18
24
  // src/runtime.ts
19
25
  import { randomUUID } from "crypto";
20
26
  import { mkdir, realpath, writeFile } from "fs/promises";
21
27
  import { tmpdir } from "os";
22
28
  import { join, resolve, sep } from "path";
23
- import {
24
- holoMailDefaults,
25
- normalizeAppEnv
26
- } from "@holo-js/config";
29
+
30
+ // src/markdown.ts
31
+ function escapeHtml(value) {
32
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#039;");
33
+ }
34
+ function isSafeMailHref(href) {
35
+ const trimmed = href.trim();
36
+ if (trimmed.startsWith("#") || trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../")) return true;
37
+ try {
38
+ const url = new URL(trimmed);
39
+ return url.protocol === "https:" || url.protocol === "http:" || url.protocol === "mailto:";
40
+ } catch {
41
+ return false;
42
+ }
43
+ }
44
+ function renderMarkdownInline(markdown) {
45
+ return escapeHtml(markdown).replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, label, href) => {
46
+ return isSafeMailHref(href) ? `<a href="${escapeHtml(href)}">${label}</a>` : label;
47
+ }).replace(/`([^`]+)`/g, "<code>$1</code>").replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>").replace(/\*([^*]+)\*/g, "<em>$1</em>");
48
+ }
49
+ function stripMarkdownSyntax(markdown) {
50
+ return markdown.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1 ($2)").replace(/`([^`]+)`/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/^#{1,6}\s+/gm, "").replace(/^[-*]\s+/gm, "").trim();
51
+ }
52
+ function renderMarkdown(markdown) {
53
+ const blocks = markdown.trim().split(/\n\s*\n/g).map((block) => block.trim()).filter(Boolean);
54
+ return blocks.map((block) => {
55
+ const lines = block.split("\n").map((line) => line.trim()).filter(Boolean);
56
+ if (lines.every((line) => /^[-*]\s+/.test(line))) {
57
+ return `<ul>${lines.map((line) => `<li>${renderMarkdownInline(line.replace(/^[-*]\s+/, ""))}</li>`).join("")}</ul>`;
58
+ }
59
+ const heading = lines.length === 1 ? lines[0].match(/^(#{1,6})\s+(.+)$/) : null;
60
+ if (heading) {
61
+ const [, markers, title] = heading;
62
+ const level = markers.length;
63
+ return `<h${level}>${renderMarkdownInline(title)}</h${level}>`;
64
+ }
65
+ return `<p>${lines.map(renderMarkdownInline).join("<br />")}</p>`;
66
+ }).join("\n");
67
+ }
27
68
 
28
69
  // src/registry.ts
29
70
  function normalizeDriverName(value) {
@@ -55,6 +96,9 @@ function registerMailDriver(name, driver, options = {}) {
55
96
  function getRegisteredMailDriver(name) {
56
97
  return getRegistry().get(normalizeDriverName(name));
57
98
  }
99
+ function unregisterMailDriver(name) {
100
+ return getRegistry().delete(normalizeDriverName(name));
101
+ }
58
102
  function listRegisteredMailDrivers() {
59
103
  return Object.freeze([...getRegistry().values()]);
60
104
  }
@@ -66,12 +110,66 @@ var mailRegistryInternals = {
66
110
  normalizeDriverName
67
111
  };
68
112
 
69
- // src/runtime.ts
70
- var HOLO_MAIL_DELIVER_JOB = "holo.mail.deliver";
71
- var LOCAL_ATTACHMENT_ROOTS = Object.freeze([
72
- tmpdir(),
73
- resolve(process.cwd(), "storage")
74
- ]);
113
+ // src/plugins.ts
114
+ import {
115
+ loadHoloPluginContributionModules,
116
+ loadHoloPluginDefinitions
117
+ } from "@holo-js/kernel";
118
+ var loadedProjectRoots = /* @__PURE__ */ new Set();
119
+ var registeredPluginDrivers = /* @__PURE__ */ new Map();
120
+ function isRecord(value) {
121
+ return !!value && typeof value === "object" && !Array.isArray(value);
122
+ }
123
+ function resolveMailDriver(moduleValue, packageName, driverName) {
124
+ const candidate = isRecord(moduleValue) && typeof moduleValue.default !== "undefined" ? moduleValue.default : isRecord(moduleValue) && typeof moduleValue.driver !== "undefined" ? moduleValue.driver : moduleValue;
125
+ if (!isRecord(candidate) || typeof candidate.send !== "function") {
126
+ throw new Error(`[@holo-js/mail] Plugin ${packageName} mail driver "${driverName}" must export send().`);
127
+ }
128
+ return {
129
+ send: candidate.send
130
+ };
131
+ }
132
+ async function loadMailPluginDrivers(projectRoot = process.cwd(), pluginNames = []) {
133
+ const loadKey = `${projectRoot}\0${[...pluginNames].sort().join("\0")}`;
134
+ if (loadedProjectRoots.has(loadKey)) {
135
+ return;
136
+ }
137
+ const plugins = await loadHoloPluginDefinitions(projectRoot, pluginNames);
138
+ const contributions = await loadHoloPluginContributionModules(projectRoot, plugins, "mail", "drivers");
139
+ for (const contribution of contributions) {
140
+ const previous = getRegisteredMailDriver(contribution.name);
141
+ const driver = resolveMailDriver(contribution.module, contribution.plugin.packageName, contribution.name);
142
+ registerMailDriver(
143
+ contribution.name,
144
+ driver,
145
+ { replaceExisting: true }
146
+ );
147
+ const registered = getRegisteredMailDriver(contribution.name);
148
+ if (registered) {
149
+ const existingPluginDriver = registeredPluginDrivers.get(registered.name);
150
+ registeredPluginDrivers.set(registered.name, {
151
+ driver,
152
+ previous: existingPluginDriver ? existingPluginDriver.previous : previous?.driver
153
+ });
154
+ }
155
+ }
156
+ loadedProjectRoots.add(loadKey);
157
+ }
158
+ function resetMailPluginDrivers() {
159
+ for (const [driverName, registered] of registeredPluginDrivers) {
160
+ if (getRegisteredMailDriver(driverName)?.driver !== registered.driver) {
161
+ continue;
162
+ }
163
+ unregisterMailDriver(driverName);
164
+ if (registered.previous) {
165
+ registerMailDriver(driverName, registered.previous, { replaceExisting: true });
166
+ }
167
+ }
168
+ loadedProjectRoots.clear();
169
+ registeredPluginDrivers.clear();
170
+ }
171
+
172
+ // src/errors.ts
75
173
  var MailError = class extends Error {
76
174
  code;
77
175
  constructor(message, code = "MAIL_ERROR", options) {
@@ -118,6 +216,52 @@ var MailSendError = class extends MailError {
118
216
  this.driver = details.driver;
119
217
  }
120
218
  };
219
+
220
+ // src/preview.ts
221
+ function escapePreviewHtml(value) {
222
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
223
+ }
224
+ function formatPreviewAddress(address) {
225
+ return address.name ? `${escapePreviewHtml(address.name)} &lt;${escapePreviewHtml(address.email)}&gt;` : escapePreviewHtml(address.email);
226
+ }
227
+ function createMailPreviewHtml(preview) {
228
+ const header = [
229
+ `<h1>${escapePreviewHtml(preview.subject)}</h1>`,
230
+ `<p><strong>From:</strong> ${formatPreviewAddress(preview.from)}</p>`,
231
+ `<p><strong>Reply-To:</strong> ${formatPreviewAddress(preview.replyTo)}</p>`,
232
+ `<p><strong>To:</strong> ${preview.to.map(formatPreviewAddress).join(", ")}</p>`,
233
+ preview.cc.length > 0 ? `<p><strong>Cc:</strong> ${preview.cc.map(formatPreviewAddress).join(", ")}</p>` : "",
234
+ preview.bcc.length > 0 ? `<p><strong>Bcc:</strong> ${preview.bcc.map(formatPreviewAddress).join(", ")}</p>` : ""
235
+ ].filter(Boolean).join("");
236
+ const body = preview.html ? `<pre>${escapePreviewHtml(preview.html)}</pre>` : preview.text ? `<pre>${escapePreviewHtml(preview.text)}</pre>` : "<p>No rendered content is available.</p>";
237
+ return `<!doctype html><html><head><meta charset="utf-8"><title>${escapePreviewHtml(preview.subject)}</title></head><body>${header}${body}</body></html>`;
238
+ }
239
+ function createMailPreviewResponse(preview, format) {
240
+ if (format === "json") {
241
+ return new Response(JSON.stringify(preview, null, 2), {
242
+ status: 200,
243
+ headers: { "content-type": "application/json; charset=utf-8" }
244
+ });
245
+ }
246
+ if (format === "text") {
247
+ if (typeof preview.text !== "string") throw new MailPreviewFormatUnavailableError(format);
248
+ return new Response(preview.text, {
249
+ status: 200,
250
+ headers: { "content-type": "text/plain; charset=utf-8" }
251
+ });
252
+ }
253
+ return new Response(createMailPreviewHtml(preview), {
254
+ status: 200,
255
+ headers: { "content-type": "text/html; charset=utf-8" }
256
+ });
257
+ }
258
+
259
+ // src/runtime.ts
260
+ var HOLO_MAIL_DELIVER_JOB = "holo.mail.deliver";
261
+ var LOCAL_ATTACHMENT_ROOTS = Object.freeze([
262
+ tmpdir(),
263
+ resolve(process.cwd(), "storage")
264
+ ]);
121
265
  function getRuntimeState() {
122
266
  const runtime = globalThis;
123
267
  runtime.__holoMailRuntime__ ??= {};
@@ -249,7 +393,7 @@ function getResolvedConfig() {
249
393
  return getRuntimeBindings().config ?? holoMailDefaults;
250
394
  }
251
395
  function resolveCurrentEnvironment() {
252
- return normalizeAppEnv(process.env.APP_ENV ?? process.env.NODE_ENV);
396
+ return normalizeMailEnvironment(process.env.APP_ENV ?? process.env.NODE_ENV);
253
397
  }
254
398
  function createPreviewPolicy(config = getResolvedConfig()) {
255
399
  return Object.freeze({
@@ -263,50 +407,8 @@ function assertPreviewEnabled(config = getResolvedConfig()) {
263
407
  throw new MailPreviewDisabledError(policy);
264
408
  }
265
409
  }
266
- function createPreviewHtml(preview) {
267
- const header = [
268
- `<h1>${escapeHtml(preview.subject)}</h1>`,
269
- `<p><strong>From:</strong> ${formatAddress(preview.from)}</p>`,
270
- `<p><strong>Reply-To:</strong> ${formatAddress(preview.replyTo)}</p>`,
271
- `<p><strong>To:</strong> ${preview.to.map(formatAddress).join(", ")}</p>`,
272
- preview.cc.length > 0 ? `<p><strong>Cc:</strong> ${preview.cc.map(formatAddress).join(", ")}</p>` : "",
273
- preview.bcc.length > 0 ? `<p><strong>Bcc:</strong> ${preview.bcc.map(formatAddress).join(", ")}</p>` : ""
274
- ].filter(Boolean).join("");
275
- const body = preview.html ? `<pre>${escapeHtml(preview.html)}</pre>` : preview.text ? `<pre>${escapeHtml(preview.text)}</pre>` : "<p>No rendered content is available.</p>";
276
- return `<!doctype html><html><head><meta charset="utf-8"><title>${escapeHtml(preview.subject)}</title></head><body>${header}${body}</body></html>`;
277
- }
278
- function escapeHtml(value) {
279
- return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
280
- }
281
- function formatAddress(address) {
282
- return address.name ? `${escapeHtml(address.name)} &lt;${escapeHtml(address.email)}&gt;` : escapeHtml(address.email);
283
- }
284
410
  function renderPreviewResponse(preview, format) {
285
- if (format === "json") {
286
- return new Response(JSON.stringify(preview, null, 2), {
287
- status: 200,
288
- headers: {
289
- "content-type": "application/json; charset=utf-8"
290
- }
291
- });
292
- }
293
- if (format === "text") {
294
- if (typeof preview.text !== "string") {
295
- throw new MailPreviewFormatUnavailableError(format);
296
- }
297
- return new Response(preview.text, {
298
- status: 200,
299
- headers: {
300
- "content-type": "text/plain; charset=utf-8"
301
- }
302
- });
303
- }
304
- return new Response(createPreviewHtml(preview), {
305
- status: 200,
306
- headers: {
307
- "content-type": "text/html; charset=utf-8"
308
- }
309
- });
411
+ return createMailPreviewResponse(preview, format);
310
412
  }
311
413
  function getMailerConfig(mailer, config = getResolvedConfig()) {
312
414
  const mailerConfig = config.mailers[mailer];
@@ -641,6 +743,21 @@ function resolveDriver(mail2, options, config) {
641
743
  implementation: registered.driver
642
744
  });
643
745
  }
746
+ function isMailDriverNotRegisteredError(error) {
747
+ return error instanceof MailError && error.code === "MAIL_DRIVER_NOT_REGISTERED";
748
+ }
749
+ async function resolveDriverWithPluginFallback(mail2, options, config) {
750
+ try {
751
+ return resolveDriver(mail2, options, config);
752
+ } catch (error) {
753
+ if (!isMailDriverNotRegisteredError(error)) {
754
+ throw error;
755
+ }
756
+ }
757
+ const state = getRuntimeState();
758
+ await loadMailPluginDrivers(state.projectRoot, state.pluginNames);
759
+ return resolveDriver(mail2, options, config);
760
+ }
644
761
  function resolveDriverByName(mailer, driver) {
645
762
  const builtIn = builtInDrivers[driver];
646
763
  if (builtIn) {
@@ -663,6 +780,18 @@ function resolveDriverByName(mailer, driver) {
663
780
  implementation: registered.driver
664
781
  });
665
782
  }
783
+ async function resolveDriverByNameWithPluginFallback(mailer, driver) {
784
+ try {
785
+ return resolveDriverByName(mailer, driver);
786
+ } catch (error) {
787
+ if (!isMailDriverNotRegisteredError(error)) {
788
+ throw error;
789
+ }
790
+ }
791
+ const state = getRuntimeState();
792
+ await loadMailPluginDrivers(state.projectRoot, state.pluginNames);
793
+ return resolveDriverByName(mailer, driver);
794
+ }
666
795
  function serializeQueuedAttachment(attachment) {
667
796
  return Object.freeze({
668
797
  source: attachment.source,
@@ -748,7 +877,7 @@ async function deliverResolvedMail(mail2, resolvedDriver, context) {
748
877
  }
749
878
  }
750
879
  async function runQueuedMailDelivery(payload) {
751
- const resolvedDriver = resolveDriverByName(payload.mailer, payload.driver);
880
+ const resolvedDriver = await resolveDriverByNameWithPluginFallback(payload.mailer, payload.driver);
752
881
  const context = Object.freeze({
753
882
  messageId: payload.messageId,
754
883
  mailer: payload.mailer,
@@ -817,14 +946,6 @@ async function renderView(input) {
817
946
  }
818
947
  return html;
819
948
  }
820
- function stripMarkdownSyntax(markdown) {
821
- return markdown.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1 ($2)").replace(/`([^`]+)`/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/^#{1,6}\s+/gm, "").replace(/^[-*]\s+/gm, "").trim();
822
- }
823
- function renderMarkdownInline(markdown) {
824
- return escapeHtml(markdown).replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, label, href) => {
825
- return isSafeMailHref(href) ? `<a href="${escapeHtml(href)}">${label}</a>` : label;
826
- }).replace(/`([^`]+)`/g, "<code>$1</code>").replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>").replace(/\*([^*]+)\*/g, "<em>$1</em>");
827
- }
828
949
  async function resolveLocalAttachmentPath(path) {
829
950
  const resolvedPath = await realpath(path);
830
951
  const allowedRoots = await Promise.all(LOCAL_ATTACHMENT_ROOTS.map(async (root) => await realpath(root).catch(() => null)));
@@ -833,37 +954,6 @@ async function resolveLocalAttachmentPath(path) {
833
954
  }
834
955
  throw new MailError("[@holo-js/mail] Path attachments must resolve inside an allowed attachment directory.", "MAIL_ATTACHMENT_UNSAFE_PATH");
835
956
  }
836
- function isSafeMailHref(href) {
837
- const trimmed = href.trim();
838
- if (trimmed.startsWith("#") || trimmed.startsWith("/") || trimmed.startsWith("./") || trimmed.startsWith("../")) {
839
- return true;
840
- }
841
- try {
842
- const url = new URL(trimmed);
843
- return url.protocol === "https:" || url.protocol === "http:" || url.protocol === "mailto:";
844
- } catch {
845
- return false;
846
- }
847
- }
848
- function renderMarkdown(markdown) {
849
- const blocks = markdown.trim().split(/\n\s*\n/g).map((block) => block.trim()).filter(Boolean);
850
- return blocks.map((block) => {
851
- const lines = block.split("\n").map((line) => line.trim()).filter(Boolean);
852
- if (lines.length === 0) {
853
- return "";
854
- }
855
- if (lines.every((line) => /^[-*]\s+/.test(line))) {
856
- return `<ul>${lines.map((line) => `<li>${renderMarkdownInline(line.replace(/^[-*]\s+/, ""))}</li>`).join("")}</ul>`;
857
- }
858
- const heading = lines.length === 1 ? lines[0].match(/^(#{1,6})\s+(.+)$/) : null;
859
- if (heading) {
860
- const [, markers, title] = heading;
861
- const level = markers.length;
862
- return `<h${level}>${renderMarkdownInline(title)}</h${level}>`;
863
- }
864
- return `<p>${lines.map(renderMarkdownInline).join("<br />")}</p>`;
865
- }).join("\n");
866
- }
867
957
  function resolveMailerName(mail2, config) {
868
958
  return mail2.mailer ?? config.default;
869
959
  }
@@ -1027,7 +1117,7 @@ var PendingSend = class {
1027
1117
  async #execute(execution = {}) {
1028
1118
  const options = Object.freeze({ ...this.#options });
1029
1119
  const config = getResolvedConfig();
1030
- const resolvedDriver = resolveDriver(this.#mail, options, config);
1120
+ const resolvedDriver = await resolveDriverWithPluginFallback(this.#mail, options, config);
1031
1121
  const plan = resolveQueuePlan(this.#mail, options, resolvedDriver.mailer, config);
1032
1122
  const messageId = randomUUID();
1033
1123
  const prepared = await prepareMailSend(this.#mail, plan.queued);
@@ -1093,7 +1183,18 @@ function resolveMailInput(mail2, overrides) {
1093
1183
  return normalizeMailDefinition(mergeMailDefinitionInputs(mail2, overrides));
1094
1184
  }
1095
1185
  function configureMailRuntime(bindings) {
1096
- getRuntimeState().bindings = bindings;
1186
+ const state = getRuntimeState();
1187
+ if (!bindings) {
1188
+ state.bindings = void 0;
1189
+ state.projectRoot = void 0;
1190
+ state.pluginNames = void 0;
1191
+ return;
1192
+ }
1193
+ const { projectRoot, plugins, ...runtimeBindings } = bindings;
1194
+ state.bindings = runtimeBindings;
1195
+ const normalizedProjectRoot = typeof projectRoot === "string" ? projectRoot.trim() : "";
1196
+ state.projectRoot = normalizedProjectRoot ? normalizedProjectRoot : void 0;
1197
+ state.pluginNames = Object.freeze([...new Set((plugins ?? []).map((plugin) => plugin.trim()).filter((plugin) => plugin.length > 0))]);
1097
1198
  }
1098
1199
  function getMailRuntimeBindings() {
1099
1200
  return getRuntimeBindings();
@@ -1101,12 +1202,15 @@ function getMailRuntimeBindings() {
1101
1202
  function resetMailRuntime() {
1102
1203
  const state = getRuntimeState();
1103
1204
  state.bindings = void 0;
1205
+ state.projectRoot = void 0;
1206
+ state.pluginNames = void 0;
1104
1207
  state.fakeSent = void 0;
1105
1208
  state.previewArtifacts = void 0;
1106
1209
  state.loadQueueModule = void 0;
1107
1210
  state.loadDbModule = void 0;
1108
1211
  state.loadNodemailerModule = void 0;
1109
1212
  state.loadStorageModule = void 0;
1213
+ resetMailPluginDrivers();
1110
1214
  }
1111
1215
  function sendMail(mail2, overrides) {
1112
1216
  return new PendingSend(resolveMailInput(mail2, overrides));
@@ -1163,7 +1267,6 @@ function getMailRuntime() {
1163
1267
  }
1164
1268
 
1165
1269
  // src/index.ts
1166
- import { defineMailConfig } from "@holo-js/config";
1167
1270
  var mail = Object.freeze({
1168
1271
  previewMail,
1169
1272
  renderMailPreview,
@@ -1188,20 +1291,24 @@ export {
1188
1291
  getMailRuntime,
1189
1292
  getMailRuntimeBindings,
1190
1293
  getRegisteredMailDriver,
1294
+ holoMailDefaults,
1191
1295
  inferMimeTypeFromName,
1192
1296
  isAttachmentQueueSafe,
1193
1297
  isMailDefinition,
1194
1298
  listFakeSentMails,
1195
1299
  listPreviewMailArtifacts,
1196
1300
  listRegisteredMailDrivers,
1301
+ loadMailPluginDrivers,
1197
1302
  mailRegistryInternals,
1198
1303
  mergeMailDefinitionInputs,
1304
+ normalizeMailConfig,
1199
1305
  normalizeMailDefinition,
1200
1306
  previewMail,
1201
1307
  registerMailDriver,
1202
1308
  renderMailPreview,
1203
1309
  resetFakeSentMails,
1204
1310
  resetMailDriverRegistry,
1311
+ resetMailPluginDrivers,
1205
1312
  resetMailRuntime,
1206
1313
  resetPreviewMailArtifacts,
1207
1314
  resolveAttachmentDefinition,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holo-js/mail",
3
- "version": "0.2.5",
3
+ "version": "0.3.0",
4
4
  "description": "Holo-JS Framework - mail contracts, preview helpers, and fluent send helpers",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -10,6 +10,11 @@
10
10
  "import": "./dist/index.mjs",
11
11
  "default": "./dist/index.mjs"
12
12
  },
13
+ "./config": {
14
+ "types": "./dist/config.d.ts",
15
+ "import": "./dist/config.mjs",
16
+ "default": "./dist/config.mjs"
17
+ },
13
18
  "./contracts": {
14
19
  "types": "./dist/contracts.d.ts",
15
20
  "import": "./dist/contracts.mjs",
@@ -28,12 +33,13 @@
28
33
  "test": "vitest --run"
29
34
  },
30
35
  "dependencies": {
31
- "@holo-js/config": "^0.2.5",
36
+ "@holo-js/config": "^0.3.0",
37
+ "@holo-js/kernel": "^0.3.0",
32
38
  "nodemailer": "^6.10.1"
33
39
  },
34
40
  "peerDependencies": {
35
- "@holo-js/queue": "^0.2.5",
36
- "@holo-js/storage": "^0.2.5"
41
+ "@holo-js/queue": "^0.3.0",
42
+ "@holo-js/storage": "^0.3.0"
37
43
  },
38
44
  "peerDependenciesMeta": {
39
45
  "@holo-js/queue": {