@useinsider/guido 3.19.0-beta.c8d2be8 → 3.19.0-beta.cd75ff0

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 (86) hide show
  1. package/README.md +1 -0
  2. package/dist/@types/config/schemas.js +102 -87
  3. package/dist/components/Guido.vue.js +4 -4
  4. package/dist/components/Guido.vue2.js +83 -75
  5. package/dist/components/organisms/AutoSaveController.vue2.js +10 -8
  6. package/dist/components/organisms/base/AuthErrorModal.vue.js +18 -0
  7. package/dist/components/organisms/base/AuthErrorModal.vue2.js +42 -0
  8. package/dist/components/organisms/header/EditorActions.vue.js +2 -2
  9. package/dist/components/organisms/header/EditorActions.vue2.js +83 -48
  10. package/dist/components/organisms/header/LeftSlot.vue.js +5 -5
  11. package/dist/components/organisms/header/LeftSlot.vue2.js +16 -12
  12. package/dist/components/organisms/validation/ValidationErrorDrawer.vue.js +20 -0
  13. package/dist/components/organisms/validation/ValidationErrorDrawer.vue2.js +50 -0
  14. package/dist/components/wrappers/WpDrawer.vue.js +9 -7
  15. package/dist/components/wrappers/WpDrawer.vue2.js +1 -1
  16. package/dist/components/wrappers/WpModal.vue.js +1 -1
  17. package/dist/components/wrappers/WpModal.vue2.js +4 -3
  18. package/dist/composables/useActionsApi.js +52 -47
  19. package/dist/composables/useAuthError.js +52 -0
  20. package/dist/composables/useAutoSave.js +44 -42
  21. package/dist/composables/useCodeEditorApi.js +23 -9
  22. package/dist/composables/useHtmlCompiler.js +37 -26
  23. package/dist/composables/useHtmlValidator.js +2 -0
  24. package/dist/composables/useHttp.js +48 -49
  25. package/dist/composables/useLegacyStrategyConversion.js +23 -19
  26. package/dist/composables/useSave.js +92 -45
  27. package/dist/composables/useSaveValidation.js +284 -0
  28. package/dist/composables/useSaveValidationRollout.js +20 -0
  29. package/dist/composables/useStripo.js +24 -20
  30. package/dist/composables/useToaster.js +17 -15
  31. package/dist/composables/useTranslations.js +3 -2
  32. package/dist/composables/useVersionHistoryApi.js +33 -24
  33. package/dist/composables/validators/useCouponBlockValidator.js +24 -21
  34. package/dist/composables/validators/useLiquidValidator.js +15 -15
  35. package/dist/composables/validators/useUnsubscribeBlockValidator.js +34 -27
  36. package/dist/config/migrator/index.js +14 -13
  37. package/dist/config/migrator/recommendationPartnerMigrator.js +50 -0
  38. package/dist/enums/extensions/recommendationBlock.js +30 -29
  39. package/dist/enums/save-validation.js +4 -0
  40. package/dist/extensions/Blocks/Recommendation/block.js +15 -14
  41. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +45 -38
  42. package/dist/extensions/Blocks/Recommendation/controls/main/strategy.js +72 -49
  43. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +30 -29
  44. package/dist/extensions/Blocks/Recommendation/utils/partnerScope.js +25 -0
  45. package/dist/guido.css +1 -1
  46. package/dist/services/settingsApi.js +11 -0
  47. package/dist/src/@types/config/schemas.d.ts +20 -0
  48. package/dist/src/@types/save-validation.d.ts +19 -0
  49. package/dist/src/components/Guido.vue.d.ts +17 -1
  50. package/dist/src/components/organisms/base/AuthErrorModal.vue.d.ts +2 -0
  51. package/dist/src/components/organisms/header/EditorActions.vue.d.ts +17 -1
  52. package/dist/src/components/organisms/header/HeaderWrapper.vue.d.ts +17 -1
  53. package/dist/src/components/organisms/header/RightSlot.vue.d.ts +17 -1
  54. package/dist/src/components/organisms/validation/ValidationErrorDrawer.vue.d.ts +2 -0
  55. package/dist/src/components/wrappers/WpDrawer.vue.d.ts +4 -3
  56. package/dist/src/components/wrappers/WpModal.vue.d.ts +5 -1
  57. package/dist/src/composables/useAuthError.d.ts +12 -0
  58. package/dist/src/composables/useAutoSave.d.ts +4 -1
  59. package/dist/src/composables/useConfig.d.ts +4 -0
  60. package/dist/src/composables/useHtmlValidator.d.ts +21 -0
  61. package/dist/src/composables/useHttp.d.ts +2 -0
  62. package/dist/src/composables/useSave.d.ts +6 -1
  63. package/dist/src/composables/useSaveValidation.d.ts +6 -0
  64. package/dist/src/composables/useSaveValidationRollout.d.ts +4 -0
  65. package/dist/src/composables/validators/useCouponBlockValidator.d.ts +3 -0
  66. package/dist/src/composables/validators/useUnsubscribeBlockValidator.d.ts +10 -0
  67. package/dist/src/config/migrator/index.d.ts +2 -1
  68. package/dist/src/config/migrator/recommendationPartnerMigrator.d.ts +19 -0
  69. package/dist/src/enums/extensions/recommendationBlock.d.ts +1 -1
  70. package/dist/src/enums/save-validation.d.ts +27 -0
  71. package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +6 -0
  72. package/dist/src/extensions/Blocks/Recommendation/utils/partnerScope.d.ts +33 -0
  73. package/dist/src/services/settingsApi.d.ts +6 -0
  74. package/dist/src/stores/config.d.ts +52 -0
  75. package/dist/src/stores/save-validation.d.ts +22 -0
  76. package/dist/src/stores/setup-settings.d.ts +4 -0
  77. package/dist/src/utils/stripoEditorApi.d.ts +4 -0
  78. package/dist/src/utils/templatePreparation.d.ts +30 -0
  79. package/dist/static/styles/components/recommendation-strategy.css.js +9 -0
  80. package/dist/stores/config.js +84 -67
  81. package/dist/stores/save-validation.js +30 -0
  82. package/dist/stores/setup-settings.js +10 -0
  83. package/dist/stores/unsubscribe.js +3 -3
  84. package/dist/utils/stripoEditorApi.js +11 -0
  85. package/dist/utils/templatePreparation.js +39 -36
  86. package/package.json +2 -1
@@ -1,99 +1,98 @@
1
- import { getCsrfToken as O } from "../utils/genericUtil.js";
2
- import { ref as m, computed as w } from "vue";
3
- const k = (g = {}) => {
4
- const h = m(!1), n = m(null), u = m(null), b = w(() => h.value), y = w(() => n.value !== null), v = {
1
+ import { getCsrfToken as D } from "../utils/genericUtil.js";
2
+ import { ref as T, computed as w } from "vue";
3
+ import { isPanelOrigin as j, reportAuthError as X } from "./useAuthError.js";
4
+ const k = [401, 403, 419], _ = (g = {}) => {
5
+ const f = T(!1), n = T(null), c = T(null), b = w(() => f.value), y = w(() => n.value !== null), E = {
5
6
  baseURL: "",
6
7
  timeout: 1e4,
7
8
  retry: 0,
8
9
  retryDelay: 1e3,
9
10
  headers: {
10
11
  "Content-Type": "application/json",
11
- "X-CSRF-TOKEN": O(),
12
+ "X-CSRF-TOKEN": D(),
12
13
  Accept: "application/json",
13
- // The panel backend only answers 401/419 to ajax requests; without this a guest
14
- // fetch gets a 302 to the login page and never sees an auth status.
14
+ // Without this a guest fetch gets a 302 to the login page, never an auth status.
15
15
  "X-Requested-With": "XMLHttpRequest"
16
16
  },
17
17
  ...g
18
18
  }, x = (e, r) => {
19
19
  if (e.startsWith("http://") || e.startsWith("https://"))
20
20
  return e;
21
- const t = r || v.baseURL || "";
21
+ const t = r || E.baseURL || "";
22
22
  return t.endsWith("/") && e.startsWith("/") ? `${t}${e.slice(1)}` : t.endsWith("/") || e.startsWith("/") ? `${t}${e}` : `${t}/${e}`;
23
- }, P = (e) => new Promise((r, t) => {
23
+ }, R = (e) => new Promise((r, t) => {
24
24
  setTimeout(() => {
25
25
  t(new Error("Request timeout"));
26
26
  }, e);
27
- }), C = (e) => new Promise((r) => {
27
+ }), P = (e) => new Promise((r) => {
28
28
  setTimeout(r, e);
29
29
  }), i = async (e, r, t, W = {}) => {
30
- n.value = null, h.value = !0, u.value = new AbortController();
31
- const a = { ...v, ...W }, E = x(r, a.baseURL), c = {
30
+ n.value = null, f.value = !0, c.value = new AbortController();
31
+ const a = { ...E, ...W }, p = x(r, a.baseURL), d = {
32
32
  method: e,
33
33
  headers: { ...a.headers },
34
- signal: u.value.signal,
34
+ signal: c.value.signal,
35
35
  ...a
36
36
  };
37
- t && ["POST", "PUT", "PATCH"].includes(e) && (t instanceof FormData ? (delete c.headers["Content-Type"], c.body = t) : c.body = JSON.stringify(t));
38
- const T = async (p = 0) => {
39
- if (window.isSessionExpired)
40
- throw n.value = { message: "Session expired" }, new Error("Session expired");
37
+ t && ["POST", "PUT", "PATCH"].includes(e) && (t instanceof FormData ? (delete d.headers["Content-Type"], d.body = t) : d.body = JSON.stringify(t));
38
+ const v = async (m = 0) => {
39
+ if (window.isSessionExpired) {
40
+ n.value = { message: "Session expired", authReported: !0 };
41
+ const o = new Error("Session expired");
42
+ throw o.authReported = !0, o;
43
+ }
41
44
  try {
42
- const o = [fetch(E, c)];
43
- a.timeout && o.push(P(a.timeout));
45
+ const o = [fetch(p, d)];
46
+ a.timeout && o.push(R(a.timeout));
44
47
  const s = await Promise.race(o);
45
48
  if (!s.ok) {
46
- const D = `HTTP Error: ${s.status} ${s.statusText}`, f = new Error(D);
47
- throw f.status = s.status, f.statusText = s.statusText, f.response = s, [401, 403, 419].includes(s.status) && Promise.resolve().then(() => s.clone().text().catch(() => "")).then((H) => window.dispatchEvent(new CustomEvent("insider:http-auth-error", {
48
- detail: {
49
- status: s.status,
50
- body: H,
51
- url: new URL(E, window.location.href).href
52
- }
53
- }))).catch(() => {
54
- }), f;
49
+ const H = `HTTP Error: ${s.status} ${s.statusText}`, l = new Error(H);
50
+ throw l.status = s.status, l.statusText = s.statusText, l.response = s, l.authReported = k.includes(s.status) && j(p), l.authReported && Promise.resolve().then(() => s.clone().text().catch(() => "")).then((O) => X(
51
+ s.status,
52
+ O,
53
+ new URL(p, window.location.href).href
54
+ )).catch(() => {
55
+ }), l;
55
56
  }
56
- let d;
57
- const l = s.headers.get("content-type");
58
- return l != null && l.includes("application/json") ? d = await s.json() : l != null && l.includes("text/") ? d = await s.text() : d = await s.blob(), {
59
- data: d,
57
+ let h;
58
+ const u = s.headers.get("content-type");
59
+ return u != null && u.includes("application/json") ? h = await s.json() : u != null && u.includes("text/") ? h = await s.text() : h = await s.blob(), {
60
+ data: h,
60
61
  status: s.status,
61
62
  statusText: s.statusText,
62
63
  headers: s.headers
63
64
  };
64
65
  } catch (o) {
65
66
  const s = o instanceof Error && o.name === "AbortError";
66
- if (p < (a.retry || 0) && !s)
67
- return await C(a.retryDelay || 1e3), T(p + 1);
67
+ if (m < (a.retry || 0) && !s)
68
+ return await P(a.retryDelay || 1e3), v(m + 1);
68
69
  throw o instanceof Error ? o.name === "AbortError" ? n.value = { message: "Request was cancelled" } : n.value = { message: o.message } : n.value = o, o;
69
70
  }
70
71
  };
71
72
  try {
72
- return await T();
73
+ return await v();
73
74
  } finally {
74
- h.value = !1, u.value = null;
75
+ f.value = !1, c.value = null;
75
76
  }
76
- }, R = (e, r) => i("GET", e, void 0, r), U = (e, r, t) => i("POST", e, r, t), $ = (e, r, t) => i("PUT", e, r, t), q = (e, r, t) => i("PATCH", e, r, t), A = (e, r) => i("DELETE", e, void 0, r), L = (e) => {
77
- u.value && u.value.abort(e);
78
- }, S = () => {
77
+ }, A = (e, r) => i("GET", e, void 0, r), S = (e, r, t) => i("POST", e, r, t), U = (e, r, t) => i("PUT", e, r, t), C = (e, r, t) => i("PATCH", e, r, t), $ = (e, r) => i("DELETE", e, void 0, r), q = (e) => {
78
+ c.value && c.value.abort(e);
79
+ }, L = () => {
79
80
  n.value = null;
80
81
  };
81
82
  return {
82
- // State
83
83
  loading: b,
84
84
  error: w(() => n.value),
85
85
  hasError: y,
86
- // Methods
87
86
  request: i,
88
- get: R,
89
- post: U,
90
- put: $,
91
- patch: q,
92
- delete: A,
93
- cancel: L,
94
- clearError: S
87
+ get: A,
88
+ post: S,
89
+ put: U,
90
+ patch: C,
91
+ delete: $,
92
+ cancel: q,
93
+ clearError: L
95
94
  };
96
95
  };
97
96
  export {
98
- k as useHttp
97
+ _ as useHttp
99
98
  };
@@ -1,23 +1,23 @@
1
- import { useRecommendation as I } from "./useRecommendation.js";
2
- import { useToaster as C } from "./useToaster.js";
1
+ import { useRecommendation as C } from "./useRecommendation.js";
2
+ import { useToaster as I } from "./useToaster.js";
3
3
  import { useTranslations as v } from "./useTranslations.js";
4
4
  import { ToasterTypeOptions as g } from "../enums/toaster.js";
5
5
  import "../extensions/Blocks/Recommendation/constants/selectors.js";
6
6
  import { CURRENT_CONFIG_VERSION as L } from "../extensions/Blocks/Recommendation/constants/defaultConfig.js";
7
7
  import { useRecommendationExtensionStore as b } from "../extensions/Blocks/Recommendation/store/recommendation.js";
8
- import { resolveMigrationTarget as R, buildLegacyMigrationPayload as T } from "../extensions/Blocks/Recommendation/utils/legacyStrategyMigration.js";
9
- import { commitBlockChangeOutsidePanel as h } from "../extensions/Blocks/Recommendation/utils/nodeConfigWriter.js";
10
- import { useConfigStore as k } from "../stores/config.js";
8
+ import { resolveMigrationTarget as k, buildLegacyMigrationPayload as R } from "../extensions/Blocks/Recommendation/utils/legacyStrategyMigration.js";
9
+ import { commitBlockChangeOutsidePanel as T } from "../extensions/Blocks/Recommendation/utils/nodeConfigWriter.js";
10
+ import { useConfigStore as h } from "../stores/config.js";
11
11
  const F = () => {
12
- const e = b(), r = k(), { buildCampaignUrl: m } = I(), { showToaster: i } = C(), c = v();
12
+ const e = b(), r = h(), { buildCampaignUrl: m } = C(), { showToaster: c } = I(), a = v();
13
13
  return { convertLegacyStrategy: async () => {
14
14
  const t = e.currentRecommendationId;
15
15
  if (t === null || !e.isLegacyStrategyBlock || e.legacyConversionLoading)
16
16
  return "";
17
- const { strategy: d, productIds: y, size: l, strategyId: p, nodeConfigVersion: u } = e.recommendationConfigs, f = { strategyId: p, size: l, nodeConfigVersion: u }, s = R({
17
+ const { strategy: d, productIds: y, size: l, strategyId: p, nodeConfigVersion: u } = e.recommendationConfigs, f = { strategyId: p, size: l, nodeConfigVersion: u }, s = k({
18
18
  campaignId: r.campaignId,
19
19
  templateLibraryId: r.templateLibraryId
20
- }), S = T({
20
+ }), S = R({
21
21
  blockId: t,
22
22
  url: m(String(t)),
23
23
  campaignId: r.campaignId,
@@ -29,30 +29,34 @@ const F = () => {
29
29
  let o;
30
30
  try {
31
31
  o = await e.convertLegacyStrategy(S, s);
32
- } catch (n) {
33
- return console.error("[useLegacyStrategyConversion] migration failed", n), i({
32
+ } catch (i) {
33
+ return console.error("[useLegacyStrategyConversion] migration failed", i), c({
34
34
  type: g.Error,
35
- message: c("email-editor.legacy-strategy-update-failed")
35
+ message: a("email-editor.legacy-strategy-migration-failed"),
36
+ actionButton: {
37
+ text: a("ds-steps.create-recommendation-strategy"),
38
+ onClick: () => e.openStrategyCreation()
39
+ }
36
40
  }), "";
37
41
  }
38
42
  try {
39
43
  await e.fetchStrategyProductsForBlock(t);
40
- } catch (n) {
41
- console.error("[useLegacyStrategyConversion] product refetch failed", n);
44
+ } catch (i) {
45
+ console.error("[useLegacyStrategyConversion] product refetch failed", i);
42
46
  }
43
- const a = e.blockStates[t];
44
- return a ? h(
47
+ const n = e.blockStates[t];
48
+ return n ? T(
45
49
  t,
46
50
  {
47
51
  strategyId: o,
48
- size: a.recommendationConfigs.size,
52
+ size: n.recommendationConfigs.size,
49
53
  configVersion: L
50
54
  },
51
- a.recommendationProducts,
55
+ n.recommendationProducts,
52
56
  "Converted legacy recommendation strategy"
53
- ) ? (e.currentRecommendationId === t && e.openLegacyStrategyUpdatedModal(), o) : (console.error("[useLegacyStrategyConversion] node write failed", t), e.revertLegacyConversion(t, f), i({
57
+ ) ? (e.currentRecommendationId === t && e.openLegacyStrategyUpdatedModal(), o) : (console.error("[useLegacyStrategyConversion] node write failed", t), e.revertLegacyConversion(t, f), c({
54
58
  type: g.Error,
55
- message: c("email-editor.legacy-strategy-update-failed")
59
+ message: a("email-editor.legacy-strategy-update-failed")
56
60
  }), "") : o;
57
61
  } };
58
62
  };
@@ -1,55 +1,102 @@
1
- import { useActionsApi as T } from "./useActionsApi.js";
1
+ import { useActionsApi as R } from "./useActionsApi.js";
2
2
  import { useConfig as q } from "./useConfig.js";
3
- import { useEditorLock as C } from "./useEditorLock.js";
4
- import { useSaveStart as L, useSaveComplete as M } from "./useGuidoActions.js";
5
- import { useSyncModuleExtractor as D } from "./useSyncModuleExtractor.js";
6
- import { ToasterTypeOptions as P } from "../enums/toaster.js";
7
- import { useStripoApi as U } from "../services/stripoApi.js";
8
- import { useTemplatePreparation as A } from "../utils/templatePreparation.js";
9
- import { useAllowlist as h } from "./useAllowlist.js";
10
- import { useHtmlValidator as F } from "./useHtmlValidator.js";
11
- import { useToaster as O } from "./useToaster.js";
12
- import { useCouponBlockValidator as W } from "./validators/useCouponBlockValidator.js";
13
- import { useLiquidValidator as j } from "./validators/useLiquidValidator.js";
14
- import { useSyncModuleBlockValidator as z } from "./validators/useSyncModuleBlockValidator.js";
15
- import { useUnsubscribeBlockValidator as G } from "./validators/useUnsubscribeBlockValidator.js";
16
- const rt = () => {
17
- const n = L(), c = M(), { validateHtml: d } = F(), { validateLiquidSyntax: m } = j(), { validateCouponBlockTags: u } = W(), { validateUnsubscribeBlockUniqueness: p, validateUnsubscribeBlockHasTemplate: f } = G(), { validateSyncModuleBlocks: v } = z(), { callbacks: s, isFeatureEnabled: r } = q(), { extractSyncModuleData: S } = D(), { setSyncModuleUnsubscriptionPages: b } = U(), { editorSave: w } = T(), { showToaster: V } = O(), { getBlockedDomains: y, resolveToast: k } = h(), { runExclusive: x } = C(), H = async (a, o) => {
18
- var l;
19
- n();
20
- const { prepareTemplateDetails: B } = A(), t = await B();
21
- if (!v(t.rawHtml, o) || !u(t.compiledHtml) || !p(t.compiledHtml) || !f(t.compiledHtml))
22
- return;
23
- if (r("liquidSyntax")) {
24
- if (!await m(t.compiledHtml))
25
- return;
26
- } else if (!await d(t.compiledHtml, t.dynamicContentList, !0))
27
- return;
28
- if ((l = s.value) != null && l.externalValidation && !await s.value.externalValidation(t))
3
+ import { useEditorLock as O } from "./useEditorLock.js";
4
+ import { useSaveStart as D, useSaveComplete as F } from "./useGuidoActions.js";
5
+ import { useSaveValidation as j } from "./useSaveValidation.js";
6
+ import { useSaveValidationRollout as z } from "./useSaveValidationRollout.js";
7
+ import { useSyncModuleExtractor as G } from "./useSyncModuleExtractor.js";
8
+ import { ToasterTypeOptions as I } from "../enums/toaster.js";
9
+ import { useStripoApi as J } from "../services/stripoApi.js";
10
+ import { useConfigStore as K } from "../stores/config.js";
11
+ import { useDynamicContentStore as N } from "../stores/dynamic-content.js";
12
+ import { useSaveValidationStore as Q } from "../stores/save-validation.js";
13
+ import { useTemplatePreparation as X, getHtmlReferencedUnsubscribePages as Y } from "../utils/templatePreparation.js";
14
+ import { useAllowlist as Z } from "./useAllowlist.js";
15
+ import { useHtmlValidator as _ } from "./useHtmlValidator.js";
16
+ import { useToaster as $ } from "./useToaster.js";
17
+ import { useCouponBlockValidator as tt } from "./validators/useCouponBlockValidator.js";
18
+ import { useLiquidValidator as et } from "./validators/useLiquidValidator.js";
19
+ import { useSyncModuleBlockValidator as ot } from "./validators/useSyncModuleBlockValidator.js";
20
+ import { useUnsubscribeBlockValidator as st } from "./validators/useUnsubscribeBlockValidator.js";
21
+ const Bt = () => {
22
+ const r = D(), p = F(), { validateHtml: v } = _(), { validateLiquidSyntax: S } = et(), { validateCouponBlockTags: y } = tt(), { validateUnsubscribeBlockUniqueness: w, validateUnsubscribeBlockHasTemplate: E } = st(), { validateSyncModuleBlocks: n } = ot(), { callbacks: a, isFeatureEnabled: m } = q(), { extractSyncModuleData: b } = G(), { setSyncModuleUnsubscriptionPages: g } = J(), { editorSave: H, getTemplateData: V } = R(), { showToaster: x } = $(), { getBlockedDomains: k, resolveToast: C } = Z(), { prepareTemplateDetails: d } = X(), { collectSaveValidationErrors: B } = j(), { isEnabled: h } = z(), T = Q(), P = N(), f = K(), { runExclusive: l } = O(), c = async (t, e, o) => {
23
+ if (!m("allowlistEnabled"))
24
+ return !1;
25
+ const i = await k(t, e);
26
+ if (!i.length)
27
+ return !1;
28
+ if (!o) {
29
+ const s = await C(i);
30
+ s != null && s.message && x({
31
+ type: I.Warning,
32
+ message: s.message,
33
+ actionButton: s.actionButton
34
+ });
35
+ }
36
+ return !0;
37
+ }, L = async (t) => !y(t.compiledHtml) || !w(t.compiledHtml) || !E(t.compiledHtml) ? !1 : m("liquidSyntax") ? S(t.compiledHtml) : v(t.compiledHtml, t.dynamicContentList, !0), u = async (t, e) => {
38
+ if (!await H())
29
39
  return;
30
- if (r("allowlistEnabled")) {
31
- const i = await y(t.compiledHtml, t.css);
32
- if (i.length) {
33
- if (!o) {
34
- const e = await k(i);
35
- e != null && e.message && V({
36
- type: P.Warning,
37
- message: e.message,
38
- actionButton: e.actionButton
39
- });
40
+ const { unsubscribePayload: i, stripoModules: s } = b(t.rawHtml);
41
+ if (await g(i), t.modules = s, !e.suppress) {
42
+ const W = e.blankCompiled ? { compiledHtml: "", ampHtml: "" } : {};
43
+ p({ ...t, ...W, silent: e.silent });
44
+ }
45
+ return t;
46
+ }, U = async (t, e) => {
47
+ var i;
48
+ r();
49
+ const o = await d();
50
+ if (n(o.rawHtml, e)) {
51
+ if (h.value && !e) {
52
+ const s = await B(
53
+ o.compiledHtml,
54
+ o.dynamicContentList
55
+ );
56
+ if (s.length) {
57
+ T.open(s, "save");
58
+ return;
40
59
  }
60
+ } else if (!await L(o))
41
61
  return;
42
- }
62
+ if (!((i = a.value) != null && i.externalValidation && !await a.value.externalValidation(o)) && !await c(o.compiledHtml, o.css, e))
63
+ return u(o, { silent: e, suppress: t });
43
64
  }
44
- if (!await w())
65
+ }, A = async () => {
66
+ var e;
67
+ if (!f.allowSaveWithErrors)
68
+ return;
69
+ r();
70
+ const t = await d();
71
+ if (n(t.rawHtml, !1) && !((e = a.value) != null && e.externalValidation && !await a.value.externalValidation(t)) && !await c(t.compiledHtml, t.css, !1))
72
+ return u(t, { silent: !1, blankCompiled: !0 });
73
+ }, M = async () => {
74
+ if (!f.allowSaveWithErrors)
45
75
  return;
46
- const { unsubscribePayload: E, stripoModules: g } = S(t.rawHtml);
47
- return await b(E), t.modules = g, a || c({ ...t, silent: o }), t;
76
+ r();
77
+ const { html: t, css: e } = await V();
78
+ if (!n(t, !0) || await c(t, e, !0))
79
+ return;
80
+ const o = Y(t), i = {
81
+ dynamicContentList: P.getSelectedDynamicContentList,
82
+ compiledHtml: "",
83
+ rawHtml: t,
84
+ css: e,
85
+ ampHtml: "",
86
+ ampErrors: [],
87
+ modules: [],
88
+ recommendation: { campaignUrls: {}, configs: {} },
89
+ unsubscribe: {
90
+ status: o.length > 0,
91
+ config: o
92
+ }
93
+ };
94
+ return u(i, { silent: !0 });
48
95
  };
49
- return { save: (a = !1, o = !1) => x(
50
- () => H(a, o)
51
- ) };
96
+ return { save: (t = !1, e = !1) => l(
97
+ () => U(t, e)
98
+ ), saveWithErrors: () => l(A), saveRawOnly: () => l(M) };
52
99
  };
53
100
  export {
54
- rt as useSave
101
+ Bt as useSave
55
102
  };
@@ -0,0 +1,284 @@
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 M } from "../extensions/Blocks/Checkbox/constants/selectors.js";
6
+ import { itemsBlockDynamicVariables as U } 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 { getInvalidFields as G, STRATEGY_FIELD_KEY as q, RecommendationAssignStrategyKey as H, RecommendationRequiredFieldsKey as V } from "../extensions/Blocks/Recommendation/validation/requiredFields.js";
10
+ import { useConfigStore as X } from "../stores/config.js";
11
+ import { useDynamicContentStore as j } from "../stores/dynamic-content.js";
12
+ import { useRecommendationStore as z } from "../stores/recommendation.js";
13
+ import { base64EncodeWithSpecialChars as T } from "../utils/base64.js";
14
+ import { escapeHtml as $ } from "../utils/htmlEscape.js";
15
+ import { useTemplatePreparation as Q } from "../utils/templatePreparation.js";
16
+ import { useActionsApi as Z } from "./useActionsApi.js";
17
+ import { useConfig as J } from "./useConfig.js";
18
+ import { useHtmlCompiler as ee } from "./useHtmlCompiler.js";
19
+ import { buildPartnerAttributeNameSet as te, getInvalidDynamicContentTags as ne, getRecommendationBlockIds as ie, findInvalidCustomFields as oe } from "./useHtmlValidator.js";
20
+ import { useHttp as re } from "./useHttp.js";
21
+ import { useTranslations as se } from "./useTranslations.js";
22
+ import { findInvalidCouponBlocks as ae, COUPON_TAG_MODIFIED_KEY as ce } from "./validators/useCouponBlockValidator.js";
23
+ import { findDuplicateUnsubPageTypes as le, countUnsubBlocksWithoutPage as me, EMPTY_BLOCK_KEY as ue } from "./validators/useUnsubscribeBlockValidator.js";
24
+ const de = "newsletter.html-content-is-empty", pe = "custom-fields.missing-closing-braces", ge = "custom-fields.missing-opening-braces", I = "custom-fields.invalid-custom-fields", fe = "newsletter.display-conditions-invalid-syntax", ye = "custom-conditions.wrong-equality-operators", Ee = "custom-conditions.missing-if-endif-tag", Ie = "custom-conditions.no-space-after-braces", Se = "custom-conditions.no-braces-inside-if-tag", _e = "newsletter.fill-all-necessary-fields", Ce = "newsletter.invalid-image-type", he = "unsubscribe-templates.select-checkbox-groups", ke = "unsubscribe-templates.select-radio-button-groups", Te = "newsletter.invalid-url-link-for-toaster", F = "journey-builder.liquid-validation-failed", Fe = "/newsletter/template-library/check-template-html-body", Re = /src="[^"]*\.(svg|pst)"/gm, O = /({%(.*?)%})/g, Oe = ["if", "endif", "else", "elif", "now"], Le = (t) => !Oe.includes(t.toLowerCase()), Ne = (t) => ["if", "endif"].includes(t.toLowerCase());
25
+ function be(t) {
26
+ return ae(t).map((i) => ({
27
+ rule: o.CouponTagModified,
28
+ type: r.Warning,
29
+ key: ce,
30
+ detail: i
31
+ }));
32
+ }
33
+ function Ae(t) {
34
+ return le(t).map((i) => ({
35
+ rule: o.DuplicateUnsubBlock,
36
+ type: r.Error,
37
+ key: i.messageKey
38
+ }));
39
+ }
40
+ function ve(t) {
41
+ return Array.from({ length: me(t) }, () => ({
42
+ rule: o.UnsubBlockMissingTemplate,
43
+ type: r.Warning,
44
+ key: ue
45
+ }));
46
+ }
47
+ function De(t, i) {
48
+ const n = t.match(O);
49
+ if (!n)
50
+ return [];
51
+ const l = [];
52
+ return n.forEach((u) => {
53
+ const c = u.slice(2, -2).trim().match(/(".*?"|[^"\s]+)(?=\s*|\s*$)/g);
54
+ if (c && c.length > 0) {
55
+ const [d] = c;
56
+ Le(d) && !i.includes(d) && l.push({
57
+ rule: o.CustomFieldScript,
58
+ type: r.Warning,
59
+ key: I,
60
+ detail: d
61
+ });
62
+ }
63
+ }), l;
64
+ }
65
+ function Ke(t) {
66
+ return t.length > 0 ? [] : [{ rule: o.EmptyHtml, type: r.Warning, key: de }];
67
+ }
68
+ function we(t) {
69
+ const i = (t.match(/{/gm) || []).length, n = (t.match(/}/gm) || []).length;
70
+ return i > n ? [{
71
+ rule: o.MismatchedBraces,
72
+ type: r.Warning,
73
+ key: pe
74
+ }] : i < n ? [{
75
+ rule: o.MismatchedBraces,
76
+ type: r.Warning,
77
+ key: ge
78
+ }] : [];
79
+ }
80
+ function Be(t) {
81
+ return ne(t).map((i) => ({
82
+ rule: o.InvalidDynamicTag,
83
+ type: r.Warning,
84
+ key: I,
85
+ detail: i
86
+ }));
87
+ }
88
+ function Ye(t, i, n, l) {
89
+ const u = ie(t);
90
+ return oe(t, i, u, n, l).map((c) => ({
91
+ rule: o.InvalidCustomField,
92
+ type: r.Error,
93
+ key: I,
94
+ detail: c
95
+ }));
96
+ }
97
+ function xe(t) {
98
+ const i = t.match(/{%(.*?)%}/g), n = [], l = [];
99
+ if (i && i.forEach((u) => {
100
+ const c = u.match(Y), d = u.match(x), E = (c == null ? void 0 : c.join("")) || "";
101
+ (!c || u !== E) && !d && l.push({
102
+ rule: o.ConditionsSyntax,
103
+ type: r.Error,
104
+ key: fe,
105
+ detail: u
106
+ }), c && c.forEach((g) => {
107
+ g.trim() === "=" && l.push({
108
+ rule: o.WrongEqualityOperator,
109
+ type: r.Error,
110
+ key: ye
111
+ }), (g.match(/^[a-zA-Z]*$/g) ?? []).forEach((f) => {
112
+ Ne(f) && n.push(f);
113
+ });
114
+ });
115
+ }), n.length) {
116
+ const u = n.filter((d) => d === "if").length, c = n.filter((d) => d === "endif").length;
117
+ u !== c && l.push({
118
+ rule: o.MissingIfEndif,
119
+ type: r.Error,
120
+ key: Ee
121
+ });
122
+ }
123
+ return l;
124
+ }
125
+ function Me(t) {
126
+ const i = (t.match(/{% /gm) || []).length, n = (t.match(/ %}/gm) || []).length;
127
+ return i === n ? [] : [{
128
+ rule: o.ConditionTagSpacing,
129
+ type: r.Warning,
130
+ key: Ie
131
+ }];
132
+ }
133
+ function Ue(t) {
134
+ return (t.match(O) || []).filter((n) => n.includes("if")).map((n) => (n.match(/{{.*}}/gm) || []).length).reduce((n, l) => n + l, 0) === 0 ? [] : [{
135
+ rule: o.BracesInsideIfTag,
136
+ type: r.Warning,
137
+ key: Se
138
+ }];
139
+ }
140
+ function Pe(t) {
141
+ return (t.match(Re) ?? []).map((i) => ({
142
+ rule: o.InvalidImageType,
143
+ type: r.Error,
144
+ key: Ce,
145
+ detail: i
146
+ }));
147
+ }
148
+ function R(t, i, n, l) {
149
+ const u = new DOMParser().parseFromString(t, "text/html");
150
+ return Array.from(u.querySelectorAll(i)).filter((c) => {
151
+ var d;
152
+ return !((d = c.id) != null && d.trim());
153
+ }).map(() => ({ rule: n, type: r.Error, key: l }));
154
+ }
155
+ const dt = () => {
156
+ const { config: t, isFeatureEnabled: i } = J(), n = se(), { post: l } = re(), { getCompiledEmail: u, getTemplateData: c } = Z(), { compileHtml: d } = ee(), { hydrateRecommendationStore: E } = Q(), g = j(), f = z(), y = W(), L = X(), N = (e) => ({
157
+ rule: e.rule,
158
+ type: e.type,
159
+ detail: e.detail,
160
+ message: "key" in e ? n(e.key) : $(e.message)
161
+ }), b = async (e) => {
162
+ if (e.size === 0)
163
+ return /* @__PURE__ */ new Set();
164
+ try {
165
+ await y.fetchRecommendationFilters();
166
+ } catch {
167
+ return null;
168
+ }
169
+ return te(y.filterList);
170
+ }, A = async (e) => {
171
+ try {
172
+ const s = await l(Fe, { html: T(e) }), { status: a, message: m } = s.data, p = [];
173
+ return a || p.push({
174
+ rule: o.InvalidLinks,
175
+ type: r.Error,
176
+ // status===undefined means a bare message instead of a boolean; surface it,
177
+ // but fall back to the canned key when it's missing so a malformed body
178
+ // never renders a literal 'undefined' row (SD-147430).
179
+ ...a === void 0 && m ? { message: m } : { key: Te }
180
+ }), m === n(k) && p.push({
181
+ rule: o.LinkCheckInProgress,
182
+ type: r.Error,
183
+ key: k
184
+ }), p;
185
+ } catch (s) {
186
+ return console.warn("Link-check collector failed; skipping the link rule.", s), [];
187
+ }
188
+ }, v = async (e) => {
189
+ try {
190
+ const s = await l("/newsletter/contents/validate-syntax", [{
191
+ identifier: "default",
192
+ syntax: "liquid",
193
+ contents: {
194
+ subject: null,
195
+ preheader: null,
196
+ html: T(e),
197
+ ampHtml: null
198
+ }
199
+ }]);
200
+ if (!Array.isArray(s.data)) {
201
+ const m = s.data;
202
+ return [{
203
+ rule: o.LiquidSyntax,
204
+ type: r.Warning,
205
+ message: m.message ?? n(F)
206
+ }];
207
+ }
208
+ const [a] = s.data;
209
+ return Object.values((a == null ? void 0 : a.errors) ?? {}).map((m) => ({
210
+ rule: o.LiquidSyntax,
211
+ type: r.Warning,
212
+ message: m.replace(/^line \d+:\s*/, "")
213
+ }));
214
+ } catch (s) {
215
+ if (s != null && s.authReported)
216
+ throw s;
217
+ return [{
218
+ rule: o.LiquidSyntax,
219
+ type: r.Error,
220
+ message: n(F)
221
+ }];
222
+ }
223
+ }, D = () => {
224
+ const e = Object.values(y.blockStates).map((a) => G(a.recommendationConfigs, y)).filter((a) => a.length > 0).map((a) => {
225
+ const m = a.every((p) => p === q);
226
+ return {
227
+ rule: o.RecommendationRequiredFields,
228
+ type: m ? r.Warning : r.Error,
229
+ key: m ? H : V
230
+ };
231
+ }), s = (f.recommendationConfigs ?? []).filter((a) => a.filters.some((m) => m.value === "")).map(() => ({
232
+ rule: o.RecommendationFilters,
233
+ type: r.Error,
234
+ key: _e
235
+ }));
236
+ return [...e, ...s];
237
+ }, K = async (e, s) => {
238
+ var C, h;
239
+ const a = [
240
+ ...s.map((w) => w.value),
241
+ ...U,
242
+ ...((h = (C = t.value) == null ? void 0 : C.template) == null ? void 0 : h.customFieldAttributes) ?? []
243
+ ], m = B(e), p = await b(m), { isTransactional: _ } = L;
244
+ return [
245
+ ...await A(e),
246
+ ..._ ? [] : De(e, a),
247
+ ...Ke(e),
248
+ ...we(e),
249
+ ...Be(e),
250
+ ..._ ? [] : Ye(e, a, m, p),
251
+ ...xe(e),
252
+ ...Me(e),
253
+ ...Ue(e),
254
+ ...D(),
255
+ ...Pe(e),
256
+ ...R(
257
+ e,
258
+ M,
259
+ o.CheckboxUnsubGroup,
260
+ he
261
+ ),
262
+ ...R(
263
+ e,
264
+ P,
265
+ o.RadioButtonUnsubGroup,
266
+ ke
267
+ )
268
+ ];
269
+ }, S = async (e, s) => [
270
+ ...be(e),
271
+ ...Ae(e),
272
+ ...ve(e),
273
+ ...i("liquidSyntax") ? await v(e) : await K(e, s)
274
+ ].map(N);
275
+ return { collectSaveValidationErrors: S, validateCurrentTemplate: async () => {
276
+ const { html: e } = await c();
277
+ await E(e);
278
+ const { html: s } = await u({ minimize: !0, resetDataSavedFlag: !1 }), { compiledHtml: a } = d(s);
279
+ return S(a, g.getSelectedDynamicContentList);
280
+ } };
281
+ };
282
+ export {
283
+ dt as useSaveValidation
284
+ };