@gravito/signal 1.0.0-alpha.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 (50) hide show
  1. package/README.md +129 -0
  2. package/dist/OrbitMail-2Z7ZTKYA.mjs +7 -0
  3. package/dist/OrbitMail-BGV32HWN.mjs +7 -0
  4. package/dist/OrbitMail-FUYZQSAV.mjs +7 -0
  5. package/dist/OrbitMail-NAPCRK7B.mjs +7 -0
  6. package/dist/OrbitMail-REGJ276B.mjs +7 -0
  7. package/dist/OrbitMail-TCFBJWDT.mjs +7 -0
  8. package/dist/OrbitMail-XZZW6U4N.mjs +7 -0
  9. package/dist/OrbitSignal-ZKKMEC27.mjs +7 -0
  10. package/dist/ReactRenderer-L5INVYKT.mjs +27 -0
  11. package/dist/VueRenderer-S65ZARRI.mjs +37129 -0
  12. package/dist/VueRenderer-Z5PRVBNH.mjs +37298 -0
  13. package/dist/chunk-3U2CYJO5.mjs +367 -0
  14. package/dist/chunk-3XFC4T6M.mjs +392 -0
  15. package/dist/chunk-6DZX6EAA.mjs +37 -0
  16. package/dist/chunk-DT3R2TNV.mjs +367 -0
  17. package/dist/chunk-GADWIVC4.mjs +400 -0
  18. package/dist/chunk-HHKFAMSE.mjs +380 -0
  19. package/dist/chunk-OKRNL6PN.mjs +400 -0
  20. package/dist/chunk-ULN3GMY2.mjs +367 -0
  21. package/dist/chunk-XAWO7RSP.mjs +398 -0
  22. package/dist/index.d.mts +278 -0
  23. package/dist/index.d.ts +278 -0
  24. package/dist/index.js +38150 -0
  25. package/dist/index.mjs +316 -0
  26. package/package.json +73 -0
  27. package/src/Mailable.ts +245 -0
  28. package/src/OrbitSignal.ts +158 -0
  29. package/src/Queueable.ts +9 -0
  30. package/src/dev/DevMailbox.ts +64 -0
  31. package/src/dev/DevServer.ts +89 -0
  32. package/src/dev/ui/mailbox.ts +68 -0
  33. package/src/dev/ui/preview.ts +59 -0
  34. package/src/dev/ui/shared.ts +46 -0
  35. package/src/index.ts +20 -0
  36. package/src/renderers/HtmlRenderer.ts +22 -0
  37. package/src/renderers/ReactRenderer.ts +35 -0
  38. package/src/renderers/Renderer.ts +11 -0
  39. package/src/renderers/TemplateRenderer.ts +34 -0
  40. package/src/renderers/VueRenderer.ts +37 -0
  41. package/src/transports/LogTransport.ts +17 -0
  42. package/src/transports/MemoryTransport.ts +11 -0
  43. package/src/transports/SesTransport.ts +56 -0
  44. package/src/transports/SmtpTransport.ts +50 -0
  45. package/src/transports/Transport.ts +8 -0
  46. package/src/types.ts +71 -0
  47. package/tests/mailable.test.ts +77 -0
  48. package/tests/renderers.test.ts +56 -0
  49. package/tests/transports.test.ts +52 -0
  50. package/tsconfig.json +19 -0
@@ -0,0 +1,398 @@
1
+ // src/dev/DevMailbox.ts
2
+ import { randomUUID } from "crypto";
3
+ var DevMailbox = class {
4
+ entries = [];
5
+ maxEntries = 50;
6
+ add(message) {
7
+ const entry = {
8
+ id: randomUUID(),
9
+ envelope: {
10
+ from: message.from,
11
+ to: message.to,
12
+ ...message.cc ? { cc: message.cc } : {},
13
+ ...message.bcc ? { bcc: message.bcc } : {},
14
+ ...message.replyTo ? { replyTo: message.replyTo } : {},
15
+ subject: message.subject,
16
+ ...message.priority ? { priority: message.priority } : {},
17
+ ...message.attachments ? { attachments: message.attachments } : {}
18
+ },
19
+ html: message.html,
20
+ ...message.text ? { text: message.text } : {},
21
+ sentAt: /* @__PURE__ */ new Date()
22
+ };
23
+ this.entries.unshift(entry);
24
+ if (this.entries.length > this.maxEntries) {
25
+ this.entries = this.entries.slice(0, this.maxEntries);
26
+ }
27
+ return entry;
28
+ }
29
+ list() {
30
+ return this.entries;
31
+ }
32
+ get(id) {
33
+ return this.entries.find((e) => e.id === id);
34
+ }
35
+ delete(id) {
36
+ const index = this.entries.findIndex((e) => e.id === id);
37
+ if (index !== -1) {
38
+ this.entries.splice(index, 1);
39
+ return true;
40
+ }
41
+ return false;
42
+ }
43
+ clear() {
44
+ this.entries = [];
45
+ }
46
+ };
47
+
48
+ // src/dev/ui/shared.ts
49
+ var styles = `
50
+ :root {
51
+ --primary: #6366f1;
52
+ --primary-dark: #4f46e5;
53
+ --bg-dark: #0f172a;
54
+ --bg-card: #1e293b;
55
+ --text: #f1f5f9;
56
+ --text-muted: #94a3b8;
57
+ --border: #334155;
58
+ --danger: #ef4444;
59
+ }
60
+ body { background: var(--bg-dark); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; margin: 0; padding: 20px; }
61
+ .container { max-width: 1000px; margin: 0 auto; }
62
+ .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
63
+ .title { font-size: 24px; font-weight: bold; display: flex; align-items: center; gap: 10px; }
64
+ .card { background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
65
+ .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; }
66
+ .btn:hover { background: var(--bg-card-hover); }
67
+ .btn-primary { background: var(--primary); color: white; }
68
+ .btn-primary:hover { background: var(--primary-dark); }
69
+ .btn-danger { background: var(--danger); color: white; }
70
+ .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; }
71
+ .list-item:last-child { border-bottom: none; }
72
+ .list-item:hover { background: #334155; }
73
+ .meta { display: flex; justify-content: space-between; font-size: 14px; color: var(--text-muted); }
74
+ .subject { font-weight: 600; font-size: 16px; }
75
+ .from { color: #cbd5e1; }
76
+ .badge { background: #475569; padding: 2px 6px; border-radius: 4px; font-size: 12px; }
77
+ .badge-high { background: #dc2626; color: white; }
78
+ .empty { padding: 40px; text-align: center; color: var(--text-muted); }
79
+ `;
80
+ var layout = (title, content) => `
81
+ <!DOCTYPE html>
82
+ <html>
83
+ <head>
84
+ <title>${title} - Gravito Mailbox</title>
85
+ <style>${styles}</style>
86
+ </head>
87
+ <body>
88
+ <div class="container">
89
+ ${content}
90
+ </div>
91
+ </body>
92
+ </html>
93
+ `;
94
+
95
+ // src/dev/ui/mailbox.ts
96
+ function formatAddress(addr) {
97
+ return addr.name ? `${addr.name} <${addr.address}>` : addr.address;
98
+ }
99
+ function timeAgo(date) {
100
+ const seconds = Math.floor((Date.now() - date.getTime()) / 1e3);
101
+ if (seconds < 60) {
102
+ return "Just now";
103
+ }
104
+ const minutes = Math.floor(seconds / 60);
105
+ if (minutes < 60) {
106
+ return `${minutes}m ago`;
107
+ }
108
+ const hours = Math.floor(minutes / 60);
109
+ if (hours < 24) {
110
+ return `${hours}h ago`;
111
+ }
112
+ return date.toLocaleDateString();
113
+ }
114
+ function getMailboxHtml(entries, prefix) {
115
+ const list = entries.length === 0 ? '<div class="empty">No emails found in mailbox.</div>' : entries.map(
116
+ (entry) => `
117
+ <a href="${prefix}/${entry.id}" class="list-item">
118
+ <div class="meta">
119
+ <span class="from">${formatAddress(entry.envelope.from || { address: "Unknown" })}</span>
120
+ <span>${timeAgo(entry.sentAt)}</span>
121
+ </div>
122
+ <div class="subject">
123
+ ${entry.envelope.priority === "high" ? '<span class="badge badge-high">High</span> ' : ""}
124
+ ${entry.envelope.subject || "(No Subject)"}
125
+ </div>
126
+ <div class="meta" style="margin-top: 4px;">
127
+ To: ${entry.envelope.to?.map((t) => t.address).join(", ")}
128
+ </div>
129
+ </a>
130
+ `
131
+ ).join("");
132
+ const content = `
133
+ <div class="header">
134
+ <div class="title">\u{1F4EC} Gravito Mailbox</div>
135
+ ${entries.length > 0 ? `<button onclick="clearAll()" class="btn btn-danger">Clear All</button>` : ""}
136
+ </div>
137
+
138
+ <div class="card">
139
+ ${list}
140
+ </div>
141
+
142
+ <script>
143
+ async function clearAll() {
144
+ if (!confirm('Clear all emails?')) return;
145
+ await fetch('${prefix}', { method: 'DELETE' });
146
+ window.location.reload();
147
+ }
148
+ </script>
149
+ `;
150
+ return layout("Inbox", content);
151
+ }
152
+
153
+ // src/dev/ui/preview.ts
154
+ function getPreviewHtml(entry, prefix) {
155
+ const from = entry.envelope.from ? `${entry.envelope.from.name || ""} &lt;${entry.envelope.from.address}&gt;` : "Unknown";
156
+ const to = entry.envelope.to?.map((t) => t.address).join(", ") || "Unknown";
157
+ const content = `
158
+ <div class="header">
159
+ <div class="title">
160
+ <a href="${prefix}" class="btn">\u2190 Back</a>
161
+ <span style="margin-left: 10px">Email Preview</span>
162
+ </div>
163
+ <button onclick="deleteEmail('${entry.id}')" class="btn btn-danger">Delete</button>
164
+ </div>
165
+
166
+ <div class="card" style="margin-bottom: 20px; padding: 20px;">
167
+ <div style="font-size: 18px; font-weight: bold; margin-bottom: 10px;">${entry.envelope.subject || "(No Subject)"}</div>
168
+ <div class="meta" style="margin-bottom: 5px;">From: ${from}</div>
169
+ <div class="meta" style="margin-bottom: 5px;">To: ${to}</div>
170
+ <div class="meta">Date: ${entry.sentAt.toLocaleString()}</div>
171
+ </div>
172
+
173
+ <div style="margin-bottom: 10px;">
174
+ <button onclick="setView('html')" id="btn-html" class="btn btn-primary">HTML</button>
175
+ <button onclick="setView('text')" id="btn-text" class="btn">Text</button>
176
+ <button onclick="setView('raw')" id="btn-raw" class="btn">Raw JSON</button>
177
+ <a href="${prefix}/${entry.id}/html" target="_blank" class="btn">Open HTML \u2197</a>
178
+ </div>
179
+
180
+ <div class="card" style="height: 600px; display: flex;">
181
+ <iframe id="preview-frame" src="${prefix}/${entry.id}/html" style="width: 100%; height: 100%; border: none;"></iframe>
182
+ <pre id="raw-view" style="display: none; padding: 20px; overflow: auto; width: 100%;">${JSON.stringify(entry, null, 2)}</pre>
183
+ <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>
184
+ </div>
185
+
186
+ <script>
187
+ function setView(mode) {
188
+ document.getElementById('preview-frame').style.display = mode === 'html' ? 'block' : 'none';
189
+ document.getElementById('raw-view').style.display = mode === 'raw' ? 'block' : 'none';
190
+ document.getElementById('text-view').style.display = mode === 'text' ? 'block' : 'none';
191
+
192
+ document.getElementById('btn-html').className = mode === 'html' ? 'btn btn-primary' : 'btn';
193
+ document.getElementById('btn-text').className = mode === 'text' ? 'btn btn-primary' : 'btn';
194
+ document.getElementById('btn-raw').className = mode === 'raw' ? 'btn btn-primary' : 'btn';
195
+ }
196
+
197
+ async function deleteEmail(id) {
198
+ if (!confirm('Delete this email?')) return;
199
+ await fetch('${prefix}/' + id, { method: 'DELETE' });
200
+ window.location.href = '${prefix}';
201
+ }
202
+ </script>
203
+ `;
204
+ return layout(entry.envelope.subject || "Preview", content);
205
+ }
206
+
207
+ // src/dev/DevServer.ts
208
+ var DevServer = class {
209
+ constructor(mailbox, base = "/__mail") {
210
+ this.mailbox = mailbox;
211
+ this.base = base;
212
+ }
213
+ register(core) {
214
+ const router = core.router;
215
+ const prefix = this.base.replace(/\/$/, "");
216
+ router.get(prefix, (ctx) => {
217
+ const entries = this.mailbox.list();
218
+ return ctx.html(getMailboxHtml(entries, prefix));
219
+ });
220
+ router.get(`${prefix}/:id`, (ctx) => {
221
+ const id = ctx.req.param("id");
222
+ const entry = this.mailbox.get(id);
223
+ if (!entry) {
224
+ return ctx.text("Email not found", 404);
225
+ }
226
+ return ctx.html(getPreviewHtml(entry, prefix));
227
+ });
228
+ router.get(`${prefix}/:id/html`, (ctx) => {
229
+ const id = ctx.req.param("id");
230
+ const entry = this.mailbox.get(id);
231
+ if (!entry) {
232
+ return ctx.text("Not found", 404);
233
+ }
234
+ return ctx.html(entry.html);
235
+ });
236
+ router.get(`${prefix}/:id/text`, (ctx) => {
237
+ const id = ctx.req.param("id");
238
+ const entry = this.mailbox.get(id);
239
+ if (!entry) {
240
+ return ctx.text("Not found", 404);
241
+ }
242
+ return ctx.text(entry.text || "No text content", 200, {
243
+ "Content-Type": "text/plain; charset=utf-8"
244
+ });
245
+ });
246
+ router.get(`${prefix}/:id/raw`, (ctx) => {
247
+ const id = ctx.req.param("id");
248
+ const entry = this.mailbox.get(id);
249
+ if (!entry) {
250
+ return ctx.json({ error: "Not found" }, 404);
251
+ }
252
+ return ctx.json(entry);
253
+ });
254
+ router.get(`${prefix}/:id/delete`, (ctx) => {
255
+ return ctx.text("Method not allowed", 405);
256
+ });
257
+ router.delete(`${prefix}/:id`, (ctx) => {
258
+ const success = this.mailbox.delete(ctx.req.param("id"));
259
+ return ctx.json({ success });
260
+ });
261
+ router.delete(prefix, (ctx) => {
262
+ this.mailbox.clear();
263
+ return ctx.json({ success: true });
264
+ });
265
+ core.logger.info(`[OrbitMail] Dev Mailbox available at ${prefix}`);
266
+ }
267
+ };
268
+
269
+ // src/transports/LogTransport.ts
270
+ var LogTransport = class {
271
+ async send(message) {
272
+ console.log("\n\u{1F4E7} [OrbitMail] Email Sent (Simulated):");
273
+ console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
274
+ console.log(
275
+ `From: ${message.from.name ? `${message.from.name} <${message.from.address}>` : message.from.address}`
276
+ );
277
+ console.log(`To: ${message.to.map((t) => t.address).join(", ")}`);
278
+ console.log(`Subject: ${message.subject}`);
279
+ console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501");
280
+ console.log(`[Content Size]: ${message.html.length} chars (HTML)`);
281
+ console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
282
+ }
283
+ };
284
+
285
+ // src/transports/MemoryTransport.ts
286
+ var MemoryTransport = class {
287
+ constructor(mailbox) {
288
+ this.mailbox = mailbox;
289
+ }
290
+ async send(message) {
291
+ this.mailbox.add(message);
292
+ }
293
+ };
294
+
295
+ // src/OrbitMail.ts
296
+ var OrbitMail = class _OrbitMail {
297
+ static instance;
298
+ config;
299
+ devMailbox;
300
+ constructor(config = {}) {
301
+ this.config = config;
302
+ _OrbitMail.instance = this;
303
+ }
304
+ /**
305
+ * Get the singleton instance of OrbitMail
306
+ */
307
+ static getInstance() {
308
+ if (!_OrbitMail.instance) {
309
+ throw new Error("OrbitMail has not been initialized. Call OrbitMail.configure() first.");
310
+ }
311
+ return _OrbitMail.instance;
312
+ }
313
+ /**
314
+ * Configure the OrbitMail instance
315
+ */
316
+ static configure(config) {
317
+ if (!config.transport && !config.devMode) {
318
+ console.warn("[OrbitMail] No transport provided, falling back to LogTransport");
319
+ config.transport = new LogTransport();
320
+ }
321
+ return new _OrbitMail(config);
322
+ }
323
+ /**
324
+ * Install the orbit into PlanetCore
325
+ */
326
+ install(core) {
327
+ core.logger.info("[OrbitMail] Initializing Mail Service (Exposed as: mail)");
328
+ if (!this.config.transport && !this.config.devMode) {
329
+ this.config.transport = new LogTransport();
330
+ }
331
+ if (this.config.devMode) {
332
+ this.devMailbox = new DevMailbox();
333
+ this.config.transport = new MemoryTransport(this.devMailbox);
334
+ core.logger.info("[OrbitMail] Dev Mode Enabled: Emails will be intercepted to Dev Mailbox");
335
+ const devServer = new DevServer(this.devMailbox, this.config.devUiPrefix || "/__mail");
336
+ devServer.register(core);
337
+ }
338
+ core.app.use("*", async (c, next) => {
339
+ c.set("mail", {
340
+ send: (mailable) => this.send(mailable),
341
+ queue: (mailable) => this.queue(mailable)
342
+ });
343
+ await next();
344
+ });
345
+ }
346
+ /**
347
+ * Send a mailable instance
348
+ */
349
+ async send(mailable) {
350
+ const envelope = await mailable.buildEnvelope(this.config);
351
+ if (!envelope.from) {
352
+ throw new Error('Message is missing "from" address');
353
+ }
354
+ if (!envelope.to || envelope.to.length === 0) {
355
+ throw new Error('Message is missing "to" address');
356
+ }
357
+ const content = await mailable.renderContent();
358
+ const message = {
359
+ ...envelope,
360
+ from: envelope.from,
361
+ to: envelope.to,
362
+ subject: envelope.subject || "(No Subject)",
363
+ priority: envelope.priority || "normal",
364
+ html: content.html
365
+ };
366
+ if (content.text) {
367
+ message.text = content.text;
368
+ }
369
+ if (!this.config.transport) {
370
+ throw new Error("[OrbitMail] No transport configured. Did you call configure() or register the orbit?");
371
+ }
372
+ await this.config.transport.send(message);
373
+ }
374
+ /**
375
+ * Queue a mailable instance
376
+ *
377
+ * Push a mailable into the queue for execution.
378
+ * Requires OrbitQueue to be installed and available in the context.
379
+ */
380
+ async queue(mailable) {
381
+ const queue = this.queueService;
382
+ if (queue) {
383
+ await queue.push(mailable);
384
+ } else {
385
+ console.warn(
386
+ "[OrbitMail] Queue service not available, sending immediately. Install OrbitQueue to enable queuing."
387
+ );
388
+ await this.send(mailable);
389
+ }
390
+ }
391
+ };
392
+
393
+ export {
394
+ DevMailbox,
395
+ LogTransport,
396
+ MemoryTransport,
397
+ OrbitMail
398
+ };
@@ -0,0 +1,278 @@
1
+ import { Queueable } from '@gravito/stream';
2
+ export { Queueable } from '@gravito/stream';
3
+ import { GravitoOrbit, PlanetCore } from 'gravito-core';
4
+
5
+ interface Transport {
6
+ /**
7
+ * Send the given message
8
+ */
9
+ send(message: Message): Promise<void>;
10
+ }
11
+
12
+ interface Address {
13
+ name?: string;
14
+ address: string;
15
+ }
16
+ interface Attachment {
17
+ filename: string;
18
+ content: string | Buffer;
19
+ contentType?: string;
20
+ cid?: string;
21
+ encoding?: string;
22
+ }
23
+ interface Envelope {
24
+ from?: Address | undefined;
25
+ to?: Address[] | undefined;
26
+ cc?: Address[] | undefined;
27
+ bcc?: Address[] | undefined;
28
+ replyTo?: Address | undefined;
29
+ subject?: string | undefined;
30
+ priority?: 'high' | 'normal' | 'low' | undefined;
31
+ attachments?: Attachment[] | undefined;
32
+ }
33
+ interface Message extends Envelope {
34
+ from: Address;
35
+ to: Address[];
36
+ subject: string;
37
+ html: string;
38
+ text?: string;
39
+ headers?: Record<string, string>;
40
+ }
41
+ interface MailConfig {
42
+ /**
43
+ * Default sender address
44
+ */
45
+ from?: Address;
46
+ /**
47
+ * The transport mechanism used to send emails
48
+ */
49
+ transport?: Transport;
50
+ /**
51
+ * Enable development mode (intercepts emails)
52
+ */
53
+ devMode?: boolean | undefined;
54
+ /**
55
+ * Directory where email templates are located (for OrbitPrism)
56
+ * Default: src/emails
57
+ */
58
+ viewsDir?: string | undefined;
59
+ /**
60
+ * URL prefix for Dev UI
61
+ * Default: /__mail
62
+ */
63
+ devUiPrefix?: string | undefined;
64
+ /**
65
+ * Translation function for i18n support
66
+ */
67
+ translator?: ((key: string, replace?: Record<string, unknown>, locale?: string) => string) | undefined;
68
+ }
69
+
70
+ interface MailboxEntry {
71
+ id: string;
72
+ envelope: Envelope;
73
+ html: string;
74
+ text?: string;
75
+ sentAt: Date;
76
+ }
77
+ declare class DevMailbox {
78
+ private entries;
79
+ private maxEntries;
80
+ add(message: Message): MailboxEntry;
81
+ list(): MailboxEntry[];
82
+ get(id: string): MailboxEntry | undefined;
83
+ delete(id: string): boolean;
84
+ clear(): void;
85
+ }
86
+
87
+ interface RenderResult {
88
+ html: string;
89
+ text?: string;
90
+ }
91
+ interface Renderer {
92
+ /**
93
+ * Render the content into HTML and optionally plain text
94
+ */
95
+ render(data: Record<string, unknown>): Promise<RenderResult>;
96
+ }
97
+
98
+ type ComponentType = any;
99
+ declare abstract class Mailable implements Queueable {
100
+ protected envelope: Partial<Envelope>;
101
+ protected renderer?: Renderer;
102
+ private rendererResolver?;
103
+ protected renderData: Record<string, unknown>;
104
+ from(address: string | Address): this;
105
+ to(address: string | Address | (string | Address)[]): this;
106
+ cc(address: string | Address | (string | Address)[]): this;
107
+ bcc(address: string | Address | (string | Address)[]): this;
108
+ replyTo(address: string | Address): this;
109
+ subject(subject: string): this;
110
+ priority(level: 'high' | 'normal' | 'low'): this;
111
+ attach(attachment: Attachment): this;
112
+ /**
113
+ * Set the content using raw HTML string.
114
+ */
115
+ html(content: string): this;
116
+ /**
117
+ * Set the content using an OrbitPrism template.
118
+ * @param template Template name (relative to viewsDir/emails)
119
+ * @param data Data to pass to the template
120
+ */
121
+ view(template: string, data?: Record<string, unknown>): this;
122
+ /**
123
+ * Set the content using a React component.
124
+ * Dynamically imports ReactRenderer to avoid hard dependency errors if React is not installed.
125
+ */
126
+ react<P extends object>(component: ComponentType, props?: P): this;
127
+ /**
128
+ * Set the content using a Vue component.
129
+ * Dynamically imports VueRenderer to avoid hard dependency errors if Vue is not installed.
130
+ */
131
+ vue<P extends object>(component: ComponentType, props?: P): this;
132
+ /**
133
+ * Setup the mailable. This is where you call from(), to(), view(), etc.
134
+ */
135
+ abstract build(): this;
136
+ queueName?: string;
137
+ connectionName?: string;
138
+ delaySeconds?: number;
139
+ onQueue(queue: string): this;
140
+ onConnection(connection: string): this;
141
+ delay(seconds: number): this;
142
+ /**
143
+ * Queue the mailable for sending.
144
+ */
145
+ queue(): Promise<void>;
146
+ protected currentLocale?: string;
147
+ protected translator?: (key: string, replace?: Record<string, unknown>, locale?: string) => string;
148
+ /**
149
+ * Set the locale for the message.
150
+ */
151
+ locale(locale: string): this;
152
+ /**
153
+ * Internal: Set the translator function (called by OrbitSignal)
154
+ */
155
+ setTranslator(translator: (key: string, replace?: Record<string, unknown>, locale?: string) => string): void;
156
+ /**
157
+ * Translate a string using the configured translator.
158
+ */
159
+ t(key: string, replace?: Record<string, unknown>): string;
160
+ /**
161
+ * Compile the envelope based on config defaults and mailable settings.
162
+ */
163
+ buildEnvelope(configPromise: MailConfig | Promise<MailConfig>): Promise<Envelope>;
164
+ /**
165
+ * execute the renderer
166
+ */
167
+ renderContent(): Promise<{
168
+ html: string;
169
+ text?: string;
170
+ }>;
171
+ private normalizeAddressArray;
172
+ }
173
+
174
+ declare class OrbitSignal implements GravitoOrbit {
175
+ private static instance?;
176
+ private config;
177
+ private devMailbox?;
178
+ constructor(config?: MailConfig);
179
+ /**
180
+ * Get the singleton instance of OrbitSignal
181
+ */
182
+ static getInstance(): OrbitSignal;
183
+ /**
184
+ * Configure the OrbitSignal instance
185
+ */
186
+ static configure(config: MailConfig): OrbitSignal;
187
+ /**
188
+ * Install the orbit into PlanetCore
189
+ */
190
+ install(core: PlanetCore): void;
191
+ /**
192
+ * Send a mailable instance
193
+ */
194
+ send(mailable: Mailable): Promise<void>;
195
+ /**
196
+ * Queue a mailable instance
197
+ *
198
+ * Push a mailable into the queue for execution.
199
+ * Requires OrbitStream to be installed and available in the context.
200
+ */
201
+ queue(mailable: Mailable): Promise<void>;
202
+ }
203
+ declare module 'hono' {
204
+ interface ContextVariableMap {
205
+ mail: {
206
+ send: (mailable: Mailable) => Promise<void>;
207
+ queue: (mailable: Mailable) => Promise<void>;
208
+ };
209
+ }
210
+ }
211
+ declare module 'gravito-core' {
212
+ interface GravitoVariables {
213
+ /** Mail service for sending emails */
214
+ mail?: {
215
+ send: (mailable: Mailable) => Promise<void>;
216
+ queue: (mailable: Mailable) => Promise<void>;
217
+ };
218
+ }
219
+ }
220
+
221
+ declare class HtmlRenderer implements Renderer {
222
+ private content;
223
+ constructor(content: string);
224
+ render(): Promise<RenderResult>;
225
+ private stripHtml;
226
+ }
227
+
228
+ declare class TemplateRenderer implements Renderer {
229
+ private engine;
230
+ private template;
231
+ constructor(templateName: string, viewsDir?: string);
232
+ render(data: Record<string, unknown>): Promise<RenderResult>;
233
+ private stripHtml;
234
+ }
235
+
236
+ declare class LogTransport implements Transport {
237
+ send(message: Message): Promise<void>;
238
+ }
239
+
240
+ declare class MemoryTransport implements Transport {
241
+ private mailbox;
242
+ constructor(mailbox: DevMailbox);
243
+ send(message: Message): Promise<void>;
244
+ }
245
+
246
+ interface SesConfig {
247
+ region: string;
248
+ accessKeyId?: string;
249
+ secretAccessKey?: string;
250
+ }
251
+ declare class SesTransport implements Transport {
252
+ private transporter;
253
+ constructor(config: SesConfig);
254
+ send(message: Message): Promise<void>;
255
+ private formatAddress;
256
+ }
257
+
258
+ interface SmtpConfig {
259
+ host: string;
260
+ port: number;
261
+ secure?: boolean;
262
+ auth?: {
263
+ user: string;
264
+ pass: string;
265
+ };
266
+ tls?: {
267
+ rejectUnauthorized?: boolean;
268
+ ciphers?: string;
269
+ };
270
+ }
271
+ declare class SmtpTransport implements Transport {
272
+ private transporter;
273
+ constructor(config: SmtpConfig);
274
+ send(message: Message): Promise<void>;
275
+ private formatAddress;
276
+ }
277
+
278
+ export { type Address, type Attachment, DevMailbox, type Envelope, HtmlRenderer, LogTransport, type MailConfig, Mailable, type MailboxEntry, MemoryTransport, type Message, OrbitSignal, type RenderResult, type Renderer, SesTransport, SmtpTransport, TemplateRenderer, type Transport };