best-email-builder 0.1.0 → 0.2.0

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.
Files changed (37) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +11 -1
  3. package/dist/{chunk-2PMZMNKA.js → chunk-MTALS5GC.js} +98 -3
  4. package/dist/chunk-MTALS5GC.js.map +1 -0
  5. package/dist/index.js +977 -299
  6. package/dist/index.js.map +1 -1
  7. package/dist/server.js +13 -13
  8. package/dist/server.js.map +1 -1
  9. package/dist/styles.css +1 -1
  10. package/dist/types/adapters/grapesjs/GrapesJsAdapter.d.ts +1 -1
  11. package/dist/types/adapters/grapesjs/unwrapModule.d.ts +18 -0
  12. package/dist/types/blocks/columns/index.d.ts +3 -0
  13. package/dist/types/blocks/heading/index.d.ts +3 -0
  14. package/dist/types/blocks/hero/index.d.ts +3 -0
  15. package/dist/types/blocks/navbar/index.d.ts +6 -0
  16. package/dist/types/blocks/social/index.d.ts +13 -0
  17. package/dist/types/core/EmailEditor.d.ts +12 -4
  18. package/dist/types/core/constants.d.ts +1 -1
  19. package/dist/types/features/templates/TemplateGallery.d.ts +3 -0
  20. package/dist/types/features/templates/gallery.d.ts +41 -0
  21. package/dist/types/features/templates/index.d.ts +2 -0
  22. package/dist/types/features/templates/starter.d.ts +22 -0
  23. package/dist/types/index.d.ts +1 -0
  24. package/dist/types/server/compileMjml.d.ts +3 -3
  25. package/dist/types/server/render.d.ts +4 -4
  26. package/dist/types/server/templates.d.ts +2 -2
  27. package/dist/types/ui/BlockSidebar.d.ts +2 -1
  28. package/dist/types/ui/Drawer.d.ts +19 -0
  29. package/dist/types/ui/EmailBuilder.d.ts +2 -0
  30. package/dist/types/ui/PropertiesPanel.d.ts +2 -1
  31. package/dist/types/ui/Toolbar.d.ts +9 -2
  32. package/dist/types/ui/primitives/button.d.ts +1 -1
  33. package/dist/types/ui/useLayoutWidth.d.ts +10 -0
  34. package/package.json +8 -7
  35. package/dist/chunk-2PMZMNKA.js.map +0 -1
  36. package/dist/chunk-3EOCXTI2.js +0 -652
  37. package/dist/chunk-3EOCXTI2.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server/assets.js","../src/server/errors.js","../src/validation/mjmlValidator.js","../src/validation/quality.js","../src/renderer/prepareMjml.js","../src/server/compileMjml.js","../src/server/render.js","../src/server/sendTest.js","../src/validation/projectValidator.js","../src/server/templates.js","../src/server/variables.js","../src/server/handler.js","../src/server/node.js"],"sourcesContent":["// @ts-check\n/**\n * UPLOADED IMAGES.\n *\n * The type is read from the BYTES (PNG, JPEG, GIF only), never trusted from the\n * file name or the declared type: an SVG renamed `.png` carries scripts and is\n * refused. The public key is derived from the content hash (32 hex characters\n * of SHA-256 + extension): the same file uploaded twice is stored once, and a\n * key reveals nothing and cannot be guessed.\n *\n * Storage is a REPOSITORY supplied by the application (database, object\n * storage…); `createMemoryAssetRepository` is the reference implementation.\n */\n\nimport { createHash } from 'node:crypto';\nimport { identity } from '../i18n/index.js';\nimport { ASSET_KEY_PATTERN, MAX_IMAGE_BYTES, detectImage } from '../validation/images.js';\nimport { HttpError } from './errors.js';\n\n/**\n * @typedef {{\n * key: string,\n * sha256: string,\n * contentType: string,\n * size: number,\n * name: string|null,\n * data: Uint8Array,\n * createdBy: string|null,\n * }} AssetRecord\n *\n * @typedef {{\n * findBySha256: (sha256: string) => Promise<{key: string}|null>,\n * insert: (record: AssetRecord) => Promise<void>,\n * get: (key: string) => Promise<{contentType: string, data: Uint8Array, sha256?: string}|null>,\n * }} AssetRepository\n */\n\n/**\n * @param {Uint8Array} bytes\n * @param {{t?: import('../i18n/index.js').Translate}} [options]\n */\nexport function inspectAsset(bytes, { t = identity } = {}) {\n if (!bytes || bytes.length === 0) throw new HttpError(400, t('Empty file'), 'EMPTY_FILE');\n if (bytes.length > MAX_IMAGE_BYTES) {\n throw new HttpError(413, t('Image too large: {max} MB maximum', { max: MAX_IMAGE_BYTES / (1024 * 1024) }), 'TOO_LARGE');\n }\n const image = detectImage(bytes);\n if (!image) throw new HttpError(415, t('Format rejected: PNG, JPEG or GIF expected'), 'UNSUPPORTED_IMAGE');\n const sha256 = createHash('sha256').update(bytes).digest('hex');\n return { type: image.type, key: `${sha256.slice(0, 32)}.${image.ext}`, sha256 };\n}\n\nfunction cleanName(name) {\n // eslint-disable-next-line no-control-regex\n return String(name || '').replace(/[\\u0000-\\u001f\\u007f<>\"]/g, '').trim().slice(0, 255) || null;\n}\n\n/**\n * @param {{repository: AssetRepository, publicPath: string, t?: import('../i18n/index.js').Translate}} options\n */\nexport function createAssetService({ repository, publicPath, t = identity }) {\n const prefix = publicPath.endsWith('/') ? publicPath : `${publicPath}/`;\n return {\n /**\n * @param {{bytes: Uint8Array, name?: string|null, user?: {id?: string|number, name?: string}|null}} input\n */\n async upload({ bytes, name = null, user = null }) {\n const { type, key, sha256 } = inspectAsset(bytes, { t });\n const result = { key, path: `${prefix}${key}`, contentType: type, size: bytes.length };\n const existing = await repository.findBySha256(sha256);\n if (existing) return { ...result, key: existing.key, path: `${prefix}${existing.key}`, created: false };\n try {\n await repository.insert({\n key,\n sha256,\n contentType: type,\n size: bytes.length,\n name: cleanName(name),\n data: bytes,\n createdBy: user ? String(user.name ?? user.id ?? '') || null : null,\n });\n } catch (error) {\n // Two simultaneous uploads of the same file: the other one won, same image.\n if (await repository.findBySha256(sha256)) return { ...result, created: false };\n throw error;\n }\n return { ...result, created: true };\n },\n\n /** @param {string} key */\n async get(key) {\n if (!ASSET_KEY_PATTERN.test(String(key || ''))) return null;\n return repository.get(key);\n },\n };\n}\n\n/** @returns {AssetRepository & {size: () => number}} */\nexport function createMemoryAssetRepository() {\n /** @type {Map<string, AssetRecord>} */\n const byKey = new Map();\n return {\n async findBySha256(sha256) {\n for (const record of byKey.values()) if (record.sha256 === sha256) return { key: record.key };\n return null;\n },\n async insert(record) {\n if (byKey.has(record.key)) throw new Error('duplicate asset');\n byKey.set(record.key, { ...record, data: new Uint8Array(record.data) });\n },\n async get(key) {\n const record = byKey.get(key);\n return record ? { contentType: record.contentType, data: record.data, sha256: record.sha256 } : null;\n },\n size: () => byKey.size,\n };\n}\n","// @ts-check\n/**\n * Errors with an HTTP status: services throw them, the handler turns them into\n * JSON responses (`{ error, code }`). Any other error is a 500 whose message is\n * not exposed.\n */\n\nexport class HttpError extends Error {\n /**\n * @param {number} status\n * @param {string} message\n * @param {string} [code]\n */\n constructor(status, message, code) {\n super(message);\n this.name = 'HttpError';\n this.status = status;\n this.code = code;\n }\n}\n\n/** Validators and the document reader throw errors carrying a `status`. */\nexport function statusOf(error) {\n const status = Number(/** @type {any} */ (error)?.status);\n return Number.isInteger(status) && status >= 400 && status < 600 ? status : 500;\n}\n","// @ts-check\n/**\n * What an MJML document may contain before the server compiles it.\n *\n * The MJML comes from the browser (editor export). The server does not take it\n * at face value: it compiles it, stores it and will send it to recipients.\n * Three families of rejection:\n *\n * 1. tags that would take the template out of bounds: `mj-include` (would\n * read a server FILE at compile time), `mj-raw` (free HTML, without any of\n * MJML's guarantees), `mj-style` / `mj-font` (external CSS and fonts,\n * outside the builder's scope);\n * 2. anything that executes or embeds: `<script>`, `<iframe>`, `<object>`,\n * `<form>`, `on*` attributes, CSS `expression()`;\n * 3. dangerous URLs (see `urls.js`).\n *\n * Only tags from two closed lists pass: the MJML components produced by the\n * blocks, and the formatting HTML written by the rich text editor inside an\n * `mj-text` or `mj-button`.\n *\n * Dependency-free module (no parser): it can be tested outside a browser, and\n * runs before compilation, which alone truly parses the MJML.\n */\n\nimport { identity } from '../i18n/index.js';\nimport { IMAGE_ATTRIBUTES, LINK_ATTRIBUTES, decodeEntities, isSafeUrl } from './urls.js';\n\n/** Maximum MJML size (UTF-16 units, approximated by the string length). */\nexport const MJML_MAX_LENGTH = 512 * 1024;\n\n/** Accepted MJML components. */\nexport const MJML_TAGS = new Set([\n 'mjml',\n 'mj-head',\n 'mj-body',\n 'mj-attributes',\n 'mj-all',\n 'mj-class',\n 'mj-preview',\n 'mj-title',\n 'mj-breakpoint',\n 'mj-wrapper',\n 'mj-section',\n 'mj-group',\n 'mj-column',\n 'mj-text',\n 'mj-image',\n 'mj-button',\n 'mj-divider',\n 'mj-spacer',\n 'mj-table',\n 'mj-social',\n 'mj-social-element',\n 'mj-navbar',\n 'mj-navbar-link',\n 'mj-hero',\n]);\n\n/** Formatting HTML accepted inside a text or a button. */\nexport const INLINE_TAGS = new Set([\n 'a',\n 'b',\n 'strong',\n 'i',\n 'em',\n 'u',\n 's',\n 'strike',\n 'del',\n 'br',\n 'span',\n 'p',\n 'div',\n 'font',\n 'sup',\n 'sub',\n 'small',\n 'big',\n 'ul',\n 'ol',\n 'li',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'blockquote',\n 'code',\n 'pre',\n 'hr',\n 'img',\n 'table',\n 'tbody',\n 'thead',\n 'tr',\n 'td',\n 'th',\n]);\n\n/**\n * Tags rejected by name (explicit message rather than \"tag not allowed\").\n * Values are English message templates, translated when the issue is built.\n */\nconst FORBIDDEN_TAGS = new Map([\n ['mj-include', 'mj-include would read a file from the server'],\n ['mj-raw', 'mj-raw (free HTML) is not allowed'],\n ['mj-style', 'mj-style (free CSS) is outside the builder'],\n ['mj-font', 'mj-font (external font) is outside the builder'],\n ['script', 'script not allowed'],\n ['iframe', 'iframe not allowed'],\n ['frame', 'frame not allowed'],\n ['object', 'object not allowed'],\n ['embed', 'embed not allowed'],\n ['form', 'form not allowed'],\n ['input', 'form field not allowed'],\n ['button', 'HTML button not allowed (use the Button block)'],\n ['textarea', 'form field not allowed'],\n ['select', 'form field not allowed'],\n ['style', 'style sheet not allowed'],\n ['link', 'external resource not allowed'],\n ['meta', 'meta tag not allowed'],\n ['base', 'base tag not allowed'],\n ['svg', 'SVG not allowed'],\n ['math', 'MathML not allowed'],\n]);\n\n/**\n * @typedef {{code: string, message: string, tag?: string, attribute?: string}} ValidationIssue\n */\n\nconst TAG_RE = /<\\s*(\\/?)\\s*([a-zA-Z][\\w:-]*)([^>]*)>/g;\nconst ATTR_RE = /([^\\s=/>\"']+)(?:\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s\"'>]+)))?/g;\n\n/** @param {string} source */\nfunction* attributesOf(source) {\n ATTR_RE.lastIndex = 0;\n let match;\n while ((match = ATTR_RE.exec(source))) {\n const value = match[2] ?? match[3] ?? match[4] ?? '';\n yield { name: match[1].toLowerCase(), value };\n }\n}\n\n/**\n * Inline style check: no `expression()`, no non-https `url()`.\n * @param {string} style\n */\nfunction styleIsSafe(style) {\n const css = decodeEntities(style).toLowerCase().replace(/\\s+/g, '');\n if (css.includes('expression(') || css.includes('javascript:') || css.includes('behavior:')) return false;\n const urls = css.match(/url\\(([^)]*)\\)/g) || [];\n return urls.every((u) => isSafeUrl(u.slice(4, -1).replace(/^['\"]|['\"]$/g, ''), 'image'));\n}\n\n/**\n * Validates an MJML document. Does not throw: returns the list of issues.\n *\n * @param {string} mjml\n * @param {{t?: import('../i18n/index.js').Translate}} [options]\n * @returns {{ok: boolean, issues: ValidationIssue[]}}\n */\nexport function validateMjml(mjml, { t = identity } = {}) {\n /** @type {ValidationIssue[]} */\n const issues = [];\n const source = typeof mjml === 'string' ? mjml : '';\n\n if (!source.trim()) {\n return { ok: false, issues: [{ code: 'EMPTY', message: t('Empty MJML') }] };\n }\n if (source.length > MJML_MAX_LENGTH) {\n issues.push({\n code: 'TOO_LARGE',\n message: t('MJML too large ({size} KB, maximum {max} KB)', {\n size: Math.round(source.length / 1024),\n max: MJML_MAX_LENGTH / 1024,\n }),\n });\n }\n\n // Comments are not parsed as tags, but an Outlook conditional comment\n // (`<!--[if mso]>`) has no business in MJML written by the builder.\n const withoutComments = source.replace(/<!--([\\s\\S]*?)-->/g, (whole, body) => {\n if (/\\[if\\s/i.test(body)) {\n issues.push({ code: 'CONDITIONAL_COMMENT', message: t('conditional comment not allowed') });\n }\n return '';\n });\n if (/<!\\[CDATA\\[/i.test(withoutComments)) {\n issues.push({ code: 'CDATA', message: t('CDATA section not allowed') });\n }\n\n const trimmed = withoutComments.trim();\n if (!/^<mjml[\\s>]/i.test(trimmed)) {\n issues.push({ code: 'ROOT', message: t('the document must start with <mjml>') });\n }\n if (!/<mj-body[\\s>]/i.test(trimmed)) {\n issues.push({ code: 'BODY', message: t('missing <mj-body>') });\n }\n\n TAG_RE.lastIndex = 0;\n let match;\n while ((match = TAG_RE.exec(withoutComments))) {\n const closing = match[1] === '/';\n const tag = match[2].toLowerCase();\n if (FORBIDDEN_TAGS.has(tag)) {\n const template = FORBIDDEN_TAGS.get(tag);\n issues.push({ code: 'FORBIDDEN_TAG', message: template ? t(template) : tag, tag });\n continue;\n }\n if (!MJML_TAGS.has(tag) && !INLINE_TAGS.has(tag)) {\n issues.push({ code: 'UNKNOWN_TAG', message: t('tag not allowed: <{tag}>', { tag }), tag });\n continue;\n }\n if (closing) continue;\n\n for (const { name, value } of attributesOf(match[3])) {\n if (name.startsWith('on')) {\n issues.push({\n code: 'EVENT_ATTRIBUTE',\n message: t('attribute {name} not allowed', { name }),\n tag,\n attribute: name,\n });\n } else if (LINK_ATTRIBUTES.has(name) && !isSafeUrl(value, 'link')) {\n issues.push({ code: 'UNSAFE_URL', message: t('URL rejected ({name})', { name }), tag, attribute: name });\n } else if (IMAGE_ATTRIBUTES.has(name) && !isSafeUrl(value, 'image')) {\n issues.push({\n code: 'UNSAFE_IMAGE',\n message: t('image rejected ({name}): https only', { name }),\n tag,\n attribute: name,\n });\n } else if (name === 'style' && !styleIsSafe(value)) {\n issues.push({ code: 'UNSAFE_STYLE', message: t('inline style rejected'), tag, attribute: name });\n }\n }\n }\n\n return { ok: issues.length === 0, issues };\n}\n\n/**\n * Validates and throws a readable 422 error listing the first rejections.\n * @param {string} mjml\n * @param {{t?: import('../i18n/index.js').Translate}} [options]\n */\nexport function assertValidMjml(mjml, { t = identity } = {}) {\n const { ok, issues } = validateMjml(mjml, { t });\n if (ok) return;\n const shown = issues.slice(0, 3).map((i) => i.message).join('; ');\n const more = issues.length - 3;\n const error = /** @type {Error & {status?: number, issues?: ValidationIssue[]}} */ (\n new Error(\n more > 0\n ? t('Template rejected: {issues} (+{more})', { issues: shown, more })\n : t('Template rejected: {issues}', { issues: shown })\n )\n );\n error.status = 422;\n error.issues = issues;\n throw error;\n}\n","// @ts-check\n/**\n * QUALITY CHECKS for a template — issues that do not block saving, but that\n * should be known before sending.\n *\n * missing unsubscribe a legal requirement for bulk sending;\n * image without file an empty frame for the recipient;\n * image without alt text Outlook blocks images by default: the alternative\n * text is then all that can be read;\n * button without link a button that leads nowhere;\n * http link flagged as \"not secure\" by some email clients;\n * empty subject the message goes out as \"(no subject)\";\n * size > 102 KB Gmail clips the message and hides the end —\n * including the unsubscribe link.\n *\n * Dependency-free module: used by the preview (server) and tested under Node.\n */\n\nimport { identity } from '../i18n/index.js';\n\n/** Gmail threshold beyond which the message is clipped (\"[Message clipped]\"). */\nexport const GMAIL_CLIP_BYTES = 102 * 1024;\n\n/**\n * @typedef {{code: string, level: 'warning'|'info', message: string, count?: number}} QualityIssue\n */\n\n/** @param {string} source @param {RegExp} pattern */\nfunction count(source, pattern) {\n return (source.match(pattern) || []).length;\n}\n\n/**\n * @param {{mjml: string, html?: string, subject?: string|null}} input\n * @param {{t?: import('../i18n/index.js').Translate}} [options]\n * @returns {QualityIssue[]}\n */\nexport function qualityReport({ mjml, html = '', subject = null }, { t = identity } = {}) {\n const source = String(mjml || '');\n /** @type {QualityIssue[]} */\n const issues = [];\n\n if (!/\\{\\{\\s*unsubscribe\\s*\\}\\}|\\[UNSUBSCRIBE\\]/i.test(source)) {\n issues.push({ code: 'NO_UNSUBSCRIBE', level: 'warning', message: t('Missing unsubscribe link') });\n }\n\n const images = source.match(/<mj-image\\b[^>]*>/gi) || [];\n const noSrc = images.filter((tag) => !/\\ssrc=\"[^\"]+\"/i.test(tag)).length;\n if (noSrc) {\n issues.push({\n code: 'IMAGE_NO_SRC',\n level: 'warning',\n count: noSrc,\n message: t('Image without file ({count})', { count: noSrc }),\n });\n }\n const noAlt = images.filter((tag) => !/\\salt=\"[^\"]+\"/i.test(tag)).length;\n if (noAlt) {\n issues.push({\n code: 'IMAGE_NO_ALT',\n level: 'warning',\n count: noAlt,\n message: t('Image without alt text ({count})', { count: noAlt }),\n });\n }\n\n const noHref = count(source, /<mj-button\\b(?![^>]*\\shref=\"[^\"]+\")[^>]*>/gi);\n if (noHref) {\n issues.push({\n code: 'BUTTON_NO_LINK',\n level: 'warning',\n count: noHref,\n message: t('Button without link ({count})', { count: noHref }),\n });\n }\n\n const http = count(source, /\\shref=\"http:\\/\\//gi);\n if (http) {\n issues.push({\n code: 'HTTP_LINK',\n level: 'info',\n count: http,\n message: t('Insecure http link ({count})', { count: http }),\n });\n }\n\n if (subject !== null && !String(subject).trim()) {\n issues.push({ code: 'NO_SUBJECT', level: 'warning', message: t('Empty subject') });\n }\n\n const bytes = new TextEncoder().encode(String(html || '')).length;\n if (bytes > GMAIL_CLIP_BYTES) {\n issues.push({\n code: 'GMAIL_CLIP',\n level: 'warning',\n message: t('Message is {size} KB: Gmail clips messages over {limit} KB', {\n size: Math.round(bytes / 1024),\n limit: GMAIL_CLIP_BYTES / 1024,\n }),\n });\n }\n\n return issues;\n}\n","// @ts-check\n/**\n * Prepares MJML before compilation — shared by the server preview, the test\n * send and saving.\n *\n * The subject and the preview text (preheader) are METADATA of the template,\n * typed outside the canvas: they do not live in the engine project. They are\n * written here, into `<mj-head>`, at compile time:\n *\n * `<mj-title>` document title (browser tab, some webmails);\n * `<mj-preview>` the grey text that follows the subject in the inbox.\n *\n * `lang` is set when missing: otherwise screen readers read the message with\n * the voice of the system language.\n */\n\nfunction escapeXml(text) {\n return String(text ?? '')\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n\n/**\n * @param {string} mjml\n * @param {{title?: string|null, preheader?: string|null, lang?: string}} [meta]\n * @returns {string}\n */\nexport function prepareMjml(mjml, { title = null, preheader = null, lang = 'en' } = {}) {\n let out = String(mjml || '')\n .replace(/<mj-title(?:\\s[^>]*)?>[\\s\\S]*?<\\/mj-title>/gi, '')\n .replace(/<mj-preview(?:\\s[^>]*)?>[\\s\\S]*?<\\/mj-preview>/gi, '');\n\n out = out.replace(/<mjml(\\s[^>]*)?>/i, (tag, attrs = '') =>\n /\\slang\\s*=/.test(attrs) ? tag : `<mjml lang=\"${escapeXml(lang)}\"${attrs}>`\n );\n\n const head = [\n title && String(title).trim() ? `<mj-title>${escapeXml(String(title).trim())}</mj-title>` : '',\n preheader && String(preheader).trim() ? `<mj-preview>${escapeXml(String(preheader).trim())}</mj-preview>` : '',\n ].join('');\n if (!head) return out;\n\n if (/<mj-head(\\s[^>]*)?>/i.test(out)) {\n return out.replace(/<mj-head(\\s[^>]*)?>/i, (tag) => `${tag}${head}`);\n }\n return out.replace(/<mjml(\\s[^>]*)?>/i, (tag) => `${tag}<mj-head>${head}</mj-head>`);\n}\n","// @ts-check\n/**\n * MJML → HTML compilation, ON THE SERVER — the only one that counts.\n *\n * The canvas compiles too (mjml-browser) to display the email while editing;\n * but the saved HTML, a test's and a campaign's all come from here. The\n * browser never sends HTML to the server.\n *\n * ignoreIncludes `mj-include` would read a server file (the validator\n * already rejects it; belt and braces);\n * fonts: {} no Google font added by default: one more external link in\n * the message, for fonts the blocks do not use;\n * validationLevel soft: MJML deviations come back as warnings. Blocking them\n * would lose work for a mistyped attribute value; safety is\n * enforced upstream by `validation/`.\n */\n\nimport mjml2html from 'mjml';\n\n/** @typedef {{line?: number, message: string, tagName?: string}} CompileWarning */\n\n/**\n * @param {string} mjml\n * @returns {{html: string, warnings: CompileWarning[]}}\n */\nexport function compileMjml(mjml) {\n const compile = /** @type {any} */ (mjml2html);\n const result = compile(String(mjml || ''), {\n validationLevel: 'soft',\n keepComments: false,\n beautify: false,\n minify: false,\n ignoreIncludes: true,\n fonts: {},\n });\n return {\n html: String(result?.html || ''),\n warnings: (result?.errors || []).map((/** @type {any} */ e) => ({\n line: e.line,\n message: String(e.formattedMessage || e.message || e),\n tagName: e.tagName,\n })),\n };\n}\n","// @ts-check\n/**\n * Rendering of a template outside of a campaign — preview and test send.\n *\n * MJML ─► validation ─► compilation (mjml, server) ─► variables ─► links ─► HTML\n *\n * Same validation and compilation steps as saving (`templates.js`): what the\n * preview shows is what will be stored. Variables get SAMPLE values (the real\n * address for a test); the unsubscribe link is inert.\n */\n\nimport { identity } from '../i18n/index.js';\nimport { assertValidMjml } from '../validation/mjmlValidator.js';\nimport { qualityReport } from '../validation/quality.js';\nimport { DEFAULT_VARIABLES } from '../features/variables/catalog.js';\nimport { applyVariables, unknownVariables } from '../features/variables/service.js';\nimport { prepareMjml } from '../renderer/prepareMjml.js';\nimport { absoluteAssetUrls, applyServiceLinks } from '../renderer/links.js';\nimport { compileMjml } from './compileMjml.js';\n\n/**\n * @typedef {import('../i18n/index.js').Translate} Translate\n * @typedef {import('../features/variables/catalog.js').EmailVariable} EmailVariable\n */\n\n/**\n * Validation + compilation, once per template. The quality report also lists\n * tags that are not in the catalog (typos), which would render empty.\n *\n * @param {{mjml: string, subject?: string|null, preheader?: string|null}} input\n * @param {{t?: Translate, lang?: string, variables?: EmailVariable[]}} [options]\n */\nexport function compileTemplate(\n { mjml, subject = null, preheader = null },\n { t = identity, lang = 'en', variables = DEFAULT_VARIABLES } = {}\n) {\n assertValidMjml(mjml, { t });\n const { html, warnings } = compileMjml(prepareMjml(mjml, { title: subject, preheader, lang }));\n const quality = qualityReport({ mjml, html, subject }, { t });\n const unknown = unknownVariables([mjml, subject || '', preheader || ''].join('\\n'), variables);\n if (unknown.length) {\n quality.push({\n code: 'UNKNOWN_VARIABLE',\n level: 'warning',\n count: unknown.length,\n message: t('Unknown variables (rendered empty): {tags}', { tags: unknown.map((k) => `{{${k}}}`).join(', ') }),\n });\n }\n return { compiled: html, warnings, quality };\n}\n\n/**\n * Personalization of compiled HTML, once per recipient. `baseUrl` makes\n * uploaded images absolute — omit it for a preview inside the application,\n * which reads them from its own origin.\n *\n * @param {string} compiled\n * @param {{\n * values: Record<string, string|null|undefined>,\n * unsubscribeUrl: string,\n * baseUrl?: string|null,\n * variables?: EmailVariable[],\n * }} options\n */\nexport function personalize(compiled, { values, unsubscribeUrl, baseUrl = null, variables = DEFAULT_VARIABLES }) {\n const html = applyServiceLinks(applyVariables(compiled, values, variables), { unsubscribeUrl });\n return absoluteAssetUrls(html, baseUrl);\n}\n\n/**\n * Compiles and personalizes in one call — for a single email.\n *\n * @param {{\n * mjml: string,\n * subject?: string|null,\n * preheader?: string|null,\n * values?: Record<string, string|null|undefined>,\n * unsubscribeUrl?: string,\n * baseUrl?: string|null,\n * variables?: EmailVariable[],\n * t?: Translate,\n * lang?: string,\n * }} input\n */\nexport function renderEmail({\n mjml,\n subject = null,\n preheader = null,\n values = {},\n unsubscribeUrl = '#',\n baseUrl = null,\n variables = DEFAULT_VARIABLES,\n t = identity,\n lang = 'en',\n}) {\n const { compiled, warnings, quality } = compileTemplate({ mjml, subject, preheader }, { t, lang, variables });\n return { html: personalize(compiled, { values, unsubscribeUrl, baseUrl, variables }), warnings, quality };\n}\n\n/**\n * Plain-text alternative (clients without HTML, spam filters).\n * @param {string} html\n */\nexport function htmlToText(html) {\n return String(html || '')\n .replace(/<(style|head)[\\s\\S]*?<\\/\\1>/gi, '')\n .replace(/<(br|\\/p|\\/div|\\/tr|\\/h\\d)[^>]*>/gi, '\\n')\n .replace(/<[^>]+>/g, '')\n .replace(/&nbsp;/g, ' ')\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n .replace(/&amp;/g, '&')\n .replace(/[ \\t]+\\n/g, '\\n')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim();\n}\n","// @ts-check\n/**\n * TEST SEND of a template — to proofreading addresses only.\n *\n * A browser preview proves nothing about what Outlook (Word's engine) will do:\n * a test in a real mailbox is the only real check.\n *\n * - same rendering as the preview, one address at a time (`to` is never a\n * list: reviewers do not see each other);\n * - subject prefixed with \"[TEST]\"; the email variable gets the address being\n * served, the others sample values; inert unsubscribe link;\n * - a bounded number of addresses — beyond that it is no longer a test;\n * - one failure does not stop the others: each address gets its result.\n *\n * The transport is INJECTED, which makes this testable without sending.\n */\n\nimport { identity } from '../i18n/index.js';\nimport { DEFAULT_VARIABLES } from '../features/variables/catalog.js';\nimport { applyVariablesToText, sampleValues } from '../features/variables/service.js';\nimport { HttpError } from './errors.js';\nimport { compileTemplate, htmlToText, personalize } from './render.js';\n\nexport const MAX_TEST_RECIPIENTS = 10;\nconst EMAIL_SHAPE = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]{2,}$/;\n\n/**\n * @typedef {{to: string, subject: string, html: string, text: string}} OutgoingEmail\n * @typedef {{send: (email: OutgoingEmail) => Promise<unknown>}} EmailTransport\n */\n\n/**\n * @param {{\n * mjml: string,\n * subject?: string|null,\n * preheader?: string|null,\n * emails: string[]|string,\n * transport: EmailTransport,\n * baseUrl?: string|null,\n * unsubscribeUrl?: string,\n * variables?: import('../features/variables/catalog.js').EmailVariable[],\n * maxRecipients?: number,\n * t?: import('../i18n/index.js').Translate,\n * lang?: string,\n * }} input\n */\nexport async function sendTestEmail({\n mjml,\n subject = null,\n preheader = null,\n emails,\n transport,\n baseUrl = null,\n unsubscribeUrl = '#',\n variables = DEFAULT_VARIABLES,\n maxRecipients = MAX_TEST_RECIPIENTS,\n t = identity,\n lang = 'en',\n}) {\n const list = (Array.isArray(emails) ? emails : String(emails || '').split(/[,;\\s]+/))\n .map((e) => String(e || '').trim().toLowerCase())\n .filter(Boolean);\n const valid = [...new Set(list.filter((e) => EMAIL_SHAPE.test(e)))];\n const invalid = list.filter((e) => !EMAIL_SHAPE.test(e));\n if (!valid.length) throw new HttpError(400, t('No usable test address'), 'NO_RECIPIENT');\n if (valid.length > maxRecipients) {\n throw new HttpError(400, t('{max} test addresses at most', { max: maxRecipients }), 'TOO_MANY_RECIPIENTS');\n }\n\n // Compiled once (validation included), then personalized per address.\n const { compiled, warnings, quality } = compileTemplate({ mjml, subject, preheader }, { t, lang, variables });\n\n const results = [];\n for (const email of valid) {\n try {\n const values = sampleValues(variables, { 'contact.email': email });\n const html = personalize(compiled, { values, unsubscribeUrl, baseUrl, variables });\n const personal = applyVariablesToText(String(subject || '').trim(), values, variables) || t('No subject');\n await transport.send({ to: email, subject: `[TEST] ${personal}`.slice(0, 200), html, text: htmlToText(html) });\n results.push({ email, ok: true, error: null });\n } catch (error) {\n // Short message: a gateway error can carry the whole HTML.\n results.push({ email, ok: false, error: String(/** @type {Error} */ (error)?.message || 'failed').slice(0, 300) });\n }\n }\n\n return {\n sent: results.filter((r) => r.ok).length,\n failed: results.filter((r) => !r.ok).length,\n invalid,\n results,\n warnings,\n quality,\n };\n}\n","// @ts-check\n/**\n * What a PROJECT (the editor's project data) may contain.\n *\n * The project is reloaded as-is into the editor canvas — an iframe of the host\n * page. It does NOT go through the editor engine's HTML parser (which drops\n * scripts and `on*` attributes): it is already structured data. The check must\n * therefore happen here, on save; otherwise a tampered project would run code\n * in the browser of the next user who opens it.\n *\n * The walk is generic (any object, any array) rather than modelled on the\n * engine's format: with another engine or another version, the rule still\n * holds.\n */\n\nimport { readDocument } from '../core/document.js';\nimport { identity } from '../i18n/index.js';\nimport { IMAGE_ATTRIBUTES, LINK_ATTRIBUTES, isSafeUrl } from './urls.js';\n\n/** Maximum size of a project's JSON. */\nexport const PROJECT_MAX_LENGTH = 2 * 1024 * 1024;\n\nconst MAX_DEPTH = 200;\n/** Forbidden HTML/MJML tags (a component's `tagName`). */\nconst DANGEROUS_TAGS = /^(script|iframe|frame|object|embed|form|base|link|meta|svg|math|mj-include|mj-raw)$/i;\n/**\n * Forbidden component types (`type`). Shorter list than the tags: for the\n * engine, `link` is the type of an `<a>` link — unrelated to the `<link>` tag\n * (external style sheet), which is still rejected through its `tagName`.\n */\nconst DANGEROUS_TYPES = /^(script|iframe|frame|object|embed|form|svg|mj-include|mj-raw)$/i;\n\n/**\n * @typedef {import('./mjmlValidator.js').ValidationIssue} ValidationIssue\n */\n\n/**\n * @param {unknown} raw envelope (object or JSON string)\n * @param {{t?: import('../i18n/index.js').Translate}} [options]\n * @returns {{ok: boolean, issues: ValidationIssue[], document: import('../core/document.js').EmailDocument|null, json: string}}\n */\nexport function validateProject(raw, { t = identity } = {}) {\n /** @type {ValidationIssue[]} */\n const issues = [];\n let document;\n try {\n document = readDocument(raw);\n } catch (error) {\n return {\n ok: false,\n issues: [{ code: 'ENVELOPE', message: /** @type {Error} */ (error).message }],\n document: null,\n json: '',\n };\n }\n\n const json = JSON.stringify(document);\n if (json.length > PROJECT_MAX_LENGTH) {\n issues.push({\n code: 'TOO_LARGE',\n message: t('project too large ({size} KB, maximum {max} KB)', {\n size: Math.round(json.length / 1024),\n max: PROJECT_MAX_LENGTH / 1024,\n }),\n });\n }\n\n /** @param {any} node @param {number} depth */\n function walk(node, depth) {\n if (depth > MAX_DEPTH) {\n issues.push({ code: 'TOO_DEEP', message: t('project too deep') });\n return;\n }\n if (Array.isArray(node)) {\n for (const item of node) walk(item, depth + 1);\n return;\n }\n if (!node || typeof node !== 'object') return;\n\n for (const [key, pattern] of /** @type {[string, RegExp][]} */ ([['type', DANGEROUS_TYPES], ['tagName', DANGEROUS_TAGS]])) {\n const value = node[key];\n if (typeof value === 'string' && pattern.test(value)) {\n issues.push({ code: 'FORBIDDEN_TAG', message: t('forbidden element in project: {tag}', { tag: value }), tag: value });\n }\n }\n for (const key of ['script', 'script-export', 'script-props']) {\n const value = node[key];\n if (value && (typeof value !== 'object' || Object.keys(value).length > 0)) {\n issues.push({ code: 'SCRIPT', message: t('component script not allowed') });\n }\n }\n for (const key of ['content', 'components']) {\n const value = node[key];\n if (typeof value === 'string' && /<\\s*(script|iframe|object|embed)\\b|javascript:/i.test(value)) {\n issues.push({ code: 'UNSAFE_CONTENT', message: t('executable content not allowed') });\n }\n }\n const attributes = node.attributes;\n if (attributes && typeof attributes === 'object' && !Array.isArray(attributes)) {\n for (const [name, value] of Object.entries(attributes)) {\n const lower = name.toLowerCase();\n if (lower.startsWith('on')) {\n issues.push({ code: 'EVENT_ATTRIBUTE', message: t('attribute {name} not allowed', { name }), attribute: name });\n } else if (LINK_ATTRIBUTES.has(lower) && !isSafeUrl(String(value ?? ''), 'link')) {\n issues.push({ code: 'UNSAFE_URL', message: t('URL rejected ({name})', { name }), attribute: name });\n } else if (IMAGE_ATTRIBUTES.has(lower) && !isSafeUrl(String(value ?? ''), 'image')) {\n issues.push({ code: 'UNSAFE_IMAGE', message: t('image rejected ({name})', { name }), attribute: name });\n }\n }\n }\n for (const [key, value] of Object.entries(node)) {\n if (key !== 'attributes' && value && typeof value === 'object') walk(value, depth + 1);\n }\n }\n\n walk(document.project, 0);\n return { ok: issues.length === 0, issues, document, json };\n}\n\n/**\n * Validates and throws a readable 422 error listing the first rejections.\n * @param {unknown} raw\n * @param {{t?: import('../i18n/index.js').Translate}} [options]\n */\nexport function assertValidProject(raw, { t = identity } = {}) {\n const result = validateProject(raw, { t });\n if (result.ok && result.document) return { document: result.document, json: result.json };\n const shown = result.issues.slice(0, 3).map((i) => i.message).join('; ');\n const error = /** @type {Error & {status?: number, issues?: ValidationIssue[]}} */ (\n new Error(t('Project rejected: {issues}', { issues: shown }))\n );\n error.status = 422;\n error.issues = result.issues;\n throw error;\n}\n","// @ts-check\n/**\n * TEMPLATES — the server-side business rules, independent of the database.\n *\n * ── THE PROJECT IS THE SOURCE, HTML A RESULT ────────────────────────────────\n * Every write receives the PROJECT (versioned envelope) and its MJML export.\n * The server validates both (`validation/`), compiles the MJML itself and only\n * stores ITS HTML. No route accepts HTML coming from the browser.\n *\n * ── CONCURRENT WRITES ───────────────────────────────────────────────────────\n * Two people can open the same template. Each write carries the `revision`\n * read at load time; the repository refuses the write (409) when someone else\n * saved in the meantime — instead of silently overwriting their work.\n *\n * ── VERSIONS ───────────────────────────────────────────────────────────────\n * One version per EXPLICIT save (`createVersion`), never per autosave. Only the\n * latest `maxVersions` stay listed.\n *\n * Storage is a REPOSITORY supplied by the application (see the typedef below\n * and `createMemoryTemplateRepository`, the reference implementation).\n */\n\nimport { identity } from '../i18n/index.js';\nimport { EMAIL_BUILDER_VERSION, EMAIL_DOCUMENT_SCHEMA_VERSION } from '../core/constants.js';\nimport { assertValidProject } from '../validation/projectValidator.js';\nimport { assertValidMjml } from '../validation/mjmlValidator.js';\nimport { prepareMjml } from '../renderer/prepareMjml.js';\nimport { compileMjml } from './compileMjml.js';\nimport { HttpError } from './errors.js';\n\n/**\n * @typedef {{\n * name: string,\n * description: string|null,\n * subject: string|null,\n * preheader: string|null,\n * projectJson: string,\n * mjml: string,\n * html: string,\n * builderVersion: string,\n * schemaVersion: number,\n * }} TemplateFields\n *\n * @typedef {TemplateFields & {\n * id: string|number,\n * revision: string,\n * createdAt: string|Date|null,\n * updatedAt: string|Date|null,\n * createdBy: string|null,\n * updatedBy: string|null,\n * }} TemplateRecord\n *\n * @typedef {{\n * id: string|number,\n * versionNumber: number,\n * name: string,\n * subject: string|null,\n * preheader: string|null,\n * projectJson: string,\n * mjml: string,\n * createdBy: string|null,\n * createdAt: string|Date|null,\n * }} VersionRecord\n *\n * Every method may be async. `update` MUST compare `expectedRevision` and write\n * atomically (e.g. `UPDATE … WHERE id = ? AND revision = ?`), returning\n * `{ conflict: true, current }` on mismatch and `null` when the id is unknown.\n * `addVersion` numbers versions (max + 1) and keeps the latest `keep` listed.\n *\n * @typedef {{\n * list: () => Promise<TemplateRecord[]>,\n * find: (id: string) => Promise<TemplateRecord|null>,\n * insert: (fields: TemplateFields & {createdBy: string|null}) => Promise<TemplateRecord>,\n * update: (id: string, fields: Partial<TemplateFields> & {updatedBy: string|null},\n * options: {expectedRevision: string|null}) =>\n * Promise<TemplateRecord | {conflict: true, current: TemplateRecord} | null>,\n * remove: (id: string) => Promise<boolean>,\n * addVersion: (templateId: string, version: Omit<VersionRecord, 'id'|'versionNumber'|'createdAt'>,\n * options: {keep: number}) => Promise<{id: string|number, versionNumber: number}>,\n * listVersions: (templateId: string) => Promise<VersionRecord[]>,\n * findVersion: (templateId: string, versionId: string) => Promise<VersionRecord|null>,\n * transaction?: <T>(work: () => Promise<T>) => Promise<T>,\n * }} TemplateRepository\n *\n * @typedef {{id?: string|number, name?: string}|null} Actor\n */\n\n/** Column sizes. */\nexport const LIMITS = { name: 200, description: 1000, subject: 250, preheader: 250 };\n\nexport const MAX_ACTIVE_VERSIONS = 50;\n\nfunction text(value, max) {\n const out = String(value ?? '').trim();\n return out ? out.slice(0, max) : null;\n}\n\nconst iso = (value) => (value ? new Date(value).toISOString() : null);\nconst actorName = (/** @type {Actor} */ user) => (user ? String(user.name ?? user.id ?? '') || null : null);\n\nfunction parseProject(json) {\n try {\n return JSON.parse(json);\n } catch {\n return null;\n }\n}\n\n/** Record → API object. */\nfunction toTemplate(/** @type {TemplateRecord} */ row, { withProject = true } = {}) {\n return {\n id: row.id,\n name: row.name,\n description: row.description,\n subject: row.subject,\n preheader: row.preheader,\n revision: row.revision,\n builderVersion: row.builderVersion,\n schemaVersion: row.schemaVersion,\n createdBy: row.createdBy,\n updatedBy: row.updatedBy,\n createdAt: iso(row.createdAt),\n updatedAt: iso(row.updatedAt),\n ...(withProject ? { document: parseProject(row.projectJson), mjml: row.mjml, html: row.html } : {}),\n };\n}\n\n/** @param {VersionRecord} row */\nfunction toVersion(row, { withProject = false } = {}) {\n return {\n id: row.id,\n versionNumber: row.versionNumber,\n name: row.name,\n subject: row.subject,\n createdBy: row.createdBy,\n createdAt: iso(row.createdAt),\n ...(withProject ? { preheader: row.preheader, document: parseProject(row.projectJson), mjml: row.mjml } : {}),\n };\n}\n\n/**\n * @param {{\n * repository: TemplateRepository,\n * t?: import('../i18n/index.js').Translate,\n * lang?: string,\n * maxVersions?: number,\n * }} options\n */\nexport function createTemplateService({ repository, t = identity, lang = 'en', maxVersions = MAX_ACTIVE_VERSIONS }) {\n const inTransaction = (work) => (repository.transaction ? repository.transaction(work) : work());\n\n /**\n * Validates the project and the MJML, compiles the final HTML.\n * @param {{document: any, mjml: any, subject?: string|null, preheader?: string|null}} input\n */\n function compile({ document, mjml, subject = null, preheader = null }) {\n const { json } = assertValidProject(document, { t });\n assertValidMjml(mjml, { t });\n const { html, warnings } = compileMjml(prepareMjml(mjml, { title: subject, preheader, lang }));\n if (!html) throw new HttpError(422, t('Empty MJML compilation'), 'EMPTY_COMPILATION');\n return { projectJson: json, mjml: String(mjml), html, warnings };\n }\n\n async function findOrThrow(id) {\n const row = await repository.find(String(id));\n if (!row) throw new HttpError(404, t('Template not found'), 'NOT_FOUND');\n return row;\n }\n\n const service = {\n compile,\n\n async list() {\n const rows = await repository.list();\n return rows.map((row) => toTemplate(row, { withProject: false }));\n },\n\n /** @param {string|number} id */\n async get(id) {\n return toTemplate(await findOrThrow(id));\n },\n\n /**\n * Creates a template and its version 1.\n * @param {any} body\n * @param {Actor} [user]\n */\n async create(body, user = null) {\n const name = text(body?.name, LIMITS.name);\n if (!name) throw new HttpError(400, t('Name required'), 'NAME_REQUIRED');\n const subject = text(body?.subject, LIMITS.subject);\n const preheader = text(body?.preheader, LIMITS.preheader);\n const { projectJson, mjml, html, warnings } = compile({ document: body?.document, mjml: body?.mjml, subject, preheader });\n const by = actorName(user);\n\n const row = await inTransaction(async () => {\n const created = await repository.insert({\n name,\n description: text(body?.description, LIMITS.description),\n subject,\n preheader,\n projectJson,\n mjml,\n html,\n builderVersion: EMAIL_BUILDER_VERSION,\n schemaVersion: EMAIL_DOCUMENT_SCHEMA_VERSION,\n createdBy: by,\n });\n await repository.addVersion(\n String(created.id),\n { name, subject, preheader, projectJson, mjml, createdBy: by },\n { keep: maxVersions }\n );\n return created;\n });\n return { template: toTemplate(row), warnings };\n },\n\n /**\n * Saves a template. Fields left undefined keep their stored value.\n * @param {string|number} id\n * @param {any} body\n * @param {Actor} [user]\n */\n async save(id, body, user = null) {\n const current = await findOrThrow(id);\n const name = body?.name !== undefined ? text(body.name, LIMITS.name) : current.name;\n if (!name) throw new HttpError(400, t('Name required'), 'NAME_REQUIRED');\n const subject = body?.subject !== undefined ? text(body.subject, LIMITS.subject) : current.subject;\n const preheader = body?.preheader !== undefined ? text(body.preheader, LIMITS.preheader) : current.preheader;\n const description =\n body?.description !== undefined ? text(body.description, LIMITS.description) : current.description;\n const { projectJson, mjml, html, warnings } = compile({ document: body?.document, mjml: body?.mjml, subject, preheader });\n const by = actorName(user);\n\n const result = await inTransaction(async () => {\n const updated = await repository.update(\n String(id),\n {\n name,\n description,\n subject,\n preheader,\n projectJson,\n mjml,\n html,\n builderVersion: EMAIL_BUILDER_VERSION,\n schemaVersion: EMAIL_DOCUMENT_SCHEMA_VERSION,\n updatedBy: by,\n },\n { expectedRevision: body?.revision ? String(body.revision) : null }\n );\n if (!updated) throw new HttpError(404, t('Template not found'), 'NOT_FOUND');\n if ('conflict' in updated) {\n const who = updated.current.updatedBy;\n throw new HttpError(\n 409,\n who\n ? t('Template changed in the meantime by {user} — reload it before saving', { user: who })\n : t('Template changed in the meantime — reload it before saving'),\n 'CONFLICT'\n );\n }\n const version = body?.createVersion\n ? await repository.addVersion(\n String(id),\n { name, subject, preheader, projectJson, mjml, createdBy: by },\n { keep: maxVersions }\n )\n : null;\n return { row: updated, version };\n });\n\n // The editor keeps its own document: only what changed server side is returned.\n const { document: _omit, ...template } = toTemplate(result.row);\n return { template, warnings, version: result.version };\n },\n\n /** @param {string|number} id @param {Actor} [user] */\n async duplicate(id, user = null) {\n const source = await findOrThrow(id);\n return service.create(\n {\n name: t('{name} (copy)', { name: source.name }).slice(0, LIMITS.name),\n description: source.description,\n subject: source.subject,\n preheader: source.preheader,\n document: parseProject(source.projectJson),\n mjml: source.mjml,\n },\n user\n );\n },\n\n /** @param {string|number} id */\n async remove(id) {\n if (!(await repository.remove(String(id)))) throw new HttpError(404, t('Template not found'), 'NOT_FOUND');\n return { id };\n },\n\n /** @param {string|number} id */\n async listVersions(id) {\n await findOrThrow(id);\n const rows = await repository.listVersions(String(id));\n return rows.map((row) => toVersion(row));\n },\n\n /** @param {string|number} id @param {string|number} versionId */\n async getVersion(id, versionId) {\n const row = await repository.findVersion(String(id), String(versionId));\n if (!row) throw new HttpError(404, t('Version not found'), 'NOT_FOUND');\n return toVersion(row, { withProject: true });\n },\n };\n return service;\n}\n\n/**\n * In-memory repository — reference implementation of the contract, used by the\n * tests and the example application. Data is lost on restart.\n *\n * @returns {TemplateRepository}\n */\nexport function createMemoryTemplateRepository() {\n /** @type {Map<string, TemplateRecord>} */\n const rows = new Map();\n /** @type {Map<string, VersionRecord[]>} */\n const versions = new Map();\n let nextId = 1;\n let nextVersionId = 1;\n let clock = 0;\n // Strictly increasing, even within the same millisecond.\n const stamp = () => {\n clock = Math.max(clock + 1, Date.now());\n return new Date(clock);\n };\n const copy = (row) => (row ? { ...row } : null);\n\n return {\n async list() {\n return [...rows.values()]\n .map(copy)\n .sort((a, b) => new Date(b.updatedAt || b.createdAt).getTime() - new Date(a.updatedAt || a.createdAt).getTime());\n },\n async find(id) {\n return copy(rows.get(id));\n },\n async insert(fields) {\n const at = stamp();\n const row = { ...fields, id: nextId++, revision: at.toISOString(), createdAt: at, updatedAt: null, updatedBy: null };\n rows.set(String(row.id), row);\n return copy(row);\n },\n async update(id, fields, { expectedRevision }) {\n const row = rows.get(id);\n if (!row) return null;\n if (expectedRevision && expectedRevision !== row.revision) return { conflict: true, current: copy(row) };\n const at = stamp();\n Object.assign(row, fields, { updatedAt: at, revision: at.toISOString() });\n return copy(row);\n },\n async remove(id) {\n versions.delete(id);\n return rows.delete(id);\n },\n async addVersion(templateId, version, { keep }) {\n const list = versions.get(templateId) || [];\n const versionNumber = list.reduce((max, v) => Math.max(max, v.versionNumber), 0) + 1;\n const entry = { ...version, id: nextVersionId++, versionNumber, createdAt: stamp() };\n versions.set(templateId, [...list, entry].slice(-keep));\n return { id: entry.id, versionNumber };\n },\n async listVersions(templateId) {\n return [...(versions.get(templateId) || [])].reverse().map(copy);\n },\n async findVersion(templateId, versionId) {\n return copy((versions.get(templateId) || []).find((v) => String(v.id) === versionId));\n },\n };\n}\n","// @ts-check\n/**\n * CUSTOM VARIABLES — created by users in the builder, shared by every template.\n *\n * System variables (the `variables` option of the handler) are read-only; a\n * custom variable can never take one of their keys. A key never changes after\n * creation: templates contain it.\n *\n * Storage is a REPOSITORY supplied by the application;\n * `createMemoryVariableRepository` is the reference implementation.\n */\n\nimport { identity } from '../i18n/index.js';\nimport { DEFAULT_VARIABLES, mergeVariables, validateCustomVariable } from '../features/variables/catalog.js';\nimport { HttpError } from './errors.js';\n\n/**\n * @typedef {import('../features/variables/catalog.js').EmailVariable} EmailVariable\n *\n * @typedef {{\n * list: () => Promise<EmailVariable[]>,\n * insert: (variable: EmailVariable & {createdBy: string|null}) => Promise<void>,\n * update: (key: string, variable: EmailVariable & {updatedBy: string|null}) => Promise<boolean>,\n * remove: (key: string) => Promise<boolean>,\n * }} VariableRepository\n */\n\nconst actorName = (user) => (user ? String(user.name ?? user.id ?? '') || null : null);\n\n/** Public shape (no audit fields). @param {any} row @returns {EmailVariable} */\nconst toVariable = (row) => ({\n key: row.key,\n label: row.label,\n group: row.group,\n sample: row.sample || '',\n fallback: row.fallback || '',\n description: row.description || '',\n custom: true,\n});\n\n/**\n * @param {{\n * repository?: VariableRepository|null,\n * system?: EmailVariable[],\n * t?: import('../i18n/index.js').Translate,\n * }} options\n */\nexport function createVariableService({ repository = null, system = DEFAULT_VARIABLES, t = identity }) {\n const requireRepository = () => {\n if (!repository) throw new HttpError(501, t('Custom variables are not configured'), 'NO_VARIABLE_STORE');\n return repository;\n };\n\n const service = {\n /** Custom variables only. */\n async listCustom() {\n return repository ? (await repository.list()).map(toVariable) : [];\n },\n\n /** System + custom: the catalog used for rendering. */\n async catalog() {\n return mergeVariables(system, await service.listCustom());\n },\n\n /** @param {any} input @param {any} [user] */\n async create(input, user = null) {\n const store = requireRepository();\n const existing = [...system, ...(await service.listCustom())];\n const { variable, errors } = validateCustomVariable(input, { existing, t });\n if (errors) throw Object.assign(new HttpError(400, Object.values(errors)[0], 'INVALID_VARIABLE'), { issues: errors });\n await store.insert({ ...variable, createdBy: actorName(user) });\n return variable;\n },\n\n /** @param {string} key @param {any} input @param {any} [user] */\n async update(key, input, user = null) {\n const store = requireRepository();\n const current = (await service.listCustom()).find((v) => v.key === key);\n if (!current) throw new HttpError(404, t('Variable not found'), 'NOT_FOUND');\n const { variable, errors } = validateCustomVariable({ ...current, ...input, key }, { editing: key, t });\n if (errors) throw Object.assign(new HttpError(400, Object.values(errors)[0], 'INVALID_VARIABLE'), { issues: errors });\n await store.update(key, { ...variable, updatedBy: actorName(user) });\n return variable;\n },\n\n /** @param {string} key */\n async remove(key) {\n if (!(await requireRepository().remove(key))) throw new HttpError(404, t('Variable not found'), 'NOT_FOUND');\n return { key };\n },\n };\n return service;\n}\n\n/** @returns {VariableRepository} */\nexport function createMemoryVariableRepository() {\n /** @type {Map<string, any>} */\n const rows = new Map();\n return {\n async list() {\n return [...rows.values()].map((row) => ({ ...row })).sort((a, b) => a.label.localeCompare(b.label));\n },\n async insert(variable) {\n if (rows.has(variable.key)) throw new Error('duplicate variable');\n rows.set(variable.key, { ...variable, createdAt: new Date() });\n },\n async update(key, variable) {\n if (!rows.has(key)) return false;\n rows.set(key, { ...rows.get(key), ...variable, key, updatedAt: new Date() });\n return true;\n },\n async remove(key) {\n return rows.delete(key);\n },\n };\n}\n","// @ts-check\n/**\n * HTTP HANDLER — every route the builder needs, as one function\n * `(request: Request) => Promise<Response>` (Fetch API).\n *\n * Works as is in Next.js route handlers, Hono, Bun, Deno, Cloudflare Workers;\n * with Express / Node's `http` through `toNodeListener` (see `node.js`).\n *\n * GET {base}/templates read list\n * POST {base}/templates write create, or { duplicateOf }\n * GET {base}/templates/:id read template\n * PUT {base}/templates/:id write save\n * DELETE {base}/templates/:id write delete\n * GET {base}/templates/:id/versions read versions\n * GET {base}/templates/:id/versions/:vid read version with its project\n * POST {base}/render read preview (server rendering)\n * POST {base}/test write test send (needs `transport`)\n * POST {base}/assets write image upload (multipart, field `file`)\n * GET {base}/variables read custom variables\n * POST {base}/variables write create a custom variable\n * PUT {base}/variables/:key write edit (label, group, sample, fallback)\n * DELETE {base}/variables/:key write delete\n * GET {assetsPath}:key PUBLIC uploaded image\n *\n * Images are PUBLIC on purpose: mail clients download them without any\n * session. Their keys are content hashes (unguessable), and they are served\n * with headers that forbid any interpretation other than an image.\n *\n * `authorize(request, { action })` is REQUIRED: it returns the user (any\n * object with `id`/`name`, recorded as author) or null to refuse (401).\n * Throw an `HttpError(403, …)` to refuse with another status.\n */\n\nimport { translatorFor } from '../i18n/index.js';\nimport { DEFAULT_VARIABLES, defineVariables } from '../features/variables/catalog.js';\nimport { applyVariablesToText, sampleValues } from '../features/variables/service.js';\nimport { MAX_IMAGE_BYTES } from '../validation/images.js';\nimport { createAssetService } from './assets.js';\nimport { HttpError, statusOf } from './errors.js';\nimport { compileTemplate, personalize } from './render.js';\nimport { MAX_TEST_RECIPIENTS, sendTestEmail } from './sendTest.js';\nimport { createTemplateService } from './templates.js';\nimport { createVariableService } from './variables.js';\n\n/**\n * @typedef {'read'|'write'} Action\n * @typedef {{id?: string|number, name?: string}} User\n *\n * @typedef {{\n * templates: import('./templates.js').TemplateRepository,\n * assets: import('./assets.js').AssetRepository,\n * authorize: (request: Request, context: {action: Action}) => Promise<User|null|false> | User | null | false,\n * basePath?: string,\n * assetsPath?: string,\n * publicUrl?: string|null,\n * transport?: import('./sendTest.js').EmailTransport|null,\n * variables?: import('../features/variables/catalog.js').EmailVariable[],\n * variableStore?: import('./variables.js').VariableRepository|null,\n * locale?: string,\n * messages?: Record<string, string>,\n * lang?: string,\n * previewUnsubscribeUrl?: string,\n * maxVersions?: number,\n * maxTestRecipients?: number,\n * rateLimits?: {testPerHour?: number, uploadsPerHour?: number},\n * onError?: (error: unknown, request: Request) => void,\n * }} HandlerOptions\n */\n\n/** Largest JSON body accepted (project 2 MB + MJML 512 KB + metadata). */\nconst MAX_JSON_BYTES = 3 * 1024 * 1024;\nconst HOUR = 60 * 60 * 1000;\n\nconst json = (data, status = 200) =>\n new Response(JSON.stringify(data), {\n status,\n headers: { 'Content-Type': 'application/json; charset=utf-8', 'Cache-Control': 'no-store' },\n });\n\n/** Sliding one-hour window per key, in memory (one process). */\nfunction createLimiter(perHour) {\n /** @type {Map<string, number[]>} */\n const hits = new Map();\n return (key) => {\n if (!perHour) return true;\n const now = Date.now();\n const recent = (hits.get(key) || []).filter((at) => now - at < HOUR);\n if (recent.length >= perHour) {\n hits.set(key, recent);\n return false;\n }\n recent.push(now);\n hits.set(key, recent);\n return true;\n };\n}\n\n/**\n * The returned function also exposes `handler.catalog()`: system + custom\n * variables, for the application's own sending pipeline (`personalize`).\n *\n * @param {HandlerOptions} options\n * @returns {((request: Request) => Promise<Response>) & {catalog: () => Promise<import('../features/variables/catalog.js').EmailVariable[]>}}\n */\nexport function createEmailBuilderHandler(options) {\n const {\n templates: templateRepository,\n assets: assetRepository,\n authorize,\n basePath = '/api/email-builder',\n publicUrl = null,\n transport = null,\n locale = 'en',\n messages,\n lang = locale.slice(0, 2),\n previewUnsubscribeUrl = '#',\n maxVersions,\n maxTestRecipients = MAX_TEST_RECIPIENTS,\n rateLimits = {},\n onError = (error) => console.error('[best-email-builder]', error),\n } = options || /** @type {any} */ ({});\n if (typeof authorize !== 'function') throw new Error('createEmailBuilderHandler: `authorize` is required');\n if (!templateRepository || !assetRepository) {\n throw new Error('createEmailBuilderHandler: `templates` and `assets` repositories are required');\n }\n\n const base = basePath.replace(/\\/+$/, '');\n const assetsPath = (options.assetsPath || `${base}/assets/`).replace(/\\/*$/, '/');\n const variables = defineVariables(options.variables || DEFAULT_VARIABLES);\n const t = translatorFor(locale, messages);\n const templates = createTemplateService({ repository: templateRepository, t, lang, maxVersions });\n const assets = createAssetService({ repository: assetRepository, publicPath: assetsPath, t });\n const customVariables = createVariableService({ repository: options.variableStore || null, system: variables, t });\n const testLimiter = createLimiter(rateLimits.testPerHour ?? 20);\n const uploadLimiter = createLimiter(rateLimits.uploadsPerHour ?? 100);\n\n /** @param {Request} request @param {Action} action */\n async function userFor(request, action) {\n const user = await authorize(request, { action });\n if (!user) throw new HttpError(401, t('Authentication required'), 'UNAUTHORIZED');\n return /** @type {User} */ (user);\n }\n\n const userKey = (/** @type {User} */ user) => String(user.id ?? user.name ?? 'anonymous');\n\n /** @param {Request} request */\n async function readJson(request) {\n const length = Number(request.headers.get('content-length') || 0);\n if (length > MAX_JSON_BYTES) throw new HttpError(413, t('Request too large'), 'TOO_LARGE');\n const body = await request.text();\n if (body.length > MAX_JSON_BYTES) throw new HttpError(413, t('Request too large'), 'TOO_LARGE');\n try {\n return body ? JSON.parse(body) : {};\n } catch {\n throw new HttpError(400, t('Invalid JSON'), 'INVALID_JSON');\n }\n }\n\n /** @param {string} key */\n async function serveAsset(key) {\n const asset = await assets.get(key);\n if (!asset) return new Response('Not found', { status: 404, headers: { 'Content-Type': 'text/plain' } });\n return new Response(/** @type {any} */ (asset.data), {\n status: 200,\n headers: {\n 'Content-Type': asset.contentType,\n 'Content-Length': String(asset.data.length),\n 'Cache-Control': 'public, max-age=31536000, immutable',\n ETag: `\"${asset.sha256 || key}\"`,\n 'X-Content-Type-Options': 'nosniff',\n 'Content-Security-Policy': \"default-src 'none'; sandbox\",\n },\n });\n }\n\n /** @param {Request} request */\n async function route(request) {\n const url = new URL(request.url);\n const path = url.pathname;\n const method = request.method.toUpperCase();\n\n if (path.startsWith(assetsPath) && (method === 'GET' || method === 'HEAD')) {\n return serveAsset(decodeURIComponent(path.slice(assetsPath.length)));\n }\n if (path !== base && !path.startsWith(`${base}/`)) return null;\n const parts = path.slice(base.length).split('/').filter(Boolean).map(decodeURIComponent);\n const [section, id, sub, subId] = parts;\n\n if (section === 'templates') {\n if (!id && method === 'GET') {\n await userFor(request, 'read');\n return json({ data: await templates.list() });\n }\n if (!id && method === 'POST') {\n const user = await userFor(request, 'write');\n const body = await readJson(request);\n const result =\n body?.duplicateOf !== undefined && body?.duplicateOf !== null\n ? await templates.duplicate(body.duplicateOf, user)\n : await templates.create(body, user);\n return json({ data: result }, 201);\n }\n if (id && !sub && method === 'GET') {\n await userFor(request, 'read');\n return json({ data: await templates.get(id) });\n }\n if (id && !sub && method === 'PUT') {\n const user = await userFor(request, 'write');\n return json({ data: await templates.save(id, await readJson(request), user) });\n }\n if (id && !sub && method === 'DELETE') {\n await userFor(request, 'write');\n return json({ data: await templates.remove(id) });\n }\n if (id && sub === 'versions' && method === 'GET') {\n await userFor(request, 'read');\n return json({ data: subId ? await templates.getVersion(id, subId) : await templates.listVersions(id) });\n }\n }\n\n if (section === 'render' && !id && method === 'POST') {\n await userFor(request, 'read');\n const body = await readJson(request);\n const catalog = await customVariables.catalog();\n const { compiled, warnings, quality } = compileTemplate(\n { mjml: body?.mjml, subject: body?.subject, preheader: body?.preheader },\n { t, lang, variables: catalog }\n );\n // Same origin as the application: uploaded images stay relative.\n const values = sampleValues(catalog);\n const html = personalize(compiled, { values, unsubscribeUrl: previewUnsubscribeUrl, variables: catalog });\n return json({\n data: {\n html,\n subject: applyVariablesToText(body?.subject || '', values, catalog),\n preheader: applyVariablesToText(body?.preheader || '', values, catalog),\n warnings,\n quality,\n },\n });\n }\n\n if (section === 'test' && !id && method === 'POST') {\n const user = await userFor(request, 'write');\n if (!transport) throw new HttpError(501, t('Test sending is not configured'), 'NO_TRANSPORT');\n if (!testLimiter(userKey(user))) throw new HttpError(429, t('Too many test sends — try again later'), 'RATE_LIMITED');\n const body = await readJson(request);\n const result = await sendTestEmail({\n mjml: body?.mjml,\n subject: body?.subject,\n preheader: body?.preheader,\n emails: body?.emails,\n transport,\n baseUrl: publicUrl,\n unsubscribeUrl: previewUnsubscribeUrl,\n variables: await customVariables.catalog(),\n maxRecipients: maxTestRecipients,\n t,\n lang,\n });\n return json({ data: result });\n }\n\n if (section === 'assets' && !id && method === 'POST') {\n const user = await userFor(request, 'write');\n if (!uploadLimiter(userKey(user))) throw new HttpError(429, t('Too many uploads — try again later'), 'RATE_LIMITED');\n const length = Number(request.headers.get('content-length') || 0);\n if (length > MAX_IMAGE_BYTES + 64 * 1024) {\n throw new HttpError(413, t('Image too large: {max} MB maximum', { max: MAX_IMAGE_BYTES / (1024 * 1024) }), 'TOO_LARGE');\n }\n let form;\n try {\n form = await request.formData();\n } catch {\n throw new HttpError(400, t('Multipart form expected'), 'INVALID_FORM');\n }\n const file = form.get('file');\n if (!file || typeof file === 'string') throw new HttpError(400, t('File missing'), 'FILE_MISSING');\n const bytes = new Uint8Array(await file.arrayBuffer());\n const result = await assets.upload({ bytes, name: file.name, user });\n return json({ data: result }, result.created ? 201 : 200);\n }\n\n if (section === 'variables') {\n if (!id && method === 'GET') {\n await userFor(request, 'read');\n return json({ data: await customVariables.listCustom() });\n }\n if (!id && method === 'POST') {\n const user = await userFor(request, 'write');\n return json({ data: await customVariables.create(await readJson(request), user) }, 201);\n }\n if (id && !sub && method === 'PUT') {\n const user = await userFor(request, 'write');\n return json({ data: await customVariables.update(id, await readJson(request), user) });\n }\n if (id && !sub && method === 'DELETE') {\n await userFor(request, 'write');\n return json({ data: await customVariables.remove(id) });\n }\n }\n\n throw new HttpError(404, t('Route not found'), 'ROUTE_NOT_FOUND');\n }\n\n async function handle(request) {\n try {\n const response = await route(request);\n return response || json({ error: t('Route not found'), code: 'ROUTE_NOT_FOUND' }, 404);\n } catch (error) {\n const status = statusOf(error);\n if (status >= 500 && status !== 501) {\n onError(error, request);\n return json({ error: t('Internal error'), code: 'INTERNAL' }, 500);\n }\n const err = /** @type {any} */ (error);\n return json({ error: err.message, code: err.code || undefined, issues: err.issues || undefined }, status);\n }\n }\n\n handle.catalog = () => customVariables.catalog();\n return handle;\n}\n","// @ts-check\n/**\n * Bridge between a Fetch-API handler and Node's `http` / Express / Connect.\n *\n * app.use(toNodeListener(handler)); // Express\n * http.createServer(toNodeListener(handler)) // plain Node\n *\n * Requests the handler does not own (404 with code ROUTE_NOT_FOUND, outside its\n * routes) are passed to `next()` when there is one.\n */\n\nimport { Readable } from 'node:stream';\n\n/**\n * @param {(request: Request) => Promise<Response>} handler\n * @param {{origin?: string}} [options]\n */\nexport function toNodeListener(handler, { origin } = {}) {\n /**\n * @param {import('node:http').IncomingMessage} req\n * @param {import('node:http').ServerResponse} res\n * @param {(error?: unknown) => void} [next]\n */\n return async function listener(req, res, next) {\n try {\n const host = origin || `http://${req.headers.host || 'localhost'}`;\n const url = new URL(req.url || '/', host);\n const method = (req.method || 'GET').toUpperCase();\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (Array.isArray(value)) value.forEach((v) => headers.append(key, v));\n else if (value !== undefined) headers.set(key, value);\n }\n const hasBody = method !== 'GET' && method !== 'HEAD';\n const request = new Request(url, {\n method,\n headers,\n body: hasBody ? /** @type {any} */ (Readable.toWeb(req)) : undefined,\n // @ts-ignore — required by Node for streamed bodies\n duplex: hasBody ? 'half' : undefined,\n });\n const response = await handler(request);\n if (response.status === 404 && next) {\n const body = await response.clone().json().catch(() => null);\n if (body?.code === 'ROUTE_NOT_FOUND') return next();\n }\n res.statusCode = response.status;\n response.headers.forEach((value, key) => res.setHeader(key, value));\n if (!response.body || method === 'HEAD') {\n res.end();\n return;\n }\n res.end(Buffer.from(await response.arrayBuffer()));\n } catch (error) {\n if (next) next(error);\n else {\n res.statusCode = 500;\n res.end();\n }\n }\n };\n}\n"],"mappings":"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","names":["text","json","json","actorName"]}
1
+ {"version":3,"sources":["../src/server/assets.js","../src/server/errors.js","../src/validation/mjmlValidator.js","../src/validation/quality.js","../src/renderer/prepareMjml.js","../src/server/compileMjml.js","../src/server/render.js","../src/server/sendTest.js","../src/validation/projectValidator.js","../src/server/templates.js","../src/server/variables.js","../src/server/handler.js","../src/server/node.js"],"sourcesContent":["// @ts-check\n/**\n * UPLOADED IMAGES.\n *\n * The type is read from the BYTES (PNG, JPEG, GIF only), never trusted from the\n * file name or the declared type: an SVG renamed `.png` carries scripts and is\n * refused. The public key is derived from the content hash (32 hex characters\n * of SHA-256 + extension): the same file uploaded twice is stored once, and a\n * key reveals nothing and cannot be guessed.\n *\n * Storage is a REPOSITORY supplied by the application (database, object\n * storage…); `createMemoryAssetRepository` is the reference implementation.\n */\n\nimport { createHash } from 'node:crypto';\nimport { identity } from '../i18n/index.js';\nimport { ASSET_KEY_PATTERN, MAX_IMAGE_BYTES, detectImage } from '../validation/images.js';\nimport { HttpError } from './errors.js';\n\n/**\n * @typedef {{\n * key: string,\n * sha256: string,\n * contentType: string,\n * size: number,\n * name: string|null,\n * data: Uint8Array,\n * createdBy: string|null,\n * }} AssetRecord\n *\n * @typedef {{\n * findBySha256: (sha256: string) => Promise<{key: string}|null>,\n * insert: (record: AssetRecord) => Promise<void>,\n * get: (key: string) => Promise<{contentType: string, data: Uint8Array, sha256?: string}|null>,\n * }} AssetRepository\n */\n\n/**\n * @param {Uint8Array} bytes\n * @param {{t?: import('../i18n/index.js').Translate}} [options]\n */\nexport function inspectAsset(bytes, { t = identity } = {}) {\n if (!bytes || bytes.length === 0) throw new HttpError(400, t('Empty file'), 'EMPTY_FILE');\n if (bytes.length > MAX_IMAGE_BYTES) {\n throw new HttpError(413, t('Image too large: {max} MB maximum', { max: MAX_IMAGE_BYTES / (1024 * 1024) }), 'TOO_LARGE');\n }\n const image = detectImage(bytes);\n if (!image) throw new HttpError(415, t('Format rejected: PNG, JPEG or GIF expected'), 'UNSUPPORTED_IMAGE');\n const sha256 = createHash('sha256').update(bytes).digest('hex');\n return { type: image.type, key: `${sha256.slice(0, 32)}.${image.ext}`, sha256 };\n}\n\nfunction cleanName(name) {\n // eslint-disable-next-line no-control-regex\n return String(name || '').replace(/[\\u0000-\\u001f\\u007f<>\"]/g, '').trim().slice(0, 255) || null;\n}\n\n/**\n * @param {{repository: AssetRepository, publicPath: string, t?: import('../i18n/index.js').Translate}} options\n */\nexport function createAssetService({ repository, publicPath, t = identity }) {\n const prefix = publicPath.endsWith('/') ? publicPath : `${publicPath}/`;\n return {\n /**\n * @param {{bytes: Uint8Array, name?: string|null, user?: {id?: string|number, name?: string}|null}} input\n */\n async upload({ bytes, name = null, user = null }) {\n const { type, key, sha256 } = inspectAsset(bytes, { t });\n const result = { key, path: `${prefix}${key}`, contentType: type, size: bytes.length };\n const existing = await repository.findBySha256(sha256);\n if (existing) return { ...result, key: existing.key, path: `${prefix}${existing.key}`, created: false };\n try {\n await repository.insert({\n key,\n sha256,\n contentType: type,\n size: bytes.length,\n name: cleanName(name),\n data: bytes,\n createdBy: user ? String(user.name ?? user.id ?? '') || null : null,\n });\n } catch (error) {\n // Two simultaneous uploads of the same file: the other one won, same image.\n if (await repository.findBySha256(sha256)) return { ...result, created: false };\n throw error;\n }\n return { ...result, created: true };\n },\n\n /** @param {string} key */\n async get(key) {\n if (!ASSET_KEY_PATTERN.test(String(key || ''))) return null;\n return repository.get(key);\n },\n };\n}\n\n/** @returns {AssetRepository & {size: () => number}} */\nexport function createMemoryAssetRepository() {\n /** @type {Map<string, AssetRecord>} */\n const byKey = new Map();\n return {\n async findBySha256(sha256) {\n for (const record of byKey.values()) if (record.sha256 === sha256) return { key: record.key };\n return null;\n },\n async insert(record) {\n if (byKey.has(record.key)) throw new Error('duplicate asset');\n byKey.set(record.key, { ...record, data: new Uint8Array(record.data) });\n },\n async get(key) {\n const record = byKey.get(key);\n return record ? { contentType: record.contentType, data: record.data, sha256: record.sha256 } : null;\n },\n size: () => byKey.size,\n };\n}\n","// @ts-check\n/**\n * Errors with an HTTP status: services throw them, the handler turns them into\n * JSON responses (`{ error, code }`). Any other error is a 500 whose message is\n * not exposed.\n */\n\nexport class HttpError extends Error {\n /**\n * @param {number} status\n * @param {string} message\n * @param {string} [code]\n */\n constructor(status, message, code) {\n super(message);\n this.name = 'HttpError';\n this.status = status;\n this.code = code;\n }\n}\n\n/** Validators and the document reader throw errors carrying a `status`. */\nexport function statusOf(error) {\n const status = Number(/** @type {any} */ (error)?.status);\n return Number.isInteger(status) && status >= 400 && status < 600 ? status : 500;\n}\n","// @ts-check\n/**\n * What an MJML document may contain before the server compiles it.\n *\n * The MJML comes from the browser (editor export). The server does not take it\n * at face value: it compiles it, stores it and will send it to recipients.\n * Three families of rejection:\n *\n * 1. tags that would take the template out of bounds: `mj-include` (would\n * read a server FILE at compile time), `mj-raw` (free HTML, without any of\n * MJML's guarantees), `mj-style` / `mj-font` (external CSS and fonts,\n * outside the builder's scope);\n * 2. anything that executes or embeds: `<script>`, `<iframe>`, `<object>`,\n * `<form>`, `on*` attributes, CSS `expression()`;\n * 3. dangerous URLs (see `urls.js`).\n *\n * Only tags from two closed lists pass: the MJML components produced by the\n * blocks, and the formatting HTML written by the rich text editor inside an\n * `mj-text` or `mj-button`.\n *\n * Dependency-free module (no parser): it can be tested outside a browser, and\n * runs before compilation, which alone truly parses the MJML.\n */\n\nimport { identity } from '../i18n/index.js';\nimport { IMAGE_ATTRIBUTES, LINK_ATTRIBUTES, decodeEntities, isSafeUrl } from './urls.js';\n\n/** Maximum MJML size (UTF-16 units, approximated by the string length). */\nexport const MJML_MAX_LENGTH = 512 * 1024;\n\n/** Accepted MJML components. */\nexport const MJML_TAGS = new Set([\n 'mjml',\n 'mj-head',\n 'mj-body',\n 'mj-attributes',\n 'mj-all',\n 'mj-class',\n 'mj-preview',\n 'mj-title',\n 'mj-breakpoint',\n 'mj-wrapper',\n 'mj-section',\n 'mj-group',\n 'mj-column',\n 'mj-text',\n 'mj-image',\n 'mj-button',\n 'mj-divider',\n 'mj-spacer',\n 'mj-table',\n 'mj-social',\n 'mj-social-element',\n 'mj-navbar',\n 'mj-navbar-link',\n 'mj-hero',\n]);\n\n/** Formatting HTML accepted inside a text or a button. */\nexport const INLINE_TAGS = new Set([\n 'a',\n 'b',\n 'strong',\n 'i',\n 'em',\n 'u',\n 's',\n 'strike',\n 'del',\n 'br',\n 'span',\n 'p',\n 'div',\n 'font',\n 'sup',\n 'sub',\n 'small',\n 'big',\n 'ul',\n 'ol',\n 'li',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'blockquote',\n 'code',\n 'pre',\n 'hr',\n 'img',\n 'table',\n 'tbody',\n 'thead',\n 'tr',\n 'td',\n 'th',\n]);\n\n/**\n * Tags rejected by name (explicit message rather than \"tag not allowed\").\n * Values are English message templates, translated when the issue is built.\n */\nconst FORBIDDEN_TAGS = new Map([\n ['mj-include', 'mj-include would read a file from the server'],\n ['mj-raw', 'mj-raw (free HTML) is not allowed'],\n ['mj-style', 'mj-style (free CSS) is outside the builder'],\n ['mj-font', 'mj-font (external font) is outside the builder'],\n ['script', 'script not allowed'],\n ['iframe', 'iframe not allowed'],\n ['frame', 'frame not allowed'],\n ['object', 'object not allowed'],\n ['embed', 'embed not allowed'],\n ['form', 'form not allowed'],\n ['input', 'form field not allowed'],\n ['button', 'HTML button not allowed (use the Button block)'],\n ['textarea', 'form field not allowed'],\n ['select', 'form field not allowed'],\n ['style', 'style sheet not allowed'],\n ['link', 'external resource not allowed'],\n ['meta', 'meta tag not allowed'],\n ['base', 'base tag not allowed'],\n ['svg', 'SVG not allowed'],\n ['math', 'MathML not allowed'],\n]);\n\n/**\n * @typedef {{code: string, message: string, tag?: string, attribute?: string}} ValidationIssue\n */\n\nconst TAG_RE = /<\\s*(\\/?)\\s*([a-zA-Z][\\w:-]*)([^>]*)>/g;\nconst ATTR_RE = /([^\\s=/>\"']+)(?:\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s\"'>]+)))?/g;\n\n/** @param {string} source */\nfunction* attributesOf(source) {\n ATTR_RE.lastIndex = 0;\n let match;\n while ((match = ATTR_RE.exec(source))) {\n const value = match[2] ?? match[3] ?? match[4] ?? '';\n yield { name: match[1].toLowerCase(), value };\n }\n}\n\n/**\n * Inline style check: no `expression()`, no non-https `url()`.\n * @param {string} style\n */\nfunction styleIsSafe(style) {\n const css = decodeEntities(style).toLowerCase().replace(/\\s+/g, '');\n if (css.includes('expression(') || css.includes('javascript:') || css.includes('behavior:')) return false;\n const urls = css.match(/url\\(([^)]*)\\)/g) || [];\n return urls.every((u) => isSafeUrl(u.slice(4, -1).replace(/^['\"]|['\"]$/g, ''), 'image'));\n}\n\n/**\n * Validates an MJML document. Does not throw: returns the list of issues.\n *\n * @param {string} mjml\n * @param {{t?: import('../i18n/index.js').Translate}} [options]\n * @returns {{ok: boolean, issues: ValidationIssue[]}}\n */\nexport function validateMjml(mjml, { t = identity } = {}) {\n /** @type {ValidationIssue[]} */\n const issues = [];\n const source = typeof mjml === 'string' ? mjml : '';\n\n if (!source.trim()) {\n return { ok: false, issues: [{ code: 'EMPTY', message: t('Empty MJML') }] };\n }\n if (source.length > MJML_MAX_LENGTH) {\n issues.push({\n code: 'TOO_LARGE',\n message: t('MJML too large ({size} KB, maximum {max} KB)', {\n size: Math.round(source.length / 1024),\n max: MJML_MAX_LENGTH / 1024,\n }),\n });\n }\n\n // Comments are not parsed as tags, but an Outlook conditional comment\n // (`<!--[if mso]>`) has no business in MJML written by the builder.\n const withoutComments = source.replace(/<!--([\\s\\S]*?)-->/g, (whole, body) => {\n if (/\\[if\\s/i.test(body)) {\n issues.push({ code: 'CONDITIONAL_COMMENT', message: t('conditional comment not allowed') });\n }\n return '';\n });\n if (/<!\\[CDATA\\[/i.test(withoutComments)) {\n issues.push({ code: 'CDATA', message: t('CDATA section not allowed') });\n }\n\n const trimmed = withoutComments.trim();\n if (!/^<mjml[\\s>]/i.test(trimmed)) {\n issues.push({ code: 'ROOT', message: t('the document must start with <mjml>') });\n }\n if (!/<mj-body[\\s>]/i.test(trimmed)) {\n issues.push({ code: 'BODY', message: t('missing <mj-body>') });\n }\n\n TAG_RE.lastIndex = 0;\n let match;\n while ((match = TAG_RE.exec(withoutComments))) {\n const closing = match[1] === '/';\n const tag = match[2].toLowerCase();\n if (FORBIDDEN_TAGS.has(tag)) {\n const template = FORBIDDEN_TAGS.get(tag);\n issues.push({ code: 'FORBIDDEN_TAG', message: template ? t(template) : tag, tag });\n continue;\n }\n if (!MJML_TAGS.has(tag) && !INLINE_TAGS.has(tag)) {\n issues.push({ code: 'UNKNOWN_TAG', message: t('tag not allowed: <{tag}>', { tag }), tag });\n continue;\n }\n if (closing) continue;\n\n for (const { name, value } of attributesOf(match[3])) {\n if (name.startsWith('on')) {\n issues.push({\n code: 'EVENT_ATTRIBUTE',\n message: t('attribute {name} not allowed', { name }),\n tag,\n attribute: name,\n });\n } else if (LINK_ATTRIBUTES.has(name) && !isSafeUrl(value, 'link')) {\n issues.push({ code: 'UNSAFE_URL', message: t('URL rejected ({name})', { name }), tag, attribute: name });\n } else if (IMAGE_ATTRIBUTES.has(name) && !isSafeUrl(value, 'image')) {\n issues.push({\n code: 'UNSAFE_IMAGE',\n message: t('image rejected ({name}): https only', { name }),\n tag,\n attribute: name,\n });\n } else if (name === 'style' && !styleIsSafe(value)) {\n issues.push({ code: 'UNSAFE_STYLE', message: t('inline style rejected'), tag, attribute: name });\n }\n }\n }\n\n return { ok: issues.length === 0, issues };\n}\n\n/**\n * Validates and throws a readable 422 error listing the first rejections.\n * @param {string} mjml\n * @param {{t?: import('../i18n/index.js').Translate}} [options]\n */\nexport function assertValidMjml(mjml, { t = identity } = {}) {\n const { ok, issues } = validateMjml(mjml, { t });\n if (ok) return;\n const shown = issues.slice(0, 3).map((i) => i.message).join('; ');\n const more = issues.length - 3;\n const error = /** @type {Error & {status?: number, issues?: ValidationIssue[]}} */ (\n new Error(\n more > 0\n ? t('Template rejected: {issues} (+{more})', { issues: shown, more })\n : t('Template rejected: {issues}', { issues: shown })\n )\n );\n error.status = 422;\n error.issues = issues;\n throw error;\n}\n","// @ts-check\n/**\n * QUALITY CHECKS for a template — issues that do not block saving, but that\n * should be known before sending.\n *\n * missing unsubscribe a legal requirement for bulk sending;\n * image without file an empty frame for the recipient;\n * image without alt text Outlook blocks images by default: the alternative\n * text is then all that can be read;\n * button without link a button that leads nowhere;\n * http link flagged as \"not secure\" by some email clients;\n * empty subject the message goes out as \"(no subject)\";\n * size > 102 KB Gmail clips the message and hides the end —\n * including the unsubscribe link.\n *\n * Dependency-free module: used by the preview (server) and tested under Node.\n */\n\nimport { identity } from '../i18n/index.js';\n\n/** Gmail threshold beyond which the message is clipped (\"[Message clipped]\"). */\nexport const GMAIL_CLIP_BYTES = 102 * 1024;\n\n/**\n * @typedef {{code: string, level: 'warning'|'info', message: string, count?: number}} QualityIssue\n */\n\n/** @param {string} source @param {RegExp} pattern */\nfunction count(source, pattern) {\n return (source.match(pattern) || []).length;\n}\n\n/**\n * @param {{mjml: string, html?: string, subject?: string|null}} input\n * @param {{t?: import('../i18n/index.js').Translate}} [options]\n * @returns {QualityIssue[]}\n */\nexport function qualityReport({ mjml, html = '', subject = null }, { t = identity } = {}) {\n const source = String(mjml || '');\n /** @type {QualityIssue[]} */\n const issues = [];\n\n if (!/\\{\\{\\s*unsubscribe\\s*\\}\\}|\\[UNSUBSCRIBE\\]/i.test(source)) {\n issues.push({ code: 'NO_UNSUBSCRIBE', level: 'warning', message: t('Missing unsubscribe link') });\n }\n\n const images = source.match(/<mj-image\\b[^>]*>/gi) || [];\n const noSrc = images.filter((tag) => !/\\ssrc=\"[^\"]+\"/i.test(tag)).length;\n if (noSrc) {\n issues.push({\n code: 'IMAGE_NO_SRC',\n level: 'warning',\n count: noSrc,\n message: t('Image without file ({count})', { count: noSrc }),\n });\n }\n const noAlt = images.filter((tag) => !/\\salt=\"[^\"]+\"/i.test(tag)).length;\n if (noAlt) {\n issues.push({\n code: 'IMAGE_NO_ALT',\n level: 'warning',\n count: noAlt,\n message: t('Image without alt text ({count})', { count: noAlt }),\n });\n }\n\n const noHref = count(source, /<mj-button\\b(?![^>]*\\shref=\"[^\"]+\")[^>]*>/gi);\n if (noHref) {\n issues.push({\n code: 'BUTTON_NO_LINK',\n level: 'warning',\n count: noHref,\n message: t('Button without link ({count})', { count: noHref }),\n });\n }\n\n const http = count(source, /\\shref=\"http:\\/\\//gi);\n if (http) {\n issues.push({\n code: 'HTTP_LINK',\n level: 'info',\n count: http,\n message: t('Insecure http link ({count})', { count: http }),\n });\n }\n\n if (subject !== null && !String(subject).trim()) {\n issues.push({ code: 'NO_SUBJECT', level: 'warning', message: t('Empty subject') });\n }\n\n const bytes = new TextEncoder().encode(String(html || '')).length;\n if (bytes > GMAIL_CLIP_BYTES) {\n issues.push({\n code: 'GMAIL_CLIP',\n level: 'warning',\n message: t('Message is {size} KB: Gmail clips messages over {limit} KB', {\n size: Math.round(bytes / 1024),\n limit: GMAIL_CLIP_BYTES / 1024,\n }),\n });\n }\n\n return issues;\n}\n","// @ts-check\n/**\n * Prepares MJML before compilation — shared by the server preview, the test\n * send and saving.\n *\n * The subject and the preview text (preheader) are METADATA of the template,\n * typed outside the canvas: they do not live in the engine project. They are\n * written here, into `<mj-head>`, at compile time:\n *\n * `<mj-title>` document title (browser tab, some webmails);\n * `<mj-preview>` the grey text that follows the subject in the inbox.\n *\n * `lang` is set when missing: otherwise screen readers read the message with\n * the voice of the system language.\n */\n\nfunction escapeXml(text) {\n return String(text ?? '')\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n\n/**\n * @param {string} mjml\n * @param {{title?: string|null, preheader?: string|null, lang?: string}} [meta]\n * @returns {string}\n */\nexport function prepareMjml(mjml, { title = null, preheader = null, lang = 'en' } = {}) {\n let out = String(mjml || '')\n .replace(/<mj-title(?:\\s[^>]*)?>[\\s\\S]*?<\\/mj-title>/gi, '')\n .replace(/<mj-preview(?:\\s[^>]*)?>[\\s\\S]*?<\\/mj-preview>/gi, '');\n\n out = out.replace(/<mjml(\\s[^>]*)?>/i, (tag, attrs = '') =>\n /\\slang\\s*=/.test(attrs) ? tag : `<mjml lang=\"${escapeXml(lang)}\"${attrs}>`\n );\n\n const head = [\n title && String(title).trim() ? `<mj-title>${escapeXml(String(title).trim())}</mj-title>` : '',\n preheader && String(preheader).trim() ? `<mj-preview>${escapeXml(String(preheader).trim())}</mj-preview>` : '',\n ].join('');\n if (!head) return out;\n\n if (/<mj-head(\\s[^>]*)?>/i.test(out)) {\n return out.replace(/<mj-head(\\s[^>]*)?>/i, (tag) => `${tag}${head}`);\n }\n return out.replace(/<mjml(\\s[^>]*)?>/i, (tag) => `${tag}<mj-head>${head}</mj-head>`);\n}\n","// @ts-check\n/**\n * MJML → HTML compilation, ON THE SERVER — the only one that counts.\n *\n * The canvas compiles too (mjml-browser) to display the email while editing;\n * but the saved HTML, a test's and a campaign's all come from here. The\n * browser never sends HTML to the server.\n *\n * ignoreIncludes `mj-include` would read a server file (the validator\n * already rejects it; belt and braces);\n * fonts: {} no Google font added by default: one more external link in\n * the message, for fonts the blocks do not use;\n * validationLevel soft: MJML deviations come back as warnings. Blocking them\n * would lose work for a mistyped attribute value; safety is\n * enforced upstream by `validation/`.\n */\n\nimport mjml2html from 'mjml';\n\n/** @typedef {{line?: number, message: string, tagName?: string}} CompileWarning */\n\n/**\n * @param {string} mjml\n * @returns {Promise<{html: string, warnings: CompileWarning[]}>}\n */\nexport async function compileMjml(mjml) {\n const compile = /** @type {any} */ (mjml2html);\n // MJML 5 compiles asynchronously; `await` also accepts a synchronous result.\n const result = await compile(String(mjml || ''), {\n validationLevel: 'soft',\n keepComments: false,\n beautify: false,\n minify: false,\n ignoreIncludes: true,\n fonts: {},\n });\n return {\n html: String(result?.html || ''),\n warnings: (result?.errors || []).map((/** @type {any} */ e) => ({\n line: e.line,\n message: String(e.formattedMessage || e.message || e),\n tagName: e.tagName,\n })),\n };\n}\n","// @ts-check\n/**\n * Rendering of a template outside of a campaign — preview and test send.\n *\n * MJML ─► validation ─► compilation (mjml, server) ─► variables ─► links ─► HTML\n *\n * Same validation and compilation steps as saving (`templates.js`): what the\n * preview shows is what will be stored. Variables get SAMPLE values (the real\n * address for a test); the unsubscribe link is inert.\n */\n\nimport { identity } from '../i18n/index.js';\nimport { assertValidMjml } from '../validation/mjmlValidator.js';\nimport { qualityReport } from '../validation/quality.js';\nimport { DEFAULT_VARIABLES } from '../features/variables/catalog.js';\nimport { applyVariables, unknownVariables } from '../features/variables/service.js';\nimport { prepareMjml } from '../renderer/prepareMjml.js';\nimport { absoluteAssetUrls, applyServiceLinks } from '../renderer/links.js';\nimport { compileMjml } from './compileMjml.js';\n\n/**\n * @typedef {import('../i18n/index.js').Translate} Translate\n * @typedef {import('../features/variables/catalog.js').EmailVariable} EmailVariable\n */\n\n/**\n * Validation + compilation, once per template. The quality report also lists\n * tags that are not in the catalog (typos), which would render empty.\n *\n * @param {{mjml: string, subject?: string|null, preheader?: string|null}} input\n * @param {{t?: Translate, lang?: string, variables?: EmailVariable[]}} [options]\n */\nexport async function compileTemplate(\n { mjml, subject = null, preheader = null },\n { t = identity, lang = 'en', variables = DEFAULT_VARIABLES } = {}\n) {\n assertValidMjml(mjml, { t });\n const { html, warnings } = await compileMjml(prepareMjml(mjml, { title: subject, preheader, lang }));\n const quality = qualityReport({ mjml, html, subject }, { t });\n const unknown = unknownVariables([mjml, subject || '', preheader || ''].join('\\n'), variables);\n if (unknown.length) {\n quality.push({\n code: 'UNKNOWN_VARIABLE',\n level: 'warning',\n count: unknown.length,\n message: t('Unknown variables (rendered empty): {tags}', { tags: unknown.map((k) => `{{${k}}}`).join(', ') }),\n });\n }\n return { compiled: html, warnings, quality };\n}\n\n/**\n * Personalization of compiled HTML, once per recipient. `baseUrl` makes\n * uploaded images absolute — omit it for a preview inside the application,\n * which reads them from its own origin.\n *\n * @param {string} compiled\n * @param {{\n * values: Record<string, string|null|undefined>,\n * unsubscribeUrl: string,\n * baseUrl?: string|null,\n * variables?: EmailVariable[],\n * }} options\n */\nexport function personalize(compiled, { values, unsubscribeUrl, baseUrl = null, variables = DEFAULT_VARIABLES }) {\n const html = applyServiceLinks(applyVariables(compiled, values, variables), { unsubscribeUrl });\n return absoluteAssetUrls(html, baseUrl);\n}\n\n/**\n * Compiles and personalizes in one call — for a single email.\n *\n * @param {{\n * mjml: string,\n * subject?: string|null,\n * preheader?: string|null,\n * values?: Record<string, string|null|undefined>,\n * unsubscribeUrl?: string,\n * baseUrl?: string|null,\n * variables?: EmailVariable[],\n * t?: Translate,\n * lang?: string,\n * }} input\n */\nexport async function renderEmail({\n mjml,\n subject = null,\n preheader = null,\n values = {},\n unsubscribeUrl = '#',\n baseUrl = null,\n variables = DEFAULT_VARIABLES,\n t = identity,\n lang = 'en',\n}) {\n const { compiled, warnings, quality } = await compileTemplate({ mjml, subject, preheader }, { t, lang, variables });\n return { html: personalize(compiled, { values, unsubscribeUrl, baseUrl, variables }), warnings, quality };\n}\n\n/**\n * Plain-text alternative (clients without HTML, spam filters).\n * @param {string} html\n */\nexport function htmlToText(html) {\n return String(html || '')\n .replace(/<(style|head)[\\s\\S]*?<\\/\\1>/gi, '')\n .replace(/<(br|\\/p|\\/div|\\/tr|\\/h\\d)[^>]*>/gi, '\\n')\n .replace(/<[^>]+>/g, '')\n .replace(/&nbsp;/g, ' ')\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n .replace(/&amp;/g, '&')\n .replace(/[ \\t]+\\n/g, '\\n')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim();\n}\n","// @ts-check\n/**\n * TEST SEND of a template — to proofreading addresses only.\n *\n * A browser preview proves nothing about what Outlook (Word's engine) will do:\n * a test in a real mailbox is the only real check.\n *\n * - same rendering as the preview, one address at a time (`to` is never a\n * list: reviewers do not see each other);\n * - subject prefixed with \"[TEST]\"; the email variable gets the address being\n * served, the others sample values; inert unsubscribe link;\n * - a bounded number of addresses — beyond that it is no longer a test;\n * - one failure does not stop the others: each address gets its result.\n *\n * The transport is INJECTED, which makes this testable without sending.\n */\n\nimport { identity } from '../i18n/index.js';\nimport { DEFAULT_VARIABLES } from '../features/variables/catalog.js';\nimport { applyVariablesToText, sampleValues } from '../features/variables/service.js';\nimport { HttpError } from './errors.js';\nimport { compileTemplate, htmlToText, personalize } from './render.js';\n\nexport const MAX_TEST_RECIPIENTS = 10;\nconst EMAIL_SHAPE = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]{2,}$/;\n\n/**\n * @typedef {{to: string, subject: string, html: string, text: string}} OutgoingEmail\n * @typedef {{send: (email: OutgoingEmail) => Promise<unknown>}} EmailTransport\n */\n\n/**\n * @param {{\n * mjml: string,\n * subject?: string|null,\n * preheader?: string|null,\n * emails: string[]|string,\n * transport: EmailTransport,\n * baseUrl?: string|null,\n * unsubscribeUrl?: string,\n * variables?: import('../features/variables/catalog.js').EmailVariable[],\n * maxRecipients?: number,\n * t?: import('../i18n/index.js').Translate,\n * lang?: string,\n * }} input\n */\nexport async function sendTestEmail({\n mjml,\n subject = null,\n preheader = null,\n emails,\n transport,\n baseUrl = null,\n unsubscribeUrl = '#',\n variables = DEFAULT_VARIABLES,\n maxRecipients = MAX_TEST_RECIPIENTS,\n t = identity,\n lang = 'en',\n}) {\n const list = (Array.isArray(emails) ? emails : String(emails || '').split(/[,;\\s]+/))\n .map((e) => String(e || '').trim().toLowerCase())\n .filter(Boolean);\n const valid = [...new Set(list.filter((e) => EMAIL_SHAPE.test(e)))];\n const invalid = list.filter((e) => !EMAIL_SHAPE.test(e));\n if (!valid.length) throw new HttpError(400, t('No usable test address'), 'NO_RECIPIENT');\n if (valid.length > maxRecipients) {\n throw new HttpError(400, t('{max} test addresses at most', { max: maxRecipients }), 'TOO_MANY_RECIPIENTS');\n }\n\n // Compiled once (validation included), then personalized per address.\n const { compiled, warnings, quality } = await compileTemplate({ mjml, subject, preheader }, { t, lang, variables });\n\n const results = [];\n for (const email of valid) {\n try {\n const values = sampleValues(variables, { 'contact.email': email });\n const html = personalize(compiled, { values, unsubscribeUrl, baseUrl, variables });\n const personal = applyVariablesToText(String(subject || '').trim(), values, variables) || t('No subject');\n await transport.send({ to: email, subject: `[TEST] ${personal}`.slice(0, 200), html, text: htmlToText(html) });\n results.push({ email, ok: true, error: null });\n } catch (error) {\n // Short message: a gateway error can carry the whole HTML.\n results.push({ email, ok: false, error: String(/** @type {Error} */ (error)?.message || 'failed').slice(0, 300) });\n }\n }\n\n return {\n sent: results.filter((r) => r.ok).length,\n failed: results.filter((r) => !r.ok).length,\n invalid,\n results,\n warnings,\n quality,\n };\n}\n","// @ts-check\n/**\n * What a PROJECT (the editor's project data) may contain.\n *\n * The project is reloaded as-is into the editor canvas — an iframe of the host\n * page. It does NOT go through the editor engine's HTML parser (which drops\n * scripts and `on*` attributes): it is already structured data. The check must\n * therefore happen here, on save; otherwise a tampered project would run code\n * in the browser of the next user who opens it.\n *\n * The walk is generic (any object, any array) rather than modelled on the\n * engine's format: with another engine or another version, the rule still\n * holds.\n */\n\nimport { readDocument } from '../core/document.js';\nimport { identity } from '../i18n/index.js';\nimport { IMAGE_ATTRIBUTES, LINK_ATTRIBUTES, isSafeUrl } from './urls.js';\n\n/** Maximum size of a project's JSON. */\nexport const PROJECT_MAX_LENGTH = 2 * 1024 * 1024;\n\nconst MAX_DEPTH = 200;\n/** Forbidden HTML/MJML tags (a component's `tagName`). */\nconst DANGEROUS_TAGS = /^(script|iframe|frame|object|embed|form|base|link|meta|svg|math|mj-include|mj-raw)$/i;\n/**\n * Forbidden component types (`type`). Shorter list than the tags: for the\n * engine, `link` is the type of an `<a>` link — unrelated to the `<link>` tag\n * (external style sheet), which is still rejected through its `tagName`.\n */\nconst DANGEROUS_TYPES = /^(script|iframe|frame|object|embed|form|svg|mj-include|mj-raw)$/i;\n\n/**\n * @typedef {import('./mjmlValidator.js').ValidationIssue} ValidationIssue\n */\n\n/**\n * @param {unknown} raw envelope (object or JSON string)\n * @param {{t?: import('../i18n/index.js').Translate}} [options]\n * @returns {{ok: boolean, issues: ValidationIssue[], document: import('../core/document.js').EmailDocument|null, json: string}}\n */\nexport function validateProject(raw, { t = identity } = {}) {\n /** @type {ValidationIssue[]} */\n const issues = [];\n let document;\n try {\n document = readDocument(raw);\n } catch (error) {\n return {\n ok: false,\n issues: [{ code: 'ENVELOPE', message: /** @type {Error} */ (error).message }],\n document: null,\n json: '',\n };\n }\n\n const json = JSON.stringify(document);\n if (json.length > PROJECT_MAX_LENGTH) {\n issues.push({\n code: 'TOO_LARGE',\n message: t('project too large ({size} KB, maximum {max} KB)', {\n size: Math.round(json.length / 1024),\n max: PROJECT_MAX_LENGTH / 1024,\n }),\n });\n }\n\n /** @param {any} node @param {number} depth */\n function walk(node, depth) {\n if (depth > MAX_DEPTH) {\n issues.push({ code: 'TOO_DEEP', message: t('project too deep') });\n return;\n }\n if (Array.isArray(node)) {\n for (const item of node) walk(item, depth + 1);\n return;\n }\n if (!node || typeof node !== 'object') return;\n\n for (const [key, pattern] of /** @type {[string, RegExp][]} */ ([['type', DANGEROUS_TYPES], ['tagName', DANGEROUS_TAGS]])) {\n const value = node[key];\n if (typeof value === 'string' && pattern.test(value)) {\n issues.push({ code: 'FORBIDDEN_TAG', message: t('forbidden element in project: {tag}', { tag: value }), tag: value });\n }\n }\n for (const key of ['script', 'script-export', 'script-props']) {\n const value = node[key];\n if (value && (typeof value !== 'object' || Object.keys(value).length > 0)) {\n issues.push({ code: 'SCRIPT', message: t('component script not allowed') });\n }\n }\n for (const key of ['content', 'components']) {\n const value = node[key];\n if (typeof value === 'string' && /<\\s*(script|iframe|object|embed)\\b|javascript:/i.test(value)) {\n issues.push({ code: 'UNSAFE_CONTENT', message: t('executable content not allowed') });\n }\n }\n const attributes = node.attributes;\n if (attributes && typeof attributes === 'object' && !Array.isArray(attributes)) {\n for (const [name, value] of Object.entries(attributes)) {\n const lower = name.toLowerCase();\n if (lower.startsWith('on')) {\n issues.push({ code: 'EVENT_ATTRIBUTE', message: t('attribute {name} not allowed', { name }), attribute: name });\n } else if (LINK_ATTRIBUTES.has(lower) && !isSafeUrl(String(value ?? ''), 'link')) {\n issues.push({ code: 'UNSAFE_URL', message: t('URL rejected ({name})', { name }), attribute: name });\n } else if (IMAGE_ATTRIBUTES.has(lower) && !isSafeUrl(String(value ?? ''), 'image')) {\n issues.push({ code: 'UNSAFE_IMAGE', message: t('image rejected ({name})', { name }), attribute: name });\n }\n }\n }\n for (const [key, value] of Object.entries(node)) {\n if (key !== 'attributes' && value && typeof value === 'object') walk(value, depth + 1);\n }\n }\n\n walk(document.project, 0);\n return { ok: issues.length === 0, issues, document, json };\n}\n\n/**\n * Validates and throws a readable 422 error listing the first rejections.\n * @param {unknown} raw\n * @param {{t?: import('../i18n/index.js').Translate}} [options]\n */\nexport function assertValidProject(raw, { t = identity } = {}) {\n const result = validateProject(raw, { t });\n if (result.ok && result.document) return { document: result.document, json: result.json };\n const shown = result.issues.slice(0, 3).map((i) => i.message).join('; ');\n const error = /** @type {Error & {status?: number, issues?: ValidationIssue[]}} */ (\n new Error(t('Project rejected: {issues}', { issues: shown }))\n );\n error.status = 422;\n error.issues = result.issues;\n throw error;\n}\n","// @ts-check\n/**\n * TEMPLATES — the server-side business rules, independent of the database.\n *\n * ── THE PROJECT IS THE SOURCE, HTML A RESULT ────────────────────────────────\n * Every write receives the PROJECT (versioned envelope) and its MJML export.\n * The server validates both (`validation/`), compiles the MJML itself and only\n * stores ITS HTML. No route accepts HTML coming from the browser.\n *\n * ── CONCURRENT WRITES ───────────────────────────────────────────────────────\n * Two people can open the same template. Each write carries the `revision`\n * read at load time; the repository refuses the write (409) when someone else\n * saved in the meantime — instead of silently overwriting their work.\n *\n * ── VERSIONS ───────────────────────────────────────────────────────────────\n * One version per EXPLICIT save (`createVersion`), never per autosave. Only the\n * latest `maxVersions` stay listed.\n *\n * Storage is a REPOSITORY supplied by the application (see the typedef below\n * and `createMemoryTemplateRepository`, the reference implementation).\n */\n\nimport { identity } from '../i18n/index.js';\nimport { EMAIL_BUILDER_VERSION, EMAIL_DOCUMENT_SCHEMA_VERSION } from '../core/constants.js';\nimport { assertValidProject } from '../validation/projectValidator.js';\nimport { assertValidMjml } from '../validation/mjmlValidator.js';\nimport { prepareMjml } from '../renderer/prepareMjml.js';\nimport { compileMjml } from './compileMjml.js';\nimport { HttpError } from './errors.js';\n\n/**\n * @typedef {{\n * name: string,\n * description: string|null,\n * subject: string|null,\n * preheader: string|null,\n * projectJson: string,\n * mjml: string,\n * html: string,\n * builderVersion: string,\n * schemaVersion: number,\n * }} TemplateFields\n *\n * @typedef {TemplateFields & {\n * id: string|number,\n * revision: string,\n * createdAt: string|Date|null,\n * updatedAt: string|Date|null,\n * createdBy: string|null,\n * updatedBy: string|null,\n * }} TemplateRecord\n *\n * @typedef {{\n * id: string|number,\n * versionNumber: number,\n * name: string,\n * subject: string|null,\n * preheader: string|null,\n * projectJson: string,\n * mjml: string,\n * createdBy: string|null,\n * createdAt: string|Date|null,\n * }} VersionRecord\n *\n * Every method may be async. `update` MUST compare `expectedRevision` and write\n * atomically (e.g. `UPDATE … WHERE id = ? AND revision = ?`), returning\n * `{ conflict: true, current }` on mismatch and `null` when the id is unknown.\n * `addVersion` numbers versions (max + 1) and keeps the latest `keep` listed.\n *\n * @typedef {{\n * list: () => Promise<TemplateRecord[]>,\n * find: (id: string) => Promise<TemplateRecord|null>,\n * insert: (fields: TemplateFields & {createdBy: string|null}) => Promise<TemplateRecord>,\n * update: (id: string, fields: Partial<TemplateFields> & {updatedBy: string|null},\n * options: {expectedRevision: string|null}) =>\n * Promise<TemplateRecord | {conflict: true, current: TemplateRecord} | null>,\n * remove: (id: string) => Promise<boolean>,\n * addVersion: (templateId: string, version: Omit<VersionRecord, 'id'|'versionNumber'|'createdAt'>,\n * options: {keep: number}) => Promise<{id: string|number, versionNumber: number}>,\n * listVersions: (templateId: string) => Promise<VersionRecord[]>,\n * findVersion: (templateId: string, versionId: string) => Promise<VersionRecord|null>,\n * transaction?: <T>(work: () => Promise<T>) => Promise<T>,\n * }} TemplateRepository\n *\n * @typedef {{id?: string|number, name?: string}|null} Actor\n */\n\n/** Column sizes. */\nexport const LIMITS = { name: 200, description: 1000, subject: 250, preheader: 250 };\n\nexport const MAX_ACTIVE_VERSIONS = 50;\n\nfunction text(value, max) {\n const out = String(value ?? '').trim();\n return out ? out.slice(0, max) : null;\n}\n\nconst iso = (value) => (value ? new Date(value).toISOString() : null);\nconst actorName = (/** @type {Actor} */ user) => (user ? String(user.name ?? user.id ?? '') || null : null);\n\nfunction parseProject(json) {\n try {\n return JSON.parse(json);\n } catch {\n return null;\n }\n}\n\n/** Record → API object. */\nfunction toTemplate(/** @type {TemplateRecord} */ row, { withProject = true } = {}) {\n return {\n id: row.id,\n name: row.name,\n description: row.description,\n subject: row.subject,\n preheader: row.preheader,\n revision: row.revision,\n builderVersion: row.builderVersion,\n schemaVersion: row.schemaVersion,\n createdBy: row.createdBy,\n updatedBy: row.updatedBy,\n createdAt: iso(row.createdAt),\n updatedAt: iso(row.updatedAt),\n ...(withProject ? { document: parseProject(row.projectJson), mjml: row.mjml, html: row.html } : {}),\n };\n}\n\n/** @param {VersionRecord} row */\nfunction toVersion(row, { withProject = false } = {}) {\n return {\n id: row.id,\n versionNumber: row.versionNumber,\n name: row.name,\n subject: row.subject,\n createdBy: row.createdBy,\n createdAt: iso(row.createdAt),\n ...(withProject ? { preheader: row.preheader, document: parseProject(row.projectJson), mjml: row.mjml } : {}),\n };\n}\n\n/**\n * @param {{\n * repository: TemplateRepository,\n * t?: import('../i18n/index.js').Translate,\n * lang?: string,\n * maxVersions?: number,\n * }} options\n */\nexport function createTemplateService({ repository, t = identity, lang = 'en', maxVersions = MAX_ACTIVE_VERSIONS }) {\n const inTransaction = (work) => (repository.transaction ? repository.transaction(work) : work());\n\n /**\n * Validates the project and the MJML, compiles the final HTML.\n * @param {{document: any, mjml: any, subject?: string|null, preheader?: string|null}} input\n */\n async function compile({ document, mjml, subject = null, preheader = null }) {\n const { json } = assertValidProject(document, { t });\n assertValidMjml(mjml, { t });\n const { html, warnings } = await compileMjml(prepareMjml(mjml, { title: subject, preheader, lang }));\n if (!html) throw new HttpError(422, t('Empty MJML compilation'), 'EMPTY_COMPILATION');\n return { projectJson: json, mjml: String(mjml), html, warnings };\n }\n\n async function findOrThrow(id) {\n const row = await repository.find(String(id));\n if (!row) throw new HttpError(404, t('Template not found'), 'NOT_FOUND');\n return row;\n }\n\n const service = {\n compile,\n\n async list() {\n const rows = await repository.list();\n return rows.map((row) => toTemplate(row, { withProject: false }));\n },\n\n /** @param {string|number} id */\n async get(id) {\n return toTemplate(await findOrThrow(id));\n },\n\n /**\n * Creates a template and its version 1.\n * @param {any} body\n * @param {Actor} [user]\n */\n async create(body, user = null) {\n const name = text(body?.name, LIMITS.name);\n if (!name) throw new HttpError(400, t('Name required'), 'NAME_REQUIRED');\n const subject = text(body?.subject, LIMITS.subject);\n const preheader = text(body?.preheader, LIMITS.preheader);\n const { projectJson, mjml, html, warnings } = await compile({ document: body?.document, mjml: body?.mjml, subject, preheader });\n const by = actorName(user);\n\n const row = await inTransaction(async () => {\n const created = await repository.insert({\n name,\n description: text(body?.description, LIMITS.description),\n subject,\n preheader,\n projectJson,\n mjml,\n html,\n builderVersion: EMAIL_BUILDER_VERSION,\n schemaVersion: EMAIL_DOCUMENT_SCHEMA_VERSION,\n createdBy: by,\n });\n await repository.addVersion(\n String(created.id),\n { name, subject, preheader, projectJson, mjml, createdBy: by },\n { keep: maxVersions }\n );\n return created;\n });\n return { template: toTemplate(row), warnings };\n },\n\n /**\n * Saves a template. Fields left undefined keep their stored value.\n * @param {string|number} id\n * @param {any} body\n * @param {Actor} [user]\n */\n async save(id, body, user = null) {\n const current = await findOrThrow(id);\n const name = body?.name !== undefined ? text(body.name, LIMITS.name) : current.name;\n if (!name) throw new HttpError(400, t('Name required'), 'NAME_REQUIRED');\n const subject = body?.subject !== undefined ? text(body.subject, LIMITS.subject) : current.subject;\n const preheader = body?.preheader !== undefined ? text(body.preheader, LIMITS.preheader) : current.preheader;\n const description =\n body?.description !== undefined ? text(body.description, LIMITS.description) : current.description;\n const { projectJson, mjml, html, warnings } = await compile({ document: body?.document, mjml: body?.mjml, subject, preheader });\n const by = actorName(user);\n\n const result = await inTransaction(async () => {\n const updated = await repository.update(\n String(id),\n {\n name,\n description,\n subject,\n preheader,\n projectJson,\n mjml,\n html,\n builderVersion: EMAIL_BUILDER_VERSION,\n schemaVersion: EMAIL_DOCUMENT_SCHEMA_VERSION,\n updatedBy: by,\n },\n { expectedRevision: body?.revision ? String(body.revision) : null }\n );\n if (!updated) throw new HttpError(404, t('Template not found'), 'NOT_FOUND');\n if ('conflict' in updated) {\n const who = updated.current.updatedBy;\n throw new HttpError(\n 409,\n who\n ? t('Template changed in the meantime by {user} — reload it before saving', { user: who })\n : t('Template changed in the meantime — reload it before saving'),\n 'CONFLICT'\n );\n }\n const version = body?.createVersion\n ? await repository.addVersion(\n String(id),\n { name, subject, preheader, projectJson, mjml, createdBy: by },\n { keep: maxVersions }\n )\n : null;\n return { row: updated, version };\n });\n\n // The editor keeps its own document: only what changed server side is returned.\n const { document: _omit, ...template } = toTemplate(result.row);\n return { template, warnings, version: result.version };\n },\n\n /** @param {string|number} id @param {Actor} [user] */\n async duplicate(id, user = null) {\n const source = await findOrThrow(id);\n return service.create(\n {\n name: t('{name} (copy)', { name: source.name }).slice(0, LIMITS.name),\n description: source.description,\n subject: source.subject,\n preheader: source.preheader,\n document: parseProject(source.projectJson),\n mjml: source.mjml,\n },\n user\n );\n },\n\n /** @param {string|number} id */\n async remove(id) {\n if (!(await repository.remove(String(id)))) throw new HttpError(404, t('Template not found'), 'NOT_FOUND');\n return { id };\n },\n\n /** @param {string|number} id */\n async listVersions(id) {\n await findOrThrow(id);\n const rows = await repository.listVersions(String(id));\n return rows.map((row) => toVersion(row));\n },\n\n /** @param {string|number} id @param {string|number} versionId */\n async getVersion(id, versionId) {\n const row = await repository.findVersion(String(id), String(versionId));\n if (!row) throw new HttpError(404, t('Version not found'), 'NOT_FOUND');\n return toVersion(row, { withProject: true });\n },\n };\n return service;\n}\n\n/**\n * In-memory repository — reference implementation of the contract, used by the\n * tests and the example application. Data is lost on restart.\n *\n * @returns {TemplateRepository}\n */\nexport function createMemoryTemplateRepository() {\n /** @type {Map<string, TemplateRecord>} */\n const rows = new Map();\n /** @type {Map<string, VersionRecord[]>} */\n const versions = new Map();\n let nextId = 1;\n let nextVersionId = 1;\n let clock = 0;\n // Strictly increasing, even within the same millisecond.\n const stamp = () => {\n clock = Math.max(clock + 1, Date.now());\n return new Date(clock);\n };\n const copy = (row) => (row ? { ...row } : null);\n\n return {\n async list() {\n return [...rows.values()]\n .map(copy)\n .sort((a, b) => new Date(b.updatedAt || b.createdAt).getTime() - new Date(a.updatedAt || a.createdAt).getTime());\n },\n async find(id) {\n return copy(rows.get(id));\n },\n async insert(fields) {\n const at = stamp();\n const row = { ...fields, id: nextId++, revision: at.toISOString(), createdAt: at, updatedAt: null, updatedBy: null };\n rows.set(String(row.id), row);\n return copy(row);\n },\n async update(id, fields, { expectedRevision }) {\n const row = rows.get(id);\n if (!row) return null;\n if (expectedRevision && expectedRevision !== row.revision) return { conflict: true, current: copy(row) };\n const at = stamp();\n Object.assign(row, fields, { updatedAt: at, revision: at.toISOString() });\n return copy(row);\n },\n async remove(id) {\n versions.delete(id);\n return rows.delete(id);\n },\n async addVersion(templateId, version, { keep }) {\n const list = versions.get(templateId) || [];\n const versionNumber = list.reduce((max, v) => Math.max(max, v.versionNumber), 0) + 1;\n const entry = { ...version, id: nextVersionId++, versionNumber, createdAt: stamp() };\n versions.set(templateId, [...list, entry].slice(-keep));\n return { id: entry.id, versionNumber };\n },\n async listVersions(templateId) {\n return [...(versions.get(templateId) || [])].reverse().map(copy);\n },\n async findVersion(templateId, versionId) {\n return copy((versions.get(templateId) || []).find((v) => String(v.id) === versionId));\n },\n };\n}\n","// @ts-check\n/**\n * CUSTOM VARIABLES — created by users in the builder, shared by every template.\n *\n * System variables (the `variables` option of the handler) are read-only; a\n * custom variable can never take one of their keys. A key never changes after\n * creation: templates contain it.\n *\n * Storage is a REPOSITORY supplied by the application;\n * `createMemoryVariableRepository` is the reference implementation.\n */\n\nimport { identity } from '../i18n/index.js';\nimport { DEFAULT_VARIABLES, mergeVariables, validateCustomVariable } from '../features/variables/catalog.js';\nimport { HttpError } from './errors.js';\n\n/**\n * @typedef {import('../features/variables/catalog.js').EmailVariable} EmailVariable\n *\n * @typedef {{\n * list: () => Promise<EmailVariable[]>,\n * insert: (variable: EmailVariable & {createdBy: string|null}) => Promise<void>,\n * update: (key: string, variable: EmailVariable & {updatedBy: string|null}) => Promise<boolean>,\n * remove: (key: string) => Promise<boolean>,\n * }} VariableRepository\n */\n\nconst actorName = (user) => (user ? String(user.name ?? user.id ?? '') || null : null);\n\n/** Public shape (no audit fields). @param {any} row @returns {EmailVariable} */\nconst toVariable = (row) => ({\n key: row.key,\n label: row.label,\n group: row.group,\n sample: row.sample || '',\n fallback: row.fallback || '',\n description: row.description || '',\n custom: true,\n});\n\n/**\n * @param {{\n * repository?: VariableRepository|null,\n * system?: EmailVariable[],\n * t?: import('../i18n/index.js').Translate,\n * }} options\n */\nexport function createVariableService({ repository = null, system = DEFAULT_VARIABLES, t = identity }) {\n const requireRepository = () => {\n if (!repository) throw new HttpError(501, t('Custom variables are not configured'), 'NO_VARIABLE_STORE');\n return repository;\n };\n\n const service = {\n /** Custom variables only. */\n async listCustom() {\n return repository ? (await repository.list()).map(toVariable) : [];\n },\n\n /** System + custom: the catalog used for rendering. */\n async catalog() {\n return mergeVariables(system, await service.listCustom());\n },\n\n /** @param {any} input @param {any} [user] */\n async create(input, user = null) {\n const store = requireRepository();\n const existing = [...system, ...(await service.listCustom())];\n const { variable, errors } = validateCustomVariable(input, { existing, t });\n if (errors) throw Object.assign(new HttpError(400, Object.values(errors)[0], 'INVALID_VARIABLE'), { issues: errors });\n await store.insert({ ...variable, createdBy: actorName(user) });\n return variable;\n },\n\n /** @param {string} key @param {any} input @param {any} [user] */\n async update(key, input, user = null) {\n const store = requireRepository();\n const current = (await service.listCustom()).find((v) => v.key === key);\n if (!current) throw new HttpError(404, t('Variable not found'), 'NOT_FOUND');\n const { variable, errors } = validateCustomVariable({ ...current, ...input, key }, { editing: key, t });\n if (errors) throw Object.assign(new HttpError(400, Object.values(errors)[0], 'INVALID_VARIABLE'), { issues: errors });\n await store.update(key, { ...variable, updatedBy: actorName(user) });\n return variable;\n },\n\n /** @param {string} key */\n async remove(key) {\n if (!(await requireRepository().remove(key))) throw new HttpError(404, t('Variable not found'), 'NOT_FOUND');\n return { key };\n },\n };\n return service;\n}\n\n/** @returns {VariableRepository} */\nexport function createMemoryVariableRepository() {\n /** @type {Map<string, any>} */\n const rows = new Map();\n return {\n async list() {\n return [...rows.values()].map((row) => ({ ...row })).sort((a, b) => a.label.localeCompare(b.label));\n },\n async insert(variable) {\n if (rows.has(variable.key)) throw new Error('duplicate variable');\n rows.set(variable.key, { ...variable, createdAt: new Date() });\n },\n async update(key, variable) {\n if (!rows.has(key)) return false;\n rows.set(key, { ...rows.get(key), ...variable, key, updatedAt: new Date() });\n return true;\n },\n async remove(key) {\n return rows.delete(key);\n },\n };\n}\n","// @ts-check\n/**\n * HTTP HANDLER — every route the builder needs, as one function\n * `(request: Request) => Promise<Response>` (Fetch API).\n *\n * Works as is in Next.js route handlers, Hono, Bun, Deno, Cloudflare Workers;\n * with Express / Node's `http` through `toNodeListener` (see `node.js`).\n *\n * GET {base}/templates read list\n * POST {base}/templates write create, or { duplicateOf }\n * GET {base}/templates/:id read template\n * PUT {base}/templates/:id write save\n * DELETE {base}/templates/:id write delete\n * GET {base}/templates/:id/versions read versions\n * GET {base}/templates/:id/versions/:vid read version with its project\n * POST {base}/render read preview (server rendering)\n * POST {base}/test write test send (needs `transport`)\n * POST {base}/assets write image upload (multipart, field `file`)\n * GET {base}/variables read custom variables\n * POST {base}/variables write create a custom variable\n * PUT {base}/variables/:key write edit (label, group, sample, fallback)\n * DELETE {base}/variables/:key write delete\n * GET {assetsPath}:key PUBLIC uploaded image\n *\n * Images are PUBLIC on purpose: mail clients download them without any\n * session. Their keys are content hashes (unguessable), and they are served\n * with headers that forbid any interpretation other than an image.\n *\n * `authorize(request, { action })` is REQUIRED: it returns the user (any\n * object with `id`/`name`, recorded as author) or null to refuse (401).\n * Throw an `HttpError(403, …)` to refuse with another status.\n */\n\nimport { translatorFor } from '../i18n/index.js';\nimport { DEFAULT_VARIABLES, defineVariables } from '../features/variables/catalog.js';\nimport { applyVariablesToText, sampleValues } from '../features/variables/service.js';\nimport { MAX_IMAGE_BYTES } from '../validation/images.js';\nimport { createAssetService } from './assets.js';\nimport { HttpError, statusOf } from './errors.js';\nimport { compileTemplate, personalize } from './render.js';\nimport { MAX_TEST_RECIPIENTS, sendTestEmail } from './sendTest.js';\nimport { createTemplateService } from './templates.js';\nimport { createVariableService } from './variables.js';\n\n/**\n * @typedef {'read'|'write'} Action\n * @typedef {{id?: string|number, name?: string}} User\n *\n * @typedef {{\n * templates: import('./templates.js').TemplateRepository,\n * assets: import('./assets.js').AssetRepository,\n * authorize: (request: Request, context: {action: Action}) => Promise<User|null|false> | User | null | false,\n * basePath?: string,\n * assetsPath?: string,\n * publicUrl?: string|null,\n * transport?: import('./sendTest.js').EmailTransport|null,\n * variables?: import('../features/variables/catalog.js').EmailVariable[],\n * variableStore?: import('./variables.js').VariableRepository|null,\n * locale?: string,\n * messages?: Record<string, string>,\n * lang?: string,\n * previewUnsubscribeUrl?: string,\n * maxVersions?: number,\n * maxTestRecipients?: number,\n * rateLimits?: {testPerHour?: number, uploadsPerHour?: number},\n * onError?: (error: unknown, request: Request) => void,\n * }} HandlerOptions\n */\n\n/** Largest JSON body accepted (project 2 MB + MJML 512 KB + metadata). */\nconst MAX_JSON_BYTES = 3 * 1024 * 1024;\nconst HOUR = 60 * 60 * 1000;\n\nconst json = (data, status = 200) =>\n new Response(JSON.stringify(data), {\n status,\n headers: { 'Content-Type': 'application/json; charset=utf-8', 'Cache-Control': 'no-store' },\n });\n\n/** Sliding one-hour window per key, in memory (one process). */\nfunction createLimiter(perHour) {\n /** @type {Map<string, number[]>} */\n const hits = new Map();\n return (key) => {\n if (!perHour) return true;\n const now = Date.now();\n const recent = (hits.get(key) || []).filter((at) => now - at < HOUR);\n if (recent.length >= perHour) {\n hits.set(key, recent);\n return false;\n }\n recent.push(now);\n hits.set(key, recent);\n return true;\n };\n}\n\n/**\n * The returned function also exposes `handler.catalog()`: system + custom\n * variables, for the application's own sending pipeline (`personalize`).\n *\n * @param {HandlerOptions} options\n * @returns {((request: Request) => Promise<Response>) & {catalog: () => Promise<import('../features/variables/catalog.js').EmailVariable[]>}}\n */\nexport function createEmailBuilderHandler(options) {\n const {\n templates: templateRepository,\n assets: assetRepository,\n authorize,\n basePath = '/api/email-builder',\n publicUrl = null,\n transport = null,\n locale = 'en',\n messages,\n lang = locale.slice(0, 2),\n previewUnsubscribeUrl = '#',\n maxVersions,\n maxTestRecipients = MAX_TEST_RECIPIENTS,\n rateLimits = {},\n onError = (error) => console.error('[best-email-builder]', error),\n } = options || /** @type {any} */ ({});\n if (typeof authorize !== 'function') throw new Error('createEmailBuilderHandler: `authorize` is required');\n if (!templateRepository || !assetRepository) {\n throw new Error('createEmailBuilderHandler: `templates` and `assets` repositories are required');\n }\n\n const base = basePath.replace(/\\/+$/, '');\n const assetsPath = (options.assetsPath || `${base}/assets/`).replace(/\\/*$/, '/');\n const variables = defineVariables(options.variables || DEFAULT_VARIABLES);\n const t = translatorFor(locale, messages);\n const templates = createTemplateService({ repository: templateRepository, t, lang, maxVersions });\n const assets = createAssetService({ repository: assetRepository, publicPath: assetsPath, t });\n const customVariables = createVariableService({ repository: options.variableStore || null, system: variables, t });\n const testLimiter = createLimiter(rateLimits.testPerHour ?? 20);\n const uploadLimiter = createLimiter(rateLimits.uploadsPerHour ?? 100);\n\n /** @param {Request} request @param {Action} action */\n async function userFor(request, action) {\n const user = await authorize(request, { action });\n if (!user) throw new HttpError(401, t('Authentication required'), 'UNAUTHORIZED');\n return /** @type {User} */ (user);\n }\n\n const userKey = (/** @type {User} */ user) => String(user.id ?? user.name ?? 'anonymous');\n\n /** @param {Request} request */\n async function readJson(request) {\n const length = Number(request.headers.get('content-length') || 0);\n if (length > MAX_JSON_BYTES) throw new HttpError(413, t('Request too large'), 'TOO_LARGE');\n const body = await request.text();\n if (body.length > MAX_JSON_BYTES) throw new HttpError(413, t('Request too large'), 'TOO_LARGE');\n try {\n return body ? JSON.parse(body) : {};\n } catch {\n throw new HttpError(400, t('Invalid JSON'), 'INVALID_JSON');\n }\n }\n\n /** @param {string} key */\n async function serveAsset(key) {\n const asset = await assets.get(key);\n if (!asset) return new Response('Not found', { status: 404, headers: { 'Content-Type': 'text/plain' } });\n return new Response(/** @type {any} */ (asset.data), {\n status: 200,\n headers: {\n 'Content-Type': asset.contentType,\n 'Content-Length': String(asset.data.length),\n 'Cache-Control': 'public, max-age=31536000, immutable',\n ETag: `\"${asset.sha256 || key}\"`,\n 'X-Content-Type-Options': 'nosniff',\n 'Content-Security-Policy': \"default-src 'none'; sandbox\",\n },\n });\n }\n\n /** @param {Request} request */\n async function route(request) {\n const url = new URL(request.url);\n const path = url.pathname;\n const method = request.method.toUpperCase();\n\n if (path.startsWith(assetsPath) && (method === 'GET' || method === 'HEAD')) {\n return serveAsset(decodeURIComponent(path.slice(assetsPath.length)));\n }\n if (path !== base && !path.startsWith(`${base}/`)) return null;\n const parts = path.slice(base.length).split('/').filter(Boolean).map(decodeURIComponent);\n const [section, id, sub, subId] = parts;\n\n if (section === 'templates') {\n if (!id && method === 'GET') {\n await userFor(request, 'read');\n return json({ data: await templates.list() });\n }\n if (!id && method === 'POST') {\n const user = await userFor(request, 'write');\n const body = await readJson(request);\n const result =\n body?.duplicateOf !== undefined && body?.duplicateOf !== null\n ? await templates.duplicate(body.duplicateOf, user)\n : await templates.create(body, user);\n return json({ data: result }, 201);\n }\n if (id && !sub && method === 'GET') {\n await userFor(request, 'read');\n return json({ data: await templates.get(id) });\n }\n if (id && !sub && method === 'PUT') {\n const user = await userFor(request, 'write');\n return json({ data: await templates.save(id, await readJson(request), user) });\n }\n if (id && !sub && method === 'DELETE') {\n await userFor(request, 'write');\n return json({ data: await templates.remove(id) });\n }\n if (id && sub === 'versions' && method === 'GET') {\n await userFor(request, 'read');\n return json({ data: subId ? await templates.getVersion(id, subId) : await templates.listVersions(id) });\n }\n }\n\n if (section === 'render' && !id && method === 'POST') {\n await userFor(request, 'read');\n const body = await readJson(request);\n const catalog = await customVariables.catalog();\n const { compiled, warnings, quality } = await compileTemplate(\n { mjml: body?.mjml, subject: body?.subject, preheader: body?.preheader },\n { t, lang, variables: catalog }\n );\n // Same origin as the application: uploaded images stay relative.\n const values = sampleValues(catalog);\n const html = personalize(compiled, { values, unsubscribeUrl: previewUnsubscribeUrl, variables: catalog });\n return json({\n data: {\n html,\n subject: applyVariablesToText(body?.subject || '', values, catalog),\n preheader: applyVariablesToText(body?.preheader || '', values, catalog),\n warnings,\n quality,\n },\n });\n }\n\n if (section === 'test' && !id && method === 'POST') {\n const user = await userFor(request, 'write');\n if (!transport) throw new HttpError(501, t('Test sending is not configured'), 'NO_TRANSPORT');\n if (!testLimiter(userKey(user))) throw new HttpError(429, t('Too many test sends — try again later'), 'RATE_LIMITED');\n const body = await readJson(request);\n const result = await sendTestEmail({\n mjml: body?.mjml,\n subject: body?.subject,\n preheader: body?.preheader,\n emails: body?.emails,\n transport,\n baseUrl: publicUrl,\n unsubscribeUrl: previewUnsubscribeUrl,\n variables: await customVariables.catalog(),\n maxRecipients: maxTestRecipients,\n t,\n lang,\n });\n return json({ data: result });\n }\n\n if (section === 'assets' && !id && method === 'POST') {\n const user = await userFor(request, 'write');\n if (!uploadLimiter(userKey(user))) throw new HttpError(429, t('Too many uploads — try again later'), 'RATE_LIMITED');\n const length = Number(request.headers.get('content-length') || 0);\n if (length > MAX_IMAGE_BYTES + 64 * 1024) {\n throw new HttpError(413, t('Image too large: {max} MB maximum', { max: MAX_IMAGE_BYTES / (1024 * 1024) }), 'TOO_LARGE');\n }\n let form;\n try {\n form = await request.formData();\n } catch {\n throw new HttpError(400, t('Multipart form expected'), 'INVALID_FORM');\n }\n const file = form.get('file');\n if (!file || typeof file === 'string') throw new HttpError(400, t('File missing'), 'FILE_MISSING');\n const bytes = new Uint8Array(await file.arrayBuffer());\n const result = await assets.upload({ bytes, name: file.name, user });\n return json({ data: result }, result.created ? 201 : 200);\n }\n\n if (section === 'variables') {\n if (!id && method === 'GET') {\n await userFor(request, 'read');\n return json({ data: await customVariables.listCustom() });\n }\n if (!id && method === 'POST') {\n const user = await userFor(request, 'write');\n return json({ data: await customVariables.create(await readJson(request), user) }, 201);\n }\n if (id && !sub && method === 'PUT') {\n const user = await userFor(request, 'write');\n return json({ data: await customVariables.update(id, await readJson(request), user) });\n }\n if (id && !sub && method === 'DELETE') {\n await userFor(request, 'write');\n return json({ data: await customVariables.remove(id) });\n }\n }\n\n throw new HttpError(404, t('Route not found'), 'ROUTE_NOT_FOUND');\n }\n\n async function handle(request) {\n try {\n const response = await route(request);\n return response || json({ error: t('Route not found'), code: 'ROUTE_NOT_FOUND' }, 404);\n } catch (error) {\n const status = statusOf(error);\n if (status >= 500 && status !== 501) {\n onError(error, request);\n return json({ error: t('Internal error'), code: 'INTERNAL' }, 500);\n }\n const err = /** @type {any} */ (error);\n return json({ error: err.message, code: err.code || undefined, issues: err.issues || undefined }, status);\n }\n }\n\n handle.catalog = () => customVariables.catalog();\n return handle;\n}\n","// @ts-check\n/**\n * Bridge between a Fetch-API handler and Node's `http` / Express / Connect.\n *\n * app.use(toNodeListener(handler)); // Express\n * http.createServer(toNodeListener(handler)) // plain Node\n *\n * Requests the handler does not own (404 with code ROUTE_NOT_FOUND, outside its\n * routes) are passed to `next()` when there is one.\n */\n\nimport { Readable } from 'node:stream';\n\n/**\n * @param {(request: Request) => Promise<Response>} handler\n * @param {{origin?: string}} [options]\n */\nexport function toNodeListener(handler, { origin } = {}) {\n /**\n * @param {import('node:http').IncomingMessage} req\n * @param {import('node:http').ServerResponse} res\n * @param {(error?: unknown) => void} [next]\n */\n return async function listener(req, res, next) {\n try {\n const host = origin || `http://${req.headers.host || 'localhost'}`;\n const url = new URL(req.url || '/', host);\n const method = (req.method || 'GET').toUpperCase();\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (Array.isArray(value)) value.forEach((v) => headers.append(key, v));\n else if (value !== undefined) headers.set(key, value);\n }\n const hasBody = method !== 'GET' && method !== 'HEAD';\n const request = new Request(url, {\n method,\n headers,\n body: hasBody ? /** @type {any} */ (Readable.toWeb(req)) : undefined,\n // @ts-ignore — required by Node for streamed bodies\n duplex: hasBody ? 'half' : undefined,\n });\n const response = await handler(request);\n if (response.status === 404 && next) {\n const body = await response.clone().json().catch(() => null);\n if (body?.code === 'ROUTE_NOT_FOUND') return next();\n }\n res.statusCode = response.status;\n response.headers.forEach((value, key) => res.setHeader(key, value));\n if (!response.body || method === 'HEAD') {\n res.end();\n return;\n }\n res.end(Buffer.from(await response.arrayBuffer()));\n } catch (error) {\n if (next) next(error);\n else {\n res.statusCode = 500;\n res.end();\n }\n }\n };\n}\n"],"mappings":"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","names":["text","json","json","actorName"]}