@useinsider/guido 3.14.1-beta.9ad6513 → 3.15.0-beta.244c73c

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.
@@ -1,43 +1,57 @@
1
- import { useActionsApi as T } from "./useActionsApi.js";
2
- import { useToaster as f } from "./useToaster.js";
3
- import { useTranslations as y } from "./useTranslations.js";
4
- import { ToasterTypeOptions as n } from "../enums/toaster.js";
5
- import { ref as v } from "vue";
6
- const E = 250, s = v({}), o = {}, j = () => {
7
- const { updateHtmlAndCss: l } = T(), { showToaster: p } = f(), i = y(), u = (e) => {
8
- s.value[e] === void 0 && (s.value = { ...s.value, [e]: "pending" });
9
- }, c = (e, t, r) => {
10
- s.value = { ...s.value, [e]: "applying" };
1
+ import { useActionsApi as d } from "./useActionsApi.js";
2
+ import { useConfig as D } from "./useConfig.js";
3
+ import { useToaster as h } from "./useToaster.js";
4
+ import { useTranslations as E } from "./useTranslations.js";
5
+ import { ToasterTypeOptions as f } from "../enums/toaster.js";
6
+ import { useDynamicContentStore as A } from "../stores/dynamic-content.js";
7
+ import { flattenDynamicContentList as S } from "../utils/genericUtil.js";
8
+ import { extractUsedDynamicContent as O } from "../utils/registerUsedDynamicContent.js";
9
+ import { ref as U } from "vue";
10
+ const w = 250, o = U({}), s = {}, u = /* @__PURE__ */ new Map(), z = () => {
11
+ const { updateHtmlAndCss: c } = d(), { showToaster: m } = h(), y = E(), { template: T } = D(), v = A(), g = (e, t) => {
12
+ const a = y(e);
13
+ return a === e ? t : a;
14
+ }, C = (e) => {
15
+ o.value[e] === void 0 && (o.value = { ...o.value, [e]: "pending" });
16
+ }, l = (e, t, a) => {
17
+ var n;
18
+ o.value = { ...o.value, [e]: "applying" };
11
19
  try {
12
- l(t, r), s.value = { ...s.value, [e]: "applied" }, p({
13
- type: n.Success,
14
- message: i("newsletter.ai-template-applied")
15
- });
16
- } catch (a) {
17
- s.value = { ...s.value, [e]: "failed" }, p({
18
- type: n.Error,
19
- message: a instanceof Error ? a.message : "Failed to apply template"
20
+ c(t, a), o.value = { ...o.value, [e]: "applied" };
21
+ try {
22
+ const r = S((n = T.value) == null ? void 0 : n.dynamicContentList), p = O(t, r);
23
+ p.length > 0 && v.registerDynamicContent(p);
24
+ } catch {
25
+ }
26
+ u.get(e) !== t && (u.set(e, t), m({
27
+ type: f.Success,
28
+ message: g("newsletter.ai-template-applied", "Template applied to the editor")
29
+ }));
30
+ } catch (r) {
31
+ o.value = { ...o.value, [e]: "failed" }, m({
32
+ type: f.Error,
33
+ message: r instanceof Error ? r.message : "Failed to apply template"
20
34
  });
21
35
  }
22
- }, m = (e) => {
23
- const t = o[e];
24
- t && (clearTimeout(t), delete o[e]);
36
+ }, i = (e) => {
37
+ const t = s[e];
38
+ t && (clearTimeout(t), delete s[e]);
25
39
  };
26
- return { applyStatus: s, markPending: u, applyTemplate: (e, t) => {
27
- t.html && (m(e), c(e, t.html, t.css ?? ""));
40
+ return { applyStatus: o, markPending: C, applyTemplate: (e, t) => {
41
+ t.html && (i(e), l(e, t.html, t.css ?? ""));
28
42
  }, applyTemplateDebounced: (e, t) => {
29
43
  if (!t.html)
30
44
  return;
31
- m(e);
32
- const { html: r } = t, a = t.css ?? "";
33
- o[e] = setTimeout(() => {
34
- delete o[e], c(e, r, a);
35
- }, E);
45
+ i(e);
46
+ const { html: a } = t, n = t.css ?? "";
47
+ s[e] = setTimeout(() => {
48
+ delete s[e], l(e, a, n);
49
+ }, w);
36
50
  } };
37
- }, k = () => {
38
- Object.values(o).forEach(clearTimeout), Object.keys(o).forEach((l) => delete o[l]), s.value = {};
51
+ }, G = () => {
52
+ Object.values(s).forEach(clearTimeout), Object.keys(s).forEach((c) => delete s[c]), o.value = {};
39
53
  };
40
54
  export {
41
- k as resetEmailTemplateApplier,
42
- j as useEmailTemplateApplier
55
+ G as resetEmailTemplateApplier,
56
+ z as useEmailTemplateApplier
43
57
  };
@@ -1,64 +1,69 @@
1
- import { useConfig as W } from "./useConfig.js";
2
- import { getIgnoredRecommendationBlockIds as G } from "../config/compiler/utils/recommendationIgnoreUtils.js";
3
- import { TemplateTypes as H } from "../enums/defaults.js";
4
- import { DISPLAY_CONDITIONS_REGEX as P, DISPLAY_CONDITIONS_EXCEPTIONS_REGEX as X, CampaignCouldNotBeSavedKey as $, CanNotMakeAnyChangesForRunningKey as Y, DATA_ATTRIBUTE_REGEX as U, DYNAMIC_CONTENT_TAG_REGEX as j, ALLOWED_DYNAMIC_SYSTEM_TOKENS as q, VALID_DYNAMIC_VARIABLE_REGEX as K } from "../enums/html-validator.js";
1
+ import { useConfig as V } from "./useConfig.js";
2
+ import { getIgnoredRecommendationBlockIds as M } from "../config/compiler/utils/recommendationIgnoreUtils.js";
3
+ import { TemplateTypes as W } from "../enums/defaults.js";
4
+ import { DISPLAY_CONDITIONS_REGEX as G, DISPLAY_CONDITIONS_EXCEPTIONS_REGEX as H, CampaignCouldNotBeSavedKey as P, CanNotMakeAnyChangesForRunningKey as X, DATA_ATTRIBUTE_REGEX as $, DYNAMIC_CONTENT_TAG_REGEX as Y, ALLOWED_DYNAMIC_SYSTEM_TOKENS as U, VALID_DYNAMIC_VARIABLE_REGEX as j } from "../enums/html-validator.js";
5
5
  import { ToasterTypeOptions as c } from "../enums/toaster.js";
6
- import { itemsBlockDynamicVariables as z } from "../extensions/Blocks/Items/enums/productEnums.js";
7
- import { RECOMMENDATION_VARIABLE_BUILTIN_ATTRIBUTES as Z } from "../extensions/Blocks/Recommendation/constants/selectors.js";
8
- import { useRecommendationExtensionStore as J } from "../extensions/Blocks/Recommendation/store/recommendation.js";
9
- import { resolveProductAttrValue as Q } from "../extensions/Blocks/Recommendation/templates/utils.js";
10
- import { RecommendationRequiredFieldsKey as ee } from "../extensions/Blocks/Recommendation/validation/requiredFields.js";
11
- import { useRecommendationStore as te } from "../stores/recommendation.js";
12
- import { base64EncodeWithSpecialChars as ne } from "../utils/base64.js";
13
- import { useHttp as se } from "./useHttp.js";
14
- import { useToaster as re } from "./useToaster.js";
15
- import { useTranslations as oe } from "./useTranslations.js";
16
- const ie = /recommendation-id="(\d+)"/g;
17
- function ae(t) {
18
- return [...t.matchAll(ie)].map((m) => m[1]);
6
+ import { itemsBlockDynamicVariables as q } from "../extensions/Blocks/Items/enums/productEnums.js";
7
+ import { RECOMMENDATION_VARIABLE_BUILTIN_ATTRIBUTES as K } from "../extensions/Blocks/Recommendation/constants/selectors.js";
8
+ import { useRecommendationExtensionStore as z } from "../extensions/Blocks/Recommendation/store/recommendation.js";
9
+ import { resolveProductAttrValue as Z } from "../extensions/Blocks/Recommendation/templates/utils.js";
10
+ import { RecommendationRequiredFieldsKey as J } from "../extensions/Blocks/Recommendation/validation/requiredFields.js";
11
+ import { useRecommendationStore as Q } from "../stores/recommendation.js";
12
+ import { base64EncodeWithSpecialChars as ee } from "../utils/base64.js";
13
+ import { computed as te } from "vue";
14
+ import { useHttp as ne } from "./useHttp.js";
15
+ import { useToaster as se } from "./useToaster.js";
16
+ import { useTranslations as re } from "./useTranslations.js";
17
+ const oe = /recommendation-id="(\d+)"/g;
18
+ function ie(n) {
19
+ return [...n.matchAll(oe)].map((m) => m[1]);
19
20
  }
20
- function ce(t, m) {
21
- return m.some((f) => t.startsWith(`${f}_`));
21
+ function ae(n, m) {
22
+ return m.some((f) => n.startsWith(`${f}_`));
22
23
  }
23
- const le = /^(\d+)_\d+_(.+)$/;
24
- function me(t) {
25
- const m = t.match(le);
24
+ const ce = /^(\d+)_\d+_(.+)$/;
25
+ function le(n) {
26
+ const m = n.match(ce);
26
27
  return m ? { recoId: m[1], attribute: m[2] } : null;
27
28
  }
28
- function ue(t) {
29
- const m = new Set(Z);
30
- return Object.values(t).forEach((f) => {
31
- m.add(Q(f.attributeName, t).toLowerCase());
29
+ function me(n) {
30
+ const m = new Set(K);
31
+ return Object.values(n).forEach((f) => {
32
+ m.add(Z(f.attributeName, n).toLowerCase());
32
33
  }), m;
33
34
  }
34
- function de(t, m, f, o, d) {
35
- const u = t.match(/{{([a-zA-Z0-9_.\s]*)}}/gm);
35
+ function ue(n, m, f, o, d) {
36
+ const u = n.match(/{{([a-zA-Z0-9_.\s]*)}}/gm);
36
37
  if (!u)
37
38
  return [];
38
- const p = new Set(m.map((h) => h.toLowerCase())), E = [];
39
+ const g = new Set(m.map((h) => h.toLowerCase())), E = [];
39
40
  return u.forEach((h) => {
40
- const g = h.slice(2, -2).trim().toLowerCase();
41
- if (g !== "" && p.has(g))
41
+ const p = h.slice(2, -2).trim().toLowerCase();
42
+ if (p !== "" && g.has(p))
42
43
  return;
43
- const y = me(g);
44
+ const y = le(p);
44
45
  if (y && o.has(y.recoId)) {
45
46
  if (!d || d.has(y.attribute))
46
47
  return;
47
- E.push(g);
48
+ E.push(p);
48
49
  return;
49
50
  }
50
- ce(g, f) || g.includes(".") || E.push(g);
51
+ ae(p, f) || p.includes(".") || E.push(p);
51
52
  }), E;
52
53
  }
53
- function fe(t) {
54
- return (t.replace(U, "$1").match(j) ?? []).filter((o) => {
55
- const d = o.slice(2, -2), u = d.indexOf("|"), p = (u === -1 ? d : d.slice(0, u)).trim();
56
- return q.includes(p) ? !1 : K.test(p) ? u !== -1 && d.slice(u + 1).trim() === "" : !0;
54
+ function de(n) {
55
+ return (n.replace($, "$1").match(Y) ?? []).filter((o) => {
56
+ const d = o.slice(2, -2), u = d.indexOf("|"), g = (u === -1 ? d : d.slice(0, u)).trim();
57
+ return U.includes(g) ? !1 : j.test(g) ? u !== -1 && d.slice(u + 1).trim() === "" : !0;
57
58
  });
58
59
  }
59
60
  const xe = () => {
60
- var b, S;
61
- const { showToaster: t } = re(), { post: m } = se(), { config: f } = W(), o = oe(), d = te(), u = J(), p = ((S = (b = f.value) == null ? void 0 : b.partner) == null ? void 0 : S.messageType) === H.transactional, E = async (e) => {
61
+ const { showToaster: n } = se(), { post: m } = ne(), { config: f } = V(), o = re(), d = Q(), u = z(), g = te(
62
+ () => {
63
+ var e, t;
64
+ return ((t = (e = f.value) == null ? void 0 : e.partner) == null ? void 0 : t.messageType) === W.transactional;
65
+ }
66
+ ), E = async (e) => {
62
67
  if (e.size === 0)
63
68
  return /* @__PURE__ */ new Set();
64
69
  try {
@@ -66,61 +71,61 @@ const xe = () => {
66
71
  } catch {
67
72
  return null;
68
73
  }
69
- return ue(u.filterList);
74
+ return me(u.filterList);
70
75
  }, h = async (e) => {
71
- const n = await m(
76
+ const s = await m(
72
77
  "/newsletter/template-library/check-template-html-body",
73
- { html: ne(e) }
74
- ), { status: r, message: a } = n.data;
75
- return r || t({
78
+ { html: ee(e) }
79
+ ), { status: r, message: a } = s.data;
80
+ return r || n({
76
81
  type: c.Error,
77
82
  message: r === void 0 ? a : o("newsletter.invalid-url-link-for-toaster")
78
- }), o($), a === o(Y) && t({
83
+ }), o(P), a === o(X) && n({
79
84
  type: c.Error,
80
85
  message: o("newsletter.already-in-progress")
81
86
  }), r;
82
- }, g = (e) => !["if", "endif", "else", "elif", "now"].includes(e.toLowerCase()), y = (e) => ["if", "endif"].includes(e.toLowerCase()), T = (e, s) => {
83
- const n = e.match(/({%(.*?)%})/g);
87
+ }, p = (e) => !["if", "endif", "else", "elif", "now"].includes(e.toLowerCase()), y = (e) => ["if", "endif"].includes(e.toLowerCase()), S = (e, t) => {
88
+ const s = e.match(/({%(.*?)%})/g);
84
89
  let r = !0;
85
- return n !== null && !p && n.forEach((a) => {
90
+ return s !== null && !g.value && s.forEach((a) => {
86
91
  const i = a.slice(2, -2).trim().match(/(".*?"|[^"\s]+)(?=\s*|\s*$)/g);
87
92
  if (i && i.length > 0) {
88
93
  const [l] = i;
89
- g(l) && !s.includes(l) && (t({
94
+ p(l) && !t.includes(l) && (n({
90
95
  type: c.Warning,
91
96
  message: o("custom-fields.invalid-custom-fields")
92
97
  }), r = !1);
93
98
  }
94
99
  }), r;
95
- }, w = async (e, s, n) => {
96
- const r = n ? await h(e) : !0;
97
- return T(e, s) && r;
98
- }, R = (e) => e.length > 0 ? !0 : (t({
100
+ }, A = async (e, t, s) => {
101
+ const r = s ? await h(e) : !0;
102
+ return S(e, t) && r;
103
+ }, T = (e) => e.length > 0 ? !0 : (n({
99
104
  type: c.Warning,
100
105
  message: o("newsletter.html-content-is-empty")
101
- }), !1), _ = (e) => {
102
- const s = (e.match(/{/gm) || []).length, n = (e.match(/}/gm) || []).length;
103
- return s > n && t({
106
+ }), !1), w = (e) => {
107
+ const t = (e.match(/{/gm) || []).length, s = (e.match(/}/gm) || []).length;
108
+ return t > s && n({
104
109
  type: c.Warning,
105
110
  message: o("custom-fields.missing-closing-braces")
106
- }), s < n && t({
111
+ }), t < s && n({
107
112
  type: c.Warning,
108
113
  message: o("custom-fields.missing-opening-braces")
109
- }), s === n;
110
- }, N = (e) => {
111
- const s = fe(e).length === 0;
112
- return s || t({
114
+ }), t === s;
115
+ }, R = (e) => {
116
+ const t = de(e).length === 0;
117
+ return t || n({
113
118
  type: c.Warning,
114
119
  message: o("custom-fields.invalid-custom-fields")
115
- }), s;
116
- }, x = (e, s, n, r) => {
117
- if (p)
120
+ }), t;
121
+ }, _ = (e, t, s, r) => {
122
+ if (g.value)
118
123
  return !0;
119
- const a = de(
124
+ const a = ue(
120
125
  e,
126
+ t,
127
+ ie(e),
121
128
  s,
122
- ae(e),
123
- n,
124
129
  r
125
130
  );
126
131
  if (a.length > 0) {
@@ -129,91 +134,91 @@ const xe = () => {
129
134
  ${a.map((l) => `<li>${l}</li>`).join("")}
130
135
  </ul>
131
136
  `;
132
- return t({
137
+ return n({
133
138
  type: c.Error,
134
139
  message: o("custom-fields.invalid-custom-fields") + i
135
140
  }), !1;
136
141
  }
137
142
  return !0;
138
- }, O = (e) => {
139
- const s = e.match(/{%(.*?)%}/g), n = [];
143
+ }, N = (e) => {
144
+ const t = e.match(/{%(.*?)%}/g), s = [];
140
145
  let r = !0;
141
- if (s && s.forEach((a) => {
142
- const i = a.match(P), l = a.match(X), v = (i == null ? void 0 : i.join("")) || "";
143
- (!i || a !== v) && !l && (t({
146
+ if (t && t.forEach((a) => {
147
+ const i = a.match(G), l = a.match(H), C = (i == null ? void 0 : i.join("")) || "";
148
+ (!i || a !== C) && !l && (n({
144
149
  type: c.Error,
145
150
  message: o("newsletter.display-conditions-invalid-syntax")
146
- }), r = !1), i && i.forEach((C) => {
147
- C.trim() === "=" && (t({
151
+ }), r = !1), i && i.forEach((v) => {
152
+ v.trim() === "=" && (n({
148
153
  type: c.Error,
149
154
  message: o("custom-conditions.wrong-equality-operators")
150
155
  }), r = !1);
151
- const I = C.match(/^[a-zA-Z]*$/g);
152
- I && I.forEach((A) => {
153
- y(A) && n.push(A);
156
+ const I = v.match(/^[a-zA-Z]*$/g);
157
+ I && I.forEach((b) => {
158
+ y(b) && s.push(b);
154
159
  });
155
160
  });
156
- }), n.length) {
157
- const a = n.filter((l) => l === "if"), i = n.filter((l) => l === "endif");
158
- a.length !== i.length && (t({
161
+ }), s.length) {
162
+ const a = s.filter((l) => l === "if"), i = s.filter((l) => l === "endif");
163
+ a.length !== i.length && (n({
159
164
  type: c.Error,
160
165
  message: o("custom-conditions.missing-if-endif-tag")
161
166
  }), r = !1);
162
167
  }
163
168
  return r;
164
- }, k = (e) => {
165
- const s = (e.match(/{% /gm) || []).length, n = (e.match(/ %}/gm) || []).length, r = s === n;
166
- return r || t({
169
+ }, x = (e) => {
170
+ const t = (e.match(/{% /gm) || []).length, s = (e.match(/ %}/gm) || []).length, r = t === s;
171
+ return r || n({
167
172
  type: c.Warning,
168
173
  message: o("custom-conditions.no-space-after-braces")
169
174
  }), r;
170
- }, B = (e) => (e.match(/({%(.*?)%})/g) || []).filter((n) => n.includes("if")).map((n) => (n.match(/{{.*}}/gm) || []).length).reduce((n, r) => n + r, 0) > 0 ? (t({
175
+ }, O = (e) => (e.match(/({%(.*?)%})/g) || []).filter((s) => s.includes("if")).map((s) => (s.match(/{{.*}}/gm) || []).length).reduce((s, r) => s + r, 0) > 0 ? (n({
171
176
  type: c.Warning,
172
177
  message: o("custom-conditions.no-braces-inside-if-tag")
173
- }), !1) : !0, D = () => u.hasInvalidBlock() ? (t({
178
+ }), !1) : !0, k = () => u.hasInvalidBlock() ? (n({
174
179
  type: c.Error,
175
- message: o(ee)
176
- }), !1) : !0, F = () => d.recommendationConfigs && Object.values(d.recommendationConfigs).find((s) => s.filters.find((n) => n.value === "")) !== void 0 ? (t({
180
+ message: o(J)
181
+ }), !1) : !0, B = () => d.recommendationConfigs && Object.values(d.recommendationConfigs).find((t) => t.filters.find((s) => s.value === "")) !== void 0 ? (n({
177
182
  type: c.Error,
178
183
  message: o("newsletter.fill-all-necessary-fields")
179
- }), !1) : !0, L = (e) => {
180
- const s = /src="[^"]*\.(svg|pst)"/gm;
181
- return e.match(s) === null ? !0 : (t({
184
+ }), !1) : !0, D = (e) => {
185
+ const t = /src="[^"]*\.(svg|pst)"/gm;
186
+ return e.match(t) === null ? !0 : (n({
182
187
  type: c.Error,
183
188
  message: o("newsletter.invalid-image-type")
184
189
  }), !1);
185
- }, V = (e) => {
190
+ }, F = (e) => {
186
191
  const r = new DOMParser().parseFromString(e, "text/html").querySelectorAll(".checkbox-block-v2");
187
192
  return Array.from(r).find((i) => {
188
193
  var l;
189
194
  return !((l = i.id) != null && l.trim());
190
- }) ? (t({
195
+ }) ? (n({
191
196
  type: c.Error,
192
197
  message: o("unsubscribe-templates.select-checkbox-groups")
193
198
  }), !1) : !0;
194
- }, M = (e) => {
199
+ }, L = (e) => {
195
200
  const r = new DOMParser().parseFromString(e, "text/html").querySelectorAll(".radio-button-v2");
196
201
  return Array.from(r).find((i) => {
197
202
  var l;
198
203
  return !((l = i.id) != null && l.trim());
199
- }) ? (t({
204
+ }) ? (n({
200
205
  type: c.Error,
201
206
  message: o("unsubscribe-templates.select-radio-button-groups")
202
207
  }), !1) : !0;
203
208
  };
204
- return { validateHtml: async (e, s, n = !1) => {
205
- var v, C;
209
+ return { validateHtml: async (e, t, s = !1) => {
210
+ var C, v;
206
211
  const r = [
207
- ...s.map((I) => I.value),
208
- ...z,
209
- ...((C = (v = f.value) == null ? void 0 : v.template) == null ? void 0 : C.customFieldAttributes) ?? []
210
- ], a = G(e), i = await E(a);
211
- return await w(e, r, n) && R(e) && _(e) && N(e) && x(e, r, a, i) && O(e) && k(e) && B(e) && D() && F() && L(e) && V(e) && M(e);
212
+ ...t.map((I) => I.value),
213
+ ...q,
214
+ ...((v = (C = f.value) == null ? void 0 : C.template) == null ? void 0 : v.customFieldAttributes) ?? []
215
+ ], a = M(e), i = await E(a);
216
+ return await A(e, r, s) && T(e) && w(e) && R(e) && _(e, r, a, i) && N(e) && x(e) && O(e) && k() && B() && D(e) && F(e) && L(e);
212
217
  } };
213
218
  };
214
219
  export {
215
- ue as buildPartnerAttributeNameSet,
216
- de as findInvalidCustomFields,
217
- me as parseRecommendationVariable,
220
+ me as buildPartnerAttributeNameSet,
221
+ ue as findInvalidCustomFields,
222
+ le as parseRecommendationVariable,
218
223
  xe as useHtmlValidator
219
224
  };
@@ -3,20 +3,21 @@ import { MinDeviceViewport as C, DefaultPadding as b } from "../enums/recommenda
3
3
  import { useRecommendationExtensionStore as l } from "../extensions/Blocks/Recommendation/store/recommendation.js";
4
4
  import { generateCompleteFilterQuery as h } from "../extensions/Blocks/Recommendation/utils/filterUtil.js";
5
5
  import { getPartnerRecommendationParams as y } from "../extensions/Blocks/Recommendation/utils/partnerCustomizations.js";
6
- import { useConfigStore as x } from "../stores/config.js";
7
- const k = () => ({
6
+ import { sanitizeProductIds as x } from "../extensions/Blocks/Recommendation/utils/productIds.js";
7
+ import { useConfigStore as P } from "../stores/config.js";
8
+ const B = () => ({
8
9
  calculateCardWidth: ({
9
10
  mobileLeftPadding: a,
10
11
  mobileRightPadding: s,
11
12
  cardsInRow: c,
12
- unresponsive: o
13
+ unresponsive: n
13
14
  }) => {
14
- const r = o ? c : 1, e = a + s + (r - 1) * b;
15
+ const r = n ? c : 1, e = a + s + (r - 1) * b;
15
16
  return (C - e) / r;
16
17
  },
17
18
  getRecommendationCampaignData: (a) => {
18
- const s = l(), c = Number(a), o = s.blockStates[c];
19
- if (!o)
19
+ const s = l(), c = Number(a), n = s.blockStates[c];
20
+ if (!n)
20
21
  return {
21
22
  textTrimming: !1,
22
23
  orientation: "vertical",
@@ -29,7 +30,7 @@ const k = () => ({
29
30
  discountBeforeTextValue: "",
30
31
  discountAfterTextValue: ""
31
32
  };
32
- const { recommendationConfigs: r } = o, e = r.orientation === "grid" ? "vertical" : "horizontal";
33
+ const { recommendationConfigs: r } = n, e = r.orientation === "grid" ? "vertical" : "horizontal";
33
34
  return {
34
35
  textTrimming: r.textTrimming,
35
36
  orientation: e,
@@ -45,7 +46,7 @@ const k = () => ({
45
46
  },
46
47
  buildCampaignUrl: (a, s) => {
47
48
  var m;
48
- const c = l(), o = x(), r = Number(a);
49
+ const c = l(), n = P(), r = Number(a);
49
50
  let e;
50
51
  if (s)
51
52
  e = s;
@@ -53,25 +54,25 @@ const k = () => ({
53
54
  const i = c.blockStates[r];
54
55
  if (!i)
55
56
  return "";
56
- const { recommendationConfigs: n } = i;
57
+ const { recommendationConfigs: o } = i;
57
58
  e = {
58
- strategy: n.strategy,
59
- language: n.language,
60
- currencyCode: n.currencySettings.value,
61
- size: n.size,
62
- productIds: n.productIds,
63
- filters: n.filters,
64
- shuffleProducts: n.shuffleProducts
59
+ strategy: o.strategy,
60
+ language: o.language,
61
+ currencyCode: o.currencySettings.value,
62
+ size: o.size,
63
+ productIds: o.productIds,
64
+ filters: o.filters,
65
+ shuffleProducts: o.shuffleProducts
65
66
  };
66
67
  }
67
68
  const f = ((m = I().find((i) => i.key === e.strategy)) == null ? void 0 : m.path) || "", t = new URLSearchParams();
68
- t.set("locale", e.language), t.set("currency", e.currencyCode), t.set("partnerName", o.partnerName), t.set("size", e.size), t.set("details", "true"), t.set("campaignId", o.variationId), e.strategy === "manualMerchandising" ? t.set("productId", e.productIds.join(",")) : e.strategy === "similarViewed" && t.set("productId", "{itemId}"), e.strategy === "userBased" && t.set("userId", "{user_id}");
69
+ t.set("locale", e.language), t.set("currency", e.currencyCode), t.set("partnerName", n.partnerName), t.set("size", e.size), t.set("details", "true"), t.set("campaignId", n.variationId), e.strategy === "manualMerchandising" ? t.set("productId", x(e.productIds)) : e.strategy === "similarViewed" && t.set("productId", "{itemId}"), e.strategy === "userBased" && t.set("userId", "{user_id}");
69
70
  const g = e.filters.filter((i) => i.isValid), d = h(g);
70
- d && t.set("filter", d), e.shuffleProducts && t.set("shuffle", "true"), Object.entries(y(o.partnerName, e.strategy)).forEach(([i, n]) => t.set(i, n));
71
+ d && t.set("filter", d), e.shuffleProducts && t.set("shuffle", "true"), Object.entries(y(n.partnerName, e.strategy)).forEach(([i, o]) => t.set(i, o));
71
72
  const p = decodeURIComponent(t.toString()), u = `${R.RECOMMENDATION_API_URL}/v2/${f}?${p}`;
72
73
  return c.recommendationCampaignUrls[a] = u, u;
73
74
  }
74
75
  });
75
76
  export {
76
- k as useRecommendation
77
+ B as useRecommendation
77
78
  };
@@ -1,23 +1,23 @@
1
- var c = Object.defineProperty;
2
- var l = (s, o, t) => o in s ? c(s, o, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[o] = t;
3
- var a = (s, o, t) => l(s, typeof o != "symbol" ? o + "" : o, t);
4
- import { UEAttr as h } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
5
- import { CommonControl as d } from "../../../common-control.js";
1
+ var d = Object.defineProperty;
2
+ var h = (r, o, t) => o in r ? d(r, o, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[o] = t;
3
+ var c = (r, o, t) => h(r, typeof o != "symbol" ? o + "" : o, t);
4
+ import { UEAttr as l } from "../../../../../node_modules/@stripoinc/ui-editor-extensions/dist/esm/index.js";
5
+ import { CommonControl as g } from "../../../common-control.js";
6
6
  import { RecommendationConfigService as n } from "../../services/configService.js";
7
7
  import { useRecommendationExtensionStore as m } from "../../store/recommendation.js";
8
- import { mapLegacyStrategy as g } from "../../utils/legacyStrategyMap.js";
9
- const u = "recommendation-algorithm-control", i = {
8
+ import { mapLegacyStrategy as u } from "../../utils/legacyStrategyMap.js";
9
+ const p = "recommendation-algorithm-control", e = {
10
10
  ALGORITHM: "strategy",
11
11
  PRODUCT_IDS: "productIds"
12
12
  };
13
- class R extends d {
13
+ class S extends g {
14
14
  constructor() {
15
15
  super(...arguments);
16
16
  // Store is used ONLY for API-fetched data (algorithms list), not for config
17
- a(this, "store", m());
17
+ c(this, "store", m());
18
18
  }
19
19
  getId() {
20
- return u;
20
+ return p;
21
21
  }
22
22
  getTemplate() {
23
23
  return `
@@ -25,13 +25,13 @@ class R extends d {
25
25
  ${this._GuTwoColumns([
26
26
  this._GuLabel({ text: this.api.translate("Recommendation Algorithm") }),
27
27
  this._GuSelect({
28
- name: i.ALGORITHM,
28
+ name: e.ALGORITHM,
29
29
  placeholder: this.api.translate("Select Recommendation Algorithm"),
30
30
  options: this.store.getActivePredictiveAlgorithms
31
31
  }),
32
- this._GuLabel({ text: this.api.translate("Product Ids"), name: `${i.PRODUCT_IDS}_label` }),
32
+ this._GuLabel({ text: this.api.translate("Product Ids"), name: `${e.PRODUCT_IDS}_label` }),
33
33
  this._GuTextInput({
34
- name: i.PRODUCT_IDS,
34
+ name: e.PRODUCT_IDS,
35
35
  placeholder: this.api.translate("Enter Product Ids"),
36
36
  className: "es-180w"
37
37
  })
@@ -46,29 +46,28 @@ class R extends d {
46
46
  super.onTemplateNodeUpdated(t), this._initializeSelectItems(), this._setFormValues();
47
47
  }
48
48
  _setFormValues() {
49
- const t = n.getConfig(this.currentNode), e = g(t.strategy) ?? t.strategy;
50
- this._setProductIdsVisibility(e), this.api.updateValues({
51
- [i.ALGORITHM]: e,
52
- [i.PRODUCT_IDS]: t.productIds.join(",")
53
- });
49
+ const t = n.getConfig(this.currentNode), s = u(t.strategy) ?? t.strategy;
50
+ this._setProductIdsVisibility(s);
51
+ const i = { [e.ALGORITHM]: s }, a = t.productIds.join(",");
52
+ this.api.getValues()[e.PRODUCT_IDS] !== a && (i[e.PRODUCT_IDS] = a), this.api.updateValues(i);
54
53
  }
55
54
  _initializeSelectItems() {
56
- var e;
57
- const t = (e = this.store) == null ? void 0 : e.getActivePredictiveAlgorithms;
55
+ var s;
56
+ const t = (s = this.store) == null ? void 0 : s.getActivePredictiveAlgorithms;
58
57
  if (t != null && t.length)
59
58
  try {
60
59
  this.api.setUIEAttribute(
61
- i.ALGORITHM,
62
- h.SELECTPICKER.items,
60
+ e.ALGORITHM,
61
+ l.SELECTPICKER.items,
63
62
  t
64
63
  );
65
- } catch (r) {
66
- console.warn("[AlgorithmControl] Failed to set algorithm options:", r);
64
+ } catch (i) {
65
+ console.warn("[AlgorithmControl] Failed to set algorithm options:", i);
67
66
  }
68
67
  }
69
68
  _setProductIdsVisibility(t) {
70
- const r = (t ?? n.getConfig(this.currentNode).strategy) === "manualMerchandising";
71
- this.api.setVisibility(i.PRODUCT_IDS, r), this.api.setVisibility(`${i.PRODUCT_IDS}_label`, r);
69
+ const i = (t ?? n.getConfig(this.currentNode).strategy) === "manualMerchandising";
70
+ this.api.setVisibility(e.PRODUCT_IDS, i), this.api.setVisibility(`${e.PRODUCT_IDS}_label`, i);
72
71
  }
73
72
  _onAlgorithmChange(t) {
74
73
  !this.currentNode || n.getConfig(this.currentNode).strategy === t || (n.updateConfig(
@@ -79,25 +78,25 @@ class R extends d {
79
78
  ), this.store.patchCurrentBlockConfig({ strategy: t }), this._setProductIdsVisibility(t));
80
79
  }
81
80
  _onProductIdsChange(t) {
82
- if (!this.currentNode)
81
+ if (!this.currentNode || n.getConfig(this.currentNode).productIds.join(",") === t)
83
82
  return;
84
- const e = t.split(",").map((r) => r.trim()).filter(Boolean);
83
+ const i = t.split(",");
85
84
  n.updateConfig(
86
85
  this.api,
87
86
  this.currentNode,
88
- { productIds: e },
87
+ { productIds: i },
89
88
  "Updated product IDs"
90
- ), this.store.patchCurrentBlockConfig({ productIds: e });
89
+ ), this.store.patchCurrentBlockConfig({ productIds: i });
91
90
  }
92
91
  _listenToFormUpdates() {
93
- this.api.onValueChanged(i.ALGORITHM, (t) => {
92
+ this.api.onValueChanged(e.ALGORITHM, (t) => {
94
93
  this._onAlgorithmChange(t);
95
- }), this.api.onValueChanged(i.PRODUCT_IDS, (t) => {
94
+ }), this.api.onValueChanged(e.PRODUCT_IDS, (t) => {
96
95
  this._onProductIdsChange(t);
97
96
  });
98
97
  }
99
98
  }
100
99
  export {
101
- u as ALGORITHM_CONTROL_ID,
102
- R as AlgorithmControl
100
+ p as ALGORITHM_CONTROL_ID,
101
+ S as AlgorithmControl
103
102
  };
@@ -1,15 +1,16 @@
1
1
  import { getRecommendationFeedSourceMaps as g, getOperatorOptions as R, PriceAttributes as C } from "../../../../enums/extensions/recommendationBlock.js";
2
2
  import { useRecommendationApi as y } from "../../../../services/recommendationApi.js";
3
3
  import { useConfigStore as G } from "../../../../stores/config.js";
4
- import { defineStore as F } from "pinia";
4
+ import { defineStore as P } from "pinia";
5
5
  import "../constants/selectors.js";
6
- import { DEFAULT_MOBILE_CARDS_IN_ROW as P, DEFAULT_CARDS_IN_ROW as D } from "../constants/layout.js";
6
+ import { DEFAULT_MOBILE_CARDS_IN_ROW as F, DEFAULT_CARDS_IN_ROW as D } from "../constants/layout.js";
7
7
  import { EXCLUDED_ALGORITHM_IDS as w } from "../constants/defaultConfig.js";
8
8
  import { getDefaultProducts as S } from "../templates/utils.js";
9
9
  import { generateCompleteFilterQuery as b } from "../utils/filterUtil.js";
10
10
  import { getPartnerRecommendationParams as v } from "../utils/partnerCustomizations.js";
11
- import { isFilterValid as N } from "../validation/filterSchema.js";
12
- import { isConfigValid as x } from "../validation/requiredFields.js";
11
+ import { sanitizeProductIds as N } from "../utils/productIds.js";
12
+ import { isFilterValid as x } from "../validation/filterSchema.js";
13
+ import { isConfigValid as L } from "../validation/requiredFields.js";
13
14
  const h = y();
14
15
  let m = null, u = null, d = null;
15
16
  function k() {
@@ -28,7 +29,7 @@ function k() {
28
29
  productIds: [],
29
30
  id: 1,
30
31
  language: "en_US",
31
- mobileCardsInRow: P,
32
+ mobileCardsInRow: F,
32
33
  mobileLayoutEnabled: !1,
33
34
  orientation: "grid",
34
35
  recommendedProducts: [],
@@ -54,7 +55,7 @@ function I() {
54
55
  filterSnapshot: null
55
56
  };
56
57
  }
57
- const L = () => ({
58
+ const U = () => ({
58
59
  recommendationCampaignUrls: {},
59
60
  activePredictiveAlgorithms: [],
60
61
  languages: {},
@@ -63,8 +64,8 @@ const L = () => ({
63
64
  blockStates: {},
64
65
  currentRecommendationId: null,
65
66
  configVersion: 0
66
- }), $ = F("guidoRecommendationExtension", {
67
- state: () => L(),
67
+ }), H = P("guidoRecommendationExtension", {
68
+ state: () => U(),
68
69
  getters: {
69
70
  // ====================================================================
70
71
  // Proxy Getters — Backward Compatible Access to Current Block State
@@ -383,7 +384,7 @@ const L = () => ({
383
384
  const n = [...e.recommendationConfigs.filters];
384
385
  n[r] = {
385
386
  ...t,
386
- isValid: N(t)
387
+ isValid: x(t)
387
388
  }, e.recommendationConfigs.filters = n;
388
389
  }
389
390
  },
@@ -429,7 +430,7 @@ const L = () => ({
429
430
  * every block's recommendationConfigs across user edits.
430
431
  */
431
432
  hasInvalidBlock() {
432
- return Object.values(this.blockStates).some((t) => !x(t.recommendationConfigs, this));
433
+ return Object.values(this.blockStates).some((t) => !L(t.recommendationConfigs, this));
433
434
  },
434
435
  // ====================================================================
435
436
  // Per-Block Product Fetching
@@ -458,7 +459,7 @@ const L = () => ({
458
459
  details: !0,
459
460
  campaignId: o.variationId
460
461
  };
461
- r.strategy === "manualMerchandising" ? a.productId = r.productIds.join(",") : r.strategy === "similarViewed" && (a.productId = "{itemId}"), r.strategy === "userBased" && (a.userId = "{user_id}"), c && (a.filter = c), r.shuffleProducts && (a.shuffle = !0), Object.assign(
462
+ r.strategy === "manualMerchandising" ? a.productId = N(r.productIds) : r.strategy === "similarViewed" && (a.productId = "{itemId}"), r.strategy === "userBased" && (a.userId = "{user_id}"), c && (a.filter = c), r.shuffleProducts && (a.shuffle = !0), Object.assign(
462
463
  a,
463
464
  v(o.partnerName, r.strategy)
464
465
  );
@@ -479,5 +480,5 @@ const L = () => ({
479
480
  }
480
481
  });
481
482
  export {
482
- $ as useRecommendationExtensionStore
483
+ H as useRecommendationExtensionStore
483
484
  };
@@ -0,0 +1,6 @@
1
+ function n(t) {
2
+ return t.map((i) => i.trim()).filter(Boolean).join(",");
3
+ }
4
+ export {
5
+ n as sanitizeProductIds
6
+ };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Builds the request/URL `productId` value from raw product ID segments.
3
+ * Trimming happens here — NOT on input change — so the user's in-progress
4
+ * text (trailing commas, spaces) is never rewritten under the caret (SD-147111).
5
+ */
6
+ export declare function sanitizeProductIds(ids: string[]): string;
@@ -36,4 +36,12 @@ export declare const useDynamicContentStore: import("pinia").StoreDefinition<"gu
36
36
  }[];
37
37
  }, {
38
38
  setSelectedDynamicContent(dynamicContent: DynamicContent): void;
39
+ /**
40
+ * Append-only bulk registration for dynamic content used by an
41
+ * AI-applied template. Unlike `setSelectedDynamicContent` it doesn't
42
+ * touch `selectedDynamicContent`; the getter removes duplicates, so
43
+ * re-registering the same entries is harmless. Items are shallow-copied so Pinia
44
+ * reactivity can't write a later mutation back into the host's catalog.
45
+ */
46
+ registerDynamicContent(items: DynamicContent[]): void;
39
47
  }>;
@@ -0,0 +1,8 @@
1
+ import type { DynamicContent } from '@@/Types/generic';
2
+ export declare const extractUsedDynamicContent: (html: string, flattenedCatalog: DynamicContent[] | undefined | null) => DynamicContent[];
3
+ /**
4
+ * Dev-diagnostic sibling of `extractUsedDynamicContent`: the used token names
5
+ * with NO catalog match (so intentionally not registered). Surfaced via a
6
+ * dev-gated `console.debug` to spot a neo↔guido vocabulary mismatch. Never throws.
7
+ */
8
+ export declare const getUnmatchedDynamicContentTokens: (html: string, flattenedCatalog: DynamicContent[] | undefined | null) => string[];
@@ -1,6 +1,6 @@
1
1
  import { removeDuplicatesFromArray as n } from "../utils/arrayUtil.js";
2
2
  import { defineStore as a } from "pinia";
3
- const s = a("guidoDynamicContent", {
3
+ const c = a("guidoDynamicContent", {
4
4
  state: () => ({
5
5
  selectedDynamicContent: { text: "", value: "" },
6
6
  selectedDynamicContentList: []
@@ -8,6 +8,16 @@ const s = a("guidoDynamicContent", {
8
8
  actions: {
9
9
  setSelectedDynamicContent(t) {
10
10
  this.selectedDynamicContent = t, this.selectedDynamicContentList.push(t);
11
+ },
12
+ /**
13
+ * Append-only bulk registration for dynamic content used by an
14
+ * AI-applied template. Unlike `setSelectedDynamicContent` it doesn't
15
+ * touch `selectedDynamicContent`; the getter removes duplicates, so
16
+ * re-registering the same entries is harmless. Items are shallow-copied so Pinia
17
+ * reactivity can't write a later mutation back into the host's catalog.
18
+ */
19
+ registerDynamicContent(t) {
20
+ this.selectedDynamicContentList.push(...t.map((e) => ({ ...e })));
11
21
  }
12
22
  },
13
23
  getters: {
@@ -20,5 +30,5 @@ const s = a("guidoDynamicContent", {
20
30
  }
21
31
  });
22
32
  export {
23
- s as useDynamicContentStore
33
+ c as useDynamicContentStore
24
34
  };
@@ -0,0 +1,23 @@
1
+ import { DATA_ATTRIBUTE_REGEX as T } from "../enums/html-validator.js";
2
+ const d = /{{([a-zA-Z0-9_.\s]*)}}/gm, h = (n, e) => {
3
+ if (!e || e.length === 0)
4
+ return { matched: [], unmatchedTokens: [] };
5
+ const s = n.replace(T, "$1").match(d);
6
+ if (!s)
7
+ return { matched: [], unmatchedTokens: [] };
8
+ const u = new Map(
9
+ e.map((c) => [c.value.trim().toLowerCase(), c])
10
+ ), a = /* @__PURE__ */ new Set(), o = [], m = [];
11
+ return s.forEach((c) => {
12
+ const t = c.slice(2, -2).trim().toLowerCase();
13
+ if (t === "" || a.has(t))
14
+ return;
15
+ a.add(t);
16
+ const r = u.get(t);
17
+ r ? o.push(r) : m.push(t);
18
+ }), { matched: o, unmatchedTokens: m };
19
+ }, E = (n, e) => h(n, e).matched, k = (n, e) => h(n, e).unmatchedTokens;
20
+ export {
21
+ E as extractUsedDynamicContent,
22
+ k as getUnmatchedDynamicContentTokens
23
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@useinsider/guido",
3
- "version": "3.14.1-beta.9ad6513",
3
+ "version": "3.15.0-beta.244c73c",
4
4
  "description": "Guido is a Vue + TypeScript wrapper for Email Plugin. Easily embed the email editor in your Vue applications.",
5
5
  "main": "./dist/guido.umd.cjs",
6
6
  "module": "./dist/library.js",