@zenky/storefront-vue 0.10.0 → 0.10.3

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 (54) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +52 -2
  3. package/dist/cart/index.d.ts +31 -2
  4. package/dist/cart/index.js +5 -6
  5. package/dist/catalog/index.js +2 -2
  6. package/dist/{chunk-BIGTKVC6.js → chunk-57IOMDAM.js} +3 -3
  7. package/dist/{chunk-BIGTKVC6.js.map → chunk-57IOMDAM.js.map} +1 -1
  8. package/dist/{chunk-DKWDKVXQ.js → chunk-7FRMMCR5.js} +12 -10
  9. package/dist/chunk-7FRMMCR5.js.map +1 -0
  10. package/dist/{chunk-EYRCAUUP.js → chunk-E3G7NGCH.js} +2 -2
  11. package/dist/chunk-E3G7NGCH.js.map +1 -0
  12. package/dist/{chunk-VL3T36VM.js → chunk-FFBZKQRV.js} +14 -7
  13. package/dist/chunk-FFBZKQRV.js.map +1 -0
  14. package/dist/{chunk-OQLU7FSR.js → chunk-G2X4SBCO.js} +199 -29
  15. package/dist/chunk-G2X4SBCO.js.map +1 -0
  16. package/dist/{chunk-AL5W5JSD.js → chunk-GLTOJTZO.js} +2 -2
  17. package/dist/{chunk-AL5W5JSD.js.map → chunk-GLTOJTZO.js.map} +1 -1
  18. package/dist/{chunk-IGDDWGS4.js → chunk-JLFBAAFT.js} +20 -11
  19. package/dist/chunk-JLFBAAFT.js.map +1 -0
  20. package/dist/{chunk-PJ2LEKW6.js → chunk-MZ66ARDL.js} +18 -3
  21. package/dist/chunk-MZ66ARDL.js.map +1 -0
  22. package/dist/{chunk-4MJKN7PL.js → chunk-N3NOTJUC.js} +4 -4
  23. package/dist/{chunk-4MJKN7PL.js.map → chunk-N3NOTJUC.js.map} +1 -1
  24. package/dist/{chunk-2OD7CLKD.js → chunk-RYYY7MVP.js} +2 -2
  25. package/dist/chunk-RYYY7MVP.js.map +1 -0
  26. package/dist/{chunk-PWHIDGKW.js → chunk-TEG7EL4N.js} +12 -3
  27. package/dist/chunk-TEG7EL4N.js.map +1 -0
  28. package/dist/index.d.ts +1 -1
  29. package/dist/index.js +11 -11
  30. package/dist/layout/index.js +8 -4
  31. package/dist/layout/index.js.map +1 -1
  32. package/dist/order/index.js.map +1 -1
  33. package/dist/pagination/index.js.map +1 -1
  34. package/dist/ports/index.js +1 -1
  35. package/dist/products/index.js +2 -2
  36. package/dist/promotions/index.js +3 -3
  37. package/dist/seo/index.js +5 -5
  38. package/dist/seo/index.js.map +1 -1
  39. package/dist/snippets/index.js +1 -1
  40. package/dist/stores/index.js +3 -3
  41. package/dist/utils/index.d.ts +2 -2
  42. package/dist/utils/index.js +1 -1
  43. package/docs/consumer-setup.md +275 -0
  44. package/docs/ports.md +144 -0
  45. package/docs/releasing.md +46 -0
  46. package/package.json +26 -4
  47. package/dist/chunk-2OD7CLKD.js.map +0 -1
  48. package/dist/chunk-DKWDKVXQ.js.map +0 -1
  49. package/dist/chunk-EYRCAUUP.js.map +0 -1
  50. package/dist/chunk-IGDDWGS4.js.map +0 -1
  51. package/dist/chunk-OQLU7FSR.js.map +0 -1
  52. package/dist/chunk-PJ2LEKW6.js.map +0 -1
  53. package/dist/chunk-PWHIDGKW.js.map +0 -1
  54. package/dist/chunk-VL3T36VM.js.map +0 -1
package/CHANGELOG.md ADDED
@@ -0,0 +1,46 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and releases follow
5
+ [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## Unreleased
8
+
9
+ ### Added
10
+
11
+ - Pull-request CI for formatting, linting, types, coverage, tests, package exports,
12
+ Node/runtime compatibility, bundle budgets, and secret scanning.
13
+ - Dependabot updates for npm dependencies and GitHub Actions.
14
+ - Public security, contribution, conduct, runtime-port, and release documentation.
15
+ - Coverage ratchet and regression tests for checkout state and credential-safe logging.
16
+ - Cart composables for evaluating product restrictions, confirming blocking
17
+ restrictions, and reading variants that remain visible after optimistic updates.
18
+
19
+ ### Changed
20
+
21
+ - GitHub Actions are pinned to immutable commits with minimum workflow permissions.
22
+ - QR-code encoding now uses Web Platform APIs and works without Node `Buffer` or browser
23
+ globals, improving Node SSR and edge-runtime compatibility.
24
+ - npm package metadata now includes its homepage, issue tracker, and supported engines.
25
+
26
+ ### Fixed
27
+
28
+ - Empty post-checkout orders no longer trigger a minimum-order-price violation.
29
+
30
+ ### Security
31
+
32
+ - Cart and checkout errors now pass through recursive redaction before being logged.
33
+ - Direct cart logging no longer emits complete order, credentials, request, or API error
34
+ objects.
35
+
36
+ ## 0.10.0 - 2026-07-21
37
+
38
+ ### Added
39
+
40
+ - GitHub release workflow for npm OIDC trusted publishing.
41
+
42
+ ### Changed
43
+
44
+ - Package renamed to `@zenky/storefront-vue`.
45
+ - Storefront runtime state and cart flows were isolated across Vue applications and SSR
46
+ requests.
package/README.md CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  Headless storefront engine for Zenky — Vue 3 + Pinia, no framework lock-in.
4
4
 
5
+ [![CI](https://github.com/zenky/storefront-vue/actions/workflows/ci.yml/badge.svg)](https://github.com/zenky/storefront-vue/actions/workflows/ci.yml)
6
+ [![npm](https://img.shields.io/npm/v/@zenky/storefront-vue.svg)](https://www.npmjs.com/package/@zenky/storefront-vue)
7
+
5
8
  One engine behind many storefronts. The cart, order building, checkout, catalog resolution, delivery slots, SEO/JSON-LD, theme, and store/profile state all live here as framework-agnostic composables and Pinia stores. The consumer — a Nuxt site, a QR-code menu, a new template — brings the framework glue (cookies, config, toasts, HTTP transport, `useHead`) and draws its own UI on top.
6
9
 
7
10
  The design is Ports & Adapters: the core declares small ports for everything it needs from the outside world, the consumer provides adapters once, and every composable reads them through `useRuntime()`. The core never imports `useCookie`, `useRoute`, `useHead`, `$fetch`, or anything Nuxt-specific.
@@ -18,7 +21,7 @@ Peer dependencies (the consumer already has these):
18
21
  {
19
22
  "vue": "^3.5.0",
20
23
  "pinia": "^3.0.0",
21
- "@zenky/storefront-api": ">=0.0.64",
24
+ "@zenky/storefront-api": ">=0.0.68",
22
25
  "@zenky/integrations-sdk": ">=0.1.3"
23
26
  }
24
27
  ```
@@ -96,6 +99,10 @@ import {
96
99
  } from '@zenky/storefront-vue/ports'
97
100
  ```
98
101
 
102
+ The full public contract, adapter examples, SSR lifecycle, and security boundary are in
103
+ [Runtime Ports](docs/ports.md). For a step-by-step consumer integration and upgrade
104
+ checklist, see [Consumer Setup](docs/consumer-setup.md).
105
+
99
106
  ## Subpath Imports
100
107
 
101
108
  Each public domain is its own entry point, so bundlers only pull in what you import.
@@ -113,6 +120,31 @@ import { usePaginatedList } from '@zenky/storefront-vue/pagination'
113
120
 
114
121
  Available subpaths: `ports`, `money`, `utils`, `theme`, `stores`, `products`, `catalog`, `cart`, `order`, `promotions`, `seo`, `layout`, `pagination`, `snippets`, `website-settings`.
115
122
 
123
+ ## Runtime Compatibility
124
+
125
+ - ESM-only, targeting ES2022.
126
+ - Vue 3.5+ and Pinia 3.
127
+ - Node.js 20.19+ for SSR and package tooling; Node.js 22 is recommended for production and development.
128
+ - Modern browsers and edge runtimes with standard Web Platform APIs.
129
+
130
+ CI imports every public entry in Node SSR and scans the emitted ESM for CommonJS, Node
131
+ built-ins, `Buffer`, and `process`. Browser-only behavior is guarded; for example,
132
+ online-payment retry accepts an explicit `currentUrl` when no `window` is available.
133
+
134
+ For SSR, create a new Vue app, Pinia instance, storefront runtime, and storage adapter per
135
+ request. Never share `createMemoryStorage()` between users.
136
+
137
+ ## Package Size
138
+
139
+ Every domain has a tree-shakeable subpath. CI measures the transitive gzip graph for the
140
+ root, cart, and utils entries plus all emitted JavaScript. The budgets are regression
141
+ ceilings, not size targets; increasing one requires review and a release-note explanation.
142
+
143
+ ```bash
144
+ npm run build
145
+ npm run check:size
146
+ ```
147
+
116
148
  ## Pinia Stores
117
149
 
118
150
  The `stores` subpath exposes setup-style Pinia stores (ids namespaced `zenky-*`): cart, order settings, store profile, catalog, products, auth, promotions. They are plain state containers — no API calls, no UI logic — and they are the reactive backbone the composables read from. Install Pinia before mounting the storefront plugin.
@@ -138,9 +170,15 @@ majorToMinor(650, 'RUB') // 65000 — user input back into the store
138
170
  ```bash
139
171
  npm run dev # tsup --watch
140
172
  npm run build # ESM + .d.ts to dist/
173
+ npm run lint # Biome lint
174
+ npm run format # Biome format --write
141
175
  npm run typecheck # tsc --noEmit
142
176
  npm test # vitest
177
+ npm run test:coverage # vitest + V8 coverage report
178
+ npm run check # format + lint + types + tests + build + exports
143
179
  npm run check:exports # publint + are-the-types-wrong (esm-only profile)
180
+ npm run check:runtime # import all entries in SSR + reject Node-only output
181
+ npm run check:size # enforce gzip bundle budgets
144
182
  ```
145
183
 
146
184
  ### Local linking with yalc
@@ -165,7 +203,19 @@ npm run build && npx yalc push
165
203
  - `test` / `test:watch` — run the Vitest suite.
166
204
  - `typecheck` — type-only compile.
167
205
  - `check:exports` — validate the exports map and published types.
168
- - `prepublishOnly` — typecheck → test → build → check:exports.
206
+ - `check:runtime` — verify Node SSR imports and browser/edge-safe ESM output.
207
+ - `check:size` — enforce root/domain/all-output gzip budgets.
208
+ - `check:ci` — quality gate with the V8 coverage ratchet.
209
+ - `prepublishOnly` — run the release quality gate.
210
+
211
+ ## Contributing and Releases
212
+
213
+ Read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request. User-visible changes
214
+ belong in [CHANGELOG.md](CHANGELOG.md); maintainer steps are documented in the
215
+ [release process](docs/releasing.md).
216
+
217
+ Security vulnerabilities must be reported privately according to
218
+ [SECURITY.md](SECURITY.md), never through a public issue.
169
219
 
170
220
  ## License
171
221
 
@@ -1,4 +1,4 @@
1
- import { ProductModifiersRequest, OrderProductVariant, PaymentMethod, OrderPaymentMethodRequest, OrderAuthenticationMethod, OrderCredentials, Order, OrderCheckoutTotal, OrderSettings, OrderPaymentMethod, OrderDeliveryMethod, InclusionRequest, ZenkyStorefront, SetOrderCustomerRequest, SetOrderDeliveryRequest, OrderPromocode, OrderCheckoutResult, RecaptchaAction } from '@zenky/storefront-api';
1
+ import { ProductModifiersRequest, OrderProductVariant, PaymentMethod, OrderPaymentMethodRequest, OrderAuthenticationMethod, OrderCredentials, Order, OrderCheckoutTotal, OrderSettings, OrderPaymentMethod, OrderDeliveryMethod, InclusionRequest, ZenkyStorefront, SetOrderCustomerRequest, SetOrderDeliveryRequest, OrderPromocode, OrderCheckoutResult, RecaptchaAction, EvaluatedRestriction, Media, Product } from '@zenky/storefront-api';
2
2
  import { ComputedRef, MaybeRefOrGetter, Ref } from 'vue';
3
3
  import { ActivityReporter, Notifier, Result } from '../ports/index.js';
4
4
  import '@zenky/integrations-sdk';
@@ -556,4 +556,33 @@ interface StorefrontCart {
556
556
  }
557
557
  declare function useStorefrontCart(): StorefrontCart;
558
558
 
559
- export { type AttachedCustomerCache, type BonusesReconciliation, type BonusesReconciliationDeps, CASH_DENOMINATIONS_MAJOR, type CachedCustomerView, type CartCredentials, type CartDependencies, type CartErrorScope, type CartInitialization, type CartItemRequest, type CartLineKeyParams, type CartMutationComposable, type CartOperations, type CartQuantities, type CartRecalculation, type CartRecalculationDeps, type CartStockWatcher, type CashChangePresetsOptions, type CheckoutFlowFlags, type CheckoutSubmitComposable, type CustomerAttachComposable, DEFAULT_ROUND_STEPS_MAJOR, type MainPaymentRequestParams, type MutationKind, type MutationOptions, ORDER_EVENTS, ORDER_WITH, type OrderBonusesComposable, type OrderBonusesDeps, type OrderCredentialsLike, type OrderDelivery, type OrderDeliveryDeps, type OrderPromocodeComposable, type OrderPromocodeDeps, type OrderSettingsComposable, type OrderTotal, type PaymentSelection, type PaymentSelectionComposable, type RecaptchaPayloadProvider, type RetryOptions, type StorefrontCart, buildCartLineKey, buildCashChangePresets, buildMainPaymentRequest, getCartErrorCode, getCartErrorHttpCode, getCartErrorMessage, getCashDenominationsMinor, getDefaultRoundStepsMinor, getValidationErrors, logCartError, normalizeAddress, useBonusesReconciliation, useCartAddressSync, useCartCredentials, useCartCustomerAttachment, useCartDeliveryAttachment, useCartInitialization, useCartMutation, useCartOperations, useCartQuantities, useCartRecalculation, useCartStockWatcher, useCheckoutFlow, useCheckoutSubmit, useCustomerAttach, useOnlinePaymentRetry, useOrderBonuses, useOrderChannel, useOrderDelivery, useOrderPromocode, useOrderSettings, useOrderTotal, usePaymentSelection, useStorefrontCart, variantLineKey };
559
+ /** Данные товара для показа в модалке гейта (как строка корзины). */
560
+ interface RestrictionGateProduct {
561
+ name: string;
562
+ image: Media | null;
563
+ }
564
+ interface RestrictionGate {
565
+ isOpen: Ref<boolean>;
566
+ blocking: Ref<EvaluatedRestriction[]>;
567
+ product: Ref<RestrictionGateProduct | null>;
568
+ confirmBlocking: (items: EvaluatedRestriction[], gateProduct: RestrictionGateProduct) => Promise<boolean>;
569
+ confirm: () => void;
570
+ cancel: () => void;
571
+ }
572
+ declare function useRestrictionGate(): RestrictionGate;
573
+
574
+ type RestrictedProduct = Pick<Product, 'id' | 'restrictions' | 'name' | 'images'>;
575
+ interface RestrictedAddToCart {
576
+ checkRestrictionsAllowed: (product: RestrictedProduct, alreadyInCart: boolean) => Promise<boolean>;
577
+ addWithRestrictionCheck: (product: RestrictedProduct, request: CartItemRequest, alreadyInCart: boolean) => Promise<void>;
578
+ }
579
+ declare function useRestrictedAddToCart(): RestrictedAddToCart;
580
+
581
+ /**
582
+ * Позиции корзины, видимые покупателю: серверное количество с учётом оптимистичной дельты.
583
+ * Единый источник для списка, футера и подсветки — иначе блокировка чекаута, поиск
584
+ * блокирующей позиции и реальный рендер списка могут разъехаться.
585
+ */
586
+ declare function useVisibleCartVariants(): ComputedRef<OrderProductVariant[]>;
587
+
588
+ export { type AttachedCustomerCache, type BonusesReconciliation, type BonusesReconciliationDeps, CASH_DENOMINATIONS_MAJOR, type CachedCustomerView, type CartCredentials, type CartDependencies, type CartErrorScope, type CartInitialization, type CartItemRequest, type CartLineKeyParams, type CartMutationComposable, type CartOperations, type CartQuantities, type CartRecalculation, type CartRecalculationDeps, type CartStockWatcher, type CashChangePresetsOptions, type CheckoutFlowFlags, type CheckoutSubmitComposable, type CustomerAttachComposable, DEFAULT_ROUND_STEPS_MAJOR, type MainPaymentRequestParams, type MutationKind, type MutationOptions, ORDER_EVENTS, ORDER_WITH, type OrderBonusesComposable, type OrderBonusesDeps, type OrderCredentialsLike, type OrderDelivery, type OrderDeliveryDeps, type OrderPromocodeComposable, type OrderPromocodeDeps, type OrderSettingsComposable, type OrderTotal, type PaymentSelection, type PaymentSelectionComposable, type RecaptchaPayloadProvider, type RestrictedAddToCart, type RestrictionGate, type RestrictionGateProduct, type RetryOptions, type StorefrontCart, buildCartLineKey, buildCashChangePresets, buildMainPaymentRequest, getCartErrorCode, getCartErrorHttpCode, getCartErrorMessage, getCashDenominationsMinor, getDefaultRoundStepsMinor, getValidationErrors, logCartError, normalizeAddress, useBonusesReconciliation, useCartAddressSync, useCartCredentials, useCartCustomerAttachment, useCartDeliveryAttachment, useCartInitialization, useCartMutation, useCartOperations, useCartQuantities, useCartRecalculation, useCartStockWatcher, useCheckoutFlow, useCheckoutSubmit, useCustomerAttach, useOnlinePaymentRetry, useOrderBonuses, useOrderChannel, useOrderDelivery, useOrderPromocode, useOrderSettings, useOrderTotal, usePaymentSelection, useRestrictedAddToCart, useRestrictionGate, useStorefrontCart, useVisibleCartVariants, variantLineKey };
@@ -1,13 +1,12 @@
1
- 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 } from '../chunk-OQLU7FSR.js';
2
- export { useCartCredentials } from '../chunk-4MJKN7PL.js';
1
+ 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 } from '../chunk-G2X4SBCO.js';
2
+ export { useCartCredentials } from '../chunk-N3NOTJUC.js';
3
3
  import '../chunk-5QCUH3FF.js';
4
4
  import '../chunk-Z3IW4ODZ.js';
5
5
  import '../chunk-BWUKMWHL.js';
6
- import '../chunk-PWHIDGKW.js';
7
- import '../chunk-EYRCAUUP.js';
8
- import '../chunk-VL3T36VM.js';
6
+ import '../chunk-TEG7EL4N.js';
7
+ import '../chunk-E3G7NGCH.js';
8
+ import '../chunk-FFBZKQRV.js';
9
9
  import '../chunk-V3VVDQIR.js';
10
10
  import '../chunk-RO25KNM5.js';
11
- import '../chunk-AL5W5JSD.js';
12
11
  //# sourceMappingURL=index.js.map
13
12
  //# sourceMappingURL=index.js.map
@@ -1,4 +1,4 @@
1
- export { catalogRoutes, collectDescendantIds, findCategoryById, findCategoryByShortId, findRootAncestor, getCategoryPath, groupProductsBySubcategory, useCollections } from '../chunk-2OD7CLKD.js';
2
- export { getCity, getRemoteCatalog, getStock } from '../chunk-EYRCAUUP.js';
1
+ export { catalogRoutes, collectDescendantIds, findCategoryById, findCategoryByShortId, findRootAncestor, getCategoryPath, groupProductsBySubcategory, useCollections } from '../chunk-RYYY7MVP.js';
2
+ export { getCity, getRemoteCatalog, getStock } from '../chunk-E3G7NGCH.js';
3
3
  //# sourceMappingURL=index.js.map
4
4
  //# sourceMappingURL=index.js.map
@@ -1,4 +1,4 @@
1
- import { useStoreProfileStore } from './chunk-PWHIDGKW.js';
1
+ import { useStoreProfileStore } from './chunk-TEG7EL4N.js';
2
2
  import { defineStore, storeToRefs } from 'pinia';
3
3
  import { ref, computed } from 'vue';
4
4
 
@@ -34,5 +34,5 @@ function useStorefrontProfile() {
34
34
  }
35
35
 
36
36
  export { useProductsStore, useStore, useStorefrontProfile };
37
- //# sourceMappingURL=chunk-BIGTKVC6.js.map
38
- //# sourceMappingURL=chunk-BIGTKVC6.js.map
37
+ //# sourceMappingURL=chunk-57IOMDAM.js.map
38
+ //# sourceMappingURL=chunk-57IOMDAM.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/stores/products.ts","../src/stores/useStore.ts","../src/stores/useStorefrontProfile.ts"],"names":["computed"],"mappings":";;;;AAaO,IAAM,gBAAA,GAAmB,WAAA,CAAY,gBAAA,EAAkB,MAAqB;AAC/E,EAAA,MAAM,QAAA,GAAW,GAAA,CAAe,EAAE,CAAA;AAElC,EAAA,SAAS,YAAY,IAAA,EAAuB;AACxC,IAAA,QAAA,CAAS,KAAA,GAAQ,IAAA;AAAA,EACrB;AAEA,EAAA,MAAM,cAAc,QAAA,CAAS,MAAM,QAAA,CAAS,KAAA,CAAM,SAAS,CAAC,CAAA;AAE5D,EAAA,OAAO,EAAE,QAAA,EAAU,WAAA,EAAa,WAAA,EAAY;AAChD,CAAC;AChBM,SAAS,QAAA,GAId;AACE,EAAA,MAAM,eAAe,oBAAA,EAAqB;AAE1C,EAAA,OAAO;AAAA,IACH,KAAA,EAAOA,QAAAA,CAAS,MAAM,YAAA,CAAa,KAAM,CAAA;AAAA,IACzC,IAAA,EAAMA,QAAAA,CAAS,MAAM,YAAA,CAAa,IAAK,CAAA;AAAA,IACvC,KAAA,EAAOA,QAAAA,CAAS,MAAM,YAAA,CAAa,KAAK;AAAA,GAC5C;AACJ;ACbO,SAAS,oBAAA,GAAuB;AACnC,EAAA,MAAM,eAAe,oBAAA,EAAqB;AAC1C,EAAA,MAAM,EAAE,KAAA,EAAO,IAAA,EAAM,OAAO,UAAA,EAAW,GAAI,YAAY,YAAY,CAAA;AAEnE,EAAA,MAAM,OAAA,GAAUA,QAAAA,CAAS,MAAM,YAAA,CAAa,OAAO,CAAA;AACnD,EAAA,MAAM,QAAA,GAAWA,QAAAA,CAAS,MAAM,YAAA,CAAa,QAAQ,CAAA;AAErD,EAAA,OAAO;AAAA,IACH,KAAA;AAAA,IACA,IAAA;AAAA,IACA,KAAA;AAAA,IACA,UAAA;AAAA,IACA,OAAA;AAAA,IACA;AAAA,GACJ;AACJ","file":"chunk-BIGTKVC6.js","sourcesContent":["import { defineStore } from 'pinia'\nimport { computed, ref, type ComputedRef, type Ref } from 'vue'\n\nimport type { Product } from '@zenky/storefront-api'\n\n// Явный тип возврата — сохраняет `Product` именованной ссылкой в .d.ts вместо разворачивания.\nexport interface ProductsStore {\n products: Ref<Product[]>\n setProducts: (list: Product[]) => void\n hasProducts: ComputedRef<boolean>\n}\n\n// Товары держим в сторе, но наполняем из каталога (не из отдельного API-вызова).\nexport const useProductsStore = defineStore('zenky-products', (): ProductsStore => {\n const products = ref<Product[]>([])\n\n function setProducts(list: Product[]): void {\n products.value = list\n }\n\n const hasProducts = computed(() => products.value.length > 0)\n\n return { products, setProducts, hasProducts }\n})\n","import { computed, type ComputedRef } from 'vue'\n\nimport type { City, Stock, Store } from '@zenky/storefront-api'\n\nimport { useStoreProfileStore } from './store-profile'\n\n/** Аксессор загруженного стора/города/склада (store/city — гарантированно загружены). */\nexport function useStore(): {\n store: ComputedRef<Store>\n city: ComputedRef<City>\n stock: ComputedRef<Stock | null>\n} {\n const profileStore = useStoreProfileStore()\n\n return {\n store: computed(() => profileStore.store!),\n city: computed(() => profileStore.city!),\n stock: computed(() => profileStore.stock),\n }\n}\n","import { storeToRefs } from 'pinia'\nimport { computed } from 'vue'\n\nimport { useStoreProfileStore } from './store-profile'\n\n/** Реактивный доступ к профилю (store/city/stock/categories + флаги готовности). */\nexport function useStorefrontProfile() {\n const profileStore = useStoreProfileStore()\n const { store, city, stock, categories } = storeToRefs(profileStore)\n\n const isReady = computed(() => profileStore.isReady)\n const hasStock = computed(() => profileStore.hasStock)\n\n return {\n store,\n city,\n stock,\n categories,\n isReady,\n hasStock,\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/stores/products.ts","../src/stores/useStore.ts","../src/stores/useStorefrontProfile.ts"],"names":["computed"],"mappings":";;;;AAaO,IAAM,gBAAA,GAAmB,WAAA,CAAY,gBAAA,EAAkB,MAAqB;AAC/E,EAAA,MAAM,QAAA,GAAW,GAAA,CAAe,EAAE,CAAA;AAElC,EAAA,SAAS,YAAY,IAAA,EAAuB;AACxC,IAAA,QAAA,CAAS,KAAA,GAAQ,IAAA;AAAA,EACrB;AAEA,EAAA,MAAM,cAAc,QAAA,CAAS,MAAM,QAAA,CAAS,KAAA,CAAM,SAAS,CAAC,CAAA;AAE5D,EAAA,OAAO,EAAE,QAAA,EAAU,WAAA,EAAa,WAAA,EAAY;AAChD,CAAC;AChBM,SAAS,QAAA,GAId;AACE,EAAA,MAAM,eAAe,oBAAA,EAAqB;AAE1C,EAAA,OAAO;AAAA,IACH,KAAA,EAAOA,QAAAA,CAAS,MAAM,YAAA,CAAa,KAAM,CAAA;AAAA,IACzC,IAAA,EAAMA,QAAAA,CAAS,MAAM,YAAA,CAAa,IAAK,CAAA;AAAA,IACvC,KAAA,EAAOA,QAAAA,CAAS,MAAM,YAAA,CAAa,KAAK;AAAA,GAC5C;AACJ;ACbO,SAAS,oBAAA,GAAuB;AACnC,EAAA,MAAM,eAAe,oBAAA,EAAqB;AAC1C,EAAA,MAAM,EAAE,KAAA,EAAO,IAAA,EAAM,OAAO,UAAA,EAAW,GAAI,YAAY,YAAY,CAAA;AAEnE,EAAA,MAAM,OAAA,GAAUA,QAAAA,CAAS,MAAM,YAAA,CAAa,OAAO,CAAA;AACnD,EAAA,MAAM,QAAA,GAAWA,QAAAA,CAAS,MAAM,YAAA,CAAa,QAAQ,CAAA;AAErD,EAAA,OAAO;AAAA,IACH,KAAA;AAAA,IACA,IAAA;AAAA,IACA,KAAA;AAAA,IACA,UAAA;AAAA,IACA,OAAA;AAAA,IACA;AAAA,GACJ;AACJ","file":"chunk-57IOMDAM.js","sourcesContent":["import { defineStore } from 'pinia'\nimport { computed, ref, type ComputedRef, type Ref } from 'vue'\n\nimport type { Product } from '@zenky/storefront-api'\n\n// Явный тип возврата — сохраняет `Product` именованной ссылкой в .d.ts вместо разворачивания.\nexport interface ProductsStore {\n products: Ref<Product[]>\n setProducts: (list: Product[]) => void\n hasProducts: ComputedRef<boolean>\n}\n\n// Товары держим в сторе, но наполняем из каталога (не из отдельного API-вызова).\nexport const useProductsStore = defineStore('zenky-products', (): ProductsStore => {\n const products = ref<Product[]>([])\n\n function setProducts(list: Product[]): void {\n products.value = list\n }\n\n const hasProducts = computed(() => products.value.length > 0)\n\n return { products, setProducts, hasProducts }\n})\n","import { computed, type ComputedRef } from 'vue'\n\nimport type { City, Stock, Store } from '@zenky/storefront-api'\n\nimport { useStoreProfileStore } from './store-profile'\n\n/** Аксессор загруженного стора/города/склада (store/city — гарантированно загружены). */\nexport function useStore(): {\n store: ComputedRef<Store>\n city: ComputedRef<City>\n stock: ComputedRef<Stock | null>\n} {\n const profileStore = useStoreProfileStore()\n\n return {\n store: computed(() => profileStore.store!),\n city: computed(() => profileStore.city!),\n stock: computed(() => profileStore.stock),\n }\n}\n","import { storeToRefs } from 'pinia'\nimport { computed } from 'vue'\n\nimport { useStoreProfileStore } from './store-profile'\n\n/** Реактивный доступ к профилю (store/city/stock/categories + флаги готовности). */\nexport function useStorefrontProfile() {\n const profileStore = useStoreProfileStore()\n const { store, city, stock, categories } = storeToRefs(profileStore)\n\n const isReady = computed(() => profileStore.isReady)\n const hasStock = computed(() => profileStore.hasStock)\n\n return {\n store,\n city,\n stock,\n categories,\n isReady,\n hasStock,\n }\n}\n"]}
@@ -33,8 +33,7 @@ function wrapSnippetJs(snippet) {
33
33
  function parseTagAttributes(raw) {
34
34
  const attrs = {};
35
35
  const re = /([a-zA-Z][\w:-]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
36
- let match;
37
- while ((match = re.exec(raw)) !== null) {
36
+ for (const match of raw.matchAll(re)) {
38
37
  const name = match[1];
39
38
  if (name) attrs[name] = match[2] ?? match[3] ?? match[4] ?? true;
40
39
  }
@@ -42,15 +41,18 @@ function parseTagAttributes(raw) {
42
41
  }
43
42
  function parseHeadHtml(html) {
44
43
  const result = { meta: [], link: [], script: [] };
45
- let m;
46
44
  const metaRe = /<meta\b([^>]*)>/gi;
47
- while ((m = metaRe.exec(html)) !== null) result.meta.push(parseTagAttributes(m[1] ?? ""));
45
+ for (const match of html.matchAll(metaRe)) {
46
+ result.meta.push(parseTagAttributes(match[1] ?? ""));
47
+ }
48
48
  const linkRe = /<link\b([^>]*)>/gi;
49
- while ((m = linkRe.exec(html)) !== null) result.link.push(parseTagAttributes(m[1] ?? ""));
49
+ for (const match of html.matchAll(linkRe)) {
50
+ result.link.push(parseTagAttributes(match[1] ?? ""));
51
+ }
50
52
  const scriptRe = /<script\b([^>]*)>([\s\S]*?)<\/script>/gi;
51
- while ((m = scriptRe.exec(html)) !== null) {
52
- const attrs = parseTagAttributes(m[1] ?? "");
53
- const inner = (m[2] ?? "").trim();
53
+ for (const match of html.matchAll(scriptRe)) {
54
+ const attrs = parseTagAttributes(match[1] ?? "");
55
+ const inner = (match[2] ?? "").trim();
54
56
  result.script.push(inner ? { ...attrs, innerHTML: inner } : attrs);
55
57
  }
56
58
  return result;
@@ -87,5 +89,5 @@ function buildSnippetHead(snippets) {
87
89
  }
88
90
 
89
91
  export { buildSnippetHead, normalizeSnippets };
90
- //# sourceMappingURL=chunk-DKWDKVXQ.js.map
91
- //# sourceMappingURL=chunk-DKWDKVXQ.js.map
92
+ //# sourceMappingURL=chunk-7FRMMCR5.js.map
93
+ //# sourceMappingURL=chunk-7FRMMCR5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/snippets/snippets.ts","../src/snippets/buildSnippetHead.ts"],"names":[],"mappings":"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","file":"chunk-7FRMMCR5.js","sourcesContent":["export type SnippetPosition = 'head_start' | 'head_end' | 'body_start' | 'body_end'\nexport type SnippetLanguage = 'html' | 'css' | 'javascript'\n\nexport interface Snippet {\n id: string\n name?: string\n position: SnippetPosition\n language: SnippetLanguage\n code: string\n enabled: boolean\n}\n\nexport interface RawSnippetItem {\n language: SnippetLanguage\n content: string\n enabled?: boolean\n name?: string\n}\n\nexport interface SnippetGroup {\n region: SnippetPosition\n snippets: RawSnippetItem[]\n}\n\nexport function normalizeSnippets(groups: SnippetGroup[] | null | undefined): Snippet[] {\n if (!Array.isArray(groups)) return []\n\n return groups.flatMap((group, groupIndex) =>\n (group.snippets ?? [])\n .filter((item) => !!item.content?.trim())\n .map((item, itemIndex): Snippet => {\n const snippet: Snippet = {\n id: `${group.region}-${groupIndex}-${itemIndex}`,\n position: group.region,\n language: item.language,\n code: item.content,\n enabled: item.enabled ?? true,\n }\n // exactOptionalPropertyTypes: name задаём только если оно есть\n if (item.name !== undefined) snippet.name = item.name\n return snippet\n })\n )\n}\n","import type { Snippet } from './snippets'\n\n/** Head-нагрузка для инъекции сниппетов (потребитель отдаёт её в useHead). */\nexport interface SnippetHead {\n meta: Record<string, string | boolean>[]\n link: Record<string, string | boolean>[]\n script: Record<string, unknown>[]\n style: Record<string, string | boolean>[]\n}\n\n/** JS-сниппет в try/catch с dispatch `zenky:snippet-error` при падении. */\nfunction wrapSnippetJs(snippet: Snippet): string {\n const id = JSON.stringify(snippet.id)\n return `;(() => {\n try {\n ${snippet.code}\n } catch (error) {\n console.error('[snippet:error]', { id: ${id}, error });\n window.dispatchEvent(new CustomEvent('zenky:snippet-error', {\n detail: { id: ${id}, message: error instanceof Error ? error.message : String(error) },\n }));\n }\n})();`\n}\n\ntype HeadAttrs = Record<string, string | boolean>\n\ninterface ParsedHeadHtml {\n meta: HeadAttrs[]\n link: HeadAttrs[]\n script: Array<HeadAttrs & { innerHTML?: string }>\n}\n\nfunction parseTagAttributes(raw: string): HeadAttrs {\n const attrs: HeadAttrs = {}\n const re = /([a-zA-Z][\\w:-]*)(?:\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s\"'=<>`]+)))?/g\n for (const match of raw.matchAll(re)) {\n const name = match[1]\n if (name) attrs[name] = match[2] ?? match[3] ?? match[4] ?? true\n }\n return attrs\n}\n\nfunction parseHeadHtml(html: string): ParsedHeadHtml {\n const result: ParsedHeadHtml = { meta: [], link: [], script: [] }\n\n const metaRe = /<meta\\b([^>]*)>/gi\n for (const match of html.matchAll(metaRe)) {\n result.meta.push(parseTagAttributes(match[1] ?? ''))\n }\n\n const linkRe = /<link\\b([^>]*)>/gi\n for (const match of html.matchAll(linkRe)) {\n result.link.push(parseTagAttributes(match[1] ?? ''))\n }\n\n const scriptRe = /<script\\b([^>]*)>([\\s\\S]*?)<\\/script>/gi\n for (const match of html.matchAll(scriptRe)) {\n const attrs = parseTagAttributes(match[1] ?? '')\n const inner = (match[2] ?? '').trim()\n result.script.push(inner ? { ...attrs, innerHTML: inner } : attrs)\n }\n\n return result\n}\n\n/**\n * Раскладывает сниппеты магазина в head-нагрузку: JS (обёрнут в try/catch),\n * CSS (style), head-HTML (распарсенные meta/link/script). Инъекция — у потребителя.\n */\nexport function buildSnippetHead(snippets: Snippet[]): SnippetHead {\n const enabled = snippets.filter((s) => s.enabled)\n\n const jsSnippets = enabled.filter((s) => s.language === 'javascript')\n const cssSnippets = enabled.filter((s) => s.language === 'css')\n const headHtmlSnippets = enabled.filter(\n (s) => s.language === 'html' && s.position.startsWith('head')\n )\n\n const parsedHead = headHtmlSnippets.map((s) => parseHeadHtml(s.code))\n const headMeta = parsedHead.flatMap((p) => p.meta)\n const headLink = parsedHead.flatMap((p) => p.link)\n const headScripts = parsedHead.flatMap((p) => p.script)\n\n return {\n meta: headMeta,\n link: headLink,\n script: [\n ...jsSnippets.map((snippet) => ({\n key: `snippet-js-${snippet.id}`,\n innerHTML: wrapSnippetJs(snippet),\n body: snippet.position.startsWith('body'),\n 'data-snippet-id': snippet.id,\n })),\n ...headScripts,\n ],\n style: cssSnippets.map((snippet) => ({\n key: `snippet-css-${snippet.id}`,\n innerHTML: snippet.code,\n 'data-snippet-id': snippet.id,\n })),\n }\n}\n"]}
@@ -41,5 +41,5 @@ function getRemoteCatalog(store, city, stock) {
41
41
  }
42
42
 
43
43
  export { getCity, getRemoteCatalog, getStock };
44
- //# sourceMappingURL=chunk-EYRCAUUP.js.map
45
- //# sourceMappingURL=chunk-EYRCAUUP.js.map
44
+ //# sourceMappingURL=chunk-E3G7NGCH.js.map
45
+ //# sourceMappingURL=chunk-E3G7NGCH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/catalog/catalogResolver.ts"],"names":[],"mappings":";AAEO,SAAS,OAAA,CAAQ,OAAc,MAAA,EAAqC;AACvE,EAAA,IAAI,CAAC,KAAA,CAAM,MAAA,CAAO,MAAA,EAAQ,OAAO,IAAA;AAEjC,EAAA,MAAM,WAAA,GAAc,KAAA,CAAM,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,UAAU,CAAA,IAAK,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,IAAK,IAAA;AAEjF,EAAA,IAAI,CAAC,QAAQ,OAAO,WAAA;AAEpB,EAAA,OAAO,KAAA,CAAM,OAAO,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,EAAA,KAAO,MAAM,CAAA,IAAK,WAAA;AACxD;AAEO,SAAS,QAAA,CAAS,MAAY,OAAA,EAAuC;AACxE,EAAA,IAAI,CAAC,IAAA,CAAK,MAAA,EAAQ,MAAA,EAAQ,OAAO,IAAA;AAEjC,EAAA,MAAM,YAAA,GAAe,IAAA,CAAK,MAAA,CAAO,CAAC,CAAA,IAAK,IAAA;AAEvC,EAAA,IAAI,CAAC,SAAS,OAAO,YAAA;AAErB,EAAA,OAAO,IAAA,CAAK,OAAO,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,EAAA,KAAO,OAAO,CAAA,IAAK,YAAA;AACxD;AAEO,SAAS,gBAAA,CACZ,KAAA,EACA,IAAA,EACA,KAAA,EACoB;AACpB,EAAA,IAAI,CAAC,KAAA,CAAM,QAAA,CAAS,MAAA,EAAQ;AACxB,IAAA,OAAA,CAAQ,KAAK,4CAAA,EAA8C;AAAA,MACvD,SAAS,KAAA,CAAM,EAAA;AAAA,MACf,QAAQ,IAAA,CAAK,EAAA;AAAA,MACb,OAAA,EAAS,OAAO,EAAA,IAAM;AAAA,KACzB,CAAA;AACD,IAAA,OAAO,IAAA;AAAA,EACX;AAGA,EAAA,IAAI,KAAA,EAAO;AACP,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAA,KAAY,IAAA,CAAK,EAAA,IAAM,CAAA,CAAE,QAAA,KAAa,MAAM,EAAE,CAAA;AACzF,IAAA,IAAI,OAAO,OAAO,KAAA;AAAA,EACtB;AAEA,EAAA,MAAM,QAAA,GAAW,KAAA,CAAM,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAA,KAAY,IAAA,CAAK,EAAA,IAAM,CAAA,CAAE,QAAA,KAAa,IAAI,CAAA;AACxF,EAAA,IAAI,UAAU,OAAO,QAAA;AAErB,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAA,KAAY,IAAA,IAAQ,CAAA,CAAE,QAAA,KAAa,IAAI,CAAA;AACnF,EAAA,IAAI,QAAQ,OAAO,MAAA;AAEnB,EAAA,OAAA,CAAQ,KAAK,gEAAA,EAAkE;AAAA,IAC3E,SAAS,KAAA,CAAM,EAAA;AAAA,IACf,QAAQ,IAAA,CAAK,EAAA;AAAA,IACb,OAAA,EAAS,OAAO,EAAA,IAAM,IAAA;AAAA,IACtB,iBAAA,EAAmB,KAAA,CAAM,QAAA,CAAS,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,MAC1C,SAAS,CAAA,CAAE,OAAA;AAAA,MACX,UAAU,CAAA,CAAE;AAAA,KAChB,CAAE;AAAA,GACL,CAAA;AAED,EAAA,OAAO,IAAA;AACX","file":"chunk-E3G7NGCH.js","sourcesContent":["import type { City, RemoteCatalog, Stock, Store } from '@zenky/storefront-api'\n\nexport function getCity(store: Store, cityId?: string | null): City | null {\n if (!store.cities.length) return null\n\n const defaultCity = store.cities.find((c) => c.is_default) ?? store.cities[0] ?? null\n\n if (!cityId) return defaultCity\n\n return store.cities.find((c) => c.id === cityId) ?? defaultCity\n}\n\nexport function getStock(city: City, stockId?: string | null): Stock | null {\n if (!city.stocks?.length) return null\n\n const defaultStock = city.stocks[0] ?? null\n\n if (!stockId) return defaultStock\n\n return city.stocks.find((s) => s.id === stockId) ?? defaultStock\n}\n\nexport function getRemoteCatalog(\n store: Store,\n city: City,\n stock: Stock | null\n): RemoteCatalog | null {\n if (!store.catalogs.length) {\n console.warn('[catalog-resolver] store.catalogs is empty', {\n storeId: store.id,\n cityId: city.id,\n stockId: stock?.id ?? null,\n })\n return null\n }\n\n // Приоритет: точное совпадение city+stock → city-only → глобальный.\n if (stock) {\n const exact = store.catalogs.find((c) => c.city_id === city.id && c.stock_id === stock.id)\n if (exact) return exact\n }\n\n const cityOnly = store.catalogs.find((c) => c.city_id === city.id && c.stock_id === null)\n if (cityOnly) return cityOnly\n\n const global = store.catalogs.find((c) => c.city_id === null && c.stock_id === null)\n if (global) return global\n\n console.warn('[catalog-resolver] no catalog matched (exact/city-only/global)', {\n storeId: store.id,\n cityId: city.id,\n stockId: stock?.id ?? null,\n availableCatalogs: store.catalogs.map((c) => ({\n city_id: c.city_id,\n stock_id: c.stock_id,\n })),\n })\n\n return null\n}\n"]}
@@ -158,8 +158,11 @@ var useCartStore = defineStore("zenky-cart", () => {
158
158
  }
159
159
  }
160
160
  function setOrder(next) {
161
- if (!next || !next.id || !next.token) {
162
- console.warn("[cart] setOrder ignored \u2014 missing id/token", next);
161
+ if (!next?.id || !next.token) {
162
+ console.warn("[cart] setOrder ignored \u2014 missing id/token", {
163
+ hasId: Boolean(next?.id),
164
+ hasToken: Boolean(next?.token)
165
+ });
163
166
  return;
164
167
  }
165
168
  order.value = next;
@@ -169,8 +172,11 @@ var useCartStore = defineStore("zenky-cart", () => {
169
172
  }
170
173
  }
171
174
  function patchOrder(partial) {
172
- if (!partial || !partial.id || !partial.token) {
173
- console.warn("[cart] patchOrder ignored \u2014 missing id/token", partial);
175
+ if (!partial?.id || !partial.token) {
176
+ console.warn("[cart] patchOrder ignored \u2014 missing id/token", {
177
+ hasId: Boolean(partial?.id),
178
+ hasToken: Boolean(partial?.token)
179
+ });
174
180
  return;
175
181
  }
176
182
  if (!order.value) {
@@ -272,7 +278,8 @@ var useCartStore = defineStore("zenky-cart", () => {
272
278
  function setCheckoutTotal(value) {
273
279
  checkoutTotal.value = value;
274
280
  const min = value.min_price;
275
- minPriceViolation.value = min != null && value.total > 0 && value.total < min ? {
281
+ const hasItems = (order.value?.variants?.length ?? 0) > 0;
282
+ minPriceViolation.value = hasItems && min != null && value.total > 0 && value.total < min ? {
276
283
  remaining: Math.max(0, min - value.total),
277
284
  progress: Math.min(1, value.total / min),
278
285
  minPrice: min
@@ -382,5 +389,5 @@ var useCartStore = defineStore("zenky-cart", () => {
382
389
  });
383
390
 
384
391
  export { useCartStore, useOrderSettingsStore };
385
- //# sourceMappingURL=chunk-VL3T36VM.js.map
386
- //# sourceMappingURL=chunk-VL3T36VM.js.map
392
+ //# sourceMappingURL=chunk-FFBZKQRV.js.map
393
+ //# sourceMappingURL=chunk-FFBZKQRV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/stores/order-settings.ts","../src/stores/cart.ts"],"names":["defineStore","ref","raw","computed"],"mappings":"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","file":"chunk-FFBZKQRV.js","sourcesContent":["import { DeliveryMethod } from '@zenky/storefront-api'\nimport { defineStore } from 'pinia'\nimport { computed, ref, type ComputedRef, type Ref } from 'vue'\n\nimport type { City, Stock } from '@zenky/storefront-api'\n\nimport { useRuntime } from '../ports/runtime'\n\n// Ключи совпадают с прежними CookieKey.* — чтобы не потерять сохранённые настройки.\nconst KEY_DELIVERY_METHOD = 'zenky_delivery_method'\nconst KEY_ON_PREMISE_TABLE = 'zenky_on_premise_table'\nconst KEY_SELECTED_STOCK_ID = 'zenky_selected_stock_id'\nconst KEY_SELECTED_ADDRESS = 'zenky_selected_address'\nconst KEY_SELECTED_ADDRESS_ID = 'zenky_selected_address_id'\nconst KEY_SELECTED_ADDRESS_NAME = 'zenky_selected_address_name'\n\nconst VALID_DELIVERY_METHODS = new Set<string>(Object.values(DeliveryMethod))\n\nexport interface OrderSettingsStore {\n deliveryMethod: Ref<DeliveryMethod>\n onPremiseTable: Ref<string | null>\n selectedStock: Ref<Stock | null>\n selectedAddress: Ref<string | null>\n selectedAddressId: Ref<string | null>\n selectedAddressName: Ref<string | null>\n setDeliveryMethod: (method: DeliveryMethod) => void\n setOnPremiseTable: (table: string | null) => void\n setSelectedStock: (stock: Stock | null) => void\n setSelectedAddress: (\n address: string | null,\n addressId?: string | null,\n addressName?: string | null\n ) => void\n validateDeliveryMethod: (availableMethods: DeliveryMethod[]) => void\n restoreFromCookies: (city: City) => void\n isDelivery: ComputedRef<boolean>\n isPickup: ComputedRef<boolean>\n isOnPremise: ComputedRef<boolean>\n needsAddress: ComputedRef<boolean>\n needsStock: ComputedRef<boolean>\n needsTable: ComputedRef<boolean>\n}\n\nexport const useOrderSettingsStore = defineStore('zenky-order-settings', (): OrderSettingsStore => {\n const { storage } = useRuntime()\n\n const deliveryMethod = ref<DeliveryMethod>(DeliveryMethod.Delivery)\n const onPremiseTable = ref<string | null>(null)\n const selectedStock = ref<Stock | null>(null)\n const selectedAddress = ref<string | null>(null)\n const selectedAddressId = ref<string | null>(null)\n const selectedAddressName = ref<string | null>(null)\n\n function setDeliveryMethod(method: DeliveryMethod): void {\n deliveryMethod.value = method\n storage.set(KEY_DELIVERY_METHOD, method)\n }\n\n function setOnPremiseTable(table: string | null): void {\n onPremiseTable.value = table\n if (table) storage.set(KEY_ON_PREMISE_TABLE, table)\n else storage.remove(KEY_ON_PREMISE_TABLE)\n }\n\n function setSelectedStock(stock: Stock | null): void {\n selectedStock.value = stock\n if (stock) storage.set(KEY_SELECTED_STOCK_ID, stock.id)\n else storage.remove(KEY_SELECTED_STOCK_ID)\n }\n\n function setSelectedAddress(\n address: string | null,\n addressId?: string | null,\n addressName?: string | null\n ): void {\n selectedAddress.value = address\n selectedAddressId.value = addressId ?? null\n selectedAddressName.value = addressName ?? null\n writeOrRemove(KEY_SELECTED_ADDRESS, address)\n writeOrRemove(KEY_SELECTED_ADDRESS_ID, addressId ?? null)\n writeOrRemove(KEY_SELECTED_ADDRESS_NAME, addressName ?? null)\n }\n\n function writeOrRemove(key: string, value: string | null): void {\n if (value) storage.set(key, value)\n else storage.remove(key)\n }\n\n /** Если текущий метод не входит в доступные — переключить на первый доступный. */\n function validateDeliveryMethod(availableMethods: DeliveryMethod[]): void {\n const first = availableMethods[0]\n if (!first) return\n\n if (!availableMethods.includes(deliveryMethod.value)) {\n deliveryMethod.value = first\n storage.set(KEY_DELIVERY_METHOD, first)\n }\n }\n\n /** Восстановить настройки из cookies. Вызывать после загрузки города. */\n function restoreFromCookies(city: City): void {\n const savedMethod = storage.get(KEY_DELIVERY_METHOD)\n if (savedMethod && VALID_DELIVERY_METHODS.has(savedMethod)) {\n deliveryMethod.value = savedMethod as DeliveryMethod\n }\n\n const savedStockId = storage.get(KEY_SELECTED_STOCK_ID)\n const stocks = city.stocks ?? []\n if (savedStockId && stocks.length) {\n const stock = stocks.find((s) => s.id === savedStockId) ?? null\n selectedStock.value = stock\n if (!stock) storage.remove(KEY_SELECTED_STOCK_ID)\n }\n\n const savedAddress = storage.get(KEY_SELECTED_ADDRESS)\n if (savedAddress) selectedAddress.value = savedAddress\n\n const savedAddressId = storage.get(KEY_SELECTED_ADDRESS_ID)\n if (savedAddressId) selectedAddressId.value = savedAddressId\n\n const savedAddressName = storage.get(KEY_SELECTED_ADDRESS_NAME)\n if (savedAddressName) selectedAddressName.value = savedAddressName\n\n const savedTable = storage.get(KEY_ON_PREMISE_TABLE)\n if (savedTable) onPremiseTable.value = savedTable\n }\n\n const isDelivery = computed(() => deliveryMethod.value === DeliveryMethod.Delivery)\n const isPickup = computed(() => deliveryMethod.value === DeliveryMethod.Pickup)\n const isOnPremise = computed(() => deliveryMethod.value === DeliveryMethod.OnPremise)\n const needsAddress = computed(() => deliveryMethod.value === DeliveryMethod.Delivery)\n const needsStock = computed(\n () =>\n deliveryMethod.value === DeliveryMethod.Pickup ||\n deliveryMethod.value === DeliveryMethod.OnPremise\n )\n const needsTable = computed(() => deliveryMethod.value === DeliveryMethod.OnPremise)\n\n return {\n deliveryMethod,\n onPremiseTable,\n selectedStock,\n selectedAddress,\n selectedAddressId,\n selectedAddressName,\n setDeliveryMethod,\n setOnPremiseTable,\n setSelectedStock,\n setSelectedAddress,\n validateDeliveryMethod,\n restoreFromCookies,\n isDelivery,\n isPickup,\n isOnPremise,\n needsAddress,\n needsStock,\n needsTable,\n }\n})\n","import { PaymentMethod } from '@zenky/storefront-api'\nimport { defineStore } from 'pinia'\nimport { computed, ref, type ComputedRef, type Ref } from 'vue'\n\nimport type {\n DeliveryMethod,\n Order,\n OrderCheckoutTotal,\n OrderDeliveryMethod,\n OrderPaymentMethod,\n OrderPaymentTransaction,\n OrderProductVariant,\n OrderSettings,\n} from '@zenky/storefront-api'\n\nimport { useRuntime } from '../ports/runtime'\nimport { useOrderSettingsStore } from './order-settings'\n\n// Ключи совпадают с прежними CookieKey.* — чтобы не потерять сохранённое.\nconst KEY_COMMENT = 'zenky_order_comment'\nconst KEY_PERSONS_COUNT = 'zenky_order_persons_count'\n\nexport interface CartCredentials {\n id: string\n token: string\n}\n\n/** Намерение по бонусам (business-state) — для headless-reconciliation. */\nexport interface AppliedBonusesIntent {\n enabled: boolean\n /** null = «списать максимум». */\n desiredAmountMinor: number | null\n /** PIN-flow: бонусы сняты, нужно подтвердить заново. */\n requiresConfirmation: boolean\n}\n\nexport interface MinPriceViolation {\n remaining: number\n progress: number\n minPrice: number\n}\n\nfunction emptyBonusesIntent(): AppliedBonusesIntent {\n return { enabled: false, desiredAmountMinor: null, requiresConfirmation: false }\n}\n\nfunction parsePersonsCount(raw: string | null): number {\n const parsed = raw ? Number.parseInt(raw, 10) : NaN\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 0\n}\n\nexport interface CartStore {\n order: Ref<Order | null>\n credentials: Ref<CartCredentials | null>\n loading: Ref<boolean>\n initializing: Ref<boolean>\n optimisticAdjustments: Ref<Record<string, number>>\n lineOptimistic: Ref<Record<string, number>>\n deliveryMethods: Ref<OrderDeliveryMethod[]>\n orderSettings: Ref<OrderSettings | null>\n comment: Ref<string>\n personsCount: Ref<number>\n checkoutTotal: Ref<OrderCheckoutTotal | null>\n selectedPaymentMethod: Ref<PaymentMethod | null>\n selectedPaymentBill: Ref<number | null>\n selectedCardTokenId: Ref<string | null>\n selectedSaveCard: Ref<boolean>\n bonusesIntent: Ref<AppliedBonusesIntent>\n deliveryTimeIntent: Ref<string | null>\n minPriceViolation: Ref<MinPriceViolation | null>\n setOrder: (order: Order) => void\n patchOrder: (partial: Order) => void\n setCredentials: (credentials: CartCredentials) => void\n setLoading: (value: boolean) => void\n setInitializing: (value: boolean) => void\n adjustOptimistic: (productId: string, delta: number) => void\n clearOptimistic: (productId: string) => void\n adjustLineOptimistic: (lineKey: string, delta: number) => void\n setDeliveryMethods: (methods: OrderDeliveryMethod[]) => void\n setOrderSettings: (settings: OrderSettings) => void\n setComment: (value: string) => void\n setPersonsCount: (value: number) => void\n setSelectedPaymentMethod: (method: PaymentMethod | null) => void\n setSelectedPaymentBill: (bill: number | null) => void\n setSelectedCardTokenId: (id: string | null) => void\n setSelectedSaveCard: (value: boolean) => void\n setBonusesIntentEnabled: (value: boolean) => void\n setDesiredBonusesAmount: (value: number | null) => void\n setBonusesRequiresConfirmation: (value: boolean) => void\n clearBonusesIntent: () => void\n setDeliveryTimeIntent: (value: string | null) => void\n restorePaymentSelection: (payments: OrderPaymentTransaction[]) => void\n validatePaymentSelection: () => void\n setOrderPaymentsState: (payments: OrderPaymentTransaction[]) => void\n setCheckoutTotal: (value: OrderCheckoutTotal) => void\n clearCart: () => void\n hasOrder: ComputedRef<boolean>\n hasCredentials: ComputedRef<boolean>\n variants: ComputedRef<OrderProductVariant[]>\n totalPrice: ComputedRef<number>\n itemCount: ComputedRef<number>\n availableDeliveryMethodIds: ComputedRef<DeliveryMethod[]>\n paymentMethods: ComputedRef<OrderPaymentMethod[]>\n hasBonusesApplied: ComputedRef<boolean>\n productQuantities: ComputedRef<Record<string, number>>\n}\n\nexport const useCartStore = defineStore('zenky-cart', (): CartStore => {\n const { storage } = useRuntime()\n\n const order = ref<Order | null>(null)\n const credentials = ref<CartCredentials | null>(null)\n const loading = ref(false)\n const initializing = ref(false)\n const optimisticAdjustments = ref<Record<string, number>>({})\n const lineOptimistic = ref<Record<string, number>>({})\n const deliveryMethods = ref<OrderDeliveryMethod[]>([])\n const orderSettings = ref<OrderSettings | null>(null)\n const comment = ref(storage.get(KEY_COMMENT) ?? '')\n const personsCount = ref(parsePersonsCount(storage.get(KEY_PERSONS_COUNT)))\n const checkoutTotal = ref<OrderCheckoutTotal | null>(null)\n const selectedPaymentMethod = ref<PaymentMethod | null>(null)\n const selectedPaymentBill = ref<number | null>(null)\n const selectedCardTokenId = ref<string | null>(null)\n const selectedSaveCard = ref(false)\n const bonusesIntent = ref<AppliedBonusesIntent>(emptyBonusesIntent())\n const deliveryTimeIntent = ref<string | null>(null)\n const minPriceViolation = ref<MinPriceViolation | null>(null)\n\n function restorePaymentSelection(payments: OrderPaymentTransaction[]): void {\n const restored = payments.find((p) => {\n const raw = p.method as unknown\n const id = typeof raw === 'string' ? raw : ((raw as { id?: string } | null)?.id ?? null)\n return id !== PaymentMethod.Bonuses\n })\n if (!restored) return\n\n const raw = restored.method as unknown\n const methodId = (\n typeof raw === 'string' ? raw : ((raw as { id?: PaymentMethod } | null)?.id ?? null)\n ) as PaymentMethod | null\n if (!methodId || selectedPaymentMethod.value === methodId) return\n\n selectedPaymentMethod.value = methodId\n\n const meta = restored.transaction_meta as { bill?: number | null } | null | undefined\n const billMinor = meta?.bill ?? null\n const newBill = billMinor != null && billMinor > 0 ? billMinor : null\n if (selectedPaymentBill.value !== newBill) {\n selectedPaymentBill.value = newBill\n }\n }\n\n function validatePaymentSelection(): void {\n if (!selectedPaymentMethod.value) return\n const methods = orderSettings.value?.payment_methods ?? []\n if (!methods.some((m) => m.id === selectedPaymentMethod.value)) {\n selectedPaymentMethod.value = null\n selectedPaymentBill.value = null\n }\n }\n\n function setOrder(next: Order): void {\n if (!next?.id || !next.token) {\n console.warn('[cart] setOrder ignored — missing id/token', {\n hasId: Boolean(next?.id),\n hasToken: Boolean(next?.token),\n })\n return\n }\n\n order.value = next\n credentials.value = { id: next.id, token: next.token }\n\n if (Array.isArray(next.payments) && next.payments.length > 0) {\n restorePaymentSelection(next.payments as unknown as OrderPaymentTransaction[])\n }\n }\n\n function patchOrder(partial: Order): void {\n if (!partial?.id || !partial.token) {\n console.warn('[cart] patchOrder ignored — missing id/token', {\n hasId: Boolean(partial?.id),\n hasToken: Boolean(partial?.token),\n })\n return\n }\n\n if (!order.value) {\n order.value = partial\n } else {\n const merged: Order = { ...order.value }\n for (const key of Object.keys(partial) as (keyof Order)[]) {\n const value = partial[key]\n if (value !== undefined) {\n ;(merged as unknown as Record<string, unknown>)[key as string] = value\n }\n }\n order.value = merged\n }\n\n credentials.value = { id: partial.id, token: partial.token }\n\n if (Array.isArray(partial.payments) && partial.payments.length > 0) {\n restorePaymentSelection(partial.payments as unknown as OrderPaymentTransaction[])\n }\n }\n\n function setCredentials(next: CartCredentials): void {\n credentials.value = next\n }\n\n function setLoading(value: boolean): void {\n loading.value = value\n }\n\n function setInitializing(value: boolean): void {\n initializing.value = value\n }\n\n function adjustOptimistic(productId: string, delta: number): void {\n const next = (optimisticAdjustments.value[productId] ?? 0) + delta\n if (next === 0) delete optimisticAdjustments.value[productId]\n else optimisticAdjustments.value[productId] = next\n }\n\n function clearOptimistic(productId: string): void {\n delete optimisticAdjustments.value[productId]\n }\n\n function adjustLineOptimistic(lineKey: string, delta: number): void {\n const next = (lineOptimistic.value[lineKey] ?? 0) + delta\n if (next === 0) delete lineOptimistic.value[lineKey]\n else lineOptimistic.value[lineKey] = next\n }\n\n function setDeliveryMethods(methods: OrderDeliveryMethod[]): void {\n deliveryMethods.value = methods\n const settingsStore = useOrderSettingsStore()\n settingsStore.validateDeliveryMethod(methods.map((m) => m.id))\n }\n\n function setOrderSettings(settings: OrderSettings): void {\n orderSettings.value = settings\n setDeliveryMethods(settings.delivery_methods)\n validatePaymentSelection()\n }\n\n function setComment(value: string): void {\n comment.value = value\n if (value) storage.set(KEY_COMMENT, value)\n else storage.remove(KEY_COMMENT)\n }\n\n function setPersonsCount(value: number): void {\n personsCount.value = value\n if (value > 0) storage.set(KEY_PERSONS_COUNT, String(value))\n else storage.remove(KEY_PERSONS_COUNT)\n }\n\n function setSelectedPaymentMethod(method: PaymentMethod | null): void {\n selectedPaymentMethod.value = method\n }\n\n function setSelectedPaymentBill(bill: number | null): void {\n selectedPaymentBill.value = bill\n }\n\n function setSelectedCardTokenId(id: string | null): void {\n selectedCardTokenId.value = id\n }\n\n function setSelectedSaveCard(value: boolean): void {\n selectedSaveCard.value = value\n }\n\n function setBonusesIntentEnabled(value: boolean): void {\n bonusesIntent.value.enabled = value\n if (!value) {\n bonusesIntent.value.desiredAmountMinor = null\n bonusesIntent.value.requiresConfirmation = false\n }\n }\n\n function setDesiredBonusesAmount(value: number | null): void {\n bonusesIntent.value.desiredAmountMinor = value\n }\n\n function setBonusesRequiresConfirmation(value: boolean): void {\n bonusesIntent.value.requiresConfirmation = value\n }\n\n function clearBonusesIntent(): void {\n bonusesIntent.value = emptyBonusesIntent()\n }\n\n function setDeliveryTimeIntent(value: string | null): void {\n deliveryTimeIntent.value = value\n }\n\n function setOrderPaymentsState(payments: OrderPaymentTransaction[]): void {\n if (order.value) {\n order.value = { ...order.value, payments } as Order\n }\n restorePaymentSelection(payments)\n }\n\n function setCheckoutTotal(value: OrderCheckoutTotal): void {\n checkoutTotal.value = value\n\n const min = value.min_price\n // Пустой заказ (нет variants) с ненулевой доставкой в total — не нарушение минималки.\n const hasItems = (order.value?.variants?.length ?? 0) > 0\n minPriceViolation.value =\n hasItems && min != null && value.total > 0 && value.total < min\n ? {\n remaining: Math.max(0, min - value.total),\n progress: Math.min(1, value.total / min),\n minPrice: min,\n }\n : null\n }\n\n function clearCart(): void {\n order.value = null\n credentials.value = null\n checkoutTotal.value = null\n optimisticAdjustments.value = {}\n lineOptimistic.value = {}\n orderSettings.value = null\n comment.value = ''\n personsCount.value = 0\n storage.remove(KEY_COMMENT)\n storage.remove(KEY_PERSONS_COUNT)\n selectedPaymentMethod.value = null\n selectedPaymentBill.value = null\n selectedCardTokenId.value = null\n selectedSaveCard.value = false\n bonusesIntent.value = emptyBonusesIntent()\n deliveryTimeIntent.value = null\n minPriceViolation.value = null\n }\n\n const hasOrder = computed(() => order.value !== null)\n const hasCredentials = computed(() => credentials.value !== null)\n const variants = computed<OrderProductVariant[]>(() => order.value?.variants ?? [])\n const totalPrice = computed(() => order.value?.total_price ?? 0)\n const itemCount = computed(() => variants.value.reduce((sum, v) => sum + v.quantity, 0))\n const availableDeliveryMethodIds = computed<DeliveryMethod[]>(() =>\n deliveryMethods.value.map((m) => m.id)\n )\n const paymentMethods = computed<OrderPaymentMethod[]>(\n () => orderSettings.value?.payment_methods ?? []\n )\n const hasBonusesApplied = computed(() => {\n const payments = order.value?.payments ?? []\n return (payments as unknown as Array<{ method: unknown }>).some((p) => {\n const raw = p.method\n const id = typeof raw === 'string' ? raw : ((raw as { id?: string } | null)?.id ?? null)\n return id === PaymentMethod.Bonuses\n })\n })\n const productQuantities = computed<Record<string, number>>(() => {\n const map: Record<string, number> = {}\n for (const variant of variants.value) {\n const pid = variant.product_id\n map[pid] = (map[pid] ?? 0) + variant.quantity\n }\n return map\n })\n\n return {\n order,\n credentials,\n loading,\n initializing,\n optimisticAdjustments,\n lineOptimistic,\n deliveryMethods,\n orderSettings,\n comment,\n personsCount,\n checkoutTotal,\n selectedPaymentMethod,\n selectedPaymentBill,\n selectedCardTokenId,\n selectedSaveCard,\n bonusesIntent,\n deliveryTimeIntent,\n minPriceViolation,\n setOrder,\n patchOrder,\n setCredentials,\n setLoading,\n setInitializing,\n adjustOptimistic,\n clearOptimistic,\n adjustLineOptimistic,\n setDeliveryMethods,\n setOrderSettings,\n setComment,\n setPersonsCount,\n setSelectedPaymentMethod,\n setSelectedPaymentBill,\n setSelectedCardTokenId,\n setSelectedSaveCard,\n setBonusesIntentEnabled,\n setDesiredBonusesAmount,\n setBonusesRequiresConfirmation,\n clearBonusesIntent,\n setDeliveryTimeIntent,\n restorePaymentSelection,\n validatePaymentSelection,\n setOrderPaymentsState,\n setCheckoutTotal,\n clearCart,\n hasOrder,\n hasCredentials,\n variants,\n totalPrice,\n itemCount,\n availableDeliveryMethodIds,\n paymentMethods,\n hasBonusesApplied,\n productQuantities,\n }\n})\n"]}