@holo-js/mail 0.1.4 → 0.1.6

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,9 +1,11 @@
1
- // src/contracts.ts
1
+ // src/contracts-types.ts
2
2
  import { defineMailConfig } from "@holo-js/config";
3
3
  var HOLO_MAIL_DEFINITION_MARKER = /* @__PURE__ */ Symbol.for("holo-js.mail.definition");
4
4
  var BUILT_IN_MAIL_DRIVERS = ["preview", "log", "fake", "smtp"];
5
5
  var MAIL_PRIORITY_VALUES = ["high", "normal", "low"];
6
6
  var MAIL_ATTACHMENT_DISPOSITIONS = ["attachment", "inline"];
7
+
8
+ // src/contracts.ts
7
9
  function isObject(value) {
8
10
  return !!value && typeof value === "object" && !Array.isArray(value);
9
11
  }
@@ -37,7 +39,13 @@ function normalizeDelayValue(value, label) {
37
39
  return value;
38
40
  }
39
41
  function normalizeJsonValue(value, label) {
40
- if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
42
+ if (value === null || typeof value === "string" || typeof value === "boolean") {
43
+ return value;
44
+ }
45
+ if (typeof value === "number") {
46
+ if (!Number.isFinite(value)) {
47
+ throw new Error(`[@holo-js/mail] ${label} must be JSON-serializable.`);
48
+ }
41
49
  return value;
42
50
  }
43
51
  if (Array.isArray(value)) {
@@ -495,45 +503,13 @@ function attachContent(content, options) {
495
503
  ...options
496
504
  });
497
505
  }
498
- var mailInternals = {
499
- BUILT_IN_MAIL_DRIVERS,
500
- HOLO_MAIL_DEFINITION_MARKER,
501
- MAIL_ATTACHMENT_DISPOSITIONS,
502
- MAIL_PRIORITY_VALUES,
503
- attachContent,
504
- attachFromPath,
505
- attachFromStorage,
506
- createAttachmentMetadata,
507
- createAttachmentResolutionPlan,
508
- createAttachmentResolutionPlans,
509
- inferMimeTypeFromName,
510
- inferAttachmentName,
511
- inferAttachmentSource,
512
- isObject,
513
- isAttachmentQueueSafe,
514
- isValidEmail,
515
- mergeMailDefinitionInputs,
516
- normalizeAddress,
517
- normalizeAttachment,
518
- normalizeAttachments,
519
- normalizeDelayValue,
520
- normalizeHeaders,
521
- normalizeJsonValue,
522
- normalizeMailDefinition,
523
- normalizeOptionalString,
524
- normalizePriority,
525
- normalizeQueueOptions,
526
- normalizeRecipients,
527
- normalizeRenderSource,
528
- normalizeRequiredString,
529
- resolveAttachmentDefinition,
530
- resolveNormalizedAttachment,
531
- normalizeTags,
532
- normalizeViewIdentifier
533
- };
534
506
 
535
507
  export {
536
508
  defineMailConfig,
509
+ HOLO_MAIL_DEFINITION_MARKER,
510
+ BUILT_IN_MAIL_DRIVERS,
511
+ MAIL_PRIORITY_VALUES,
512
+ MAIL_ATTACHMENT_DISPOSITIONS,
537
513
  inferMimeTypeFromName,
538
514
  createAttachmentMetadata,
539
515
  resolveNormalizedAttachment,
@@ -547,6 +523,5 @@ export {
547
523
  defineMail,
548
524
  attachFromPath,
549
525
  attachFromStorage,
550
- attachContent,
551
- mailInternals
526
+ attachContent
552
527
  };
@@ -1,6 +1,8 @@
1
1
  import { HoloAppEnv, NormalizedHoloMailConfig } from '@holo-js/config';
2
2
  export { defineMailConfig } from '@holo-js/config';
3
3
 
4
+ declare const HOLO_MAIL_DEFINITION_MARKER: unique symbol;
5
+ declare const BUILT_IN_MAIL_DRIVERS: readonly ["preview", "log", "fake", "smtp"];
4
6
  declare const MAIL_PRIORITY_VALUES: readonly ["high", "normal", "low"];
5
7
  declare const MAIL_ATTACHMENT_DISPOSITIONS: readonly ["attachment", "inline"];
6
8
  type MailJsonPrimitive = string | number | boolean | null;
@@ -248,31 +250,14 @@ interface PendingMailSend<TResult = MailSendResult> extends PromiseLike<TResult>
248
250
  catch<TResult1 = never>(onrejected?: ((reason: unknown) => TResult1 | PromiseLike<TResult1>) | null): Promise<TResult | TResult1>;
249
251
  finally(onfinally?: (() => void) | null): Promise<TResult>;
250
252
  }
251
- declare function isObject(value: unknown): value is Record<string, unknown>;
252
- declare function normalizeOptionalString(value: string | undefined, label: string): string | undefined;
253
- declare function normalizeRequiredString(value: string, label: string): string;
254
- declare function normalizeDelayValue(value: MailDelayValue, label: string): MailDelayValue;
255
- declare function normalizeJsonValue(value: unknown, label: string): MailJsonValue;
256
- declare function normalizeHeaders(headers: Readonly<Record<string, string>> | undefined): Readonly<Record<string, string>>;
257
- declare function normalizeTags(tags: readonly string[] | undefined): readonly string[];
258
- declare function normalizeViewIdentifier(value: string, label: string): string;
259
- declare function isValidEmail(value: string): boolean;
260
- declare function normalizeAddress(input: MailAddressInput, label: string): MailAddress;
261
- declare function normalizeRecipients(value: MailRecipientsInput | undefined, label: string, required: boolean): readonly MailAddress[];
262
- declare function normalizePriority(value: MailPriority | undefined): MailPriority | undefined;
263
- declare function inferAttachmentName(value: MailAttachmentInput): string | undefined;
253
+
264
254
  declare function inferMimeTypeFromName(name: string | undefined): string | undefined;
265
- declare function inferAttachmentSource(value: MailAttachmentInput): MailAttachmentMetadata['source'];
266
255
  declare function createAttachmentMetadata(attachment: MailAttachmentInput): MailAttachmentMetadata;
267
- declare function normalizeAttachment(input: MailAttachmentInput, index: number): MailAttachmentInput;
268
- declare function normalizeAttachments(attachments: readonly MailAttachmentInput[] | undefined): readonly MailAttachmentInput[];
269
256
  declare function resolveNormalizedAttachment(attachment: MailAttachmentInput): ResolvedMailAttachment;
270
257
  declare function isAttachmentQueueSafe(attachment: MailAttachmentInput): boolean;
271
258
  declare function resolveAttachmentDefinition(attachment: MailAttachmentInput): Promise<MailAttachmentInput>;
272
259
  declare function createAttachmentResolutionPlan(attachment: MailAttachmentInput, context: MailAttachmentResolutionContext): MailAttachmentResolutionPlan;
273
260
  declare function createAttachmentResolutionPlans(attachments: readonly MailAttachmentInput[], context: MailAttachmentResolutionContext): readonly MailAttachmentResolutionPlan[];
274
- declare function normalizeRenderSource(render: MailRenderSource | undefined): Readonly<MailRenderSource> | undefined;
275
- declare function normalizeQueueOptions(value: boolean | MailQueueOptions | undefined): boolean | Readonly<MailQueueOptions> | undefined;
276
261
  declare function isMailDefinition(value: unknown): value is MailDefinition;
277
262
  declare function mergeMailDefinitionInputs(base: MailDefinition | MailDefinitionInput, overrides: MailOverrideInput | undefined): MailDefinitionInput;
278
263
  declare function normalizeMailDefinition(input: MailDefinition | MailDefinitionInput): MailDefinition;
@@ -280,41 +265,5 @@ declare function defineMail<TMail extends MailDefinitionInput>(input: TMail): Ma
280
265
  declare function attachFromPath(path: string, options?: MailAttachmentHelperOptions): MailPathAttachment;
281
266
  declare function attachFromStorage(path: string, options?: MailStorageAttachmentHelperOptions): MailStorageAttachment;
282
267
  declare function attachContent(content: MailAttachmentContent, options: MailContentAttachmentHelperOptions): MailContentAttachment;
283
- declare const mailInternals: {
284
- BUILT_IN_MAIL_DRIVERS: readonly ["preview", "log", "fake", "smtp"];
285
- HOLO_MAIL_DEFINITION_MARKER: symbol;
286
- MAIL_ATTACHMENT_DISPOSITIONS: readonly ["attachment", "inline"];
287
- MAIL_PRIORITY_VALUES: readonly ["high", "normal", "low"];
288
- attachContent: typeof attachContent;
289
- attachFromPath: typeof attachFromPath;
290
- attachFromStorage: typeof attachFromStorage;
291
- createAttachmentMetadata: typeof createAttachmentMetadata;
292
- createAttachmentResolutionPlan: typeof createAttachmentResolutionPlan;
293
- createAttachmentResolutionPlans: typeof createAttachmentResolutionPlans;
294
- inferMimeTypeFromName: typeof inferMimeTypeFromName;
295
- inferAttachmentName: typeof inferAttachmentName;
296
- inferAttachmentSource: typeof inferAttachmentSource;
297
- isObject: typeof isObject;
298
- isAttachmentQueueSafe: typeof isAttachmentQueueSafe;
299
- isValidEmail: typeof isValidEmail;
300
- mergeMailDefinitionInputs: typeof mergeMailDefinitionInputs;
301
- normalizeAddress: typeof normalizeAddress;
302
- normalizeAttachment: typeof normalizeAttachment;
303
- normalizeAttachments: typeof normalizeAttachments;
304
- normalizeDelayValue: typeof normalizeDelayValue;
305
- normalizeHeaders: typeof normalizeHeaders;
306
- normalizeJsonValue: typeof normalizeJsonValue;
307
- normalizeMailDefinition: typeof normalizeMailDefinition;
308
- normalizeOptionalString: typeof normalizeOptionalString;
309
- normalizePriority: typeof normalizePriority;
310
- normalizeQueueOptions: typeof normalizeQueueOptions;
311
- normalizeRecipients: typeof normalizeRecipients;
312
- normalizeRenderSource: typeof normalizeRenderSource;
313
- normalizeRequiredString: typeof normalizeRequiredString;
314
- resolveAttachmentDefinition: typeof resolveAttachmentDefinition;
315
- resolveNormalizedAttachment: typeof resolveNormalizedAttachment;
316
- normalizeTags: typeof normalizeTags;
317
- normalizeViewIdentifier: typeof normalizeViewIdentifier;
318
- };
319
268
 
320
- export { type BuiltInMailDriverRegistry, type HoloMailDriverRegistry, type MailAddress, type MailAddressInput, type MailAttachmentBase, type MailAttachmentContent, type MailAttachmentDisposition, type MailAttachmentHelperOptions, type MailAttachmentInput, type MailAttachmentMetadata, type MailAttachmentResolutionContext, type MailAttachmentResolutionPlan, type MailContentAttachment, type MailContentAttachmentHelperOptions, type MailContentSourceKind, type MailDefinition, type MailDefinitionInput, type MailDelayValue, type MailDriver, type MailDriverExecutionContext, type MailDriverName, type MailDriverRegistry, type MailJsonObject, type MailJsonValue, type MailOverrideInput, type MailPathAttachment, type MailPreviewFormat, type MailPreviewInput, type MailPreviewPolicy, type MailPreviewResult, type MailPriority, type MailQueueOptions, type MailRecipientsInput, type MailRenderPreviewInput, type MailRenderSource, type MailResolvableAttachment, type MailResolvedAttachmentPayload, type MailRuntimeBindings, type MailSendInput, type MailSendOptions, type MailSendResult, type MailStorageAttachment, type MailStorageAttachmentHelperOptions, type MailViewRenderInput, type MailViewRenderer, type PendingMailSend, type RegisterMailDriverOptions, type RegisteredMailDriver, type ResolvedMail, type ResolvedMailAttachment, attachContent, attachFromPath, attachFromStorage, createAttachmentMetadata, createAttachmentResolutionPlan, createAttachmentResolutionPlans, defineMail, inferMimeTypeFromName, isAttachmentQueueSafe, isMailDefinition, mailInternals, mergeMailDefinitionInputs, normalizeMailDefinition, resolveAttachmentDefinition, resolveNormalizedAttachment };
269
+ export { BUILT_IN_MAIL_DRIVERS, type BuiltInMailDriverRegistry, HOLO_MAIL_DEFINITION_MARKER, type HoloMailDriverRegistry, MAIL_ATTACHMENT_DISPOSITIONS, MAIL_PRIORITY_VALUES, type MailAddress, type MailAddressInput, type MailAttachmentBase, type MailAttachmentContent, type MailAttachmentDisposition, type MailAttachmentHelperOptions, type MailAttachmentInput, type MailAttachmentMetadata, type MailAttachmentResolutionContext, type MailAttachmentResolutionPlan, type MailContentAttachment, type MailContentAttachmentHelperOptions, type MailContentSourceKind, type MailDefinition, type MailDefinitionInput, type MailDelayValue, type MailDriver, type MailDriverExecutionContext, type MailDriverName, type MailDriverRegistry, type MailJsonObject, type MailJsonValue, type MailOverrideInput, type MailPathAttachment, type MailPreviewFormat, type MailPreviewInput, type MailPreviewPolicy, type MailPreviewResult, type MailPriority, type MailQueueOptions, type MailRecipientsInput, type MailRenderPreviewInput, type MailRenderSource, type MailResolvableAttachment, type MailResolvedAttachmentPayload, type MailRuntimeBindings, type MailSendInput, type MailSendOptions, type MailSendResult, type MailStorageAttachment, type MailStorageAttachmentHelperOptions, type MailViewRenderInput, type MailViewRenderer, type PendingMailSend, type RegisterMailDriverOptions, type RegisteredMailDriver, type ResolvedMail, type ResolvedMailAttachment, attachContent, attachFromPath, attachFromStorage, createAttachmentMetadata, createAttachmentResolutionPlan, createAttachmentResolutionPlans, defineMail, inferMimeTypeFromName, isAttachmentQueueSafe, isMailDefinition, mergeMailDefinitionInputs, normalizeMailDefinition, resolveAttachmentDefinition, resolveNormalizedAttachment };
@@ -1,4 +1,8 @@
1
1
  import {
2
+ BUILT_IN_MAIL_DRIVERS,
3
+ HOLO_MAIL_DEFINITION_MARKER,
4
+ MAIL_ATTACHMENT_DISPOSITIONS,
5
+ MAIL_PRIORITY_VALUES,
2
6
  attachContent,
3
7
  attachFromPath,
4
8
  attachFromStorage,
@@ -10,13 +14,16 @@ import {
10
14
  inferMimeTypeFromName,
11
15
  isAttachmentQueueSafe,
12
16
  isMailDefinition,
13
- mailInternals,
14
17
  mergeMailDefinitionInputs,
15
18
  normalizeMailDefinition,
16
19
  resolveAttachmentDefinition,
17
20
  resolveNormalizedAttachment
18
- } from "./chunk-UB6ANQ2F.mjs";
21
+ } from "./chunk-BQCGZOVF.mjs";
19
22
  export {
23
+ BUILT_IN_MAIL_DRIVERS,
24
+ HOLO_MAIL_DEFINITION_MARKER,
25
+ MAIL_ATTACHMENT_DISPOSITIONS,
26
+ MAIL_PRIORITY_VALUES,
20
27
  attachContent,
21
28
  attachFromPath,
22
29
  attachFromStorage,
@@ -28,7 +35,6 @@ export {
28
35
  inferMimeTypeFromName,
29
36
  isAttachmentQueueSafe,
30
37
  isMailDefinition,
31
- mailInternals,
32
38
  mergeMailDefinitionInputs,
33
39
  normalizeMailDefinition,
34
40
  resolveAttachmentDefinition,
package/dist/index.d.ts CHANGED
@@ -1,163 +1,7 @@
1
- import * as _holo_js_config from '@holo-js/config';
2
- import { NormalizedHoloMailConfig, HoloAppEnv } from '@holo-js/config';
1
+ import { ResolvedMail, MailDriverExecutionContext, MailSendResult, MailPreviewPolicy, MailPreviewFormat, MailRuntimeBindings, MailDefinition, MailDefinitionInput, MailOverrideInput, MailPreviewResult, PendingMailSend, RegisteredMailDriver, MailDriver, RegisterMailDriverOptions } from './contracts.js';
2
+ 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
3
  export { HoloMailConfig, NormalizedHoloMailConfig, defineMailConfig } from '@holo-js/config';
4
- import { ResolvedMail, MailDriverExecutionContext, MailSendResult, MailPreviewPolicy, MailPreviewFormat, MailRuntimeBindings, MailDefinition, MailDefinitionInput, MailOverrideInput, MailPreviewResult, PendingMailSend, MailDelayValue, MailDriver, ResolvedMailAttachment, MailAddress, MailJsonObject, MailRenderSource, MailViewRenderInput, RegisteredMailDriver, RegisterMailDriverOptions } from './contracts.js';
5
- export { BuiltInMailDriverRegistry, HoloMailDriverRegistry, MailAddressInput, MailAttachmentBase, MailAttachmentContent, MailAttachmentDisposition, MailAttachmentHelperOptions, MailAttachmentInput, MailAttachmentMetadata, MailAttachmentResolutionContext, MailAttachmentResolutionPlan, MailContentAttachment, MailContentAttachmentHelperOptions, MailContentSourceKind, MailDriverName, MailJsonValue, MailPathAttachment, MailPreviewInput, MailPriority, MailQueueOptions, MailRecipientsInput, MailRenderPreviewInput, MailResolvableAttachment, MailResolvedAttachmentPayload, MailSendInput, MailSendOptions, MailStorageAttachment, MailStorageAttachmentHelperOptions, MailViewRenderer, attachContent, attachFromPath, attachFromStorage, createAttachmentMetadata, createAttachmentResolutionPlan, createAttachmentResolutionPlans, defineMail, inferMimeTypeFromName, isAttachmentQueueSafe, isMailDefinition, mailInternals, mergeMailDefinitionInputs, normalizeMailDefinition, resolveAttachmentDefinition, resolveNormalizedAttachment } from './contracts.js';
6
4
 
7
- type RuntimeState = {
8
- bindings?: MailRuntimeBindings;
9
- fakeSent?: FakeSentMail[];
10
- previewArtifacts?: MailPreviewArtifact[];
11
- loadQueueModule?: () => Promise<QueueModule>;
12
- loadDbModule?: () => Promise<DbModule | null>;
13
- loadNodemailerModule?: () => Promise<NodemailerModule>;
14
- loadStorageModule?: () => Promise<StorageModule>;
15
- };
16
- type MutableSendOptions = {
17
- mailer?: string;
18
- connection?: string;
19
- queue?: string;
20
- delay?: MailDelayValue;
21
- afterCommit?: boolean;
22
- };
23
- type ResolvedEnvelope = {
24
- readonly config: NormalizedHoloMailConfig;
25
- readonly mailer: string;
26
- readonly from: MailAddress;
27
- readonly replyTo: MailAddress;
28
- readonly to: readonly MailAddress[];
29
- readonly cc: readonly MailAddress[];
30
- readonly bcc: readonly MailAddress[];
31
- };
32
- type RenderedContent = {
33
- readonly kind: 'text' | 'html' | 'markdown' | 'render';
34
- readonly html?: string;
35
- readonly text?: string;
36
- readonly source: Readonly<{
37
- readonly kind: 'text' | 'html' | 'markdown' | 'render';
38
- readonly markdown?: string;
39
- readonly rawHtml?: string;
40
- readonly render?: Readonly<MailRenderSource>;
41
- }>;
42
- };
43
- type PreviewComputation = ResolvedEnvelope & {
44
- readonly preview: MailPreviewResult;
45
- };
46
- type SendPreparation = {
47
- readonly attachments: readonly ResolvedMailAttachment[];
48
- };
49
- type ResolvedDriver = {
50
- readonly mailer: string;
51
- readonly driver: string;
52
- readonly implementation: MailDriver;
53
- };
54
- type ResolvedQueuePlan = {
55
- readonly queued: boolean;
56
- readonly connection?: string;
57
- readonly queue?: string;
58
- readonly delay?: MailDelayValue;
59
- readonly afterCommit: boolean;
60
- };
61
- type QueueDispatchChain = {
62
- onConnection(name: string): QueueDispatchChain;
63
- onQueue(name: string): QueueDispatchChain;
64
- delay(value: number | Date): QueueDispatchChain;
65
- dispatch(): Promise<unknown>;
66
- };
67
- type QueueModule = {
68
- defineJob(definition: {
69
- handle(payload: QueuedMailDeliveryPayload): Promise<unknown> | unknown;
70
- }): unknown;
71
- dispatch(jobName: string, payload: QueuedMailDeliveryPayload): QueueDispatchChain;
72
- getRegisteredQueueJob(name: string): unknown;
73
- registerQueueJob(definition: unknown, options: {
74
- name: string;
75
- }): void;
76
- };
77
- type DbModule = {
78
- connectionAsyncContext: {
79
- getActive(): {
80
- connection: {
81
- getScope(): {
82
- kind: string;
83
- };
84
- afterCommit(callback: () => Promise<void>): void;
85
- };
86
- } | undefined;
87
- };
88
- };
89
- type NodemailerAddress = {
90
- readonly address: string;
91
- readonly name?: string;
92
- };
93
- type NodemailerAttachment = {
94
- readonly filename: string;
95
- readonly contentType?: string;
96
- readonly contentDisposition?: string;
97
- readonly cid?: string;
98
- readonly path?: string;
99
- readonly content?: string | Uint8Array;
100
- };
101
- type NodemailerMailMessage = {
102
- readonly messageId: string;
103
- readonly from: NodemailerAddress;
104
- readonly replyTo: NodemailerAddress;
105
- readonly to: readonly NodemailerAddress[];
106
- readonly cc?: readonly NodemailerAddress[];
107
- readonly bcc?: readonly NodemailerAddress[];
108
- readonly subject: string;
109
- readonly html?: string;
110
- readonly text?: string;
111
- readonly attachments?: readonly NodemailerAttachment[];
112
- readonly headers?: Readonly<Record<string, string>>;
113
- };
114
- type NodemailerSendResult = {
115
- readonly messageId?: string;
116
- readonly response?: string;
117
- };
118
- type NodemailerTransport = {
119
- sendMail(message: NodemailerMailMessage): Promise<NodemailerSendResult>;
120
- };
121
- type NodemailerModule = {
122
- createTransport(options: Record<string, unknown>): NodemailerTransport;
123
- };
124
- type StorageDisk = {
125
- readonly driver: string;
126
- path(path: string): string;
127
- getBytes(path: string): Promise<Uint8Array | null>;
128
- };
129
- type StorageModule = {
130
- Storage: {
131
- disk(diskName?: string): StorageDisk;
132
- };
133
- };
134
- type SerializedQueuedAttachment = Readonly<{
135
- readonly source: ResolvedMailAttachment['source'];
136
- readonly name: string;
137
- readonly contentType?: string;
138
- readonly disposition: ResolvedMailAttachment['disposition'];
139
- readonly contentId?: string;
140
- readonly path?: string;
141
- readonly storage?: {
142
- readonly path: string;
143
- readonly disk?: string;
144
- };
145
- readonly content?: string | Readonly<{
146
- readonly encoding: 'base64';
147
- readonly value: string;
148
- }>;
149
- }>;
150
- type QueuedResolvedMailPayload = Readonly<Omit<ResolvedMail, 'attachments'> & {
151
- readonly attachments: readonly SerializedQueuedAttachment[];
152
- }>;
153
- type QueuedMailDeliveryPayload = Readonly<{
154
- readonly messageId: string;
155
- readonly mailer: string;
156
- readonly driver: string;
157
- readonly queued: true;
158
- readonly deferred?: boolean;
159
- readonly mail: QueuedResolvedMailPayload;
160
- }>;
161
5
  interface FakeSentMail {
162
6
  readonly messageId: string;
163
7
  readonly createdAt: Date;
@@ -195,80 +39,6 @@ declare class MailSendError extends MailError {
195
39
  readonly message?: string;
196
40
  }, options?: ErrorOptions);
197
41
  }
198
- declare function getRuntimeState(): RuntimeState;
199
- declare function getRuntimeBindings(): MailRuntimeBindings;
200
- declare function dynamicImport<TModule>(specifier: string): Promise<TModule>;
201
- declare function loadQueueModule(): Promise<QueueModule>;
202
- declare function loadDbModule(): Promise<DbModule | null>;
203
- declare function resolveNodemailerModule(module: unknown): NodemailerModule;
204
- declare function loadNodemailerModule(): Promise<NodemailerModule>;
205
- declare function resolveStorageModule(module: unknown): StorageModule;
206
- declare function loadStorageModule(): Promise<StorageModule>;
207
- declare function getFakeSentState(): FakeSentMail[];
208
- declare function getPreviewArtifactState(): MailPreviewArtifact[];
209
- declare function normalizeExecutionString(value: string, label: string): string;
210
- declare function normalizeExecutionDelay(value: MailDelayValue): MailDelayValue;
211
- declare function getResolvedConfig(): NormalizedHoloMailConfig;
212
- declare function resolveCurrentEnvironment(): HoloAppEnv;
213
- declare function createPreviewPolicy(config?: NormalizedHoloMailConfig): MailPreviewPolicy;
214
- declare function assertPreviewEnabled(config?: NormalizedHoloMailConfig): void;
215
- declare function createPreviewHtml(preview: MailPreviewResult): string;
216
- declare function escapeHtml(value: string): string;
217
- declare function formatAddress(address: {
218
- readonly email: string;
219
- readonly name?: string;
220
- }): string;
221
- declare function renderPreviewResponse(preview: MailPreviewResult, format: MailPreviewFormat): Response;
222
- declare function getMailerConfig(mailer: string, config?: NormalizedHoloMailConfig): _holo_js_config.NormalizedHoloMailMailerConfig;
223
- declare function isQueueRequested(mail: MailDefinition, mailer: string, options: MutableSendOptions | Readonly<MutableSendOptions>, config?: NormalizedHoloMailConfig): boolean;
224
- declare function resolveQueuePlan(mail: MailDefinition, options: MutableSendOptions | Readonly<MutableSendOptions>, mailer: string, config?: NormalizedHoloMailConfig): ResolvedQueuePlan;
225
- declare function prepareMailSend(mail: MailDefinition, queued: boolean): Promise<SendPreparation>;
226
- declare function createResolvedMail(preview: MailPreviewResult, attachments: readonly ResolvedMailAttachment[]): ResolvedMail;
227
- declare function createSendContext(messageId: string, resolvedDriver: ResolvedDriver, queued: boolean, deferred?: boolean): MailDriverExecutionContext;
228
- declare function freezeSendResult(result: MailSendResult): Readonly<MailSendResult>;
229
- declare function createBaseSendResult(context: MailDriverExecutionContext, overrides?: Partial<MailSendResult>): Readonly<MailSendResult>;
230
- declare function normalizeDriverResult(result: MailSendResult | undefined, context: MailDriverExecutionContext): Readonly<MailSendResult>;
231
- declare function createMailRecord<TRecord extends FakeSentMail | MailPreviewArtifact>(context: MailDriverExecutionContext, mail: Readonly<ResolvedMail>, result: Readonly<MailSendResult>): TRecord;
232
- declare function persistPreviewArtifact(artifact: MailPreviewArtifact): Promise<void>;
233
- declare function createSmtpAddress(address: MailAddress): NodemailerAddress;
234
- declare function createSmtpHeaders(mail: Readonly<ResolvedMail>): Readonly<Record<string, string>> | undefined;
235
- declare function createSmtpAttachment(attachment: ResolvedMailAttachment): Promise<NodemailerAttachment>;
236
- declare function createSmtpMessage(mail: Readonly<ResolvedMail>, context: Readonly<MailDriverExecutionContext>): Promise<NodemailerMailMessage>;
237
- declare function sendViaSmtp(mail: Readonly<ResolvedMail>, context: Readonly<MailDriverExecutionContext>): Promise<Readonly<MailSendResult>>;
238
- declare function resolveDriver(mail: MailDefinition, options: MutableSendOptions | Readonly<MutableSendOptions>, config: NormalizedHoloMailConfig): ResolvedDriver;
239
- declare function resolveDriverByName(mailer: string, driver: string): ResolvedDriver;
240
- declare function serializeQueuedAttachment(attachment: ResolvedMailAttachment): SerializedQueuedAttachment;
241
- declare function deserializeQueuedAttachment(attachment: SerializedQueuedAttachment): ResolvedMailAttachment;
242
- declare function createQueuedMailPayload(mail: ResolvedMail, context: MailDriverExecutionContext): QueuedMailDeliveryPayload;
243
- declare function createResolvedMailFromQueuedPayload(payload: QueuedMailDeliveryPayload): ResolvedMail;
244
- declare function deliverResolvedMail(mail: ResolvedMail, resolvedDriver: ResolvedDriver, context: MailDriverExecutionContext): Promise<Readonly<MailSendResult>>;
245
- declare function runQueuedMailDelivery(payload: QueuedMailDeliveryPayload): Promise<Readonly<MailSendResult>>;
246
- declare function ensureMailQueueJobRegistered(queueModule?: QueueModule): Promise<QueueModule>;
247
- declare function dispatchQueuedMail(mail: ResolvedMail, context: MailDriverExecutionContext, plan: ResolvedQueuePlan): Promise<Readonly<MailSendResult>>;
248
- declare function deferSendUntilCommit(context: MailDriverExecutionContext, callback: () => Promise<Readonly<MailSendResult>>): Promise<Readonly<MailSendResult> | null>;
249
- declare function renderView(input: MailViewRenderInput): Promise<string>;
250
- declare function stripMarkdownSyntax(markdown: string): string;
251
- declare function renderMarkdownInline(markdown: string): string;
252
- declare function renderMarkdown(markdown: string): string;
253
- declare function resolveSourceKind(mail: MailDefinition): RenderedContent['kind'];
254
- declare function resolveMailerName(mail: MailDefinition, config: NormalizedHoloMailConfig): string;
255
- declare function resolveEnvelope(mail: MailDefinition): ResolvedEnvelope;
256
- declare function createMarkdownWrapperProps(mail: MailDefinition, envelope: ResolvedEnvelope, html: string, text: string): MailJsonObject;
257
- declare function renderContent(mail: MailDefinition, envelope: ResolvedEnvelope): Promise<RenderedContent>;
258
- declare function computePreview(mail: MailDefinition): Promise<PreviewComputation>;
259
- declare class PendingSend implements PendingMailSend<MailSendResult> {
260
- #private;
261
- constructor(mail: MailDefinition, options?: MutableSendOptions);
262
- using(name: string): PendingSend;
263
- onConnection(name: string): PendingSend;
264
- onQueue(name: string): PendingSend;
265
- delay(value: MailDelayValue): PendingSend;
266
- afterCommit(): PendingSend;
267
- then<TResult1 = MailSendResult, TResult2 = never>(onfulfilled?: ((value: MailSendResult) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
268
- catch<TResult1 = never>(onrejected?: ((reason: unknown) => TResult1 | PromiseLike<TResult1>) | null): Promise<MailSendResult | TResult1>;
269
- finally(onfinally?: (() => void) | null): Promise<MailSendResult>;
270
- private execute;
271
- }
272
42
  interface MailRuntimeFacade {
273
43
  sendMail(mail: MailDefinition | MailDefinitionInput, overrides?: MailOverrideInput): PendingMailSend<MailSendResult>;
274
44
  previewMail(mail: MailDefinition | MailDefinitionInput, overrides?: MailOverrideInput): Promise<MailPreviewResult>;
@@ -278,7 +48,6 @@ declare function listFakeSentMails(): readonly FakeSentMail[];
278
48
  declare function resetFakeSentMails(): void;
279
49
  declare function listPreviewMailArtifacts(): readonly MailPreviewArtifact[];
280
50
  declare function resetPreviewMailArtifacts(): void;
281
- declare function resolveMailInput(mail: MailDefinition | MailDefinitionInput, overrides?: MailOverrideInput): MailDefinition;
282
51
  declare function configureMailRuntime(bindings?: MailRuntimeBindings): void;
283
52
  declare function getMailRuntimeBindings(): MailRuntimeBindings;
284
53
  declare function resetMailRuntime(): void;
@@ -286,78 +55,6 @@ declare function sendMail(mail: MailDefinition | MailDefinitionInput, overrides?
286
55
  declare function previewMail(mail: MailDefinition | MailDefinitionInput, overrides?: MailOverrideInput): Promise<MailPreviewResult>;
287
56
  declare function renderMailPreview(mail: MailDefinition | MailDefinitionInput, overrides?: MailOverrideInput, format?: MailPreviewFormat): Promise<Response>;
288
57
  declare function getMailRuntime(): MailRuntimeFacade;
289
- declare const mailRuntimeInternals: {
290
- HOLO_MAIL_DELIVER_JOB: string;
291
- MailSendError: typeof MailSendError;
292
- MailError: typeof MailError;
293
- MailPreviewDisabledError: typeof MailPreviewDisabledError;
294
- MailPreviewFormatUnavailableError: typeof MailPreviewFormatUnavailableError;
295
- PendingSend: typeof PendingSend;
296
- assertPreviewEnabled: typeof assertPreviewEnabled;
297
- builtInDrivers: Readonly<Record<string, MailDriver<MailSendResult>>>;
298
- createBaseSendResult: typeof createBaseSendResult;
299
- createMailRecord: typeof createMailRecord;
300
- createQueuedMailPayload: typeof createQueuedMailPayload;
301
- createResolvedMail: typeof createResolvedMail;
302
- createSmtpAddress: typeof createSmtpAddress;
303
- createSmtpAttachment: typeof createSmtpAttachment;
304
- createSmtpHeaders: typeof createSmtpHeaders;
305
- createSmtpMessage: typeof createSmtpMessage;
306
- createSendContext: typeof createSendContext;
307
- createResolvedMailFromQueuedPayload: typeof createResolvedMailFromQueuedPayload;
308
- computePreview: typeof computePreview;
309
- createMarkdownWrapperProps: typeof createMarkdownWrapperProps;
310
- createPreviewHtml: typeof createPreviewHtml;
311
- createPreviewPolicy: typeof createPreviewPolicy;
312
- deferSendUntilCommit: typeof deferSendUntilCommit;
313
- deliverResolvedMail: typeof deliverResolvedMail;
314
- deserializeQueuedAttachment: typeof deserializeQueuedAttachment;
315
- dispatchQueuedMail: typeof dispatchQueuedMail;
316
- dynamicImport: typeof dynamicImport;
317
- ensureMailQueueJobRegistered: typeof ensureMailQueueJobRegistered;
318
- escapeHtml: typeof escapeHtml;
319
- formatAddress: typeof formatAddress;
320
- freezeSendResult: typeof freezeSendResult;
321
- getFakeSentState: typeof getFakeSentState;
322
- getMailerConfig: typeof getMailerConfig;
323
- getPreviewArtifactState: typeof getPreviewArtifactState;
324
- getResolvedConfig: typeof getResolvedConfig;
325
- getRuntimeBindings: typeof getRuntimeBindings;
326
- getRuntimeState: typeof getRuntimeState;
327
- loadDbModule: typeof loadDbModule;
328
- loadNodemailerModule: typeof loadNodemailerModule;
329
- loadQueueModule: typeof loadQueueModule;
330
- loadStorageModule: typeof loadStorageModule;
331
- isQueueRequested: typeof isQueueRequested;
332
- normalizeExecutionDelay: typeof normalizeExecutionDelay;
333
- normalizeExecutionString: typeof normalizeExecutionString;
334
- normalizeDriverResult: typeof normalizeDriverResult;
335
- persistPreviewArtifact: typeof persistPreviewArtifact;
336
- prepareMailSend: typeof prepareMailSend;
337
- renderContent: typeof renderContent;
338
- renderMarkdown: typeof renderMarkdown;
339
- renderMarkdownInline: typeof renderMarkdownInline;
340
- renderPreviewResponse: typeof renderPreviewResponse;
341
- renderView: typeof renderView;
342
- resolveDriver: typeof resolveDriver;
343
- resolveDriverByName: typeof resolveDriverByName;
344
- resolveCurrentEnvironment: typeof resolveCurrentEnvironment;
345
- resolveEnvelope: typeof resolveEnvelope;
346
- resolveQueuePlan: typeof resolveQueuePlan;
347
- resolveMailInput: typeof resolveMailInput;
348
- resolveMailerName: typeof resolveMailerName;
349
- resolveNodemailerModule: typeof resolveNodemailerModule;
350
- resolveSourceKind: typeof resolveSourceKind;
351
- resolveStorageModule: typeof resolveStorageModule;
352
- runQueuedMailDelivery: typeof runQueuedMailDelivery;
353
- sendViaSmtp: typeof sendViaSmtp;
354
- serializeQueuedAttachment: typeof serializeQueuedAttachment;
355
- setDbModuleLoader(loader: (() => Promise<DbModule | null>) | undefined): void;
356
- setNodemailerModuleLoader(loader: (() => Promise<NodemailerModule>) | undefined): void;
357
- setQueueModuleLoader(loader: (() => Promise<QueueModule>) | undefined): void;
358
- setStorageModuleLoader(loader: (() => Promise<StorageModule>) | undefined): void;
359
- stripMarkdownSyntax: typeof stripMarkdownSyntax;
360
- };
361
58
 
362
59
  declare function normalizeDriverName(value: string): string;
363
60
  declare function getRegistry(): Map<string, RegisteredMailDriver>;
@@ -376,4 +73,4 @@ declare const mail: Readonly<{
376
73
  sendMail: typeof sendMail;
377
74
  }>;
378
75
 
379
- export { type FakeSentMail, MailAddress, MailDefinition, MailDefinitionInput, MailDelayValue, MailDriver, MailDriverExecutionContext, MailError, MailJsonObject, MailOverrideInput, type MailPreviewArtifact, MailPreviewDisabledError, MailPreviewFormat, MailPreviewFormatUnavailableError, MailPreviewPolicy, MailPreviewResult, MailRenderSource, MailRuntimeBindings, MailSendError, MailSendResult, MailViewRenderInput, PendingMailSend, RegisterMailDriverOptions, RegisteredMailDriver, ResolvedMail, ResolvedMailAttachment, configureMailRuntime, mail as default, getMailRuntime, getMailRuntimeBindings, getRegisteredMailDriver, listFakeSentMails, listPreviewMailArtifacts, listRegisteredMailDrivers, mailRegistryInternals, mailRuntimeInternals, previewMail, registerMailDriver, renderMailPreview, resetFakeSentMails, resetMailDriverRegistry, resetMailRuntime, resetPreviewMailArtifacts, sendMail };
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 };
package/dist/index.mjs CHANGED
@@ -9,17 +9,17 @@ import {
9
9
  inferMimeTypeFromName,
10
10
  isAttachmentQueueSafe,
11
11
  isMailDefinition,
12
- mailInternals,
13
12
  mergeMailDefinitionInputs,
14
13
  normalizeMailDefinition,
15
14
  resolveAttachmentDefinition,
16
15
  resolveNormalizedAttachment
17
- } from "./chunk-UB6ANQ2F.mjs";
16
+ } from "./chunk-BQCGZOVF.mjs";
18
17
 
19
18
  // src/runtime.ts
20
19
  import { randomUUID } from "crypto";
21
- import { mkdir, writeFile } from "fs/promises";
22
- import { join } from "path";
20
+ import { mkdir, realpath, writeFile } from "fs/promises";
21
+ import { tmpdir } from "os";
22
+ import { join, resolve, sep } from "path";
23
23
  import {
24
24
  holoMailDefaults,
25
25
  normalizeAppEnv
@@ -68,6 +68,10 @@ var mailRegistryInternals = {
68
68
 
69
69
  // src/runtime.ts
70
70
  var HOLO_MAIL_DELIVER_JOB = "holo.mail.deliver";
71
+ var LOCAL_ATTACHMENT_ROOTS = Object.freeze([
72
+ tmpdir(),
73
+ resolve(process.cwd(), "storage")
74
+ ]);
71
75
  var MailError = class extends Error {
72
76
  code;
73
77
  constructor(message, code = "MAIL_ERROR", options) {
@@ -128,53 +132,40 @@ function dynamicImport(specifier) {
128
132
  specifier
129
133
  );
130
134
  }
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
- }
135
+ function isModuleNotFoundError(error) {
136
+ return !!(error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND");
137
+ }
138
+ async function loadPeerModule(options) {
146
139
  try {
147
- return await dynamicImport("@holo-js/queue");
140
+ const module = options.loadOverride ? await options.loadOverride() : await dynamicImport(options.specifier);
141
+ return options.resolve ? options.resolve(module) : module;
148
142
  } catch (error) {
149
- if (error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") {
143
+ if (isModuleNotFoundError(error)) {
144
+ return options.missing(error);
145
+ }
146
+ throw error;
147
+ }
148
+ }
149
+ async function loadQueueModule() {
150
+ return loadPeerModule({
151
+ loadOverride: getRuntimeState().loadQueueModule,
152
+ specifier: "@holo-js/queue",
153
+ missing() {
150
154
  throw new MailError(
151
155
  "[@holo-js/mail] Queued or delayed mail delivery requires @holo-js/queue to be installed.",
152
156
  "MAIL_QUEUE_MODULE_MISSING"
153
157
  );
154
158
  }
155
- throw error;
156
- }
159
+ });
157
160
  }
158
161
  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") {
162
+ return loadPeerModule({
163
+ loadOverride: getRuntimeState().loadDbModule,
164
+ specifier: "@holo-js/db",
165
+ missing() {
174
166
  return null;
175
167
  }
176
- throw error;
177
- }
168
+ });
178
169
  }
179
170
  function resolveNodemailerModule(module) {
180
171
  if (module && typeof module === "object" && "createTransport" in module && typeof module.createTransport === "function") {
@@ -189,33 +180,18 @@ function resolveNodemailerModule(module) {
189
180
  );
190
181
  }
191
182
  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") {
183
+ return loadPeerModule({
184
+ loadOverride: getRuntimeState().loadNodemailerModule,
185
+ specifier: "nodemailer",
186
+ resolve: resolveNodemailerModule,
187
+ missing(error) {
211
188
  throw new MailError(
212
189
  "[@holo-js/mail] SMTP delivery requires nodemailer to be installed.",
213
190
  "MAIL_SMTP_MODULE_MISSING",
214
191
  { cause: error }
215
192
  );
216
193
  }
217
- throw error;
218
- }
194
+ });
219
195
  }
220
196
  function resolveStorageModule(module) {
221
197
  if (module && typeof module === "object" && "Storage" in module && module.Storage && typeof module.Storage?.disk === "function") {
@@ -227,33 +203,18 @@ function resolveStorageModule(module) {
227
203
  );
228
204
  }
229
205
  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") {
206
+ return loadPeerModule({
207
+ loadOverride: getRuntimeState().loadStorageModule,
208
+ specifier: "@holo-js/storage",
209
+ resolve: resolveStorageModule,
210
+ missing(error) {
249
211
  throw new MailError(
250
212
  "[@holo-js/mail] Storage-backed attachments require @holo-js/storage to be installed.",
251
213
  "MAIL_STORAGE_MODULE_MISSING",
252
214
  { cause: error }
253
215
  );
254
216
  }
255
- throw error;
256
- }
217
+ });
257
218
  }
258
219
  function getFakeSentState() {
259
220
  const state = getRuntimeState();
@@ -311,7 +272,7 @@ function createPreviewHtml(preview) {
311
272
  preview.cc.length > 0 ? `<p><strong>Cc:</strong> ${preview.cc.map(formatAddress).join(", ")}</p>` : "",
312
273
  preview.bcc.length > 0 ? `<p><strong>Bcc:</strong> ${preview.bcc.map(formatAddress).join(", ")}</p>` : ""
313
274
  ].filter(Boolean).join("");
314
- const body = preview.html ? preview.html : preview.text ? `<pre>${escapeHtml(preview.text)}</pre>` : "<p>No rendered content is available.</p>";
275
+ const body = preview.html ? `<pre>${escapeHtml(preview.html)}</pre>` : preview.text ? `<pre>${escapeHtml(preview.text)}</pre>` : "<p>No rendered content is available.</p>";
315
276
  return `<!doctype html><html><head><meta charset="utf-8"><title>${escapeHtml(preview.subject)}</title></head><body>${header}${body}</body></html>`;
316
277
  }
317
278
  function escapeHtml(value) {
@@ -483,10 +444,10 @@ async function persistPreviewArtifact(artifact) {
483
444
  );
484
445
  }
485
446
  function createSmtpAddress(address) {
486
- return Object.freeze({
447
+ return {
487
448
  address: address.email,
488
449
  ...typeof address.name === "string" ? { name: address.name } : {}
489
- });
450
+ };
490
451
  }
491
452
  function createSmtpHeaders(mail2) {
492
453
  const headers = { ...mail2.headers };
@@ -519,19 +480,19 @@ async function createSmtpAttachment(attachment) {
519
480
  ...typeof attachment.contentId === "string" ? { cid: attachment.contentId } : {}
520
481
  };
521
482
  if (typeof attachment.path === "string") {
522
- return Object.freeze({
483
+ return {
523
484
  ...base,
524
- path: attachment.path
525
- });
485
+ path: await resolveLocalAttachmentPath(attachment.path)
486
+ };
526
487
  }
527
488
  if (attachment.storage) {
528
489
  const storageModule = await loadStorageModule();
529
490
  const disk = storageModule.Storage.disk(attachment.storage.disk);
530
491
  if (disk.driver !== "s3") {
531
- return Object.freeze({
492
+ return {
532
493
  ...base,
533
494
  path: disk.path(attachment.storage.path)
534
- });
495
+ };
535
496
  }
536
497
  const bytes = await disk.getBytes(attachment.storage.path);
537
498
  if (!bytes) {
@@ -540,16 +501,16 @@ async function createSmtpAttachment(attachment) {
540
501
  "MAIL_STORAGE_ATTACHMENT_MISSING"
541
502
  );
542
503
  }
543
- return Object.freeze({
504
+ return {
544
505
  ...base,
545
506
  content: bytes
546
- });
507
+ };
547
508
  }
548
509
  if (typeof attachment.content !== "undefined") {
549
- return Object.freeze({
510
+ return {
550
511
  ...base,
551
512
  content: attachment.content
552
- });
513
+ };
553
514
  }
554
515
  throw new MailError(
555
516
  `[@holo-js/mail] Attachment "${attachment.name}" could not be translated for SMTP delivery.`,
@@ -557,21 +518,21 @@ async function createSmtpAttachment(attachment) {
557
518
  );
558
519
  }
559
520
  async function createSmtpMessage(mail2, context) {
560
- const attachments = mail2.attachments.length > 0 ? Object.freeze(await Promise.all(mail2.attachments.map(createSmtpAttachment))) : void 0;
521
+ const attachments = mail2.attachments.length > 0 ? await Promise.all(mail2.attachments.map(createSmtpAttachment)) : void 0;
561
522
  const headers = createSmtpHeaders(mail2);
562
- return Object.freeze({
523
+ return {
563
524
  messageId: context.messageId,
564
525
  from: createSmtpAddress(mail2.from),
565
526
  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)) } : {},
527
+ to: mail2.to.map(createSmtpAddress),
528
+ ...mail2.cc.length > 0 ? { cc: mail2.cc.map(createSmtpAddress) } : {},
529
+ ...mail2.bcc.length > 0 ? { bcc: mail2.bcc.map(createSmtpAddress) } : {},
569
530
  subject: mail2.subject,
570
531
  ...typeof mail2.html === "string" ? { html: mail2.html } : {},
571
532
  ...typeof mail2.text === "string" ? { text: mail2.text } : {},
572
533
  ...attachments ? { attachments } : {},
573
534
  ...headers ? { headers } : {}
574
- });
535
+ };
575
536
  }
576
537
  async function sendViaSmtp(mail2, context) {
577
538
  const nodemailer = await loadNodemailerModule();
@@ -860,7 +821,29 @@ function stripMarkdownSyntax(markdown) {
860
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();
861
822
  }
862
823
  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>");
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
+ async function resolveLocalAttachmentPath(path) {
829
+ const resolvedPath = await realpath(path);
830
+ const allowedRoots = await Promise.all(LOCAL_ATTACHMENT_ROOTS.map(async (root) => await realpath(root).catch(() => null)));
831
+ if (allowedRoots.some((root) => root && (resolvedPath === root || resolvedPath.startsWith(`${root}${sep}`)))) {
832
+ return resolvedPath;
833
+ }
834
+ throw new MailError("[@holo-js/mail] Path attachments must resolve inside an allowed attachment directory.", "MAIL_ATTACHMENT_UNSAFE_PATH");
835
+ }
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
+ }
864
847
  }
865
848
  function renderMarkdown(markdown) {
866
849
  const blocks = markdown.trim().split(/\n\s*\n/g).map((block) => block.trim()).filter(Boolean);
@@ -881,18 +864,6 @@ function renderMarkdown(markdown) {
881
864
  return `<p>${lines.map(renderMarkdownInline).join("<br />")}</p>`;
882
865
  }).join("\n");
883
866
  }
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
867
  function resolveMailerName(mail2, config) {
897
868
  return mail2.mailer ?? config.default;
898
869
  }
@@ -1190,86 +1161,6 @@ function getMailRuntime() {
1190
1161
  renderMailPreview
1191
1162
  });
1192
1163
  }
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
1164
 
1274
1165
  // src/index.ts
1275
1166
  import { defineMailConfig } from "@holo-js/config";
@@ -1303,9 +1194,7 @@ export {
1303
1194
  listFakeSentMails,
1304
1195
  listPreviewMailArtifacts,
1305
1196
  listRegisteredMailDrivers,
1306
- mailInternals,
1307
1197
  mailRegistryInternals,
1308
- mailRuntimeInternals,
1309
1198
  mergeMailDefinitionInputs,
1310
1199
  normalizeMailDefinition,
1311
1200
  previewMail,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holo-js/mail",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Holo-JS Framework - mail contracts, preview helpers, and fluent send helpers",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -28,12 +28,12 @@
28
28
  "test": "vitest --run"
29
29
  },
30
30
  "dependencies": {
31
- "@holo-js/config": "^0.1.4",
31
+ "@holo-js/config": "^0.1.6",
32
32
  "nodemailer": "^6.10.1"
33
33
  },
34
34
  "peerDependencies": {
35
- "@holo-js/queue": "^0.1.4",
36
- "@holo-js/storage": "^0.1.4"
35
+ "@holo-js/queue": "^0.1.6",
36
+ "@holo-js/storage": "^0.1.6"
37
37
  },
38
38
  "peerDependenciesMeta": {
39
39
  "@holo-js/queue": {
@@ -47,6 +47,6 @@
47
47
  "@types/node": "^22.10.2",
48
48
  "tsup": "^8.3.5",
49
49
  "typescript": "^5.7.2",
50
- "vitest": "^2.1.8"
50
+ "vitest": "^4.1.5"
51
51
  }
52
52
  }