feat: initial @armco/mesa-sdk with flexible mailConfig
This commit is contained in:
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
node_modules/
|
||||
dist/
|
||||
*.log
|
||||
.env
|
||||
.env.local
|
||||
.DS_Store
|
||||
coverage/
|
||||
297
README.md
Normal file
297
README.md
Normal file
@@ -0,0 +1,297 @@
|
||||
# @armco/mesa-sdk
|
||||
|
||||
Official Node.js SDK for the Mesa/Sandesh Email Platform.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @armco/mesa-sdk
|
||||
# or
|
||||
yarn add @armco/mesa-sdk
|
||||
# or
|
||||
pnpm add @armco/mesa-sdk
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
```typescript
|
||||
import { MesaClient } from '@armco/mesa-sdk';
|
||||
|
||||
const mesa = new MesaClient({
|
||||
apiKey: process.env.MESA_API_KEY!,
|
||||
});
|
||||
|
||||
await mesa.send({
|
||||
from: { email: 'noreply@example.com', name: 'My App' },
|
||||
to: 'user@example.com',
|
||||
subject: 'Welcome!',
|
||||
html: '<h1>Hello World</h1>',
|
||||
});
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Basic Configuration
|
||||
|
||||
```typescript
|
||||
const mesa = new MesaClient({
|
||||
apiKey: 'your-api-key', // Required
|
||||
baseUrl: 'https://custom.api.url', // Optional (default: https://api.sandesh.armco.dev)
|
||||
timeout: 30000, // Optional (default: 30000ms)
|
||||
retries: 3, // Optional (default: 3)
|
||||
});
|
||||
```
|
||||
|
||||
### Mail Configuration
|
||||
|
||||
The `mailConfig` object allows you to set default email settings. **This is a flexible JSON config** - new settings added to Sandesh can be passed here without SDK updates.
|
||||
|
||||
```typescript
|
||||
const mesa = new MesaClient({
|
||||
apiKey: process.env.MESA_API_KEY!,
|
||||
mailConfig: {
|
||||
// Core settings (typed)
|
||||
defaultFrom: { email: 'noreply@example.com', name: 'My App' },
|
||||
defaultReplyTo: { email: 'support@example.com' },
|
||||
trackClicks: true,
|
||||
trackOpens: true,
|
||||
trackingDomain: 'track.example.com',
|
||||
defaultTags: ['transactional'],
|
||||
sandbox: false,
|
||||
ipPool: 'dedicated-pool',
|
||||
sendTimeOptimization: true,
|
||||
|
||||
// Any additional config fields (forward compatible)
|
||||
customField: 'value',
|
||||
newFeatureFlag: true,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
#### Available Mail Config Options
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------|------|---------|-------------|
|
||||
| `defaultFrom` | `EmailAddress` | - | Default sender address |
|
||||
| `defaultReplyTo` | `EmailAddress` | - | Default reply-to address |
|
||||
| `trackClicks` | `boolean` | `true` | Enable click tracking |
|
||||
| `trackOpens` | `boolean` | `true` | Enable open tracking |
|
||||
| `trackingDomain` | `string` | - | Custom tracking domain |
|
||||
| `defaultTags` | `string[]` | - | Tags applied to all emails |
|
||||
| `sandbox` | `boolean` | `false` | Sandbox mode (emails logged, not sent) |
|
||||
| `ipPool` | `string` | - | Dedicated IP pool name |
|
||||
| `sendTimeOptimization` | `boolean` | `false` | AI-powered send time optimization |
|
||||
| `[key: string]` | `unknown` | - | Any additional config for future features |
|
||||
|
||||
## Sending Emails
|
||||
|
||||
### Basic Send
|
||||
|
||||
```typescript
|
||||
const response = await mesa.send({
|
||||
to: 'user@example.com',
|
||||
subject: 'Hello',
|
||||
html: '<p>Welcome to our platform!</p>',
|
||||
text: 'Welcome to our platform!',
|
||||
});
|
||||
|
||||
console.log('Email queued:', response.id);
|
||||
```
|
||||
|
||||
### Multiple Recipients
|
||||
|
||||
```typescript
|
||||
await mesa.send({
|
||||
to: [
|
||||
{ email: 'user1@example.com', name: 'User One' },
|
||||
{ email: 'user2@example.com', name: 'User Two' },
|
||||
],
|
||||
cc: 'manager@example.com',
|
||||
bcc: ['audit@example.com'],
|
||||
subject: 'Team Update',
|
||||
html: '<p>Important announcement...</p>',
|
||||
});
|
||||
```
|
||||
|
||||
### Using Templates
|
||||
|
||||
```typescript
|
||||
await mesa.send({
|
||||
to: 'user@example.com',
|
||||
subject: 'Order Confirmation',
|
||||
templateId: 'order-confirmation',
|
||||
templateData: {
|
||||
orderNumber: 'ORD-12345',
|
||||
items: [
|
||||
{ name: 'Widget', quantity: 2, price: 29.99 },
|
||||
],
|
||||
total: 59.98,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### With Attachments
|
||||
|
||||
```typescript
|
||||
import { readFileSync } from 'fs';
|
||||
|
||||
await mesa.send({
|
||||
to: 'user@example.com',
|
||||
subject: 'Your Invoice',
|
||||
html: '<p>Please find your invoice attached.</p>',
|
||||
attachments: [
|
||||
{
|
||||
filename: 'invoice.pdf',
|
||||
content: readFileSync('./invoice.pdf').toString('base64'),
|
||||
contentType: 'application/pdf',
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
### Scheduled Send
|
||||
|
||||
```typescript
|
||||
await mesa.send({
|
||||
to: 'user@example.com',
|
||||
subject: 'Reminder',
|
||||
html: '<p>Don\'t forget about your appointment!</p>',
|
||||
scheduledAt: '2024-01-15T09:00:00Z',
|
||||
});
|
||||
```
|
||||
|
||||
### Override Tracking per Email
|
||||
|
||||
```typescript
|
||||
await mesa.send({
|
||||
to: 'user@example.com',
|
||||
subject: 'Sensitive Information',
|
||||
html: '<p>Your password reset link...</p>',
|
||||
tracking: {
|
||||
clicks: false, // Disable click tracking for this email
|
||||
opens: false, // Disable open tracking for this email
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Batch Sending
|
||||
|
||||
```typescript
|
||||
const responses = await mesa.sendBatch([
|
||||
{ to: 'user1@example.com', subject: 'Hello User 1', html: '...' },
|
||||
{ to: 'user2@example.com', subject: 'Hello User 2', html: '...' },
|
||||
{ to: 'user3@example.com', subject: 'Hello User 3', html: '...' },
|
||||
]);
|
||||
|
||||
responses.forEach((res) => console.log('Sent:', res.id));
|
||||
```
|
||||
|
||||
## Check Email Status
|
||||
|
||||
```typescript
|
||||
const status = await mesa.getStatus('email-id-here');
|
||||
console.log(status);
|
||||
```
|
||||
|
||||
## Webhook Handling
|
||||
|
||||
### Verify Webhook Signature
|
||||
|
||||
```typescript
|
||||
import express from 'express';
|
||||
|
||||
const app = express();
|
||||
|
||||
app.post('/webhooks/mesa', express.raw({ type: 'application/json' }), (req, res) => {
|
||||
const signature = req.headers['x-mesa-signature'] as string;
|
||||
const payload = req.body.toString();
|
||||
|
||||
if (!mesa.verifyWebhook(payload, signature, process.env.WEBHOOK_SECRET!)) {
|
||||
return res.status(401).send('Invalid signature');
|
||||
}
|
||||
|
||||
const event = mesa.parseWebhook(payload);
|
||||
|
||||
switch (event.event) {
|
||||
case 'email.delivered':
|
||||
console.log('Email delivered:', event.messageId);
|
||||
break;
|
||||
case 'email.bounced':
|
||||
console.log('Email bounced:', event.messageId, event.data);
|
||||
break;
|
||||
case 'email.opened':
|
||||
console.log('Email opened:', event.messageId);
|
||||
break;
|
||||
case 'email.clicked':
|
||||
console.log('Link clicked:', event.data.url);
|
||||
break;
|
||||
}
|
||||
|
||||
res.status(200).send('OK');
|
||||
});
|
||||
```
|
||||
|
||||
## Runtime Configuration Updates
|
||||
|
||||
```typescript
|
||||
// Update mail config at runtime
|
||||
mesa.updateMailConfig({
|
||||
sandbox: true, // Enable sandbox mode
|
||||
});
|
||||
|
||||
// Get current config
|
||||
const config = mesa.getMailConfig();
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```typescript
|
||||
import { MesaClient, MesaAPIError, MesaValidationError } from '@armco/mesa-sdk';
|
||||
|
||||
try {
|
||||
await mesa.send({ /* ... */ });
|
||||
} catch (error) {
|
||||
if (error instanceof MesaValidationError) {
|
||||
console.error('Validation error:', error.message, 'Field:', error.field);
|
||||
} else if (error instanceof MesaAPIError) {
|
||||
console.error('API error:', error.message);
|
||||
console.error('Status:', error.statusCode);
|
||||
console.error('Code:', error.code);
|
||||
console.error('Details:', error.details);
|
||||
} else {
|
||||
console.error('Unknown error:', error);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## TypeScript Support
|
||||
|
||||
Full TypeScript support with exported types:
|
||||
|
||||
```typescript
|
||||
import type {
|
||||
MesaConfig,
|
||||
MailConfig,
|
||||
SendEmailOptions,
|
||||
SendEmailResponse,
|
||||
EmailAddress,
|
||||
Attachment,
|
||||
WebhookEvent,
|
||||
WebhookPayload,
|
||||
} from '@armco/mesa-sdk';
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Recommended setup:
|
||||
|
||||
```bash
|
||||
# .env
|
||||
MESA_API_KEY=your-api-key
|
||||
MESA_API_URL=https://api.sandesh.armco.dev # Optional
|
||||
WEBHOOK_SECRET=your-webhook-secret
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
49
package.json
Normal file
49
package.json
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "@armco/mesa-sdk",
|
||||
"version": "1.0.0",
|
||||
"description": "Official Node.js SDK for Sandesh/Mesa Email Platform",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsup src/index.ts --format cjs,esm --dts",
|
||||
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
||||
"test": "vitest",
|
||||
"lint": "eslint src --ext .ts",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"keywords": [
|
||||
"email",
|
||||
"sandesh",
|
||||
"mesa",
|
||||
"armco",
|
||||
"smtp",
|
||||
"transactional-email",
|
||||
"email-api"
|
||||
],
|
||||
"author": "Armco",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://gitea.armco.dev/Restruct-Corporate-Advantage/mesa-client-sdk"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.10.6",
|
||||
"tsup": "^8.0.1",
|
||||
"typescript": "^5.3.3",
|
||||
"vitest": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
}
|
||||
245
src/client.ts
Normal file
245
src/client.ts
Normal file
@@ -0,0 +1,245 @@
|
||||
import { MesaAPIError, MesaValidationError } from './errors';
|
||||
import type {
|
||||
MesaConfig,
|
||||
MailConfig,
|
||||
SendEmailOptions,
|
||||
SendEmailResponse,
|
||||
EmailAddress,
|
||||
WebhookPayload,
|
||||
} from './types';
|
||||
|
||||
const DEFAULT_BASE_URL = 'https://api.sandesh.armco.dev';
|
||||
const DEFAULT_TIMEOUT = 30000;
|
||||
const DEFAULT_RETRIES = 3;
|
||||
|
||||
/**
|
||||
* Mesa Email SDK Client
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const mesa = new MesaClient({
|
||||
* apiKey: 'your-api-key',
|
||||
* mailConfig: {
|
||||
* defaultFrom: { email: 'noreply@example.com', name: 'My App' },
|
||||
* trackClicks: true,
|
||||
* trackOpens: true,
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* await mesa.send({
|
||||
* to: 'user@example.com',
|
||||
* subject: 'Hello',
|
||||
* html: '<h1>Welcome!</h1>'
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export class MesaClient {
|
||||
private readonly apiKey: string;
|
||||
private readonly baseUrl: string;
|
||||
private readonly timeout: number;
|
||||
private readonly retries: number;
|
||||
private readonly mailConfig: MailConfig;
|
||||
|
||||
constructor(config: MesaConfig) {
|
||||
if (!config.apiKey) {
|
||||
throw new MesaValidationError('API key is required', 'apiKey');
|
||||
}
|
||||
|
||||
this.apiKey = config.apiKey;
|
||||
this.baseUrl = (config.baseUrl || DEFAULT_BASE_URL).replace(/\/$/, '');
|
||||
this.timeout = config.timeout || DEFAULT_TIMEOUT;
|
||||
this.retries = config.retries || DEFAULT_RETRIES;
|
||||
this.mailConfig = config.mailConfig || {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an email
|
||||
*/
|
||||
async send(options: SendEmailOptions): Promise<SendEmailResponse> {
|
||||
const payload = this.buildSendPayload(options);
|
||||
return this.request<SendEmailResponse>('POST', '/v1/emails', payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send multiple emails in a batch
|
||||
*/
|
||||
async sendBatch(emails: SendEmailOptions[]): Promise<SendEmailResponse[]> {
|
||||
const payloads = emails.map((opts) => this.buildSendPayload(opts));
|
||||
return this.request<SendEmailResponse[]>('POST', '/v1/emails/batch', { emails: payloads });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get email status by ID
|
||||
*/
|
||||
async getStatus(emailId: string): Promise<Record<string, unknown>> {
|
||||
return this.request<Record<string, unknown>>('GET', `/v1/emails/${emailId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify webhook signature
|
||||
*/
|
||||
verifyWebhook(payload: string, signature: string, secret: string): boolean {
|
||||
const crypto = require('crypto');
|
||||
const expectedSignature = crypto
|
||||
.createHmac('sha256', secret)
|
||||
.update(payload)
|
||||
.digest('hex');
|
||||
|
||||
return crypto.timingSafeEqual(
|
||||
Buffer.from(signature),
|
||||
Buffer.from(expectedSignature)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse webhook payload
|
||||
*/
|
||||
parseWebhook(body: string | Record<string, unknown>): WebhookPayload {
|
||||
const data = typeof body === 'string' ? JSON.parse(body) : body;
|
||||
return data as WebhookPayload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update mail configuration at runtime
|
||||
*/
|
||||
updateMailConfig(config: Partial<MailConfig>): void {
|
||||
Object.assign(this.mailConfig, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current mail configuration
|
||||
*/
|
||||
getMailConfig(): MailConfig {
|
||||
return { ...this.mailConfig };
|
||||
}
|
||||
|
||||
private buildSendPayload(options: SendEmailOptions): Record<string, unknown> {
|
||||
const from = options.from || this.mailConfig.defaultFrom;
|
||||
if (!from) {
|
||||
throw new MesaValidationError(
|
||||
'Sender address is required. Set "from" in options or "defaultFrom" in mailConfig.',
|
||||
'from'
|
||||
);
|
||||
}
|
||||
|
||||
const tracking = {
|
||||
clicks: options.tracking?.clicks ?? this.mailConfig.trackClicks ?? true,
|
||||
opens: options.tracking?.opens ?? this.mailConfig.trackOpens ?? true,
|
||||
};
|
||||
|
||||
const tags = [
|
||||
...(this.mailConfig.defaultTags || []),
|
||||
...(options.tags || []),
|
||||
];
|
||||
|
||||
return {
|
||||
from: this.normalizeAddress(from),
|
||||
to: this.normalizeAddresses(options.to),
|
||||
subject: options.subject,
|
||||
html: options.html,
|
||||
text: options.text,
|
||||
cc: options.cc ? this.normalizeAddresses(options.cc) : undefined,
|
||||
bcc: options.bcc ? this.normalizeAddresses(options.bcc) : undefined,
|
||||
reply_to: this.normalizeAddress(options.replyTo || this.mailConfig.defaultReplyTo),
|
||||
template_id: options.templateId,
|
||||
template_data: options.templateData,
|
||||
attachments: options.attachments,
|
||||
headers: options.headers,
|
||||
tags: tags.length > 0 ? tags : undefined,
|
||||
metadata: options.metadata,
|
||||
scheduled_at: options.scheduledAt,
|
||||
tracking,
|
||||
sandbox: this.mailConfig.sandbox,
|
||||
ip_pool: this.mailConfig.ipPool,
|
||||
...this.getExtendedConfig(options),
|
||||
};
|
||||
}
|
||||
|
||||
private getExtendedConfig(options: SendEmailOptions): Record<string, unknown> {
|
||||
const knownKeys = new Set([
|
||||
'to', 'from', 'subject', 'html', 'text', 'cc', 'bcc', 'replyTo',
|
||||
'templateId', 'templateData', 'attachments', 'headers', 'tags',
|
||||
'metadata', 'scheduledAt', 'tracking',
|
||||
]);
|
||||
|
||||
const extended: Record<string, unknown> = {};
|
||||
for (const [key, value] of Object.entries(options)) {
|
||||
if (!knownKeys.has(key)) {
|
||||
extended[key] = value;
|
||||
}
|
||||
}
|
||||
return extended;
|
||||
}
|
||||
|
||||
private normalizeAddress(addr: EmailAddress | string | undefined): EmailAddress | undefined {
|
||||
if (!addr) return undefined;
|
||||
if (typeof addr === 'string') {
|
||||
return { email: addr };
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
private normalizeAddresses(
|
||||
addrs: EmailAddress | EmailAddress[] | string | string[]
|
||||
): EmailAddress[] {
|
||||
const arr = Array.isArray(addrs) ? addrs : [addrs];
|
||||
return arr.map((a) => (typeof a === 'string' ? { email: a } : a));
|
||||
}
|
||||
|
||||
private async request<T>(
|
||||
method: string,
|
||||
path: string,
|
||||
body?: unknown
|
||||
): Promise<T> {
|
||||
const url = `${this.baseUrl}${path}`;
|
||||
let lastError: Error | null = null;
|
||||
|
||||
for (let attempt = 0; attempt <= this.retries; attempt++) {
|
||||
try {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
||||
|
||||
const response = await fetch(url, {
|
||||
method,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${this.apiKey}`,
|
||||
'User-Agent': '@armco/mesa-sdk/1.0.0',
|
||||
},
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new MesaAPIError(
|
||||
errorData.message || `Request failed with status ${response.status}`,
|
||||
response.status,
|
||||
errorData.code || 'API_ERROR',
|
||||
errorData.details
|
||||
);
|
||||
}
|
||||
|
||||
return (await response.json()) as T;
|
||||
} catch (error) {
|
||||
lastError = error as Error;
|
||||
|
||||
if (error instanceof MesaAPIError && error.statusCode < 500) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (attempt < this.retries) {
|
||||
await this.sleep(Math.pow(2, attempt) * 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw lastError;
|
||||
}
|
||||
|
||||
private sleep(ms: number): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
}
|
||||
57
src/errors.ts
Normal file
57
src/errors.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Base error class for Mesa SDK
|
||||
*/
|
||||
export class MesaError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = 'MesaError';
|
||||
Object.setPrototypeOf(this, MesaError.prototype);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* API error with status code and response details
|
||||
*/
|
||||
export class MesaAPIError extends MesaError {
|
||||
public readonly statusCode: number;
|
||||
public readonly code: string;
|
||||
public readonly details?: Record<string, unknown>;
|
||||
|
||||
constructor(
|
||||
message: string,
|
||||
statusCode: number,
|
||||
code: string,
|
||||
details?: Record<string, unknown>
|
||||
) {
|
||||
super(message);
|
||||
this.name = 'MesaAPIError';
|
||||
this.statusCode = statusCode;
|
||||
this.code = code;
|
||||
this.details = details;
|
||||
Object.setPrototypeOf(this, MesaAPIError.prototype);
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
name: this.name,
|
||||
message: this.message,
|
||||
statusCode: this.statusCode,
|
||||
code: this.code,
|
||||
details: this.details,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation error for invalid input
|
||||
*/
|
||||
export class MesaValidationError extends MesaError {
|
||||
public readonly field: string;
|
||||
|
||||
constructor(message: string, field: string) {
|
||||
super(message);
|
||||
this.name = 'MesaValidationError';
|
||||
this.field = field;
|
||||
Object.setPrototypeOf(this, MesaValidationError.prototype);
|
||||
}
|
||||
}
|
||||
12
src/index.ts
Normal file
12
src/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export { MesaClient } from './client';
|
||||
export { MesaError, MesaAPIError, MesaValidationError } from './errors';
|
||||
export type {
|
||||
MesaConfig,
|
||||
MailConfig,
|
||||
SendEmailOptions,
|
||||
SendEmailResponse,
|
||||
EmailAddress,
|
||||
Attachment,
|
||||
WebhookEvent,
|
||||
WebhookPayload,
|
||||
} from './types';
|
||||
192
src/types.ts
Normal file
192
src/types.ts
Normal file
@@ -0,0 +1,192 @@
|
||||
/**
|
||||
* Mesa SDK Configuration
|
||||
*/
|
||||
export interface MesaConfig {
|
||||
/** API Key for authentication */
|
||||
apiKey: string;
|
||||
|
||||
/** Base URL of Mesa/Sandesh API (default: https://api.sandesh.armco.dev) */
|
||||
baseUrl?: string;
|
||||
|
||||
/** Request timeout in milliseconds (default: 30000) */
|
||||
timeout?: number;
|
||||
|
||||
/** Number of retry attempts for failed requests (default: 3) */
|
||||
retries?: number;
|
||||
|
||||
/**
|
||||
* Mail configuration object - flexible JSON config for email settings.
|
||||
* New settings added to Sandesh can be passed here without SDK updates.
|
||||
*/
|
||||
mailConfig?: MailConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flexible mail configuration - supports any key-value pairs.
|
||||
* Core fields are typed, additional fields are allowed for extensibility.
|
||||
*/
|
||||
export interface MailConfig {
|
||||
/** Default sender email address */
|
||||
defaultFrom?: EmailAddress;
|
||||
|
||||
/** Default reply-to address */
|
||||
defaultReplyTo?: EmailAddress;
|
||||
|
||||
/** Enable click tracking (default: true) */
|
||||
trackClicks?: boolean;
|
||||
|
||||
/** Enable open tracking (default: true) */
|
||||
trackOpens?: boolean;
|
||||
|
||||
/** Custom tracking domain */
|
||||
trackingDomain?: string;
|
||||
|
||||
/** Default tags to apply to all emails */
|
||||
defaultTags?: string[];
|
||||
|
||||
/** Sandbox mode - emails are logged but not sent */
|
||||
sandbox?: boolean;
|
||||
|
||||
/** IP pool name for dedicated IPs */
|
||||
ipPool?: string;
|
||||
|
||||
/** Send time optimization */
|
||||
sendTimeOptimization?: boolean;
|
||||
|
||||
/** Allow any additional config fields for forward compatibility */
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Email address with optional display name
|
||||
*/
|
||||
export interface EmailAddress {
|
||||
email: string;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Email attachment
|
||||
*/
|
||||
export interface Attachment {
|
||||
/** Filename */
|
||||
filename: string;
|
||||
|
||||
/** Base64 encoded content */
|
||||
content: string;
|
||||
|
||||
/** MIME type (e.g., 'application/pdf') */
|
||||
contentType?: string;
|
||||
|
||||
/** Content-ID for inline attachments */
|
||||
cid?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for sending an email
|
||||
*/
|
||||
export interface SendEmailOptions {
|
||||
/** Recipient(s) */
|
||||
to: EmailAddress | EmailAddress[] | string | string[];
|
||||
|
||||
/** Sender (overrides mailConfig.defaultFrom) */
|
||||
from?: EmailAddress | string;
|
||||
|
||||
/** Email subject */
|
||||
subject: string;
|
||||
|
||||
/** HTML body */
|
||||
html?: string;
|
||||
|
||||
/** Plain text body */
|
||||
text?: string;
|
||||
|
||||
/** CC recipients */
|
||||
cc?: EmailAddress | EmailAddress[] | string | string[];
|
||||
|
||||
/** BCC recipients */
|
||||
bcc?: EmailAddress | EmailAddress[] | string | string[];
|
||||
|
||||
/** Reply-to address */
|
||||
replyTo?: EmailAddress | string;
|
||||
|
||||
/** Template ID to use */
|
||||
templateId?: string;
|
||||
|
||||
/** Template variables */
|
||||
templateData?: Record<string, unknown>;
|
||||
|
||||
/** File attachments */
|
||||
attachments?: Attachment[];
|
||||
|
||||
/** Custom headers */
|
||||
headers?: Record<string, string>;
|
||||
|
||||
/** Tags for categorization */
|
||||
tags?: string[];
|
||||
|
||||
/** Custom metadata */
|
||||
metadata?: Record<string, string>;
|
||||
|
||||
/** Schedule send time (ISO 8601) */
|
||||
scheduledAt?: string;
|
||||
|
||||
/** Override tracking settings for this email */
|
||||
tracking?: {
|
||||
clicks?: boolean;
|
||||
opens?: boolean;
|
||||
};
|
||||
|
||||
/** Allow any additional options for forward compatibility */
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Response from sending an email
|
||||
*/
|
||||
export interface SendEmailResponse {
|
||||
/** Unique message ID */
|
||||
id: string;
|
||||
|
||||
/** Status of the send operation */
|
||||
status: 'queued' | 'sent' | 'scheduled';
|
||||
|
||||
/** Timestamp */
|
||||
timestamp: string;
|
||||
|
||||
/** Additional response data */
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Webhook event types
|
||||
*/
|
||||
export type WebhookEvent =
|
||||
| 'email.sent'
|
||||
| 'email.delivered'
|
||||
| 'email.bounced'
|
||||
| 'email.complained'
|
||||
| 'email.opened'
|
||||
| 'email.clicked'
|
||||
| 'email.unsubscribed'
|
||||
| 'inbound.received';
|
||||
|
||||
/**
|
||||
* Webhook payload
|
||||
*/
|
||||
export interface WebhookPayload {
|
||||
/** Event type */
|
||||
event: WebhookEvent;
|
||||
|
||||
/** Event timestamp */
|
||||
timestamp: string;
|
||||
|
||||
/** Message ID */
|
||||
messageId: string;
|
||||
|
||||
/** Recipient email */
|
||||
recipient?: string;
|
||||
|
||||
/** Event-specific data */
|
||||
data: Record<string, unknown>;
|
||||
}
|
||||
25
tsconfig.json
Normal file
25
tsconfig.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"lib": ["ES2020", "DOM"],
|
||||
"types": ["node"],
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
Reference in New Issue
Block a user