@useinsider/guido 3.18.1-beta.dea994d → 3.18.1-beta.e39fb14

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 (52) hide show
  1. package/README.md +1 -0
  2. package/dist/@types/config/schemas.js +28 -26
  3. package/dist/components/Guido.vue.js +8 -8
  4. package/dist/components/Guido.vue2.js +53 -51
  5. package/dist/components/organisms/AutoSaveController.vue2.js +9 -8
  6. package/dist/components/organisms/header/EditorActions.vue.js +2 -2
  7. package/dist/components/organisms/header/EditorActions.vue2.js +86 -56
  8. package/dist/components/organisms/header/LeftSlot.vue.js +5 -5
  9. package/dist/components/organisms/header/LeftSlot.vue2.js +16 -12
  10. package/dist/components/organisms/validation/ValidationErrorDrawer.vue.js +20 -0
  11. package/dist/components/organisms/validation/ValidationErrorDrawer.vue2.js +48 -0
  12. package/dist/components/wrappers/WpDrawer.vue.js +9 -7
  13. package/dist/components/wrappers/WpDrawer.vue2.js +1 -1
  14. package/dist/composables/useHtmlCompiler.js +37 -26
  15. package/dist/composables/useHtmlValidator.js +2 -0
  16. package/dist/composables/usePreviewMode.js +11 -12
  17. package/dist/composables/useSave.js +90 -45
  18. package/dist/composables/useSaveValidation.js +279 -0
  19. package/dist/composables/useSaveValidationRollout.js +9 -0
  20. package/dist/composables/validators/useCouponBlockValidator.js +24 -21
  21. package/dist/composables/validators/useUnsubscribeBlockValidator.js +34 -27
  22. package/dist/config/compiler/htmlCompilerRules.js +6 -14
  23. package/dist/enums/save-validation.js +4 -0
  24. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +41 -34
  25. package/dist/guido.css +1 -1
  26. package/dist/src/@types/config/schemas.d.ts +4 -0
  27. package/dist/src/@types/save-validation.d.ts +19 -0
  28. package/dist/src/components/Guido.vue.d.ts +17 -1
  29. package/dist/src/components/organisms/header/EditorActions.vue.d.ts +17 -1
  30. package/dist/src/components/organisms/header/HeaderWrapper.vue.d.ts +17 -1
  31. package/dist/src/components/organisms/header/RightSlot.vue.d.ts +17 -1
  32. package/dist/src/components/organisms/validation/ValidationErrorDrawer.vue.d.ts +2 -0
  33. package/dist/src/components/wrappers/WpDrawer.vue.d.ts +4 -3
  34. package/dist/src/composables/useConfig.d.ts +2 -0
  35. package/dist/src/composables/useHtmlValidator.d.ts +21 -0
  36. package/dist/src/composables/useSave.d.ts +6 -1
  37. package/dist/src/composables/useSaveValidation.d.ts +6 -0
  38. package/dist/src/composables/useSaveValidationRollout.d.ts +3 -0
  39. package/dist/src/composables/validators/useCouponBlockValidator.d.ts +3 -0
  40. package/dist/src/composables/validators/useUnsubscribeBlockValidator.d.ts +10 -0
  41. package/dist/src/enums/save-validation.d.ts +27 -0
  42. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +5 -0
  43. package/dist/src/stores/config.d.ts +30 -0
  44. package/dist/src/stores/save-validation.d.ts +22 -0
  45. package/dist/src/utils/templatePreparation.d.ts +30 -0
  46. package/dist/static/styles/customEditorStyle.css.js +0 -14
  47. package/dist/stores/config.js +39 -22
  48. package/dist/stores/save-validation.js +30 -0
  49. package/dist/utils/templatePreparation.js +28 -25
  50. package/package.json +1 -1
  51. package/dist/src/utils/appendMobileHiddenColumnCss.d.ts +0 -2
  52. package/dist/utils/appendMobileHiddenColumnCss.js +0 -11
@@ -0,0 +1,279 @@
1
+ import { getIgnoredRecommendationBlockIds as B } from "../config/compiler/utils/recommendationIgnoreUtils.js";
2
+ import { CanNotMakeAnyChangesForRunningKey as k, DISPLAY_CONDITIONS_REGEX as Y, DISPLAY_CONDITIONS_EXCEPTIONS_REGEX as x } from "../enums/html-validator.js";
3
+ import { SaveValidationRule as o } from "../enums/save-validation.js";
4
+ import { ToasterTypeOptions as r } from "../enums/toaster.js";
5
+ import { BLOCK_ROOT_SELECTOR as U } from "../extensions/Blocks/Checkbox/constants/selectors.js";
6
+ import { itemsBlockDynamicVariables as M } from "../extensions/Blocks/Items/enums/productEnums.js";
7
+ import { BLOCK_ROOT_SELECTOR as P } from "../extensions/Blocks/RadioButton/constants/selectors.js";
8
+ import { useRecommendationExtensionStore as W } from "../extensions/Blocks/Recommendation/store/recommendation.js";
9
+ import { RecommendationRequiredFieldsKey as G } from "../extensions/Blocks/Recommendation/validation/requiredFields.js";
10
+ import { useConfigStore as q } from "../stores/config.js";
11
+ import { useDynamicContentStore as H } from "../stores/dynamic-content.js";
12
+ import { useRecommendationStore as V } from "../stores/recommendation.js";
13
+ import { base64EncodeWithSpecialChars as T } from "../utils/base64.js";
14
+ import { useTemplatePreparation as X } from "../utils/templatePreparation.js";
15
+ import { useActionsApi as j } from "./useActionsApi.js";
16
+ import { useConfig as z } from "./useConfig.js";
17
+ import { useHtmlCompiler as $ } from "./useHtmlCompiler.js";
18
+ import { buildPartnerAttributeNameSet as Q, getInvalidDynamicContentTags as Z, getRecommendationBlockIds as J, findInvalidCustomFields as tt } from "./useHtmlValidator.js";
19
+ import { useHttp as et } from "./useHttp.js";
20
+ import { useTranslations as nt } from "./useTranslations.js";
21
+ import { findInvalidCouponBlocks as it, COUPON_TAG_MODIFIED_KEY as ot } from "./validators/useCouponBlockValidator.js";
22
+ import { findDuplicateUnsubPageTypes as rt, countUnsubBlocksWithoutPage as st, EMPTY_BLOCK_KEY as at } from "./validators/useUnsubscribeBlockValidator.js";
23
+ const ct = "newsletter.html-content-is-empty", lt = "custom-fields.missing-closing-braces", ut = "custom-fields.missing-opening-braces", I = "custom-fields.invalid-custom-fields", mt = "newsletter.display-conditions-invalid-syntax", dt = "custom-conditions.wrong-equality-operators", pt = "custom-conditions.missing-if-endif-tag", gt = "custom-conditions.no-space-after-braces", ft = "custom-conditions.no-braces-inside-if-tag", yt = "newsletter.fill-all-necessary-fields", Et = "newsletter.invalid-image-type", It = "unsubscribe-templates.select-checkbox-groups", St = "unsubscribe-templates.select-radio-button-groups", Ct = "newsletter.invalid-url-link-for-toaster", F = "journey-builder.liquid-validation-failed", _t = "/newsletter/template-library/check-template-html-body", ht = /src="[^"]*\.(svg|pst)"/gm, R = /({%(.*?)%})/g, kt = ["if", "endif", "else", "elif", "now"], Tt = (e) => !kt.includes(e.toLowerCase()), Ft = (e) => ["if", "endif"].includes(e.toLowerCase());
24
+ function Ot(e) {
25
+ return it(e).map((i) => ({
26
+ rule: o.CouponTagModified,
27
+ type: r.Warning,
28
+ key: ot,
29
+ detail: i
30
+ }));
31
+ }
32
+ function Rt(e) {
33
+ return rt(e).map((i) => ({
34
+ rule: o.DuplicateUnsubBlock,
35
+ type: r.Error,
36
+ key: i.messageKey
37
+ }));
38
+ }
39
+ function Lt(e) {
40
+ return Array.from({ length: st(e) }, () => ({
41
+ rule: o.UnsubBlockMissingTemplate,
42
+ type: r.Warning,
43
+ key: at
44
+ }));
45
+ }
46
+ function Nt(e, i) {
47
+ const n = e.match(R);
48
+ if (!n)
49
+ return [];
50
+ const a = [];
51
+ return n.forEach((u) => {
52
+ const s = u.slice(2, -2).trim().match(/(".*?"|[^"\s]+)(?=\s*|\s*$)/g);
53
+ if (s && s.length > 0) {
54
+ const [m] = s;
55
+ Tt(m) && !i.includes(m) && a.push({
56
+ rule: o.CustomFieldScript,
57
+ type: r.Warning,
58
+ key: I,
59
+ detail: m
60
+ });
61
+ }
62
+ }), a;
63
+ }
64
+ function bt(e) {
65
+ return e.length > 0 ? [] : [{ rule: o.EmptyHtml, type: r.Warning, key: ct }];
66
+ }
67
+ function At(e) {
68
+ const i = (e.match(/{/gm) || []).length, n = (e.match(/}/gm) || []).length;
69
+ return i > n ? [{
70
+ rule: o.MismatchedBraces,
71
+ type: r.Warning,
72
+ key: lt
73
+ }] : i < n ? [{
74
+ rule: o.MismatchedBraces,
75
+ type: r.Warning,
76
+ key: ut
77
+ }] : [];
78
+ }
79
+ function Dt(e) {
80
+ return Z(e).map((i) => ({
81
+ rule: o.InvalidDynamicTag,
82
+ type: r.Warning,
83
+ key: I,
84
+ detail: i
85
+ }));
86
+ }
87
+ function vt(e, i, n, a) {
88
+ const u = J(e);
89
+ return tt(e, i, u, n, a).map((s) => ({
90
+ rule: o.InvalidCustomField,
91
+ type: r.Error,
92
+ key: I,
93
+ detail: s
94
+ }));
95
+ }
96
+ function Kt(e) {
97
+ const i = e.match(/{%(.*?)%}/g), n = [], a = [];
98
+ if (i && i.forEach((u) => {
99
+ const s = u.match(Y), m = u.match(x), y = (s == null ? void 0 : s.join("")) || "";
100
+ (!s || u !== y) && !m && a.push({
101
+ rule: o.ConditionsSyntax,
102
+ type: r.Error,
103
+ key: mt,
104
+ detail: u
105
+ }), s && s.forEach((g) => {
106
+ g.trim() === "=" && a.push({
107
+ rule: o.WrongEqualityOperator,
108
+ type: r.Error,
109
+ key: dt
110
+ }), (g.match(/^[a-zA-Z]*$/g) ?? []).forEach((f) => {
111
+ Ft(f) && n.push(f);
112
+ });
113
+ });
114
+ }), n.length) {
115
+ const u = n.filter((m) => m === "if").length, s = n.filter((m) => m === "endif").length;
116
+ u !== s && a.push({
117
+ rule: o.MissingIfEndif,
118
+ type: r.Error,
119
+ key: pt
120
+ });
121
+ }
122
+ return a;
123
+ }
124
+ function wt(e) {
125
+ const i = (e.match(/{% /gm) || []).length, n = (e.match(/ %}/gm) || []).length;
126
+ return i === n ? [] : [{
127
+ rule: o.ConditionTagSpacing,
128
+ type: r.Warning,
129
+ key: gt
130
+ }];
131
+ }
132
+ function Bt(e) {
133
+ return (e.match(R) || []).filter((n) => n.includes("if")).map((n) => (n.match(/{{.*}}/gm) || []).length).reduce((n, a) => n + a, 0) === 0 ? [] : [{
134
+ rule: o.BracesInsideIfTag,
135
+ type: r.Warning,
136
+ key: ft
137
+ }];
138
+ }
139
+ function Yt(e) {
140
+ return (e.match(ht) ?? []).map((i) => ({
141
+ rule: o.InvalidImageType,
142
+ type: r.Error,
143
+ key: Et,
144
+ detail: i
145
+ }));
146
+ }
147
+ function O(e, i, n, a) {
148
+ const u = new DOMParser().parseFromString(e, "text/html");
149
+ return Array.from(u.querySelectorAll(i)).filter((s) => {
150
+ var m;
151
+ return !((m = s.id) != null && m.trim());
152
+ }).map(() => ({ rule: n, type: r.Error, key: a }));
153
+ }
154
+ const ae = () => {
155
+ const { config: e, isFeatureEnabled: i } = z(), n = nt(), { post: a } = et(), { getCompiledEmail: u, getTemplateData: s } = j(), { compileHtml: m } = $(), { hydrateRecommendationStore: y } = X(), g = H(), f = V(), E = W(), L = q(), N = (t) => ({
156
+ rule: t.rule,
157
+ type: t.type,
158
+ detail: t.detail,
159
+ message: "key" in t ? n(t.key) : t.message
160
+ }), b = async (t) => {
161
+ if (t.size === 0)
162
+ return /* @__PURE__ */ new Set();
163
+ try {
164
+ await E.fetchRecommendationFilters();
165
+ } catch {
166
+ return null;
167
+ }
168
+ return Q(E.filterList);
169
+ }, A = async (t) => {
170
+ try {
171
+ const l = await a(_t, { html: T(t) }), { status: c, message: d } = l.data, p = [];
172
+ return c || p.push({
173
+ rule: o.InvalidLinks,
174
+ type: r.Error,
175
+ // status===undefined means a bare message instead of a boolean; surface it,
176
+ // but fall back to the canned key when it's missing so a malformed body
177
+ // never renders a literal 'undefined' row (SD-147430).
178
+ ...c === void 0 && d ? { message: d } : { key: Ct }
179
+ }), d === n(k) && p.push({
180
+ rule: o.LinkCheckInProgress,
181
+ type: r.Error,
182
+ key: k
183
+ }), p;
184
+ } catch (l) {
185
+ return console.warn("Link-check collector failed; skipping the link rule.", l), [];
186
+ }
187
+ }, D = async (t) => {
188
+ try {
189
+ const l = await a("/newsletter/contents/validate-syntax", [{
190
+ identifier: "default",
191
+ syntax: "liquid",
192
+ contents: {
193
+ subject: null,
194
+ preheader: null,
195
+ html: T(t),
196
+ ampHtml: null
197
+ }
198
+ }]);
199
+ if (!Array.isArray(l.data)) {
200
+ const d = l.data;
201
+ return [{
202
+ rule: o.LiquidSyntax,
203
+ type: r.Warning,
204
+ message: d.message ?? n(F)
205
+ }];
206
+ }
207
+ const [c] = l.data;
208
+ return Object.values((c == null ? void 0 : c.errors) ?? {}).map((d) => ({
209
+ rule: o.LiquidSyntax,
210
+ type: r.Warning,
211
+ message: d.replace(/^line \d+:\s*/, "")
212
+ }));
213
+ } catch {
214
+ return [{
215
+ rule: o.LiquidSyntax,
216
+ type: r.Error,
217
+ message: n(F)
218
+ }];
219
+ }
220
+ }, v = () => {
221
+ const t = E.invalidBlockIds().map((c) => ({
222
+ rule: o.RecommendationRequiredFields,
223
+ type: r.Error,
224
+ key: G,
225
+ detail: c
226
+ })), l = (f.recommendationConfigs ?? []).filter((c) => c.filters.some((d) => d.value === "")).map(() => ({
227
+ rule: o.RecommendationFilters,
228
+ type: r.Error,
229
+ key: yt
230
+ }));
231
+ return [...t, ...l];
232
+ }, K = async (t, l) => {
233
+ var _, h;
234
+ const c = [
235
+ ...l.map((w) => w.value),
236
+ ...M,
237
+ ...((h = (_ = e.value) == null ? void 0 : _.template) == null ? void 0 : h.customFieldAttributes) ?? []
238
+ ], d = B(t), p = await b(d), { isTransactional: C } = L;
239
+ return [
240
+ ...await A(t),
241
+ ...C ? [] : Nt(t, c),
242
+ ...bt(t),
243
+ ...At(t),
244
+ ...Dt(t),
245
+ ...C ? [] : vt(t, c, d, p),
246
+ ...Kt(t),
247
+ ...wt(t),
248
+ ...Bt(t),
249
+ ...v(),
250
+ ...Yt(t),
251
+ ...O(
252
+ t,
253
+ U,
254
+ o.CheckboxUnsubGroup,
255
+ It
256
+ ),
257
+ ...O(
258
+ t,
259
+ P,
260
+ o.RadioButtonUnsubGroup,
261
+ St
262
+ )
263
+ ];
264
+ }, S = async (t, l) => [
265
+ ...Ot(t),
266
+ ...Rt(t),
267
+ ...Lt(t),
268
+ ...i("liquidSyntax") ? await D(t) : await K(t, l)
269
+ ].map(N);
270
+ return { collectSaveValidationErrors: S, validateCurrentTemplate: async () => {
271
+ const { html: t } = await s();
272
+ await y(t);
273
+ const { html: l } = await u({ minimize: !0, resetDataSavedFlag: !1 }), { compiledHtml: c } = m(l);
274
+ return S(c, g.getSelectedDynamicContentList);
275
+ } };
276
+ };
277
+ export {
278
+ ae as useSaveValidation
279
+ };
@@ -0,0 +1,9 @@
1
+ import { useConfigStore as t } from "../stores/config.js";
2
+ import { computed as o } from "vue";
3
+ const n = ["newsletteruatcamp", "newsletterqacamp"], c = () => {
4
+ const e = t();
5
+ return { isEnabled: o(() => n.includes(e.partnerName)) };
6
+ };
7
+ export {
8
+ c as useSaveValidationRollout
9
+ };
@@ -1,24 +1,27 @@
1
- import { ToasterTypeOptions as c } from "../../enums/toaster.js";
2
- import { COUPON_PLACEHOLDER_DEFAULT as i, COUPON_PLACEHOLDER_LIQUID as l } from "../../extensions/Blocks/CouponBlock/template.js";
3
- import { useToaster as m } from "../useToaster.js";
4
- import { useTranslations as p } from "../useTranslations.js";
5
- const u = /* @__PURE__ */ new Set([
6
- i,
7
- l
8
- ]), A = () => {
9
- const { showToaster: t } = m(), r = p();
10
- return { validateCouponBlockTags: (e) => {
11
- const n = new DOMParser().parseFromString(e, "text/html");
12
- return Array.from(n.querySelectorAll(".coupon-block")).find((s) => {
13
- var o;
14
- const a = ((o = s.textContent) == null ? void 0 : o.trim()) ?? "";
15
- return !u.has(a);
16
- }) ? (t({
17
- type: c.Warning,
18
- message: r("newsletter.coupon-tag-modified")
19
- }), !1) : !0;
20
- } };
1
+ import { ToasterTypeOptions as n } from "../../enums/toaster.js";
2
+ import { COUPON_PLACEHOLDER_DEFAULT as s, COUPON_PLACEHOLDER_LIQUID as a } from "../../extensions/Blocks/CouponBlock/template.js";
3
+ import { useToaster as i } from "../useToaster.js";
4
+ import { useTranslations as l } from "../useTranslations.js";
5
+ const c = /* @__PURE__ */ new Set([
6
+ s,
7
+ a
8
+ ]), p = "newsletter.coupon-tag-modified";
9
+ function u(r) {
10
+ const e = new DOMParser().parseFromString(r, "text/html");
11
+ return Array.from(e.querySelectorAll(".coupon-block")).map((o) => {
12
+ var t;
13
+ return ((t = o.textContent) == null ? void 0 : t.trim()) ?? "";
14
+ }).filter((o) => !c.has(o));
15
+ }
16
+ const T = () => {
17
+ const { showToaster: r } = i(), e = l();
18
+ return { validateCouponBlockTags: (t) => u(t).length > 0 ? (r({
19
+ type: n.Warning,
20
+ message: e(p)
21
+ }), !1) : !0 };
21
22
  };
22
23
  export {
23
- A as useCouponBlockValidator
24
+ p as COUPON_TAG_MODIFIED_KEY,
25
+ u as findInvalidCouponBlocks,
26
+ T as useCouponBlockValidator
24
27
  };
@@ -1,8 +1,8 @@
1
- import { ToasterTypeOptions as i } from "../../enums/toaster.js";
1
+ import { ToasterTypeOptions as c } from "../../enums/toaster.js";
2
2
  import { PAGE_TYPES as u } from "../../enums/unsubscribe.js";
3
- import { UNSUBSCRIBE_BLOCK_SELECTOR as l, DATA_ATTRIBUTES as a } from "../../extensions/Blocks/Unsubscribe/utils/constants.js";
4
- import { useToaster as E } from "../useToaster.js";
5
- import { useTranslations as m } from "../useTranslations.js";
3
+ import { UNSUBSCRIBE_BLOCK_SELECTOR as i, DATA_ATTRIBUTES as a } from "../../extensions/Blocks/Unsubscribe/utils/constants.js";
4
+ import { useToaster as p } from "../useToaster.js";
5
+ import { useTranslations as l } from "../useTranslations.js";
6
6
  const T = [
7
7
  {
8
8
  pageType: u.GLOBAL_UNSUBSCRIBE,
@@ -12,30 +12,37 @@ const T = [
12
12
  pageType: u.SUBSCRIPTION_PREFERENCE_CENTER,
13
13
  messageKey: "unsubscription-preference.duplicate-pref-center"
14
14
  }
15
- ], g = "unsubscription-preference.duplicate-both", b = "unsubscription-preference.select-page-to-continue", P = () => {
16
- const { showToaster: c } = E(), p = m();
17
- return { validateUnsubscribeBlockUniqueness: (n) => {
18
- const o = new DOMParser().parseFromString(n, "text/html"), r = /* @__PURE__ */ new Map();
19
- o.querySelectorAll(l).forEach((e) => {
20
- const s = Number(e.getAttribute(a.PAGE_TYPE));
21
- r.set(s, (r.get(s) ?? 0) + 1);
22
- });
23
- const t = T.filter((e) => (r.get(e.pageType) ?? 0) > 1).map((e) => e.messageKey);
24
- return t.length ? (c({
25
- type: i.Error,
26
- message: p(t.length > 1 ? g : t[0])
15
+ ], g = "unsubscription-preference.duplicate-both", E = "unsubscription-preference.select-page-to-continue";
16
+ function m(r) {
17
+ const s = new DOMParser().parseFromString(r, "text/html"), t = /* @__PURE__ */ new Map();
18
+ return s.querySelectorAll(i).forEach((n) => {
19
+ const e = Number(n.getAttribute(a.PAGE_TYPE));
20
+ t.set(e, (t.get(e) ?? 0) + 1);
21
+ }), T.filter((n) => (t.get(n.pageType) ?? 0) > 1);
22
+ }
23
+ function b(r) {
24
+ const s = new DOMParser().parseFromString(r, "text/html");
25
+ return Array.from(s.querySelectorAll(i)).filter((t) => {
26
+ const n = t.getAttribute(a.PAGE_TYPE), e = t.getAttribute(a.PAGE_LIST);
27
+ return !n || !e;
28
+ }).length;
29
+ }
30
+ const y = () => {
31
+ const { showToaster: r } = p(), s = l();
32
+ return { validateUnsubscribeBlockUniqueness: (e) => {
33
+ const o = m(e);
34
+ return o.length ? (r({
35
+ type: c.Error,
36
+ message: s(o.length > 1 ? g : o[0].messageKey)
27
37
  }), !1) : !0;
28
- }, validateUnsubscribeBlockHasTemplate: (n) => {
29
- const o = new DOMParser().parseFromString(n, "text/html");
30
- return Array.from(o.querySelectorAll(l)).some((t) => {
31
- const e = t.getAttribute(a.PAGE_TYPE), s = t.getAttribute(a.PAGE_LIST);
32
- return !e || !s;
33
- }) ? (c({
34
- type: i.Warning,
35
- message: p(b)
36
- }), !1) : !0;
37
- } };
38
+ }, validateUnsubscribeBlockHasTemplate: (e) => b(e) === 0 ? !0 : (r({
39
+ type: c.Warning,
40
+ message: s(E)
41
+ }), !1) };
38
42
  };
39
43
  export {
40
- P as useUnsubscribeBlockValidator
44
+ E as EMPTY_BLOCK_KEY,
45
+ b as countUnsubBlocksWithoutPage,
46
+ m as findDuplicateUnsubPageTypes,
47
+ y as useUnsubscribeBlockValidator
41
48
  };
@@ -1,6 +1,5 @@
1
- import { appendMobileHiddenColumnCss as c } from "../../utils/appendMobileHiddenColumnCss.js";
2
- import { stripFontTags as l } from "../../utils/stripFontTags.js";
3
- const g = [
1
+ import { stripFontTags as p } from "../../utils/stripFontTags.js";
2
+ const d = [
4
3
  {
5
4
  id: "fix-url-encoding-start",
6
5
  description: "Replace {%22 with %7B%22 to fix URL encoding",
@@ -59,7 +58,7 @@ const g = [
59
58
  id: "strip-font-tags",
60
59
  description: 'Unwrap browser-translate-injected <font dir="auto"> tags, keeping inner content; other <font> elements are left intact.',
61
60
  type: "custom",
62
- processor: (e) => l(e),
61
+ processor: (e) => p(e),
63
62
  priority: 15
64
63
  },
65
64
  {
@@ -130,13 +129,6 @@ const g = [
130
129
  },
131
130
  priority: 30
132
131
  },
133
- {
134
- id: "append-mobile-hidden-column-css",
135
- description: "Inject media-query CSS that hides mobile-hidden columns and their gap cells",
136
- type: "custom",
137
- processor: (e) => c(e),
138
- priority: 31
139
- },
140
132
  {
141
133
  id: "replace-old-image-domain",
142
134
  description: "Replacing old image domains",
@@ -165,8 +157,8 @@ const g = [
165
157
  processor: (e) => {
166
158
  const t = (i) => i.toString(16).padStart(2, "0");
167
159
  return e.replace(/rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/gi, (i, o, n, r) => {
168
- const [s, a, p] = [Number(o), Number(n), Number(r)];
169
- return s > 255 || a > 255 || p > 255 ? i : `#${t(s)}${t(a)}${t(p)}`;
160
+ const [s, a, c] = [Number(o), Number(n), Number(r)];
161
+ return s > 255 || a > 255 || c > 255 ? i : `#${t(s)}${t(a)}${t(c)}`;
170
162
  }).replace(/#([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3\b/gi, "#$1$2$3");
171
163
  },
172
164
  priority: 44
@@ -208,5 +200,5 @@ const g = [
208
200
  }
209
201
  ];
210
202
  export {
211
- g as defaultHtmlCompilerRules
203
+ d as defaultHtmlCompilerRules
212
204
  };
@@ -0,0 +1,4 @@
1
+ var n = /* @__PURE__ */ ((s) => (s.CouponTagModified = "coupon-tag-modified", s.DuplicateUnsubBlock = "duplicate-unsub-block", s.UnsubBlockMissingTemplate = "unsub-block-missing-template", s.LiquidSyntax = "liquid-syntax", s.InvalidLinks = "invalid-links", s.LinkCheckInProgress = "link-check-in-progress", s.CustomFieldScript = "custom-field-script", s.EmptyHtml = "empty-html", s.MismatchedBraces = "mismatched-braces", s.InvalidDynamicTag = "invalid-dynamic-tag", s.InvalidCustomField = "invalid-custom-field", s.ConditionsSyntax = "conditions-syntax", s.WrongEqualityOperator = "wrong-equality-operator", s.MissingIfEndif = "missing-if-endif", s.ConditionTagSpacing = "condition-tag-spacing", s.BracesInsideIfTag = "braces-inside-if-tag", s.RecommendationRequiredFields = "recommendation-required-fields", s.RecommendationFilters = "recommendation-filters", s.InvalidImageType = "invalid-image-type", s.CheckboxUnsubGroup = "checkbox-unsub-group", s.RadioButtonUnsubGroup = "radio-button-unsub-group", s))(n || {});
2
+ export {
3
+ n as SaveValidationRule
4
+ };
@@ -1,22 +1,22 @@
1
- import { getRecommendationFeedSourceMaps as b, getOperatorOptions as G, PriceAttributes as v } from "../../../../enums/extensions/recommendationBlock.js";
2
- import { useRecommendationApi as D } from "../../../../services/recommendationApi.js";
3
- import { useConfigStore as P } from "../../../../stores/config.js";
4
- import { defineStore as F } from "pinia";
1
+ import { getRecommendationFeedSourceMaps as b, getOperatorOptions as G, PriceAttributes as D } from "../../../../enums/extensions/recommendationBlock.js";
2
+ import { useRecommendationApi as P } from "../../../../services/recommendationApi.js";
3
+ import { useConfigStore as F } from "../../../../stores/config.js";
4
+ import { defineStore as _ } from "pinia";
5
5
  import "../constants/selectors.js";
6
- import { DEFAULT_MOBILE_CARDS_IN_ROW as _, DEFAULT_CARDS_IN_ROW as U } from "../constants/layout.js";
7
- import { DEFAULT_NODE_CONFIG as N, EXCLUDED_ALGORITHM_IDS as L } from "../constants/defaultConfig.js";
8
- import { getDefaultProducts as R } from "../templates/utils.js";
9
- import { generateCompleteFilterQuery as k } from "../utils/filterUtil.js";
10
- import { getPartnerRecommendationParams as x } from "../utils/partnerCustomizations.js";
6
+ import { DEFAULT_MOBILE_CARDS_IN_ROW as U, DEFAULT_CARDS_IN_ROW as N } from "../constants/layout.js";
7
+ import { DEFAULT_NODE_CONFIG as L, EXCLUDED_ALGORITHM_IDS as x } from "../constants/defaultConfig.js";
8
+ import { getDefaultProducts as k } from "../templates/utils.js";
9
+ import { generateCompleteFilterQuery as R } from "../utils/filterUtil.js";
10
+ import { getPartnerRecommendationParams as B } from "../utils/partnerCustomizations.js";
11
11
  import { sanitizeProductIds as A } from "../utils/productIds.js";
12
- import { isFilterValid as B } from "../validation/filterSchema.js";
13
- import { isConfigValid as M } from "../validation/requiredFields.js";
14
- const u = D();
12
+ import { isFilterValid as M } from "../validation/filterSchema.js";
13
+ import { isConfigValid as C } from "../validation/requiredFields.js";
14
+ const u = P();
15
15
  let f = null, h = null, g = null, S = null;
16
16
  const p = /* @__PURE__ */ new Map();
17
- function w() {
17
+ function v() {
18
18
  return {
19
- cardsInRow: U,
19
+ cardsInRow: N,
20
20
  currencySettings: {
21
21
  name: "USD",
22
22
  value: "USD",
@@ -30,7 +30,7 @@ function w() {
30
30
  productIds: [],
31
31
  id: 1,
32
32
  language: "en_US",
33
- mobileCardsInRow: _,
33
+ mobileCardsInRow: U,
34
34
  mobileLayoutEnabled: !1,
35
35
  orientation: "grid",
36
36
  recommendedProducts: [],
@@ -46,9 +46,9 @@ function w() {
46
46
  priceHideIfSameAsDiscounted: !1
47
47
  };
48
48
  }
49
- function C() {
49
+ function w() {
50
50
  return {
51
- recommendationConfigs: w(),
51
+ recommendationConfigs: v(),
52
52
  recommendationProducts: [],
53
53
  filterStatus: !1,
54
54
  filterSelectionDrawerStatus: !1,
@@ -59,7 +59,7 @@ function C() {
59
59
  filterSnapshot: null
60
60
  };
61
61
  }
62
- const z = () => ({
62
+ const O = () => ({
63
63
  recommendationCampaignUrls: {},
64
64
  activePredictiveAlgorithms: [],
65
65
  languages: {},
@@ -71,8 +71,8 @@ const z = () => ({
71
71
  blockStates: {},
72
72
  currentRecommendationId: null,
73
73
  configVersion: 0
74
- }), Y = F("guidoRecommendationExtension", {
75
- state: () => z(),
74
+ }), Y = _("guidoRecommendationExtension", {
75
+ state: () => O(),
76
76
  getters: {
77
77
  // ====================================================================
78
78
  // Proxy Getters — Backward Compatible Access to Current Block State
@@ -82,7 +82,7 @@ const z = () => ({
82
82
  * This allows all existing code that reads `store.recommendationConfigs` to work unchanged.
83
83
  */
84
84
  recommendationConfigs(t) {
85
- return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs : w();
85
+ return t.currentRecommendationId !== null && t.blockStates[t.currentRecommendationId] ? t.blockStates[t.currentRecommendationId].recommendationConfigs : v();
86
86
  },
87
87
  /**
88
88
  * Proxy getter: delegates to blockStates[currentRecommendationId].recommendationProducts
@@ -138,7 +138,7 @@ const z = () => ({
138
138
  },
139
139
  getActivePredictiveAlgorithms: (t) => {
140
140
  const e = b(), r = [];
141
- return t.activePredictiveAlgorithms.filter((n) => !L.includes(n)).forEach((n) => {
141
+ return t.activePredictiveAlgorithms.filter((n) => !x.includes(n)).forEach((n) => {
142
142
  r.push(...e.filter((s) => s.id === n));
143
143
  }), r.map((n) => ({
144
144
  text: n.name,
@@ -156,7 +156,7 @@ const z = () => ({
156
156
  getFilterList() {
157
157
  return Object.values(this.filterList).filter((t) => t.isFilterable !== 0 && t.isFilterable !== !1).map((t) => {
158
158
  let e;
159
- return t.type === "productAttribute" ? e = `product_attributes.${t.attributeName}` : v.includes(t.attributeName) ? e = `${t.attributeName}.${this.recommendationConfigs.currencySettings.value}` : e = t.attributeName, {
159
+ return t.type === "productAttribute" ? e = `product_attributes.${t.attributeName}` : D.includes(t.attributeName) ? e = `${t.attributeName}.${this.recommendationConfigs.currencySettings.value}` : e = t.attributeName, {
160
160
  text: t.displayName,
161
161
  value: e,
162
162
  type: t.attributeType
@@ -178,7 +178,7 @@ const z = () => ({
178
178
  setCurrentBlock(t) {
179
179
  this.blockStates[t] || (this.blockStates = {
180
180
  ...this.blockStates,
181
- [t]: C()
181
+ [t]: w()
182
182
  }), this.currentRecommendationId = t;
183
183
  },
184
184
  /**
@@ -246,7 +246,7 @@ const z = () => ({
246
246
  decimalCount: String(e.currencyDecimalCount),
247
247
  decimalSeparator: r(e.currencyDecimalSeparator, ","),
248
248
  thousandSeparator: r(e.currencyThousandSeparator, ".")
249
- }, s = !this.blockStates[t], i = s ? C() : this.blockStates[t];
249
+ }, s = !this.blockStates[t], i = s ? w() : this.blockStates[t];
250
250
  i.recommendationConfigs = {
251
251
  ...i.recommendationConfigs,
252
252
  strategy: e.strategy,
@@ -380,7 +380,7 @@ const z = () => ({
380
380
  });
381
381
  },
382
382
  removeStrategy() {
383
- this.patchCurrentBlockConfig({ strategyId: "", size: N.size });
383
+ this.patchCurrentBlockConfig({ strategyId: "", size: L.size });
384
384
  },
385
385
  // ====================================================================
386
386
  // Shared Data Fetching (fetched once, used by all blocks)
@@ -452,7 +452,7 @@ const z = () => ({
452
452
  const n = [...e.recommendationConfigs.filters];
453
453
  n[r] = {
454
454
  ...t,
455
- isValid: B(t)
455
+ isValid: M(t)
456
456
  }, e.recommendationConfigs.filters = n;
457
457
  }
458
458
  },
@@ -490,7 +490,7 @@ const z = () => ({
490
490
  }), e.recommendationConfigs.filters = r;
491
491
  },
492
492
  generateFilterQuery() {
493
- return k(this.recommendationConfigs.filters);
493
+ return R(this.recommendationConfigs.filters);
494
494
  },
495
495
  /**
496
496
  * Validation-only check invoked at save-CTA time. Defined as an action
@@ -498,7 +498,14 @@ const z = () => ({
498
498
  * every block's recommendationConfigs across user edits.
499
499
  */
500
500
  hasInvalidBlock() {
501
- return Object.values(this.blockStates).some((t) => !M(t.recommendationConfigs, this));
501
+ return Object.values(this.blockStates).some((t) => !C(t.recommendationConfigs, this));
502
+ },
503
+ /**
504
+ * Per-instance counterpart to `hasInvalidBlock`: the ids of every block
505
+ * that fails required-field validation, one drawer row each (SD-147430).
506
+ */
507
+ invalidBlockIds() {
508
+ return Object.entries(this.blockStates).filter(([, t]) => !C(t.recommendationConfigs, this)).map(([t]) => t);
502
509
  },
503
510
  // ====================================================================
504
511
  // Per-Block Product Fetching
@@ -519,12 +526,12 @@ const z = () => ({
519
526
  },
520
527
  async _doFetchProducts() {
521
528
  var d;
522
- const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = P();
529
+ const t = this.currentRecommendationId, e = this.blockStates[t], { recommendationConfigs: r } = e, n = F();
523
530
  if (r.strategyId && n.isFeatureEnabled("reusableRecommendationStrategy")) {
524
531
  await this._doFetchStrategyProducts(t);
525
532
  return;
526
533
  }
527
- const s = r.filters.filter((m) => m.isValid), i = k(s), o = ((d = b().find((m) => m.key === r.strategy)) == null ? void 0 : d.path) || "", c = parseInt(r.size) || 6, a = {
534
+ const s = r.filters.filter((m) => m.isValid), i = R(s), o = ((d = b().find((m) => m.key === r.strategy)) == null ? void 0 : d.path) || "", c = parseInt(r.size) || 6, a = {
528
535
  locale: r.language,
529
536
  currency: r.currencySettings.value,
530
537
  partnerName: n.partnerName,
@@ -534,7 +541,7 @@ const z = () => ({
534
541
  };
535
542
  r.strategy === "manualMerchandising" ? a.productId = A(r.productIds) : r.strategy === "similarViewed" && (a.productId = "{itemId}"), r.strategy === "userBased" && (a.userId = "{user_id}"), i && (a.filter = i), r.shuffleProducts && (a.shuffle = !0), Object.assign(
536
543
  a,
537
- x(n.partnerName, r.strategy)
544
+ B(n.partnerName, r.strategy)
538
545
  );
539
546
  let l;
540
547
  try {
@@ -567,10 +574,10 @@ const z = () => ({
567
574
  _setBlockProducts(t, e, r) {
568
575
  if (!this.blockStates[t])
569
576
  return;
570
- const n = e.length > 0 ? e : R(r);
577
+ const n = e.length > 0 ? e : k(r);
571
578
  this.blockStates[t].recommendationProducts = [
572
579
  ...n.slice(0, r),
573
- ...R(Math.max(0, r - n.length))
580
+ ...k(Math.max(0, r - n.length))
574
581
  ];
575
582
  }
576
583
  }