@useinsider/guido 3.12.0-beta.b73c720 → 3.12.0-beta.bec0e55

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 (80) hide show
  1. package/dist/@types/config/schemas.js +8 -13
  2. package/dist/components/Guido.vue.js +6 -6
  3. package/dist/components/Guido.vue2.js +49 -50
  4. package/dist/components/organisms/email-preview/desktop-preview/DesktopPreview.vue.js +1 -1
  5. package/dist/components/organisms/email-preview/desktop-preview/DesktopPreview.vue2.js +8 -8
  6. package/dist/components/organisms/email-preview/mobile-preview/ContentView.vue.js +5 -5
  7. package/dist/components/organisms/email-preview/mobile-preview/ContentView.vue2.js +10 -10
  8. package/dist/composables/usePreviewInteractionGuard.js +36 -11
  9. package/dist/composables/useRecommendation.js +51 -82
  10. package/dist/composables/useRecommendationPreview.js +61 -60
  11. package/dist/config/migrator/recommendationMigrator.js +4 -7
  12. package/dist/enums/date.js +3 -4
  13. package/dist/enums/extensions/recommendationBlock.js +22 -28
  14. package/dist/extensions/Blocks/Recommendation/constants/controlIds.js +1 -1
  15. package/dist/extensions/Blocks/Recommendation/constants/defaultConfig.js +11 -12
  16. package/dist/extensions/Blocks/Recommendation/controls/blockBackground/index.js +10 -0
  17. package/dist/extensions/Blocks/Recommendation/controls/main/index.js +109 -123
  18. package/dist/extensions/Blocks/Recommendation/extension.js +23 -21
  19. package/dist/extensions/Blocks/Recommendation/iconsRegistry.js +1 -1
  20. package/dist/extensions/Blocks/Recommendation/settingsPanel.js +41 -40
  21. package/dist/extensions/Blocks/Recommendation/store/recommendation.js +102 -256
  22. package/dist/extensions/Blocks/Recommendation/validation/requiredFields.js +12 -24
  23. package/dist/extensions/Blocks/Unsubscribe/utils/constants.js +3 -2
  24. package/dist/extensions/Blocks/controlFactories.js +125 -75
  25. package/dist/guido.css +1 -1
  26. package/dist/services/recommendationApi.js +27 -65
  27. package/dist/src/@types/config/schemas.d.ts +0 -10
  28. package/dist/src/composables/useConfig.d.ts +0 -2
  29. package/dist/src/composables/usePreviewInteractionGuard.d.ts +1 -1
  30. package/dist/src/composables/useRecommendation.d.ts +0 -2
  31. package/dist/src/enums/extensions/recommendationBlock.d.ts +0 -10
  32. package/dist/src/extensions/Blocks/Recommendation/constants/controlIds.d.ts +1 -0
  33. package/dist/src/extensions/Blocks/Recommendation/controls/blockBackground/index.d.ts +14 -0
  34. package/dist/src/extensions/Blocks/Recommendation/controls/index.d.ts +1 -0
  35. package/dist/src/extensions/Blocks/Recommendation/controls/main/index.d.ts +1 -3
  36. package/dist/src/extensions/Blocks/Recommendation/store/recommendation.d.ts +1 -606
  37. package/dist/src/extensions/Blocks/Recommendation/types/nodeConfig.d.ts +0 -7
  38. package/dist/src/extensions/Blocks/Recommendation/validation/requiredFields.d.ts +1 -3
  39. package/dist/src/extensions/Blocks/Unsubscribe/utils/constants.d.ts +1 -0
  40. package/dist/src/extensions/Blocks/controlFactories.d.ts +36 -0
  41. package/dist/src/services/recommendationApi.d.ts +1 -7
  42. package/dist/src/stores/config.d.ts +0 -18
  43. package/dist/src/utils/genericUtil.d.ts +0 -9
  44. package/dist/src/utils/urlSchemes.d.ts +6 -0
  45. package/dist/static/styles/base.css.js +0 -15
  46. package/dist/utils/dateUtil.js +10 -23
  47. package/dist/utils/genericUtil.js +1 -10
  48. package/dist/utils/templatePreparation.js +58 -62
  49. package/dist/utils/urlSchemes.js +4 -0
  50. package/package.json +1 -1
  51. package/dist/components/organisms/extensions/recommendation/StrategyDetailDrawer.vue.js +0 -36
  52. package/dist/components/organisms/extensions/recommendation/StrategyDetailDrawer.vue2.js +0 -244
  53. package/dist/components/organisms/extensions/recommendation/strategy-detail/CampaignTable.vue.js +0 -18
  54. package/dist/components/organisms/extensions/recommendation/strategy-detail/CampaignTable.vue2.js +0 -42
  55. package/dist/components/organisms/extensions/recommendation/strategy-detail/FilterHumanizer.vue.js +0 -20
  56. package/dist/components/organisms/extensions/recommendation/strategy-detail/FilterHumanizer.vue2.js +0 -18
  57. package/dist/components/organisms/extensions/recommendation/strategy-detail/HumanizerCondition.vue.js +0 -20
  58. package/dist/components/organisms/extensions/recommendation/strategy-detail/HumanizerCondition.vue2.js +0 -60
  59. package/dist/components/organisms/extensions/recommendation/strategy-detail/HumanizerGroup.vue.js +0 -19
  60. package/dist/components/organisms/extensions/recommendation/strategy-detail/HumanizerGroup.vue2.js +0 -19
  61. package/dist/components/organisms/extensions/recommendation/strategy-detail/InfoDisplayValue.vue.js +0 -22
  62. package/dist/components/organisms/extensions/recommendation/strategy-detail/InfoDisplayValue.vue2.js +0 -29
  63. package/dist/composables/useStrategyFilters.js +0 -16
  64. package/dist/enums/extensions/filteringV2.js +0 -8
  65. package/dist/enums/extensions/strategyDetail.js +0 -148
  66. package/dist/extensions/Blocks/Recommendation/controls/main/strategy.js +0 -146
  67. package/dist/extensions/Blocks/Recommendation/utils/strategyHumanizer.js +0 -97
  68. package/dist/extensions/Blocks/Recommendation/utils/strategySummary.js +0 -10
  69. package/dist/src/components/organisms/extensions/recommendation/StrategyDetailDrawer.vue.d.ts +0 -2
  70. package/dist/src/components/organisms/extensions/recommendation/strategy-detail/CampaignTable.vue.d.ts +0 -43
  71. package/dist/src/components/organisms/extensions/recommendation/strategy-detail/FilterHumanizer.vue.d.ts +0 -29
  72. package/dist/src/components/organisms/extensions/recommendation/strategy-detail/HumanizerCondition.vue.d.ts +0 -40
  73. package/dist/src/components/organisms/extensions/recommendation/strategy-detail/HumanizerGroup.vue.d.ts +0 -38
  74. package/dist/src/components/organisms/extensions/recommendation/strategy-detail/InfoDisplayValue.vue.d.ts +0 -51
  75. package/dist/src/composables/useStrategyFilters.d.ts +0 -24
  76. package/dist/src/enums/extensions/filteringV2.d.ts +0 -72
  77. package/dist/src/enums/extensions/strategyDetail.d.ts +0 -90
  78. package/dist/src/extensions/Blocks/Recommendation/controls/main/strategy.d.ts +0 -61
  79. package/dist/src/extensions/Blocks/Recommendation/utils/strategyHumanizer.d.ts +0 -50
  80. package/dist/src/extensions/Blocks/Recommendation/utils/strategySummary.d.ts +0 -34
@@ -54,13 +54,6 @@ export interface RecommendationNodeConfig {
54
54
  * @example 'mostPopular', 'complementaryItems', 'manualMerchandising'
55
55
  */
56
56
  strategy: string;
57
- /**
58
- * Reusable Recommendation Strategy (RRS) id from Smart Recommender.
59
- * Empty string means no strategy is assigned.
60
- * Only used when the `reusableRecommendationStrategy` feature is enabled;
61
- * when set, it supersedes `strategy` for building the recommendation URL.
62
- */
63
- strategyId: string;
64
57
  /**
65
58
  * Product IDs for manual merchandising strategy
66
59
  * Only used when strategy === 'manualMerchandising'
@@ -7,15 +7,13 @@ import type { Currency, Languages } from '@@/Types/recommendation';
7
7
  export interface ExtensionStoreSlice {
8
8
  languages: Languages;
9
9
  currencyList: Currency[];
10
- /** Whether the `reusableRecommendationStrategy` feature is enabled. */
11
- isRrsEnabled: boolean;
12
10
  }
13
11
  export interface RequiredField {
14
12
  key: string;
15
13
  canBeEmpty?: boolean;
16
14
  getValue: (config: PerBlockConfigs) => string;
17
15
  getAvailableOptions?: (store: ExtensionStoreSlice) => string[];
18
- condition?: (config: PerBlockConfigs, store: ExtensionStoreSlice) => boolean;
16
+ condition?: (config: PerBlockConfigs) => boolean;
19
17
  }
20
18
  export declare const REQUIRED_RECOMMENDATION_FIELDS: RequiredField[];
21
19
  export declare const RecommendationRequiredFieldsKey = "newsletter.recommendation-fill-required-fields";
@@ -9,3 +9,4 @@ export declare const DATA_ATTRIBUTES: {
9
9
  readonly BLOCK_ID: "data-unsubscribe-block-id";
10
10
  };
11
11
  export declare const UNSUBSCRIBE_BLOCK_SELECTOR = ".unsubscribe-block-v2";
12
+ export declare const UNSUBSCRIBE_LINK_SELECTOR = "a[data-unsubscribe-link=\"true\"], a.unsubscribe-link";
@@ -115,6 +115,42 @@ export declare function createTextBackgroundColorControl(controlId: string, targ
115
115
  isVisible(_node: ImmutableHtmlNode): boolean;
116
116
  };
117
117
  };
118
+ /**
119
+ * Factory for a background-color control that paints a whole extension block (SD-145082).
120
+ *
121
+ * Base is `ButtonBackgroundColorBuiltInControl` because its `GENERAL.BACKGROUND_COLOR` form key
122
+ * round-trips through `getValues()`; the block-text base (`TextBlockBackgroundBuiltInControl`, the
123
+ * built-in this replaces) reports `textBlockBackgroundColor`, which our read path never sees. That
124
+ * borrowed base targets the block's Buy buttons, so `getTargetNodes` here is a GUARD that keeps it
125
+ * off the buttons — not the write path.
126
+ *
127
+ * The write is entirely ours (`getAdditionalModifications`): Stripo's built-in emits a stylesheet
128
+ * rule that inline styles from regeneration outrank (SD-143023), so it would silently no-op on the
129
+ * next edit. We write inline `background-color` + `bgcolor` on the block `<td>` instead — surviving
130
+ * regeneration and reaching Outlook, matching the migrator's convention.
131
+ */
132
+ export declare function createBlockBackgroundColorControl(controlId: string, blockRootSelector?: string): {
133
+ new (): {
134
+ getId(): string;
135
+ /** The parent control renders its own generic title; restore the block-level wording. */
136
+ getLabels(): {
137
+ title: string;
138
+ };
139
+ /** Guard: redirect the borrowed button parent onto the block `<td>` so it never paints the Buy buttons. */
140
+ getTargetNodes(root: ImmutableHtmlNode): ImmutableHtmlNode[];
141
+ /**
142
+ * Writes the picked background onto the block as an inline style plus a `bgcolor`
143
+ * attribute. This is the whole mechanism, not a mirror: the parent control's own
144
+ * write never reaches the block element (verified at runtime). `bgcolor` is the
145
+ * channel Outlook honours on a `<td>`, and matches what the migrator emits.
146
+ */
147
+ getAdditionalModifications(root: ImmutableHtmlNode): import("@stripoinc/ui-editor-extensions").TemplateModifier<import("@stripoinc/ui-editor-extensions").HtmlNodeModifier, import("@stripoinc/ui-editor-extensions").CssNodeModifier> | undefined;
148
+ getParentControlId(): string;
149
+ api: import("@stripoinc/ui-editor-extensions").ControlApi;
150
+ getModificationDescription(): import("@stripoinc/ui-editor-extensions").ModificationDescription | undefined;
151
+ isVisible(_node: ImmutableHtmlNode): boolean;
152
+ };
153
+ };
118
154
  /**
119
155
  * Factory function to create paddings controls for different block elements
120
156
  * When targetBlockId and containerSelector are not provided, applies directly to root
@@ -1,12 +1,6 @@
1
- import type { InitialResponse, FiltersResponse, RecommendationProduct, RecommendationProductsParams, RrsCampaign, RrsStrategy, RrsStrategyUrl } from '@@/Types/recommendation';
2
- /** Translation lookup, matching `useTranslations()`. */
3
- type TranslateFn = (key: string, params?: Record<string, string | number | boolean>) => string;
1
+ import type { InitialResponse, FiltersResponse, RecommendationProduct, RecommendationProductsParams } from '@@/Types/recommendation';
4
2
  export declare const useRecommendationApi: () => {
5
3
  fetchRecommendationCreateData: () => Promise<InitialResponse>;
6
4
  fetchRecommendationFilters: () => Promise<FiltersResponse>;
7
5
  fetchRecommendationProducts: (strategy: string, params: RecommendationProductsParams) => Promise<RecommendationProduct[]>;
8
- fetchRecommendationStrategies: () => Promise<RrsStrategy[]>;
9
- fetchStrategyCampaigns: (strategyId: string, trans: TranslateFn) => Promise<RrsCampaign[]>;
10
- fetchStrategyRecommendationUrl: (strategyId: string) => Promise<RrsStrategyUrl | null>;
11
6
  };
12
- export {};
@@ -101,7 +101,6 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
101
101
  modulesDisabled: boolean;
102
102
  liquidSyntax: boolean;
103
103
  autosave: boolean;
104
- reusableRecommendationStrategy: boolean;
105
104
  };
106
105
  blocks: {
107
106
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -239,7 +238,6 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
239
238
  modulesDisabled: boolean;
240
239
  liquidSyntax: boolean;
241
240
  autosave: boolean;
242
- reusableRecommendationStrategy: boolean;
243
241
  };
244
242
  blocks: {
245
243
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -377,7 +375,6 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
377
375
  modulesDisabled: boolean;
378
376
  liquidSyntax: boolean;
379
377
  autosave: boolean;
380
- reusableRecommendationStrategy: boolean;
381
378
  };
382
379
  blocks: {
383
380
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -515,7 +512,6 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
515
512
  modulesDisabled: boolean;
516
513
  liquidSyntax: boolean;
517
514
  autosave: boolean;
518
- reusableRecommendationStrategy: boolean;
519
515
  };
520
516
  blocks: {
521
517
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -653,7 +649,6 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
653
649
  modulesDisabled: boolean;
654
650
  liquidSyntax: boolean;
655
651
  autosave: boolean;
656
- reusableRecommendationStrategy: boolean;
657
652
  };
658
653
  blocks: {
659
654
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -791,7 +786,6 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
791
786
  modulesDisabled: boolean;
792
787
  liquidSyntax: boolean;
793
788
  autosave: boolean;
794
- reusableRecommendationStrategy: boolean;
795
789
  };
796
790
  blocks: {
797
791
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -929,7 +923,6 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
929
923
  modulesDisabled: boolean;
930
924
  liquidSyntax: boolean;
931
925
  autosave: boolean;
932
- reusableRecommendationStrategy: boolean;
933
926
  };
934
927
  blocks: {
935
928
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -1067,7 +1060,6 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1067
1060
  modulesDisabled: boolean;
1068
1061
  liquidSyntax: boolean;
1069
1062
  autosave: boolean;
1070
- reusableRecommendationStrategy: boolean;
1071
1063
  };
1072
1064
  blocks: {
1073
1065
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -1205,7 +1197,6 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1205
1197
  modulesDisabled: boolean;
1206
1198
  liquidSyntax: boolean;
1207
1199
  autosave: boolean;
1208
- reusableRecommendationStrategy: boolean;
1209
1200
  };
1210
1201
  blocks: {
1211
1202
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -1343,7 +1334,6 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1343
1334
  modulesDisabled: boolean;
1344
1335
  liquidSyntax: boolean;
1345
1336
  autosave: boolean;
1346
- reusableRecommendationStrategy: boolean;
1347
1337
  };
1348
1338
  blocks: {
1349
1339
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -1481,7 +1471,6 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1481
1471
  modulesDisabled: boolean;
1482
1472
  liquidSyntax: boolean;
1483
1473
  autosave: boolean;
1484
- reusableRecommendationStrategy: boolean;
1485
1474
  };
1486
1475
  blocks: {
1487
1476
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -1619,7 +1608,6 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1619
1608
  modulesDisabled: boolean;
1620
1609
  liquidSyntax: boolean;
1621
1610
  autosave: boolean;
1622
- reusableRecommendationStrategy: boolean;
1623
1611
  };
1624
1612
  blocks: {
1625
1613
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -1757,7 +1745,6 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1757
1745
  modulesDisabled: boolean;
1758
1746
  liquidSyntax: boolean;
1759
1747
  autosave: boolean;
1760
- reusableRecommendationStrategy: boolean;
1761
1748
  };
1762
1749
  blocks: {
1763
1750
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -1895,7 +1882,6 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
1895
1882
  modulesDisabled: boolean;
1896
1883
  liquidSyntax: boolean;
1897
1884
  autosave: boolean;
1898
- reusableRecommendationStrategy: boolean;
1899
1885
  };
1900
1886
  blocks: {
1901
1887
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -2033,7 +2019,6 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
2033
2019
  modulesDisabled: boolean;
2034
2020
  liquidSyntax: boolean;
2035
2021
  autosave: boolean;
2036
- reusableRecommendationStrategy: boolean;
2037
2022
  };
2038
2023
  blocks: {
2039
2024
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -2171,7 +2156,6 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
2171
2156
  modulesDisabled: boolean;
2172
2157
  liquidSyntax: boolean;
2173
2158
  autosave: boolean;
2174
- reusableRecommendationStrategy: boolean;
2175
2159
  };
2176
2160
  blocks: {
2177
2161
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -2309,7 +2293,6 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
2309
2293
  modulesDisabled: boolean;
2310
2294
  liquidSyntax: boolean;
2311
2295
  autosave: boolean;
2312
- reusableRecommendationStrategy: boolean;
2313
2296
  };
2314
2297
  blocks: {
2315
2298
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -2447,7 +2430,6 @@ export declare const useConfigStore: import("pinia").StoreDefinition<"guido-conf
2447
2430
  modulesDisabled: boolean;
2448
2431
  liquidSyntax: boolean;
2449
2432
  autosave: boolean;
2450
- reusableRecommendationStrategy: boolean;
2451
2433
  };
2452
2434
  blocks: {
2453
2435
  excludeDefaults: ("amp-accordion" | "amp-carousel" | "amp-form-controls" | "banner-block" | "button-block" | "html-block" | "image-block" | "menu-block" | "social-block" | "spacer-block" | "text-block" | "timer-block" | "video-block")[];
@@ -25,12 +25,3 @@ export declare const getCsrfToken: () => string;
25
25
  export declare const getUrlParams: () => URLSearchParams;
26
26
  export declare const getUrlParam: (paramName: string, defaultValue?: string) => string;
27
27
  export declare const replaceString: (string?: string, values?: Record<string, string | number>, isRemoveEmptyValues?: boolean) => string;
28
- /**
29
- * Parses a JSON-string field, yielding `fallback` on absent or malformed input.
30
- *
31
- * Smart Recommender returns several strategy fields (`filters`, `base_product`, ...)
32
- * as embedded JSON strings, and every surface that reads them must degrade to a
33
- * sensible empty value rather than break. Shared so a hardening change (rejecting
34
- * non-object payloads, logging a malformed blob) lands on all of them at once.
35
- */
36
- export declare const parseJsonField: <T>(raw: string | undefined, fallback: T) => T;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * URL schemes that are safe to follow from partner-authored email HTML. Shared so a
3
+ * future bypass fix lands in one place — the preview guard and the recommendation
4
+ * sanitizer both gate on it. Stored with the trailing colon to match `anchor.protocol`.
5
+ */
6
+ export declare const BASE_SAFE_URL_SCHEMES: readonly string[];
@@ -10,21 +10,6 @@ ue-stripe-thumb,
10
10
  height: 100% !important;
11
11
  min-height: 0 !important;
12
12
  }
13
-
14
- /*
15
- * The Recommendation Strategy select and its details-drawer icon button share a
16
- * single cell of Stripo's two-column panel grid, so they need their own row box.
17
- */
18
- .strategy-control__select-row {
19
- display: flex;
20
- align-items: center;
21
- gap: 8px;
22
- }
23
-
24
- .strategy-control__select-row ue-select {
25
- flex: 1;
26
- min-width: 0;
27
- }
28
13
  `;
29
14
  export {
30
15
  n as default
@@ -1,19 +1,10 @@
1
- import { DEFAULT_LOCALE as r, DEFAULT as a, DEFAULT_TZ as c } from "../enums/date.js";
2
- const m = (t) => t ? t instanceof Date ? t : typeof t == "string" ? t.length === 10 && /^\d{10}$/.test(t) ? new Date(Number(t) * 1e3) : new Date(t) : typeof t == "number" ? t.toString().length === 10 ? new Date(t * 1e3) : new Date(t) : /* @__PURE__ */ new Date() : /* @__PURE__ */ new Date(), s = () => {
1
+ import { DEFAULT_LOCALE as r, DEFAULT as o } from "../enums/date.js";
2
+ const c = () => {
3
3
  const t = window.l10n;
4
- return (t == null ? void 0 : t.locale) === a ? r : (t == null ? void 0 : t.locale) || r;
5
- }, u = new Intl.DateTimeFormat().resolvedOptions().timeZone, f = () => {
6
- const t = window.l10n;
7
- return (t == null ? void 0 : t.timezone) || u || c;
8
- }, D = (t, e = s(), n = f()) => {
9
- const o = {
10
- year: "numeric",
11
- month: "2-digit",
12
- day: "2-digit",
13
- timeZone: n
14
- }, i = m(t);
15
- return new Intl.DateTimeFormat(e, o).format(i);
16
- }, T = (t) => {
4
+ return (t == null ? void 0 : t.locale) === o ? r : (t == null ? void 0 : t.locale) || r;
5
+ };
6
+ new Intl.DateTimeFormat().resolvedOptions().timeZone;
7
+ const i = (t) => {
17
8
  if (!t)
18
9
  return "";
19
10
  const e = new Date(t);
@@ -25,13 +16,9 @@ const m = (t) => t ? t instanceof Date ? t : typeof t == "string" ? t.length ===
25
16
  second: "2-digit",
26
17
  hour12: !1
27
18
  }).format(e);
28
- }, h = (t) => t ? Math.floor(Date.now() / 1e3) >= t : !1;
19
+ }, m = (t) => t ? Math.floor(Date.now() / 1e3) >= t : !1;
29
20
  export {
30
- m as convertToDateObject,
31
- T as formatPatchDate,
32
- D as formatShortDate,
33
- h as isAfterDate,
34
- u as localTz,
35
- s as locale,
36
- f as timezone
21
+ i as formatPatchDate,
22
+ m as isAfterDate,
23
+ c as locale
37
24
  };
@@ -58,20 +58,11 @@ const o = (l) => {
58
58
  }, i = () => {
59
59
  var l;
60
60
  return ((l = document.head.querySelector('meta[name="csrf-token"]')) == null ? void 0 : l.getAttribute("content")) ?? "";
61
- }, k = (l, s) => {
62
- if (!l)
63
- return s;
64
- try {
65
- return JSON.parse(l);
66
- } catch {
67
- return s;
68
- }
69
61
  };
70
62
  export {
71
63
  v as buildMergeTagEntries,
72
64
  f as dynamicContentToMergeTags,
73
65
  b as flattenDynamicContentList,
74
66
  i as getCsrfToken,
75
- o as mergeTagToDynamicContent,
76
- k as parseJsonField
67
+ o as mergeTagToDynamicContent
77
68
  };
@@ -1,60 +1,59 @@
1
- import { useActionsApi as D } from "../composables/useActionsApi.js";
2
- import { useHtmlCompiler as P } from "../composables/useHtmlCompiler.js";
3
- import { DEFAULT_CURRENCY as d, DEFAULT_NODE_CONFIG as n } from "../extensions/Blocks/Recommendation/constants/defaultConfig.js";
4
- import { useRecommendationExtensionStore as A } from "../extensions/Blocks/Recommendation/store/recommendation.js";
5
- import { mapLegacyStrategy as I } from "../extensions/Blocks/Recommendation/utils/legacyStrategyMap.js";
6
- import { DATA_ATTRIBUTES as b } from "../extensions/Blocks/Unsubscribe/utils/constants.js";
7
- import { parsePageList as E } from "../extensions/Blocks/Unsubscribe/utils/utils.js";
8
- import { useDynamicContentStore as H } from "../stores/dynamic-content.js";
9
- import { useUnsubscribeStore as U } from "../stores/unsubscribe.js";
10
- function F(m) {
11
- const o = new DOMParser().parseFromString(m, "text/html").querySelectorAll(`[${b.PAGE_LIST}]`), r = [];
12
- return o.forEach((t) => {
13
- const c = t.getAttribute(b.PAGE_LIST);
14
- c && r.push(...E(c));
15
- }), [...new Set(r)];
1
+ import { useActionsApi as P } from "../composables/useActionsApi.js";
2
+ import { useHtmlCompiler as w } from "../composables/useHtmlCompiler.js";
3
+ import { DEFAULT_CURRENCY as l, DEFAULT_NODE_CONFIG as i } from "../extensions/Blocks/Recommendation/constants/defaultConfig.js";
4
+ import { useRecommendationExtensionStore as b } from "../extensions/Blocks/Recommendation/store/recommendation.js";
5
+ import { mapLegacyStrategy as E } from "../extensions/Blocks/Recommendation/utils/legacyStrategyMap.js";
6
+ import { DATA_ATTRIBUTES as h } from "../extensions/Blocks/Unsubscribe/utils/constants.js";
7
+ import { parsePageList as H } from "../extensions/Blocks/Unsubscribe/utils/utils.js";
8
+ import { useDynamicContentStore as U } from "../stores/dynamic-content.js";
9
+ import { useUnsubscribeStore as F } from "../stores/unsubscribe.js";
10
+ function L(s) {
11
+ const o = new DOMParser().parseFromString(s, "text/html").querySelectorAll(`[${h.PAGE_LIST}]`), t = [];
12
+ return o.forEach((a) => {
13
+ const n = a.getAttribute(h.PAGE_LIST);
14
+ n && t.push(...H(n));
15
+ }), [...new Set(t)];
16
16
  }
17
- async function L(m) {
18
- const l = new DOMParser().parseFromString(m, "text/html").querySelectorAll(".recommendation-block-v2");
19
- if (l.length === 0)
17
+ async function R(s) {
18
+ const m = new DOMParser().parseFromString(s, "text/html").querySelectorAll(".recommendation-block-v2");
19
+ if (m.length === 0)
20
20
  return;
21
- const o = A(), r = /* @__PURE__ */ new Set();
22
- l.forEach((t) => {
23
- var g, f, y, a, S;
24
- const c = t.getAttribute("recommendation-id"), p = c ? Number(c) : NaN;
25
- if (!Number.isFinite(p))
21
+ const o = b();
22
+ m.forEach((t) => {
23
+ var c, p, g, f, r;
24
+ const a = t.getAttribute("recommendation-id"), n = a ? Number(a) : NaN;
25
+ if (!Number.isFinite(n))
26
26
  return;
27
- const s = t.getAttribute("esd-ext-config");
28
- if (!s)
27
+ const d = t.getAttribute("esd-ext-config");
28
+ if (!d)
29
29
  return;
30
30
  let e;
31
31
  try {
32
- e = JSON.parse(s);
32
+ e = JSON.parse(d);
33
33
  } catch {
34
34
  return;
35
35
  }
36
36
  if (!e || typeof e != "object" || Array.isArray(e))
37
37
  return;
38
- const i = {
38
+ const y = {
39
39
  // Normalize legacy strategy keys (e.g. `mostViewed` → `mostPopular`) so
40
40
  // already-saved blocks that never re-run the migrator don't emit a
41
41
  // strategy-less campaign URL. `||` also catches '' / undefined (SD-144882).
42
- strategy: I(e.strategy) || n.strategy,
43
- strategyId: e.strategyId ?? n.strategyId,
44
- language: e.language ?? n.language,
45
- size: e.size ?? n.size,
42
+ strategy: E(e.strategy) || i.strategy,
43
+ language: e.language ?? i.language,
44
+ size: e.size ?? i.size,
46
45
  // Spread the default arrays so each block gets a fresh reference
47
46
  // instead of sharing the singleton in DEFAULT_NODE_CONFIG.
48
- productIds: e.productIds ?? [...n.productIds],
49
- filters: e.filters ?? [...n.filters],
50
- shuffleProducts: e.shuffleProducts ?? n.shuffleProducts,
51
- currencyCode: ((g = e.currency) == null ? void 0 : g.code) ?? d.code,
52
- currencyAlignment: ((f = e.currency) == null ? void 0 : f.alignment) ?? d.alignment,
53
- currencyDecimalCount: ((y = e.currency) == null ? void 0 : y.decimalCount) ?? d.decimalCount,
54
- currencyDecimalSeparator: ((a = e.currency) == null ? void 0 : a.decimalSeparator) ?? d.decimalSeparator,
55
- currencyThousandSeparator: ((S = e.currency) == null ? void 0 : S.thousandSeparator) ?? d.thousandSeparator
47
+ productIds: e.productIds ?? [...i.productIds],
48
+ filters: e.filters ?? [...i.filters],
49
+ shuffleProducts: e.shuffleProducts ?? i.shuffleProducts,
50
+ currencyCode: ((c = e.currency) == null ? void 0 : c.code) ?? l.code,
51
+ currencyAlignment: ((p = e.currency) == null ? void 0 : p.alignment) ?? l.alignment,
52
+ currencyDecimalCount: ((g = e.currency) == null ? void 0 : g.decimalCount) ?? l.decimalCount,
53
+ currencyDecimalSeparator: ((f = e.currency) == null ? void 0 : f.decimalSeparator) ?? l.decimalSeparator,
54
+ currencyThousandSeparator: ((r = e.currency) == null ? void 0 : r.thousandSeparator) ?? l.thousandSeparator
56
55
  };
57
- o.seedBlockUrlConfig(p, i), r.add(i.strategyId);
56
+ o.seedBlockUrlConfig(n, y);
58
57
  });
59
58
  try {
60
59
  await o.fetchRecommendationCreateData();
@@ -64,47 +63,44 @@ async function L(m) {
64
63
  t
65
64
  );
66
65
  }
67
- r.delete(""), await Promise.all(
68
- [...r].map((t) => o.fetchStrategyUrlTemplate(t))
69
- );
70
66
  }
71
67
  const G = () => {
72
- const m = H(), u = U(), { getCompiledEmail: l, getTemplateData: o } = D(), { compileHtml: r, compileAmpHtml: t } = P();
68
+ const s = U(), u = F(), { getCompiledEmail: m, getTemplateData: o } = P(), { compileHtml: t, compileAmpHtml: a } = w();
73
69
  return {
74
70
  prepareTemplateDetails: async () => {
75
- const { html: p, ampHtml: s = "", ampErrors: e = [] } = await l({
71
+ const { html: d, ampHtml: e = "", ampErrors: y = [] } = await m({
76
72
  minimize: !0,
77
73
  resetDataSavedFlag: !1
78
- }), { html: i, css: g, syncModulesIds: f = [] } = await o();
79
- u.selectedUnsubscribePages.length && await u.fetchTemplates(), await L(i);
80
- const { compiledHtml: y, stats: a, appliedRules: S } = r(p), C = s && t(s).compiledHtml, T = m.getSelectedDynamicContentList, w = A(), h = F(i);
74
+ }), { html: c, css: p, syncModulesIds: g = [] } = await o();
75
+ u.selectedUnsubscribePages.length && await u.fetchTemplates(), await R(c);
76
+ const { compiledHtml: f, stats: r, appliedRules: A } = t(d), C = e && a(e).compiledHtml, T = s.getSelectedDynamicContentList, D = b(), S = L(c);
81
77
  return console.debug("HTML Compilation Stats:", {
82
- originalSize: a.originalSize,
83
- compiledSize: a.compiledSize,
84
- reduction: `${a.reductionPercentage.toFixed(2)}%`,
85
- appliedRules: S,
86
- executionTime: `${a.executionTime.toFixed(2)}ms`
78
+ originalSize: r.originalSize,
79
+ compiledSize: r.compiledSize,
80
+ reduction: `${r.reductionPercentage.toFixed(2)}%`,
81
+ appliedRules: A,
82
+ executionTime: `${r.executionTime.toFixed(2)}ms`
87
83
  }), {
88
84
  dynamicContentList: T,
89
- compiledHtml: y,
90
- rawHtml: i,
91
- css: g,
85
+ compiledHtml: f,
86
+ rawHtml: c,
87
+ css: p,
92
88
  ampHtml: C,
93
- ampErrors: e,
94
- modules: f.map(Number),
89
+ ampErrors: y,
90
+ modules: g.map(Number),
95
91
  recommendation: {
96
- campaignUrls: w.recommendationCampaignUrls,
92
+ campaignUrls: D.recommendationCampaignUrls,
97
93
  configs: {}
98
94
  },
99
95
  unsubscribe: {
100
- status: h.length > 0,
101
- config: h
96
+ status: S.length > 0,
97
+ config: S
102
98
  }
103
99
  };
104
100
  }
105
101
  };
106
102
  };
107
103
  export {
108
- F as getHtmlReferencedUnsubscribePages,
104
+ L as getHtmlReferencedUnsubscribePages,
109
105
  G as useTemplatePreparation
110
106
  };
@@ -0,0 +1,4 @@
1
+ const t = ["http:", "https:", "mailto:"];
2
+ export {
3
+ t as BASE_SAFE_URL_SCHEMES
4
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@useinsider/guido",
3
- "version": "3.12.0-beta.b73c720",
3
+ "version": "3.12.0-beta.bec0e55",
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",
@@ -1,36 +0,0 @@
1
- import o from "./StrategyDetailDrawer.vue2.js";
2
- /* empty css */
3
- import l from "../../../../_virtual/_plugin-vue2_normalizer.js";
4
- var c = function() {
5
- var i;
6
- var a = this, s = a._self._c, t = a._self._setupProxy;
7
- return s(t.InDrawer, { attrs: { id: "recommendation-strategy-detail-drawer", "close-button-status": "", "full-width-content-status": "", "overlay-status": "", size: "medium", state: "default", "description-status": !1, "footer-status": !1, status: t.isOpen, "title-text": ((i = t.strategy) == null ? void 0 : i.name) || "" }, on: { onCloseEvent: t.close }, scopedSlots: a._u([{ key: "headerBottomSlot", fn: function() {
8
- return [t.strategy ? s(t.InTabs, { staticClass: "p-r z-4 px-5", attrs: { "active-tab": t.activeTab, "add-new-tab-active": !1, draggable: !1, "dropdown-tabs": !1, "tabs-list": t.tabs, "use-router": !1 }, on: { click: function(e) {
9
- t.activeTab = e;
10
- } } }) : a._e()];
11
- }, proxy: !0 }, { key: "contentSlot", fn: function() {
12
- return [t.strategy ? s("div", [t.activeTab === t.DetailTabs.GENERAL ? s("div", [s("div", { staticClass: "px-5 py-5 bor-b-s-s bor-b-c-6 bor-b-w-1" }, [s("div", { staticClass: "f-s-13 f-w-600 mb-3" }, [a._v(" " + a._s(t.trans("discovery-strategy.suitable-page-and-channel-types")) + " ")]), a._l(t.visibleRows(t.pageAndChannelDetails), function(e, r) {
13
- return s(t.InfoDisplayValue, { key: e.title, attrs: { index: r, title: e.title, "translate-value": e.translateValue, value: e.value } });
14
- })], 2), s("div", { staticClass: "px-5 py-5 bor-b-s-s bor-b-c-6 bor-b-w-1" }, [s("div", { staticClass: "f-s-13 f-w-600 mb-3" }, [a._v(" " + a._s(t.trans("discovery-strategy.recommendation-widget-preferences")) + " ")]), a._l(t.visibleRows(t.widgetPreferences), function(e, r) {
15
- return s(t.InfoDisplayValue, { key: e.title, attrs: { index: r, title: e.title, "translate-value": e.translateValue, value: e.value } });
16
- })], 2), s("div", { staticClass: "px-5 py-5 bor-b-s-s bor-b-c-6 bor-b-w-1" }, [s("div", { staticClass: "f-s-13 f-w-600 mb-3" }, [a._v(" " + a._s(t.trans("discovery-strategy.advanced-personalization-criteria")) + " ")]), a._l(t.visibleRows(t.advancedPersonalization), function(e, r) {
17
- return s(t.InfoDisplayValue, { key: e.title, attrs: { index: r, title: e.title, "translate-value": e.translateValue, value: e.value } });
18
- })], 2), s("div", { staticClass: "px-5 py-5" }, [s("div", { staticClass: "f-s-13 f-w-600 mb-3" }, [a._v(" " + a._s(t.trans("discovery-strategy.filters")) + " ")]), s(t.FilterHumanizer, { attrs: { filters: t.strategy.filters } })], 1)]) : a._e(), t.activeTab === t.DetailTabs.OTHER ? s("div", [s("div", { staticClass: "px-5 py-5 bor-b-s-s bor-b-c-6 bor-b-w-1 other-details" }, a._l(t.visibleRows(t.otherDetails), function(e, r) {
19
- return s(t.InfoDisplayValue, { key: e.title, attrs: { icon: e.icon, "icon-tooltip": e.iconTooltip, "icon-tooltip-after-clicked": e.iconTooltipAfterClicked, index: r, title: e.title, "translate-value": e.translateValue, value: e.value }, on: { iconClick: function(b) {
20
- var n;
21
- (n = e.iconClick) == null || n.call(e, e.value);
22
- } } });
23
- }), 1), s("div", { staticClass: "px-5 py-5" }, [s(t.CampaignTable, { attrs: { campaigns: t.store.strategyCampaigns, loading: t.store.strategyCampaignsLoading } })], 1)]) : a._e()]) : a._e()];
24
- }, proxy: !0 }]) });
25
- }, u = [], v = /* @__PURE__ */ l(
26
- o,
27
- c,
28
- u,
29
- !1,
30
- null,
31
- "008cf324"
32
- );
33
- const d = v.exports;
34
- export {
35
- d as default
36
- };