@useinsider/guido 1.0.1-beta.fce60db → 1.0.2-beta.02ef88a

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 (106) hide show
  1. package/README.md +31 -1
  2. package/dist/@types/events.d.ts +6 -0
  3. package/dist/@types/generic.d.ts +4 -0
  4. package/dist/components/Guido.vue.d.ts +3 -1
  5. package/dist/components/Guido.vue.js +8 -8
  6. package/dist/components/Guido.vue2.js +75 -61
  7. package/dist/components/organisms/LoadingWrapper.vue.d.ts +2 -0
  8. package/dist/components/organisms/LoadingWrapper.vue.js +18 -0
  9. package/dist/components/organisms/LoadingWrapper.vue2.js +12 -0
  10. package/dist/components/organisms/email-preview/PreviewContainer.vue.js +12 -12
  11. package/dist/components/organisms/email-preview/PreviewContainer.vue2.js +7 -8
  12. package/dist/components/organisms/email-preview/amp/AmpToggle.vue.js +3 -2
  13. package/dist/components/organisms/header/HeaderWrapper.vue.d.ts +3 -1
  14. package/dist/components/organisms/header/HeaderWrapper.vue.js +6 -6
  15. package/dist/components/organisms/header/HeaderWrapper.vue2.js +15 -9
  16. package/dist/components/organisms/header/LeftSlot.vue.js +10 -9
  17. package/dist/components/organisms/header/MiddleSlot.vue.js +6 -6
  18. package/dist/components/organisms/header/MiddleSlot.vue2.js +15 -16
  19. package/dist/components/organisms/header/RightSlot.vue.d.ts +3 -1
  20. package/dist/components/organisms/header/RightSlot.vue.js +6 -4
  21. package/dist/components/organisms/header/RightSlot.vue2.js +26 -28
  22. package/dist/components/organisms/header/ViewOptions.vue.js +11 -11
  23. package/dist/components/organisms/header/ViewOptions.vue2.js +14 -15
  24. package/dist/components/organisms/header/version-history/ViewOptions.vue.js +11 -11
  25. package/dist/components/organisms/header/version-history/ViewOptions.vue2.js +5 -5
  26. package/dist/components/organisms/onboarding/AMPOnboarding.vue.d.ts +2 -0
  27. package/dist/components/organisms/onboarding/AMPOnboarding.vue.js +20 -0
  28. package/dist/components/organisms/onboarding/AMPOnboarding.vue2.js +37 -0
  29. package/dist/components/organisms/onboarding/GenericOnboarding.vue.d.ts +2 -0
  30. package/dist/components/organisms/onboarding/GenericOnboarding.vue.js +21 -0
  31. package/dist/components/organisms/onboarding/GenericOnboarding.vue2.js +83 -0
  32. package/dist/components/organisms/onboarding/NewVersionPopup.vue.d.ts +2 -0
  33. package/dist/components/organisms/onboarding/NewVersionPopup.vue.js +17 -0
  34. package/dist/components/organisms/onboarding/NewVersionPopup.vue2.js +30 -0
  35. package/dist/components/organisms/onboarding/OnboardingWrapper.vue.d.ts +2 -0
  36. package/dist/components/organisms/onboarding/OnboardingWrapper.vue.js +19 -0
  37. package/dist/components/organisms/onboarding/OnboardingWrapper.vue2.js +43 -0
  38. package/dist/components/organisms/onboarding/TextBlockOnboarding.vue.d.ts +2 -0
  39. package/dist/components/organisms/onboarding/TextBlockOnboarding.vue.js +21 -0
  40. package/dist/components/organisms/onboarding/TextBlockOnboarding.vue2.js +74 -0
  41. package/dist/components/organisms/onboarding/VersionHistoryOnboarding.vue.d.ts +2 -0
  42. package/dist/components/organisms/onboarding/VersionHistoryOnboarding.vue.js +20 -0
  43. package/dist/components/organisms/onboarding/VersionHistoryOnboarding.vue2.js +37 -0
  44. package/dist/composables/useCustomInterfaceAppearance.js +25 -19
  45. package/dist/composables/useHtmlCompiler.js +17 -9
  46. package/dist/composables/useHtmlValidator.d.ts +3 -0
  47. package/dist/composables/useHtmlValidator.js +120 -0
  48. package/dist/composables/usePartner.d.ts +1 -0
  49. package/dist/composables/usePartner.js +16 -9
  50. package/dist/composables/usePreviewMode.js +13 -13
  51. package/dist/composables/useRecommendation.d.ts +19 -0
  52. package/dist/composables/useRecommendation.js +27 -0
  53. package/dist/composables/useSave.d.ts +4 -0
  54. package/dist/composables/useSave.js +15 -0
  55. package/dist/composables/useStripo.js +43 -39
  56. package/dist/composables/useStripoEventHandler.d.ts +3 -0
  57. package/dist/composables/useStripoEventHandler.js +20 -0
  58. package/dist/composables/useToaster.js +17 -17
  59. package/dist/config/compiler/outlookCompilerRules.d.ts +2 -0
  60. package/dist/config/compiler/outlookCompilerRules.js +36 -0
  61. package/dist/config/compiler/recommendationCompilerRules.d.ts +2 -0
  62. package/dist/config/compiler/recommendationCompilerRules.js +83 -0
  63. package/dist/config/compiler/socialCompilerRules.d.ts +2 -0
  64. package/dist/config/compiler/socialCompilerRules.js +21 -0
  65. package/dist/config/compiler/unsubscribeCompilerRules.d.ts +2 -0
  66. package/dist/config/compiler/unsubscribeCompilerRules.js +64 -0
  67. package/dist/enums/defaults.d.ts +6 -1
  68. package/dist/enums/defaults.js +45 -9
  69. package/dist/enums/html-validator.d.ts +6 -0
  70. package/dist/enums/html-validator.js +7 -0
  71. package/dist/enums/onboarding.d.ts +1 -0
  72. package/dist/enums/onboarding.js +8 -0
  73. package/dist/enums/recommendation.d.ts +54 -0
  74. package/dist/enums/recommendation.js +56 -0
  75. package/dist/enums/unsubscribe.d.ts +15 -0
  76. package/dist/enums/unsubscribe.js +17 -0
  77. package/dist/guido.css +1 -1
  78. package/dist/mock/api/user-modal-state.d.ts +2 -0
  79. package/dist/mock/api/validator.d.ts +2 -0
  80. package/dist/services/onboardingApi.d.ts +4 -0
  81. package/dist/services/onboardingApi.js +23 -0
  82. package/dist/static/assets/onboarding-img.svg.js +4 -0
  83. package/dist/static/styles/components/alert-message.css.js +32 -2
  84. package/dist/static/styles/components/button.css.js +32 -2
  85. package/dist/static/styles/components/loader.css.js +10 -0
  86. package/dist/static/styles/components/notification.css.js +55 -0
  87. package/dist/static/styles/components/popup.css.js +68 -0
  88. package/dist/static/styles/components/wide-panel.css.js +12 -3
  89. package/dist/static/styles/customEditorStyle.css.js +6 -0
  90. package/dist/static/styles/variables.css.js +10 -0
  91. package/dist/stores/dynamic-content.d.ts +12 -0
  92. package/dist/stores/dynamic-content.js +7 -6
  93. package/dist/stores/editor.d.ts +194 -1
  94. package/dist/stores/editor.js +18 -2
  95. package/dist/stores/onboarding.d.ts +1068 -0
  96. package/dist/stores/onboarding.js +95 -0
  97. package/dist/stores/preview.d.ts +0 -8
  98. package/dist/stores/preview.js +12 -20
  99. package/dist/stores/recommendation.d.ts +10 -0
  100. package/dist/stores/recommendation.js +9 -0
  101. package/dist/stores/unsubscribe.d.ts +8 -0
  102. package/dist/stores/unsubscribe.js +9 -0
  103. package/dist/utils/genericUtil.d.ts +5 -0
  104. package/dist/utils/genericUtil.js +9 -6
  105. package/dist/utils/templatePreparation.js +21 -14
  106. package/package.json +7 -4
@@ -0,0 +1,95 @@
1
+ import { useOnboardingApi as t } from "../services/onboardingApi.js";
2
+ import { defineStore as s } from "pinia";
3
+ const e = () => ({
4
+ cardIndex: 0,
5
+ isActive: !1,
6
+ config: []
7
+ }), a = () => ({
8
+ onboardings: {
9
+ newVersionPopup: e(),
10
+ genericOnboarding: e(),
11
+ textBlockOnboarding: e(),
12
+ versionHistoryOnboarding: e(),
13
+ ampOnboarding: e()
14
+ },
15
+ userModalState: {},
16
+ isFetched: !1
17
+ }), g = s("onboarding", {
18
+ state: () => a(),
19
+ getters: {
20
+ getOnboarding: (n) => (o) => n.onboardings[o],
21
+ isActive: (n) => (o) => n.onboardings[o].isActive,
22
+ getCurrentCard: (n) => (o) => {
23
+ const i = n.onboardings[o];
24
+ return i.config[i.cardIndex];
25
+ },
26
+ getGenericCurrentCard: (n) => {
27
+ const { genericOnboarding: o } = n.onboardings;
28
+ return o.config[o.cardIndex];
29
+ },
30
+ getTextBlockCurrentCard: (n) => {
31
+ const { textBlockOnboarding: o } = n.onboardings;
32
+ return o.config[o.cardIndex];
33
+ },
34
+ getVersionHistoryCurrentCard: (n) => {
35
+ const { versionHistoryOnboarding: o } = n.onboardings;
36
+ return o.config[o.cardIndex];
37
+ },
38
+ getAmpCurrentCard: (n) => {
39
+ const { ampOnboarding: o } = n.onboardings;
40
+ return o.config[o.cardIndex];
41
+ },
42
+ shouldShowOnboarding: (n) => (o) => {
43
+ const i = n.userModalState.Guido;
44
+ return o === "newVersionPopup" && (i != null && i.genericOnboarding) ? !1 : i ? !i[o] : !0;
45
+ }
46
+ },
47
+ actions: {
48
+ closeOtherOnboardings(n) {
49
+ Object.keys(this.onboardings).forEach((o) => {
50
+ const i = o;
51
+ i !== n && this.onboardings[i].isActive && (this.onboardings[i].isActive = !1, this.onboardings[i].cardIndex = 0, this.onboardings[i].config = []);
52
+ });
53
+ },
54
+ start(n) {
55
+ this.closeOtherOnboardings(n), this.onboardings[n].cardIndex = 0, this.onboardings[n].isActive = !0;
56
+ },
57
+ async close(n) {
58
+ await this.setUserModalState(n, "true", "Guido"), this.onboardings[n].isActive = !1, this.onboardings[n].cardIndex = 0, this.onboardings[n].config = [];
59
+ },
60
+ next(n) {
61
+ this.onboardings[n].cardIndex < this.onboardings[n].config.length - 1 && this.onboardings[n].cardIndex++;
62
+ },
63
+ previous(n) {
64
+ this.onboardings[n].cardIndex > 0 && this.onboardings[n].cardIndex--;
65
+ },
66
+ setConfig(n, o) {
67
+ this.onboardings[n].config = o ?? [], this.onboardings[n].cardIndex >= this.onboardings[n].config.length && (this.onboardings[n].cardIndex = 0);
68
+ },
69
+ async onDiscoverNowClicked() {
70
+ await this.setUserModalState("newVersionPopup", "true", "Guido"), await this.setUserModalState("genericOnboarding", "true", "Guido"), this.start("genericOnboarding");
71
+ },
72
+ onRemindMeLater() {
73
+ sessionStorage.setItem("newVersionPopupDismissed", "true");
74
+ },
75
+ async onNewVersionPopupClose() {
76
+ await this.setUserModalState("newVersionPopup", "true", "Guido");
77
+ },
78
+ isOnboardingActive() {
79
+ return Object.values(this.onboardings).some((n) => n.isActive);
80
+ },
81
+ async fetchUserModalState() {
82
+ if (this.isFetched)
83
+ return;
84
+ const { fetchUserModalState: n } = t(), o = await n();
85
+ this.userModalState = o, this.isFetched = !0;
86
+ },
87
+ async setUserModalState(n, o, i) {
88
+ const { setUserModalState: r } = t();
89
+ await r(n, o, i), this.isFetched = !1, await this.fetchUserModalState();
90
+ }
91
+ }
92
+ });
93
+ export {
94
+ g as useOnboardingStore
95
+ };
@@ -5,10 +5,8 @@ export declare const usePreviewStore: import("pinia").StoreDefinition<"preview",
5
5
  ampHtml: string;
6
6
  ampErrors: string[];
7
7
  templateHtml: string;
8
- isLoading: boolean;
9
8
  isLoaded: boolean;
10
9
  isAMPErrorModalVisible: boolean;
11
- isPreviewModeOpen: boolean;
12
10
  } & import("pinia").PiniaCustomStateProperties<PreviewState>) => boolean;
13
11
  previewHtml(): string;
14
12
  hasAMP: (state: {
@@ -16,20 +14,16 @@ export declare const usePreviewStore: import("pinia").StoreDefinition<"preview",
16
14
  ampHtml: string;
17
15
  ampErrors: string[];
18
16
  templateHtml: string;
19
- isLoading: boolean;
20
17
  isLoaded: boolean;
21
18
  isAMPErrorModalVisible: boolean;
22
- isPreviewModeOpen: boolean;
23
19
  } & import("pinia").PiniaCustomStateProperties<PreviewState>) => boolean;
24
20
  hasAMPErrors: (state: {
25
21
  emailFormat: EmailFormat;
26
22
  ampHtml: string;
27
23
  ampErrors: string[];
28
24
  templateHtml: string;
29
- isLoading: boolean;
30
25
  isLoaded: boolean;
31
26
  isAMPErrorModalVisible: boolean;
32
- isPreviewModeOpen: boolean;
33
27
  } & import("pinia").PiniaCustomStateProperties<PreviewState>) => boolean;
34
28
  showAMPErrorButton(): boolean;
35
29
  isAMPResponsive(): boolean;
@@ -41,6 +35,4 @@ export declare const usePreviewStore: import("pinia").StoreDefinition<"preview",
41
35
  setEmailFormat(format: EmailFormat): void;
42
36
  closeErrorModal(): void;
43
37
  openErrorModal(): void;
44
- openPreviewMode(): void;
45
- closePreviewMode(): void;
46
38
  }>;
@@ -1,25 +1,23 @@
1
- import { defineStore as t } from "pinia";
1
+ import { defineStore as e } from "pinia";
2
2
  const s = () => ({
3
3
  emailFormat: "html",
4
4
  ampHtml: "",
5
5
  ampErrors: [],
6
6
  templateHtml: "",
7
- isLoading: !1,
8
7
  isLoaded: !1,
9
- isAMPErrorModalVisible: !1,
10
- isPreviewModeOpen: !1
11
- }), o = t("preview", {
8
+ isAMPErrorModalVisible: !1
9
+ }), a = e("preview", {
12
10
  state: () => s(),
13
11
  getters: {
14
- isAMPModeEnabled: (e) => e.emailFormat === "AMP",
12
+ isAMPModeEnabled: (t) => t.emailFormat === "AMP",
15
13
  previewHtml() {
16
14
  return this.isAMPModeEnabled ? this.ampHtml : this.templateHtml;
17
15
  },
18
- hasAMP: (e) => {
16
+ hasAMP: (t) => {
19
17
  var r;
20
- return !!((r = e.ampHtml) != null && r.trim());
18
+ return !!((r = t.ampHtml) != null && r.trim());
21
19
  },
22
- hasAMPErrors: (e) => e.ampErrors.length > 0,
20
+ hasAMPErrors: (t) => t.ampErrors.length > 0,
23
21
  showAMPErrorButton() {
24
22
  return this.isAMPModeEnabled && this.hasAMPErrors;
25
23
  },
@@ -28,26 +26,20 @@ const s = () => ({
28
26
  }
29
27
  },
30
28
  actions: {
31
- updateAMPData(e) {
32
- this.ampHtml = e.ampHtml ?? "", this.ampErrors = e.ampErrors ?? [], this.emailFormat = e.ampHtml ? "AMP" : "html";
29
+ updateAMPData(t) {
30
+ this.ampHtml = t.ampHtml ?? "", this.ampErrors = t.ampErrors ?? [], this.emailFormat = t.ampHtml ? "AMP" : "html";
33
31
  },
34
- setEmailFormat(e) {
35
- this.emailFormat = e;
32
+ setEmailFormat(t) {
33
+ this.emailFormat = t;
36
34
  },
37
35
  closeErrorModal() {
38
36
  this.isAMPErrorModalVisible = !1;
39
37
  },
40
38
  openErrorModal() {
41
39
  this.isAMPErrorModalVisible = !0;
42
- },
43
- openPreviewMode() {
44
- this.isPreviewModeOpen = !0;
45
- },
46
- closePreviewMode() {
47
- this.isPreviewModeOpen = !1;
48
40
  }
49
41
  }
50
42
  });
51
43
  export {
52
- o as usePreviewStore
44
+ a as usePreviewStore
53
45
  };
@@ -0,0 +1,10 @@
1
+ type RecommendationConfig = {
2
+ filters: {
3
+ value: string;
4
+ }[];
5
+ };
6
+ interface StoreState {
7
+ recommendationConfigs: RecommendationConfig[];
8
+ }
9
+ export declare const useRecommendationStore: import("pinia").StoreDefinition<"recommendation", StoreState, {}, {}>;
10
+ export {};
@@ -0,0 +1,9 @@
1
+ import { defineStore as e } from "pinia";
2
+ const t = () => ({
3
+ recommendationConfigs: []
4
+ }), n = e("recommendation", {
5
+ state: () => t()
6
+ });
7
+ export {
8
+ n as useRecommendationStore
9
+ };
@@ -0,0 +1,8 @@
1
+ type UnsubscribePage = {
2
+ type: number;
3
+ };
4
+ interface StoreState {
5
+ selectedUnsubscribePages: UnsubscribePage[];
6
+ }
7
+ export declare const useUnsubscribeStore: import("pinia").StoreDefinition<"unsubscribe", StoreState, {}, {}>;
8
+ export {};
@@ -0,0 +1,9 @@
1
+ import { defineStore as e } from "pinia";
2
+ const t = () => ({
3
+ selectedUnsubscribePages: []
4
+ }), r = e("unsubscribe", {
5
+ state: () => t()
6
+ });
7
+ export {
8
+ r as useUnsubscribeStore
9
+ };
@@ -1,3 +1,8 @@
1
1
  import type { DynamicContent, MergeTag } from '@@/Types/generic';
2
2
  export declare const dynamicContentToMergeTags: (dynamicContentList: DynamicContent[]) => MergeTag[];
3
3
  export declare const getCsrfToken: () => string;
4
+ /**
5
+ * URL Parameter utilities
6
+ */
7
+ export declare const getUrlParams: () => URLSearchParams;
8
+ export declare const getUrlParam: (paramName: string, defaultValue?: string) => string;
@@ -1,11 +1,14 @@
1
- const l = (e) => e.map((a) => ({
1
+ const e = (l) => l.map((a) => a.format ? {
2
+ label: `${a.text} | ${a.format.key}=${a.format.value}`,
3
+ value: `{{${a.value}|${a.format.key}=${a.format.value}}}`
4
+ } : {
2
5
  label: a.fallback ? `${a.text} | ${a.fallback}` : a.text,
3
6
  value: a.fallback ? `{{${a.value}|${a.fallback}}}` : `{{${a.value}}}`
4
- })), t = () => {
5
- var e;
6
- return ((e = document.head.querySelector('meta[name="csrf-token"]')) == null ? void 0 : e.getAttribute("content")) ?? "";
7
+ }), r = () => {
8
+ var l;
9
+ return ((l = document.head.querySelector('meta[name="csrf-token"]')) == null ? void 0 : l.getAttribute("content")) ?? "";
7
10
  };
8
11
  export {
9
- l as dynamicContentToMergeTags,
10
- t as getCsrfToken
12
+ e as dynamicContentToMergeTags,
13
+ r as getCsrfToken
11
14
  };
@@ -1,21 +1,28 @@
1
- import { useActionsApi as c } from "../composables/useActionsApi.js";
2
- import { useDynamicContentStore as l } from "../stores/dynamic-content.js";
3
- const y = () => {
4
- const t = l(), { getCompiledEmail: e, getTemplateData: a } = c();
1
+ import { useActionsApi as g } from "../composables/useActionsApi.js";
2
+ import { useHtmlCompiler as S } from "../composables/useHtmlCompiler.js";
3
+ import { useDynamicContentStore as f } from "../stores/dynamic-content.js";
4
+ const H = () => {
5
+ const t = f(), { getCompiledEmail: i, getTemplateData: o } = g(), { compileHtml: n } = S();
5
6
  return {
6
7
  prepareTemplateDetails: async () => {
7
- const { html: n, ampHtml: s = "", ampErrors: o = [] } = await e({
8
+ const { html: a, ampHtml: s = "", ampErrors: m = [] } = await i({
8
9
  minimize: !0,
9
10
  resetDataSavedFlag: !1
10
- }), { html: m, css: i, syncModulesIds: r = [] } = await a();
11
- return {
12
- dynamicContentList: t.getSelectedDynamicContentList,
13
- compiledHtml: n,
14
- rawHtml: m,
15
- css: i,
11
+ }), { html: r, css: c, syncModulesIds: l = [] } = await o(), { compiledHtml: p, stats: e, appliedRules: d } = n(a), u = t.getSelectedDynamicContentList;
12
+ return console.debug("HTML Compilation Stats:", {
13
+ originalSize: e.originalSize,
14
+ compiledSize: e.compiledSize,
15
+ reduction: `${e.reductionPercentage.toFixed(2)}%`,
16
+ appliedRules: d,
17
+ executionTime: `${e.executionTime.toFixed(2)}ms`
18
+ }), {
19
+ dynamicContentList: u,
20
+ compiledHtml: p,
21
+ rawHtml: r,
22
+ css: c,
16
23
  ampHtml: s,
17
- ampErrors: o,
18
- modules: r,
24
+ ampErrors: m,
25
+ modules: l,
19
26
  recommendation: {
20
27
  campaignUrls: {},
21
28
  configs: {}
@@ -29,5 +36,5 @@ const y = () => {
29
36
  };
30
37
  };
31
38
  export {
32
- y as useTemplatePreparation
39
+ H as useTemplatePreparation
33
40
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@useinsider/guido",
3
- "version": "1.0.1-beta.fce60db",
3
+ "version": "1.0.2-beta.02ef88a",
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",
@@ -23,13 +23,15 @@
23
23
  "preview": "vite preview",
24
24
  "lint": "NODE_OPTIONS=--max-old-space-size=2048 eslint ./ && bun run type-check",
25
25
  "lint:fix": "NODE_OPTIONS=--max-old-space-size=2048 eslint --fix ./",
26
- "type-check": "vue-tsc --noEmit --project tsconfig.config.json && vue-tsc --noEmit --project tsconfig.app.json"
26
+ "type-check": "vue-tsc --noEmit --project tsconfig.config.json && vue-tsc --noEmit --project tsconfig.app.json",
27
+ "test:visual-update": "npx playwright test --update-snapshots --reporter html",
28
+ "prepare": "husky"
27
29
  },
28
30
  "keywords": [],
29
31
  "author": "",
30
32
  "license": "ISC",
31
33
  "dependencies": {
32
- "@stripoinc/ui-editor-extensions": "^1.0.6",
34
+ "@stripoinc/ui-editor-extensions": "1.0.6",
33
35
  "@useinsider/design-system-vue": "0.14.8",
34
36
  "@vueuse/core": "11.3.0",
35
37
  "lodash-es": "4.17.21",
@@ -42,7 +44,7 @@
42
44
  "@cspell/eslint-plugin": "8.17.5",
43
45
  "@eslint/eslintrc": "3.3.0",
44
46
  "@eslint/js": "8.57.1",
45
- "@playwright/test": "^1.54.2",
47
+ "@playwright/test": "1.55.0",
46
48
  "@stylistic/eslint-plugin": "3.1.0",
47
49
  "@stylistic/eslint-plugin-migrate": "3.1.0",
48
50
  "@types/eslint": "8.56.12",
@@ -64,6 +66,7 @@
64
66
  "eslint-plugin-vue": "10.1.0",
65
67
  "eslint-plugin-vue-scoped-css": "2.6.1",
66
68
  "globals": "16.0.0",
69
+ "husky": "9.1.7",
67
70
  "msw": "2.10.3",
68
71
  "sass": "1.69.7",
69
72
  "typescript": "5.5.4",