@gravito/signal 3.1.0 → 3.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/index.cjs +55126 -81925
  2. package/dist/index.d.ts +513 -1
  3. package/dist/index.js +61492 -0
  4. package/dist/index.mjs +55301 -82110
  5. package/package.json +12 -1
  6. package/CHANGELOG.md +0 -74
  7. package/build.ts +0 -133
  8. package/dist/index.cjs.map +0 -712
  9. package/dist/index.mjs.map +0 -710
  10. package/doc/ADVANCED_RENDERING.md +0 -71
  11. package/doc/DISTRIBUTED_MESSAGING.md +0 -79
  12. package/doc/OPTIMIZATION_PLAN.md +0 -496
  13. package/package.json.bak +0 -75
  14. package/scripts/check-coverage.ts +0 -64
  15. package/src/Mailable.ts +0 -674
  16. package/src/OrbitSignal.ts +0 -451
  17. package/src/Queueable.ts +0 -9
  18. package/src/TypedMailable.ts +0 -96
  19. package/src/dev/DevMailbox.ts +0 -146
  20. package/src/dev/DevServer.ts +0 -192
  21. package/src/dev/storage/FileMailboxStorage.ts +0 -66
  22. package/src/dev/storage/MailboxStorage.ts +0 -15
  23. package/src/dev/storage/MemoryMailboxStorage.ts +0 -36
  24. package/src/dev/ui/mailbox.ts +0 -77
  25. package/src/dev/ui/preview.ts +0 -103
  26. package/src/dev/ui/shared.ts +0 -60
  27. package/src/errors.ts +0 -69
  28. package/src/events.ts +0 -72
  29. package/src/index.ts +0 -41
  30. package/src/renderers/HtmlRenderer.ts +0 -41
  31. package/src/renderers/MjmlRenderer.ts +0 -73
  32. package/src/renderers/ReactMjmlRenderer.ts +0 -94
  33. package/src/renderers/ReactRenderer.ts +0 -66
  34. package/src/renderers/Renderer.ts +0 -67
  35. package/src/renderers/TemplateRenderer.ts +0 -84
  36. package/src/renderers/VueMjmlRenderer.ts +0 -99
  37. package/src/renderers/VueRenderer.ts +0 -71
  38. package/src/renderers/mjml-templates.ts +0 -50
  39. package/src/transports/BaseTransport.ts +0 -148
  40. package/src/transports/LogTransport.ts +0 -55
  41. package/src/transports/MemoryTransport.ts +0 -55
  42. package/src/transports/SesTransport.ts +0 -129
  43. package/src/transports/SmtpTransport.ts +0 -184
  44. package/src/transports/Transport.ts +0 -45
  45. package/src/types.ts +0 -309
  46. package/src/utils/html.ts +0 -43
  47. package/src/webhooks/SendGridWebhookDriver.ts +0 -80
  48. package/src/webhooks/SesWebhookDriver.ts +0 -44
  49. package/tests/DevMailbox.test.ts +0 -54
  50. package/tests/FileMailboxStorage.test.ts +0 -56
  51. package/tests/MjmlLayout.test.ts +0 -28
  52. package/tests/MjmlRenderer.test.ts +0 -53
  53. package/tests/OrbitSignalWebhook.test.ts +0 -56
  54. package/tests/ReactMjmlRenderer.test.ts +0 -33
  55. package/tests/SendGridWebhookDriver.test.ts +0 -69
  56. package/tests/SesWebhookDriver.test.ts +0 -46
  57. package/tests/VueMjmlRenderer.test.ts +0 -35
  58. package/tests/dev-server.test.ts +0 -66
  59. package/tests/log-transport.test.ts +0 -21
  60. package/tests/mailable-extra.test.ts +0 -68
  61. package/tests/mailable.test.ts +0 -77
  62. package/tests/orbit-signal.test.ts +0 -43
  63. package/tests/renderers.test.ts +0 -58
  64. package/tests/template-renderer.test.ts +0 -24
  65. package/tests/transports.test.ts +0 -52
  66. package/tests/ui.test.ts +0 -37
  67. package/tsconfig.build.json +0 -24
  68. package/tsconfig.json +0 -9
@@ -1,192 +0,0 @@
1
- import type { GravitoContext, PlanetCore } from '@gravito/core'
2
- import type { DevMailbox } from './DevMailbox'
3
- import { getMailboxHtml } from './ui/mailbox'
4
- import { getPreviewHtml } from './ui/preview'
5
-
6
- /**
7
- * Configuration options for the development mail server.
8
- *
9
- * @public
10
- */
11
- export type DevServerOptions = {
12
- /** Allow access in production environment (use with caution) */
13
- allowInProduction?: boolean
14
- /** Custom gate function to control access to the dev UI */
15
- gate?: (c: GravitoContext) => boolean | Promise<boolean>
16
- }
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
- */
36
- export class DevServer {
37
- constructor(
38
- private mailbox: DevMailbox,
39
- private base = '/__mail',
40
- private options?: DevServerOptions
41
- ) {}
42
-
43
- private async canAccess(ctx: GravitoContext): Promise<boolean> {
44
- const isProduction = process.env.NODE_ENV === 'production'
45
- if (isProduction && !this.options?.allowInProduction && !this.options?.gate) {
46
- return false
47
- }
48
- if (this.options?.gate) {
49
- return await this.options.gate(ctx)
50
- }
51
- return true
52
- }
53
-
54
- register(core: PlanetCore): void {
55
- const router = core.router
56
- const isProduction = process.env.NODE_ENV === 'production'
57
-
58
- if (isProduction && !this.options?.allowInProduction && !this.options?.gate) {
59
- core.logger.warn(
60
- '[OrbitSignal] Dev Mailbox disabled in production. Configure a gate to allow access.'
61
- )
62
- return
63
- }
64
-
65
- // Remove trailing slash
66
- const prefix = this.base.replace(/\/$/, '')
67
-
68
- const wrap = (handler: (ctx: GravitoContext) => Response | Promise<Response>) => {
69
- return async (ctx: GravitoContext) => {
70
- const allowed = await this.canAccess(ctx)
71
- if (!allowed) {
72
- return ctx.text('Unauthorized', 403)
73
- }
74
- return await handler(ctx)
75
- }
76
- }
77
-
78
- // 1. Mailbox List
79
- router.get(
80
- prefix,
81
- wrap(async (ctx) => {
82
- const entries = await this.mailbox.list()
83
- ctx.header('Content-Type', 'text/html; charset=utf-8')
84
- return ctx.html(getMailboxHtml(entries, prefix))
85
- })
86
- )
87
-
88
- // 2. Single Email Preview
89
- router.get(
90
- `${prefix}/:id`,
91
- wrap(async (ctx) => {
92
- const id = ctx.req.param('id')
93
- if (!id) {
94
- return ctx.text('Bad Request', 400)
95
- }
96
-
97
- const entry = await this.mailbox.get(id)
98
- if (!entry) {
99
- return ctx.text('Email not found', 404)
100
- }
101
- ctx.header('Content-Type', 'text/html; charset=utf-8')
102
- return ctx.html(getPreviewHtml(entry, prefix))
103
- })
104
- )
105
-
106
- // 3. Iframe Content: HTML
107
- router.get(
108
- `${prefix}/:id/html`,
109
- wrap(async (ctx) => {
110
- const id = ctx.req.param('id')
111
- if (!id) {
112
- return ctx.text('Bad Request', 400)
113
- }
114
-
115
- const entry = await this.mailbox.get(id)
116
- if (!entry) {
117
- return ctx.text('Not found', 404)
118
- }
119
- ctx.header('Content-Type', 'text/html; charset=utf-8')
120
- return ctx.html(entry.html)
121
- })
122
- )
123
-
124
- // 4. Iframe Content: Text
125
- router.get(
126
- `${prefix}/:id/text`,
127
- wrap(async (ctx) => {
128
- const id = ctx.req.param('id')
129
- if (!id) {
130
- return ctx.text('Bad Request', 400)
131
- }
132
-
133
- const entry = await this.mailbox.get(id)
134
- if (!entry) {
135
- return ctx.text('Not found', 404)
136
- }
137
-
138
- ctx.header('Content-Type', 'text/plain; charset=utf-8')
139
- return ctx.text(entry.text || 'No text content', 200)
140
- })
141
- )
142
-
143
- // 5. Raw JSON
144
- router.get(
145
- `${prefix}/:id/raw`,
146
- wrap(async (ctx) => {
147
- const id = ctx.req.param('id')
148
- if (!id) {
149
- return ctx.json({ error: 'Bad Request' }, 400)
150
- }
151
-
152
- const entry = await this.mailbox.get(id)
153
- if (!entry) {
154
- return ctx.json({ error: 'Not found' }, 404)
155
- }
156
- return ctx.json(entry)
157
- })
158
- )
159
-
160
- // 6. API: Delete Single
161
- router.get(
162
- `${prefix}/:id/delete`,
163
- wrap((ctx) => {
164
- return ctx.text('Method not allowed', 405)
165
- })
166
- )
167
-
168
- router.delete(
169
- `${prefix}/:id`,
170
- wrap(async (ctx) => {
171
- const id = ctx.req.param('id')
172
- if (!id) {
173
- return ctx.json({ success: false, error: 'Bad Request' }, 400)
174
- }
175
-
176
- const success = await this.mailbox.delete(id)
177
- return ctx.json({ success })
178
- })
179
- )
180
-
181
- // 7. API: Clear All
182
- router.delete(
183
- prefix,
184
- wrap(async (ctx) => {
185
- await this.mailbox.clear()
186
- return ctx.json({ success: true })
187
- })
188
- )
189
-
190
- core.logger.info(`[OrbitSignal] Dev Mailbox available at ${prefix}`)
191
- }
192
- }
@@ -1,66 +0,0 @@
1
- import { mkdir, readFile, unlink, writeFile } from 'node:fs/promises'
2
- import { join } from 'node:path'
3
- import type { MailboxEntry } from '../DevMailbox'
4
- import type { MailboxStorage } from './MailboxStorage'
5
-
6
- /**
7
- * File-based storage for DevMailbox (Persistence).
8
- * Stores emails as JSON in a specified directory.
9
- */
10
- export class FileMailboxStorage implements MailboxStorage {
11
- private filePath: string
12
-
13
- constructor(storageDir: string) {
14
- this.filePath = join(storageDir, 'mailbox.json')
15
- }
16
-
17
- async all(): Promise<MailboxEntry[]> {
18
- try {
19
- const content = await readFile(this.filePath, 'utf-8')
20
- return JSON.parse(content)
21
- } catch (_e) {
22
- return []
23
- }
24
- }
25
-
26
- async push(entry: MailboxEntry): Promise<void> {
27
- const entries = await this.all()
28
- entries.unshift(entry)
29
- await this.save(entries)
30
- }
31
-
32
- async trim(max: number): Promise<void> {
33
- const entries = await this.all()
34
- if (entries.length > max) {
35
- await this.save(entries.slice(0, max))
36
- }
37
- }
38
-
39
- async clear(): Promise<void> {
40
- try {
41
- await unlink(this.filePath)
42
- } catch (_e) {
43
- // Ignore if file doesn't exist
44
- }
45
- }
46
-
47
- async delete(id: string): Promise<boolean> {
48
- const entries = await this.all()
49
- const index = entries.findIndex((e) => e.id === id)
50
- if (index !== -1) {
51
- entries.splice(index, 1)
52
- await this.save(entries)
53
- return true
54
- }
55
- return false
56
- }
57
-
58
- private async save(entries: MailboxEntry[]): Promise<void> {
59
- try {
60
- await mkdir(join(this.filePath, '..'), { recursive: true })
61
- await writeFile(this.filePath, JSON.stringify(entries, null, 2), 'utf-8')
62
- } catch (error) {
63
- console.error('[FileMailboxStorage] Failed to save mailbox:', error)
64
- }
65
- }
66
- }
@@ -1,15 +0,0 @@
1
- import type { MailboxEntry } from '../DevMailbox'
2
-
3
- /**
4
- * Interface for DevMailbox storage engines.
5
- */
6
- export interface MailboxStorage {
7
- /** Retrieve all entries. */
8
- all(): Promise<MailboxEntry[]>
9
- /** Add a single entry. */
10
- push(entry: MailboxEntry): Promise<void>
11
- /** Trim entries to a specific count. */
12
- trim(max: number): Promise<void>
13
- clear(): Promise<void>
14
- delete?(id: string): Promise<boolean>
15
- }
@@ -1,36 +0,0 @@
1
- import type { MailboxEntry } from '../DevMailbox'
2
- import type { MailboxStorage } from './MailboxStorage'
3
-
4
- /**
5
- * Memory-based storage for DevMailbox (Default).
6
- */
7
- export class MemoryMailboxStorage implements MailboxStorage {
8
- private entries: MailboxEntry[] = []
9
-
10
- async all(): Promise<MailboxEntry[]> {
11
- return [...this.entries]
12
- }
13
-
14
- async push(entry: MailboxEntry): Promise<void> {
15
- this.entries.unshift(entry)
16
- }
17
-
18
- async trim(max: number): Promise<void> {
19
- if (this.entries.length > max) {
20
- this.entries = this.entries.slice(0, max)
21
- }
22
- }
23
-
24
- async clear(): Promise<void> {
25
- this.entries = []
26
- }
27
-
28
- async delete(id: string): Promise<boolean> {
29
- const index = this.entries.findIndex((e) => e.id === id)
30
- if (index !== -1) {
31
- this.entries.splice(index, 1)
32
- return true
33
- }
34
- return false
35
- }
36
- }
@@ -1,77 +0,0 @@
1
- import type { MailboxEntry } from '../DevMailbox'
2
- import { layout } from './shared'
3
-
4
- function formatAddress(addr: { name?: string; address: string }): string {
5
- return addr.name ? `${addr.name} <${addr.address}>` : addr.address
6
- }
7
-
8
- function timeAgo(date: Date): string {
9
- const seconds = Math.floor((Date.now() - date.getTime()) / 1000)
10
- if (seconds < 60) {
11
- return 'Just now'
12
- }
13
- const minutes = Math.floor(seconds / 60)
14
- if (minutes < 60) {
15
- return `${minutes}m ago`
16
- }
17
- const hours = Math.floor(minutes / 60)
18
- if (hours < 24) {
19
- return `${hours}h ago`
20
- }
21
- return date.toLocaleDateString()
22
- }
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
- */
33
- export function getMailboxHtml(entries: MailboxEntry[], prefix: string): string {
34
- const list =
35
- entries.length === 0
36
- ? '<div class="empty">No emails found in mailbox.</div>'
37
- : entries
38
- .map(
39
- (entry) => `
40
- <a href="${prefix}/${entry.id}" class="list-item">
41
- <div class="meta">
42
- <span class="from">${formatAddress(entry.envelope.from || { address: 'Unknown' })}</span>
43
- <span>${timeAgo(entry.sentAt)}</span>
44
- </div>
45
- <div class="subject">
46
- ${entry.envelope.priority === 'high' ? '<span class="badge badge-high">High</span> ' : ''}
47
- ${entry.envelope.subject || '(No Subject)'}
48
- </div>
49
- <div class="meta" style="margin-top: 4px;">
50
- To: ${entry.envelope.to?.map((t: any) => t.address).join(', ')}
51
- </div>
52
- </a>
53
- `
54
- )
55
- .join('')
56
-
57
- const content = `
58
- <div class="header">
59
- <div class="title">📬 Gravito Mailbox</div>
60
- ${entries.length > 0 ? `<button onclick="clearAll()" class="btn btn-danger">Clear All</button>` : ''}
61
- </div>
62
-
63
- <div class="card">
64
- ${list}
65
- </div>
66
-
67
- <script>
68
- async function clearAll() {
69
- if (!confirm('Clear all emails?')) return;
70
- await fetch('${prefix}', { method: 'DELETE' });
71
- window.location.reload();
72
- }
73
- </script>
74
- `
75
-
76
- return layout('Inbox', content)
77
- }
@@ -1,103 +0,0 @@
1
- import type { MailboxEntry } from '../DevMailbox'
2
- import { layout } from './shared'
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
- */
13
- export function getPreviewHtml(entry: MailboxEntry, prefix: string): string {
14
- const from = entry.envelope.from
15
- ? `${entry.envelope.from.name || ''} &lt;${entry.envelope.from.address}&gt;`
16
- : 'Unknown'
17
- const to = entry.envelope.to?.map((t: any) => t.address).join(', ') || 'Unknown'
18
-
19
- const content = `
20
- <div class="header">
21
- <div class="title">
22
- <a href="${prefix}" class="btn">← Back</a>
23
- <span style="margin-left: 10px">Email Preview</span>
24
- </div>
25
- <button onclick="deleteEmail('${entry.id}')" class="btn btn-danger">Delete</button>
26
- </div>
27
-
28
- <div class="card" style="margin-bottom: 20px; padding: 20px;">
29
- <div style="font-size: 18px; font-weight: bold; margin-bottom: 10px;">${entry.envelope.subject || '(No Subject)'}</div>
30
- <div class="meta" style="margin-bottom: 5px;">From: ${from}</div>
31
- <div class="meta" style="margin-bottom: 5px;">To: ${to}</div>
32
- ${
33
- entry.envelope.cc
34
- ? `<div class="meta" style="margin-bottom: 5px;">CC: ${entry.envelope.cc.map((t: any) => t.address).join(', ')}</div>`
35
- : ''
36
- }
37
- ${
38
- entry.envelope.bcc
39
- ? `<div class="meta" style="margin-bottom: 5px;">BCC: ${entry.envelope.bcc.map((t: any) => t.address).join(', ')}</div>`
40
- : ''
41
- }
42
- ${entry.envelope.priority ? `<div class="meta" style="margin-bottom: 5px;">Priority: ${entry.envelope.priority}</div>` : ''}
43
- <div class="meta">Date: ${entry.sentAt.toLocaleString()}</div>
44
- ${
45
- entry.envelope.attachments && entry.envelope.attachments.length > 0
46
- ? `
47
- <div style="margin-top: 15px; border-top: 1px solid var(--border); padding-top: 10px;">
48
- <div style="font-size: 14px; font-weight: bold; margin-bottom: 5px; color: var(--text-muted);">Attachments (${entry.envelope.attachments.length})</div>
49
- <div style="display: flex; gap: 10px; flex-wrap: wrap;">
50
- ${entry.envelope.attachments
51
- .map(
52
- (att: any) => `
53
- <div style="background: var(--bg-dark); padding: 8px 12px; border-radius: 6px; border: 1px solid var(--border); display: flex; align-items: center; gap: 8px;">
54
- <span style="font-size: 20px;">📎</span>
55
- <div>
56
- <div style="font-size: 14px; font-weight: 500;">${att.filename || 'untitled'}</div>
57
- <div style="font-size: 12px; color: var(--text-muted);">${att.contentType || 'application/octet-stream'}</div>
58
- </div>
59
- </div>
60
- `
61
- )
62
- .join('')}
63
- </div>
64
- </div>
65
- `
66
- : ''
67
- }
68
- </div>
69
-
70
- <div style="margin-bottom: 10px;">
71
- <button onclick="setView('html')" id="btn-html" class="btn btn-primary">HTML</button>
72
- <button onclick="setView('text')" id="btn-text" class="btn">Text</button>
73
- <button onclick="setView('raw')" id="btn-raw" class="btn">Raw JSON</button>
74
- <a href="${prefix}/${entry.id}/html" target="_blank" class="btn">Open HTML ↗</a>
75
- </div>
76
-
77
- <div class="card" style="height: 600px; display: flex;">
78
- <iframe id="preview-frame" src="${prefix}/${entry.id}/html" style="width: 100%; height: 100%; border: none;"></iframe>
79
- <pre id="raw-view" style="display: none; padding: 20px; overflow: auto; width: 100%;">${JSON.stringify(entry, null, 2)}</pre>
80
- <pre id="text-view" style="display: none; padding: 20px; overflow: auto; width: 100%; white-space: pre-wrap; font-family: monospace;">${entry.text || 'No text content'}</pre>
81
- </div>
82
-
83
- <script>
84
- function setView(mode) {
85
- document.getElementById('preview-frame').style.display = mode === 'html' ? 'block' : 'none';
86
- document.getElementById('raw-view').style.display = mode === 'raw' ? 'block' : 'none';
87
- document.getElementById('text-view').style.display = mode === 'text' ? 'block' : 'none';
88
-
89
- document.getElementById('btn-html').className = mode === 'html' ? 'btn btn-primary' : 'btn';
90
- document.getElementById('btn-text').className = mode === 'text' ? 'btn btn-primary' : 'btn';
91
- document.getElementById('btn-raw').className = mode === 'raw' ? 'btn btn-primary' : 'btn';
92
- }
93
-
94
- async function deleteEmail(id) {
95
- if (!confirm('Delete this email?')) return;
96
- await fetch('${prefix}/' + id, { method: 'DELETE' });
97
- window.location.href = '${prefix}';
98
- }
99
- </script>
100
- `
101
-
102
- return layout(entry.envelope.subject || 'Preview', content)
103
- }
@@ -1,60 +0,0 @@
1
- /**
2
- * CSS styles for the development mail UI.
3
- * @internal
4
- */
5
- export const styles = `
6
- :root {
7
- --primary: #6366f1;
8
- --primary-dark: #4f46e5;
9
- --bg-dark: #0f172a;
10
- --bg-card: #1e293b;
11
- --text: #f1f5f9;
12
- --text-muted: #94a3b8;
13
- --border: #334155;
14
- --danger: #ef4444;
15
- }
16
- body { background: var(--bg-dark); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; margin: 0; padding: 20px; }
17
- .container { max-width: 1000px; margin: 0 auto; }
18
- .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
19
- .title { font-size: 24px; font-weight: bold; display: flex; align-items: center; gap: 10px; }
20
- .card { background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
21
- .btn { background: var(--border); color: var(--text); border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; text-decoration: none; font-size: 14px; transition: 0.2s; }
22
- .btn:hover { background: var(--bg-card-hover); }
23
- .btn-primary { background: var(--primary); color: white; }
24
- .btn-primary:hover { background: var(--primary-dark); }
25
- .btn-danger { background: var(--danger); color: white; }
26
- .list-item { padding: 16px; border-bottom: 1px solid var(--border); display: flex; flex-direction: column; gap: 4px; text-decoration: none; color: inherit; transition: background 0.2s; }
27
- .list-item:last-child { border-bottom: none; }
28
- .list-item:hover { background: #334155; }
29
- .meta { display: flex; justify-content: space-between; font-size: 14px; color: var(--text-muted); }
30
- .subject { font-weight: 600; font-size: 16px; }
31
- .from { color: #cbd5e1; }
32
- .badge { background: #475569; padding: 2px 6px; border-radius: 4px; font-size: 12px; }
33
- .badge-high { background: #dc2626; color: white; }
34
- .empty { padding: 40px; text-align: center; color: var(--text-muted); }
35
- `
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
- */
46
- export const layout = (title: string, content: string) => `
47
- <!DOCTYPE html>
48
- <html>
49
- <head>
50
- <meta charset="utf-8">
51
- <title>${title} - Gravito Mailbox</title>
52
- <style>${styles}</style>
53
- </head>
54
- <body>
55
- <div class="container">
56
- ${content}
57
- </div>
58
- </body>
59
- </html>
60
- `
package/src/errors.ts DELETED
@@ -1,69 +0,0 @@
1
- /**
2
- * Mail transport error codes.
3
- *
4
- * Categorizes common failure modes in the mail delivery process to allow
5
- * for programmatic handling (e.g., retries on rate limits).
6
- *
7
- * @public
8
- * @since 3.1.0
9
- */
10
- export enum MailErrorCode {
11
- /** Connection to mail server failed */
12
- CONNECTION_FAILED = 'CONNECTION_FAILED',
13
- /** Authentication with mail server failed */
14
- AUTH_FAILED = 'AUTH_FAILED',
15
- /** One or more recipients were rejected by the server */
16
- RECIPIENT_REJECTED = 'RECIPIENT_REJECTED',
17
- /** The message was rejected by the server */
18
- MESSAGE_REJECTED = 'MESSAGE_REJECTED',
19
- /** Rate limit exceeded */
20
- RATE_LIMIT = 'RATE_LIMIT',
21
- /** Unknown or unclassified error */
22
- UNKNOWN = 'UNKNOWN',
23
- }
24
-
25
- /**
26
- * Error class for mail transport failures.
27
- *
28
- * Provides structured error information for mail sending failures,
29
- * including error codes and original cause tracking for debugging.
30
- *
31
- * @example
32
- * ```typescript
33
- * throw new MailTransportError(
34
- * 'Failed to connect to SMTP server',
35
- * MailErrorCode.CONNECTION_FAILED,
36
- * originalError
37
- * )
38
- * ```
39
- *
40
- * @public
41
- * @since 3.1.0
42
- */
43
- export class MailTransportError extends Error {
44
- /**
45
- * Create a new mail transport error.
46
- *
47
- * @param message - Human-readable error message
48
- * @param code - Categorized error code
49
- * @param cause - Original error that caused this failure
50
- *
51
- * @example
52
- * ```typescript
53
- * const error = new MailTransportError('Auth failed', MailErrorCode.AUTH_FAILED);
54
- * ```
55
- */
56
- constructor(
57
- message: string,
58
- public readonly code: MailErrorCode,
59
- public readonly cause?: Error
60
- ) {
61
- super(message)
62
- this.name = 'MailTransportError'
63
-
64
- // Maintain proper stack trace in V8 environments
65
- if (Error.captureStackTrace) {
66
- Error.captureStackTrace(this, MailTransportError)
67
- }
68
- }
69
- }
package/src/events.ts DELETED
@@ -1,72 +0,0 @@
1
- import type { Mailable } from './Mailable'
2
- import type { Message } from './types'
3
-
4
- /**
5
- * Mail event types.
6
- *
7
- * Defines the available lifecycle hooks for the mail service.
8
- *
9
- * @public
10
- * @since 3.1.0
11
- */
12
- export type MailEventType =
13
- | 'beforeSend'
14
- | 'afterSend'
15
- | 'sendFailed'
16
- | 'beforeRender'
17
- | 'afterRender'
18
- | 'webhookReceived'
19
-
20
- /**
21
- * Mail lifecycle event.
22
- *
23
- * Emitted at key points during email sending lifecycle.
24
- * Used for logging, analytics, or custom processing.
25
- *
26
- * @example
27
- * ```typescript
28
- * mail.on('afterSend', async (event) => {
29
- * console.log('Email sent to:', event.message?.to)
30
- * })
31
- * ```
32
- *
33
- * @public
34
- * @since 3.1.0
35
- */
36
- export interface MailEvent {
37
- /** Type of event */
38
- type: MailEventType
39
- /** Mailable instance that triggered the event */
40
- mailable: Mailable
41
- /** Finalized message (available for send events) */
42
- message?: Message
43
- /** Error that occurred (available for sendFailed event) */
44
- error?: Error
45
- /** Timestamp when event occurred */
46
- timestamp: Date
47
- /** Webhook payload (available for webhookReceived event) */
48
- webhook?: {
49
- driver: string
50
- event: string
51
- payload: any
52
- }
53
- }
54
-
55
- /**
56
- * Mail event handler function.
57
- *
58
- * Defines the signature for functions that subscribe to mail events.
59
- *
60
- * @param event - The mail event object
61
- *
62
- * @example
63
- * ```typescript
64
- * const handler: MailEventHandler = (event) => {
65
- * console.log(`Event ${event.type} triggered`);
66
- * };
67
- * ```
68
- *
69
- * @public
70
- * @since 3.1.0
71
- */
72
- export type MailEventHandler = (event: MailEvent) => void | Promise<void>