@gravito/signal 3.0.4 → 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 (124) hide show
  1. package/README.md +26 -15
  2. package/dist/Mailable.d.ts +453 -0
  3. package/dist/OrbitSignal.d.ts +267 -0
  4. package/{src/Queueable.ts → dist/Queueable.d.ts} +1 -1
  5. package/{src/TypedMailable.ts → dist/TypedMailable.d.ts} +12 -13
  6. package/dist/dev/DevMailbox.d.ts +79 -0
  7. package/dist/dev/DevServer.d.ts +39 -0
  8. package/dist/dev/storage/FileMailboxStorage.d.ts +16 -0
  9. package/dist/dev/storage/MailboxStorage.d.ts +14 -0
  10. package/dist/dev/storage/MemoryMailboxStorage.d.ts +13 -0
  11. package/dist/dev/ui/mailbox.d.ts +11 -0
  12. package/dist/dev/ui/preview.d.ts +11 -0
  13. package/dist/dev/ui/shared.d.ts +15 -0
  14. package/dist/errors.d.ts +58 -0
  15. package/{src/events.ts → dist/events.d.ts} +20 -29
  16. package/dist/{lib-HJTRWKU5.mjs → index.cjs} +4687 -10983
  17. package/dist/index.d.ts +303 -1874
  18. package/dist/index.js +317 -45939
  19. package/dist/index.mjs +59748 -27
  20. package/dist/renderers/HtmlRenderer.d.ts +34 -0
  21. package/dist/renderers/MjmlRenderer.d.ts +41 -0
  22. package/dist/renderers/ReactMjmlRenderer.d.ts +38 -0
  23. package/dist/renderers/ReactRenderer.d.ts +46 -0
  24. package/{src/renderers/Renderer.ts → dist/renderers/Renderer.d.ts} +23 -24
  25. package/dist/renderers/TemplateRenderer.d.ts +55 -0
  26. package/dist/renderers/VueMjmlRenderer.d.ts +39 -0
  27. package/dist/renderers/VueRenderer.d.ts +47 -0
  28. package/dist/renderers/mjml-templates.d.ts +11 -0
  29. package/dist/transports/BaseTransport.d.ts +111 -0
  30. package/dist/transports/LogTransport.d.ts +42 -0
  31. package/dist/transports/MemoryTransport.d.ts +51 -0
  32. package/dist/transports/SesTransport.d.ts +79 -0
  33. package/dist/transports/SmtpTransport.d.ts +124 -0
  34. package/dist/transports/Transport.d.ts +44 -0
  35. package/dist/types.d.ts +294 -0
  36. package/{src/utils/html.ts → dist/utils/html.d.ts} +1 -15
  37. package/dist/webhooks/SendGridWebhookDriver.d.ts +45 -0
  38. package/dist/webhooks/SesWebhookDriver.d.ts +23 -0
  39. package/package.json +20 -8
  40. package/CHANGELOG.md +0 -74
  41. package/dist/MjmlRenderer-IUH663FT.mjs +0 -8
  42. package/dist/ReactMjmlRenderer-C3P5YO5L.mjs +0 -8
  43. package/dist/ReactRenderer-24SQ4KRU.mjs +0 -27
  44. package/dist/ReactRenderer-2JFLRVST.mjs +0 -45
  45. package/dist/ReactRenderer-CMCAOEPH.mjs +0 -28
  46. package/dist/ReactRenderer-KYNA4WKE.mjs +0 -28
  47. package/dist/ReactRenderer-LYEOSYFS.mjs +0 -28
  48. package/dist/ReactRenderer-V54CUUEI.mjs +0 -45
  49. package/dist/VueMjmlRenderer-4F4CXHDB.mjs +0 -8
  50. package/dist/VueMjmlRenderer-5WZR4CQG.mjs +0 -8
  51. package/dist/VueMjmlRenderer-U5YMWI44.mjs +0 -8
  52. package/dist/VueRenderer-3YBRQXME.mjs +0 -48
  53. package/dist/VueRenderer-46JGXTJ2.mjs +0 -48
  54. package/dist/VueRenderer-5KWD4R3C.mjs +0 -48
  55. package/dist/VueRenderer-C23U4O5E.mjs +0 -48
  56. package/dist/VueRenderer-DWTCD2RF.mjs +0 -31
  57. package/dist/VueRenderer-IIR5SYTM.mjs +0 -31
  58. package/dist/VueRenderer-LEVDFLHP.mjs +0 -31
  59. package/dist/VueRenderer-RNHSCCRI.mjs +0 -48
  60. package/dist/VueRenderer-SUP66ISX.mjs +0 -29
  61. package/dist/chunk-3WOR3XSL.mjs +0 -82
  62. package/dist/chunk-DBFIVHHG.mjs +0 -79
  63. package/dist/chunk-EBO3CZXG.mjs +0 -15
  64. package/dist/chunk-HEBXNMVQ.mjs +0 -48
  65. package/dist/chunk-KB7IDDBT.mjs +0 -82
  66. package/dist/chunk-LZL5UUPC.mjs +0 -82
  67. package/dist/chunk-W6LXIJKK.mjs +0 -57
  68. package/dist/chunk-XBIVBJS2.mjs +0 -8
  69. package/dist/index.d.mts +0 -2115
  70. package/dist/server-renderer-4IM3P5XZ.mjs +0 -37183
  71. package/dist/server-renderer-4W4FI7YG.mjs +0 -37269
  72. package/dist/server-renderer-7KWFSTPV.mjs +0 -37193
  73. package/dist/server-renderer-S5FPSTJ2.mjs +0 -37183
  74. package/dist/server-renderer-X5LUFVWT.mjs +0 -37193
  75. package/doc/OPTIMIZATION_PLAN.md +0 -496
  76. package/scripts/check-coverage.ts +0 -64
  77. package/src/Mailable.ts +0 -674
  78. package/src/OrbitSignal.ts +0 -451
  79. package/src/dev/DevMailbox.ts +0 -146
  80. package/src/dev/DevServer.ts +0 -192
  81. package/src/dev/storage/FileMailboxStorage.ts +0 -66
  82. package/src/dev/storage/MailboxStorage.ts +0 -15
  83. package/src/dev/storage/MemoryMailboxStorage.ts +0 -36
  84. package/src/dev/ui/mailbox.ts +0 -77
  85. package/src/dev/ui/preview.ts +0 -103
  86. package/src/dev/ui/shared.ts +0 -60
  87. package/src/errors.ts +0 -69
  88. package/src/index.ts +0 -41
  89. package/src/renderers/HtmlRenderer.ts +0 -41
  90. package/src/renderers/MjmlRenderer.ts +0 -73
  91. package/src/renderers/ReactMjmlRenderer.ts +0 -94
  92. package/src/renderers/ReactRenderer.ts +0 -66
  93. package/src/renderers/TemplateRenderer.ts +0 -84
  94. package/src/renderers/VueMjmlRenderer.ts +0 -99
  95. package/src/renderers/VueRenderer.ts +0 -71
  96. package/src/renderers/mjml-templates.ts +0 -50
  97. package/src/transports/BaseTransport.ts +0 -148
  98. package/src/transports/LogTransport.ts +0 -55
  99. package/src/transports/MemoryTransport.ts +0 -55
  100. package/src/transports/SesTransport.ts +0 -129
  101. package/src/transports/SmtpTransport.ts +0 -184
  102. package/src/transports/Transport.ts +0 -45
  103. package/src/types.ts +0 -309
  104. package/src/webhooks/SendGridWebhookDriver.ts +0 -80
  105. package/src/webhooks/SesWebhookDriver.ts +0 -44
  106. package/tests/DevMailbox.test.ts +0 -54
  107. package/tests/FileMailboxStorage.test.ts +0 -56
  108. package/tests/MjmlLayout.test.ts +0 -28
  109. package/tests/MjmlRenderer.test.ts +0 -53
  110. package/tests/OrbitSignalWebhook.test.ts +0 -56
  111. package/tests/ReactMjmlRenderer.test.ts +0 -33
  112. package/tests/SendGridWebhookDriver.test.ts +0 -69
  113. package/tests/SesWebhookDriver.test.ts +0 -46
  114. package/tests/VueMjmlRenderer.test.ts +0 -35
  115. package/tests/dev-server.test.ts +0 -66
  116. package/tests/log-transport.test.ts +0 -21
  117. package/tests/mailable-extra.test.ts +0 -68
  118. package/tests/mailable.test.ts +0 -77
  119. package/tests/orbit-signal.test.ts +0 -43
  120. package/tests/renderers.test.ts +0 -58
  121. package/tests/template-renderer.test.ts +0 -24
  122. package/tests/transports.test.ts +0 -52
  123. package/tests/ui.test.ts +0 -37
  124. package/tsconfig.json +0 -14
@@ -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/index.ts DELETED
@@ -1,41 +0,0 @@
1
- /**
2
- * OrbitSignal - The central Event Bus and Mail Service for Gravito.
3
- *
4
- * This module provides the core infrastructure for sending emails and managing
5
- * cross-module signals within the Gravito ecosystem. It supports multiple
6
- * transport drivers, template rendering, and a robust development environment.
7
- *
8
- * @packageDocumentation
9
- */
10
-
11
- // import './augmentation'
12
-
13
- export type { Queueable } from '@gravito/stream'
14
- export { DevMailbox, type MailboxEntry } from './dev/DevMailbox'
15
- export { MailErrorCode, MailTransportError } from './errors'
16
- export type { MailEvent, MailEventHandler, MailEventType } from './events'
17
- export { Mailable } from './Mailable'
18
- export { OrbitSignal } from './OrbitSignal'
19
- export { HtmlRenderer } from './renderers/HtmlRenderer'
20
- export { MjmlRenderer } from './renderers/MjmlRenderer'
21
- export * from './renderers/mjml-templates'
22
- export { ReactMjmlRenderer } from './renderers/ReactMjmlRenderer'
23
- export type { Renderer, RenderResult } from './renderers/Renderer'
24
- export { TemplateRenderer } from './renderers/TemplateRenderer'
25
- export { VueMjmlRenderer } from './renderers/VueMjmlRenderer'
26
- export { TypedMailable } from './TypedMailable'
27
- export { BaseTransport, type TransportOptions } from './transports/BaseTransport'
28
- export { LogTransport } from './transports/LogTransport'
29
- export { MemoryTransport } from './transports/MemoryTransport'
30
- export { SesTransport } from './transports/SesTransport'
31
- export { SmtpTransport } from './transports/SmtpTransport'
32
- export type { Transport } from './transports/Transport'
33
- export type {
34
- Address,
35
- Attachment,
36
- Envelope,
37
- MailConfig,
38
- Message,
39
- } from './types'
40
- export { type SendGridWebhookConfig, SendGridWebhookDriver } from './webhooks/SendGridWebhookDriver'
41
- export { SesWebhookDriver } from './webhooks/SesWebhookDriver'