@emailens/engine 0.11.7 → 0.11.8

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/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
- import { E as EmailClient, F as Framework, T as TransformResult, C as CSSWarning, a as CodeFix, D as DiffResult, b as ExportPromptOptions, A as AiProvider, c as AiFixResult, S as SupportLevel, d as SpamAnalysisOptions, e as SpamReport, L as LinkReport, f as AccessibilityReport, g as AccessibilityIssue, I as ImageReport, h as InboxPreview, i as SizeReport, j as TemplateReport, O as OverflowReport, V as VmlReport, k as VisualReport, l as DesignReport, m as DeliverabilityReport } from './types-bvGDId6f.cjs';
2
- export { B as BaseIssue, n as ClientTruncation, o as DeliverabilityCheck, p as DeliverabilityIssue, q as DesignIssue, r as EstimateOptions, s as ExportScope, t as FixType, u as ImageInfo, v as ImageIssue, w as InboxPreviewIssue, x as InputFormat, y as LinkIssue, z as OverflowIssue, P as PreviewResult, G as Severity, H as SizeIssue, J as SourceLocation, K as SpamIssue, M as TemplateIssue, N as TokenEstimate, Q as TokenEstimateWithWarnings, R as VisualIssue, U as VmlIssue, W as estimateAiFixTokens, X as generateFixPrompt, Y as heuristicTokenCount } from './types-bvGDId6f.cjs';
1
+ import { E as EmailClient, F as Framework, T as TransformResult, C as CSSWarning, a as CodeFix, D as DiffResult, b as ExportPromptOptions, A as AiProvider, c as AiFixResult, S as SupportLevel, d as SpamAnalysisOptions, e as SpamReport, L as LinkReport, f as AccessibilityReport, g as AccessibilityIssue, I as ImageReport, h as InboxPreview, i as SizeReport, j as TemplateReport, O as OverflowReport, V as VmlReport, k as VisualReport, l as DesignReport, m as DeliverabilityReport } from './types-DiseE-UH.cjs';
2
+ export { B as BaseIssue, n as ClientTruncation, o as DeliverabilityCheck, p as DeliverabilityIssue, q as DesignIssue, r as EstimateOptions, s as ExportScope, t as FixType, u as ImageInfo, v as ImageIssue, w as InboxPreviewIssue, x as InputFormat, y as LinkInfo, z as LinkIssue, G as OverflowIssue, P as PreviewResult, H as Severity, J as SizeIssue, K as SourceLocation, M as SpamIssue, N as TemplateIssue, Q as TokenEstimate, R as TokenEstimateWithWarnings, U as VisualIssue, W as VmlIssue, X as estimateAiFixTokens, Y as generateFixPrompt, Z as heuristicTokenCount } from './types-DiseE-UH.cjs';
3
3
  import * as cheerio from 'cheerio';
4
- export { C as CompileError, a as CompileReactEmailOptions, S as SandboxStrategy } from './react-email-DuZAgJtw.cjs';
4
+ export { C as CompileError, a as CompileReactEmailOptions, S as SandboxStrategy } from './react-email-DRkmpgX-.cjs';
5
5
 
6
6
  declare const EMAIL_CLIENTS: EmailClient[];
7
7
  declare function getClient(id: string): EmailClient | undefined;
@@ -410,12 +410,32 @@ declare function checkVml(html: string, options?: {
410
410
  * `roundrect` and `rect` are the whole surface. Anything else is left alone
411
411
  * rather than approximated.
412
412
  *
413
- * Deliberately NOT emulated: how Word *mis*-renders a nested shape. That is a
414
- * real and confirmed failure (the container disappears and text stops drawing
415
- * further down the document), but reproducing it means perturbing Chromium's
416
- * layout into a picture that is wrong in a third way, different from both the
417
- * correct render and Outlook's. `checkVml` reports it with a line number
418
- * instead, which is more useful and cannot mislead.
413
+ * How Word *mis*-renders a nested shape is emulated, but only the parts with
414
+ * evidence behind them. This note used to say the failure was not emulated at
415
+ * all, on the grounds that reproducing it meant perturbing Chromium into a
416
+ * picture wrong in a third way. That argument does not survive contact with
417
+ * the specific effects:
418
+ *
419
+ * - the container's fill and geometry disappear -> drop the box. Exact.
420
+ * - VML shapes further down stop drawing text -> blank their labels.
421
+ * Exact, and scoped to
422
+ * shapes we created.
423
+ * - the table structure terminates early, so later
424
+ * content escapes the frame -> WITHDRAWN. It had no
425
+ * methodology, and the
426
+ * fixture built to
427
+ * measure it contradicts
428
+ * it.
429
+ *
430
+ * Two of the three needed no distortion at all; the third turned out not to be
431
+ * a rendering problem but an evidence problem. Refusing to draw any of it
432
+ * meant the preview showed a tidy box with a button in it, a picture no Word
433
+ * engine produces, sitting beside a finding that said the region was
434
+ * destroyed. Between a warning and a screenshot, people believe the
435
+ * screenshot.
436
+ *
437
+ * Affected regions carry `data-vml-outlook` so a surface can label them, since
438
+ * a blank button should read as "Outlook does this" and not as our bug.
419
439
  */
420
440
  /**
421
441
  * `arcsize` is a VgFraction: a proportion of *half the shape's shorter side*,
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { E as EmailClient, F as Framework, T as TransformResult, C as CSSWarning, a as CodeFix, D as DiffResult, b as ExportPromptOptions, A as AiProvider, c as AiFixResult, S as SupportLevel, d as SpamAnalysisOptions, e as SpamReport, L as LinkReport, f as AccessibilityReport, g as AccessibilityIssue, I as ImageReport, h as InboxPreview, i as SizeReport, j as TemplateReport, O as OverflowReport, V as VmlReport, k as VisualReport, l as DesignReport, m as DeliverabilityReport } from './types-bvGDId6f.js';
2
- export { B as BaseIssue, n as ClientTruncation, o as DeliverabilityCheck, p as DeliverabilityIssue, q as DesignIssue, r as EstimateOptions, s as ExportScope, t as FixType, u as ImageInfo, v as ImageIssue, w as InboxPreviewIssue, x as InputFormat, y as LinkIssue, z as OverflowIssue, P as PreviewResult, G as Severity, H as SizeIssue, J as SourceLocation, K as SpamIssue, M as TemplateIssue, N as TokenEstimate, Q as TokenEstimateWithWarnings, R as VisualIssue, U as VmlIssue, W as estimateAiFixTokens, X as generateFixPrompt, Y as heuristicTokenCount } from './types-bvGDId6f.js';
1
+ import { E as EmailClient, F as Framework, T as TransformResult, C as CSSWarning, a as CodeFix, D as DiffResult, b as ExportPromptOptions, A as AiProvider, c as AiFixResult, S as SupportLevel, d as SpamAnalysisOptions, e as SpamReport, L as LinkReport, f as AccessibilityReport, g as AccessibilityIssue, I as ImageReport, h as InboxPreview, i as SizeReport, j as TemplateReport, O as OverflowReport, V as VmlReport, k as VisualReport, l as DesignReport, m as DeliverabilityReport } from './types-DiseE-UH.js';
2
+ export { B as BaseIssue, n as ClientTruncation, o as DeliverabilityCheck, p as DeliverabilityIssue, q as DesignIssue, r as EstimateOptions, s as ExportScope, t as FixType, u as ImageInfo, v as ImageIssue, w as InboxPreviewIssue, x as InputFormat, y as LinkInfo, z as LinkIssue, G as OverflowIssue, P as PreviewResult, H as Severity, J as SizeIssue, K as SourceLocation, M as SpamIssue, N as TemplateIssue, Q as TokenEstimate, R as TokenEstimateWithWarnings, U as VisualIssue, W as VmlIssue, X as estimateAiFixTokens, Y as generateFixPrompt, Z as heuristicTokenCount } from './types-DiseE-UH.js';
3
3
  import * as cheerio from 'cheerio';
4
- export { C as CompileError, a as CompileReactEmailOptions, S as SandboxStrategy } from './react-email-BROBcC8u.js';
4
+ export { C as CompileError, a as CompileReactEmailOptions, S as SandboxStrategy } from './react-email-B82A1R_K.js';
5
5
 
6
6
  declare const EMAIL_CLIENTS: EmailClient[];
7
7
  declare function getClient(id: string): EmailClient | undefined;
@@ -410,12 +410,32 @@ declare function checkVml(html: string, options?: {
410
410
  * `roundrect` and `rect` are the whole surface. Anything else is left alone
411
411
  * rather than approximated.
412
412
  *
413
- * Deliberately NOT emulated: how Word *mis*-renders a nested shape. That is a
414
- * real and confirmed failure (the container disappears and text stops drawing
415
- * further down the document), but reproducing it means perturbing Chromium's
416
- * layout into a picture that is wrong in a third way, different from both the
417
- * correct render and Outlook's. `checkVml` reports it with a line number
418
- * instead, which is more useful and cannot mislead.
413
+ * How Word *mis*-renders a nested shape is emulated, but only the parts with
414
+ * evidence behind them. This note used to say the failure was not emulated at
415
+ * all, on the grounds that reproducing it meant perturbing Chromium into a
416
+ * picture wrong in a third way. That argument does not survive contact with
417
+ * the specific effects:
418
+ *
419
+ * - the container's fill and geometry disappear -> drop the box. Exact.
420
+ * - VML shapes further down stop drawing text -> blank their labels.
421
+ * Exact, and scoped to
422
+ * shapes we created.
423
+ * - the table structure terminates early, so later
424
+ * content escapes the frame -> WITHDRAWN. It had no
425
+ * methodology, and the
426
+ * fixture built to
427
+ * measure it contradicts
428
+ * it.
429
+ *
430
+ * Two of the three needed no distortion at all; the third turned out not to be
431
+ * a rendering problem but an evidence problem. Refusing to draw any of it
432
+ * meant the preview showed a tidy box with a button in it, a picture no Word
433
+ * engine produces, sitting beside a finding that said the region was
434
+ * destroyed. Between a warning and a screenshot, people believe the
435
+ * screenshot.
436
+ *
437
+ * Affected regions carry `data-vml-outlook` so a surface can label them, since
438
+ * a blank button should read as "Outlook does this" and not as our bug.
419
439
  */
420
440
  /**
421
441
  * `arcsize` is a VgFraction: a proportion of *half the shape's shorter side*,
package/dist/index.js CHANGED
@@ -9840,10 +9840,23 @@ function vmlToCss(html) {
9840
9840
  solid ? `background-color:${solid}` : "",
9841
9841
  `padding:${padding}`
9842
9842
  ].filter(Boolean).join(";");
9843
+ if (/data-vml="/.test(content)) {
9844
+ return `<div data-vml="rect" data-vml-outlook="container-dropped">${content}</div>`;
9845
+ }
9843
9846
  return `<div data-vml="rect" style="${background}${box}">${content}</div>`;
9844
9847
  }
9845
9848
  );
9846
- return out.replace(/<\/?v:(?:fill|textbox|stroke|shadow|imagedata|path|formulas|handles)[^>]*?\/?>/gi, "");
9849
+ out = out.replace(/<\/?v:(?:fill|textbox|stroke|shadow|imagedata|path|formulas|handles)[^>]*?\/?>/gi, "");
9850
+ return blankLabelsAfterNestedShape(out);
9851
+ }
9852
+ function blankLabelsAfterNestedShape(html) {
9853
+ const at = html.indexOf('data-vml-outlook="container-dropped"');
9854
+ if (at === -1) return html;
9855
+ const tail = html.slice(at).replace(
9856
+ /(<div data-vml="roundrect")([^>]*>)([\s\S]*?)(<\/div>)/gi,
9857
+ (whole, open, rest, label, close) => label.trim() ? `${open}${rest.slice(0, -1)} data-vml-outlook="label-dropped">${close}` : whole
9858
+ );
9859
+ return html.slice(0, at) + tail;
9847
9860
  }
9848
9861
  function renderOutlookBranch(html) {
9849
9862
  if (!/<!--\[if/i.test(html)) return html;
@@ -9895,6 +9908,7 @@ var EMPTY_SPAM = { score: 100, level: "low", issues: [] };
9895
9908
  var EMPTY_LINKS = {
9896
9909
  totalLinks: 0,
9897
9910
  issues: [],
9911
+ links: [],
9898
9912
  breakdown: { https: 0, http: 0, mailto: 0, tel: 0, anchor: 0, javascript: 0, protocolRelative: 0, other: 0 }
9899
9913
  };
9900
9914
  var EMPTY_ACCESSIBILITY = { score: 100, issues: [] };
@@ -12995,6 +13009,7 @@ function isPlaceholderHref(href) {
12995
13009
  }
12996
13010
  function validateLinksFromDom($) {
12997
13011
  const issues = [];
13012
+ const inventory = [];
12998
13013
  const breakdown = { https: 0, http: 0, mailto: 0, tel: 0, anchor: 0, javascript: 0, protocolRelative: 0, other: 0 };
12999
13014
  const links = $("a");
13000
13015
  const totalLinks = links.length;
@@ -13004,13 +13019,22 @@ function validateLinksFromDom($) {
13004
13019
  rule: "no-links",
13005
13020
  message: "Email contains no links"
13006
13021
  });
13007
- return { totalLinks: 0, issues, breakdown };
13022
+ return { totalLinks: 0, issues, breakdown, links: [] };
13008
13023
  }
13009
13024
  const hrefCounts = /* @__PURE__ */ new Map();
13010
13025
  links.each((_, el) => {
13011
13026
  const href = $(el).attr("href") || "";
13012
13027
  const text = $(el).text().trim();
13013
13028
  const category = classifyHref(href);
13029
+ const entry = {
13030
+ href,
13031
+ text: text.slice(0, 80),
13032
+ scheme: category,
13033
+ isPlaceholder: isPlaceholderHref(href),
13034
+ issues: []
13035
+ };
13036
+ inventory.push(entry);
13037
+ const flag = (rule) => entry.issues.push(rule);
13014
13038
  const elLoc = locOfElement(el);
13015
13039
  const hrefLoc = href ? locOfAttr(el, "href") : elLoc;
13016
13040
  switch (category) {
@@ -13043,6 +13067,7 @@ function validateLinksFromDom($) {
13043
13067
  hrefCounts.set(href, (hrefCounts.get(href) || 0) + 1);
13044
13068
  }
13045
13069
  if (!href || !href.trim()) {
13070
+ flag("empty-href");
13046
13071
  issues.push(__spreadValues({
13047
13072
  severity: "error",
13048
13073
  rule: "empty-href",
@@ -13052,6 +13077,7 @@ function validateLinksFromDom($) {
13052
13077
  return;
13053
13078
  }
13054
13079
  if (category === "javascript" && !isPlaceholderHref(href)) {
13080
+ flag("javascript-href");
13055
13081
  issues.push(__spreadValues({
13056
13082
  severity: "error",
13057
13083
  rule: "javascript-href",
@@ -13062,6 +13088,7 @@ function validateLinksFromDom($) {
13062
13088
  return;
13063
13089
  }
13064
13090
  if (isPlaceholderHref(href)) {
13091
+ flag("placeholder-href");
13065
13092
  issues.push(__spreadValues({
13066
13093
  severity: "warning",
13067
13094
  rule: "placeholder-href",
@@ -13072,6 +13099,7 @@ function validateLinksFromDom($) {
13072
13099
  return;
13073
13100
  }
13074
13101
  if (category === "http") {
13102
+ flag("insecure-link");
13075
13103
  issues.push(__spreadValues({
13076
13104
  severity: "warning",
13077
13105
  rule: "insecure-link",
@@ -13081,6 +13109,7 @@ function validateLinksFromDom($) {
13081
13109
  }, hrefLoc ? { loc: hrefLoc } : {}));
13082
13110
  }
13083
13111
  if (category === "protocol-relative") {
13112
+ flag("protocol-relative");
13084
13113
  issues.push(__spreadValues({
13085
13114
  severity: "warning",
13086
13115
  rule: "protocol-relative",
@@ -13090,6 +13119,7 @@ function validateLinksFromDom($) {
13090
13119
  }, hrefLoc ? { loc: hrefLoc } : {}));
13091
13120
  }
13092
13121
  if (text && GENERIC_LINK_TEXT.has(text.toLowerCase())) {
13122
+ flag("generic-link-text");
13093
13123
  issues.push(__spreadValues({
13094
13124
  severity: "warning",
13095
13125
  rule: "generic-link-text",
@@ -13099,6 +13129,7 @@ function validateLinksFromDom($) {
13099
13129
  }, elLoc ? { loc: elLoc } : {}));
13100
13130
  }
13101
13131
  if (!text && !$(el).attr("aria-label") && !$(el).find("img[alt]").length) {
13132
+ flag("empty-link-text");
13102
13133
  issues.push(__spreadValues({
13103
13134
  severity: "error",
13104
13135
  rule: "empty-link-text",
@@ -13107,6 +13138,7 @@ function validateLinksFromDom($) {
13107
13138
  }, elLoc ? { loc: elLoc } : {}));
13108
13139
  }
13109
13140
  if (category === "mailto" && href.trim().toLowerCase() === "mailto:") {
13141
+ flag("empty-mailto");
13110
13142
  issues.push(__spreadValues({
13111
13143
  severity: "error",
13112
13144
  rule: "empty-mailto",
@@ -13116,6 +13148,7 @@ function validateLinksFromDom($) {
13116
13148
  }, hrefLoc ? { loc: hrefLoc } : {}));
13117
13149
  }
13118
13150
  if (category === "tel" && href.trim().toLowerCase() === "tel:") {
13151
+ flag("empty-tel");
13119
13152
  issues.push(__spreadValues({
13120
13153
  severity: "error",
13121
13154
  rule: "empty-tel",
@@ -13125,6 +13158,7 @@ function validateLinksFromDom($) {
13125
13158
  }, hrefLoc ? { loc: hrefLoc } : {}));
13126
13159
  }
13127
13160
  if (href.length > 2e3) {
13161
+ flag("long-url");
13128
13162
  issues.push(__spreadValues({
13129
13163
  severity: "info",
13130
13164
  rule: "long-url",
@@ -13134,7 +13168,8 @@ function validateLinksFromDom($) {
13134
13168
  }, hrefLoc ? { loc: hrefLoc } : {}));
13135
13169
  }
13136
13170
  });
13137
- links.each((_, el) => {
13171
+ links.each((i, el) => {
13172
+ var _a;
13138
13173
  const href = $(el).attr("href") || "";
13139
13174
  const trimmed = href.trim();
13140
13175
  if (trimmed.startsWith("#") && trimmed.length > 1) {
@@ -13142,6 +13177,7 @@ function validateLinksFromDom($) {
13142
13177
  const target = $(`[id="${targetId}"]`);
13143
13178
  const anchorLoc = locOfAttr(el, "href");
13144
13179
  if (target.length === 0) {
13180
+ (_a = inventory[i]) == null ? void 0 : _a.issues.push("broken-anchor");
13145
13181
  issues.push(__spreadValues({
13146
13182
  severity: "error",
13147
13183
  rule: "broken-anchor",
@@ -13162,7 +13198,7 @@ function validateLinksFromDom($) {
13162
13198
  });
13163
13199
  }
13164
13200
  }
13165
- return { totalLinks, issues, breakdown };
13201
+ return { totalLinks, issues, breakdown, links: inventory };
13166
13202
  }
13167
13203
  function validateLinks(html, options) {
13168
13204
  return fromHtml(html, EMPTY_LINKS, validateLinksFromDom, options);
@@ -14701,7 +14737,20 @@ function checkVml(html, options) {
14701
14737
  rule: "vml-nested-shape",
14702
14738
  severity: "error",
14703
14739
  message: `<v:${name}> is nested inside <v:${parent.name}>. Outlook does not support nesting one VML shape inside another.`,
14704
- detail: `Verified in Outlook Classic, and the damage is not local. Three things happen: the containing <v:${parent.name}> does not render at all (its fill and everything inside it disappear, leaving the inner shape stranded); the table structure around it terminates early, so content *after* the shape falls out of the layout and renders outside the email frame; and every VML shape further down the email stops drawing its text, so buttons and headings after this point ship as blank coloured blocks. Confirmed with byte-identical probes either side of one nested shape: the one before it renders its labels, the one after it does not. Every other client renders the HTML fallback correctly, which is why none of this is visible outside the Word engine. Fix: lift the inner shape out of <v:${parent.name}>, or drop the container shape and keep the inner one (a framed background can degrade to a solid fill colour on the <td> instead).`
14740
+ // Two effects, not the three this used to claim. The withdrawn one
14741
+ // said the table structure terminates early so content after the
14742
+ // shape falls outside the email frame. It had no methodology behind
14743
+ // it, and `T1c-blast-radius.html`, the fixture built to measure
14744
+ // exactly this, contradicts it: the block after the nested shape
14745
+ // rendered in place inside the same table with its background and
14746
+ // borders intact, and only the VML labels were missing. The likely
14747
+ // origin is over-reading "layout breaks after it" from a run where a
14748
+ // 600px hero band vanished and everything below shifted up.
14749
+ //
14750
+ // Kept as a warning to whoever edits this next: the probe sentence
14751
+ // below covers the downstream text suppression only. Do not let it
14752
+ // lend its authority to a neighbouring clause again.
14753
+ detail: `Verified in Outlook Classic, and the damage is not local. Two things happen: the containing <v:${parent.name}> does not render at all (its fill and everything inside it disappear, leaving the inner shape stranded); and every VML shape further down the email stops drawing its text, so buttons and headings after this point ship as blank coloured blocks. That second effect was confirmed with byte-identical probes either side of one nested shape: the one before it renders its labels, the one after it does not. Every other client renders the HTML fallback correctly, which is why none of this is visible outside the Word engine. Fix: lift the inner shape out of <v:${parent.name}>, or drop the container shape and keep the inner one (a framed background can degrade to a solid fill colour on the <td> instead).`
14705
14754
  }, loc));
14706
14755
  }
14707
14756
  if (!tag.selfClosing) openShapes.push(tag);