@igniter-js/mail 0.1.12 → 0.1.14

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/AGENTS.md CHANGED
@@ -193,7 +193,8 @@ The package keeps a "Zero-Bloat" policy for production:
193
193
 
194
194
  - **`react` and `@react-email/components`**: Required for template rendering.
195
195
  - **`resend`, `nodemailer`**: Peer dependencies. They are not bundled, keeping the core package small.
196
- - **`@igniter-js/core`**: Provides the foundational error class and job interfaces.
196
+ - **`@igniter-js/common`**: Provides the foundational error class and logger interfaces.
197
+ - **`@igniter-js/core`**: Provides job interfaces used by the mail queue integration.
197
198
  - **`@igniter-js/telemetry`**: Optional peer dependency. If missing, telemetry emission is safely skipped.
198
199
 
199
200
  ---
@@ -436,7 +437,7 @@ await mail.schedule(params, reminderDate);
436
437
  | ------------------------ | -------------------- | --------------------------------------------------- |
437
438
  | `IgniterMail` | Primary entry point | `create()` |
438
439
  | `IgniterMailBuilder` | Configuration engine | `withAdapter`, `withFrom`, `addTemplate`, `build` |
439
- | `IgniterMailManagerCore` | Runtime engine | `send`, `schedule`, `$Infer` |
440
+ | `IgniterMailManagerCore` | Runtime engine | `send`, `schedule`, `$Infer`, `templates.*` |
440
441
  | `IgniterMailTemplate` | Template definition | `create`, `withSubject`, `withSchema`, `withRender` |
441
442
 
442
443
  #### Adapters
@@ -479,6 +480,20 @@ The package exposes the following events via the `igniter.mail` namespace:
479
480
  | `success` | `mail.to`, `mail.queue_id` | Emitted when the job is enqueued. |
480
481
  | `error` | `mail.to`, `mail.error.message` | Emitted if the queue adapter fails. |
481
482
 
483
+ #### Group: `templates`
484
+
485
+ | Event | Attributes | Context |
486
+ | --------------- | --------------------------------------- | -------------------------------------- |
487
+ | `list.started` | `ctx.mail.template.count` | Emitted when `templates.list()` starts |
488
+ | `list.success` | `ctx.mail.template.count` | Emitted when list resolves |
489
+ | `list.error` | `ctx.mail.error.code` | Emitted on list failure |
490
+ | `get.started` | `ctx.mail.template_id` | Emitted when `templates.get()` starts |
491
+ | `get.success` | `ctx.mail.template_id` | Emitted when get resolves |
492
+ | `get.error` | `ctx.mail.error.code` | Emitted on get failure |
493
+ | `render.started`| `ctx.mail.template_id` | Emitted when render starts |
494
+ | `render.success`| `ctx.mail.template_id`, `ctx.mail.duration_ms` | Emitted when render resolves |
495
+ | `render.error` | `ctx.mail.error.code`, `ctx.mail.error.message` | Emitted on render failure |
496
+
482
497
  ---
483
498
 
484
499
  ### 15. Troubleshooting & Error Code Library
@@ -504,6 +519,27 @@ The package exposes the following events via the `igniter.mail` namespace:
504
519
  - **Mitigation:** Check the `details` array of the error to see which Zod/Schema path failed.
505
520
  - **Solution:** `data: { name: 'Valid String' }`.
506
521
 
522
+ #### `MAIL_PROVIDER_TEMPLATE_LIST_FAILED`
523
+
524
+ - **Context:** Occurs during `mail.templates.list()`.
525
+ - **Cause:** Unexpected failure while enumerating the template registry.
526
+ - **Mitigation:** Check for mutation or invalid template entries in the registry.
527
+ - **Solution:** Ensure all templates follow `IgniterMailTemplateBuilt` shape.
528
+
529
+ #### `MAIL_PROVIDER_TEMPLATE_GET_FAILED`
530
+
531
+ - **Context:** Occurs during `mail.templates.get()`.
532
+ - **Cause:** Unexpected failure while reading the template registry.
533
+ - **Mitigation:** Ensure the registry is not mutated at runtime.
534
+ - **Solution:** Rebuild the mail instance after modifying templates.
535
+
536
+ #### `MAIL_PROVIDER_TEMPLATE_RENDER_FAILED`
537
+
538
+ - **Context:** Occurs during `mail.templates.render()`.
539
+ - **Cause:** Rendering failed after validation (e.g., runtime errors inside the React template).
540
+ - **Mitigation:** Inspect the template render function for thrown errors.
541
+ - **Solution:** Render the template in isolation to reproduce the error.
542
+
507
543
  #### `MAIL_PROVIDER_SEND_FAILED`
508
544
 
509
545
  - **Context:** Occurs after rendering, during adapter execution.
package/README.md CHANGED
@@ -167,6 +167,20 @@ const mail = IgniterMail.create()
167
167
  .build()
168
168
  ```
169
169
 
170
+ ### Template Registry
171
+
172
+ Access template metadata and render previews without sending:
173
+
174
+ ```typescript
175
+ const templates = await mail.templates.list()
176
+ const template = await mail.templates.get('resetPassword')
177
+ const preview = await mail.templates.render('resetPassword', {
178
+ name: 'Jane',
179
+ resetLink: 'https://example.com/reset',
180
+ expiresAt: new Date(),
181
+ })
182
+ ```
183
+
170
184
  ### Type Safety
171
185
 
172
186
  The library provides end-to-end type safety:
@@ -444,6 +458,32 @@ await mail.schedule(
444
458
  )
445
459
  ```
446
460
 
461
+ ##### `templates.list()`
462
+
463
+ Lists template metadata available in the registry.
464
+
465
+ ```typescript
466
+ const templates = await mail.templates.list()
467
+ ```
468
+
469
+ ##### `templates.get(id)`
470
+
471
+ Gets a single template metadata entry.
472
+
473
+ ```typescript
474
+ const template = await mail.templates.get('welcome')
475
+ ```
476
+
477
+ ##### `templates.render(id, variables?)`
478
+
479
+ Renders a template to HTML and plain text without sending.
480
+
481
+ ```typescript
482
+ const preview = await mail.templates.render('welcome', {
483
+ name: 'User',
484
+ })
485
+ ```
486
+
447
487
  ### IgniterMailBuilder
448
488
 
449
489
  Fluent API for configuring the mail service.
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var core = require('@igniter-js/core');
3
+ var common = require('@igniter-js/common');
4
4
  var resend = require('resend');
5
5
  var nodemailer = require('nodemailer');
6
6
 
@@ -9,7 +9,7 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
9
  var nodemailer__default = /*#__PURE__*/_interopDefault(nodemailer);
10
10
 
11
11
  // src/errors/mail.error.ts
12
- var IgniterMailError = class _IgniterMailError extends core.IgniterError {
12
+ var IgniterMailError = class _IgniterMailError extends common.IgniterError {
13
13
  constructor(payload) {
14
14
  super({
15
15
  code: payload.code,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/errors/mail.error.ts","../../src/adapters/postmark.adapter.ts","../../src/adapters/resend.adapter.ts","../../src/adapters/sendgrid.adapter.ts","../../src/adapters/smtp.adapter.ts","../../src/adapters/mock.adapter.ts"],"names":["IgniterError","resend","Resend","nodemailer"],"mappings":"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","file":"index.js","sourcesContent":["import { IgniterError, type IgniterLogger } from '@igniter-js/core'\n\n/**\n * Known error codes thrown by `@igniter-js/mail` runtime.\n */\nexport type IgniterMailErrorCode =\n | 'MAIL_PROVIDER_FROM_REQUIRED'\n | 'MAIL_PROVIDER_ADAPTER_REQUIRED'\n | 'MAIL_PROVIDER_ADAPTER_SECRET_REQUIRED'\n | 'MAIL_PROVIDER_ADAPTER_NOT_FOUND'\n | 'MAIL_PROVIDER_TEMPLATES_REQUIRED'\n | 'MAIL_PROVIDER_TEMPLATE_NOT_FOUND'\n | 'MAIL_PROVIDER_TEMPLATE_DATA_INVALID'\n | 'MAIL_PROVIDER_SCHEDULE_DATE_INVALID'\n | 'MAIL_PROVIDER_SEND_FAILED'\n | 'MAIL_PROVIDER_SCHEDULE_FAILED'\n | 'MAIL_ADAPTER_CONFIGURATION_INVALID'\n | 'MAIL_TEMPLATE_CONFIGURATION_INVALID'\n | 'MAIL_PROVIDER_SCHEDULE_QUEUE_NOT_CONFIGURED'\n\n/**\n * Payload used to create an {@link IgniterMailError}.\n */\nexport type IgniterMailErrorPayload = {\n /** Machine-readable error code. */\n code: IgniterMailErrorCode\n /** Human-readable message. */\n message: string\n /** Optional HTTP status code when surfacing errors through HTTP boundaries. */\n statusCode?: number\n /** Optional original cause. */\n cause?: unknown\n /** Extra diagnostic details (e.g. schema issues). */\n details?: unknown\n /** Arbitrary metadata for debugging. */\n metadata?: Record<string, unknown>\n /** Optional logger used by IgniterError. */\n logger?: IgniterLogger\n}\n\n/**\n * Typed error used by the `IgniterMail` runtime.\n *\n * This is designed to be stable for extraction into `@igniter-js/mail`.\n */\nexport class IgniterMailError extends IgniterError {\n declare readonly code: IgniterMailErrorCode\n\n constructor(payload: IgniterMailErrorPayload) {\n super({\n code: payload.code,\n message: payload.message,\n statusCode: payload.statusCode,\n causer: '@igniter-js/mail',\n details: payload.details,\n metadata: payload.metadata,\n logger: payload.logger,\n })\n\n this.name = 'IgniterMailError'\n }\n\n /**\n * Type guard utility.\n */\n static is(error: unknown): error is IgniterMailError {\n return error instanceof IgniterMailError\n }\n}\n\n","import { IgniterMailError } from '../errors/mail.error'\nimport type {\n IgniterMailAdapter,\n IgniterMailAdapterCredentials,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * Postmark adapter.\n *\n * Notes:\n * - This implementation uses `fetch` (no SDK dependency).\n * - Designed to be extracted to `@igniter-js/mail/adapters/postmark`.\n *\n * @example\n * ```ts\n * const adapter = PostmarkMailAdapter.create({\n * secret: process.env.POSTMARK_SERVER_TOKEN,\n * from: 'no-reply@example.com',\n * })\n * await adapter.send({ to: 'user@example.com', subject: 'Hello', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\nexport class PostmarkMailAdapter implements IgniterMailAdapter {\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Provider credentials (secret/from).\n * @returns A Postmark adapter instance.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n * @example\n * ```ts\n * const adapter = PostmarkMailAdapter.create({ secret: 'token', from: 'no-reply@acme.com' })\n * ```\n */\n static create(credentials: IgniterMailAdapterCredentials) {\n return new PostmarkMailAdapter(credentials)\n }\n\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Provider credentials (secret/from).\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n */\n constructor(private readonly credentials: IgniterMailAdapterCredentials = {}) {}\n\n /**\n * Sends an email using Postmark (HTTP API).\n *\n * @param params - Normalized email parameters.\n * @returns A promise that resolves when the email is accepted.\n * @throws {IgniterMailError} When configuration is invalid or Postmark rejects the request.\n * @example\n * ```ts\n * await adapter.send({ to: 'user@example.com', subject: 'Hello', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\n async send(params: IgniterMailAdapterSendParams) {\n if (!this.credentials.secret) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'Postmark adapter secret is required',\n })\n }\n\n if (!this.credentials.from) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'Postmark adapter from is required',\n })\n }\n\n const token = this.credentials.secret\n const from = this.credentials.from\n\n const response = await fetch('https://api.postmarkapp.com/email', {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n 'X-Postmark-Server-Token': token,\n },\n body: JSON.stringify({\n From: from,\n To: params.to,\n Subject: params.subject,\n HtmlBody: params.html,\n TextBody: params.text,\n }),\n })\n\n if (!response.ok) {\n const body = await response.text().catch(() => '')\n\n throw new IgniterMailError({\n code: 'MAIL_PROVIDER_SEND_FAILED',\n message: 'Postmark send failed',\n metadata: {\n status: response.status,\n body,\n },\n })\n }\n }\n}","import { Resend } from 'resend'\n\nimport { IgniterMailError } from '../errors/mail.error'\nimport type {\n IgniterMailAdapter,\n IgniterMailAdapterCredentials,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * Resend adapter implementation.\n *\n * Notes:\n * - Uses the Resend SDK.\n * - Designed to be extracted to `@igniter-js/mail/adapters/resend`.\n *\n * @example\n * ```ts\n * const adapter = ResendMailAdapter.create({\n * secret: process.env.RESEND_API_KEY,\n * from: 'no-reply@example.com',\n * })\n * await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\nexport class ResendMailAdapter implements IgniterMailAdapter {\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Adapter credentials including API secret and default from.\n * @returns A configured Resend adapter.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n * @example\n * ```ts\n * const adapter = ResendMailAdapter.create({ secret: 'token', from: 'no-reply@acme.com' })\n * ```\n */\n static create(credentials: IgniterMailAdapterCredentials) {\n return new ResendMailAdapter(credentials)\n }\n\n /**\n * Creates an adapter with credentials.\n *\n * @param credentials - Adapter credentials including API secret and default from.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n */\n constructor(private readonly credentials: IgniterMailAdapterCredentials = {}) {}\n\n /**\n * Sends an email using Resend.\n *\n * @param params - Email payload to send.\n * @returns Resolves when the email is accepted by Resend.\n * @throws {IgniterMailError} When credentials are missing or Resend rejects the request.\n *\n * @example\n * ```ts\n * await adapter.send({ to: 'user@example.com', subject: 'Welcome', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\n async send(params: IgniterMailAdapterSendParams): Promise<void> {\n if (!this.credentials.secret) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'Resend adapter secret is required',\n })\n }\n\n if (!this.credentials.from) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'Resend adapter from is required',\n })\n }\n\n const resend = new Resend(this.credentials.secret)\n const from = this.credentials.from\n\n await resend.emails.create({\n to: params.to,\n from,\n subject: params.subject,\n html: params.html,\n text: params.text,\n scheduledAt: params.scheduledAt?.toISOString(),\n })\n }\n}\n","import { IgniterMailError } from '../errors/mail.error'\nimport type {\n IgniterMailAdapter,\n IgniterMailAdapterCredentials,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * SendGrid adapter implementation.\n *\n * Notes:\n * - This implementation uses `fetch` (no SDK dependency).\n * - Designed to be extracted to `@igniter-js/mail/adapters/sendgrid`.\n *\n * @example\n * ```ts\n * const adapter = SendGridMailAdapter.create({\n * secret: process.env.SENDGRID_API_KEY,\n * from: 'no-reply@example.com',\n * })\n * await adapter.send({ to: 'user@example.com', subject: 'Hello', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\nexport class SendGridMailAdapter implements IgniterMailAdapter {\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Adapter credentials including API secret and default from.\n * @returns A configured SendGrid adapter.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n * @example\n * ```ts\n * const adapter = SendGridMailAdapter.create({ secret: 'token', from: 'no-reply@acme.com' })\n * ```\n */\n static create(credentials: IgniterMailAdapterCredentials) {\n return new SendGridMailAdapter(credentials)\n }\n\n /**\n * Creates an adapter with credentials.\n *\n * @param credentials - Adapter credentials including API secret and default from.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n */\n constructor(private readonly credentials: IgniterMailAdapterCredentials = {}) {}\n\n /**\n * Sends an email using SendGrid (HTTP API).\n *\n * @param params - Email payload to send.\n * @returns Resolves when the email is accepted by SendGrid.\n * @throws {IgniterMailError} When credentials are missing or the API fails.\n *\n * @example\n * ```ts\n * await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\n async send(params: IgniterMailAdapterSendParams): Promise<void> {\n if (!this.credentials.secret) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'SendGrid adapter secret is required',\n })\n }\n\n if (!this.credentials.from) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'SendGrid adapter from is required',\n })\n }\n\n const apiKey = this.credentials.secret\n const from = this.credentials.from\n\n const response = await fetch('https://api.sendgrid.com/v3/mail/send', {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n personalizations: [\n {\n to: [{ email: params.to }],\n },\n ],\n from: { email: from },\n subject: params.subject,\n content: [\n { type: 'text/plain', value: params.text },\n { type: 'text/html', value: params.html },\n ],\n }),\n })\n\n if (!response.ok) {\n const body = await response.text().catch(() => '')\n\n throw new IgniterMailError({\n code: 'MAIL_PROVIDER_SEND_FAILED',\n message: 'SendGrid send failed',\n metadata: {\n status: response.status,\n body,\n },\n })\n }\n }\n}\n","import nodemailer from 'nodemailer'\n\nimport { IgniterMailError } from '../errors/mail.error'\nimport type {\n IgniterMailAdapter,\n IgniterMailAdapterCredentials,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * SMTP adapter implementation.\n *\n * Notes:\n * - Uses Nodemailer.\n * - `credentials.secret` must be an SMTP connection URL.\n *\n * @example\n * ```ts\n * const adapter = SmtpMailAdapter.create({\n * secret: 'smtps://user:pass@host:465',\n * from: 'no-reply@example.com',\n * })\n * await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\nexport class SmtpMailAdapter implements IgniterMailAdapter {\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Adapter credentials including SMTP URL and default from.\n * @returns A configured SMTP adapter.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n * @example\n * ```ts\n * const adapter = SmtpMailAdapter.create({ secret: 'smtps://user:pass@host:465', from: 'no-reply@acme.com' })\n * ```\n */\n static create(credentials: IgniterMailAdapterCredentials) {\n return new SmtpMailAdapter(credentials)\n }\n\n /**\n * Creates an adapter with credentials.\n *\n * @param credentials - Adapter credentials including SMTP URL and default from.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n */\n constructor(private readonly credentials: IgniterMailAdapterCredentials = {}) {}\n\n /**\n * Sends an email using Nodemailer over SMTP.\n *\n * @param params - Email payload to send.\n * @returns Resolves when the email is sent.\n * @throws {IgniterMailError} When credentials are missing or the SMTP send fails.\n *\n * @example\n * ```ts\n * await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\n async send(params: IgniterMailAdapterSendParams): Promise<void> {\n if (!this.credentials.secret) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'SMTP adapter secret is required',\n })\n }\n\n if (!this.credentials.from) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'SMTP adapter from is required',\n })\n }\n\n const smtpUrl = this.credentials.secret\n const from = this.credentials.from\n\n const transport = nodemailer.createTransport(smtpUrl, {\n connectionTimeout: 10000,\n greetingTimeout: 5000,\n socketTimeout: 10000,\n tls: {\n rejectUnauthorized: false,\n },\n })\n\n try {\n await transport.sendMail({\n from,\n to: params.to,\n subject: params.subject,\n html: params.html,\n text: params.text,\n })\n } catch(error) {\n throw new IgniterMailError({\n code: 'MAIL_PROVIDER_SEND_FAILED',\n message: 'SMTP send failed',\n metadata: {\n originalError: error,\n },\n })\n } finally {\n transport.close()\n }\n }\n}\n","import type {\n IgniterMailAdapter,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * In-memory mock adapter for `@igniter-js/mail`.\n *\n * Use this in tests to avoid real provider calls.\n */\nexport class MockMailAdapter implements IgniterMailAdapter {\n /** Creates a new mock adapter instance. */\n static create(): MockMailAdapter {\n return new MockMailAdapter()\n }\n\n /** Tracks all send calls. */\n public readonly sent: IgniterMailAdapterSendParams[] = []\n\n /** Tracks method call counts. */\n public readonly calls = {\n send: 0,\n }\n\n async send(params: IgniterMailAdapterSendParams): Promise<void> {\n this.calls.send += 1\n this.sent.push(params)\n }\n\n /** Clears all tracked state. */\n clear(): void {\n this.sent.length = 0\n this.calls.send = 0\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/errors/mail.error.ts","../../src/adapters/postmark.adapter.ts","../../src/adapters/resend.adapter.ts","../../src/adapters/sendgrid.adapter.ts","../../src/adapters/smtp.adapter.ts","../../src/adapters/mock.adapter.ts"],"names":["IgniterError","resend","Resend","nodemailer"],"mappings":"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","file":"index.js","sourcesContent":["import { IgniterError, type IgniterLogger } from \"@igniter-js/common\";\n\n/**\n * Known error codes thrown by `@igniter-js/mail` runtime.\n */\nexport type IgniterMailErrorCode =\n | 'MAIL_PROVIDER_FROM_REQUIRED'\n | 'MAIL_PROVIDER_ADAPTER_REQUIRED'\n | 'MAIL_PROVIDER_ADAPTER_SECRET_REQUIRED'\n | 'MAIL_PROVIDER_ADAPTER_NOT_FOUND'\n | 'MAIL_PROVIDER_TEMPLATES_REQUIRED'\n | 'MAIL_PROVIDER_TEMPLATE_NOT_FOUND'\n | 'MAIL_PROVIDER_TEMPLATE_DATA_INVALID'\n | 'MAIL_PROVIDER_TEMPLATE_LIST_FAILED'\n | 'MAIL_PROVIDER_TEMPLATE_GET_FAILED'\n | 'MAIL_PROVIDER_TEMPLATE_RENDER_FAILED'\n | 'MAIL_PROVIDER_SCHEDULE_DATE_INVALID'\n | 'MAIL_PROVIDER_SEND_FAILED'\n | 'MAIL_PROVIDER_SCHEDULE_FAILED'\n | 'MAIL_ADAPTER_CONFIGURATION_INVALID'\n | 'MAIL_TEMPLATE_CONFIGURATION_INVALID'\n | 'MAIL_PROVIDER_SCHEDULE_QUEUE_NOT_CONFIGURED'\n\n/**\n * Payload used to create an {@link IgniterMailError}.\n */\nexport type IgniterMailErrorPayload = {\n /** Machine-readable error code. */\n code: IgniterMailErrorCode\n /** Human-readable message. */\n message: string\n /** Optional HTTP status code when surfacing errors through HTTP boundaries. */\n statusCode?: number\n /** Optional original cause. */\n cause?: unknown\n /** Extra diagnostic details (e.g. schema issues). */\n details?: unknown\n /** Arbitrary metadata for debugging. */\n metadata?: Record<string, unknown>\n /** Optional logger used by IgniterError. */\n logger?: IgniterLogger\n}\n\n/**\n * Typed error used by the `IgniterMail` runtime.\n *\n * This is designed to be stable for extraction into `@igniter-js/mail`.\n */\nexport class IgniterMailError extends IgniterError {\n declare readonly code: IgniterMailErrorCode\n\n constructor(payload: IgniterMailErrorPayload) {\n super({\n code: payload.code,\n message: payload.message,\n statusCode: payload.statusCode,\n causer: '@igniter-js/mail',\n details: payload.details,\n metadata: payload.metadata,\n logger: payload.logger,\n })\n\n this.name = 'IgniterMailError'\n }\n\n /**\n * Type guard utility.\n */\n static is(error: unknown): error is IgniterMailError {\n return error instanceof IgniterMailError\n }\n}\n","import { IgniterMailError } from '../errors/mail.error'\nimport type {\n IgniterMailAdapter,\n IgniterMailAdapterCredentials,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * Postmark adapter.\n *\n * Notes:\n * - This implementation uses `fetch` (no SDK dependency).\n * - Designed to be extracted to `@igniter-js/mail/adapters/postmark`.\n *\n * @example\n * ```ts\n * const adapter = PostmarkMailAdapter.create({\n * secret: process.env.POSTMARK_SERVER_TOKEN,\n * from: 'no-reply@example.com',\n * })\n * await adapter.send({ to: 'user@example.com', subject: 'Hello', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\nexport class PostmarkMailAdapter implements IgniterMailAdapter {\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Provider credentials (secret/from).\n * @returns A Postmark adapter instance.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n * @example\n * ```ts\n * const adapter = PostmarkMailAdapter.create({ secret: 'token', from: 'no-reply@acme.com' })\n * ```\n */\n static create(credentials: IgniterMailAdapterCredentials) {\n return new PostmarkMailAdapter(credentials)\n }\n\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Provider credentials (secret/from).\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n */\n constructor(private readonly credentials: IgniterMailAdapterCredentials = {}) {}\n\n /**\n * Sends an email using Postmark (HTTP API).\n *\n * @param params - Normalized email parameters.\n * @returns A promise that resolves when the email is accepted.\n * @throws {IgniterMailError} When configuration is invalid or Postmark rejects the request.\n * @example\n * ```ts\n * await adapter.send({ to: 'user@example.com', subject: 'Hello', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\n async send(params: IgniterMailAdapterSendParams) {\n if (!this.credentials.secret) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'Postmark adapter secret is required',\n })\n }\n\n if (!this.credentials.from) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'Postmark adapter from is required',\n })\n }\n\n const token = this.credentials.secret\n const from = this.credentials.from\n\n const response = await fetch('https://api.postmarkapp.com/email', {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n 'X-Postmark-Server-Token': token,\n },\n body: JSON.stringify({\n From: from,\n To: params.to,\n Subject: params.subject,\n HtmlBody: params.html,\n TextBody: params.text,\n }),\n })\n\n if (!response.ok) {\n const body = await response.text().catch(() => '')\n\n throw new IgniterMailError({\n code: 'MAIL_PROVIDER_SEND_FAILED',\n message: 'Postmark send failed',\n metadata: {\n status: response.status,\n body,\n },\n })\n }\n }\n}","import { Resend } from 'resend'\n\nimport { IgniterMailError } from '../errors/mail.error'\nimport type {\n IgniterMailAdapter,\n IgniterMailAdapterCredentials,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * Resend adapter implementation.\n *\n * Notes:\n * - Uses the Resend SDK.\n * - Designed to be extracted to `@igniter-js/mail/adapters/resend`.\n *\n * @example\n * ```ts\n * const adapter = ResendMailAdapter.create({\n * secret: process.env.RESEND_API_KEY,\n * from: 'no-reply@example.com',\n * })\n * await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\nexport class ResendMailAdapter implements IgniterMailAdapter {\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Adapter credentials including API secret and default from.\n * @returns A configured Resend adapter.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n * @example\n * ```ts\n * const adapter = ResendMailAdapter.create({ secret: 'token', from: 'no-reply@acme.com' })\n * ```\n */\n static create(credentials: IgniterMailAdapterCredentials) {\n return new ResendMailAdapter(credentials)\n }\n\n /**\n * Creates an adapter with credentials.\n *\n * @param credentials - Adapter credentials including API secret and default from.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n */\n constructor(private readonly credentials: IgniterMailAdapterCredentials = {}) {}\n\n /**\n * Sends an email using Resend.\n *\n * @param params - Email payload to send.\n * @returns Resolves when the email is accepted by Resend.\n * @throws {IgniterMailError} When credentials are missing or Resend rejects the request.\n *\n * @example\n * ```ts\n * await adapter.send({ to: 'user@example.com', subject: 'Welcome', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\n async send(params: IgniterMailAdapterSendParams): Promise<void> {\n if (!this.credentials.secret) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'Resend adapter secret is required',\n })\n }\n\n if (!this.credentials.from) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'Resend adapter from is required',\n })\n }\n\n const resend = new Resend(this.credentials.secret)\n const from = this.credentials.from\n\n await resend.emails.create({\n to: params.to,\n from,\n subject: params.subject,\n html: params.html,\n text: params.text,\n scheduledAt: params.scheduledAt?.toISOString(),\n })\n }\n}\n","import { IgniterMailError } from '../errors/mail.error'\nimport type {\n IgniterMailAdapter,\n IgniterMailAdapterCredentials,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * SendGrid adapter implementation.\n *\n * Notes:\n * - This implementation uses `fetch` (no SDK dependency).\n * - Designed to be extracted to `@igniter-js/mail/adapters/sendgrid`.\n *\n * @example\n * ```ts\n * const adapter = SendGridMailAdapter.create({\n * secret: process.env.SENDGRID_API_KEY,\n * from: 'no-reply@example.com',\n * })\n * await adapter.send({ to: 'user@example.com', subject: 'Hello', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\nexport class SendGridMailAdapter implements IgniterMailAdapter {\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Adapter credentials including API secret and default from.\n * @returns A configured SendGrid adapter.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n * @example\n * ```ts\n * const adapter = SendGridMailAdapter.create({ secret: 'token', from: 'no-reply@acme.com' })\n * ```\n */\n static create(credentials: IgniterMailAdapterCredentials) {\n return new SendGridMailAdapter(credentials)\n }\n\n /**\n * Creates an adapter with credentials.\n *\n * @param credentials - Adapter credentials including API secret and default from.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n */\n constructor(private readonly credentials: IgniterMailAdapterCredentials = {}) {}\n\n /**\n * Sends an email using SendGrid (HTTP API).\n *\n * @param params - Email payload to send.\n * @returns Resolves when the email is accepted by SendGrid.\n * @throws {IgniterMailError} When credentials are missing or the API fails.\n *\n * @example\n * ```ts\n * await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\n async send(params: IgniterMailAdapterSendParams): Promise<void> {\n if (!this.credentials.secret) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'SendGrid adapter secret is required',\n })\n }\n\n if (!this.credentials.from) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'SendGrid adapter from is required',\n })\n }\n\n const apiKey = this.credentials.secret\n const from = this.credentials.from\n\n const response = await fetch('https://api.sendgrid.com/v3/mail/send', {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n personalizations: [\n {\n to: [{ email: params.to }],\n },\n ],\n from: { email: from },\n subject: params.subject,\n content: [\n { type: 'text/plain', value: params.text },\n { type: 'text/html', value: params.html },\n ],\n }),\n })\n\n if (!response.ok) {\n const body = await response.text().catch(() => '')\n\n throw new IgniterMailError({\n code: 'MAIL_PROVIDER_SEND_FAILED',\n message: 'SendGrid send failed',\n metadata: {\n status: response.status,\n body,\n },\n })\n }\n }\n}\n","import nodemailer from 'nodemailer'\n\nimport { IgniterMailError } from '../errors/mail.error'\nimport type {\n IgniterMailAdapter,\n IgniterMailAdapterCredentials,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * SMTP adapter implementation.\n *\n * Notes:\n * - Uses Nodemailer.\n * - `credentials.secret` must be an SMTP connection URL.\n *\n * @example\n * ```ts\n * const adapter = SmtpMailAdapter.create({\n * secret: 'smtps://user:pass@host:465',\n * from: 'no-reply@example.com',\n * })\n * await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\nexport class SmtpMailAdapter implements IgniterMailAdapter {\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Adapter credentials including SMTP URL and default from.\n * @returns A configured SMTP adapter.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n * @example\n * ```ts\n * const adapter = SmtpMailAdapter.create({ secret: 'smtps://user:pass@host:465', from: 'no-reply@acme.com' })\n * ```\n */\n static create(credentials: IgniterMailAdapterCredentials) {\n return new SmtpMailAdapter(credentials)\n }\n\n /**\n * Creates an adapter with credentials.\n *\n * @param credentials - Adapter credentials including SMTP URL and default from.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n */\n constructor(private readonly credentials: IgniterMailAdapterCredentials = {}) {}\n\n /**\n * Sends an email using Nodemailer over SMTP.\n *\n * @param params - Email payload to send.\n * @returns Resolves when the email is sent.\n * @throws {IgniterMailError} When credentials are missing or the SMTP send fails.\n *\n * @example\n * ```ts\n * await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\n async send(params: IgniterMailAdapterSendParams): Promise<void> {\n if (!this.credentials.secret) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'SMTP adapter secret is required',\n })\n }\n\n if (!this.credentials.from) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'SMTP adapter from is required',\n })\n }\n\n const smtpUrl = this.credentials.secret\n const from = this.credentials.from\n\n const transport = nodemailer.createTransport(smtpUrl, {\n connectionTimeout: 10000,\n greetingTimeout: 5000,\n socketTimeout: 10000,\n tls: {\n rejectUnauthorized: false,\n },\n })\n\n try {\n await transport.sendMail({\n from,\n to: params.to,\n subject: params.subject,\n html: params.html,\n text: params.text,\n })\n } catch(error) {\n throw new IgniterMailError({\n code: 'MAIL_PROVIDER_SEND_FAILED',\n message: 'SMTP send failed',\n metadata: {\n originalError: error,\n },\n })\n } finally {\n transport.close()\n }\n }\n}\n","import type {\n IgniterMailAdapter,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * In-memory mock adapter for `@igniter-js/mail`.\n *\n * Use this in tests to avoid real provider calls.\n */\nexport class MockMailAdapter implements IgniterMailAdapter {\n /** Creates a new mock adapter instance. */\n static create(): MockMailAdapter {\n return new MockMailAdapter()\n }\n\n /** Tracks all send calls. */\n public readonly sent: IgniterMailAdapterSendParams[] = []\n\n /** Tracks method call counts. */\n public readonly calls = {\n send: 0,\n }\n\n async send(params: IgniterMailAdapterSendParams): Promise<void> {\n this.calls.send += 1\n this.sent.push(params)\n }\n\n /** Clears all tracked state. */\n clear(): void {\n this.sent.length = 0\n this.calls.send = 0\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { IgniterError } from '@igniter-js/core';
1
+ import { IgniterError } from '@igniter-js/common';
2
2
  import { Resend } from 'resend';
3
3
  import nodemailer from 'nodemailer';
4
4
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/errors/mail.error.ts","../../src/adapters/postmark.adapter.ts","../../src/adapters/resend.adapter.ts","../../src/adapters/sendgrid.adapter.ts","../../src/adapters/smtp.adapter.ts","../../src/adapters/mock.adapter.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import { IgniterError, type IgniterLogger } from '@igniter-js/core'\n\n/**\n * Known error codes thrown by `@igniter-js/mail` runtime.\n */\nexport type IgniterMailErrorCode =\n | 'MAIL_PROVIDER_FROM_REQUIRED'\n | 'MAIL_PROVIDER_ADAPTER_REQUIRED'\n | 'MAIL_PROVIDER_ADAPTER_SECRET_REQUIRED'\n | 'MAIL_PROVIDER_ADAPTER_NOT_FOUND'\n | 'MAIL_PROVIDER_TEMPLATES_REQUIRED'\n | 'MAIL_PROVIDER_TEMPLATE_NOT_FOUND'\n | 'MAIL_PROVIDER_TEMPLATE_DATA_INVALID'\n | 'MAIL_PROVIDER_SCHEDULE_DATE_INVALID'\n | 'MAIL_PROVIDER_SEND_FAILED'\n | 'MAIL_PROVIDER_SCHEDULE_FAILED'\n | 'MAIL_ADAPTER_CONFIGURATION_INVALID'\n | 'MAIL_TEMPLATE_CONFIGURATION_INVALID'\n | 'MAIL_PROVIDER_SCHEDULE_QUEUE_NOT_CONFIGURED'\n\n/**\n * Payload used to create an {@link IgniterMailError}.\n */\nexport type IgniterMailErrorPayload = {\n /** Machine-readable error code. */\n code: IgniterMailErrorCode\n /** Human-readable message. */\n message: string\n /** Optional HTTP status code when surfacing errors through HTTP boundaries. */\n statusCode?: number\n /** Optional original cause. */\n cause?: unknown\n /** Extra diagnostic details (e.g. schema issues). */\n details?: unknown\n /** Arbitrary metadata for debugging. */\n metadata?: Record<string, unknown>\n /** Optional logger used by IgniterError. */\n logger?: IgniterLogger\n}\n\n/**\n * Typed error used by the `IgniterMail` runtime.\n *\n * This is designed to be stable for extraction into `@igniter-js/mail`.\n */\nexport class IgniterMailError extends IgniterError {\n declare readonly code: IgniterMailErrorCode\n\n constructor(payload: IgniterMailErrorPayload) {\n super({\n code: payload.code,\n message: payload.message,\n statusCode: payload.statusCode,\n causer: '@igniter-js/mail',\n details: payload.details,\n metadata: payload.metadata,\n logger: payload.logger,\n })\n\n this.name = 'IgniterMailError'\n }\n\n /**\n * Type guard utility.\n */\n static is(error: unknown): error is IgniterMailError {\n return error instanceof IgniterMailError\n }\n}\n\n","import { IgniterMailError } from '../errors/mail.error'\nimport type {\n IgniterMailAdapter,\n IgniterMailAdapterCredentials,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * Postmark adapter.\n *\n * Notes:\n * - This implementation uses `fetch` (no SDK dependency).\n * - Designed to be extracted to `@igniter-js/mail/adapters/postmark`.\n *\n * @example\n * ```ts\n * const adapter = PostmarkMailAdapter.create({\n * secret: process.env.POSTMARK_SERVER_TOKEN,\n * from: 'no-reply@example.com',\n * })\n * await adapter.send({ to: 'user@example.com', subject: 'Hello', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\nexport class PostmarkMailAdapter implements IgniterMailAdapter {\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Provider credentials (secret/from).\n * @returns A Postmark adapter instance.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n * @example\n * ```ts\n * const adapter = PostmarkMailAdapter.create({ secret: 'token', from: 'no-reply@acme.com' })\n * ```\n */\n static create(credentials: IgniterMailAdapterCredentials) {\n return new PostmarkMailAdapter(credentials)\n }\n\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Provider credentials (secret/from).\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n */\n constructor(private readonly credentials: IgniterMailAdapterCredentials = {}) {}\n\n /**\n * Sends an email using Postmark (HTTP API).\n *\n * @param params - Normalized email parameters.\n * @returns A promise that resolves when the email is accepted.\n * @throws {IgniterMailError} When configuration is invalid or Postmark rejects the request.\n * @example\n * ```ts\n * await adapter.send({ to: 'user@example.com', subject: 'Hello', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\n async send(params: IgniterMailAdapterSendParams) {\n if (!this.credentials.secret) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'Postmark adapter secret is required',\n })\n }\n\n if (!this.credentials.from) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'Postmark adapter from is required',\n })\n }\n\n const token = this.credentials.secret\n const from = this.credentials.from\n\n const response = await fetch('https://api.postmarkapp.com/email', {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n 'X-Postmark-Server-Token': token,\n },\n body: JSON.stringify({\n From: from,\n To: params.to,\n Subject: params.subject,\n HtmlBody: params.html,\n TextBody: params.text,\n }),\n })\n\n if (!response.ok) {\n const body = await response.text().catch(() => '')\n\n throw new IgniterMailError({\n code: 'MAIL_PROVIDER_SEND_FAILED',\n message: 'Postmark send failed',\n metadata: {\n status: response.status,\n body,\n },\n })\n }\n }\n}","import { Resend } from 'resend'\n\nimport { IgniterMailError } from '../errors/mail.error'\nimport type {\n IgniterMailAdapter,\n IgniterMailAdapterCredentials,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * Resend adapter implementation.\n *\n * Notes:\n * - Uses the Resend SDK.\n * - Designed to be extracted to `@igniter-js/mail/adapters/resend`.\n *\n * @example\n * ```ts\n * const adapter = ResendMailAdapter.create({\n * secret: process.env.RESEND_API_KEY,\n * from: 'no-reply@example.com',\n * })\n * await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\nexport class ResendMailAdapter implements IgniterMailAdapter {\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Adapter credentials including API secret and default from.\n * @returns A configured Resend adapter.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n * @example\n * ```ts\n * const adapter = ResendMailAdapter.create({ secret: 'token', from: 'no-reply@acme.com' })\n * ```\n */\n static create(credentials: IgniterMailAdapterCredentials) {\n return new ResendMailAdapter(credentials)\n }\n\n /**\n * Creates an adapter with credentials.\n *\n * @param credentials - Adapter credentials including API secret and default from.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n */\n constructor(private readonly credentials: IgniterMailAdapterCredentials = {}) {}\n\n /**\n * Sends an email using Resend.\n *\n * @param params - Email payload to send.\n * @returns Resolves when the email is accepted by Resend.\n * @throws {IgniterMailError} When credentials are missing or Resend rejects the request.\n *\n * @example\n * ```ts\n * await adapter.send({ to: 'user@example.com', subject: 'Welcome', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\n async send(params: IgniterMailAdapterSendParams): Promise<void> {\n if (!this.credentials.secret) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'Resend adapter secret is required',\n })\n }\n\n if (!this.credentials.from) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'Resend adapter from is required',\n })\n }\n\n const resend = new Resend(this.credentials.secret)\n const from = this.credentials.from\n\n await resend.emails.create({\n to: params.to,\n from,\n subject: params.subject,\n html: params.html,\n text: params.text,\n scheduledAt: params.scheduledAt?.toISOString(),\n })\n }\n}\n","import { IgniterMailError } from '../errors/mail.error'\nimport type {\n IgniterMailAdapter,\n IgniterMailAdapterCredentials,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * SendGrid adapter implementation.\n *\n * Notes:\n * - This implementation uses `fetch` (no SDK dependency).\n * - Designed to be extracted to `@igniter-js/mail/adapters/sendgrid`.\n *\n * @example\n * ```ts\n * const adapter = SendGridMailAdapter.create({\n * secret: process.env.SENDGRID_API_KEY,\n * from: 'no-reply@example.com',\n * })\n * await adapter.send({ to: 'user@example.com', subject: 'Hello', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\nexport class SendGridMailAdapter implements IgniterMailAdapter {\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Adapter credentials including API secret and default from.\n * @returns A configured SendGrid adapter.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n * @example\n * ```ts\n * const adapter = SendGridMailAdapter.create({ secret: 'token', from: 'no-reply@acme.com' })\n * ```\n */\n static create(credentials: IgniterMailAdapterCredentials) {\n return new SendGridMailAdapter(credentials)\n }\n\n /**\n * Creates an adapter with credentials.\n *\n * @param credentials - Adapter credentials including API secret and default from.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n */\n constructor(private readonly credentials: IgniterMailAdapterCredentials = {}) {}\n\n /**\n * Sends an email using SendGrid (HTTP API).\n *\n * @param params - Email payload to send.\n * @returns Resolves when the email is accepted by SendGrid.\n * @throws {IgniterMailError} When credentials are missing or the API fails.\n *\n * @example\n * ```ts\n * await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\n async send(params: IgniterMailAdapterSendParams): Promise<void> {\n if (!this.credentials.secret) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'SendGrid adapter secret is required',\n })\n }\n\n if (!this.credentials.from) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'SendGrid adapter from is required',\n })\n }\n\n const apiKey = this.credentials.secret\n const from = this.credentials.from\n\n const response = await fetch('https://api.sendgrid.com/v3/mail/send', {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n personalizations: [\n {\n to: [{ email: params.to }],\n },\n ],\n from: { email: from },\n subject: params.subject,\n content: [\n { type: 'text/plain', value: params.text },\n { type: 'text/html', value: params.html },\n ],\n }),\n })\n\n if (!response.ok) {\n const body = await response.text().catch(() => '')\n\n throw new IgniterMailError({\n code: 'MAIL_PROVIDER_SEND_FAILED',\n message: 'SendGrid send failed',\n metadata: {\n status: response.status,\n body,\n },\n })\n }\n }\n}\n","import nodemailer from 'nodemailer'\n\nimport { IgniterMailError } from '../errors/mail.error'\nimport type {\n IgniterMailAdapter,\n IgniterMailAdapterCredentials,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * SMTP adapter implementation.\n *\n * Notes:\n * - Uses Nodemailer.\n * - `credentials.secret` must be an SMTP connection URL.\n *\n * @example\n * ```ts\n * const adapter = SmtpMailAdapter.create({\n * secret: 'smtps://user:pass@host:465',\n * from: 'no-reply@example.com',\n * })\n * await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\nexport class SmtpMailAdapter implements IgniterMailAdapter {\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Adapter credentials including SMTP URL and default from.\n * @returns A configured SMTP adapter.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n * @example\n * ```ts\n * const adapter = SmtpMailAdapter.create({ secret: 'smtps://user:pass@host:465', from: 'no-reply@acme.com' })\n * ```\n */\n static create(credentials: IgniterMailAdapterCredentials) {\n return new SmtpMailAdapter(credentials)\n }\n\n /**\n * Creates an adapter with credentials.\n *\n * @param credentials - Adapter credentials including SMTP URL and default from.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n */\n constructor(private readonly credentials: IgniterMailAdapterCredentials = {}) {}\n\n /**\n * Sends an email using Nodemailer over SMTP.\n *\n * @param params - Email payload to send.\n * @returns Resolves when the email is sent.\n * @throws {IgniterMailError} When credentials are missing or the SMTP send fails.\n *\n * @example\n * ```ts\n * await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\n async send(params: IgniterMailAdapterSendParams): Promise<void> {\n if (!this.credentials.secret) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'SMTP adapter secret is required',\n })\n }\n\n if (!this.credentials.from) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'SMTP adapter from is required',\n })\n }\n\n const smtpUrl = this.credentials.secret\n const from = this.credentials.from\n\n const transport = nodemailer.createTransport(smtpUrl, {\n connectionTimeout: 10000,\n greetingTimeout: 5000,\n socketTimeout: 10000,\n tls: {\n rejectUnauthorized: false,\n },\n })\n\n try {\n await transport.sendMail({\n from,\n to: params.to,\n subject: params.subject,\n html: params.html,\n text: params.text,\n })\n } catch(error) {\n throw new IgniterMailError({\n code: 'MAIL_PROVIDER_SEND_FAILED',\n message: 'SMTP send failed',\n metadata: {\n originalError: error,\n },\n })\n } finally {\n transport.close()\n }\n }\n}\n","import type {\n IgniterMailAdapter,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * In-memory mock adapter for `@igniter-js/mail`.\n *\n * Use this in tests to avoid real provider calls.\n */\nexport class MockMailAdapter implements IgniterMailAdapter {\n /** Creates a new mock adapter instance. */\n static create(): MockMailAdapter {\n return new MockMailAdapter()\n }\n\n /** Tracks all send calls. */\n public readonly sent: IgniterMailAdapterSendParams[] = []\n\n /** Tracks method call counts. */\n public readonly calls = {\n send: 0,\n }\n\n async send(params: IgniterMailAdapterSendParams): Promise<void> {\n this.calls.send += 1\n this.sent.push(params)\n }\n\n /** Clears all tracked state. */\n clear(): void {\n this.sent.length = 0\n this.calls.send = 0\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/errors/mail.error.ts","../../src/adapters/postmark.adapter.ts","../../src/adapters/resend.adapter.ts","../../src/adapters/sendgrid.adapter.ts","../../src/adapters/smtp.adapter.ts","../../src/adapters/mock.adapter.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import { IgniterError, type IgniterLogger } from \"@igniter-js/common\";\n\n/**\n * Known error codes thrown by `@igniter-js/mail` runtime.\n */\nexport type IgniterMailErrorCode =\n | 'MAIL_PROVIDER_FROM_REQUIRED'\n | 'MAIL_PROVIDER_ADAPTER_REQUIRED'\n | 'MAIL_PROVIDER_ADAPTER_SECRET_REQUIRED'\n | 'MAIL_PROVIDER_ADAPTER_NOT_FOUND'\n | 'MAIL_PROVIDER_TEMPLATES_REQUIRED'\n | 'MAIL_PROVIDER_TEMPLATE_NOT_FOUND'\n | 'MAIL_PROVIDER_TEMPLATE_DATA_INVALID'\n | 'MAIL_PROVIDER_TEMPLATE_LIST_FAILED'\n | 'MAIL_PROVIDER_TEMPLATE_GET_FAILED'\n | 'MAIL_PROVIDER_TEMPLATE_RENDER_FAILED'\n | 'MAIL_PROVIDER_SCHEDULE_DATE_INVALID'\n | 'MAIL_PROVIDER_SEND_FAILED'\n | 'MAIL_PROVIDER_SCHEDULE_FAILED'\n | 'MAIL_ADAPTER_CONFIGURATION_INVALID'\n | 'MAIL_TEMPLATE_CONFIGURATION_INVALID'\n | 'MAIL_PROVIDER_SCHEDULE_QUEUE_NOT_CONFIGURED'\n\n/**\n * Payload used to create an {@link IgniterMailError}.\n */\nexport type IgniterMailErrorPayload = {\n /** Machine-readable error code. */\n code: IgniterMailErrorCode\n /** Human-readable message. */\n message: string\n /** Optional HTTP status code when surfacing errors through HTTP boundaries. */\n statusCode?: number\n /** Optional original cause. */\n cause?: unknown\n /** Extra diagnostic details (e.g. schema issues). */\n details?: unknown\n /** Arbitrary metadata for debugging. */\n metadata?: Record<string, unknown>\n /** Optional logger used by IgniterError. */\n logger?: IgniterLogger\n}\n\n/**\n * Typed error used by the `IgniterMail` runtime.\n *\n * This is designed to be stable for extraction into `@igniter-js/mail`.\n */\nexport class IgniterMailError extends IgniterError {\n declare readonly code: IgniterMailErrorCode\n\n constructor(payload: IgniterMailErrorPayload) {\n super({\n code: payload.code,\n message: payload.message,\n statusCode: payload.statusCode,\n causer: '@igniter-js/mail',\n details: payload.details,\n metadata: payload.metadata,\n logger: payload.logger,\n })\n\n this.name = 'IgniterMailError'\n }\n\n /**\n * Type guard utility.\n */\n static is(error: unknown): error is IgniterMailError {\n return error instanceof IgniterMailError\n }\n}\n","import { IgniterMailError } from '../errors/mail.error'\nimport type {\n IgniterMailAdapter,\n IgniterMailAdapterCredentials,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * Postmark adapter.\n *\n * Notes:\n * - This implementation uses `fetch` (no SDK dependency).\n * - Designed to be extracted to `@igniter-js/mail/adapters/postmark`.\n *\n * @example\n * ```ts\n * const adapter = PostmarkMailAdapter.create({\n * secret: process.env.POSTMARK_SERVER_TOKEN,\n * from: 'no-reply@example.com',\n * })\n * await adapter.send({ to: 'user@example.com', subject: 'Hello', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\nexport class PostmarkMailAdapter implements IgniterMailAdapter {\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Provider credentials (secret/from).\n * @returns A Postmark adapter instance.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n * @example\n * ```ts\n * const adapter = PostmarkMailAdapter.create({ secret: 'token', from: 'no-reply@acme.com' })\n * ```\n */\n static create(credentials: IgniterMailAdapterCredentials) {\n return new PostmarkMailAdapter(credentials)\n }\n\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Provider credentials (secret/from).\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n */\n constructor(private readonly credentials: IgniterMailAdapterCredentials = {}) {}\n\n /**\n * Sends an email using Postmark (HTTP API).\n *\n * @param params - Normalized email parameters.\n * @returns A promise that resolves when the email is accepted.\n * @throws {IgniterMailError} When configuration is invalid or Postmark rejects the request.\n * @example\n * ```ts\n * await adapter.send({ to: 'user@example.com', subject: 'Hello', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\n async send(params: IgniterMailAdapterSendParams) {\n if (!this.credentials.secret) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'Postmark adapter secret is required',\n })\n }\n\n if (!this.credentials.from) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'Postmark adapter from is required',\n })\n }\n\n const token = this.credentials.secret\n const from = this.credentials.from\n\n const response = await fetch('https://api.postmarkapp.com/email', {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n 'X-Postmark-Server-Token': token,\n },\n body: JSON.stringify({\n From: from,\n To: params.to,\n Subject: params.subject,\n HtmlBody: params.html,\n TextBody: params.text,\n }),\n })\n\n if (!response.ok) {\n const body = await response.text().catch(() => '')\n\n throw new IgniterMailError({\n code: 'MAIL_PROVIDER_SEND_FAILED',\n message: 'Postmark send failed',\n metadata: {\n status: response.status,\n body,\n },\n })\n }\n }\n}","import { Resend } from 'resend'\n\nimport { IgniterMailError } from '../errors/mail.error'\nimport type {\n IgniterMailAdapter,\n IgniterMailAdapterCredentials,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * Resend adapter implementation.\n *\n * Notes:\n * - Uses the Resend SDK.\n * - Designed to be extracted to `@igniter-js/mail/adapters/resend`.\n *\n * @example\n * ```ts\n * const adapter = ResendMailAdapter.create({\n * secret: process.env.RESEND_API_KEY,\n * from: 'no-reply@example.com',\n * })\n * await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\nexport class ResendMailAdapter implements IgniterMailAdapter {\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Adapter credentials including API secret and default from.\n * @returns A configured Resend adapter.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n * @example\n * ```ts\n * const adapter = ResendMailAdapter.create({ secret: 'token', from: 'no-reply@acme.com' })\n * ```\n */\n static create(credentials: IgniterMailAdapterCredentials) {\n return new ResendMailAdapter(credentials)\n }\n\n /**\n * Creates an adapter with credentials.\n *\n * @param credentials - Adapter credentials including API secret and default from.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n */\n constructor(private readonly credentials: IgniterMailAdapterCredentials = {}) {}\n\n /**\n * Sends an email using Resend.\n *\n * @param params - Email payload to send.\n * @returns Resolves when the email is accepted by Resend.\n * @throws {IgniterMailError} When credentials are missing or Resend rejects the request.\n *\n * @example\n * ```ts\n * await adapter.send({ to: 'user@example.com', subject: 'Welcome', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\n async send(params: IgniterMailAdapterSendParams): Promise<void> {\n if (!this.credentials.secret) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'Resend adapter secret is required',\n })\n }\n\n if (!this.credentials.from) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'Resend adapter from is required',\n })\n }\n\n const resend = new Resend(this.credentials.secret)\n const from = this.credentials.from\n\n await resend.emails.create({\n to: params.to,\n from,\n subject: params.subject,\n html: params.html,\n text: params.text,\n scheduledAt: params.scheduledAt?.toISOString(),\n })\n }\n}\n","import { IgniterMailError } from '../errors/mail.error'\nimport type {\n IgniterMailAdapter,\n IgniterMailAdapterCredentials,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * SendGrid adapter implementation.\n *\n * Notes:\n * - This implementation uses `fetch` (no SDK dependency).\n * - Designed to be extracted to `@igniter-js/mail/adapters/sendgrid`.\n *\n * @example\n * ```ts\n * const adapter = SendGridMailAdapter.create({\n * secret: process.env.SENDGRID_API_KEY,\n * from: 'no-reply@example.com',\n * })\n * await adapter.send({ to: 'user@example.com', subject: 'Hello', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\nexport class SendGridMailAdapter implements IgniterMailAdapter {\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Adapter credentials including API secret and default from.\n * @returns A configured SendGrid adapter.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n * @example\n * ```ts\n * const adapter = SendGridMailAdapter.create({ secret: 'token', from: 'no-reply@acme.com' })\n * ```\n */\n static create(credentials: IgniterMailAdapterCredentials) {\n return new SendGridMailAdapter(credentials)\n }\n\n /**\n * Creates an adapter with credentials.\n *\n * @param credentials - Adapter credentials including API secret and default from.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n */\n constructor(private readonly credentials: IgniterMailAdapterCredentials = {}) {}\n\n /**\n * Sends an email using SendGrid (HTTP API).\n *\n * @param params - Email payload to send.\n * @returns Resolves when the email is accepted by SendGrid.\n * @throws {IgniterMailError} When credentials are missing or the API fails.\n *\n * @example\n * ```ts\n * await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\n async send(params: IgniterMailAdapterSendParams): Promise<void> {\n if (!this.credentials.secret) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'SendGrid adapter secret is required',\n })\n }\n\n if (!this.credentials.from) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'SendGrid adapter from is required',\n })\n }\n\n const apiKey = this.credentials.secret\n const from = this.credentials.from\n\n const response = await fetch('https://api.sendgrid.com/v3/mail/send', {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n personalizations: [\n {\n to: [{ email: params.to }],\n },\n ],\n from: { email: from },\n subject: params.subject,\n content: [\n { type: 'text/plain', value: params.text },\n { type: 'text/html', value: params.html },\n ],\n }),\n })\n\n if (!response.ok) {\n const body = await response.text().catch(() => '')\n\n throw new IgniterMailError({\n code: 'MAIL_PROVIDER_SEND_FAILED',\n message: 'SendGrid send failed',\n metadata: {\n status: response.status,\n body,\n },\n })\n }\n }\n}\n","import nodemailer from 'nodemailer'\n\nimport { IgniterMailError } from '../errors/mail.error'\nimport type {\n IgniterMailAdapter,\n IgniterMailAdapterCredentials,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * SMTP adapter implementation.\n *\n * Notes:\n * - Uses Nodemailer.\n * - `credentials.secret` must be an SMTP connection URL.\n *\n * @example\n * ```ts\n * const adapter = SmtpMailAdapter.create({\n * secret: 'smtps://user:pass@host:465',\n * from: 'no-reply@example.com',\n * })\n * await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\nexport class SmtpMailAdapter implements IgniterMailAdapter {\n /**\n * Creates a new adapter instance.\n *\n * @param credentials - Adapter credentials including SMTP URL and default from.\n * @returns A configured SMTP adapter.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n * @example\n * ```ts\n * const adapter = SmtpMailAdapter.create({ secret: 'smtps://user:pass@host:465', from: 'no-reply@acme.com' })\n * ```\n */\n static create(credentials: IgniterMailAdapterCredentials) {\n return new SmtpMailAdapter(credentials)\n }\n\n /**\n * Creates an adapter with credentials.\n *\n * @param credentials - Adapter credentials including SMTP URL and default from.\n * @throws {IgniterMailError} Does not throw on creation; errors surface on send.\n */\n constructor(private readonly credentials: IgniterMailAdapterCredentials = {}) {}\n\n /**\n * Sends an email using Nodemailer over SMTP.\n *\n * @param params - Email payload to send.\n * @returns Resolves when the email is sent.\n * @throws {IgniterMailError} When credentials are missing or the SMTP send fails.\n *\n * @example\n * ```ts\n * await adapter.send({ to: 'user@example.com', subject: 'Hi', html: '<p>Hi</p>', text: 'Hi' })\n * ```\n */\n async send(params: IgniterMailAdapterSendParams): Promise<void> {\n if (!this.credentials.secret) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'SMTP adapter secret is required',\n })\n }\n\n if (!this.credentials.from) {\n throw new IgniterMailError({\n code: 'MAIL_ADAPTER_CONFIGURATION_INVALID',\n message: 'SMTP adapter from is required',\n })\n }\n\n const smtpUrl = this.credentials.secret\n const from = this.credentials.from\n\n const transport = nodemailer.createTransport(smtpUrl, {\n connectionTimeout: 10000,\n greetingTimeout: 5000,\n socketTimeout: 10000,\n tls: {\n rejectUnauthorized: false,\n },\n })\n\n try {\n await transport.sendMail({\n from,\n to: params.to,\n subject: params.subject,\n html: params.html,\n text: params.text,\n })\n } catch(error) {\n throw new IgniterMailError({\n code: 'MAIL_PROVIDER_SEND_FAILED',\n message: 'SMTP send failed',\n metadata: {\n originalError: error,\n },\n })\n } finally {\n transport.close()\n }\n }\n}\n","import type {\n IgniterMailAdapter,\n IgniterMailAdapterSendParams,\n} from '../types/adapter'\n\n/**\n * In-memory mock adapter for `@igniter-js/mail`.\n *\n * Use this in tests to avoid real provider calls.\n */\nexport class MockMailAdapter implements IgniterMailAdapter {\n /** Creates a new mock adapter instance. */\n static create(): MockMailAdapter {\n return new MockMailAdapter()\n }\n\n /** Tracks all send calls. */\n public readonly sent: IgniterMailAdapterSendParams[] = []\n\n /** Tracks method call counts. */\n public readonly calls = {\n send: 0,\n }\n\n async send(params: IgniterMailAdapterSendParams): Promise<void> {\n this.calls.send += 1\n this.sent.push(params)\n }\n\n /** Clears all tracked state. */\n clear(): void {\n this.sent.length = 0\n this.calls.send = 0\n }\n}\n"]}
package/dist/index.d.mts CHANGED
@@ -1,4 +1,5 @@
1
- import { StandardSchemaV1, JobLimiter, IgniterLogger, IgniterJobQueueAdapter, IgniterError } from '@igniter-js/core';
1
+ import { StandardSchemaV1, IgniterLogger, IgniterError } from '@igniter-js/common';
2
+ import { JobLimiter, IgniterJobQueueAdapter } from '@igniter-js/core';
2
3
  import { I as IgniterMailAdapter } from './index-CbiH0sth.mjs';
3
4
  export { b as IgniterMailAdapterCredentials, a as IgniterMailAdapterSendParams, M as MockMailAdapter, P as PostmarkMailAdapter, R as ResendMailAdapter, S as SendGridMailAdapter, c as SmtpMailAdapter } from './index-CbiH0sth.mjs';
4
5
  import { ReactElement } from 'react';
@@ -10,11 +11,44 @@ import { IgniterTelemetryManager } from '@igniter-js/telemetry';
10
11
  interface IgniterMailTemplateBuilt<TSchema extends StandardSchemaV1> {
11
12
  /** Default subject for the template (can be overridden per-send). */
12
13
  subject: string;
14
+ /** Optional display name for Studio and tooling. */
15
+ name?: string;
16
+ /** Optional description for Studio and tooling. */
17
+ description?: string;
18
+ /** Optional path hint for Studio and tooling. */
19
+ path?: string;
20
+ /** Optional created timestamp (ISO string). */
21
+ createdAt?: string;
22
+ /** Optional updated timestamp (ISO string). */
23
+ updatedAt?: string;
24
+ /** Optional list of variable names for Studio and tooling. */
25
+ variables?: string[];
13
26
  /** Schema used to validate and infer template payload. */
14
27
  schema: TSchema;
15
28
  /** React Email component renderer. */
16
29
  render: (data: StandardSchemaV1.InferInput<TSchema>) => ReactElement;
17
30
  }
31
+ /**
32
+ * Template metadata exposed by {@link IIgniterMail}.
33
+ */
34
+ interface IgniterMailTemplateMeta {
35
+ /** Template identifier (registry key). */
36
+ id: string;
37
+ /** Display name. */
38
+ name: string;
39
+ /** Description for UI listings. */
40
+ description: string;
41
+ /** Path hint for routing. */
42
+ path: string;
43
+ /** Subject line for the template. */
44
+ subject: string;
45
+ /** Creation timestamp (ISO string). */
46
+ createdAt: string;
47
+ /** Last update timestamp (ISO string). */
48
+ updatedAt: string;
49
+ /** Optional list of variables for the template. */
50
+ variables?: string[];
51
+ }
18
52
  /**
19
53
  * Extracts the valid template keys from a template map.
20
54
  */
@@ -107,6 +141,31 @@ interface IgniterMailHooks<TTemplates extends object> {
107
141
  /** Invoked after a successful send. */
108
142
  onSendSuccess?: (params: IgniterMailSendParams<TTemplates, any>) => Promise<void>;
109
143
  }
144
+ /**
145
+ * Template registry API exposed by {@link IIgniterMail}.
146
+ */
147
+ interface IgniterMailTemplatesAPI<TTemplates extends object> {
148
+ /**
149
+ * Lists all registered templates with metadata.
150
+ */
151
+ list: () => Promise<IgniterMailTemplateMeta[]>;
152
+ /**
153
+ * Resolves a template metadata entry by id.
154
+ *
155
+ * @param id - Template key.
156
+ */
157
+ get: <TSelectedTemplate extends IgniterMailTemplateKey<TTemplates>>(id: TSelectedTemplate) => Promise<IgniterMailTemplateMeta | null>;
158
+ /**
159
+ * Renders a template to HTML + text.
160
+ *
161
+ * @param id - Template key.
162
+ * @param variables - Template input payload.
163
+ */
164
+ render: <TSelectedTemplate extends IgniterMailTemplateKey<TTemplates>>(id: TSelectedTemplate, variables?: IgniterMailTemplatePayload<TTemplates[TSelectedTemplate]>) => Promise<{
165
+ html: string;
166
+ text: string;
167
+ }>;
168
+ }
110
169
  /**
111
170
  * Options used to initialize {@link IgniterMail}.
112
171
  */
@@ -133,6 +192,10 @@ interface IIgniterMail<TTemplates extends object> {
133
192
  * Access via `typeof mail.$Infer` (type-level only).
134
193
  */
135
194
  readonly $Infer: IgniterMailInfer<TTemplates>;
195
+ /**
196
+ * Template registry helpers.
197
+ */
198
+ readonly templates: IgniterMailTemplatesAPI<TTemplates>;
136
199
  /** Sends an email immediately. */
137
200
  send: <TSelectedTemplate extends IgniterMailTemplateKey<TTemplates>>(params: IgniterMailSendParams<TTemplates, TSelectedTemplate>) => Promise<void>;
138
201
  /** Schedules an email for a future date (requires queue adapter). */
@@ -223,7 +286,7 @@ declare const IgniterMailTemplate: typeof IgniterMailTemplateBuilder;
223
286
  */
224
287
  declare class IgniterMailManagerCore<TTemplates extends object> implements IIgniterMail<TTemplates> {
225
288
  private readonly adapter;
226
- private readonly templates;
289
+ private readonly templateRegistry;
227
290
  private readonly logger?;
228
291
  private readonly telemetry?;
229
292
  private readonly queue?;
@@ -235,7 +298,15 @@ declare class IgniterMailManagerCore<TTemplates extends object> implements IIgni
235
298
  * Access via `typeof mail.$Infer` (type-level only).
236
299
  */
237
300
  readonly $Infer: IgniterMailInfer<TTemplates>;
301
+ /**
302
+ * Template registry helpers.
303
+ */
304
+ readonly templates: IgniterMailTemplatesAPI<TTemplates>;
238
305
  constructor(options: IgniterMailOptions<TTemplates>);
306
+ private buildTemplateMeta;
307
+ private listTemplates;
308
+ private getTemplate;
309
+ private renderTemplate;
239
310
  private ensureQueueJobRegistered;
240
311
  private validateTemplateData;
241
312
  /**
@@ -256,7 +327,7 @@ declare class IgniterMailManagerCore<TTemplates extends object> implements IIgni
256
327
  /**
257
328
  * Known error codes thrown by `@igniter-js/mail` runtime.
258
329
  */
259
- type IgniterMailErrorCode = 'MAIL_PROVIDER_FROM_REQUIRED' | 'MAIL_PROVIDER_ADAPTER_REQUIRED' | 'MAIL_PROVIDER_ADAPTER_SECRET_REQUIRED' | 'MAIL_PROVIDER_ADAPTER_NOT_FOUND' | 'MAIL_PROVIDER_TEMPLATES_REQUIRED' | 'MAIL_PROVIDER_TEMPLATE_NOT_FOUND' | 'MAIL_PROVIDER_TEMPLATE_DATA_INVALID' | 'MAIL_PROVIDER_SCHEDULE_DATE_INVALID' | 'MAIL_PROVIDER_SEND_FAILED' | 'MAIL_PROVIDER_SCHEDULE_FAILED' | 'MAIL_ADAPTER_CONFIGURATION_INVALID' | 'MAIL_TEMPLATE_CONFIGURATION_INVALID' | 'MAIL_PROVIDER_SCHEDULE_QUEUE_NOT_CONFIGURED';
330
+ type IgniterMailErrorCode = 'MAIL_PROVIDER_FROM_REQUIRED' | 'MAIL_PROVIDER_ADAPTER_REQUIRED' | 'MAIL_PROVIDER_ADAPTER_SECRET_REQUIRED' | 'MAIL_PROVIDER_ADAPTER_NOT_FOUND' | 'MAIL_PROVIDER_TEMPLATES_REQUIRED' | 'MAIL_PROVIDER_TEMPLATE_NOT_FOUND' | 'MAIL_PROVIDER_TEMPLATE_DATA_INVALID' | 'MAIL_PROVIDER_TEMPLATE_LIST_FAILED' | 'MAIL_PROVIDER_TEMPLATE_GET_FAILED' | 'MAIL_PROVIDER_TEMPLATE_RENDER_FAILED' | 'MAIL_PROVIDER_SCHEDULE_DATE_INVALID' | 'MAIL_PROVIDER_SEND_FAILED' | 'MAIL_PROVIDER_SCHEDULE_FAILED' | 'MAIL_ADAPTER_CONFIGURATION_INVALID' | 'MAIL_TEMPLATE_CONFIGURATION_INVALID' | 'MAIL_PROVIDER_SCHEDULE_QUEUE_NOT_CONFIGURED';
260
331
  /**
261
332
  * Payload used to create an {@link IgniterMailError}.
262
333
  */
@@ -306,4 +377,4 @@ declare class IgniterMailSchema {
306
377
  static createPassthroughSchema(): StandardSchemaV1;
307
378
  }
308
379
 
309
- export { type IIgniterMail, IgniterMail, IgniterMailAdapter, IgniterMailBuilder, IgniterMailError, type IgniterMailErrorCode, type IgniterMailErrorPayload, type IgniterMailHooks, type IgniterMailInfer, IgniterMailManagerCore, type IgniterMailOptions, type IgniterMailQueueConfig, type IgniterMailQueueOptions, IgniterMailSchema, type IgniterMailSendParams, IgniterMailTemplate, IgniterMailTemplateBuilder, type IgniterMailTemplateBuilt, type IgniterMailTemplateKey, type IgniterMailTemplatePayload };
380
+ export { type IIgniterMail, IgniterMail, IgniterMailAdapter, IgniterMailBuilder, IgniterMailError, type IgniterMailErrorCode, type IgniterMailErrorPayload, type IgniterMailHooks, type IgniterMailInfer, IgniterMailManagerCore, type IgniterMailOptions, type IgniterMailQueueConfig, type IgniterMailQueueOptions, IgniterMailSchema, type IgniterMailSendParams, IgniterMailTemplate, IgniterMailTemplateBuilder, type IgniterMailTemplateBuilt, type IgniterMailTemplateKey, type IgniterMailTemplateMeta, type IgniterMailTemplatePayload, type IgniterMailTemplatesAPI };