@zenky/storefront-vue 0.10.4 → 0.10.5
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.
- package/CHANGELOG.md +30 -0
- package/README.md +63 -3
- package/dist/cart/index.js +4 -4
- package/dist/{chunk-YREGCQSB.js → chunk-4YAEIKLA.js} +4 -4
- package/dist/chunk-4YAEIKLA.js.map +1 -0
- package/dist/{chunk-TB5TBJ4U.js → chunk-CUSQYIEU.js} +6 -15
- package/dist/chunk-CUSQYIEU.js.map +1 -0
- package/dist/{chunk-SS7B2NLF.js → chunk-CY6LA6C7.js} +3 -3
- package/dist/{chunk-SS7B2NLF.js.map → chunk-CY6LA6C7.js.map} +1 -1
- package/dist/{chunk-OTSAR32A.js → chunk-MOG6UMFM.js} +2 -2
- package/dist/{chunk-OTSAR32A.js.map → chunk-MOG6UMFM.js.map} +1 -1
- package/dist/{chunk-IVG4FQE5.js → chunk-Q2TVQ3BG.js} +3 -3
- package/dist/{chunk-IVG4FQE5.js.map → chunk-Q2TVQ3BG.js.map} +1 -1
- package/dist/{chunk-NIQFYJRP.js → chunk-SHASMBMV.js} +4 -4
- package/dist/{chunk-NIQFYJRP.js.map → chunk-SHASMBMV.js.map} +1 -1
- package/dist/{chunk-YS5DXYYV.js → chunk-SPMUTUNX.js} +21 -28
- package/dist/chunk-SPMUTUNX.js.map +1 -0
- package/dist/index.js +7 -7
- package/dist/ports/index.d.ts +2 -1
- package/dist/ports/index.js +1 -1
- package/dist/products/index.js +2 -2
- package/dist/promotions/index.d.ts +13 -0
- package/dist/promotions/index.js +3 -3
- package/dist/seo/index.js +2 -2
- package/dist/stores/index.js +3 -3
- package/docs/consumer-setup.md +10 -3
- package/docs/ports.md +1 -1
- package/docs/releasing.md +2 -1
- package/package.json +5 -4
- package/dist/chunk-TB5TBJ4U.js.map +0 -1
- package/dist/chunk-YREGCQSB.js.map +0 -1
- package/dist/chunk-YS5DXYYV.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,36 @@ All notable changes to this project are documented in this file. The format foll
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and releases follow
|
|
5
5
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## 0.10.5 - 2026-07-31
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- `config` is now an optional runtime port because the current core does not read it.
|
|
12
|
+
- The framework-boundary gate now parses TypeScript syntax, so URLs and comment-like
|
|
13
|
+
text inside strings no longer hide violations or create false positives.
|
|
14
|
+
- A runtime-context gate now rejects composable or Pinia-store resolution from deferred
|
|
15
|
+
helpers and store actions; those functions must use dependencies captured in setup.
|
|
16
|
+
- Product restriction and delivery-time operations use public
|
|
17
|
+
`@zenky/storefront-api` resource methods instead of reaching through `sdk.client`.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- `authStore.initFromCookie()` now clears stale in-memory SDK credentials when the
|
|
22
|
+
current request has no auth cookies.
|
|
23
|
+
- Payment selection reuses the setup-captured order total, so checkout with applied
|
|
24
|
+
bonuses no longer resolves a composable from a later event/checkout callback.
|
|
25
|
+
- Cart delivery-method validation resolves the order-settings store once in the store
|
|
26
|
+
setup. Pinia already activates the owning instance for the duration of an action, so
|
|
27
|
+
this is consistency rather than a leak fix.
|
|
28
|
+
- Runtime-context documentation now distinguishes Vue injection context, arbitrary
|
|
29
|
+
callbacks, `app.runWithContext()`, and Pinia's conditional global fallback.
|
|
30
|
+
|
|
31
|
+
### Security
|
|
32
|
+
|
|
33
|
+
- `logCartError` redaction now covers customer addresses, e-mail and phone numbers, and
|
|
34
|
+
checkout/payment/redirect URLs, in object keys, `key=value` assignments, and query
|
|
35
|
+
parameters alike. Cart and checkout failures previously logged these verbatim.
|
|
36
|
+
|
|
7
37
|
## 0.10.4 - 2026-07-29
|
|
8
38
|
|
|
9
39
|
> **Breaking.** This release changes the runtime port contract and removes public
|
package/README.md
CHANGED
|
@@ -73,14 +73,71 @@ cart.itemCount.value // reactive
|
|
|
73
73
|
cart.totalPrice.value
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
+
### Call composables and stores synchronously in setup
|
|
77
|
+
|
|
78
|
+
Composables read the runtime through `inject()`, which works while a component
|
|
79
|
+
instance is active or while the owning Vue app is executing `app.runWithContext()`.
|
|
80
|
+
The important cases behave differently:
|
|
81
|
+
|
|
82
|
+
- **Top-level `await` in `<script setup>` is safe.** The SFC compiler wraps it in
|
|
83
|
+
`withAsyncContext()`, which restores the instance afterwards — so composables called
|
|
84
|
+
after `await bootstrap()` at the top level still resolve correctly.
|
|
85
|
+
- **A lifecycle hook has context only in its synchronous prefix.** After its first
|
|
86
|
+
`await`, the component instance is no longer active.
|
|
87
|
+
- **Event handlers, `watch` callbacks, timers, and `.then()` callbacks do not inherit
|
|
88
|
+
component injection context.** Resolve dependencies before registering the callback,
|
|
89
|
+
or deliberately re-enter the owning app with `app.runWithContext()`.
|
|
90
|
+
- **A hand-written `async setup()` is not compiler-wrapped.** Resolve dependencies
|
|
91
|
+
before its first `await`.
|
|
92
|
+
|
|
93
|
+
Resolve the composable up front and call the returned functions later.
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
// ✅ resolve in setup, call after the await
|
|
97
|
+
const { dispatchCheck } = usePromotionsChecker()
|
|
98
|
+
|
|
99
|
+
onMounted(async () => {
|
|
100
|
+
await initializeOrder()
|
|
101
|
+
dispatchCheck()
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
// ❌ resolving after an await — inject() has no instance to read from
|
|
105
|
+
onMounted(async () => {
|
|
106
|
+
await initializeOrder()
|
|
107
|
+
const { dispatchCheck } = usePromotionsChecker()
|
|
108
|
+
})
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The same applies inside event handlers, `watch` callbacks, timers, and `.then()` blocks:
|
|
112
|
+
resolve in `setup()`, keep the reference, call it from the callback. Watch for this
|
|
113
|
+
inside `try` blocks in particular — the `catch` swallows the failure and reports it as
|
|
114
|
+
whatever the surrounding operation was supposed to do, so the real cause never surfaces.
|
|
115
|
+
|
|
116
|
+
**Stores fail differently, and worse.** The Pinia stores exported from
|
|
117
|
+
`@zenky/storefront-vue/stores` read the runtime too, but a missed context does not throw:
|
|
118
|
+
Pinia falls back to its module-level active instance. Under concurrent SSR that instance
|
|
119
|
+
belongs to whichever request touched it last, so the lookup succeeds with another user's
|
|
120
|
+
state instead of failing.
|
|
121
|
+
|
|
122
|
+
Two cases behave differently, and the distinction is verified rather than assumed:
|
|
123
|
+
|
|
124
|
+
- **Inside a store action — safe.** Pinia activates the action's own instance for the
|
|
125
|
+
duration of the call, so resolving another store there returns the right one.
|
|
126
|
+
- **In a plain function after an `await`, outside any action and outside
|
|
127
|
+
`runWithContext()` — leaks.** This is the shape to watch for: it is silent, and it
|
|
128
|
+
crosses requests.
|
|
129
|
+
|
|
130
|
+
Resolve core stores in `setup()` for the same reason you resolve composables there. A
|
|
131
|
+
silent leak is far harder to notice than an exception.
|
|
132
|
+
|
|
76
133
|
## Runtime Ports
|
|
77
134
|
|
|
78
|
-
`createStorefront(runtime)` takes one object.
|
|
135
|
+
`createStorefront(runtime)` takes one object. Five ports are required; the rest are optional — omit an optional port and the feature that needs it is simply skipped.
|
|
79
136
|
|
|
80
137
|
| Port | Required | What the consumer provides |
|
|
81
138
|
| --- | --- | --- |
|
|
82
139
|
| `sdk` | yes | A `ZenkyStorefront` instance (`@zenky/storefront-api`). |
|
|
83
|
-
| `config` |
|
|
140
|
+
| `config` | no | Reserved consumer configuration; the current core does not read it. |
|
|
84
141
|
| `storage` | yes | Key/value persistence — cookies, `localStorage`, memory. |
|
|
85
142
|
| `notifier` | yes | Toasts: `error` / `success` / `warning` / `message`. |
|
|
86
143
|
| `messages` | yes | Maps stable domain message keys to localized consumer copy. |
|
|
@@ -186,13 +243,15 @@ npm run test:coverage # vitest + V8 coverage report
|
|
|
186
243
|
npm run check # format + lint + types + tests + build + exports
|
|
187
244
|
npm run check:exports # publint + are-the-types-wrong (esm-only profile)
|
|
188
245
|
npm run check:boundaries # reject Nuxt/Unhead and consumer policy in src/
|
|
246
|
+
npm run check:context # reject ambient composables/stores in deferred helpers
|
|
189
247
|
npm run check:runtime # import all entries in SSR + reject Node-only output
|
|
190
248
|
npm run check:size # enforce gzip bundle budgets
|
|
191
249
|
```
|
|
192
250
|
|
|
193
251
|
### Local linking with yalc
|
|
194
252
|
|
|
195
|
-
|
|
253
|
+
To iterate on an unreleased change without cutting a version, link the package into a
|
|
254
|
+
consumer app with [yalc](https://github.com/wclr/yalc):
|
|
196
255
|
|
|
197
256
|
```bash
|
|
198
257
|
# in this package, after a build:
|
|
@@ -213,6 +272,7 @@ npm run build && npx yalc push
|
|
|
213
272
|
- `typecheck` — type-only compile.
|
|
214
273
|
- `check:exports` — validate the exports map and published types.
|
|
215
274
|
- `check:boundaries` — reject Nuxt/Unhead APIs, routes, and tenant presets in core source.
|
|
275
|
+
- `check:context` — reject deferred composable/store resolution outside composition setup.
|
|
216
276
|
- `check:runtime` — verify Node SSR imports and browser/edge-safe ESM output.
|
|
217
277
|
- `check:size` — enforce root/domain/all-output gzip budgets.
|
|
218
278
|
- `check:ci` — quality gate with the V8 coverage ratchet.
|
package/dist/cart/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
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-
|
|
2
|
-
export { useCartCredentials } from '../chunk-
|
|
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-SPMUTUNX.js';
|
|
2
|
+
export { useCartCredentials } from '../chunk-4YAEIKLA.js';
|
|
3
3
|
import '../chunk-Z3IW4ODZ.js';
|
|
4
|
-
import '../chunk-
|
|
4
|
+
import '../chunk-SHASMBMV.js';
|
|
5
5
|
import '../chunk-E3G7NGCH.js';
|
|
6
6
|
import '../chunk-RO25KNM5.js';
|
|
7
7
|
import '../chunk-5QCUH3FF.js';
|
|
8
|
-
import '../chunk-
|
|
8
|
+
import '../chunk-CUSQYIEU.js';
|
|
9
9
|
//# sourceMappingURL=index.js.map
|
|
10
10
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useCartStore, usePromotionsStore } from './chunk-
|
|
2
|
-
import { useRuntime, useAuthStore, useStoreProfileStore } from './chunk-
|
|
1
|
+
import { useCartStore, usePromotionsStore } from './chunk-SHASMBMV.js';
|
|
2
|
+
import { useRuntime, useAuthStore, useStoreProfileStore } from './chunk-CUSQYIEU.js';
|
|
3
3
|
import { ref } from 'vue';
|
|
4
4
|
|
|
5
5
|
var KEY_ORDER_ID = "zenky_order_id";
|
|
@@ -105,5 +105,5 @@ function usePromotionsChecker() {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
export { useCartCredentials, usePromotionsChecker };
|
|
108
|
-
//# sourceMappingURL=chunk-
|
|
109
|
-
//# sourceMappingURL=chunk-
|
|
108
|
+
//# sourceMappingURL=chunk-4YAEIKLA.js.map
|
|
109
|
+
//# sourceMappingURL=chunk-4YAEIKLA.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-4YAEIKLA.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 *\n * Резолвить синхронно в setup, а после await вызывать только `dispatchCheck` — как и\n * любой композабл ядра, см. раздел «Call composables and stores synchronously in setup»\n * в README.\n *\n * ```ts\n * const { dispatchCheck } = usePromotionsChecker()\n *\n * async function addGift() {\n * await addVariantToOrder()\n * dispatchCheck()\n * }\n * ```\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"]}
|
|
@@ -5,7 +5,6 @@ import { defineStore } from 'pinia';
|
|
|
5
5
|
var RUNTIME_KEY = /* @__PURE__ */ Symbol.for("zenky.storefront.runtime");
|
|
6
6
|
var REQUIRED_PORTS = [
|
|
7
7
|
"sdk",
|
|
8
|
-
"config",
|
|
9
8
|
"storage",
|
|
10
9
|
"notifier",
|
|
11
10
|
"activity",
|
|
@@ -88,7 +87,7 @@ var AUTH_TOKEN_KEY = "zenky_auth_token";
|
|
|
88
87
|
var AUTH_PHONE_KEY = "zenky_auth_phone";
|
|
89
88
|
var YEAR_SECONDS = 60 * 60 * 24 * 365;
|
|
90
89
|
var useAuthStore = defineStore("zenky-auth", () => {
|
|
91
|
-
const { storage
|
|
90
|
+
const { storage } = useRuntime();
|
|
92
91
|
const token = ref(null);
|
|
93
92
|
const phone = ref(null);
|
|
94
93
|
const countryCode = ref("RU");
|
|
@@ -100,7 +99,6 @@ var useAuthStore = defineStore("zenky-auth", () => {
|
|
|
100
99
|
function setToken(nextToken, nextPhone, nextCountryCode) {
|
|
101
100
|
token.value = nextToken;
|
|
102
101
|
storage.set(AUTH_TOKEN_KEY, nextToken, { maxAge: YEAR_SECONDS });
|
|
103
|
-
sdk.setApiToken(nextToken);
|
|
104
102
|
if (nextPhone) {
|
|
105
103
|
phone.value = nextPhone;
|
|
106
104
|
storage.set(AUTH_PHONE_KEY, nextPhone, { maxAge: YEAR_SECONDS });
|
|
@@ -114,18 +112,11 @@ var useAuthStore = defineStore("zenky-auth", () => {
|
|
|
114
112
|
phone.value = null;
|
|
115
113
|
storage.remove(AUTH_TOKEN_KEY);
|
|
116
114
|
storage.remove(AUTH_PHONE_KEY);
|
|
117
|
-
sdk.setApiToken(null);
|
|
118
115
|
}
|
|
119
116
|
function initFromCookie() {
|
|
120
|
-
const savedToken = storage.get(AUTH_TOKEN_KEY);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
sdk.setApiToken(savedToken);
|
|
124
|
-
}
|
|
125
|
-
const savedPhone = storage.get(AUTH_PHONE_KEY);
|
|
126
|
-
if (savedPhone) {
|
|
127
|
-
phone.value = savedPhone;
|
|
128
|
-
}
|
|
117
|
+
const savedToken = storage.get(AUTH_TOKEN_KEY) || null;
|
|
118
|
+
token.value = savedToken;
|
|
119
|
+
phone.value = storage.get(AUTH_PHONE_KEY) || null;
|
|
129
120
|
}
|
|
130
121
|
return {
|
|
131
122
|
token,
|
|
@@ -140,5 +131,5 @@ var useAuthStore = defineStore("zenky-auth", () => {
|
|
|
140
131
|
});
|
|
141
132
|
|
|
142
133
|
export { createStorefront, provideStorefront, useAuthStore, useRuntime, useStoreProfileStore };
|
|
143
|
-
//# sourceMappingURL=chunk-
|
|
144
|
-
//# sourceMappingURL=chunk-
|
|
134
|
+
//# sourceMappingURL=chunk-CUSQYIEU.js.map
|
|
135
|
+
//# sourceMappingURL=chunk-CUSQYIEU.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-CUSQYIEU.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 '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 // Токен здесь только хранится и отдаётся наружу — в SDK он не пишется. Потребитель\n // передаёт его параметром в каждый вызов: мутация разделяемого клиента на SSR\n // означала бы Bearer одного пользователя в запросах других.\n const { storage } = 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\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 }\n\n function initFromCookie(): void {\n const savedToken = storage.get(AUTH_TOKEN_KEY) || null\n token.value = savedToken\n\n phone.value = storage.get(AUTH_PHONE_KEY) || null\n }\n\n return {\n token,\n phone,\n countryCode,\n isAuthenticated,\n initials,\n setToken,\n clearToken,\n initFromCookie,\n }\n})\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useStoreProfileStore } from './chunk-
|
|
1
|
+
import { useStoreProfileStore } from './chunk-CUSQYIEU.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-
|
|
38
|
-
//# sourceMappingURL=chunk-
|
|
37
|
+
//# sourceMappingURL=chunk-CY6LA6C7.js.map
|
|
38
|
+
//# sourceMappingURL=chunk-CY6LA6C7.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-
|
|
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-CY6LA6C7.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"]}
|
|
@@ -40,5 +40,5 @@ function createMemoryStorage() {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
export { KEY_MESSAGE_FORMATTER, NOOP_ACTIVITY_REPORTER, NOOP_NOTIFIER, NOOP_RUM, createMemoryStorage, err, ok };
|
|
43
|
-
//# sourceMappingURL=chunk-
|
|
44
|
-
//# sourceMappingURL=chunk-
|
|
43
|
+
//# sourceMappingURL=chunk-MOG6UMFM.js.map
|
|
44
|
+
//# sourceMappingURL=chunk-MOG6UMFM.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ports/index.ts"],"names":[],"mappings":";AAwIO,IAAM,KAAK,CAAI,KAAA,MAAgC,EAAE,EAAA,EAAI,MAAM,KAAA,EAAM;AACjE,IAAM,MAAM,CAAI,KAAA,MAAgC,EAAE,EAAA,EAAI,OAAO,KAAA,EAAM;
|
|
1
|
+
{"version":3,"sources":["../src/ports/index.ts"],"names":[],"mappings":";AAwIO,IAAM,KAAK,CAAI,KAAA,MAAgC,EAAE,EAAA,EAAI,MAAM,KAAA,EAAM;AACjE,IAAM,MAAM,CAAI,KAAA,MAAgC,EAAE,EAAA,EAAI,OAAO,KAAA,EAAM;AA2BnE,IAAM,aAAA,GAA0B;AAAA,EACnC,OAAO,MAAM;AAAA,EAAC,CAAA;AAAA,EACd,SAAS,MAAM;AAAA,EAAC,CAAA;AAAA,EAChB,SAAS,MAAM;AAAA,EAAC,CAAA;AAAA,EAChB,SAAS,MAAM;AAAA,EAAC;AACpB;AAGO,IAAM,qBAAA,GAA0C;AAAA,EACnD,MAAA,EAAQ,CAAC,GAAA,KAAQ;AACrB;AAEO,IAAM,sBAAA,GAA2C;AAAA,EACpD,OAAO,MAAM;AAAA,EAAC,CAAA;AAAA,EACd,KAAK,MAAM;AAAA,EAAC;AAChB;AAEO,IAAM,QAAA,GAAwB;AAAA,EACjC,MAAM,MAAM;AAAA,EAAC,CAAA;AAAA,EACb,SAAS,MAAM;AAAA,EAAC;AACpB;AAGO,SAAS,mBAAA,GAAsC;AAClD,EAAA,MAAM,GAAA,uBAAU,GAAA,EAAoB;AACpC,EAAA,OAAO;AAAA,IACH,KAAK,CAAC,GAAA,KAAQ,GAAA,CAAI,GAAA,CAAI,GAAG,CAAA,IAAK,IAAA;AAAA,IAC9B,GAAA,EAAK,CAAC,GAAA,EAAK,KAAA,KAAU;AACjB,MAAA,GAAA,CAAI,GAAA,CAAI,KAAK,KAAK,CAAA;AAAA,IACtB,CAAA;AAAA,IACA,MAAA,EAAQ,CAAC,GAAA,KAAQ;AACb,MAAA,GAAA,CAAI,OAAO,GAAG,CAAA;AAAA,IAClB;AAAA,GACJ;AACJ","file":"chunk-MOG6UMFM.js","sourcesContent":["// Ports — интерфейсы того, что ядру нужно от рантайма потребителя.\n// Каждый фронт (Nuxt / QR-меню / шаблон) приносит свои адаптеры.\n// Ядро НЕ знает про useCookie, useRuntimeConfig, vue-router и т.п.\n\nimport type { Ref } from 'vue'\n\nimport type { ZenkyIntegrationsSDK } from '@zenky/integrations-sdk'\nimport type { RemoteCatalog, ZenkyStorefront } from '@zenky/storefront-api'\n\nexport interface StorageOptions {\n /** Время жизни в секундах. */\n maxAge?: number\n sameSite?: 'strict' | 'lax' | 'none'\n secure?: boolean\n path?: string\n}\n\n/** Персистентное key-value хранилище (куки / localStorage / память). */\nexport interface StorageAdapter {\n get(key: string): string | null\n set(key: string, value: string, options?: StorageOptions): void\n remove(key: string, options?: StorageOptions): void\n}\n\n/** Конфиг рантайма, который ядро не должно знать откуда берётся. */\nexport interface StorefrontConfig {\n baseUrl: string\n baseAuthUrl: string\n /** Опциональный sales channel для креативов/каналов. */\n salesChannelId?: string\n}\n\n/** Уведомления пользователю (тосты). Реализация — на стороне потребителя. */\nexport interface Notifier {\n error(message: string): void\n success(message: string): void\n warning(message: string): void\n message(title: string, options?: { description?: string }): void\n}\n\n/** Stable message identifiers emitted by domain logic. Text and locale belong to the consumer. */\nexport type StorefrontMessageKey =\n | 'cart.added.title'\n | 'cart.bonuses.changed.description'\n | 'cart.bonuses.changed.title'\n | 'cart.customer.saveFailed'\n | 'cart.customer.updateAfterLoginFailed'\n | 'cart.customer.updated'\n | 'cart.customer.updatedWithName'\n | 'cart.payment.updateFailed'\n | 'cart.promocode.applyFailed'\n | 'cart.promocode.removeFailed'\n | 'cart.stock.catalogReloadFailed'\n | 'cart.stock.checkFailed'\n | 'cart.stock.itemsRemoved'\n | 'cart.stock.pricesUpdated'\n | 'cart.updateFailed'\n\nexport type StorefrontMessageParams = Readonly<\n Record<string, string | number | boolean | null | undefined>\n>\n\n/** Converts domain message identifiers into consumer-owned, localized UI copy. */\nexport interface MessageFormatter {\n format(key: StorefrontMessageKey, params?: StorefrontMessageParams): string\n}\n\n/** Репортер активности (busy-индикаторы, длительные операции). */\nexport interface ActivityReporter {\n begin(kind: string): void\n end(kind: string): void\n}\n\n/** Realtime-канал: подписка на события (WebSocket). Ядро не знает про pusher/транспорт. */\nexport interface RealtimeChannel {\n bind(event: string, handler: (data?: unknown) => void): void\n}\n\nexport interface RealtimeAdapter {\n subscribe(channelName: string): RealtimeChannel | null\n unsubscribe(channelName: string): void\n isReady: Ref<boolean>\n}\n\nexport interface RealtimePort {\n get(): RealtimeAdapter | null\n}\n\nexport interface EventsPort {\n get(): ZenkyIntegrationsSDK | null\n}\n\n/** Перезагрузка каталога при смене склада. Транспорт (fetch) — на стороне потребителя. */\nexport interface CatalogReloader {\n reload(storeId: string, remoteCatalog: RemoteCatalog): Promise<void>\n}\n\n/** RUM-репортер производительности (performance-marks). Реализация — у потребителя. */\nexport interface RumReporter {\n mark(name: string): void\n measure(metric: string, startMark: string): void\n}\n\n/** Идентификаторы commerce-события (session/anonymous) — попадают в checkout-request. */\nexport interface CommerceEventIdentity {\n session_id?: string\n anonymous_id?: string\n}\n\nexport interface CommerceAddedToCartPayload {\n productId: string\n productVariantId: string\n quantity?: number\n unitPrice?: number\n totalPrice?: number\n}\n\nexport interface CommerceRemovedFromCartPayload {\n productId: string\n productVariantId: string\n quantity?: number\n}\n\n/** Commerce-event трекинг (#29). Реализация/очередь/транспорт — у потребителя. */\nexport interface CommerceEventsPort {\n trackAddedToCart(orderId: string, payload: CommerceAddedToCartPayload): void\n trackRemovedFromCart(orderId: string, payload: CommerceRemovedFromCartPayload): void\n getIdentity(): CommerceEventIdentity\n}\n\n/**\n * Railway-style результат: компилятор не даст прочитать `.value`,\n * пока потребитель не проверил `.ok`. Замена throw на публичной границе.\n */\nexport type Result<T = void, E = unknown> = { ok: true; value: T } | { ok: false; error: E }\n\nexport const ok = <T>(value: T): Result<T, never> => ({ ok: true, value })\nexport const err = <E>(error: E): Result<never, E> => ({ ok: false, error })\n\n/** Полный рантайм, который потребитель собирает и отдаёт в createStorefront(). */\nexport interface StorefrontRuntime {\n /** SDK-клиент Zenky. Потребитель создаёт один инстанс и отдаёт сюда. */\n sdk: ZenkyStorefront\n storage: StorageAdapter\n /** Reserved consumer configuration. The current core does not read this port. */\n config?: StorefrontConfig\n notifier: Notifier\n /** Localized user-facing copy. The core emits only stable message identifiers. */\n messages: MessageFormatter\n activity: ActivityReporter\n /** Ленивый провайдер analytics-интеграции, которая может появиться после setup. */\n events?: EventsPort | null\n /** Ленивый провайдер realtime-транспорта (WebSocket). */\n realtime?: RealtimePort | null\n /** Перезагрузка каталога при смене склада. Нет — reload пропускается. */\n catalogReloader?: CatalogReloader | null\n /** RUM-метрики. Нет — инструментирование пропускается. */\n rum?: RumReporter | null\n /** Commerce-event трекинг (#29). Нет — трекинг пропускается. */\n commerceEvents?: CommerceEventsPort | null\n}\n\n// --- NOOP / in-memory заглушки (SSR, тесты, дефолты) ---\n\nexport const NOOP_NOTIFIER: Notifier = {\n error: () => {},\n success: () => {},\n warning: () => {},\n message: () => {},\n}\n\n/** Useful in headless tests where displaying a localized message is not required. */\nexport const KEY_MESSAGE_FORMATTER: MessageFormatter = {\n format: (key) => key,\n}\n\nexport const NOOP_ACTIVITY_REPORTER: ActivityReporter = {\n begin: () => {},\n end: () => {},\n}\n\nexport const NOOP_RUM: RumReporter = {\n mark: () => {},\n measure: () => {},\n}\n\n/** In-memory storage — для SSR/тестов, без побочных эффектов на реальные куки. */\nexport function createMemoryStorage(): StorageAdapter {\n const map = new Map<string, string>()\n return {\n get: (key) => map.get(key) ?? null,\n set: (key, value) => {\n map.set(key, value)\n },\n remove: (key) => {\n map.delete(key)\n },\n }\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createDebounce } from './chunk-XUGXLJZB.js';
|
|
2
2
|
import { createRuntimeState } from './chunk-5QCUH3FF.js';
|
|
3
|
-
import { useRuntime, useStoreProfileStore, useAuthStore } from './chunk-
|
|
3
|
+
import { useRuntime, useStoreProfileStore, useAuthStore } from './chunk-CUSQYIEU.js';
|
|
4
4
|
import { UnitType } from '@zenky/storefront-api';
|
|
5
5
|
import { computed, ref, watch } from 'vue';
|
|
6
6
|
|
|
@@ -561,5 +561,5 @@ function findProductInCatalog(catalog, shortId) {
|
|
|
561
561
|
}
|
|
562
562
|
|
|
563
563
|
export { canAddDirectly, findProductInCatalog, formatWeight, getProductWeight, getVariantWeight, resolveVariantPrice, toGrams, usePriceRecalculation, useProductPricing, useProductVariant, useProductVariantModifiers, useProductVariantSelector, useProductsByCategory, useRelatedProducts };
|
|
564
|
-
//# sourceMappingURL=chunk-
|
|
565
|
-
//# sourceMappingURL=chunk-
|
|
564
|
+
//# sourceMappingURL=chunk-Q2TVQ3BG.js.map
|
|
565
|
+
//# sourceMappingURL=chunk-Q2TVQ3BG.js.map
|