
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
Compose beautiful multilingual emails with XHTML templates and Nodemailer integration. Full TypeScript support included!
| Template | French | English | Use Case |
|---|---|---|---|
password-reset | ✅ | ✅ | Password reset emails |
account-creation | ✅ | ✅ | Welcome new users |
suspicious-login | ✅ | ✅ | Security alerts |
subscription-confirmation | ✅ | ✅ | Newsletter subscriptions |
newsletter-promotion | ✅ | ✅ | Marketing campaigns |
scheduled-maintenance | ✅ | ✅ | System notifications |
npm install composa
import { EmailClient, defaultSubjects, createGmailConfig } from "composa";
const mailer = new EmailClient({
defaultLang: "fr",
subjects: defaultSubjects,
transport: createGmailConfig("your-email@gmail.com", "your-app-password"),
});
// New flow: compileMail → sendMail
const { html, subject } = mailer.compileMail("password-reset", {
lang: "fr",
variables: {
USER_NAME: "Jean Dupont",
USER_EMAIL: "user@example.com",
RESET_URL: "https://yourapp.com/reset/token123",
EXPIRATION_TIME: "24 heures",
},
});
const result = await mailer.sendMail({
to: "user@example.com",
html,
subject,
});
console.log("Email sent:", result);
import { EmailClient, createGmailConfig, type EmailClientOptions, type MailOptions } from "composa";
const options: EmailClientOptions = {
defaultLang: "fr",
defaultFrom: "noreply@myapp.com",
transport: createGmailConfig("your-email@gmail.com", "your-app-password"),
};
const mailer = new EmailClient(options);
const mailOptions: MailOptions = {
to: "user@example.com",
subject: "Welcome!",
html: "<h1>Welcome to our app!</h1>"
};
const result = await mailer.send(mailOptions);
TypeScript Support: Composa includes full TypeScript definitions with IntelliSense, compile-time checking, and type safety. See TYPESCRIPT.md for detailed TypeScript usage examples.
Composa uses a component-like approach where you can compose templates step by step:
compileTemplate - Compile individual templates (like components)compileMail - Combine templates into a complete email (HTML + subject)sendMail - Send the final composed email// Compile individual components
const headerHtml = mailer.compileTemplate("email-header", {
variables: { APP_NAME: "MyApp", USER_NAME: "John" },
});
const itemListHtml = items
.map((item) =>
mailer.compileTemplate("list-item", {
variables: { NAME: item.name, PRICE: item.price },
}),
)
.join("");
// Compose the main email
const { html, subject } = mailer.compileMail("newsletter", {
variables: {
HEADER: headerHtml,
ITEM_LIST: `<ul>${itemListHtml}</ul>`,
FOOTER: mailer.compileTemplate("email-footer", {}),
},
});
// Send the composed email
await mailer.sendMail({ to: "user@example.com", html, subject });
Composa supports multiple email providers with easy configuration:
import { createGmailConfig } from "composa";
const gmailConfig = createGmailConfig("your-email@gmail.com", "your-app-password");
const mailer = new EmailClient({ transport: gmailConfig });
Setup Steps:
import {
createYahooConfig,
createOutlookConfig,
createSendGridConfig,
createMailgunConfig,
createAOLConfig,
createGMXConfig,
createZohoConfig,
createiCloudConfig
} from "composa";
// Yahoo (requires App Password)
const yahoo = createYahooConfig("your-email@yahoo.com", "your-app-password");
// Outlook
const outlook = createOutlookConfig("your-email@outlook.com", "your-password");
// SendGrid
const sendgrid = createSendGridConfig("your-sendgrid-api-key");
// Mailgun
const mailgun = createMailgunConfig("your-mailgun-api-key", "your-domain");
// AOL (requires App Password)
const aol = createAOLConfig("your-email@aol.com", "your-app-password");
// GMX
const gmx = createGMXConfig("your-email@gmx.com", "your-password");
// Zoho
const zoho = createZohoConfig("your-email@zoho.com", "your-password");
// iCloud (requires App Password)
const icloud = createiCloudConfig("your-email@icloud.com", "your-app-password");
const recipients = ["user1@example.com", "user2@example.com", "user3@example.com"];
const results = await mailer.sendBulk(recipients, {
subject: "Weekly Newsletter",
html: "<h1>Newsletter</h1><p>Check out our latest updates!</p>"
});
results.forEach(result => {
if (result.success) {
console.log(`✅ Email sent to ${result.recipient}`);
} else {
console.log(`❌ Failed to send to ${result.recipient}: ${result.error}`);
}
});
const result = await mailer.sendWithRetry({
to: "user@example.com",
subject: "Important Email",
html: "<h1>Important Information</h1>"
}, 3); // Max 3 retries
if (result.success) {
console.log("Email sent successfully:", result.messageId);
} else {
console.log("Email failed after retries:", result.error);
}
// List available templates
const availableTemplates = mailer.listAvailableTemplates("fr");
console.log("Available templates:", availableTemplates);
// Check if template exists
const exists = mailer.templateExists("password-reset", "fr");
console.log("Template exists:", exists);
// Get template information
const info = mailer.getTemplateInfo("password-reset", "fr");
console.log("Template info:", info);
// Register custom template at runtime
const customTemplate = `
<div style="background: {{USER_COLOR}};">
<h1>Hello {{USER_NAME}}!</h1>
<p>Your favorite color is {{USER_COLOR}}.</p>
</div>
`;
mailer.registerTemplateString("user-welcome", customTemplate, "fr");
// Clear template cache
mailer.clearCache();
your-project/
├── templates/
│ ├── en-EN/
│ │ ├── welcome.xhtml
│ │ └── invoice.xhtml
│ └── fr-FR/
│ ├── welcome.xhtml
│ └── invoice.xhtml
You can create your own templates by adding .xhtml files to the templates directory:
// Set custom templates path
const mailer = new EmailClient({
templatesPath: "./my-custom-templates",
defaultLang: "en"
});
Use {{VARIABLE_NAME}} syntax in your templates:
<!-- templates/en-EN/welcome.xhtml -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Welcome to {{APP_NAME}}</title>
</head>
<body>
<h1>Welcome {{USER_NAME}}!</h1>
<p>Thank you for joining {{APP_NAME}}.</p>
<p>Your email: {{USER_EMAIL}}</p>
<p>Visit us at: <a href="{{APP_URL}}">{{APP_URL}}</a></p>
<p>Need help? Contact us at {{SUPPORT_EMAIL}}</p>
</body>
</html>
Register subjects for your custom templates:
// Register subjects for custom templates
mailer.registerSubject("welcome", "en", "Welcome to {{APP_NAME}}, {{USER_NAME}}!");
mailer.registerSubject("welcome", "fr", "Bienvenue sur {{APP_NAME}}, {{USER_NAME}} !");
// Or register multiple subjects at once
mailer.registerSubjects("welcome", {
en: "Welcome to {{APP_NAME}}, {{USER_NAME}}!",
fr: "Bienvenue sur {{APP_NAME}}, {{USER_NAME}} !",
es: "¡Bienvenido a {{APP_NAME}}, {{USER_NAME}}!"
});
Register templates at runtime without files:
// Register a template string in memory
const customTemplate = `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h1 style="color: {{USER_COLOR}};">Hello {{USER_NAME}}!</h1>
<p>Your favorite color is {{USER_COLOR}}.</p>
<p>Account created: {{CREATION_DATE}}</p>
</div>
`;
mailer.registerTemplateString("user-welcome", customTemplate, "en");
// Use the registered template
const { html, subject } = mailer.compileMail("user-welcome", {
variables: {
USER_NAME: "John Doe",
USER_COLOR: "#007bff",
CREATION_DATE: new Date().toLocaleDateString()
}
});
Create reusable template components:
// Base template: templates/en-EN/email-base.xhtml
const baseTemplate = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{{PAGE_TITLE}}</title>
</head>
<body style="font-family: Arial, sans-serif;">
<header style="background: #f8f9fa; padding: 20px;">
<h1>{{APP_NAME}}</h1>
</header>
<main style="padding: 20px;">
{{CONTENT}}
</main>
<footer style="background: #f8f9fa; padding: 20px; text-align: center;">
<p>© {{CURRENT_YEAR}} {{APP_NAME}}. All rights reserved.</p>
</footer>
</body>
</html>
`;
mailer.registerTemplateString("email-base", baseTemplate, "en");
// Content template: templates/en-EN/newsletter-content.xhtml
const contentTemplate = `
<h2>Newsletter - {{NEWSLETTER_TITLE}}</h2>
<p>{{NEWSLETTER_CONTENT}}</p>
<ul>
{{NEWSLETTER_ITEMS}}
</ul>
`;
mailer.registerTemplateString("newsletter-content", contentTemplate, "en");
// Compose the final email
const content = mailer.compileTemplate("newsletter-content", {
variables: {
NEWSLETTER_TITLE: "Weekly Updates",
NEWSLETTER_CONTENT: "Here are this week's highlights:",
NEWSLETTER_ITEMS: items.map(item => `<li>${item.title}</li>`).join("")
}
});
const { html, subject } = mailer.compileMail("email-base", {
variables: {
PAGE_TITLE: "Weekly Newsletter",
CONTENT: content
}
});
// Check if template exists before using
if (mailer.templateExists("custom-template", "en")) {
const { html, subject } = mailer.compileMail("custom-template", {
variables: { USER_NAME: "John" }
});
} else {
console.error("Template 'custom-template' not found for language 'en'");
}
// Get detailed template information
const templateInfo = mailer.getTemplateInfo("custom-template", "en");
console.log("Template info:", {
name: templateInfo.name,
language: templateInfo.lang,
exists: templateInfo.exists,
source: templateInfo.source, // 'memory', 'disk', or 'none'
cached: templateInfo.cached,
path: templateInfo.path
});
const config = await mailer.testConfiguration();
console.log("Configuration test results:");
console.log(`SMTP: ${config.smtp ? '✅ Working' : '❌ Failed'}`);
console.log(`DKIM: ${config.dkim ? '✅ Enabled' : '❌ Disabled'}`);
console.log(`Host: ${config.host}:${config.port} (${config.secure ? 'Secure' : 'Insecure'})`);
console.log(`Sender: ${config.sender}`);
console.log(`Default Language: ${config.defaultLang}`);
You can configure Composa using environment variables:
# SMTP Configuration
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=your-email@gmail.com
SMTP_PASSWORD=your-app-password
# DKIM Configuration (optional)
DKIM_DOMAIN=yourdomain.com
DKIM_SELECTOR=default
DKIM_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----...
# Default sender
SMTP_FROM=noreply@yourdomain.com
// Use environment variables
const mailer = new EmailClient({
defaultLang: "en",
// Transport will be created from environment variables
});
Composa includes an interactive CLI for easy setup:
npx composa setup
This will guide you through:
Composa includes comprehensive TypeScript definitions:
import {
EmailClient,
createGmailConfig,
type EmailClientOptions,
type MailOptions,
type SendResult,
type BulkSendResult
} from "composa";
// Type-safe configuration
const options: EmailClientOptions = {
defaultFrom: "noreply@myapp.com",
defaultLang: "en",
defaults: {
APP_NAME: "My App",
APP_URL: "https://myapp.com"
},
transporter: createGmailConfig("your-email@gmail.com", "your-app-password")
};
const client = new EmailClient(options);
// Type-safe email sending
const mailOptions: MailOptions = {
to: "user@example.com",
subject: "Welcome!",
html: "<h1>Welcome to our app!</h1>"
};
const result: Promise<SendResult> = client.send(mailOptions);
See TYPESCRIPT.md for detailed TypeScript usage examples and advanced patterns.
Check out the examples directory for more usage patterns:
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)This project is licensed under the MIT License - see the LICENSE file for details.
Made with ❤️ by Puparia
FAQs
Compose beautiful multilingual emails with XHTML templates and Nodemailer
The npm package composa receives a total of 3 weekly downloads. As such, composa popularity was classified as not popular.
We found that composa demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.