[data-reveal]{opacity:1!important;transform:none!important}
E-mails
Frontend

E-mails

Register server-rendered e-mail templates in dms.email.ts, how their app config and translations are merged, and what a production build ships for them.

E-mail templates are Vue components the frontend server renders to HTML when the backend asks for an e-mail. A module registers them separately from its application, in a dms.email.ts entry.

An e-mail entry exports serverEmailTemplates and may export a plain appConfig object, such as shared branding defaults. It must not import the browser frontend module (dms.frontend.ts): the e-mail bundle is built on its own, for Node.js, and verify-source checks that it holds no browser-only module.

Who asks for a render

The backend signs its render requests, and the frontend server verifies them with the e-mail render secret: the htmlRender.serviceSecret the backend publishes in the manifest, or DMS_HTML_RENDER_SECRET when the manifest carries none. Without either, e-mail renders are refused. The render route itself is the DMS's, declared in its dms.server.ts; a module route renders a template with event.renderEmail(template, props, { locale }).

App config in e-mails

Rendering provides useDmsAppConfig() per render, merged module by module in manifest-priority order: a module's e-mail appConfig first, then its layers' app/app.config.ts. A higher-priority module's layers therefore override a lower one's e-mail appConfig, and a module's e-mail appConfig, the more specific of its two, keeps the values it gives e-mails over its own layers.

Public runtime options come from the module manifest. DMS_CLIENT_BASE_URL overrides public.dms.clientBaseUrl, which e-mails use to build links back to the dashboard.

Markup

The rendered HTML carries no Vue hydration comments (<!--[-->, <!---->, teleport anchors): an e-mail is never hydrated, so the renderer strips them. It keeps every other comment, Outlook's conditional comments included.

Translations

A production build ships the complete merged translation catalogs as JSON under dist/server/locales/, separate from the executable JavaScript. Deploy the whole dist/server directory.

  • Each render loads only its requested language and the English fallback; arbitrary translation keys and module overrides remain available.
  • An unknown language falls back to English.
  • A missing or corrupt catalog for a supported language fails the render rather than silently dropping translations.

In development

ajs dms dev builds the e-mail bundle too, once the dev server is ready, and rebuilds it whenever an e-mail template changes. The build runs in a separate, low-priority process, so page reloads and hot module replacement never wait for it; a render requested while a build is running waits for that build to finish.

Before replacing the bundle, a rebuild waits for active renders to finish. New renders wait behind that rebuild, so neither reads files while they are being replaced. A render that waits more than 60 seconds for the build fails rather than reading an incomplete bundle. A render that never finishes can hold up development rebuilds; production serves the prebuilt bundle and does not use this handoff.

Size budget

verify-source renders eight DMS templates and keeps the e-mail bundle under 256 KiB of JavaScript, reporting the locale data separately:

✔ E-mail contracts hold · 8 templates · 132 KB JS (limit 256 KB) · 217 KB locale data