@gravito/signal 3.0.0 → 3.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/Mailable.ts CHANGED
@@ -8,6 +8,27 @@ import type { Address, Attachment, Envelope, MailConfig } from './types'
8
8
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
9
  type ComponentType = any
10
10
 
11
+ /**
12
+ * Base class for all mailable messages.
13
+ *
14
+ * Provides a fluent API for building email envelopes and rendering content
15
+ * using various engines (raw HTML, Prism templates, React, or Vue).
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * class WelcomeMail extends Mailable {
20
+ * build() {
21
+ * return this.subject('Welcome!')
22
+ * .view('emails.welcome', { name: 'John' });
23
+ * }
24
+ * }
25
+ *
26
+ * await new WelcomeMail().to('john@example.com').send();
27
+ * ```
28
+ *
29
+ * @public
30
+ * @since 3.0.0
31
+ */
11
32
  export abstract class Mailable implements Queueable {
12
33
  protected envelope: Partial<Envelope> = {}
13
34
  protected renderer?: Renderer
@@ -17,41 +38,81 @@ export abstract class Mailable implements Queueable {
17
38
 
18
39
  // ===== Fluent API (Envelope Construction) =====
19
40
 
41
+ /**
42
+ * Set the sender address for the email.
43
+ *
44
+ * @param address - Email address or Address object.
45
+ */
20
46
  from(address: string | Address): this {
21
47
  this.envelope.from = typeof address === 'string' ? { address } : address
22
48
  return this
23
49
  }
24
50
 
51
+ /**
52
+ * Set the primary recipient(s) for the email.
53
+ *
54
+ * @param address - Single address, address object, or array of either.
55
+ */
25
56
  to(address: string | Address | (string | Address)[]): this {
26
57
  this.envelope.to = this.normalizeAddressArray(address)
27
58
  return this
28
59
  }
29
60
 
61
+ /**
62
+ * Set the carbon copy (CC) recipient(s).
63
+ *
64
+ * @param address - Single address, address object, or array of either.
65
+ */
30
66
  cc(address: string | Address | (string | Address)[]): this {
31
67
  this.envelope.cc = this.normalizeAddressArray(address)
32
68
  return this
33
69
  }
34
70
 
71
+ /**
72
+ * Set the blind carbon copy (BCC) recipient(s).
73
+ *
74
+ * @param address - Single address, address object, or array of either.
75
+ */
35
76
  bcc(address: string | Address | (string | Address)[]): this {
36
77
  this.envelope.bcc = this.normalizeAddressArray(address)
37
78
  return this
38
79
  }
39
80
 
81
+ /**
82
+ * Set the reply-to address.
83
+ *
84
+ * @param address - Email address or Address object.
85
+ */
40
86
  replyTo(address: string | Address): this {
41
87
  this.envelope.replyTo = typeof address === 'string' ? { address } : address
42
88
  return this
43
89
  }
44
90
 
91
+ /**
92
+ * Set the subject line for the email.
93
+ *
94
+ * @param subject - The email subject.
95
+ */
45
96
  subject(subject: string): this {
46
97
  this.envelope.subject = subject
47
98
  return this
48
99
  }
49
100
 
101
+ /**
102
+ * Set the email priority.
103
+ *
104
+ * @param level - Priority level ('high', 'normal', or 'low').
105
+ */
50
106
  emailPriority(level: 'high' | 'normal' | 'low'): this {
51
107
  this.envelope.priority = level
52
108
  return this
53
109
  }
54
110
 
111
+ /**
112
+ * Attach a file to the email.
113
+ *
114
+ * @param attachment - Attachment configuration object.
115
+ */
55
116
  attach(attachment: Attachment): this {
56
117
  this.envelope.attachments = this.envelope.attachments || []
57
118
  this.envelope.attachments.push(attachment)
@@ -62,6 +123,8 @@ export abstract class Mailable implements Queueable {
62
123
 
63
124
  /**
64
125
  * Set the content using raw HTML string.
126
+ *
127
+ * @param content - The HTML content string.
65
128
  */
66
129
  html(content: string): this {
67
130
  this.renderer = new HtmlRenderer(content)
@@ -70,8 +133,9 @@ export abstract class Mailable implements Queueable {
70
133
 
71
134
  /**
72
135
  * Set the content using an OrbitPrism template.
73
- * @param template Template name (relative to viewsDir/emails)
74
- * @param data Data to pass to the template
136
+ *
137
+ * @param template - Template name (relative to viewsDir/emails).
138
+ * @param data - Optional data to pass to the template.
75
139
  */
76
140
  view(template: string, data?: Record<string, unknown>): this {
77
141
  this.renderer = new TemplateRenderer(template, undefined) // Dir will be injected later if possible, or use default
@@ -82,6 +146,10 @@ export abstract class Mailable implements Queueable {
82
146
  /**
83
147
  * Set the content using a React component.
84
148
  * Dynamically imports ReactRenderer to avoid hard dependency errors if React is not installed.
149
+ *
150
+ * @param component - The React component to render.
151
+ * @param props - Optional props for the component.
152
+ * @param deps - Optional dependencies (React, ReactDOMServer) if not globally available.
85
153
  */
86
154
  react<P extends object>(
87
155
  component: ComponentType,
@@ -101,6 +169,10 @@ export abstract class Mailable implements Queueable {
101
169
  /**
102
170
  * Set the content using a Vue component.
103
171
  * Dynamically imports VueRenderer to avoid hard dependency errors if Vue is not installed.
172
+ *
173
+ * @param component - The Vue component to render.
174
+ * @param props - Optional props for the component.
175
+ * @param deps - Optional dependencies (Vue, VueServerRenderer) if not globally available.
104
176
  */
105
177
  vue<P extends object>(
106
178
  component: ComponentType,
@@ -122,31 +194,48 @@ export abstract class Mailable implements Queueable {
122
194
 
123
195
  /**
124
196
  * Setup the mailable. This is where you call from(), to(), view(), etc.
197
+ * This method must be implemented by concrete classes.
125
198
  */
126
199
  abstract build(): this
127
200
 
128
201
  // ===== Queueable Implementation =====
129
202
 
203
+ /** The name of the queue to push this mailable to. */
130
204
  queueName?: string
205
+ /** The connection name for the queue. */
131
206
  connectionName?: string
207
+ /** Delay in seconds before the message is sent. */
132
208
  delaySeconds?: number
209
+ /** Priority of the message in the queue. */
133
210
  priority?: number | string
134
211
 
212
+ /**
213
+ * Set the queue name for this mailable.
214
+ */
135
215
  onQueue(queue: string): this {
136
216
  this.queueName = queue
137
217
  return this
138
218
  }
139
219
 
220
+ /**
221
+ * Set the connection name for this mailable.
222
+ */
140
223
  onConnection(connection: string): this {
141
224
  this.connectionName = connection
142
225
  return this
143
226
  }
144
227
 
228
+ /**
229
+ * Set a delay for the queued mailable.
230
+ */
145
231
  delay(seconds: number): this {
146
232
  this.delaySeconds = seconds
147
233
  return this
148
234
  }
149
235
 
236
+ /**
237
+ * Set the priority for the queued mailable.
238
+ */
150
239
  withPriority(priority: string | number): this {
151
240
  this.priority = priority
152
241
  return this
@@ -154,6 +243,7 @@ export abstract class Mailable implements Queueable {
154
243
 
155
244
  /**
156
245
  * Queue the mailable for sending.
246
+ * Attempts to resolve the mail service from the PlanetCore container.
157
247
  */
158
248
  async queue(): Promise<void> {
159
249
  // We should ideally use the container to get the mail service
@@ -179,6 +269,8 @@ export abstract class Mailable implements Queueable {
179
269
 
180
270
  /**
181
271
  * Set the locale for the message.
272
+ *
273
+ * @param locale - Valid locale string (e.g., 'en', 'zh-TW').
182
274
  */
183
275
  locale(locale: string): this {
184
276
  this.currentLocale = locale
@@ -187,6 +279,7 @@ export abstract class Mailable implements Queueable {
187
279
 
188
280
  /**
189
281
  * Internal: Set the translator function (called by OrbitSignal)
282
+ * @internal
190
283
  */
191
284
  setTranslator(
192
285
  translator: (key: string, replace?: Record<string, unknown>, locale?: string) => string
@@ -196,6 +289,10 @@ export abstract class Mailable implements Queueable {
196
289
 
197
290
  /**
198
291
  * Translate a string using the configured translator.
292
+ *
293
+ * @param key - Translation key.
294
+ * @param replace - Interpolation variables.
295
+ * @returns Translated string or the key if translator is missing.
199
296
  */
200
297
  t(key: string, replace?: Record<string, unknown>): string {
201
298
  if (this.translator) {
@@ -208,6 +305,7 @@ export abstract class Mailable implements Queueable {
208
305
 
209
306
  /**
210
307
  * Compile the envelope based on config defaults and mailable settings.
308
+ * Called by OrbitSignal before rendering/sending.
211
309
  */
212
310
  async buildEnvelope(configPromise: MailConfig | Promise<MailConfig>): Promise<Envelope> {
213
311
  const config = await Promise.resolve(configPromise)
@@ -250,7 +348,8 @@ export abstract class Mailable implements Queueable {
250
348
  }
251
349
 
252
350
  /**
253
- * execute the renderer
351
+ * Execute the renderer and produce HTML/Text content.
352
+ * @returns Object containing rendered html and optional text content.
254
353
  */
255
354
  async renderContent(): Promise<{ html: string; text?: string }> {
256
355
  // Resolve lazy renderer if needed
@@ -1,4 +1,4 @@
1
- import type { GravitoOrbit, PlanetCore } from '@gravito/core'
1
+ import type { GravitoContext, GravitoNext, GravitoOrbit, PlanetCore } from '@gravito/core'
2
2
  import { DevMailbox } from './dev/DevMailbox'
3
3
  import { DevServer } from './dev/DevServer'
4
4
  import type { Mailable } from './Mailable'
@@ -6,6 +6,37 @@ import { LogTransport } from './transports/LogTransport'
6
6
  import { MemoryTransport } from './transports/MemoryTransport'
7
7
  import type { MailConfig, Message } from './types'
8
8
 
9
+ /**
10
+ * OrbitSignal - Mail service orbit for Gravito framework.
11
+ *
12
+ * Provides email sending capabilities with support for multiple transports,
13
+ * development mode with email preview UI, and queue integration.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * import { OrbitSignal } from '@gravito/signal'
18
+ * import { SmtpTransport } from '@gravito/signal'
19
+ *
20
+ * const app = new Application({
21
+ * orbits: [
22
+ * new OrbitSignal({
23
+ * transport: new SmtpTransport({
24
+ * host: 'smtp.example.com',
25
+ * port: 587,
26
+ * auth: { user: 'user', pass: 'pass' }
27
+ * }),
28
+ * from: { name: 'App', email: 'noreply@example.com' }
29
+ * })
30
+ * ]
31
+ * })
32
+ *
33
+ * // In route handler
34
+ * await c.get('mail').send(new WelcomeEmail(user))
35
+ * ```
36
+ *
37
+ * @since 3.0.0
38
+ * @public
39
+ */
9
40
  export class OrbitSignal implements GravitoOrbit {
10
41
  private config: MailConfig
11
42
  private devMailbox?: DevMailbox
@@ -43,10 +74,10 @@ export class OrbitSignal implements GravitoOrbit {
43
74
  }
44
75
 
45
76
  // 3. Register in container
46
- core.container.singleton('mail', () => this)
77
+ core.container.instance('mail', this)
47
78
 
48
79
  // 4. Inject mail service into context for easy access in routes
49
- core.adapter.use('*', async (c: any, next) => {
80
+ core.adapter.use('*', async (c: GravitoContext, next: GravitoNext) => {
50
81
  c.set('mail', this)
51
82
  return await next()
52
83
  })
@@ -54,10 +85,6 @@ export class OrbitSignal implements GravitoOrbit {
54
85
 
55
86
  /**
56
87
  * Send a mailable instance
57
- *
58
- * @param mailable - The mailable object to send.
59
- * @returns A promise that resolves when the email is sent.
60
- * @throws {Error} If the message is missing "from" or "to" addresses, or if no transport is configured.
61
88
  */
62
89
  async send(mailable: Mailable): Promise<void> {
63
90
  // 1. Build envelope and get configuration
@@ -114,3 +141,11 @@ export class OrbitSignal implements GravitoOrbit {
114
141
  await this.send(mailable)
115
142
  }
116
143
  }
144
+
145
+ // Module augmentation for GravitoVariables
146
+ declare module '@gravito/core' {
147
+ interface GravitoVariables {
148
+ /** Mail service for sending emails */
149
+ mail?: OrbitSignal
150
+ }
151
+ }
@@ -1,14 +1,40 @@
1
1
  import { randomUUID } from 'node:crypto'
2
2
  import type { Envelope, Message } from '../types'
3
3
 
4
+ /**
5
+ * Represents a captured email in the development mailbox.
6
+ *
7
+ * @public
8
+ */
4
9
  export interface MailboxEntry {
10
+ /** Unique identifier for the email */
5
11
  id: string
12
+ /** Email envelope information (from, to, subject, etc.) */
6
13
  envelope: Envelope
14
+ /** HTML content of the email */
7
15
  html: string
16
+ /** Plain text content of the email (optional) */
8
17
  text?: string
18
+ /** Timestamp when the email was captured */
9
19
  sentAt: Date
10
20
  }
11
21
 
22
+ /**
23
+ * In-memory mailbox for capturing emails during development.
24
+ *
25
+ * Stores up to 50 recent emails and provides methods to list,
26
+ * retrieve, and delete captured messages.
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * const mailbox = new DevMailbox()
31
+ * mailbox.add(message)
32
+ * const emails = mailbox.list()
33
+ * ```
34
+ *
35
+ * @since 3.0.0
36
+ * @public
37
+ */
12
38
  export class DevMailbox {
13
39
  private entries: MailboxEntry[] = []
14
40
  private maxEntries = 50
@@ -3,11 +3,36 @@ import type { DevMailbox } from './DevMailbox'
3
3
  import { getMailboxHtml } from './ui/mailbox'
4
4
  import { getPreviewHtml } from './ui/preview'
5
5
 
6
+ /**
7
+ * Configuration options for the development mail server.
8
+ *
9
+ * @public
10
+ */
6
11
  export type DevServerOptions = {
12
+ /** Allow access in production environment (use with caution) */
7
13
  allowInProduction?: boolean
14
+ /** Custom gate function to control access to the dev UI */
8
15
  gate?: (c: GravitoContext) => boolean | Promise<boolean>
9
16
  }
10
17
 
18
+ /**
19
+ * Development mail server for previewing captured emails.
20
+ *
21
+ * Provides a web UI at the configured base path (default: `/__mail`)
22
+ * to view, preview, and manage emails captured during development.
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * const mailbox = new DevMailbox()
27
+ * const devServer = new DevServer(mailbox, '/__mail', {
28
+ * gate: (c) => c.get('user')?.isAdmin
29
+ * })
30
+ * devServer.register(core)
31
+ * ```
32
+ *
33
+ * @since 3.0.0
34
+ * @public
35
+ */
11
36
  export class DevServer {
12
37
  constructor(
13
38
  private mailbox: DevMailbox,
@@ -21,6 +21,15 @@ function timeAgo(date: Date): string {
21
21
  return date.toLocaleDateString()
22
22
  }
23
23
 
24
+ /**
25
+ * Generates the HTML for the mailbox list view.
26
+ *
27
+ * @param entries - Array of mailbox entries to display
28
+ * @param prefix - Base URL prefix for the dev server
29
+ * @returns HTML string for the mailbox UI
30
+ *
31
+ * @internal
32
+ */
24
33
  export function getMailboxHtml(entries: MailboxEntry[], prefix: string): string {
25
34
  const list =
26
35
  entries.length === 0
@@ -1,6 +1,15 @@
1
1
  import type { MailboxEntry } from '../DevMailbox'
2
2
  import { layout } from './shared'
3
3
 
4
+ /**
5
+ * Generates the HTML for the email preview page.
6
+ *
7
+ * @param entry - Mailbox entry to preview
8
+ * @param prefix - Base URL prefix for the dev server
9
+ * @returns HTML string for the preview UI
10
+ *
11
+ * @internal
12
+ */
4
13
  export function getPreviewHtml(entry: MailboxEntry, prefix: string): string {
5
14
  const from = entry.envelope.from
6
15
  ? `${entry.envelope.from.name || ''} &lt;${entry.envelope.from.address}&gt;`
@@ -1,3 +1,7 @@
1
+ /**
2
+ * CSS styles for the development mail UI.
3
+ * @internal
4
+ */
1
5
  export const styles = `
2
6
  :root {
3
7
  --primary: #6366f1;
@@ -30,6 +34,15 @@ body { background: var(--bg-dark); color: var(--text); font-family: -apple-syste
30
34
  .empty { padding: 40px; text-align: center; color: var(--text-muted); }
31
35
  `
32
36
 
37
+ /**
38
+ * HTML layout wrapper for dev mail UI pages.
39
+ *
40
+ * @param title - Page title
41
+ * @param content - HTML content to inject
42
+ * @returns Complete HTML document
43
+ *
44
+ * @internal
45
+ */
33
46
  export const layout = (title: string, content: string) => `
34
47
  <!DOCTYPE html>
35
48
  <html>
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- import './augmentation'
1
+ // import './augmentation'
2
2
 
3
3
  export type { Queueable } from '@gravito/stream'
4
4
  export { DevMailbox, type MailboxEntry } from './dev/DevMailbox'
@@ -1,5 +1,22 @@
1
1
  import type { Renderer, RenderResult } from './Renderer'
2
2
 
3
+ /**
4
+ * Renderer for plain HTML content.
5
+ *
6
+ * Renders static HTML strings and automatically generates
7
+ * a plain text version by stripping HTML tags.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const renderer = new HtmlRenderer('<h1>Hello</h1><p>World</p>')
12
+ * const { html, text } = await renderer.render()
13
+ * // html: '<h1>Hello</h1><p>World</p>'
14
+ * // text: 'Hello World'
15
+ * ```
16
+ *
17
+ * @since 3.0.0
18
+ * @public
19
+ */
3
20
  export class HtmlRenderer implements Renderer {
4
21
  constructor(private content: string) {}
5
22
 
@@ -12,8 +29,8 @@ export class HtmlRenderer implements Renderer {
12
29
 
13
30
  private stripHtml(html: string): string {
14
31
  return html
15
- .replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '') // Remove styles
16
- .replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '') // Remove scripts
32
+ .replace(/<style(?:\s[^>]*)?>[\s\S]*?<\/style>/gi, '') // Remove styles
33
+ .replace(/<script(?:\s[^>]*)?>[\s\S]*?<\/script>/gi, '') // Remove scripts
17
34
  .replace(/<[^>]+>/g, '') // Remove tags
18
35
  .replace(/&nbsp;/g, ' ') // Replace non-breaking space
19
36
  .replace(/\s+/g, ' ') // Collapse whitespace
@@ -1,5 +1,25 @@
1
1
  import type { Renderer, RenderResult } from './Renderer'
2
2
 
3
+ /**
4
+ * Renderer for React component-based emails.
5
+ *
6
+ * Renders React components to static HTML for email delivery.
7
+ * Supports server-side rendering with optional dependency injection
8
+ * for testing.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { WelcomeEmail } from './emails/WelcomeEmail'
13
+ *
14
+ * const renderer = new ReactRenderer(WelcomeEmail, { name: 'John' })
15
+ * const { html, text } = await renderer.render({ date: new Date() })
16
+ * ```
17
+ *
18
+ * @typeParam P - Props type for the React component
19
+ *
20
+ * @since 3.0.0
21
+ * @public
22
+ */
3
23
  export class ReactRenderer<P extends object = object> implements Renderer {
4
24
  constructor(
5
25
  private component: any, // Use any to avoid hard React dependency in types
@@ -31,8 +51,8 @@ export class ReactRenderer<P extends object = object> implements Renderer {
31
51
 
32
52
  private stripHtml(html: string): string {
33
53
  return html
34
- .replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
35
- .replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
54
+ .replace(/<style(?:\s[^>]*)?>[\s\S]*?<\/style>/gi, '')
55
+ .replace(/<script(?:\s[^>]*)?>[\s\S]*?<\/script>/gi, '')
36
56
  .replace(/<[^>]+>/g, '')
37
57
  .replace(/&nbsp;/g, ' ')
38
58
  .replace(/\s+/g, ' ')
@@ -1,11 +1,27 @@
1
+ /**
2
+ * Result of a content rendering operation.
3
+ *
4
+ * @public
5
+ * @since 3.0.0
6
+ */
1
7
  export interface RenderResult {
8
+ /** Rendered HTML string. */
2
9
  html: string
10
+ /** Optional rendered plain text string. */
3
11
  text?: string
4
12
  }
5
13
 
14
+ /**
15
+ * Interface for email content renderers (HTML, Template, React, Vue).
16
+ *
17
+ * @public
18
+ * @since 3.0.0
19
+ */
6
20
  export interface Renderer {
7
21
  /**
8
- * Render the content into HTML and optionally plain text
22
+ * Render the content into HTML and optionally plain text.
23
+ *
24
+ * @param data - The data context for rendering.
9
25
  */
10
26
  render(data: Record<string, unknown>): Promise<RenderResult>
11
27
  }
@@ -1,5 +1,20 @@
1
1
  import type { Renderer, RenderResult } from './Renderer'
2
2
 
3
+ /**
4
+ * Renderer for template-based emails using Gravito Prism.
5
+ *
6
+ * Renders email templates from the filesystem using the Prism
7
+ * template engine with support for data binding and layouts.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const renderer = new TemplateRenderer('welcome', './src/emails')
12
+ * const { html, text } = await renderer.render({ name: 'John' })
13
+ * ```
14
+ *
15
+ * @since 3.0.0
16
+ * @public
17
+ */
3
18
  export class TemplateRenderer implements Renderer {
4
19
  private template: string
5
20
  private viewsDir: string
@@ -1,5 +1,25 @@
1
1
  import type { Renderer, RenderResult } from './Renderer'
2
2
 
3
+ /**
4
+ * Renderer for Vue component-based emails.
5
+ *
6
+ * Renders Vue 3 components to static HTML for email delivery
7
+ * using server-side rendering. Supports optional dependency
8
+ * injection for testing.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { WelcomeEmail } from './emails/WelcomeEmail.vue'
13
+ *
14
+ * const renderer = new VueRenderer(WelcomeEmail, { name: 'John' })
15
+ * const { html, text } = await renderer.render({ date: new Date() })
16
+ * ```
17
+ *
18
+ * @typeParam P - Props type for the Vue component
19
+ *
20
+ * @since 3.0.0
21
+ * @public
22
+ */
3
23
  export class VueRenderer<P extends object = object> implements Renderer {
4
24
  constructor(
5
25
  private component: any, // Use any to avoid hard Vue dependency in types
@@ -36,8 +56,8 @@ export class VueRenderer<P extends object = object> implements Renderer {
36
56
 
37
57
  private stripHtml(html: string): string {
38
58
  return html
39
- .replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
40
- .replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
59
+ .replace(/<style(?:\s[^>]*)?>[\s\S]*?<\/style>/gi, '')
60
+ .replace(/<script(?:\s[^>]*)?>[\s\S]*?<\/script>/gi, '')
41
61
  .replace(/<[^>]+>/g, '')
42
62
  .replace(/&nbsp;/g, ' ')
43
63
  .replace(/\s+/g, ' ')
@@ -1,5 +1,21 @@
1
1
  import type { Message, Transport } from '../types'
2
2
 
3
+ /**
4
+ * Log transport for development and testing.
5
+ *
6
+ * Logs email details to the console instead of sending them.
7
+ * Useful for debugging and local development.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const transport = new LogTransport()
12
+ * await transport.send(message)
13
+ * // Outputs email details to console
14
+ * ```
15
+ *
16
+ * @since 3.0.0
17
+ * @public
18
+ */
3
19
  export class LogTransport implements Transport {
4
20
  async send(message: Message): Promise<void> {
5
21
  console.log('\nšŸ“§ [OrbitSignal] Email Sent (Simulated):')
@@ -1,6 +1,22 @@
1
1
  import type { DevMailbox } from '../dev/DevMailbox'
2
2
  import type { Message, Transport } from '../types'
3
3
 
4
+ /**
5
+ * Memory transport for development mode.
6
+ *
7
+ * Captures emails to an in-memory mailbox instead of sending them.
8
+ * Used automatically when `devMode` is enabled in OrbitSignal.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const mailbox = new DevMailbox()
13
+ * const transport = new MemoryTransport(mailbox)
14
+ * await transport.send(message)
15
+ * ```
16
+ *
17
+ * @since 3.0.0
18
+ * @public
19
+ */
4
20
  export class MemoryTransport implements Transport {
5
21
  constructor(private mailbox: DevMailbox) {}
6
22