@zenky/storefront-vue 0.10.0 → 0.10.4

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 (77) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/README.md +66 -6
  3. package/dist/cart/index.d.ts +31 -2
  4. package/dist/cart/index.js +6 -9
  5. package/dist/catalog/index.d.ts +1 -11
  6. package/dist/catalog/index.js +2 -2
  7. package/dist/chunk-2CI4RW7V.js +21 -0
  8. package/dist/chunk-2CI4RW7V.js.map +1 -0
  9. package/dist/{chunk-GW2QNTAD.js → chunk-4IFPVEO7.js} +3 -113
  10. package/dist/chunk-4IFPVEO7.js.map +1 -0
  11. package/dist/{chunk-EYRCAUUP.js → chunk-E3G7NGCH.js} +2 -2
  12. package/dist/chunk-E3G7NGCH.js.map +1 -0
  13. package/dist/{chunk-IGDDWGS4.js → chunk-IVG4FQE5.js} +21 -13
  14. package/dist/chunk-IVG4FQE5.js.map +1 -0
  15. package/dist/{chunk-PJ2LEKW6.js → chunk-MZ66ARDL.js} +18 -3
  16. package/dist/chunk-MZ66ARDL.js.map +1 -0
  17. package/dist/{chunk-2OD7CLKD.js → chunk-NG7RUIWW.js} +3 -13
  18. package/dist/chunk-NG7RUIWW.js.map +1 -0
  19. package/dist/{chunk-VL3T36VM.js → chunk-NIQFYJRP.js} +53 -9
  20. package/dist/chunk-NIQFYJRP.js.map +1 -0
  21. package/dist/{chunk-AL5W5JSD.js → chunk-OTSAR32A.js} +6 -3
  22. package/dist/chunk-OTSAR32A.js.map +1 -0
  23. package/dist/{chunk-BIGTKVC6.js → chunk-SS7B2NLF.js} +3 -3
  24. package/dist/{chunk-BIGTKVC6.js.map → chunk-SS7B2NLF.js.map} +1 -1
  25. package/dist/{chunk-PWHIDGKW.js → chunk-TB5TBJ4U.js} +53 -6
  26. package/dist/chunk-TB5TBJ4U.js.map +1 -0
  27. package/dist/{chunk-4MJKN7PL.js → chunk-YREGCQSB.js} +4 -6
  28. package/dist/chunk-YREGCQSB.js.map +1 -0
  29. package/dist/{chunk-OQLU7FSR.js → chunk-YS5DXYYV.js} +228 -51
  30. package/dist/chunk-YS5DXYYV.js.map +1 -0
  31. package/dist/index.d.ts +6 -8
  32. package/dist/index.js +14 -16
  33. package/dist/order/index.d.ts +2 -0
  34. package/dist/order/index.js +2 -5
  35. package/dist/order/index.js.map +1 -1
  36. package/dist/pagination/index.js.map +1 -1
  37. package/dist/ports/index.d.ts +12 -1
  38. package/dist/ports/index.js +1 -1
  39. package/dist/products/index.js +3 -4
  40. package/dist/promotions/index.js +3 -5
  41. package/dist/seo/index.d.ts +5 -11
  42. package/dist/seo/index.js +17 -27
  43. package/dist/seo/index.js.map +1 -1
  44. package/dist/snippets/index.d.ts +21 -15
  45. package/dist/snippets/index.js +1 -1
  46. package/dist/stores/index.d.ts +1 -1
  47. package/dist/stores/index.js +3 -5
  48. package/dist/theme/index.d.ts +1 -11
  49. package/dist/theme/index.js +1 -1
  50. package/dist/utils/index.d.ts +28 -5
  51. package/dist/utils/index.js +1 -1
  52. package/dist/website-settings/index.d.ts +1 -1
  53. package/docs/consumer-setup.md +279 -0
  54. package/docs/ports.md +148 -0
  55. package/docs/releasing.md +53 -0
  56. package/package.json +27 -8
  57. package/dist/chunk-2OD7CLKD.js.map +0 -1
  58. package/dist/chunk-4MJKN7PL.js.map +0 -1
  59. package/dist/chunk-AL5W5JSD.js.map +0 -1
  60. package/dist/chunk-BWUKMWHL.js +0 -45
  61. package/dist/chunk-BWUKMWHL.js.map +0 -1
  62. package/dist/chunk-DKWDKVXQ.js +0 -91
  63. package/dist/chunk-DKWDKVXQ.js.map +0 -1
  64. package/dist/chunk-EYRCAUUP.js.map +0 -1
  65. package/dist/chunk-GW2QNTAD.js.map +0 -1
  66. package/dist/chunk-IGDDWGS4.js.map +0 -1
  67. package/dist/chunk-OQLU7FSR.js.map +0 -1
  68. package/dist/chunk-PJ2LEKW6.js.map +0 -1
  69. package/dist/chunk-PWHIDGKW.js.map +0 -1
  70. package/dist/chunk-V3VVDQIR.js +0 -27
  71. package/dist/chunk-V3VVDQIR.js.map +0 -1
  72. package/dist/chunk-VL3T36VM.js.map +0 -1
  73. package/dist/layout/index.d.ts +0 -54
  74. package/dist/layout/index.js +0 -102
  75. package/dist/layout/index.js.map +0 -1
  76. package/dist/schedule-Dd3zwHEb.d.ts +0 -27
  77. package/dist/snippets-BC5bkSjm.d.ts +0 -23
@@ -1,7 +1,45 @@
1
- import { useRuntime } from './chunk-V3VVDQIR.js';
1
+ import { ref, computed, inject, provide } from 'vue';
2
2
  import { defineStore } from 'pinia';
3
- import { ref, computed } from 'vue';
4
3
 
4
+ // src/ports/runtime.ts
5
+ var RUNTIME_KEY = /* @__PURE__ */ Symbol.for("zenky.storefront.runtime");
6
+ var REQUIRED_PORTS = [
7
+ "sdk",
8
+ "config",
9
+ "storage",
10
+ "notifier",
11
+ "activity",
12
+ "messages"
13
+ ];
14
+ function assertRequiredPorts(runtime) {
15
+ const missing = REQUIRED_PORTS.filter((port) => runtime[port] == null);
16
+ if (missing.length > 0) {
17
+ throw new Error(
18
+ `[@zenky/storefront-vue] Runtime is missing required ports: ${missing.join(", ")}. See the Runtime Ports table in the README.`
19
+ );
20
+ }
21
+ }
22
+ function createStorefront(runtime) {
23
+ assertRequiredPorts(runtime);
24
+ return {
25
+ install(app) {
26
+ app.provide(RUNTIME_KEY, runtime);
27
+ }
28
+ };
29
+ }
30
+ function provideStorefront(runtime) {
31
+ assertRequiredPorts(runtime);
32
+ provide(RUNTIME_KEY, runtime);
33
+ }
34
+ function useRuntime() {
35
+ const runtime = inject(RUNTIME_KEY);
36
+ if (!runtime) {
37
+ throw new Error(
38
+ "[@zenky/storefront-vue] Runtime was not provided. Install the plugin with app.use(createStorefront(runtime))."
39
+ );
40
+ }
41
+ return runtime;
42
+ }
5
43
  var useStoreProfileStore = defineStore("zenky-store-profile", () => {
6
44
  const store = ref(null);
7
45
  const city = ref(null);
@@ -89,9 +127,18 @@ var useAuthStore = defineStore("zenky-auth", () => {
89
127
  phone.value = savedPhone;
90
128
  }
91
129
  }
92
- return { token, phone, countryCode, isAuthenticated, initials, setToken, clearToken, initFromCookie };
130
+ return {
131
+ token,
132
+ phone,
133
+ countryCode,
134
+ isAuthenticated,
135
+ initials,
136
+ setToken,
137
+ clearToken,
138
+ initFromCookie
139
+ };
93
140
  });
94
141
 
95
- export { useAuthStore, useStoreProfileStore };
96
- //# sourceMappingURL=chunk-PWHIDGKW.js.map
97
- //# sourceMappingURL=chunk-PWHIDGKW.js.map
142
+ export { createStorefront, provideStorefront, useAuthStore, useRuntime, useStoreProfileStore };
143
+ //# sourceMappingURL=chunk-TB5TBJ4U.js.map
144
+ //# sourceMappingURL=chunk-TB5TBJ4U.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ports/runtime.ts","../src/stores/store-profile.ts","../src/stores/auth.ts"],"names":["defineStore","ref","computed"],"mappings":"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","file":"chunk-TB5TBJ4U.js","sourcesContent":["import { inject, provide, type App, type InjectionKey, type Plugin } from 'vue'\n\nimport type { StorefrontRuntime } from './index'\n\n// Composition root. Потребитель собирает рантайм (адаптеры) один раз и ставит плагин,\n// доменные composables берут его через useRuntime() вместо прямых Nuxt-вызовов.\n\nconst RUNTIME_KEY = Symbol.for('zenky.storefront.runtime') as InjectionKey<StorefrontRuntime>\n\ntype RequiredPort = {\n [K in keyof StorefrontRuntime]-?: undefined extends StorefrontRuntime[K] ? never : K\n}[keyof StorefrontRuntime]\n\nconst REQUIRED_PORTS = [\n 'sdk',\n 'config',\n 'storage',\n 'notifier',\n 'activity',\n 'messages',\n] as const satisfies readonly RequiredPort[]\n\ntype ListedPort = (typeof REQUIRED_PORTS)[number]\ntype UnlistedRequiredPort = Exclude<RequiredPort, ListedPort>\n\nconst _allRequiredPortsListed: UnlistedRequiredPort extends never ? true : never = true\nvoid _allRequiredPortsListed\n\nfunction assertRequiredPorts(runtime: StorefrontRuntime): void {\n const missing = REQUIRED_PORTS.filter((port) => runtime[port] == null)\n if (missing.length > 0) {\n throw new Error(\n `[@zenky/storefront-vue] Runtime is missing required ports: ${missing.join(', ')}. ` +\n 'See the Runtime Ports table in the README.'\n )\n }\n}\n\n/** Vue-плагин: app.use(createStorefront(runtime)). */\nexport function createStorefront(runtime: StorefrontRuntime): Plugin {\n assertRequiredPorts(runtime)\n\n return {\n install(app: App) {\n app.provide(RUNTIME_KEY, runtime)\n },\n }\n}\n\n/** Альтернатива плагину — provide внутри setup (напр. в изолированном виджете). */\nexport function provideStorefront(runtime: StorefrontRuntime): void {\n assertRequiredPorts(runtime)\n provide(RUNTIME_KEY, runtime)\n}\n\n/** Доступ к рантайму из любого composable/стора ядра. */\nexport function useRuntime(): StorefrontRuntime {\n const runtime = inject(RUNTIME_KEY)\n if (!runtime) {\n throw new Error(\n '[@zenky/storefront-vue] Runtime was not provided. ' +\n 'Install the plugin with app.use(createStorefront(runtime)).'\n )\n }\n return runtime\n}\n","import { defineStore } from 'pinia'\nimport { computed, ref, type ComputedRef, type Ref } from 'vue'\n\nimport type { City, NestedCategory, Stock, Store } from '@zenky/storefront-api'\n\nimport type { Website } from '../website-settings'\n\n// Именованный return-тип (правило dts) — иначе Pinia-инференс развернёт SDK-типы в .d.ts.\nexport interface StoreProfileStore {\n store: Ref<Store | null>\n city: Ref<City | null>\n stock: Ref<Stock | null>\n categories: Ref<NestedCategory[]>\n website: Ref<Website | null>\n initStore: (store: Store, city: City, stock: Stock | null) => void\n setStock: (stock: Stock | null) => void\n setCategories: (categories: NestedCategory[]) => void\n initWebsite: (website: Website | null) => void\n isReady: ComputedRef<boolean>\n hasStock: ComputedRef<boolean>\n currency: ComputedRef<string>\n squareLogoUrl: ComputedRef<string | null>\n}\n\nexport const useStoreProfileStore = defineStore('zenky-store-profile', (): StoreProfileStore => {\n const store = ref<Store | null>(null)\n const city = ref<City | null>(null)\n const stock = ref<Stock | null>(null)\n const categories = ref<NestedCategory[]>([])\n const website = ref<Website | null>(null)\n\n function initStore(newStore: Store, newCity: City, newStock: Stock | null): void {\n store.value = newStore\n city.value = newCity\n stock.value = newStock\n }\n\n function setStock(newStock: Stock | null): void {\n stock.value = newStock\n }\n\n function setCategories(newCategories: NestedCategory[]): void {\n categories.value = newCategories\n }\n\n function initWebsite(newWebsite: Website | null): void {\n website.value = newWebsite\n }\n\n const isReady = computed(() => store.value !== null && city.value !== null)\n const hasStock = computed(() => stock.value !== null)\n const currency = computed<string>(() => store.value?.settings?.currency ?? 'RUB')\n\n const squareLogoUrl = computed<string | null>(() => {\n const logo = store.value?.square_logo ?? store.value?.logo\n if (!logo) return null\n\n return (\n logo.hd?.url ??\n logo.xlarge?.url ??\n logo.large?.url ??\n logo.medium?.url ??\n logo.placeholder?.url ??\n null\n )\n })\n\n return {\n store,\n city,\n stock,\n categories,\n website,\n initStore,\n setStock,\n setCategories,\n initWebsite,\n isReady,\n hasStock,\n currency,\n squareLogoUrl,\n }\n})\n","import { defineStore } from 'pinia'\nimport { computed, ref, type ComputedRef, type Ref } from 'vue'\n\nimport { useRuntime } from '../ports/runtime'\n\n// Ключи совпадают с прежними CookieKey.AuthToken/AuthPhone — чтобы не разлогинить юзеров.\nconst AUTH_TOKEN_KEY = 'zenky_auth_token'\nconst AUTH_PHONE_KEY = 'zenky_auth_phone'\nconst YEAR_SECONDS = 60 * 60 * 24 * 365\n\n// Именованный return-тип (правило dts).\nexport interface AuthStore {\n token: Ref<string | null>\n phone: Ref<string | null>\n countryCode: Ref<string>\n isAuthenticated: ComputedRef<boolean>\n initials: ComputedRef<string>\n setToken: (token: string, phone?: string, countryCode?: string) => void\n clearToken: () => void\n initFromCookie: () => void\n}\n\nexport const useAuthStore = defineStore('zenky-auth', (): AuthStore => {\n // Рантайм захватываем на верхнем уровне setup (стор впервые создаётся в контексте\n // setup app.vue) — actions потом используют замыкание, не завися от inject после await.\n // SameSite/iframe-логика кук — целиком в cookie-адаптере приложения.\n const { storage, sdk } = useRuntime()\n\n const token = ref<string | null>(null)\n const phone = ref<string | null>(null)\n const countryCode = ref('RU')\n\n const isAuthenticated = computed(() => token.value !== null && token.value !== '')\n\n const initials = computed(() => {\n if (!phone.value) return '?'\n return phone.value.replace(/\\D/g, '').slice(-4)\n })\n\n function setToken(nextToken: string, nextPhone?: string, nextCountryCode?: string): void {\n token.value = nextToken\n storage.set(AUTH_TOKEN_KEY, nextToken, { maxAge: YEAR_SECONDS })\n sdk.setApiToken(nextToken)\n\n if (nextPhone) {\n phone.value = nextPhone\n storage.set(AUTH_PHONE_KEY, nextPhone, { maxAge: YEAR_SECONDS })\n }\n\n if (nextCountryCode) {\n countryCode.value = nextCountryCode\n }\n }\n\n function clearToken(): void {\n token.value = null\n phone.value = null\n storage.remove(AUTH_TOKEN_KEY)\n storage.remove(AUTH_PHONE_KEY)\n sdk.setApiToken(null)\n }\n\n function initFromCookie(): void {\n const savedToken = storage.get(AUTH_TOKEN_KEY)\n if (savedToken) {\n token.value = savedToken\n sdk.setApiToken(savedToken)\n }\n\n const savedPhone = storage.get(AUTH_PHONE_KEY)\n if (savedPhone) {\n phone.value = savedPhone\n }\n }\n\n return {\n token,\n phone,\n countryCode,\n isAuthenticated,\n initials,\n setToken,\n clearToken,\n initFromCookie,\n }\n})\n"]}
@@ -1,7 +1,5 @@
1
- import { usePromotionsStore } from './chunk-BWUKMWHL.js';
2
- import { useAuthStore, useStoreProfileStore } from './chunk-PWHIDGKW.js';
3
- import { useCartStore } from './chunk-VL3T36VM.js';
4
- import { useRuntime } from './chunk-V3VVDQIR.js';
1
+ import { useCartStore, usePromotionsStore } from './chunk-NIQFYJRP.js';
2
+ import { useRuntime, useAuthStore, useStoreProfileStore } from './chunk-TB5TBJ4U.js';
5
3
  import { ref } from 'vue';
6
4
 
7
5
  var KEY_ORDER_ID = "zenky_order_id";
@@ -107,5 +105,5 @@ function usePromotionsChecker() {
107
105
  }
108
106
 
109
107
  export { useCartCredentials, usePromotionsChecker };
110
- //# sourceMappingURL=chunk-4MJKN7PL.js.map
111
- //# sourceMappingURL=chunk-4MJKN7PL.js.map
108
+ //# sourceMappingURL=chunk-YREGCQSB.js.map
109
+ //# sourceMappingURL=chunk-YREGCQSB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cart/useCartCredentials.ts","../src/promotions/usePromotionsChecker.ts"],"names":[],"mappings":"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","file":"chunk-YREGCQSB.js","sourcesContent":["import { ref, type Ref } from 'vue'\n\nimport type { Order, OrderCredentials } from '@zenky/storefront-api'\n\nimport { useRuntime } from '../ports/runtime'\nimport { useAuthStore } from '../stores/auth'\nimport { useCartStore } from '../stores/cart'\nimport { useStoreProfileStore } from '../stores/store-profile'\n\nconst KEY_ORDER_ID = 'zenky_order_id'\nconst KEY_ORDER_TOKEN = 'zenky_order_token'\nconst WEEK_SECONDS = 60 * 60 * 24 * 7\n\n// app-энум OrderStatusKind.Pending = 'pending'.\nconst ORDER_STATUS_PENDING = 'pending'\n\nexport interface CartCredentials {\n orderIdCookie: Ref<string | null>\n orderTokenCookie: Ref<string | null>\n getStoreId: () => string\n getCityId: () => string\n getApiToken: () => string | undefined\n buildCredentials: (orderId: string, orderToken: string) => OrderCredentials\n getCredentials: () => OrderCredentials\n persistCredentials: (savedOrder: Order) => void\n clearPersistedCredentials: () => void\n isOrderActive: (order: Order) => boolean\n}\n\n/**\n * Управление credentials заказа. Персистенс order-id/token — через порт `storage`\n * (ядро не знает про useCookie). Рефы инициализируются из storage, persist пишет в оба.\n */\nexport function useCartCredentials(): CartCredentials {\n const { storage } = useRuntime()\n const cartStore = useCartStore()\n const authStore = useAuthStore()\n const profileStore = useStoreProfileStore()\n\n const orderIdRef = ref<string | null>(storage.get(KEY_ORDER_ID))\n const orderTokenRef = ref<string | null>(storage.get(KEY_ORDER_TOKEN))\n\n function getStoreId(): string {\n if (!profileStore.store) {\n throw new Error('Store is not initialized.')\n }\n\n return profileStore.store.id\n }\n\n function getCityId(): string {\n if (!profileStore.city) {\n throw new Error('City is not initialized.')\n }\n\n return profileStore.city.id\n }\n\n function getApiToken(): string | undefined {\n return authStore.isAuthenticated ? (authStore.token ?? undefined) : undefined\n }\n\n function buildCredentials(orderId: string, orderToken: string): OrderCredentials {\n const credentials: OrderCredentials = {\n id: orderId,\n token: orderToken,\n }\n\n const apiToken = getApiToken()\n\n if (apiToken) {\n credentials.api_token = apiToken\n }\n\n return credentials\n }\n\n function getCredentials(): OrderCredentials {\n if (!cartStore.credentials) {\n throw new Error('Cart has no credentials.')\n }\n\n return buildCredentials(cartStore.credentials.id, cartStore.credentials.token)\n }\n\n function persistCredentials(savedOrder: Order): void {\n orderIdRef.value = savedOrder.id\n orderTokenRef.value = savedOrder.token\n storage.set(KEY_ORDER_ID, savedOrder.id, { maxAge: WEEK_SECONDS })\n storage.set(KEY_ORDER_TOKEN, savedOrder.token, { maxAge: WEEK_SECONDS })\n }\n\n function clearPersistedCredentials(): void {\n orderIdRef.value = null\n orderTokenRef.value = null\n storage.remove(KEY_ORDER_ID)\n storage.remove(KEY_ORDER_TOKEN)\n }\n\n function isOrderActive(existingOrder: Order): boolean {\n if (!existingOrder.status) return true\n\n return String(existingOrder.status.kind) === ORDER_STATUS_PENDING\n }\n\n return {\n orderIdCookie: orderIdRef,\n orderTokenCookie: orderTokenRef,\n getStoreId,\n getCityId,\n getApiToken,\n buildCredentials,\n getCredentials,\n persistCredentials,\n clearPersistedCredentials,\n isOrderActive,\n }\n}\n","import { useRuntime } from '../ports/runtime'\nimport { useCartCredentials } from '../cart/useCartCredentials'\nimport { useCartStore } from '../stores/cart'\nimport { usePromotionsStore } from '../stores/promotions'\nimport { useStoreProfileStore } from '../stores/store-profile'\n\nexport interface PromotionsChecker {\n dispatchCheck: () => Promise<void>\n}\n\n/** Таймаут — если WS не ответил, сбросить флаг checking. */\nconst WS_RESPONSE_TIMEOUT_MS = 5000\n\n/**\n * Проверка промо-акций. Вызывает dispatchPromotionsChecker после изменения корзины.\n * Результат приходит через WebSocket (обрабатывается в useOrderChannel).\n */\nexport function usePromotionsChecker(): PromotionsChecker {\n const { sdk, events } = useRuntime()\n const cartStore = useCartStore()\n const profileStore = useStoreProfileStore()\n const promotionsStore = usePromotionsStore()\n const credentials = useCartCredentials()\n\n async function dispatchCheck(): Promise<void> {\n if (!cartStore.hasOrder || promotionsStore.checking) return\n\n const storeId = profileStore.store?.id\n if (!storeId) return\n\n promotionsStore.setChecking(true)\n promotionsStore.clearRewards() // Очистить старые — WS заново наполнит актуальные\n events?.get()?.promotionsCheckerStarted({ order: cartStore.order })\n\n try {\n const creds = credentials.getCredentials()\n await sdk.orders.dispatchPromotionsChecker(storeId, creds)\n\n setTimeout(() => {\n if (promotionsStore.checking) promotionsStore.setChecking(false)\n }, WS_RESPONSE_TIMEOUT_MS)\n } catch (e: unknown) {\n console.error('[promotions] check failed', e)\n promotionsStore.setChecking(false)\n }\n }\n\n return { dispatchCheck }\n}\n"]}
@@ -1,14 +1,12 @@
1
- import { useCartCredentials, usePromotionsChecker } from './chunk-4MJKN7PL.js';
2
- import { createRuntimeState } from './chunk-5QCUH3FF.js';
1
+ import { useCartCredentials, usePromotionsChecker } from './chunk-YREGCQSB.js';
3
2
  import { useCatalogStore } from './chunk-Z3IW4ODZ.js';
4
- import { usePromotionsStore } from './chunk-BWUKMWHL.js';
5
- import { useStoreProfileStore, useAuthStore } from './chunk-PWHIDGKW.js';
6
- import { getRemoteCatalog } from './chunk-EYRCAUUP.js';
7
- import { useCartStore, useOrderSettingsStore } from './chunk-VL3T36VM.js';
8
- import { useRuntime } from './chunk-V3VVDQIR.js';
3
+ import { useCartStore, useOrderSettingsStore, usePromotionsStore } from './chunk-NIQFYJRP.js';
4
+ import { getRemoteCatalog } from './chunk-E3G7NGCH.js';
9
5
  import { minorToMajor, majorToMinor } from './chunk-RO25KNM5.js';
6
+ import { createRuntimeState } from './chunk-5QCUH3FF.js';
7
+ import { useRuntime, useStoreProfileStore, useAuthStore } from './chunk-TB5TBJ4U.js';
10
8
  import { PaymentMethod, OrderAuthenticationMethod, DeliveryMethod, OrderCartCheckerResultAction, RecaptchaAction } from '@zenky/storefront-api';
11
- import { computed, ref, watch, watchEffect, getCurrentScope, onScopeDispose, toValue } from 'vue';
9
+ import { ref, computed, watch, watchEffect, getCurrentScope, onScopeDispose, toValue } from 'vue';
12
10
  import { storeToRefs } from 'pinia';
13
11
 
14
12
  // src/cart/constants.ts
@@ -95,16 +93,70 @@ function buildCashChangePresets({
95
93
  }
96
94
 
97
95
  // src/cart/logCartError.ts
96
+ var REDACTED = "[REDACTED]";
97
+ var MAX_LOG_DEPTH = 6;
98
+ var MAX_ARRAY_ITEMS = 25;
99
+ var MAX_OBJECT_KEYS = 50;
100
+ var SENSITIVE_KEY_PARTS = [
101
+ "authorization",
102
+ "cookie",
103
+ "credential",
104
+ "password",
105
+ "recaptcha",
106
+ "secret",
107
+ "token"
108
+ ];
109
+ var SENSITIVE_EXACT_KEYS = /* @__PURE__ */ new Set(["cvc", "cvv", "otp", "pan", "pin"]);
110
+ var SENSITIVE_ASSIGNMENT = /((?:authorization|cookie|password|recaptcha|secret|(?:api|access|auth|card|order|refresh)[_-]?token|token)\s*[=:]\s*["']?)([^\s,"';&}\]]+)/gi;
111
+ var SENSITIVE_QUERY_PARAM = /([?&#](?:authorization|password|recaptcha|secret|(?:api|access|auth|card|order|refresh)[_-]?token|token)=)([^&#\s]+)/gi;
112
+ var BEARER_TOKEN = /(bearer\s+)[a-z0-9._~+/=-]+/gi;
113
+ function isSensitiveKey(key) {
114
+ const normalized = key.replace(/[^a-z0-9]/gi, "").toLowerCase();
115
+ return SENSITIVE_EXACT_KEYS.has(normalized) || normalized.includes("confirmationcode") || normalized.includes("verificationcode") || SENSITIVE_KEY_PARTS.some((part) => normalized.includes(part));
116
+ }
117
+ function sanitizeString(value) {
118
+ return value.replace(BEARER_TOKEN, `$1${REDACTED}`).replace(SENSITIVE_QUERY_PARAM, `$1${REDACTED}`).replace(SENSITIVE_ASSIGNMENT, `$1${REDACTED}`);
119
+ }
120
+ function sanitizeLogValue(value, depth = 0, seen = /* @__PURE__ */ new WeakSet()) {
121
+ if (typeof value === "string") return sanitizeString(value);
122
+ if (value === null || typeof value !== "object") return value;
123
+ if (depth >= MAX_LOG_DEPTH) return "[TRUNCATED]";
124
+ if (seen.has(value)) return "[CIRCULAR]";
125
+ seen.add(value);
126
+ if (Array.isArray(value)) {
127
+ const sanitized = value.slice(0, MAX_ARRAY_ITEMS).map((item) => sanitizeLogValue(item, depth + 1, seen));
128
+ if (value.length > MAX_ARRAY_ITEMS) sanitized.push("[TRUNCATED]");
129
+ return sanitized;
130
+ }
131
+ const result = {};
132
+ if (value instanceof Error) {
133
+ result.name = sanitizeString(value.name);
134
+ result.message = sanitizeString(value.message);
135
+ }
136
+ let allEntries;
137
+ try {
138
+ allEntries = Object.entries(value);
139
+ } catch {
140
+ return "[UNSERIALIZABLE]";
141
+ }
142
+ const entries = allEntries.slice(0, MAX_OBJECT_KEYS);
143
+ for (const [key, nestedValue] of entries) {
144
+ if (key === "name" || key === "message") continue;
145
+ result[key] = isSensitiveKey(key) ? REDACTED : sanitizeLogValue(nestedValue, depth + 1, seen);
146
+ }
147
+ if (allEntries.length > MAX_OBJECT_KEYS) result.__truncated__ = true;
148
+ return result;
149
+ }
98
150
  function logCartError({ scope, context }, e) {
99
151
  const err = e?.err;
152
+ const safeContext = sanitizeLogValue(context);
100
153
  console.error(`[${scope}] failed`, {
101
- ...context ?? {},
154
+ ...safeContext ?? {},
102
155
  httpCode: err?.http_code,
103
156
  errorCode: err?.error_code,
104
- message: err?.message,
105
- meta: err?.meta,
106
- // raw — полный объект ошибки на случай нестандартных полей
107
- raw: e
157
+ message: sanitizeLogValue(err?.message),
158
+ meta: sanitizeLogValue(err?.meta),
159
+ error: sanitizeLogValue(e)
108
160
  });
109
161
  }
110
162
  function getCartErrorCode(e) {
@@ -115,7 +167,7 @@ function getCartErrorHttpCode(e) {
115
167
  }
116
168
  function getValidationErrors(e) {
117
169
  const err = e?.err;
118
- if (!err || err.error_code !== "validation") return {};
170
+ if (err?.error_code !== "validation") return {};
119
171
  const meta = err.meta;
120
172
  return meta?.errors ?? {};
121
173
  }
@@ -337,7 +389,7 @@ function useCartAddressSync() {
337
389
  const response = await sdk.customer.getAddresses(storeId, void 0, requestedToken);
338
390
  addresses = response.items ?? [];
339
391
  } catch (e) {
340
- console.error("[address-sync] failed to fetch saved addresses", e);
392
+ logCartError({ scope: "address-sync", context: { storeId } }, e);
341
393
  return;
342
394
  }
343
395
  if (!authStore.isAuthenticated || authStore.token !== requestedToken) return;
@@ -381,9 +433,13 @@ var ORDER_INCLUDES = [
381
433
  "variants.product.images",
382
434
  "variants.product.categories",
383
435
  "variants.modifiers.modifier",
384
- "variants.modifiers.modifiers_group"
436
+ "variants.modifiers.modifiers_group",
437
+ // Ограничения позиций (#32): is_blocking для сообщений в корзине и блокировки чекаута.
438
+ "variants.restrictions"
385
439
  ].join(",");
386
440
  var ORDER_WITH = { with: ORDER_INCLUDES };
441
+ var RUM_MARK_CART_INIT = "rum:cart:init";
442
+ var RUM_METRIC_CART_INIT = "rum_cart_init_seconds";
387
443
  var getInitializationState = createRuntimeState(() => ({ promise: null }));
388
444
  function useCartInitialization(credentials, sdk) {
389
445
  const cartStore = useCartStore();
@@ -406,10 +462,17 @@ function useCartInitialization(credentials, sdk) {
406
462
  const settings = await sdk.orders.getOrderSettings(storeId, orderCredentials);
407
463
  cartStore.setOrderSettings(settings);
408
464
  } catch (e) {
409
- console.error("[cart] Failed to load order settings", e);
465
+ logCartError(
466
+ {
467
+ scope: "cart-load-order-settings",
468
+ context: { orderId: cartStore.credentials?.id }
469
+ },
470
+ e
471
+ );
410
472
  }
411
473
  }
412
474
  async function _doInitialize() {
475
+ runtime.rum?.mark(RUM_MARK_CART_INIT);
413
476
  cartStore.setInitializing(true);
414
477
  try {
415
478
  if (credentials.orderIdCookie.value && credentials.orderTokenCookie.value) {
@@ -448,6 +511,7 @@ function useCartInitialization(credentials, sdk) {
448
511
  }
449
512
  }
450
513
  cartStore.setInitializing(false);
514
+ runtime.rum?.measure(RUM_METRIC_CART_INIT, RUM_MARK_CART_INIT);
451
515
  }
452
516
  }
453
517
  function initializeOrder() {
@@ -493,9 +557,13 @@ function useOrderBonuses(deps = {}) {
493
557
  activity.begin("bonuses");
494
558
  try {
495
559
  const orderCredentials = credentials.getCredentials();
496
- const updatedOrder = await sdk.orders.setOrderPayments(storeId, orderCredentials, {
497
- payments: [...additionalPayments, bonusesPayment]
498
- });
560
+ const updatedOrder = await sdk.orders.setOrderPayments(
561
+ storeId,
562
+ orderCredentials,
563
+ {
564
+ payments: [...additionalPayments, bonusesPayment]
565
+ }
566
+ );
499
567
  if (isStale()) return;
500
568
  if (Array.isArray(updatedOrder)) {
501
569
  cartStore.setOrderPaymentsState(updatedOrder);
@@ -527,9 +595,13 @@ function useOrderBonuses(deps = {}) {
527
595
  activity.begin("bonuses");
528
596
  try {
529
597
  const orderCredentials = credentials.getCredentials();
530
- const updatedOrder = await sdk.orders.setOrderPayments(storeId, orderCredentials, {
531
- payments: mainPayment ? [mainPayment] : []
532
- });
598
+ const updatedOrder = await sdk.orders.setOrderPayments(
599
+ storeId,
600
+ orderCredentials,
601
+ {
602
+ payments: mainPayment ? [mainPayment] : []
603
+ }
604
+ );
533
605
  if (isStale()) return;
534
606
  if (Array.isArray(updatedOrder)) {
535
607
  cartStore.setOrderPaymentsState(updatedOrder);
@@ -560,7 +632,7 @@ function useOrderBonuses(deps = {}) {
560
632
  }
561
633
  var PAYMENT_DEBOUNCE_MS = 300;
562
634
  function usePaymentSelection() {
563
- const { sdk, notifier, activity } = useRuntime();
635
+ const { sdk, notifier, messages, activity } = useRuntime();
564
636
  const credentials = useCartCredentials();
565
637
  const cartStore = useCartStore();
566
638
  const profileStore = useStoreProfileStore();
@@ -597,7 +669,7 @@ function usePaymentSelection() {
597
669
  },
598
670
  e
599
671
  );
600
- notifier.error(getCartErrorMessage(e, "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u0441\u043F\u043E\u0441\u043E\u0431 \u043E\u043F\u043B\u0430\u0442\u044B"));
672
+ notifier.error(getCartErrorMessage(e, messages.format("cart.payment.updateFailed")));
601
673
  } finally {
602
674
  activity.end("payment");
603
675
  }
@@ -644,6 +716,7 @@ function usePaymentSelection() {
644
716
  return { setPayment, syncPaymentNow, clearPayments };
645
717
  }
646
718
  function useBonusesReconciliation(deps) {
719
+ const { messages } = useRuntime();
647
720
  const cartStore = useCartStore();
648
721
  const profileStore = useStoreProfileStore();
649
722
  const { maxAvailable, apply, remove } = useOrderBonuses({ activity: deps.activity });
@@ -696,8 +769,8 @@ function useBonusesReconciliation(deps) {
696
769
  );
697
770
  }
698
771
  cartStore.setBonusesRequiresConfirmation(true);
699
- deps.notifier.message("\u0421\u043E\u0441\u0442\u0430\u0432 \u0437\u0430\u043A\u0430\u0437\u0430 \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u0441\u044F", {
700
- description: "\u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0435 \u0441\u043F\u0438\u0441\u0430\u043D\u0438\u0435 \u0431\u043E\u043D\u0443\u0441\u043E\u0432 \u043F\u043E\u0432\u0442\u043E\u0440\u043D\u043E"
772
+ deps.notifier.message(messages.format("cart.bonuses.changed.title"), {
773
+ description: messages.format("cart.bonuses.changed.description")
701
774
  });
702
775
  return;
703
776
  }
@@ -733,7 +806,7 @@ function useCartRecalculation(deps) {
733
806
  return { recalculateAfterOrderChange };
734
807
  }
735
808
  function useCustomerAttach(cache) {
736
- const { sdk, notifier, activity } = useRuntime();
809
+ const { sdk, notifier, messages, activity } = useRuntime();
737
810
  const credentials = useCartCredentials();
738
811
  const cartStore = useCartStore();
739
812
  const profileStore = useStoreProfileStore();
@@ -775,7 +848,7 @@ function useCustomerAttach(cache) {
775
848
  },
776
849
  e
777
850
  );
778
- notifier.error(getCartErrorMessage(e, "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0441\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C \u043A\u043E\u043D\u0442\u0430\u043A\u0442\u043D\u044B\u0435 \u0434\u0430\u043D\u043D\u044B\u0435"));
851
+ notifier.error(getCartErrorMessage(e, messages.format("cart.customer.saveFailed")));
779
852
  throw e;
780
853
  }
781
854
  }
@@ -787,7 +860,7 @@ function normalizePhone(phone) {
787
860
  return digits || null;
788
861
  }
789
862
  function useCartCustomerAttachment(cache) {
790
- const { sdk, notifier } = useRuntime();
863
+ const { sdk, notifier, messages } = useRuntime();
791
864
  const authStore = useAuthStore();
792
865
  const cartStore = useCartStore();
793
866
  const profileStore = useStoreProfileStore();
@@ -807,7 +880,6 @@ function useCartCustomerAttachment(cache) {
807
880
  await sdk.orders.setOrderCustomer(storeId, credentials, {
808
881
  phone: { number: authStore.phone ?? "", country: authStore.countryCode }
809
882
  });
810
- console.log("[cart] customer attached to order");
811
883
  const fresh = await sdk.orders.getOrder(storeId, credentials, ORDER_WITH);
812
884
  if (!authStore.isAuthenticated || authStore.token !== requestedToken) return;
813
885
  if (cartStore.order?.id !== requestedOrderId) return;
@@ -818,14 +890,20 @@ function useCartCustomerAttachment(cache) {
818
890
  if (previousPhone && newPhone && previousPhone !== newPhone) {
819
891
  const firstName = fresh.customer?.first_name;
820
892
  notifier.success(
821
- firstName ? `\u041A\u043E\u043D\u0442\u0430\u043A\u0442\u043D\u044B\u0435 \u0434\u0430\u043D\u043D\u044B\u0435 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u044B \u2014 ${firstName}` : "\u041A\u043E\u043D\u0442\u0430\u043A\u0442\u043D\u044B\u0435 \u0434\u0430\u043D\u043D\u044B\u0435 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u044B"
893
+ firstName ? messages.format("cart.customer.updatedWithName", { firstName }) : messages.format("cart.customer.updated")
822
894
  );
823
895
  }
824
896
  }
825
897
  } catch (e) {
826
- console.error("[cart] failed to attach customer to order", e);
898
+ logCartError(
899
+ {
900
+ scope: "cart-customer-attach",
901
+ context: { orderId: requestedOrderId }
902
+ },
903
+ e
904
+ );
827
905
  if (notifyOnPhoneChange) {
828
- notifier.error("\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u043A\u043E\u043D\u0442\u0430\u043A\u0442\u043D\u044B\u0435 \u0434\u0430\u043D\u043D\u044B\u0435 \u043F\u043E\u0441\u043B\u0435 \u0432\u0445\u043E\u0434\u0430");
906
+ notifier.error(messages.format("cart.customer.updateAfterLoginFailed"));
829
907
  }
830
908
  }
831
909
  }
@@ -843,7 +921,13 @@ function useCartCustomerAttachment(cache) {
843
921
  cartStore.setOrder(fresh);
844
922
  cache.clear();
845
923
  } catch (e) {
846
- console.error("[cart] failed to refresh order after logout", e);
924
+ logCartError(
925
+ {
926
+ scope: "cart-customer-refresh-after-logout",
927
+ context: { orderId: requestedOrderId }
928
+ },
929
+ e
930
+ );
847
931
  }
848
932
  }
849
933
  watch(
@@ -869,7 +953,7 @@ function useCartCustomerAttachment(cache) {
869
953
  var KEY_STOCK_ID = "zenky_stock_id";
870
954
  var YEAR_SECONDS = 60 * 60 * 24 * 365;
871
955
  function useCartStockWatcher() {
872
- const { sdk, notifier, storage, catalogReloader } = useRuntime();
956
+ const { sdk, notifier, messages, storage, catalogReloader } = useRuntime();
873
957
  const profileStore = useStoreProfileStore();
874
958
  const catalogStore = useCatalogStore();
875
959
  const cartStore = useCartStore();
@@ -904,7 +988,7 @@ function useCartStockWatcher() {
904
988
  },
905
989
  e
906
990
  );
907
- notifier.warning("\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u043A\u0430\u0442\u0430\u043B\u043E\u0433 \u0434\u043B\u044F \u0432\u044B\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u0441\u043A\u043B\u0430\u0434\u0430");
991
+ notifier.warning(messages.format("cart.stock.catalogReloadFailed"));
908
992
  }
909
993
  }
910
994
  async function checkCartProducts() {
@@ -916,10 +1000,10 @@ function useCartStockWatcher() {
916
1000
  const freshOrder = await sdk.orders.getOrder(storeId, orderCredentials, ORDER_WITH);
917
1001
  cartStore.setOrder(freshOrder);
918
1002
  await fetchTotal();
919
- notifier.warning("\u041D\u0435\u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0442\u043E\u0432\u0430\u0440\u044B \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B \u0438 \u0431\u044B\u043B\u0438 \u0443\u0434\u0430\u043B\u0435\u043D\u044B \u0438\u0437 \u043A\u043E\u0440\u0437\u0438\u043D\u044B");
1003
+ notifier.warning(messages.format("cart.stock.itemsRemoved"));
920
1004
  } else if (result.order_variants.length > 0) {
921
1005
  await fetchTotal();
922
- notifier.warning("\u0426\u0435\u043D\u044B \u043D\u0435\u043A\u043E\u0442\u043E\u0440\u044B\u0445 \u0442\u043E\u0432\u0430\u0440\u043E\u0432 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u044B");
1006
+ notifier.warning(messages.format("cart.stock.pricesUpdated"));
923
1007
  }
924
1008
  } catch (e) {
925
1009
  logCartError(
@@ -929,7 +1013,7 @@ function useCartStockWatcher() {
929
1013
  },
930
1014
  e
931
1015
  );
932
- notifier.warning("\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E\u0441\u0442\u044C \u0442\u043E\u0432\u0430\u0440\u043E\u0432 \u0432 \u043A\u043E\u0440\u0437\u0438\u043D\u0435");
1016
+ notifier.warning(messages.format("cart.stock.checkFailed"));
933
1017
  }
934
1018
  }
935
1019
  return { handleStockChange };
@@ -1158,11 +1242,14 @@ function useOrderPromocode(deps = {}) {
1158
1242
  }
1159
1243
  });
1160
1244
  } catch (e) {
1161
- errorMessage.value = getCartErrorMessage(e, "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u044C \u043F\u0440\u043E\u043C\u043E\u043A\u043E\u0434");
1245
+ errorMessage.value = getCartErrorMessage(
1246
+ e,
1247
+ runtime.messages.format("cart.promocode.applyFailed")
1248
+ );
1162
1249
  logCartError(
1163
1250
  {
1164
1251
  scope: "promocode-apply",
1165
- context: { code: trimmed, orderId: cartStore.credentials?.id }
1252
+ context: { codeLength: trimmed.length, orderId: cartStore.credentials?.id }
1166
1253
  },
1167
1254
  e
1168
1255
  );
@@ -1190,12 +1277,15 @@ function useOrderPromocode(deps = {}) {
1190
1277
  }
1191
1278
  });
1192
1279
  } catch (e) {
1193
- errorMessage.value = getCartErrorMessage(e, "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0443\u0431\u0440\u0430\u0442\u044C \u043F\u0440\u043E\u043C\u043E\u043A\u043E\u0434");
1280
+ errorMessage.value = getCartErrorMessage(
1281
+ e,
1282
+ runtime.messages.format("cart.promocode.removeFailed")
1283
+ );
1194
1284
  logCartError(
1195
1285
  {
1196
1286
  scope: "promocode-remove",
1197
1287
  context: {
1198
- code: applied.value?.promocode,
1288
+ codeLength: applied.value?.promocode?.length,
1199
1289
  orderId: cartStore.credentials?.id
1200
1290
  }
1201
1291
  },
@@ -1293,7 +1383,13 @@ function useCartDeliveryAttachment() {
1293
1383
  if (!recovered) orderSettings.setSelectedAddress(null, null, null);
1294
1384
  return;
1295
1385
  }
1296
- console.error("[cart-delivery-attach] failed", result.error);
1386
+ logCartError(
1387
+ {
1388
+ scope: "cart-delivery-attach",
1389
+ context: { orderId: cartStore.credentials?.id }
1390
+ },
1391
+ result.error
1392
+ );
1297
1393
  }
1298
1394
  }
1299
1395
  watch(
@@ -1319,7 +1415,7 @@ var getOperationsState = createRuntimeState(() => ({
1319
1415
  function useCartOperations(credentials, initialization, sdk, deps) {
1320
1416
  const { notifier, activity } = deps;
1321
1417
  const runtime = useRuntime();
1322
- const { events, rum, commerceEvents } = runtime;
1418
+ const { events, rum, commerceEvents, messages } = runtime;
1323
1419
  const { batches, inflight } = getOperationsState(runtime);
1324
1420
  const cartStore = useCartStore();
1325
1421
  const profileStore = useStoreProfileStore();
@@ -1438,7 +1534,9 @@ function useCartOperations(credentials, initialization, sdk, deps) {
1438
1534
  await recalculateAfterOrderChange();
1439
1535
  if (isAdd && productName) {
1440
1536
  const parts = [productName, variantLabel].filter(Boolean);
1441
- notifier.message("\u0414\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u043E:", { description: parts.join(", ") });
1537
+ notifier.message(messages.format("cart.added.title"), {
1538
+ description: parts.join(", ")
1539
+ });
1442
1540
  }
1443
1541
  });
1444
1542
  } catch (e) {
@@ -1463,7 +1561,7 @@ function useCartOperations(credentials, initialization, sdk, deps) {
1463
1561
  } else {
1464
1562
  integrations?.productQuantityDecreaseFailed({ order: cartStore.order });
1465
1563
  }
1466
- notifier.error(getCartErrorMessage(e, "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u043A\u043E\u0440\u0437\u0438\u043D\u0443"));
1564
+ notifier.error(getCartErrorMessage(e, messages.format("cart.updateFailed")));
1467
1565
  try {
1468
1566
  await refreshCart({ silent: true });
1469
1567
  } catch (refreshErr) {
@@ -1835,7 +1933,86 @@ function useOrderChannel() {
1835
1933
  });
1836
1934
  }
1837
1935
  }
1936
+ var getGateState = createRuntimeState(() => ({
1937
+ isOpen: ref(false),
1938
+ blocking: ref([]),
1939
+ product: ref(null),
1940
+ resolver: null
1941
+ }));
1942
+ function useRestrictionGate() {
1943
+ const state = getGateState(useRuntime());
1944
+ function settle(confirmed) {
1945
+ state.isOpen.value = false;
1946
+ const resolve = state.resolver;
1947
+ state.resolver = null;
1948
+ resolve?.(confirmed);
1949
+ }
1950
+ function confirmBlocking(items, gateProduct) {
1951
+ settle(false);
1952
+ state.blocking.value = items;
1953
+ state.product.value = gateProduct;
1954
+ state.isOpen.value = true;
1955
+ return new Promise((resolve) => {
1956
+ state.resolver = resolve;
1957
+ });
1958
+ }
1959
+ return {
1960
+ isOpen: state.isOpen,
1961
+ blocking: state.blocking,
1962
+ product: state.product,
1963
+ confirmBlocking,
1964
+ confirm: () => settle(true),
1965
+ cancel: () => settle(false)
1966
+ };
1967
+ }
1968
+
1969
+ // src/cart/useRestrictedAddToCart.ts
1970
+ function useRestrictedAddToCart() {
1971
+ const { sdk } = useRuntime();
1972
+ const profileStore = useStoreProfileStore();
1973
+ const { addToCart } = useStorefrontCart();
1974
+ const { confirmBlocking, isOpen: gateIsOpen } = useRestrictionGate();
1975
+ async function checkRestrictionsAllowed(product, alreadyInCart) {
1976
+ if (alreadyInCart || !product.restrictions?.length) return true;
1977
+ const storeId = profileStore.store?.id;
1978
+ const cityId = profileStore.city?.id;
1979
+ if (!storeId || !cityId) return true;
1980
+ let blocking = [];
1981
+ try {
1982
+ const url = sdk.client.getStoreUrl(storeId, `/products/${product.id}/restrictions`, {
1983
+ city_id: cityId,
1984
+ stock_id: profileStore.stock?.id ?? void 0
1985
+ });
1986
+ const response = await sdk.client.request("GET", url);
1987
+ blocking = (response?.data ?? []).filter((item) => item.is_blocking);
1988
+ } catch (error) {
1989
+ logCartError({ scope: "restrictions-check", context: { productId: product.id } }, error);
1990
+ return true;
1991
+ }
1992
+ if (!blocking.length) return true;
1993
+ if (gateIsOpen.value) return false;
1994
+ return confirmBlocking(blocking, {
1995
+ name: product.name ?? "",
1996
+ image: product.images?.[0] ?? null
1997
+ });
1998
+ }
1999
+ async function addWithRestrictionCheck(product, request, alreadyInCart) {
2000
+ if (await checkRestrictionsAllowed(product, alreadyInCart)) {
2001
+ await addToCart(request);
2002
+ }
2003
+ }
2004
+ return { checkRestrictionsAllowed, addWithRestrictionCheck };
2005
+ }
2006
+ function useVisibleCartVariants() {
2007
+ const cartStore = useCartStore();
2008
+ return computed(
2009
+ () => cartStore.variants.filter((variant) => {
2010
+ const delta = cartStore.lineOptimistic[variantLineKey(variant)] ?? 0;
2011
+ return variant.quantity + delta > 0;
2012
+ })
2013
+ );
2014
+ }
1838
2015
 
1839
- export { CASH_DENOMINATIONS_MAJOR, DEFAULT_ROUND_STEPS_MAJOR, ORDER_EVENTS, ORDER_WITH, buildCartLineKey, buildCashChangePresets, buildMainPaymentRequest, getCartErrorCode, getCartErrorHttpCode, getCartErrorMessage, getCashDenominationsMinor, getDefaultRoundStepsMinor, getValidationErrors, logCartError, normalizeAddress, useBonusesReconciliation, useCartAddressSync, useCartCustomerAttachment, useCartDeliveryAttachment, useCartInitialization, useCartMutation, useCartOperations, useCartQuantities, useCartRecalculation, useCartStockWatcher, useCheckoutFlow, useCheckoutSubmit, useCustomerAttach, useOnlinePaymentRetry, useOrderBonuses, useOrderChannel, useOrderDelivery, useOrderPromocode, useOrderSettings, useOrderTotal, usePaymentSelection, useStorefrontCart, variantLineKey };
1840
- //# sourceMappingURL=chunk-OQLU7FSR.js.map
1841
- //# sourceMappingURL=chunk-OQLU7FSR.js.map
2016
+ export { CASH_DENOMINATIONS_MAJOR, DEFAULT_ROUND_STEPS_MAJOR, ORDER_EVENTS, ORDER_WITH, buildCartLineKey, buildCashChangePresets, buildMainPaymentRequest, getCartErrorCode, getCartErrorHttpCode, getCartErrorMessage, getCashDenominationsMinor, getDefaultRoundStepsMinor, getValidationErrors, logCartError, normalizeAddress, useBonusesReconciliation, useCartAddressSync, useCartCustomerAttachment, useCartDeliveryAttachment, useCartInitialization, useCartMutation, useCartOperations, useCartQuantities, useCartRecalculation, useCartStockWatcher, useCheckoutFlow, useCheckoutSubmit, useCustomerAttach, useOnlinePaymentRetry, useOrderBonuses, useOrderChannel, useOrderDelivery, useOrderPromocode, useOrderSettings, useOrderTotal, usePaymentSelection, useRestrictedAddToCart, useRestrictionGate, useStorefrontCart, useVisibleCartVariants, variantLineKey };
2017
+ //# sourceMappingURL=chunk-YS5DXYYV.js.map
2018
+ //# sourceMappingURL=chunk-YS5DXYYV.js.map