@templatical/quality 0.7.2 → 0.8.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/dist/index.js CHANGED
@@ -1,67 +1,74 @@
1
- import { HEADING_LEVEL_FONT_SIZE as e, isButton as t, isImage as n, isMenu as r, isParagraph as i, isSection as a, isTable as o, isTitle as s } from "@templatical/types";
2
- import { Parser as c } from "htmlparser2";
1
+ import { HEADING_LEVEL_FONT_SIZE as e, isButton as t, isHtml as n, isImage as r, isMenu as i, isParagraph as a, isSection as o, isSocialIcons as s, isTable as ee, isTitle as c, isVideo as te } from "@templatical/types";
2
+ import { Parser as l } from "htmlparser2";
3
3
  //#region \0rolldown/runtime.js
4
- var l = Object.defineProperty, u = (e, t) => {
4
+ var u = Object.defineProperty, d = (e, t) => {
5
5
  let n = {};
6
- for (var r in e) l(n, r, {
6
+ for (var r in e) u(n, r, {
7
7
  get: e[r],
8
8
  enumerable: !0
9
9
  });
10
- return t || l(n, Symbol.toStringTag, { value: "Module" }), n;
11
- }, d = {
10
+ return t || u(n, Symbol.toStringTag, { value: "Module" }), n;
11
+ }, f = {
12
12
  altMaxLength: 125,
13
13
  minFontSize: 14,
14
14
  allCapsMinLength: 20,
15
15
  minTouchTargetPx: 44
16
- };
16
+ }, p = [
17
+ "localhost",
18
+ "127.0.0.1",
19
+ "0.0.0.0",
20
+ "*.local",
21
+ "*.staging.*",
22
+ "*.dev.*"
23
+ ];
17
24
  //#endregion
18
25
  //#region src/contrast.ts
19
- function f(e, t) {
20
- let n = h(e), r = h(t);
26
+ function m(e, t) {
27
+ let n = g(e), r = g(t);
21
28
  if (!n || !r) return NaN;
22
- let i = _(n), a = _(r), o = Math.max(i, a), s = Math.min(i, a);
29
+ let i = v(n), a = v(r), o = Math.max(i, a), s = Math.min(i, a);
23
30
  return (o + .05) / (s + .05);
24
31
  }
25
- var p = /^#?([0-9a-f])([0-9a-f])([0-9a-f])$/i, m = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i;
26
- function h(e) {
32
+ var ne = /^#?([0-9a-f])([0-9a-f])([0-9a-f])$/i, h = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i;
33
+ function g(e) {
27
34
  if (typeof e != "string") return null;
28
- let t = e.trim(), n = m.exec(t);
35
+ let t = e.trim(), n = h.exec(t);
29
36
  if (n) return {
30
37
  r: parseInt(n[1], 16),
31
38
  g: parseInt(n[2], 16),
32
39
  b: parseInt(n[3], 16)
33
40
  };
34
- let r = p.exec(t);
41
+ let r = ne.exec(t);
35
42
  return r ? {
36
43
  r: parseInt(r[1] + r[1], 16),
37
44
  g: parseInt(r[2] + r[2], 16),
38
45
  b: parseInt(r[3] + r[3], 16)
39
46
  } : null;
40
47
  }
41
- function g(e) {
42
- return h(e ?? "") !== null;
48
+ function _(e) {
49
+ return g(e ?? "") !== null;
43
50
  }
44
- function _({ r: e, g: t, b: n }) {
45
- let r = v(e / 255), i = v(t / 255), a = v(n / 255);
51
+ function v({ r: e, g: t, b: n }) {
52
+ let r = y(e / 255), i = y(t / 255), a = y(n / 255);
46
53
  return .2126 * r + .7152 * i + .0722 * a;
47
54
  }
48
- function v(e) {
55
+ function y(e) {
49
56
  return e <= .03928 ? e / 12.92 : ((e + .055) / 1.055) ** 2.4;
50
57
  }
51
58
  //#endregion
52
59
  //#region src/walk.ts
53
- var y = "#ffffff";
60
+ var re = "#ffffff";
54
61
  function b(e, t) {
55
- let n = g(e.settings.backgroundColor) ? e.settings.backgroundColor.toLowerCase() : y, r = (e, n) => {
56
- if (t(e, n), !a(e)) return;
57
- let i = e.styles?.backgroundColor, o = g(i) ? i.toLowerCase() : n.resolvedBackgroundColor;
62
+ let n = _(e.settings.backgroundColor) ? e.settings.backgroundColor.toLowerCase() : re, r = (e, n) => {
63
+ if (t(e, n), !o(e)) return;
64
+ let i = e.styles?.backgroundColor, a = _(i) ? i.toLowerCase() : n.resolvedBackgroundColor;
58
65
  e.children.forEach((t, i) => {
59
66
  t.forEach((t) => r(t, {
60
67
  parent: e,
61
68
  section: e,
62
69
  columnIndex: i,
63
70
  depth: n.depth + 1,
64
- resolvedBackgroundColor: o
71
+ resolvedBackgroundColor: a
65
72
  }));
66
73
  });
67
74
  };
@@ -74,91 +81,137 @@ function b(e, t) {
74
81
  });
75
82
  }
76
83
  //#endregion
84
+ //#region src/run-rules.ts
85
+ function x(e, t, n, r) {
86
+ if (n.disabled === !0) return [];
87
+ let i = ie(n, t), a = [];
88
+ function o(e, t, n) {
89
+ return {
90
+ blockId: n.blockId,
91
+ ruleId: e,
92
+ severity: t,
93
+ message: r(i.locale, e, n.params),
94
+ fix: n.fix
95
+ };
96
+ }
97
+ b(e, (e, n) => {
98
+ for (let r of t) {
99
+ let t = i.severity(r.meta.id);
100
+ if (t === "off" || !r.block) continue;
101
+ let s = r.block(e, n, i);
102
+ s !== null && a.push(o(r.meta.id, t, s));
103
+ }
104
+ });
105
+ for (let n of t) {
106
+ let t = i.severity(n.meta.id);
107
+ if (t === "off" || !n.template) continue;
108
+ let r = n.template(e, i);
109
+ for (let e of r) a.push(o(n.meta.id, t, e));
110
+ }
111
+ return a;
112
+ }
113
+ function ie(e, t) {
114
+ let n = e.rules ?? {}, r = {
115
+ ...f,
116
+ ...e.thresholds ?? {}
117
+ }, i = { nonProductionHosts: e.links?.nonProductionHosts ?? p };
118
+ return {
119
+ locale: e.locale ?? "en",
120
+ rules: n,
121
+ thresholds: r,
122
+ links: i,
123
+ severity: (e) => {
124
+ let r = n[e];
125
+ return r === void 0 ? t.find((t) => t.meta.id === e)?.meta.severity ?? "warning" : r;
126
+ }
127
+ };
128
+ }
129
+ //#endregion
77
130
  //#region src/accessibility/messages/de.ts
78
- var x = /* @__PURE__ */ u({ default: () => S }), S = {
79
- "img-missing-alt": "Bild ohne Alt-Text. Füge eine kurze Beschreibung hinzu oder markiere das Bild als dekorativ.",
80
- "img-alt-is-filename": "Alt-Text sieht wie ein Dateiname aus (\"{alt}\"). Beschreibe stattdessen kurz, was das Bild zeigt.",
81
- "img-alt-too-long": "Alt-Text ist {length} Zeichen lang; bleibe unter {max}.",
82
- "img-decorative-needs-empty-alt": "Dekoratives Bild hat Alt-Text. Entferne den Alt-Text oder hebe die Markierung als dekorativ auf.",
83
- "img-linked-no-context": "Verlinktes Bild beschreibt nur das Motiv, nicht das Linkziel. Nenne das Ziel (z. B. „Frühlingssale ansehen“).",
84
- "heading-empty": "Überschrift ist leer. Füge Text hinzu oder entferne den Block.",
85
- "heading-skip-level": "Überschrift springt von H{from} auf H{to}. Eine Ebene pro Schritt.",
86
- "heading-multiple-h1": "E-Mail enthält mehr als eine H1. Verwende H1 nur einmal für die Hauptüberschrift.",
87
- "link-empty": "Ein Link in diesem Block hat keinen Text und kein beschriebenes Bild.",
88
- "link-vague-text": "Link-Text „{text}“ ist unspezifisch. Beschreibe stattdessen das Ziel.",
89
- "link-href-empty": "Ein Link in diesem Block hat ein leeres oder „#“-href.",
90
- "link-target-blank-no-rel": "Link öffnet in neuem Tab, aber rel=\"noopener\" fehlt – ergänze es, damit das Ziel nicht auf window.opener zugreifen kann.",
91
- "text-all-caps": "Längere Texte in Großbuchstaben sind schwerer lesbar. Verwende Groß- und Kleinschreibung.",
92
- "text-low-contrast": "Überschriftskontrast beträgt {ratio}:1; WCAG AA verlangt mindestens {required}:1.",
93
- "text-too-small": "Text ist {size}px; mindestens {min}px verwenden.",
94
- "button-vague-label": "Button-Beschriftung „{text}“ ist unspezifisch. Beschreibe die Aktion.",
95
- "button-touch-target": "Button ist etwa {height}px hoch; mindestens {min}px verwenden, um Fehltipper auf Mobilgeräten zu vermeiden.",
96
- "button-low-contrast": "Buttontextkontrast beträgt {ratio}:1; WCAG AA verlangt mindestens {required}:1.",
97
- "missing-preheader": "Kein Preheader-Text gesetzt. Postfächer zeigen sonst Bruchstücke des ersten Blocks an."
98
- }, C = /* @__PURE__ */ u({ default: () => w }), w = {
99
- "img-missing-alt": "Image is missing alt text. Add a short description, or mark the image as decorative.",
100
- "img-alt-is-filename": "Alt text looks like a filename (\"{alt}\"). Replace with a short description of what the image conveys.",
101
- "img-alt-too-long": "Alt text is {length} characters; aim for under {max}.",
102
- "img-decorative-needs-empty-alt": "Decorative image has alt text. Either clear the alt text or unmark the image as decorative.",
103
- "img-linked-no-context": "Linked image alt describes the image but not the link destination. Include where the link goes (e.g. 'Buy spring sale').",
104
- "heading-empty": "Heading is empty. Add text or remove the block.",
105
- "heading-skip-level": "Heading jumps from H{from} to H{to}. Step one level at a time.",
106
- "heading-multiple-h1": "Email has more than one H1. Use H1 once for the main heading.",
107
- "link-empty": "A link in this block has no text and no described image.",
108
- "link-vague-text": "Link text \"{text}\" is vague. Describe the destination instead.",
109
- "link-href-empty": "A link in this block has an empty or '#' href.",
110
- "link-target-blank-no-rel": "Link opens in a new tab but is missing rel=\"noopener\" — add it to prevent the destination from accessing window.opener.",
111
- "text-all-caps": "Long all-caps text is harder to read for everyone. Use sentence case.",
112
- "text-low-contrast": "Heading contrast is {ratio}:1; WCAG AA requires at least {required}:1.",
113
- "text-too-small": "Text is {size}px; aim for at least {min}px.",
114
- "button-vague-label": "Button label \"{text}\" is vague. Describe the action.",
115
- "button-touch-target": "Button is roughly {height}px tall; aim for at least {min}px to avoid mis-taps on mobile.",
116
- "button-low-contrast": "Button text contrast is {ratio}:1; WCAG AA requires at least {required}:1.",
117
- "missing-preheader": "No preheader text set. Inboxes will fall back to fragments of the first block."
118
- }, T = /* @__PURE__ */ Object.assign({
119
- "./de.ts": x,
120
- "./en.ts": C
121
- }), E = {};
122
- for (let e in T) {
131
+ var ae = /* @__PURE__ */ d({ default: () => oe }), oe = {
132
+ "a11y.img-missing-alt": "Bild ohne Alt-Text. Füge eine kurze Beschreibung hinzu oder markiere das Bild als dekorativ.",
133
+ "a11y.img-alt-is-filename": "Alt-Text sieht wie ein Dateiname aus (\"{alt}\"). Beschreibe stattdessen kurz, was das Bild zeigt.",
134
+ "a11y.img-alt-too-long": "Alt-Text ist {length} Zeichen lang; bleibe unter {max}.",
135
+ "a11y.img-decorative-needs-empty-alt": "Dekoratives Bild hat Alt-Text. Entferne den Alt-Text oder hebe die Markierung als dekorativ auf.",
136
+ "a11y.img-linked-no-context": "Verlinktes Bild beschreibt nur das Motiv, nicht das Linkziel. Nenne das Ziel (z. B. „Frühlingssale ansehen“).",
137
+ "a11y.heading-empty": "Überschrift ist leer. Füge Text hinzu oder entferne den Block.",
138
+ "a11y.heading-skip-level": "Überschrift springt von H{from} auf H{to}. Eine Ebene pro Schritt.",
139
+ "a11y.heading-multiple-h1": "E-Mail enthält mehr als eine H1. Verwende H1 nur einmal für die Hauptüberschrift.",
140
+ "a11y.link-empty": "Ein Link in diesem Block hat keinen Text und kein beschriebenes Bild.",
141
+ "a11y.link-vague-text": "Link-Text „{text}“ ist unspezifisch. Beschreibe stattdessen das Ziel.",
142
+ "a11y.link-href-empty": "Ein Link in diesem Block hat ein leeres oder „#“-href.",
143
+ "a11y.link-target-blank-no-rel": "Link öffnet in neuem Tab, aber rel=\"noopener\" fehlt – ergänze es, damit das Ziel nicht auf window.opener zugreifen kann.",
144
+ "a11y.text-all-caps": "Längere Texte in Großbuchstaben sind schwerer lesbar. Verwende Groß- und Kleinschreibung.",
145
+ "a11y.text-low-contrast": "Überschriftskontrast beträgt {ratio}:1; WCAG AA verlangt mindestens {required}:1.",
146
+ "a11y.text-too-small": "Text ist {size}px; mindestens {min}px verwenden.",
147
+ "a11y.button-vague-label": "Button-Beschriftung „{text}“ ist unspezifisch. Beschreibe die Aktion.",
148
+ "a11y.button-touch-target": "Button ist etwa {height}px hoch; mindestens {min}px verwenden, um Fehltipper auf Mobilgeräten zu vermeiden.",
149
+ "a11y.button-low-contrast": "Buttontextkontrast beträgt {ratio}:1; WCAG AA verlangt mindestens {required}:1.",
150
+ "a11y.missing-preheader": "Kein Preheader-Text gesetzt. Postfächer zeigen sonst Bruchstücke des ersten Blocks an."
151
+ }, se = /* @__PURE__ */ d({ default: () => S }), S = {
152
+ "a11y.img-missing-alt": "Image is missing alt text. Add a short description, or mark the image as decorative.",
153
+ "a11y.img-alt-is-filename": "Alt text looks like a filename (\"{alt}\"). Replace with a short description of what the image conveys.",
154
+ "a11y.img-alt-too-long": "Alt text is {length} characters; aim for under {max}.",
155
+ "a11y.img-decorative-needs-empty-alt": "Decorative image has alt text. Either clear the alt text or unmark the image as decorative.",
156
+ "a11y.img-linked-no-context": "Linked image alt describes the image but not the link destination. Include where the link goes (e.g. 'Buy spring sale').",
157
+ "a11y.heading-empty": "Heading is empty. Add text or remove the block.",
158
+ "a11y.heading-skip-level": "Heading jumps from H{from} to H{to}. Step one level at a time.",
159
+ "a11y.heading-multiple-h1": "Email has more than one H1. Use H1 once for the main heading.",
160
+ "a11y.link-empty": "A link in this block has no text and no described image.",
161
+ "a11y.link-vague-text": "Link text \"{text}\" is vague. Describe the destination instead.",
162
+ "a11y.link-href-empty": "A link in this block has an empty or '#' href.",
163
+ "a11y.link-target-blank-no-rel": "Link opens in a new tab but is missing rel=\"noopener\" — add it to prevent the destination from accessing window.opener.",
164
+ "a11y.text-all-caps": "Long all-caps text is harder to read for everyone. Use sentence case.",
165
+ "a11y.text-low-contrast": "Heading contrast is {ratio}:1; WCAG AA requires at least {required}:1.",
166
+ "a11y.text-too-small": "Text is {size}px; aim for at least {min}px.",
167
+ "a11y.button-vague-label": "Button label \"{text}\" is vague. Describe the action.",
168
+ "a11y.button-touch-target": "Button is roughly {height}px tall; aim for at least {min}px to avoid mis-taps on mobile.",
169
+ "a11y.button-low-contrast": "Button text contrast is {ratio}:1; WCAG AA requires at least {required}:1.",
170
+ "a11y.missing-preheader": "No preheader text set. Inboxes will fall back to fragments of the first block."
171
+ }, C = /* @__PURE__ */ Object.assign({
172
+ "./de.ts": ae,
173
+ "./en.ts": se
174
+ }), w = {};
175
+ for (let e in C) {
123
176
  let t = /\.\/([^/]+)\.ts$/.exec(e);
124
177
  if (!t) continue;
125
178
  let n = t[1];
126
- n !== "index" && (E[n] = T[e].default);
179
+ n !== "index" && (w[n] = C[e].default);
127
180
  }
128
- var D = Object.keys(E);
129
- function O(e) {
130
- return E[e.split("-")[0]?.toLowerCase() ?? "en"] ?? E.en ?? w;
181
+ var ce = Object.keys(w);
182
+ function T(e) {
183
+ return w[e.split("-")[0]?.toLowerCase() ?? "en"] ?? w.en ?? S;
131
184
  }
132
- function k(e, t, n) {
133
- let r = O(e)[t] ?? w[t];
185
+ function E(e, t, n) {
186
+ let r = T(e)[t] ?? S[t];
134
187
  return n ? r.replace(/\{(\w+)\}/g, (e, t) => {
135
188
  let r = n[t];
136
189
  return r === void 0 ? `{${t}}` : String(r);
137
190
  }) : r;
138
191
  }
139
- var A = {
192
+ var le = {
140
193
  meta: {
141
- id: "img-missing-alt",
194
+ id: "a11y.img-missing-alt",
142
195
  severity: "error"
143
196
  },
144
197
  block(e) {
145
- return !n(e) || e.decorative === !0 || (e.alt?.trim() ?? "") !== "" || (e.src ?? "").trim() === "" ? null : { blockId: e.id };
198
+ return !r(e) || e.decorative === !0 || (e.alt?.trim() ?? "") !== "" || (e.src ?? "").trim() === "" ? null : { blockId: e.id };
146
199
  }
147
- }, ee = {
148
- id: "img-alt-is-filename",
200
+ }, ue = {
201
+ id: "a11y.img-alt-is-filename",
149
202
  severity: "warning"
150
- }, te = [
203
+ }, de = [
151
204
  /\.(jpe?g|png|gif|webp|svg)$/i,
152
205
  /^IMG[_-]?\d+/i,
153
206
  /^Untitled/i,
154
207
  /^Screen[\s_-]?Shot/i,
155
208
  /^DSC[_-]?\d+/i
156
- ], ne = {
157
- meta: ee,
209
+ ], fe = {
210
+ meta: ue,
158
211
  block(e) {
159
- if (!n(e) || e.decorative === !0) return null;
212
+ if (!r(e) || e.decorative === !0) return null;
160
213
  let t = e.alt?.trim() ?? "";
161
- return t === "" || !te.some((e) => e.test(t)) ? null : {
214
+ return t === "" || !de.some((e) => e.test(t)) ? null : {
162
215
  blockId: e.id,
163
216
  params: { alt: t },
164
217
  fix: {
@@ -167,29 +220,29 @@ var A = {
167
220
  }
168
221
  };
169
222
  }
170
- }, re = {
223
+ }, pe = {
171
224
  meta: {
172
- id: "img-alt-too-long",
225
+ id: "a11y.img-alt-too-long",
173
226
  severity: "warning"
174
227
  },
175
- block(e, t, r) {
176
- if (!n(e) || e.decorative === !0) return null;
228
+ block(e, t, n) {
229
+ if (!r(e) || e.decorative === !0) return null;
177
230
  let i = e.alt ?? "";
178
- return i.length <= r.thresholds.altMaxLength ? null : {
231
+ return i.length <= n.thresholds.altMaxLength ? null : {
179
232
  blockId: e.id,
180
233
  params: {
181
234
  length: i.length,
182
- max: r.thresholds.altMaxLength
235
+ max: n.thresholds.altMaxLength
183
236
  }
184
237
  };
185
238
  }
186
- }, ie = {
239
+ }, me = {
187
240
  meta: {
188
- id: "img-decorative-needs-empty-alt",
241
+ id: "a11y.img-decorative-needs-empty-alt",
189
242
  severity: "info"
190
243
  },
191
244
  block(e) {
192
- return !n(e) || e.decorative !== !0 || (e.alt ?? "") === "" ? null : {
245
+ return !r(e) || e.decorative !== !0 || (e.alt ?? "") === "" ? null : {
193
246
  blockId: e.id,
194
247
  fix: {
195
248
  description: "Clear alt text",
@@ -197,7 +250,7 @@ var A = {
197
250
  }
198
251
  };
199
252
  }
200
- }, j = /* @__PURE__ */ u({ default: () => M }), M = {
253
+ }, he = /* @__PURE__ */ d({ default: () => ge }), ge = {
201
254
  vagueLinkText: [
202
255
  "hier klicken",
203
256
  "hier",
@@ -240,7 +293,7 @@ var A = {
240
293
  "anschauen",
241
294
  "jetzt"
242
295
  ]
243
- }, N = /* @__PURE__ */ u({ default: () => P }), P = {
296
+ }, _e = /* @__PURE__ */ d({ default: () => ve }), ve = {
244
297
  vagueLinkText: [
245
298
  "click here",
246
299
  "here",
@@ -281,49 +334,49 @@ var A = {
281
334
  "redeem",
282
335
  "watch"
283
336
  ]
284
- }, F = /* @__PURE__ */ Object.assign({
285
- "./de.ts": j,
286
- "./en.ts": N
287
- }), I = {};
288
- for (let e in F) {
337
+ }, D = /* @__PURE__ */ Object.assign({
338
+ "./de.ts": he,
339
+ "./en.ts": _e
340
+ }), O = {};
341
+ for (let e in D) {
289
342
  let t = /\.\/([^/]+)\.ts$/.exec(e);
290
343
  if (!t) continue;
291
344
  let n = t[1];
292
- n !== "index" && (I[n] = F[e].default);
345
+ n !== "index" && (O[n] = D[e].default);
293
346
  }
294
- function L(e) {
295
- return z;
347
+ function k(e) {
348
+ return ye;
296
349
  }
297
- function R(e) {
350
+ function A(e) {
298
351
  let t = /* @__PURE__ */ new Set();
299
- for (let n of Object.values(I)) for (let r of e(n)) t.add(r);
352
+ for (let n of Object.values(O)) for (let r of e(n)) t.add(r);
300
353
  return Array.from(t);
301
354
  }
302
- var z = {
303
- vagueLinkText: R((e) => e.vagueLinkText),
304
- vagueButtonLabels: R((e) => e.vagueButtonLabels),
305
- linkedImageActionHints: R((e) => e.linkedImageActionHints)
306
- }, B = Object.keys(I);
307
- function V(e) {
355
+ var ye = {
356
+ vagueLinkText: A((e) => e.vagueLinkText),
357
+ vagueButtonLabels: A((e) => e.vagueButtonLabels),
358
+ linkedImageActionHints: A((e) => e.linkedImageActionHints)
359
+ }, be = Object.keys(O);
360
+ function j(e) {
308
361
  return e.toLowerCase().replace(/\s+/g, " ").replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu, "").trim();
309
362
  }
310
- var H = {
363
+ var xe = {
311
364
  meta: {
312
- id: "img-linked-no-context",
365
+ id: "a11y.img-linked-no-context",
313
366
  severity: "warning"
314
367
  },
315
- block(e, t, r) {
316
- if (!n(e) || e.decorative === !0 || !e.linkUrl || e.linkUrl.trim() === "") return null;
368
+ block(e, t, n) {
369
+ if (!r(e) || e.decorative === !0 || !e.linkUrl || e.linkUrl.trim() === "") return null;
317
370
  let i = (e.alt ?? "").trim();
318
371
  if (i === "") return null;
319
- let a = i.toLocaleLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean), o = L(r.locale).linkedImageActionHints;
372
+ let a = i.toLocaleLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean), o = k(n.locale).linkedImageActionHints;
320
373
  return a.some((e) => o.includes(e)) ? null : { blockId: e.id };
321
374
  }
322
375
  };
323
376
  //#endregion
324
377
  //#region src/html-utils.ts
325
- function U(e) {
326
- let t = [], n = [], r = new c({
378
+ function M(e) {
379
+ let t = [], n = [], r = new l({
327
380
  onopentag(e, t) {
328
381
  if (e === "a") {
329
382
  let e = {
@@ -353,38 +406,38 @@ function U(e) {
353
406
  });
354
407
  return r.write(e), r.end(), t;
355
408
  }
356
- function W(e) {
357
- let t = "", n = new c({ ontext(e) {
409
+ function N(e) {
410
+ let t = "", n = new l({ ontext(e) {
358
411
  t += e;
359
412
  } });
360
413
  return n.write(e), n.end(), t.trim();
361
414
  }
362
- var G = {
415
+ var Se = {
363
416
  meta: {
364
- id: "heading-empty",
417
+ id: "a11y.heading-empty",
365
418
  severity: "error"
366
419
  },
367
420
  block(e) {
368
- return !s(e) || W(e.content ?? "") !== "" ? null : { blockId: e.id };
421
+ return !c(e) || N(e.content ?? "") !== "" ? null : { blockId: e.id };
369
422
  }
370
- }, K = {
371
- id: "heading-skip-level",
423
+ }, Ce = {
424
+ id: "a11y.heading-skip-level",
372
425
  severity: "error"
373
426
  };
374
- function q(e, t) {
427
+ function P(e, t) {
375
428
  for (let n of e) {
376
- if (s(n)) {
429
+ if (c(n)) {
377
430
  t.push(n);
378
431
  continue;
379
432
  }
380
- if (a(n)) for (let e of n.children) q(e, t);
433
+ if (o(n)) for (let e of n.children) P(e, t);
381
434
  }
382
435
  }
383
- var J = {
384
- meta: K,
436
+ var we = {
437
+ meta: Ce,
385
438
  template(e) {
386
439
  let t = [];
387
- q(e.blocks, t);
440
+ P(e.blocks, t);
388
441
  let n = [], r = 0;
389
442
  for (let e of t) r !== 0 && e.level > r + 1 && n.push({
390
443
  blockId: e.id,
@@ -395,54 +448,54 @@ var J = {
395
448
  }), r = e.level;
396
449
  return n;
397
450
  }
398
- }, Y = {
399
- id: "heading-multiple-h1",
451
+ }, Te = {
452
+ id: "a11y.heading-multiple-h1",
400
453
  severity: "warning"
401
454
  };
402
- function X(e, t) {
455
+ function F(e, t) {
403
456
  for (let n of e) {
404
- if (s(n)) {
457
+ if (c(n)) {
405
458
  t.push(n);
406
459
  continue;
407
460
  }
408
- if (a(n)) for (let e of n.children) X(e, t);
461
+ if (o(n)) for (let e of n.children) F(e, t);
409
462
  }
410
463
  }
411
- var ae = {
412
- meta: Y,
464
+ var Ee = {
465
+ meta: Te,
413
466
  template(e) {
414
467
  let t = [];
415
- X(e.blocks, t);
468
+ F(e.blocks, t);
416
469
  let n = t.filter((e) => e.level === 1);
417
470
  return n.length <= 1 ? [] : n.slice(1).map((e) => ({ blockId: e.id }));
418
471
  }
419
- }, oe = {
420
- id: "link-empty",
472
+ }, De = {
473
+ id: "a11y.link-empty",
421
474
  severity: "error"
422
475
  };
423
- function se(e) {
424
- return i(e) || s(e) ? e.content : null;
476
+ function Oe(e) {
477
+ return a(e) || c(e) ? e.content : null;
425
478
  }
426
- var ce = {
427
- meta: oe,
479
+ var ke = {
480
+ meta: De,
428
481
  block(e) {
429
- let t = se(e);
430
- return t === null || !U(t).find((e) => e.text === "" && !e.hasImageWithAlt) ? null : { blockId: e.id };
482
+ let t = Oe(e);
483
+ return t === null || !M(t).find((e) => e.text === "" && !e.hasImageWithAlt) ? null : { blockId: e.id };
431
484
  }
432
- }, le = {
433
- id: "link-vague-text",
485
+ }, Ae = {
486
+ id: "a11y.link-vague-text",
434
487
  severity: "warning"
435
488
  };
436
- function ue(e) {
437
- return i(e) || s(e) ? e.content : null;
489
+ function je(e) {
490
+ return a(e) || c(e) ? e.content : null;
438
491
  }
439
- var de = {
440
- meta: le,
492
+ var Me = {
493
+ meta: Ae,
441
494
  block(e, t, n) {
442
- let r = ue(e);
495
+ let r = je(e);
443
496
  if (r === null) return null;
444
- let i = L(n.locale).vagueLinkText, a = U(r).find((e) => {
445
- let t = V(e.text);
497
+ let i = k(n.locale).vagueLinkText, a = M(r).find((e) => {
498
+ let t = j(e.text);
446
499
  return t !== "" && i.includes(t);
447
500
  });
448
501
  return a ? {
@@ -450,53 +503,53 @@ var de = {
450
503
  params: { text: a.text }
451
504
  } : null;
452
505
  }
453
- }, fe = {
454
- id: "link-href-empty",
506
+ }, Ne = {
507
+ id: "a11y.link-href-empty",
455
508
  severity: "error"
456
509
  };
457
- function pe(e) {
458
- return i(e) || s(e) ? e.content : null;
510
+ function Pe(e) {
511
+ return a(e) || c(e) ? e.content : null;
459
512
  }
460
- var me = {
461
- meta: fe,
513
+ var Fe = {
514
+ meta: Ne,
462
515
  block(e) {
463
- let t = pe(e);
464
- return t === null || !U(t).find((e) => {
516
+ let t = Pe(e);
517
+ return t === null || !M(t).find((e) => {
465
518
  let t = e.href.trim();
466
519
  return t === "" || t === "#";
467
520
  }) ? null : { blockId: e.id };
468
521
  }
469
- }, he = {
470
- id: "link-target-blank-no-rel",
522
+ }, Ie = {
523
+ id: "a11y.link-target-blank-no-rel",
471
524
  severity: "warning"
472
525
  };
473
- function ge(e) {
474
- return i(e) || s(e) ? e.content : null;
526
+ function Le(e) {
527
+ return a(e) || c(e) ? e.content : null;
475
528
  }
476
- function _e(e) {
529
+ function Re(e) {
477
530
  if (e === null) return !1;
478
531
  let t = e.toLowerCase().split(/\s+/);
479
532
  return t.includes("noopener") || t.includes("noreferrer");
480
533
  }
481
- var ve = {
482
- meta: he,
534
+ var ze = {
535
+ meta: Ie,
483
536
  block(e) {
484
- let t = ge(e);
485
- return t === null || !U(t).find((e) => e.target === "_blank" && !_e(e.rel)) ? null : {
537
+ let t = Le(e);
538
+ return t === null || !M(t).find((e) => e.target === "_blank" && !Re(e.rel)) ? null : {
486
539
  blockId: e.id,
487
540
  fix: {
488
541
  description: "Add rel=\"noopener\"",
489
542
  apply: (t) => {
490
- if (!i(e) && !s(e)) return;
491
- let n = xe(e.content ?? "");
543
+ if (!a(e) && !c(e)) return;
544
+ let n = He(e.content ?? "");
492
545
  t.updateBlock(e.id, { content: n });
493
546
  }
494
547
  }
495
548
  };
496
549
  }
497
- }, Z = /([^\s"'>/=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
498
- function ye(e) {
499
- let t = [], n = new RegExp(Z.source, Z.flags), r;
550
+ }, I = /([^\s"'>/=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
551
+ function Be(e) {
552
+ let t = [], n = new RegExp(I.source, I.flags), r;
500
553
  for (; (r = n.exec(e)) !== null;) {
501
554
  let e = r[2] ?? r[3] ?? r[4] ?? null;
502
555
  t.push({
@@ -508,13 +561,13 @@ function ye(e) {
508
561
  }
509
562
  return t;
510
563
  }
511
- function be(e) {
564
+ function Ve(e) {
512
565
  return e.some((e) => e.name.toLowerCase() === "target" && e.value !== null && e.value.toLowerCase() === "_blank");
513
566
  }
514
- function xe(e) {
567
+ function He(e) {
515
568
  return e.replace(/<a\b([^>]*)>/gi, (e, t) => {
516
- let n = ye(t);
517
- if (!be(n)) return e;
569
+ let n = Be(t);
570
+ if (!Ve(n)) return e;
518
571
  let r = n.find((e) => e.name.toLowerCase() === "rel");
519
572
  if (r) {
520
573
  let n = (r.value ?? "").toLowerCase().split(/\s+/);
@@ -525,24 +578,24 @@ function xe(e) {
525
578
  return `<a${t} rel="noopener">`;
526
579
  });
527
580
  }
528
- var Se = {
581
+ var Ue = {
529
582
  meta: {
530
- id: "text-all-caps",
583
+ id: "a11y.text-all-caps",
531
584
  severity: "warning"
532
585
  },
533
586
  block(e, t, n) {
534
- if (!i(e) && !s(e)) return null;
535
- let r = W(e.content ?? "").replace(/[^\p{L}]/gu, "");
587
+ if (!a(e) && !c(e)) return null;
588
+ let r = N(e.content ?? "").replace(/[^\p{L}]/gu, "");
536
589
  return r.length < n.thresholds.allCapsMinLength || r !== r.toLocaleUpperCase() ? null : { blockId: e.id };
537
590
  }
538
- }, Ce = {
591
+ }, We = {
539
592
  meta: {
540
- id: "text-low-contrast",
593
+ id: "a11y.text-low-contrast",
541
594
  severity: "error"
542
595
  },
543
596
  block(t, n) {
544
- if (!s(t) || !g(t.color) || !g(n.resolvedBackgroundColor)) return null;
545
- let r = e[t.level] >= 24 ? 3 : 4.5, i = f(t.color, n.resolvedBackgroundColor);
597
+ if (!c(t) || !_(t.color) || !_(n.resolvedBackgroundColor)) return null;
598
+ let r = e[t.level] >= 24 ? 3 : 4.5, i = m(t.color, n.resolvedBackgroundColor);
546
599
  return Number.isNaN(i) || i >= r ? null : {
547
600
  blockId: t.id,
548
601
  params: {
@@ -551,34 +604,34 @@ var Se = {
551
604
  }
552
605
  };
553
606
  }
554
- }, we = {
555
- id: "text-too-small",
607
+ }, Ge = {
608
+ id: "a11y.text-too-small",
556
609
  severity: "warning"
557
610
  };
558
- function Q(e) {
559
- return r(e) || o(e) ? e.fontSize : null;
611
+ function Ke(e) {
612
+ return i(e) || ee(e) ? e.fontSize : null;
560
613
  }
561
614
  //#endregion
562
615
  //#region src/accessibility/index.ts
563
- var $ = [
564
- A,
565
- ne,
566
- re,
567
- ie,
568
- H,
569
- G,
570
- J,
571
- ae,
572
- ce,
573
- de,
616
+ var L = [
617
+ le,
618
+ fe,
619
+ pe,
574
620
  me,
575
- ve,
621
+ xe,
576
622
  Se,
577
- Ce,
623
+ we,
624
+ Ee,
625
+ ke,
626
+ Me,
627
+ Fe,
628
+ ze,
629
+ Ue,
630
+ We,
578
631
  {
579
- meta: we,
632
+ meta: Ge,
580
633
  block(e, t, n) {
581
- let r = Q(e);
634
+ let r = Ke(e);
582
635
  return r === null || r >= n.thresholds.minFontSize ? null : {
583
636
  blockId: e.id,
584
637
  params: {
@@ -590,13 +643,13 @@ var $ = [
590
643
  },
591
644
  {
592
645
  meta: {
593
- id: "button-vague-label",
646
+ id: "a11y.button-vague-label",
594
647
  severity: "warning"
595
648
  },
596
649
  block(e, n, r) {
597
650
  if (!t(e)) return null;
598
- let i = V(e.text ?? "");
599
- return i === "" || !L(r.locale).vagueButtonLabels.includes(i) ? null : {
651
+ let i = j(e.text ?? "");
652
+ return i === "" || !k(r.locale).vagueButtonLabels.includes(i) ? null : {
600
653
  blockId: e.id,
601
654
  params: { text: e.text }
602
655
  };
@@ -604,7 +657,7 @@ var $ = [
604
657
  },
605
658
  {
606
659
  meta: {
607
- id: "button-touch-target",
660
+ id: "a11y.button-touch-target",
608
661
  severity: "warning"
609
662
  },
610
663
  block(e, n, r) {
@@ -623,12 +676,12 @@ var $ = [
623
676
  },
624
677
  {
625
678
  meta: {
626
- id: "button-low-contrast",
679
+ id: "a11y.button-low-contrast",
627
680
  severity: "error"
628
681
  },
629
682
  block(e) {
630
683
  if (!t(e)) return null;
631
- let n = f(e.textColor, e.backgroundColor);
684
+ let n = m(e.textColor, e.backgroundColor);
632
685
  if (Number.isNaN(n)) return null;
633
686
  let r = e.fontSize >= 24 ? 3 : 4.5;
634
687
  return n >= r ? null : {
@@ -642,7 +695,7 @@ var $ = [
642
695
  },
643
696
  {
644
697
  meta: {
645
- id: "missing-preheader",
698
+ id: "a11y.missing-preheader",
646
699
  severity: "info"
647
700
  },
648
701
  template(e) {
@@ -650,50 +703,387 @@ var $ = [
650
703
  }
651
704
  }
652
705
  ];
653
- function Te(e, t = {}) {
654
- if (t.disabled === !0) return [];
655
- let n = Ee(t), r = [];
656
- function i(e, t, r) {
706
+ function qe(e, t = {}) {
707
+ return x(e, L, t, (e, t, n) => E(e, t, n));
708
+ }
709
+ //#endregion
710
+ //#region src/structure/messages/de.ts
711
+ var Je = /* @__PURE__ */ d({ default: () => Ye }), Ye = {
712
+ "structure.duplicate-block-id": "Block-ID erscheint {count}-mal im Baum. Jeder Block muss eine eindeutige ID haben.",
713
+ "structure.section-column-mismatch": "Sektion verwendet Layout „{layout}\" (erwartet {expected} Spalten), hat aber {actual}. Deutet auf beschädigten Zustand hin.",
714
+ "structure.nested-section": "Sektion ist in einer anderen Sektion verschachtelt. Sektionen können nicht verschachtelt werden – der Renderer wird sich falsch verhalten.",
715
+ "structure.empty-section": "Sektion enthält keine Blöcke. Entferne sie oder füge Inhalt hinzu.",
716
+ "structure.empty-column": "Spalte {columnIndex} dieser Sektion ist leer. Füge Inhalt hinzu oder reduziere die Spaltenanzahl."
717
+ }, Xe = /* @__PURE__ */ d({ default: () => R }), R = {
718
+ "structure.duplicate-block-id": "Block id appears {count} times in the tree. Each block must have a unique id.",
719
+ "structure.section-column-mismatch": "Section uses layout \"{layout}\" (expects {expected} columns) but has {actual}. Indicates corrupted state.",
720
+ "structure.nested-section": "Section is nested inside another section. Sections cannot nest — the renderer will misbehave.",
721
+ "structure.empty-section": "Section has no blocks. Remove it or add content.",
722
+ "structure.empty-column": "Column {columnIndex} of this section is empty. Add content or reduce the column count."
723
+ }, z = /* @__PURE__ */ Object.assign({
724
+ "./de.ts": Je,
725
+ "./en.ts": Xe
726
+ }), B = {};
727
+ for (let e in z) {
728
+ let t = /\.\/([^/]+)\.ts$/.exec(e);
729
+ if (!t) continue;
730
+ let n = t[1];
731
+ n !== "index" && (B[n] = z[e].default);
732
+ }
733
+ var Ze = Object.keys(B);
734
+ function V(e) {
735
+ return B[e.split("-")[0]?.toLowerCase() ?? "en"] ?? B.en ?? R;
736
+ }
737
+ function H(e, t, n) {
738
+ let r = V(e)[t] ?? R[t];
739
+ return n ? r.replace(/\{(\w+)\}/g, (e, t) => {
740
+ let r = n[t];
741
+ return r === void 0 ? `{${t}}` : String(r);
742
+ }) : r;
743
+ }
744
+ //#endregion
745
+ //#region src/structure/rules/duplicate-block-id.ts
746
+ var Qe = {
747
+ id: "structure.duplicate-block-id",
748
+ severity: "error"
749
+ };
750
+ function U(e, t) {
751
+ for (let n of e) if (t.set(n.id, (t.get(n.id) ?? 0) + 1), o(n)) for (let e of n.children) U(e, t);
752
+ }
753
+ var $e = {
754
+ meta: Qe,
755
+ template(e) {
756
+ let t = /* @__PURE__ */ new Map();
757
+ U(e.blocks, t);
758
+ let n = [];
759
+ for (let [e, r] of t) r > 1 && n.push({
760
+ blockId: e,
761
+ params: { count: r }
762
+ });
763
+ return n;
764
+ }
765
+ }, et = {
766
+ id: "structure.empty-column",
767
+ severity: "warning"
768
+ };
769
+ function W(e, t) {
770
+ for (let n of e) {
771
+ if (!o(n)) continue;
772
+ let e = n;
773
+ e.children.length > 1 && e.children.forEach((n, r) => {
774
+ n.length === 0 && t.push({
775
+ blockId: e.id,
776
+ params: { columnIndex: r + 1 }
777
+ });
778
+ });
779
+ for (let n of e.children) W(n, t);
780
+ }
781
+ }
782
+ var tt = {
783
+ meta: et,
784
+ template(e) {
785
+ let t = [];
786
+ return W(e.blocks, t), t;
787
+ }
788
+ }, nt = {
789
+ id: "structure.empty-section",
790
+ severity: "warning"
791
+ };
792
+ function rt(e) {
793
+ return e.children.length === 0 ? !0 : e.children.every((e) => e.length === 0);
794
+ }
795
+ var it = {
796
+ meta: nt,
797
+ block(e) {
798
+ if (!o(e)) return null;
799
+ let t = e;
800
+ return rt(t) ? {
801
+ blockId: t.id,
802
+ fix: {
803
+ description: "Remove the empty section",
804
+ apply: (e) => {
805
+ e.removeBlock(t.id);
806
+ }
807
+ }
808
+ } : null;
809
+ }
810
+ }, at = {
811
+ meta: {
812
+ id: "structure.nested-section",
813
+ severity: "error"
814
+ },
815
+ block(e, t) {
816
+ if (!o(e) || t.section === null) return null;
817
+ let n = t.section;
657
818
  return {
658
- blockId: r.blockId,
659
- ruleId: e,
660
- severity: t,
661
- message: k(n.locale, e, r.params),
662
- fix: r.fix
819
+ blockId: e.id,
820
+ params: { parentId: n.id }
663
821
  };
664
822
  }
665
- b(e, (e, t) => {
666
- for (let a of $) {
667
- let o = n.severity(a.meta.id);
668
- if (o === "off" || !a.block) continue;
669
- let s = a.block(e, t, n);
670
- s !== null && r.push(i(a.meta.id, o, s));
823
+ }, ot = {
824
+ id: "structure.section-column-mismatch",
825
+ severity: "error"
826
+ };
827
+ function st(e) {
828
+ return e === "1" ? 1 : e === "3" ? 3 : 2;
829
+ }
830
+ //#endregion
831
+ //#region src/structure/index.ts
832
+ var G = [
833
+ $e,
834
+ it,
835
+ tt,
836
+ at,
837
+ {
838
+ meta: ot,
839
+ block(e) {
840
+ if (!o(e)) return null;
841
+ let t = e, n = st(t.columns), r = t.children.length;
842
+ return r === n ? null : {
843
+ blockId: t.id,
844
+ params: {
845
+ layout: t.columns,
846
+ expected: n,
847
+ actual: r
848
+ }
849
+ };
850
+ }
851
+ }
852
+ ];
853
+ function ct(e, t = {}) {
854
+ return x(e, G, t, (e, t, n) => H(e, t, n));
855
+ }
856
+ //#endregion
857
+ //#region src/links/messages/de.ts
858
+ var lt = /* @__PURE__ */ d({ default: () => ut }), ut = {
859
+ "link.javascript-protocol": "Die URL verwendet das „javascript:\"-Protokoll, das aus Sicherheitsgründen beim Rendern entfernt wird. Ersetze sie durch eine echte URL oder entferne sie.",
860
+ "link.unsupported-protocol": "Die URL verwendet das Protokoll „{protocol}\", das von den meisten E-Mail-Clients nicht unterstützt wird. Verwende http, https, mailto, tel oder sms.",
861
+ "link.malformed-mailto": "Der mailto:-Link ist fehlerhaft. Erwartet wird eine einzelne Empfängeradresse vor einer eventuellen Querystring (z. B. mailto:hallo@example.com).",
862
+ "link.malformed-tel": "Der tel:-Link enthält Zeichen, die keine Ziffern, +, Leerzeichen, Bindestriche, Klammern oder Punkte sind.",
863
+ "link.localhost-or-staging": "Der URL-Host „{host}\" entspricht einem Nicht-Produktionsmuster. Ersetze ihn vor dem Versand durch die Produktions-URL."
864
+ }, dt = /* @__PURE__ */ d({ default: () => K }), K = {
865
+ "link.javascript-protocol": "URL uses the \"javascript:\" protocol, which is stripped at render time for safety. Replace it with a real link or remove the URL.",
866
+ "link.unsupported-protocol": "URL uses the \"{protocol}\" protocol, which most email clients do not support. Use http, https, mailto, tel, or sms.",
867
+ "link.malformed-mailto": "mailto: link is malformed. Expected a single recipient address before any query string (e.g. mailto:hello@example.com).",
868
+ "link.malformed-tel": "tel: link contains characters that are not digits, +, spaces, dashes, parentheses, or dots.",
869
+ "link.localhost-or-staging": "URL host \"{host}\" matches a non-production pattern. Replace with the production URL before sending."
870
+ }, q = /* @__PURE__ */ Object.assign({
871
+ "./de.ts": lt,
872
+ "./en.ts": dt
873
+ }), J = {};
874
+ for (let e in q) {
875
+ let t = /\.\/([^/]+)\.ts$/.exec(e);
876
+ if (!t) continue;
877
+ let n = t[1];
878
+ n !== "index" && (J[n] = q[e].default);
879
+ }
880
+ var ft = Object.keys(J);
881
+ function Y(e) {
882
+ return J[e.split("-")[0]?.toLowerCase() ?? "en"] ?? J.en ?? K;
883
+ }
884
+ function X(e, t, n) {
885
+ let r = Y(e)[t] ?? K[t];
886
+ return n ? r.replace(/\{(\w+)\}/g, (e, t) => {
887
+ let r = n[t];
888
+ return r === void 0 ? `{${t}}` : String(r);
889
+ }) : r;
890
+ }
891
+ //#endregion
892
+ //#region src/url-walker.ts
893
+ function Z(e) {
894
+ let o = [];
895
+ return b(e, (e) => {
896
+ if (c(e) || a(e) || n(e)) {
897
+ for (let t of M(e.content)) o.push({
898
+ url: t.href,
899
+ blockId: e.id,
900
+ source: "anchor",
901
+ label: t.text
902
+ });
903
+ return;
671
904
  }
672
- });
673
- for (let t of $) {
674
- let a = n.severity(t.meta.id);
675
- if (a === "off" || !t.template) continue;
676
- let o = t.template(e, n);
677
- for (let e of o) r.push(i(t.meta.id, a, e));
905
+ if (t(e)) {
906
+ o.push({
907
+ url: e.url,
908
+ blockId: e.id,
909
+ source: "button",
910
+ label: e.text
911
+ });
912
+ return;
913
+ }
914
+ if (r(e)) {
915
+ e.linkUrl && e.linkUrl !== "" && o.push({
916
+ url: e.linkUrl,
917
+ blockId: e.id,
918
+ source: "image-link",
919
+ label: e.alt || void 0
920
+ });
921
+ return;
922
+ }
923
+ if (te(e)) {
924
+ o.push({
925
+ url: e.url,
926
+ blockId: e.id,
927
+ source: "video",
928
+ label: e.alt || void 0
929
+ });
930
+ return;
931
+ }
932
+ if (i(e)) {
933
+ for (let t of e.items) o.push({
934
+ url: t.url,
935
+ blockId: e.id,
936
+ source: "menu-item",
937
+ label: t.text
938
+ });
939
+ return;
940
+ }
941
+ if (s(e)) {
942
+ for (let t of e.icons) o.push({
943
+ url: t.url,
944
+ blockId: e.id,
945
+ source: "social-icon",
946
+ label: t.platform
947
+ });
948
+ return;
949
+ }
950
+ }), o;
951
+ }
952
+ //#endregion
953
+ //#region src/links/rules/javascript-protocol.ts
954
+ var pt = {
955
+ id: "link.javascript-protocol",
956
+ severity: "error"
957
+ };
958
+ function mt(e) {
959
+ if (!e) return !1;
960
+ let t = e.replace(/\s+/g, "");
961
+ return /^javascript:/i.test(t);
962
+ }
963
+ var ht = {
964
+ meta: pt,
965
+ template(e) {
966
+ let t = [];
967
+ for (let n of Z(e)) mt(n.url) && t.push({ blockId: n.blockId });
968
+ return t;
678
969
  }
679
- return r;
970
+ }, gt = {
971
+ id: "link.unsupported-protocol",
972
+ severity: "warning"
973
+ }, _t = new Set([
974
+ "http",
975
+ "https",
976
+ "mailto",
977
+ "tel",
978
+ "sms"
979
+ ]);
980
+ function vt(e) {
981
+ if (!e) return null;
982
+ let t = e.trim(), n = /^([a-z][a-z0-9+\-.]*):/i.exec(t);
983
+ return n ? n[1].toLowerCase() : null;
680
984
  }
681
- function Ee(e) {
682
- let t = e.rules ?? {}, n = {
683
- ...d,
684
- ...e.thresholds ?? {}
685
- };
686
- return {
687
- locale: e.locale ?? "en",
688
- rules: t,
689
- thresholds: n,
690
- severity: (e) => {
691
- let n = t[e];
692
- return n === void 0 ? $.find((t) => t.meta.id === e)?.meta.severity ?? "warning" : n;
985
+ var yt = {
986
+ meta: gt,
987
+ template(e) {
988
+ let t = [];
989
+ for (let n of Z(e)) {
990
+ let e = vt(n.url);
991
+ e !== null && e !== "javascript" && (_t.has(e) || t.push({
992
+ blockId: n.blockId,
993
+ params: { protocol: e }
994
+ }));
693
995
  }
694
- };
996
+ return t;
997
+ }
998
+ }, bt = {
999
+ id: "link.malformed-mailto",
1000
+ severity: "warning"
1001
+ };
1002
+ function Q(e) {
1003
+ let t = e.trim();
1004
+ if (!/^mailto:/i.test(t)) return !1;
1005
+ let [n] = t.slice(7).split("?", 2);
1006
+ if (n.trim() === "") return !0;
1007
+ let r = n.split(",").map((e) => e.trim());
1008
+ for (let e of r) {
1009
+ if (e === "") return !0;
1010
+ let t = e.split("@");
1011
+ if (t.length !== 2) return !0;
1012
+ let [n, r] = t;
1013
+ if (n === "" || r === "" || !r.includes(".")) return !0;
1014
+ }
1015
+ return !1;
1016
+ }
1017
+ var xt = {
1018
+ meta: bt,
1019
+ template(e) {
1020
+ let t = [];
1021
+ for (let n of Z(e)) Q(n.url) && t.push({ blockId: n.blockId });
1022
+ return t;
1023
+ }
1024
+ }, St = {
1025
+ id: "link.malformed-tel",
1026
+ severity: "warning"
1027
+ }, Ct = /^[+0-9\s().\-]+$/;
1028
+ function wt(e) {
1029
+ let t = e.trim();
1030
+ if (!/^tel:/i.test(t)) return !1;
1031
+ let n = t.slice(4).trim();
1032
+ return n === "" ? !0 : !Ct.test(n);
1033
+ }
1034
+ var Tt = {
1035
+ meta: St,
1036
+ template(e) {
1037
+ let t = [];
1038
+ for (let n of Z(e)) wt(n.url) && t.push({ blockId: n.blockId });
1039
+ return t;
1040
+ }
1041
+ }, Et = {
1042
+ id: "link.localhost-or-staging",
1043
+ severity: "warning"
1044
+ };
1045
+ function Dt(e) {
1046
+ let t = e.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
1047
+ return RegExp(`^${t}$`, "i");
1048
+ }
1049
+ function Ot(e) {
1050
+ if (!e) return null;
1051
+ let t = e.trim();
1052
+ if (!/^(https?|ftps?):\/\//i.test(t)) return null;
1053
+ try {
1054
+ return new URL(t).hostname.toLowerCase();
1055
+ } catch {
1056
+ return null;
1057
+ }
1058
+ }
1059
+ //#endregion
1060
+ //#region src/links/index.ts
1061
+ var $ = [
1062
+ ht,
1063
+ yt,
1064
+ xt,
1065
+ Tt,
1066
+ {
1067
+ meta: Et,
1068
+ template(e, t) {
1069
+ let n = t.links.nonProductionHosts;
1070
+ if (n.length === 0) return [];
1071
+ let r = n.map(Dt), i = [];
1072
+ for (let t of Z(e)) {
1073
+ let e = Ot(t.url);
1074
+ e !== null && r.some((t) => t.test(e)) && i.push({
1075
+ blockId: t.blockId,
1076
+ params: { host: e }
1077
+ });
1078
+ }
1079
+ return i;
1080
+ }
1081
+ }
1082
+ ];
1083
+ function kt(e, t = {}) {
1084
+ return x(e, $, t, (e, t, n) => X(e, t, n));
695
1085
  }
696
1086
  //#endregion
697
- export { d as DEFAULT_THRESHOLDS, $ as RULES, B as SUPPORTED_DICTIONARY_LOCALES, D as SUPPORTED_MESSAGE_LOCALES, U as extractAnchors, W as extractText, k as formatMessage, f as getContrastRatio, L as getDictionary, O as getMessages, g as isOpaqueHex, Te as lintAccessibility, h as parseHex, b as walkBlocks };
1087
+ export { L as ACCESSIBILITY_RULES, f as DEFAULT_A11Y_THRESHOLDS, p as DEFAULT_NON_PRODUCTION_HOSTS, $ as LINK_RULES, G as STRUCTURE_RULES, be as SUPPORTED_DICTIONARY_LOCALES, ft as SUPPORTED_LINK_MESSAGE_LOCALES, ce as SUPPORTED_MESSAGE_LOCALES, Ze as SUPPORTED_STRUCTURE_MESSAGE_LOCALES, M as extractAnchors, N as extractText, X as formatLinkMessage, E as formatMessage, H as formatStructureMessage, m as getContrastRatio, k as getDictionary, Y as getLinkMessages, T as getMessages, V as getStructureMessages, _ as isOpaqueHex, qe as lintAccessibility, kt as lintLinks, ct as lintStructure, g as parseHex, b as walkBlocks, Z as walkUrls };
698
1088
 
699
1089
  //# sourceMappingURL=index.js.map