Aller au contenu

JavaScript vanilla (@frontmail/browser)

@frontmail/browser est un SDK minuscule (< 3,5 ko gzippé) et sans dépendances, pour n’importe quel site web. Son API reprend celle de @emailjs/browser : le code existant ne demande que peu de modifications.

Fenêtre de terminal
npm install @frontmail/browser
import { init, send, sendForm, getStatus } from '@frontmail/browser';

À appeler une fois, avant d’envoyer :

init({
publicKey: 'pk_4f2a…',
blockHeadless: true, // facultatif, voir Protection contre les bots
blockList: { list: ['spam@example.net'], watchVariable: 'email' },
limitRate: { id: 'contact', throttle: 10_000 }, // au plus un envoi toutes les 10 s
retry: { retries: 3, baseDelayMs: 300 }, // par défaut ; `false` désactive les nouvelles tentatives
timeoutMs: 15_000, // par tentative
apiUrl: 'https://api.frontmail.dev', // par défaut
});

init('pk_4f2a…') avec la seule chaîne de la clé fonctionne aussi.

const result = await send('svc_01J9…', 'tpl_order', {
name: 'Julie',
items: [{ title: 'Chambre', qty: 2, price: 89 }],
});
// { messageId: 'msg_…', status: 'queued' | 'held', statusToken: '…', status_code: 202, text: 'OK' }
  • serviceId peut valoir null pour utiliser le service par défaut de l’organisation.
  • params est validé côté serveur par rapport au schéma du modèle.
  • options accepte turnstileToken, attachments, idempotencyKey, locale, signal (AbortSignal), une publicKey propre à l’appel et des garde-fous propres à l’appel (blockHeadless, blockList, limitRate). Une simple chaîne est traitée comme la clé publique, comme dans EmailJS.
  • locale (p. ex. 'de') choisit une langue du modèle ; une langue inconnue se rabat sur la même langue de base, puis sur la langue par défaut. result.locale est la langue dans laquelle l’e-mail a été envoyé.

La promesse est résolue aussi bien pour queued que pour held – un message retenu est accepté et sera envoyé dès que des crédits seront disponibles (voir File d’attente des e-mails retenus). Sinon, elle est rejetée avec une erreur typée (voir Gestion des erreurs).

<form id="contact">
<input name="name" required>
<input name="email" type="email" required>
<textarea name="message"></textarea>
<input type="file" name="attachment">
<div class="cf-turnstile" data-sitekey="YOUR_TURNSTILE_SITE_KEY"></div>
<button>Envoyer</button>
</form>
<script type="module">
import { init, sendForm } from '@frontmail/browser';
init({ publicKey: 'pk_4f2a…' });
document.getElementById('contact').addEventListener('submit', async (event) => {
event.preventDefault();
const { status } = await sendForm('svc_01J9…', 'tpl_contact', event.target);
// ou : sendForm('svc_01J9…', 'tpl_contact', '#contact')
});
</script>

Le formulaire est envoyé en multipart/form-data. Les champs texte deviennent des paramètres (convertis dans les types déclarés), les champs de fichier deviennent des pièces jointes, et un jeton Turnstile présent dans cf-turnstile-response est récupéré automatiquement. Utilisez la clé de site de votre propre widget Turnstile – voir Cloudflare Turnstile.

Les champs qui ne doivent pas quitter votre site sont ignorés : les champs de mot de passe (type="password"), les champs anti-CSRF des frameworks courants (csrfmiddlewaretoken, _token, authenticity_token, __RequestVerificationToken, _csrf, csrf_token) et les noms réservés de l’API (accessToken, privateKey, turnstile_key, …). Choisissez vous-même les champs avec formFields :

await sendForm('svc_01J9…', 'tpl_contact', '#contact', {
formFields: { include: ['name', 'email', 'message'] }, // uniquement ceux-ci (+ jeton Turnstile)
// ou : formFields: { exclude: ['internal_note'] }
});

Pour envoyer le formulaire dans une langue du modèle, passez { locale: 'de' } – elle est envoyée dans le champ réservé fm_locale – ou ajoutez vous-même un champ fm_locale au formulaire (p. ex. un <select name="fm_locale"> de choix de la langue) ; le champ du formulaire est prioritaire. Un champ nommé locale reste un paramètre de modèle ordinaire.

const status = await getStatus(result.messageId, { token: result.statusToken });
// { messageId, status: 'sent', createdAt, sentAt, templateId, serviceId, events: [...] }

Avec une clé publique, vous ne pouvez lire que les messages que vous avez envoyés, grâce au statusToken du résultat de l’envoi. Le SDK l’envoie dans l’en-tête X-Frontmail-Status-Token (et non dans l’URL, pour qu’il ne finisse pas dans les journaux). Voir Statuts des messages.

Épingler la version et utiliser Subresource Integrity

Section intitulée « Épingler la version et utiliser Subresource Integrity »

Une balise <script> s’exécute avec un accès complet à votre page. Chargez une version exacte et laissez le navigateur vérifier son hash : un fichier modifié (CDN ou version compromis) est alors refusé au lieu d’être exécuté.

<script
src="https://cdn.jsdelivr.net/npm/@frontmail/browser@0.1.0/dist/frontmail.umd.js"
integrity="sha384-g2Rxznkz2yb/YYSdVTkXLxZFZ2Nh2+XdhPStxh0stydXFF3gYjDuyHimWIZLJDCc"
crossorigin="anonymous"></script>
  • jsDelivr (npm) : le hash de chaque version figure dans ses notes de version GitHub (et dans le bouton « Copy HTML + SRI » de jsDelivr).
  • CDN Frontmail : https://cdn.frontmail.dev/v<version>/frontmail.umd.js ; sa valeur integrity se trouve dans https://cdn.frontmail.dev/v<version>/sri.json.
  • Pour mettre à jour, changez la version et le hash en même temps (voir le journal des modifications).

Si votre site utilise une CSP, autorisez :

connect-src https://api.frontmail.dev;
script-src https://cdn.jsdelivr.net https://challenges.cloudflare.com; # build CDN (ou cdn.frontmail.dev), Turnstile
frame-src https://challenges.cloudflare.com; # Turnstile