@zenky/storefront-vue 0.10.9 → 0.10.11

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 (67) hide show
  1. package/CHANGELOG.md +162 -0
  2. package/README.md +1 -1
  3. package/dist/auth/index.d.ts +14 -0
  4. package/dist/auth/index.js +4 -0
  5. package/dist/auth/index.js.map +1 -0
  6. package/dist/cart/index.d.ts +3 -28
  7. package/dist/cart/index.js +9 -7
  8. package/dist/catalog/index.d.ts +5 -3
  9. package/dist/catalog/index.js +2 -2
  10. package/dist/catalogEntity-DgrPsJpf.d.ts +44 -0
  11. package/dist/{chunk-AVES4WJZ.js → chunk-5CNL24PI.js} +11 -108
  12. package/dist/chunk-5CNL24PI.js.map +1 -0
  13. package/dist/{chunk-RF4ML3Q3.js → chunk-6FVXAF7L.js} +5 -4
  14. package/dist/chunk-6FVXAF7L.js.map +1 -0
  15. package/dist/chunk-AK4276ND.js +67 -0
  16. package/dist/chunk-AK4276ND.js.map +1 -0
  17. package/dist/{chunk-KFCO46RB.js → chunk-FS2UNPTY.js} +2 -2
  18. package/dist/chunk-FS2UNPTY.js.map +1 -0
  19. package/dist/chunk-IOYYCA75.js +52 -0
  20. package/dist/chunk-IOYYCA75.js.map +1 -0
  21. package/dist/chunk-KX7KXN3W.js +529 -0
  22. package/dist/chunk-KX7KXN3W.js.map +1 -0
  23. package/dist/{chunk-6QCFHSLX.js → chunk-NSOIL7PW.js} +2 -2
  24. package/dist/chunk-NSOIL7PW.js.map +1 -0
  25. package/dist/{chunk-RK5CGOM7.js → chunk-OBNMXLJO.js} +19 -3
  26. package/dist/chunk-OBNMXLJO.js.map +1 -0
  27. package/dist/{chunk-GXDTRTUT.js → chunk-RURXGVWW.js} +35 -6
  28. package/dist/chunk-RURXGVWW.js.map +1 -0
  29. package/dist/{chunk-XULHV6SS.js → chunk-TE2QPH74.js} +3 -3
  30. package/dist/{chunk-XULHV6SS.js.map → chunk-TE2QPH74.js.map} +1 -1
  31. package/dist/chunk-TE42OCFK.js +102 -0
  32. package/dist/chunk-TE42OCFK.js.map +1 -0
  33. package/dist/{chunk-GILJFP55.js → chunk-VTZYJ6OZ.js} +2 -2
  34. package/dist/chunk-VTZYJ6OZ.js.map +1 -0
  35. package/dist/{chunk-46FNHMWC.js → chunk-W43D7JYV.js} +5 -4
  36. package/dist/chunk-W43D7JYV.js.map +1 -0
  37. package/dist/{chunk-QZ3S3NN3.js → chunk-WTZXTBF6.js} +3 -47
  38. package/dist/chunk-WTZXTBF6.js.map +1 -0
  39. package/dist/index.d.ts +6 -3
  40. package/dist/index.js +16 -13
  41. package/dist/ports/index.d.ts +1 -1
  42. package/dist/ports/index.js +1 -1
  43. package/dist/products/index.d.ts +4 -3
  44. package/dist/products/index.js +4 -3
  45. package/dist/promotions/index.js +4 -3
  46. package/dist/seo/index.d.ts +5 -7
  47. package/dist/seo/index.js +16 -9
  48. package/dist/seo/index.js.map +1 -1
  49. package/dist/stores/index.d.ts +60 -4
  50. package/dist/stores/index.js +7 -4
  51. package/dist/types-CXm2F6Hz.d.ts +55 -0
  52. package/dist/useCheckoutSubmit-Be-lQd60.d.ts +30 -0
  53. package/dist/utils/index.d.ts +6 -1
  54. package/dist/utils/index.js +1 -1
  55. package/docs/architecture.md +81 -4
  56. package/package.json +7 -3
  57. package/dist/chunk-46FNHMWC.js.map +0 -1
  58. package/dist/chunk-6QCFHSLX.js.map +0 -1
  59. package/dist/chunk-AVES4WJZ.js.map +0 -1
  60. package/dist/chunk-FXKG5TFQ.js +0 -38
  61. package/dist/chunk-FXKG5TFQ.js.map +0 -1
  62. package/dist/chunk-GILJFP55.js.map +0 -1
  63. package/dist/chunk-GXDTRTUT.js.map +0 -1
  64. package/dist/chunk-KFCO46RB.js.map +0 -1
  65. package/dist/chunk-QZ3S3NN3.js.map +0 -1
  66. package/dist/chunk-RF4ML3Q3.js.map +0 -1
  67. package/dist/chunk-RK5CGOM7.js.map +0 -1
@@ -0,0 +1,30 @@
1
+ import { Ref } from 'vue';
2
+ import { OrderCheckoutResult, RecaptchaAction } from '@zenky/storefront-api';
3
+
4
+ /** reCAPTCHA payload provider (implemented by the consumer app — Google reCAPTCHA v3). */
5
+ type RecaptchaPayloadProvider = (action: RecaptchaAction) => Promise<{
6
+ token: string;
7
+ } | undefined>;
8
+ interface CheckoutSubmitComposable {
9
+ /** Last `checkoutOrder` result — needed by the UI (ConfirmationModal / online redirect) */
10
+ result: Ref<OrderCheckoutResult | null>;
11
+ submitting: Ref<boolean>;
12
+ /**
13
+ * Collects `OrderCheckoutRequest` from `cartStore` (notes + persons_count).
14
+ * Returns `OrderCheckoutResult` with confirmation/online_payment flags.
15
+ */
16
+ submit: () => Promise<OrderCheckoutResult | null>;
17
+ /** Confirm the SMS code (after checkoutOrder returned confirmation.required) */
18
+ confirm: (code: string) => Promise<boolean>;
19
+ /** Resend the code (with backoff — the UI is responsible for that) */
20
+ resendCode: () => Promise<boolean>;
21
+ cancel: () => Promise<boolean>;
22
+ }
23
+ /**
24
+ * Final order submit + SMS confirmation.
25
+ *
26
+ * @param getRecaptchaPayload reCAPTCHA token provider (app-specific, injected).
27
+ */
28
+ declare function useCheckoutSubmit(getRecaptchaPayload: RecaptchaPayloadProvider): CheckoutSubmitComposable;
29
+
30
+ export { type CheckoutSubmitComposable as C, type RecaptchaPayloadProvider as R, useCheckoutSubmit as u };
@@ -39,6 +39,11 @@ interface TodayScheduleInfo {
39
39
  declare function getTodayInfo(schedule: Schedule | null | undefined): TodayScheduleInfo | null;
40
40
  declare function getWeeklySchedule(schedule: Schedule | null | undefined, locale?: string): DayScheduleInfo[];
41
41
  declare function formatScheduleStatus(schedule: Schedule | null | undefined, locale?: string): string | null;
42
+ interface ScheduleRow {
43
+ label: string;
44
+ hours: string | null;
45
+ }
46
+ declare function groupScheduleDays(schedule: Schedule | null | undefined, locale?: string): ScheduleRow[];
42
47
 
43
48
  /**
44
49
  * Builds a QR code image URL through the Zenky media API.
@@ -86,4 +91,4 @@ declare function getApiFieldErrors(e: unknown): Record<string, string[]> | null;
86
91
  */
87
92
  declare function formatAddressCityLast(raw: string): string;
88
93
 
89
- export { type DayScheduleInfo, type OperationChain, type SocialLink, type TodayScheduleInfo, buildQrCodeUrl, createDebounce, createOperationChain, formatAddressCityLast, formatDate, formatDateTime, formatPhoneNational, formatPhoneSpaced, formatScheduleStatus, getApiErrorMessage, getApiFieldErrors, getEmailLink, getPhoneTelLink, getSocialLinks, getTodayInfo, getWebsiteLink, getWeeklySchedule, pluralize, toDateInputValue, todayInTimezone };
94
+ export { type DayScheduleInfo, type OperationChain, type ScheduleRow, type SocialLink, type TodayScheduleInfo, buildQrCodeUrl, createDebounce, createOperationChain, formatAddressCityLast, formatDate, formatDateTime, formatPhoneNational, formatPhoneSpaced, formatScheduleStatus, getApiErrorMessage, getApiFieldErrors, getEmailLink, getPhoneTelLink, getSocialLinks, getTodayInfo, getWebsiteLink, getWeeklySchedule, groupScheduleDays, pluralize, toDateInputValue, todayInTimezone };
@@ -1,4 +1,4 @@
1
- export { buildQrCodeUrl, createOperationChain, formatAddressCityLast, formatDate, formatDateTime, formatPhoneNational, formatPhoneSpaced, formatScheduleStatus, getApiErrorMessage, getApiFieldErrors, getEmailLink, getPhoneTelLink, getSocialLinks, getTodayInfo, getWebsiteLink, getWeeklySchedule, pluralize, toDateInputValue, todayInTimezone } from '../chunk-GXDTRTUT.js';
1
+ export { buildQrCodeUrl, createOperationChain, formatAddressCityLast, formatDate, formatDateTime, formatPhoneNational, formatPhoneSpaced, formatScheduleStatus, getApiErrorMessage, getApiFieldErrors, getEmailLink, getPhoneTelLink, getSocialLinks, getTodayInfo, getWebsiteLink, getWeeklySchedule, groupScheduleDays, pluralize, toDateInputValue, todayInTimezone } from '../chunk-RURXGVWW.js';
2
2
  export { createDebounce } from '../chunk-XUGXLJZB.js';
3
3
  //# sourceMappingURL=index.js.map
4
4
  //# sourceMappingURL=index.js.map
@@ -9,12 +9,17 @@ the consumer app.
9
9
  Each folder under `src/` is one subpath export (see the README's Subpath Imports
10
10
  section). This list says what each one owns.
11
11
 
12
+ - **`auth/`** — the authentication flow's shared types and channel-selection logic
13
+ (`AUTH_STEP`, `ConfirmationChannel`, `useAvailableConfirmationChannels`). The step
14
+ machine itself (`checkPhone`/`login`/`register`/`resendCode`/`dispatchReset`/
15
+ `resetPassword`) is the `authFlow` store in `stores/` — see below.
12
16
  - **`cart/`** — the largest domain. Cart mutation and quantities, checkout submission
13
17
  and confirmation, delivery and payment selection, bonuses, promocodes, customer
14
18
  attachment, and the credentials (order id/token) every cart request needs. Most
15
19
  checkout logic lives here.
16
20
  - **`catalog/`** — resolving which remote catalog snapshot applies to a city and stock,
17
- and pure lookup helpers (find a category, group products by subcategory).
21
+ resolving what a catalog item is about (a category or a products collection), and pure
22
+ lookup helpers (find a category, group products by subcategory).
18
23
  - **`money/`** — converting between minor units (kopecks) and major units (rubles).
19
24
  Nothing else in the package divides or multiplies a price by 100; everything else
20
25
  calls these functions.
@@ -34,12 +39,14 @@ section). This list says what each one owns.
34
39
  into `<head>` tags is the consumer's job (see the README's note on `useHead`).
35
40
  - **`snippets/`** — normalizing the snippet records a website carries (tracking scripts,
36
41
  custom CSS) into a consistent shape.
37
- - **`stores/`** — the seven Pinia stores: `auth`, `cart`, `catalog`, `products`,
38
- `promotions`, `store-profile`, `order-settings`. State and business logic only, no UI.
42
+ - **`stores/`** — the eight Pinia stores: `auth`, `authFlow`, `cart`, `catalog`,
43
+ `products`, `promotions`, `store-profile`, `order-settings`. State and business logic
44
+ only, no UI.
39
45
  - **`theme/`** — generating a full color palette from one brand hex value, and the CSS
40
46
  variable map the consumer injects into `<head>`.
41
47
  - **`utils/`** — small pure helpers with no domain attached: phone/date/address
42
- formatting, debouncing, pluralization, an operation chain for sequencing async work.
48
+ formatting, reading a store schedule (today's status, the week, grouped rows),
49
+ debouncing, pluralization, an operation chain for sequencing async work.
43
50
  - **`website-settings/`** — only the **runtime** shape of a website (what the storefront
44
51
  reads and renders: settings values, pages, snippets, integrations). The website
45
52
  builder's admin schema is a different concern — see below.
@@ -82,3 +89,73 @@ passed as an argument to that one function. `sdk`, `storage`, and the rest of th
82
89
  are the port case: dozens of composables across every domain need them, so they are
83
90
  resolved once through `useRuntime()` instead of being passed down through every caller in
84
91
  between.
92
+
93
+ `cart/useCheckoutSubmit.ts`'s `RecaptchaPayloadProvider` type is reused as-is by the
94
+ `authFlow` store (`stores/authFlow.ts`) instead of being redeclared — both domains need
95
+ the exact same shape at the exact same "parameter, not port" surface. If a third domain
96
+ ever needs it, this is the point to promote it into a shared location (e.g. `utils/`)
97
+ rather than adding a third copy.
98
+
99
+ ## Guarding async state against out-of-order responses
100
+
101
+ A store with several actions that can each be re-triggered before the previous call
102
+ settles (`authFlow`'s `checkPhone`/`login`/`register`/`resendCode`/`dispatchReset`, all
103
+ gated behind recaptcha and network round-trips a user can outrun by clicking again or
104
+ navigating away) needs to answer one question after every `await`: is this response
105
+ still the one the user is waiting for? Without that check, a slow, abandoned attempt
106
+ that finally resolves can overwrite state a newer attempt already set — the classic
107
+ last-write-doesn't-win race.
108
+
109
+ `authFlow` answers it with a single `operationRevision` counter plus four helpers:
110
+
111
+ - `invalidatePendingOperation()` — bumps the counter, nothing else. The primitive the
112
+ other three build on.
113
+ - `beginOperation()` — bumps the counter **and** sets `loading` to `false`, then returns
114
+ the new revision. Called once at the top of every **top-level** action (a call the UI
115
+ can trigger directly). Setting `loading` here — not leaving it to "the action's own
116
+ next line" — matters because that next line often isn't next: `register`/`resendCode`/
117
+ `dispatchReset` have a channel gate, and every request-making action checks `storeId`,
118
+ both of which can `return` before `loading.value = true` is ever reached. Without this,
119
+ a `checkPhone` in flight when the user triggers a `register()` that immediately exits
120
+ on the channel gate would invalidate `checkPhone`'s revision (so its own `finally`
121
+ loses the right to clear `loading`) while never reaching the line that would clear it
122
+ itself — `loading` stuck at `true` forever. An action that *does* reach its request
123
+ sets `loading.value = true` again a few lines later, in the same synchronous stretch
124
+ (no `await` in between), so this reads as one transition from the outside, not a
125
+ flicker.
126
+ - `isStale(revision)` — `true` once the counter has moved past the value an action
127
+ captured at its start. Checked after every `await` (recaptcha included) before the
128
+ action touches `currentStep`, `error`, `confirmationMethod`, or `loading` — if stale,
129
+ the action returns without touching state, including in `finally`.
130
+ - `cancelPendingOperation()` — same as `beginOperation()`, just for call sites that
131
+ don't need the revision because they aren't starting a new operation, only cancelling
132
+ the current one: every `goTo*`, `reset()`, `changeChannel()`. Separate name for
133
+ readability at the call site, not separate behavior: both clearing `loading` matters
134
+ in two distinct failure shapes — navigation cancelling an in-flight request with
135
+ nothing to replace it, and a fresh action's own early return doing the same before it
136
+ ever reaches its request. Diverging behavior here (one clearing `loading`, the other
137
+ assuming it doesn't need to) reintroduces the bug this guard exists to prevent.
138
+
139
+ A nested call that's a *continuation* of the same attempt rather than a new one
140
+ (`checkPhone`/`login`'s internal auto-resend, `register`'s 0-channel `login` fallback,
141
+ `selectChannel`'s dispatch) receives the parent's revision as an internal, non-public
142
+ parameter instead of calling `beginOperation()` — otherwise a parent would invalidate
143
+ itself the moment its own nested call started.
144
+
145
+ Two more things can go stale besides the revision counter itself. First, request inputs
146
+ read from mutable refs (`phone`, `country`, the channel an action is attempting) are
147
+ captured into local values before the first `await`, and that snapshot — not a live
148
+ read — is what the rest of the action uses, including any success or error handling
149
+ after the request resolves: reading `phone.value` or `confirmationMethod.value` again
150
+ after an `await` would pick up whatever the user (or unrelated code) changed it to
151
+ since, not what was actually submitted. Second, `phone`/`countryCode` invalidate the
152
+ revision on direct assignment too, via a small `customRef` (`invalidatingRef()`) instead
153
+ of a plain `ref()` — transparent to `v-model`, so the public type is still `Ref<string>`
154
+ and nothing about consuming it changes. This matters because those two are usually bound
155
+ straight to a form input: a user can edit the field without ever calling a store action,
156
+ and the revision counter alone — tied only to explicit action/navigation calls — would
157
+ never see that edit. Without it, a slow response for the phone number the user has since
158
+ changed could still land as if it were current.
159
+
160
+ This pattern is local to `authFlow` for now. If a second store grows the same shape of
161
+ problem, promote the counter/guard pair into `utils/` rather than re-deriving it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenky/storefront-vue",
3
- "version": "0.10.9",
3
+ "version": "0.10.11",
4
4
  "description": "Zenky Storefront SDK for Vue 3.",
5
5
  "keywords": [
6
6
  "zenky",
@@ -90,6 +90,10 @@
90
90
  "types": "./dist/stores/index.d.ts",
91
91
  "import": "./dist/stores/index.js"
92
92
  },
93
+ "./auth": {
94
+ "types": "./dist/auth/index.d.ts",
95
+ "import": "./dist/auth/index.js"
96
+ },
93
97
  "./package.json": "./package.json"
94
98
  },
95
99
  "scripts": {
@@ -115,7 +119,7 @@
115
119
  },
116
120
  "peerDependencies": {
117
121
  "@zenky/integrations-sdk": ">=0.1.3",
118
- "@zenky/storefront-api": "^1.0.1",
122
+ "@zenky/storefront-api": "^1.1.0",
119
123
  "pinia": "^3.0.0",
120
124
  "vue": "^3.5.0"
121
125
  },
@@ -125,7 +129,7 @@
125
129
  "@types/node": "^22.10.0",
126
130
  "@vitest/coverage-v8": "^4.1.10",
127
131
  "@zenky/integrations-sdk": "^0.1.3",
128
- "@zenky/storefront-api": "^1.0.1",
132
+ "@zenky/storefront-api": "^1.1.0",
129
133
  "pinia": "^3.0.4",
130
134
  "publint": "^0.3.21",
131
135
  "tsup": "^8.3.5",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cart/useCartCredentials.ts","../src/promotions/usePromotionsChecker.ts"],"names":[],"mappings":"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","file":"chunk-46FNHMWC.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-level enum: 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 * Manages order credentials. Order id/token persistence goes through the `storage`\n * port (the core doesn't know about useCookie). The refs are initialized from\n * storage; persisting writes to both.\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.apiToken\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/** Timeout — if the WS never responds, reset the checking flag. */\nconst WS_RESPONSE_TIMEOUT_MS = 5000\n\n/**\n * Checks promotions. Calls dispatchPromotionsChecker after the cart changes.\n * The result arrives over WebSocket (handled in useOrderChannel).\n *\n * Resolve this synchronously in setup, and call only `dispatchCheck` after an\n * await — like any core composable, see \"Call composables and stores\n * synchronously in setup\" in the 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() // Clear old ones — WS will refill with current ones\n events?.get()?.promotionsCheckerStarted({ order: cartStore.order })\n\n try {\n const creds = credentials.getCredentials()\n await sdk.orders.dispatchPromotionsChecker(storeId, creds)\n\n setTimeout(() => {\n if (promotionsStore.checking) promotionsStore.setChecking(false)\n }, WS_RESPONSE_TIMEOUT_MS)\n } catch (e: unknown) {\n console.error('[promotions] check failed', e)\n promotionsStore.setChecking(false)\n }\n }\n\n return { dispatchCheck }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/stores/catalog.ts"],"names":[],"mappings":";;;;AAsBO,IAAM,eAAA,GAAkB,WAAA,CAAY,eAAA,EAAiB,MAAoB;AAC5E,EAAA,MAAM,OAAA,GAAU,GAAA,CAAmB,EAAE,CAAA;AACrC,EAAA,MAAM,WAAA,GAAc,IAAmB,IAAI,CAAA;AAC3C,EAAA,MAAM,SAAA,GAAY,IAAI,KAAK,CAAA;AAC3B,EAAA,MAAM,SAAA,GAAY,IAAI,KAAK,CAAA;AAE3B,EAAA,SAAS,WAAA,CAAY,OAAsB,IAAA,EAA4B;AACnE,IAAA,OAAA,CAAQ,KAAA,GAAQ,KAAA;AAChB,IAAA,WAAA,CAAY,QAAQ,IAAA,IAAQ,IAAA;AAC5B,IAAA,SAAA,CAAU,KAAA,GAAQ,KAAA;AAAA,EACtB;AAEA,EAAA,SAAS,YAAA,GAAqB;AAC1B,IAAA,SAAA,CAAU,KAAA,GAAQ,IAAA;AAAA,EACtB;AAEA,EAAA,SAAS,aAAa,KAAA,EAAsB;AACxC,IAAA,SAAA,CAAU,KAAA,GAAQ,KAAA;AAAA,EACtB;AAEA,EAAA,MAAM,aAAa,QAAA,CAAS,MAAM,OAAA,CAAQ,KAAA,CAAM,SAAS,CAAC,CAAA;AAE1D,EAAA,OAAO;AAAA,IACH,OAAA;AAAA,IACA,WAAA;AAAA,IACA,SAAA;AAAA,IACA,SAAA;AAAA,IACA,WAAA;AAAA,IACA,YAAA;AAAA,IACA,YAAA;AAAA,IACA;AAAA,GACJ;AACJ,CAAC","file":"chunk-6QCFHSLX.js","sourcesContent":["import { defineStore } from 'pinia'\nimport { computed, ref, type ComputedRef, type Ref } from 'vue'\n\nimport type { CatalogItem } from '@zenky/storefront-api'\n\n// Explicit return type (not inferred): otherwise Pinia's inference structurally\n// expands the SDK types in the .d.ts and bloats it. This also satisfies the\n// \"explicit return types\" rule.\nexport interface CatalogStore {\n catalog: Ref<CatalogItem[]>\n currentHash: Ref<string | null>\n reloading: Ref<boolean>\n loadError: Ref<boolean>\n initCatalog: (items: CatalogItem[], hash?: string | null) => void\n setLoadError: () => void\n setReloading: (value: boolean) => void\n hasCatalog: ComputedRef<boolean>\n}\n\n// Setup store (doc 05): id prefixed with zenky-* to avoid colliding with the\n// consumer's own stores. A plain state container — API calls and cookies live in\n// the composables/bootstrap layer, which calls these actions.\nexport const useCatalogStore = defineStore('zenky-catalog', (): CatalogStore => {\n const catalog = ref<CatalogItem[]>([])\n const currentHash = ref<string | null>(null)\n const reloading = ref(false)\n const loadError = ref(false)\n\n function initCatalog(items: CatalogItem[], hash?: string | null): void {\n catalog.value = items\n currentHash.value = hash ?? null\n loadError.value = false\n }\n\n function setLoadError(): void {\n loadError.value = true\n }\n\n function setReloading(value: boolean): void {\n reloading.value = value\n }\n\n const hasCatalog = computed(() => catalog.value.length > 0)\n\n return {\n catalog,\n currentHash,\n reloading,\n loadError,\n initCatalog,\n setLoadError,\n setReloading,\n hasCatalog,\n }\n})\n"]}