best-email-builder 0.2.0 → 0.3.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.
- package/CHANGELOG.md +53 -0
- package/README.md +102 -0
- package/dist/builder.js +44 -0
- package/dist/builder.js.map +1 -0
- package/dist/{chunk-MTALS5GC.js → chunk-3M36VVXS.js} +36 -118
- package/dist/chunk-3M36VVXS.js.map +1 -0
- package/dist/chunk-57CTQNTK.js +56 -0
- package/dist/chunk-57CTQNTK.js.map +1 -0
- package/dist/chunk-BVOVAEEJ.js +73 -0
- package/dist/chunk-BVOVAEEJ.js.map +1 -0
- package/dist/chunk-ILRAQTHS.js +3677 -0
- package/dist/chunk-ILRAQTHS.js.map +1 -0
- package/dist/chunk-JOC752I3.js +529 -0
- package/dist/chunk-JOC752I3.js.map +1 -0
- package/dist/chunk-ZSUHOCCE.js +1351 -0
- package/dist/chunk-ZSUHOCCE.js.map +1 -0
- package/dist/index.js +86 -4865
- package/dist/index.js.map +1 -1
- package/dist/library.js +31 -0
- package/dist/library.js.map +1 -0
- package/dist/server.js +15 -11
- package/dist/server.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/adapters/grapesjs/engineConfig.d.ts +0 -14
- package/dist/types/builder.d.ts +8 -0
- package/dist/types/core/constants.d.ts +1 -1
- package/dist/types/core/scrollbars.d.ts +13 -0
- package/dist/types/defaultEditor.d.ts +8 -0
- package/dist/types/features/templates/gallery.d.ts +7 -0
- package/dist/types/index.d.ts +4 -9
- package/dist/types/library.d.ts +5 -0
- package/dist/types/storage/client.d.ts +4 -0
- package/dist/types/ui/EmailStudio.d.ts +42 -0
- package/dist/types/ui/library/TemplateLibrary.d.ts +41 -0
- package/dist/types/ui/primitives/button.d.ts +1 -1
- package/package.json +18 -1
- package/dist/chunk-MTALS5GC.js.map +0 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isAssetPath
|
|
3
|
+
} from "./chunk-3M36VVXS.js";
|
|
4
|
+
|
|
5
|
+
// src/validation/urls.js
|
|
6
|
+
var LINK_ATTRIBUTES = /* @__PURE__ */ new Set(["href", "action", "formaction", "xlink:href"]);
|
|
7
|
+
var IMAGE_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
8
|
+
"src",
|
|
9
|
+
"srcset",
|
|
10
|
+
"background-url",
|
|
11
|
+
"background",
|
|
12
|
+
"poster",
|
|
13
|
+
"icon-unwrapped-url",
|
|
14
|
+
"icon-wrapped-url"
|
|
15
|
+
]);
|
|
16
|
+
var NAMED_ENTITIES = {
|
|
17
|
+
colon: ":",
|
|
18
|
+
tab: " ",
|
|
19
|
+
newline: "\n",
|
|
20
|
+
lpar: "(",
|
|
21
|
+
rpar: ")",
|
|
22
|
+
amp: "&",
|
|
23
|
+
quot: '"',
|
|
24
|
+
apos: "'",
|
|
25
|
+
lt: "<",
|
|
26
|
+
gt: ">",
|
|
27
|
+
nbsp: " "
|
|
28
|
+
};
|
|
29
|
+
function decodeEntities(value) {
|
|
30
|
+
return String(value ?? "").replace(/&(#x[0-9a-f]+|#\d+|[a-z]+);?/gi, (whole, body) => {
|
|
31
|
+
const key = String(body).toLowerCase();
|
|
32
|
+
if (key.startsWith("#x")) return String.fromCodePoint(Number.parseInt(key.slice(2), 16) || 0);
|
|
33
|
+
if (key.startsWith("#")) return String.fromCodePoint(Number.parseInt(key.slice(1), 10) || 0);
|
|
34
|
+
return NAMED_ENTITIES[key] ?? whole;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function isMergeTag(value) {
|
|
38
|
+
return /^\{\{\s*[a-zA-Z][\w.]*\s*\}\}$/.test(String(value || "").trim());
|
|
39
|
+
}
|
|
40
|
+
function isSafeUrl(raw, kind) {
|
|
41
|
+
const value = decodeEntities(raw).trim();
|
|
42
|
+
if (!value) return true;
|
|
43
|
+
if (isMergeTag(value)) return true;
|
|
44
|
+
const compact = value.replace(/[\s\u0000-\u001f\u007f]+/g, "").toLowerCase();
|
|
45
|
+
if (kind === "image") return compact.startsWith("https://") || isAssetPath(value);
|
|
46
|
+
if (compact.startsWith("#")) return true;
|
|
47
|
+
return /^(https?:\/\/|mailto:|tel:)/.test(compact);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export {
|
|
51
|
+
LINK_ATTRIBUTES,
|
|
52
|
+
IMAGE_ATTRIBUTES,
|
|
53
|
+
decodeEntities,
|
|
54
|
+
isSafeUrl
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=chunk-57CTQNTK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/validation/urls.js"],"sourcesContent":["// @ts-check\n/**\n * URLs accepted in an email template.\n *\n * link (`href`) https, http, mailto:, tel:, `#…` anchor,\n * or a `{{ … }}` merge tag (unsubscribe,\n * variable) replaced at render time;\n * image (`src`, `background-url`…) https only: an http image is blocked or\n * flagged by most email clients, and\n * `data:` bloats the message until Gmail\n * clips it. Or an UPLOADED image (asset\n * path, see `isAssetPath`), made absolute\n * at render time (renderer/links.js).\n *\n * Everything else (`javascript:`, `data:`, `vbscript:`, `file:`…) is rejected.\n * The scheme is read AFTER decoding entities and stripping whitespace and\n * control characters: `java	script:` and `javascript:` are classics.\n */\nimport { isAssetPath } from '../renderer/links.js';\n\n/** Attributes carrying a LINK URL. */\nexport const LINK_ATTRIBUTES = new Set(['href', 'action', 'formaction', 'xlink:href']);\n\n/** Attributes carrying an IMAGE URL (MJML and HTML). */\nexport const IMAGE_ATTRIBUTES = new Set([\n 'src',\n 'srcset',\n 'background-url',\n 'background',\n 'poster',\n 'icon-unwrapped-url',\n 'icon-wrapped-url',\n]);\n\nconst NAMED_ENTITIES = {\n colon: ':',\n tab: '\\t',\n newline: '\\n',\n lpar: '(',\n rpar: ')',\n amp: '&',\n quot: '\"',\n apos: \"'\",\n lt: '<',\n gt: '>',\n nbsp: ' ',\n};\n\n/** Decodes the HTML entities of an attribute value. @param {string} value */\nexport function decodeEntities(value) {\n return String(value ?? '').replace(/&(#x[0-9a-f]+|#\\d+|[a-z]+);?/gi, (whole, body) => {\n const key = String(body).toLowerCase();\n if (key.startsWith('#x')) return String.fromCodePoint(Number.parseInt(key.slice(2), 16) || 0);\n if (key.startsWith('#')) return String.fromCodePoint(Number.parseInt(key.slice(1), 10) || 0);\n return NAMED_ENTITIES[key] ?? whole;\n });\n}\n\n/** A `{{ … }}` merge tag (variable, unsubscribe, web version). @param {string} value */\nexport function isMergeTag(value) {\n return /^\\{\\{\\s*[a-zA-Z][\\w.]*\\s*\\}\\}$/.test(String(value || '').trim());\n}\n\n/**\n * @param {string} raw\n * @param {'link'|'image'} kind\n * @returns {boolean}\n */\nexport function isSafeUrl(raw, kind) {\n const value = decodeEntities(raw).trim();\n if (!value) return true;\n if (isMergeTag(value)) return true;\n // Scheme read without whitespace or control characters (\"java\\tscript:\").\n // eslint-disable-next-line no-control-regex -- control characters are exactly what is stripped\n const compact = value.replace(/[\\s\\u0000-\\u001f\\u007f]+/g, '').toLowerCase();\n if (kind === 'image') return compact.startsWith('https://') || isAssetPath(value);\n if (compact.startsWith('#')) return true;\n return /^(https?:\\/\\/|mailto:|tel:)/.test(compact);\n}\n"],"mappings":";;;;;AAqBO,IAAM,kBAAkB,oBAAI,IAAI,CAAC,QAAQ,UAAU,cAAc,YAAY,CAAC;AAG9E,IAAM,mBAAmB,oBAAI,IAAI;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,iBAAiB;AAAA,EACrB,OAAO;AAAA,EACP,KAAK;AAAA,EACL,SAAS;AAAA,EACT,MAAM;AAAA,EACN,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,MAAM;AACR;AAGO,SAAS,eAAe,OAAO;AACpC,SAAO,OAAO,SAAS,EAAE,EAAE,QAAQ,kCAAkC,CAAC,OAAO,SAAS;AACpF,UAAM,MAAM,OAAO,IAAI,EAAE,YAAY;AACrC,QAAI,IAAI,WAAW,IAAI,EAAG,QAAO,OAAO,cAAc,OAAO,SAAS,IAAI,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC;AAC5F,QAAI,IAAI,WAAW,GAAG,EAAG,QAAO,OAAO,cAAc,OAAO,SAAS,IAAI,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC;AAC3F,WAAO,eAAe,GAAG,KAAK;AAAA,EAChC,CAAC;AACH;AAGO,SAAS,WAAW,OAAO;AAChC,SAAO,iCAAiC,KAAK,OAAO,SAAS,EAAE,EAAE,KAAK,CAAC;AACzE;AAOO,SAAS,UAAU,KAAK,MAAM;AACnC,QAAM,QAAQ,eAAe,GAAG,EAAE,KAAK;AACvC,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,WAAW,KAAK,EAAG,QAAO;AAG9B,QAAM,UAAU,MAAM,QAAQ,6BAA6B,EAAE,EAAE,YAAY;AAC3E,MAAI,SAAS,QAAS,QAAO,QAAQ,WAAW,UAAU,KAAK,YAAY,KAAK;AAChF,MAAI,QAAQ,WAAW,GAAG,EAAG,QAAO;AACpC,SAAO,8BAA8B,KAAK,OAAO;AACnD;","names":[]}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_VARIABLES
|
|
3
|
+
} from "./chunk-3M36VVXS.js";
|
|
4
|
+
|
|
5
|
+
// src/features/variables/service.js
|
|
6
|
+
function escapeHtml(text) {
|
|
7
|
+
return String(text ?? "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
8
|
+
}
|
|
9
|
+
function decodeHtml(text) {
|
|
10
|
+
return String(text ?? "").replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
11
|
+
}
|
|
12
|
+
var escapeRegExp = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
13
|
+
function patternOf(key) {
|
|
14
|
+
return new RegExp(`\\{\\{\\s*${escapeRegExp(key)}\\s*(?:\\|([^{}]*))?\\}\\}`, "g");
|
|
15
|
+
}
|
|
16
|
+
var ANY_TAG = /\{\{\s*([A-Za-z][\w.]*)\s*(?:\|[^{}]*)?\}\}/g;
|
|
17
|
+
var textVariables = (variables) => variables.filter((v) => v.kind !== "link");
|
|
18
|
+
var present = (value) => value !== null && value !== void 0 && String(value) !== "";
|
|
19
|
+
function replace(text, values, variables, html) {
|
|
20
|
+
let out = String(text || "");
|
|
21
|
+
for (const variable of textVariables(variables)) {
|
|
22
|
+
out = out.replace(patternOf(variable.key), (_, inline) => {
|
|
23
|
+
const value = values?.[variable.key];
|
|
24
|
+
if (present(value)) return html ? escapeHtml(value) : String(value);
|
|
25
|
+
const fallback = inline !== void 0 ? (html ? decodeHtml(inline) : inline).trim() : variable.fallback || "";
|
|
26
|
+
return html ? escapeHtml(fallback) : fallback;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
function applyVariables(html, values, variables = DEFAULT_VARIABLES) {
|
|
32
|
+
return replace(html, values, variables, true);
|
|
33
|
+
}
|
|
34
|
+
function applyVariablesToText(text, values, variables = DEFAULT_VARIABLES) {
|
|
35
|
+
return replace(text, values, variables, false);
|
|
36
|
+
}
|
|
37
|
+
function mapVariables(html, mapping, variables = DEFAULT_VARIABLES) {
|
|
38
|
+
let out = String(html || "");
|
|
39
|
+
for (const variable of textVariables(variables)) {
|
|
40
|
+
if (!Object.prototype.hasOwnProperty.call(mapping, variable.key)) continue;
|
|
41
|
+
out = out.replace(patternOf(variable.key), () => String(mapping[variable.key]));
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
function usedVariables(text, variables = DEFAULT_VARIABLES) {
|
|
46
|
+
const source = String(text || "");
|
|
47
|
+
return variables.filter((v) => patternOf(v.key).test(source)).map((v) => v.key);
|
|
48
|
+
}
|
|
49
|
+
function unknownVariables(text, variables = DEFAULT_VARIABLES, ignore = ["unsubscribe", "mirror"]) {
|
|
50
|
+
const known = /* @__PURE__ */ new Set([...variables.map((v) => v.key), ...ignore]);
|
|
51
|
+
const found = /* @__PURE__ */ new Set();
|
|
52
|
+
for (const match of String(text || "").matchAll(ANY_TAG)) {
|
|
53
|
+
if (!known.has(match[1])) found.add(match[1]);
|
|
54
|
+
}
|
|
55
|
+
return [...found];
|
|
56
|
+
}
|
|
57
|
+
function sampleValues(variables = DEFAULT_VARIABLES, overrides = {}) {
|
|
58
|
+
const values = Object.fromEntries(textVariables(variables).map((v) => [v.key, v.sample]));
|
|
59
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
60
|
+
if (present(value)) values[key] = String(value);
|
|
61
|
+
}
|
|
62
|
+
return values;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export {
|
|
66
|
+
applyVariables,
|
|
67
|
+
applyVariablesToText,
|
|
68
|
+
mapVariables,
|
|
69
|
+
usedVariables,
|
|
70
|
+
unknownVariables,
|
|
71
|
+
sampleValues
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=chunk-BVOVAEEJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/features/variables/service.js"],"sourcesContent":["// @ts-check\n/**\n * Variables service — replaces merge tags in compiled HTML (or in a subject).\n *\n * Only the tags of the catalog are touched, in their `{{key}}` or\n * `{{key|fallback}}` form (spaces allowed): other tags (an email provider's own\n * merge tags, `{{ mirror }}`) are left intact.\n *\n * Values are escaped in HTML: a first name \"<b>Jo</b>\" is displayed as typed,\n * it makes nothing bold — and cannot inject anything.\n *\n * Missing value → the tag's inline fallback → the variable's `fallback` → ''.\n * A raw tag never reaches a recipient.\n *\n * The unsubscribe link is NOT replaced here: only the sending pipeline knows\n * the right link (see `applyServiceLinks`).\n */\n\nimport { DEFAULT_VARIABLES } from './catalog.js';\n\n/** @typedef {import('./catalog.js').EmailVariable} EmailVariable */\n/** @typedef {Record<string, string|number|null|undefined>} VariableValues */\n\nfunction escapeHtml(text) {\n return String(text ?? '')\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n\n/** Undoes the escaping the editor applied to an inline fallback. */\nfunction decodeHtml(text) {\n return String(text ?? '')\n .replace(/ /g, ' ')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/&/g, '&');\n}\n\nconst escapeRegExp = (text) => text.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\n/** `{{ key }}` or `{{ key | fallback }}`. @param {string} key */\nfunction patternOf(key) {\n return new RegExp(`\\\\{\\\\{\\\\s*${escapeRegExp(key)}\\\\s*(?:\\\\|([^{}]*))?\\\\}\\\\}`, 'g');\n}\n\n/** Any `{{ … }}` tag that looks like a variable. */\nconst ANY_TAG = /\\{\\{\\s*([A-Za-z][\\w.]*)\\s*(?:\\|[^{}]*)?\\}\\}/g;\n\n/** @param {EmailVariable[]} variables */\nconst textVariables = (variables) => variables.filter((v) => v.kind !== 'link');\n\nconst present = (value) => value !== null && value !== undefined && String(value) !== '';\n\n/**\n * @param {string} text\n * @param {VariableValues} values\n * @param {EmailVariable[]} variables\n * @param {boolean} html\n */\nfunction replace(text, values, variables, html) {\n let out = String(text || '');\n for (const variable of textVariables(variables)) {\n out = out.replace(patternOf(variable.key), (_, inline) => {\n const value = values?.[variable.key];\n if (present(value)) return html ? escapeHtml(value) : String(value);\n const fallback = inline !== undefined ? (html ? decodeHtml(inline) : inline).trim() : variable.fallback || '';\n return html ? escapeHtml(fallback) : fallback;\n });\n }\n return out;\n}\n\n/**\n * Replaces the known tags with values (escaped) in HTML.\n *\n * @param {string} html\n * @param {VariableValues} values\n * @param {EmailVariable[]} [variables]\n */\nexport function applyVariables(html, values, variables = DEFAULT_VARIABLES) {\n return replace(html, values, variables, true);\n}\n\n/**\n * Same as `applyVariables` for PLAIN TEXT (subject line, preview text): no\n * HTML escaping.\n *\n * @param {string} text\n * @param {VariableValues} values\n * @param {EmailVariable[]} [variables]\n */\nexport function applyVariablesToText(text, values, variables = DEFAULT_VARIABLES) {\n return replace(text, values, variables, false);\n}\n\n/**\n * Replaces the known tags with RAW replacements — typically the merge tags of\n * an email provider (`{ 'contact.firstname': '{{ contact.FIRSTNAME }}' }`).\n * A tag without replacement is left untouched.\n *\n * @param {string} html\n * @param {Record<string, string>} mapping\n * @param {EmailVariable[]} [variables]\n */\nexport function mapVariables(html, mapping, variables = DEFAULT_VARIABLES) {\n let out = String(html || '');\n for (const variable of textVariables(variables)) {\n if (!Object.prototype.hasOwnProperty.call(mapping, variable.key)) continue;\n out = out.replace(patternOf(variable.key), () => String(mapping[variable.key]));\n }\n return out;\n}\n\n/**\n * Catalog keys present in a text.\n * @param {string} text\n * @param {EmailVariable[]} [variables]\n */\nexport function usedVariables(text, variables = DEFAULT_VARIABLES) {\n const source = String(text || '');\n return variables.filter((v) => patternOf(v.key).test(source)).map((v) => v.key);\n}\n\n/**\n * Tags that look like variables but are not in the catalog (typos such as\n * `{{contact.fistname}}`). `ignore` lists tags handled elsewhere.\n *\n * @param {string} text\n * @param {EmailVariable[]} [variables]\n * @param {string[]} [ignore]\n */\nexport function unknownVariables(text, variables = DEFAULT_VARIABLES, ignore = ['unsubscribe', 'mirror']) {\n const known = new Set([...variables.map((v) => v.key), ...ignore]);\n const found = new Set();\n for (const match of String(text || '').matchAll(ANY_TAG)) {\n if (!known.has(match[1])) found.add(match[1]);\n }\n return [...found];\n}\n\n/**\n * Sample values (preview, test), with optional overrides\n * (e.g. `{ 'contact.email': 'reviewer@example.com' }`).\n *\n * @param {EmailVariable[]} [variables]\n * @param {VariableValues} [overrides]\n */\nexport function sampleValues(variables = DEFAULT_VARIABLES, overrides = {}) {\n /** @type {Record<string, string>} */\n const values = Object.fromEntries(textVariables(variables).map((v) => [v.key, v.sample]));\n for (const [key, value] of Object.entries(overrides)) {\n if (present(value)) values[key] = String(value);\n }\n return values;\n}\n"],"mappings":"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","names":[]}
|