@zenky/storefront-vue 0.10.10 → 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.
- package/CHANGELOG.md +139 -0
- package/README.md +1 -1
- package/dist/auth/index.d.ts +14 -0
- package/dist/auth/index.js +4 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/cart/index.d.ts +3 -28
- package/dist/cart/index.js +8 -6
- package/dist/{chunk-4UMDIBW5.js → chunk-5CNL24PI.js} +10 -107
- package/dist/chunk-5CNL24PI.js.map +1 -0
- package/dist/{chunk-32SLDGE5.js → chunk-6FVXAF7L.js} +4 -3
- package/dist/chunk-6FVXAF7L.js.map +1 -0
- package/dist/chunk-AK4276ND.js +67 -0
- package/dist/chunk-AK4276ND.js.map +1 -0
- package/dist/chunk-IOYYCA75.js +52 -0
- package/dist/chunk-IOYYCA75.js.map +1 -0
- package/dist/chunk-KX7KXN3W.js +529 -0
- package/dist/chunk-KX7KXN3W.js.map +1 -0
- package/dist/{chunk-GXDTRTUT.js → chunk-RURXGVWW.js} +35 -6
- package/dist/chunk-RURXGVWW.js.map +1 -0
- package/dist/{chunk-XULHV6SS.js → chunk-TE2QPH74.js} +3 -3
- package/dist/{chunk-XULHV6SS.js.map → chunk-TE2QPH74.js.map} +1 -1
- package/dist/chunk-TE42OCFK.js +102 -0
- package/dist/chunk-TE42OCFK.js.map +1 -0
- package/dist/{chunk-GILJFP55.js → chunk-VTZYJ6OZ.js} +2 -2
- package/dist/chunk-VTZYJ6OZ.js.map +1 -0
- package/dist/{chunk-46FNHMWC.js → chunk-W43D7JYV.js} +5 -4
- package/dist/chunk-W43D7JYV.js.map +1 -0
- package/dist/{chunk-QZ3S3NN3.js → chunk-WTZXTBF6.js} +3 -47
- package/dist/chunk-WTZXTBF6.js.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +14 -11
- package/dist/ports/index.d.ts +1 -1
- package/dist/ports/index.js +1 -1
- package/dist/products/index.js +3 -2
- package/dist/promotions/index.js +4 -3
- package/dist/seo/index.js +5 -4
- package/dist/seo/index.js.map +1 -1
- package/dist/stores/index.d.ts +56 -1
- package/dist/stores/index.js +6 -3
- package/dist/types-CXm2F6Hz.d.ts +55 -0
- package/dist/useCheckoutSubmit-Be-lQd60.d.ts +30 -0
- package/dist/utils/index.d.ts +6 -1
- package/dist/utils/index.js +1 -1
- package/docs/architecture.md +79 -3
- package/package.json +7 -3
- package/dist/chunk-32SLDGE5.js.map +0 -1
- package/dist/chunk-46FNHMWC.js.map +0 -1
- package/dist/chunk-4UMDIBW5.js.map +0 -1
- package/dist/chunk-FXKG5TFQ.js +0 -38
- package/dist/chunk-FXKG5TFQ.js.map +0 -1
- package/dist/chunk-GILJFP55.js.map +0 -1
- package/dist/chunk-GXDTRTUT.js.map +0 -1
- package/dist/chunk-QZ3S3NN3.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -3,17 +3,19 @@ export { ActivityReporter, CatalogReloader, CommerceAddedToCartPayload, Commerce
|
|
|
3
3
|
import { Plugin } from 'vue';
|
|
4
4
|
export { Major, Minor, asMajor, asMinor, formatPrice, majorToMinor, minorToMajor, minorToMajorString } from './money/index.js';
|
|
5
5
|
export { FontConfig, PrimaryShades, ThemeColorConfig, ThemeData, buildCssVariableMap, generatePrimaryShades, generateShades, useTheme } from './theme/index.js';
|
|
6
|
-
export { DayScheduleInfo, OperationChain, SocialLink, TodayScheduleInfo, buildQrCodeUrl, createDebounce, createOperationChain, formatAddressCityLast, formatDate, formatDateTime, formatPhoneNational, formatPhoneSpaced, formatScheduleStatus, getApiErrorMessage, getApiFieldErrors, getEmailLink, getPhoneTelLink, getSocialLinks, getTodayInfo, getWebsiteLink, getWeeklySchedule, pluralize, toDateInputValue, todayInTimezone } from './utils/index.js';
|
|
6
|
+
export { DayScheduleInfo, OperationChain, ScheduleRow, SocialLink, TodayScheduleInfo, buildQrCodeUrl, createDebounce, createOperationChain, formatAddressCityLast, formatDate, formatDateTime, formatPhoneNational, formatPhoneSpaced, formatScheduleStatus, getApiErrorMessage, getApiFieldErrors, getEmailLink, getPhoneTelLink, getSocialLinks, getTodayInfo, getWebsiteLink, getWeeklySchedule, groupScheduleDays, pluralize, toDateInputValue, todayInTimezone } from './utils/index.js';
|
|
7
7
|
export { CatalogProductMatch, CategoryWithProducts, OrderCredentials, ProductListParams, ProductVariantOptionListItem, ResolvedPrice, UseProductDetailApi, UseProductListApi, canAddDirectly, findProductInCatalog, formatWeight, getProductWeight, getVariantWeight, resolveVariantPrice, toGrams, usePriceRecalculation, useProductDetail, useProductList, useProductPricing, useProductVariant, useProductVariantModifiers, useProductVariantSelector, useProductsByCategory, useRelatedProducts } from './products/index.js';
|
|
8
8
|
export { GroupedProducts, SubcategoryGroup, collectDescendantIds, findCategoryById, findCategoryByShortId, findRootAncestor, getCategoryPath, getCity, getRemoteCatalog, getStock, groupProductsBySubcategory, useCollections } from './catalog/index.js';
|
|
9
9
|
export { C as CatalogEntity, S as StaticCatalogItem, a as StaticRemoteCatalog, c as catalogItemEntity, i as isRenderableCatalogItem } from './catalogEntity-DgrPsJpf.js';
|
|
10
|
-
export { AttachedCustomerCache, BonusesReconciliation, BonusesReconciliationDeps, CASH_DENOMINATIONS_MAJOR, CachedCustomerView, CartCredentials, CartDependencies, CartErrorScope, CartInitialization, CartItemRequest, CartLineKeyParams, CartMutationComposable, CartOperations, CartQuantities, CartRecalculation, CartRecalculationDeps, CartStockWatcher, CashChangePresetsOptions, CheckoutFlowFlags,
|
|
10
|
+
export { AttachedCustomerCache, BonusesReconciliation, BonusesReconciliationDeps, CASH_DENOMINATIONS_MAJOR, CachedCustomerView, CartCredentials, CartDependencies, CartErrorScope, CartInitialization, CartItemRequest, CartLineKeyParams, CartMutationComposable, CartOperations, CartQuantities, CartRecalculation, CartRecalculationDeps, CartStockWatcher, CashChangePresetsOptions, CheckoutFlowFlags, CustomerAttachComposable, DEFAULT_ROUND_STEPS_MAJOR, MainPaymentRequestParams, MutationKind, MutationOptions, ORDER_EVENTS, ORDER_WITH, OrderBonusesComposable, OrderBonusesDeps, OrderCredentialsLike, OrderDelivery, OrderDeliveryDeps, OrderPromocodeComposable, OrderPromocodeDeps, OrderSettingsComposable, OrderTotal, PaymentSelection, PaymentSelectionComposable, RestrictedAddToCart, RestrictionGate, RestrictionGateProduct, RetryOptions, StorefrontCart, buildCartLineKey, buildCashChangePresets, buildMainPaymentRequest, getCartErrorCode, getCartErrorHttpCode, getCartErrorMessage, getCashDenominationsMinor, getDefaultRoundStepsMinor, getValidationErrors, logCartError, normalizeAddress, useBonusesReconciliation, useCartAddressSync, useCartCredentials, useCartCustomerAttachment, useCartDeliveryAttachment, useCartInitialization, useCartMutation, useCartOperations, useCartQuantities, useCartRecalculation, useCartStockWatcher, useCheckoutFlow, useCustomerAttach, useOnlinePaymentRetry, useOrderBonuses, useOrderChannel, useOrderDelivery, useOrderPromocode, useOrderSettings, useOrderTotal, usePaymentSelection, useRestrictedAddToCart, useRestrictionGate, useStorefrontCart, useVisibleCartVariants, variantLineKey } from './cart/index.js';
|
|
11
|
+
export { C as CheckoutSubmitComposable, R as RecaptchaPayloadProvider, u as useCheckoutSubmit } from './useCheckoutSubmit-Be-lQd60.js';
|
|
11
12
|
export { RawSnippetItem, Snippet, SnippetGroup, SnippetLanguage, SnippetPosition, normalizeSnippets } from './snippets/index.js';
|
|
12
13
|
export { SettingValue, Website, WebsitePage, WebsitePageListItem, WebsitePageSeo, WebsitePageType, WebsiteSettingsValues } from './website-settings/index.js';
|
|
13
|
-
export { AppliedBonusesIntent, AuthStore, CartStore, MinPriceViolation, OrderSettingsStore, PromotionsStore, RewardSelection, RewardSelectionContext, StoreProfileStore, useAuthStore, useCartStore, useCatalogStore, useOrderSettingsStore, useProductsStore, usePromotionsStore, useStore, useStoreProfileStore, useStorefrontProfile } from './stores/index.js';
|
|
14
|
+
export { AppliedBonusesIntent, AuthFlowStore, AuthStore, CartStore, MinPriceViolation, OrderSettingsStore, PromotionsStore, RESEND_TIMER_SECONDS, RewardSelection, RewardSelectionContext, StoreProfileStore, useAuthFlowStore, useAuthStore, useCartStore, useCatalogStore, useOrderSettingsStore, useProductsStore, usePromotionsStore, useStore, useStoreProfileStore, useStorefrontProfile } from './stores/index.js';
|
|
14
15
|
import '@zenky/integrations-sdk';
|
|
15
16
|
import '@zenky/storefront-api';
|
|
16
17
|
import 'pinia';
|
|
18
|
+
import './types-CXm2F6Hz.js';
|
|
17
19
|
|
|
18
20
|
/** Vue plugin: app.use(createStorefront(runtime)). */
|
|
19
21
|
declare function createStorefront(runtime: StorefrontRuntime): Plugin;
|
package/dist/index.js
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
|
+
export { canAddDirectly, findProductInCatalog, formatWeight, getProductWeight, getVariantWeight, resolveVariantPrice, toGrams, usePriceRecalculation, useProductDetail, useProductList, useProductPricing, useProductVariant, useProductVariantModifiers, useProductVariantSelector, useProductsByCategory, useRelatedProducts } from './chunk-6FVXAF7L.js';
|
|
1
2
|
export { normalizeSnippets } from './chunk-GLQXTMVW.js';
|
|
2
|
-
export { useProductsStore, useStore, useStorefrontProfile } from './chunk-
|
|
3
|
-
export { buildQrCodeUrl, createOperationChain, formatAddressCityLast, formatDate, formatDateTime, formatPhoneNational, formatPhoneSpaced, formatScheduleStatus, getApiErrorMessage, getApiFieldErrors, getEmailLink, getPhoneTelLink, getSocialLinks, getTodayInfo, getWebsiteLink, getWeeklySchedule, pluralize, toDateInputValue, todayInTimezone } from './chunk-GXDTRTUT.js';
|
|
3
|
+
export { RESEND_TIMER_SECONDS, useAuthFlowStore, useProductsStore, useStore, useStorefrontProfile } from './chunk-KX7KXN3W.js';
|
|
4
4
|
export { buildCssVariableMap, generatePrimaryShades, generateShades, useTheme } from './chunk-BUNI6MGH.js';
|
|
5
|
+
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';
|
|
6
|
+
export { createDebounce } from './chunk-XUGXLJZB.js';
|
|
5
7
|
import './chunk-EMGQXBU3.js';
|
|
6
|
-
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
8
|
+
import './chunk-AK4276ND.js';
|
|
9
|
+
export { CASH_DENOMINATIONS_MAJOR, DEFAULT_ROUND_STEPS_MAJOR, ORDER_EVENTS, ORDER_WITH, buildCartLineKey, buildCashChangePresets, buildMainPaymentRequest, getCashDenominationsMinor, getDefaultRoundStepsMinor, 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-5CNL24PI.js';
|
|
10
|
+
export { useCartCredentials } from './chunk-W43D7JYV.js';
|
|
11
|
+
import './chunk-5QCUH3FF.js';
|
|
12
|
+
export { getCartErrorCode, getCartErrorHttpCode, getCartErrorMessage, getValidationErrors, logCartError } from './chunk-TE42OCFK.js';
|
|
13
|
+
export { useCartStore, useOrderSettingsStore, usePromotionsStore } from './chunk-TE2QPH74.js';
|
|
14
|
+
export { useCatalogStore } from './chunk-NSOIL7PW.js';
|
|
15
|
+
export { createStorefront, provideStorefront, useAuthStore, useRuntime } from './chunk-WTZXTBF6.js';
|
|
16
|
+
export { useStoreProfileStore } from './chunk-IOYYCA75.js';
|
|
9
17
|
export { catalogItemEntity, collectDescendantIds, findCategoryById, findCategoryByShortId, findRootAncestor, getCategoryPath, groupProductsBySubcategory, isRenderableCatalogItem, useCollections } from './chunk-OBNMXLJO.js';
|
|
10
18
|
export { getCity, getRemoteCatalog, getStock } from './chunk-FS2UNPTY.js';
|
|
11
19
|
export { asMajor, asMinor, formatPrice, majorToMinor, minorToMajor, minorToMajorString } from './chunk-2Y6QTHXI.js';
|
|
12
|
-
export { KEY_MESSAGE_FORMATTER, NOOP_ACTIVITY_REPORTER, NOOP_NOTIFIER, NOOP_RUM, createMemoryStorage, err, ok } from './chunk-
|
|
13
|
-
export { canAddDirectly, findProductInCatalog, formatWeight, getProductWeight, getVariantWeight, resolveVariantPrice, toGrams, usePriceRecalculation, useProductDetail, useProductList, useProductPricing, useProductVariant, useProductVariantModifiers, useProductVariantSelector, useProductsByCategory, useRelatedProducts } from './chunk-32SLDGE5.js';
|
|
14
|
-
export { createDebounce } from './chunk-XUGXLJZB.js';
|
|
15
|
-
import './chunk-5QCUH3FF.js';
|
|
16
|
-
export { useCatalogStore } from './chunk-NSOIL7PW.js';
|
|
17
|
-
export { createStorefront, provideStorefront, useAuthStore, useRuntime, useStoreProfileStore } from './chunk-QZ3S3NN3.js';
|
|
20
|
+
export { KEY_MESSAGE_FORMATTER, NOOP_ACTIVITY_REPORTER, NOOP_NOTIFIER, NOOP_RUM, createMemoryStorage, err, ok } from './chunk-VTZYJ6OZ.js';
|
|
18
21
|
//# sourceMappingURL=index.js.map
|
|
19
22
|
//# sourceMappingURL=index.js.map
|
package/dist/ports/index.d.ts
CHANGED
|
@@ -32,7 +32,7 @@ interface Notifier {
|
|
|
32
32
|
}): void;
|
|
33
33
|
}
|
|
34
34
|
/** Stable message identifiers emitted by domain logic. Text and locale belong to the consumer. */
|
|
35
|
-
type StorefrontMessageKey = 'cart.added.title' | 'cart.bonuses.changed.description' | 'cart.bonuses.changed.title' | 'cart.customer.saveFailed' | 'cart.customer.updateAfterLoginFailed' | 'cart.customer.updated' | 'cart.customer.updatedWithName' | 'cart.payment.updateFailed' | 'cart.promocode.applyFailed' | 'cart.promocode.removeFailed' | 'cart.stock.catalogReloadFailed' | 'cart.stock.checkFailed' | 'cart.stock.itemsRemoved' | 'cart.stock.pricesUpdated' | 'cart.updateFailed';
|
|
35
|
+
type StorefrontMessageKey = 'auth.checkPhoneFailed' | 'auth.dispatchResetFailed' | 'auth.invalidCode' | 'auth.invalidPassword' | 'auth.otpUnavailable' | 'auth.registerFailed' | 'auth.resendCodeFailed' | 'auth.resetPasswordFailed' | 'cart.added.title' | 'cart.bonuses.changed.description' | 'cart.bonuses.changed.title' | 'cart.customer.saveFailed' | 'cart.customer.updateAfterLoginFailed' | 'cart.customer.updated' | 'cart.customer.updatedWithName' | 'cart.payment.updateFailed' | 'cart.promocode.applyFailed' | 'cart.promocode.removeFailed' | 'cart.stock.catalogReloadFailed' | 'cart.stock.checkFailed' | 'cart.stock.itemsRemoved' | 'cart.stock.pricesUpdated' | 'cart.updateFailed';
|
|
36
36
|
type StorefrontMessageParams = Readonly<Record<string, string | number | boolean | null | undefined>>;
|
|
37
37
|
/** Converts domain message identifiers into consumer-owned, localized UI copy. */
|
|
38
38
|
interface MessageFormatter {
|
package/dist/ports/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { KEY_MESSAGE_FORMATTER, NOOP_ACTIVITY_REPORTER, NOOP_NOTIFIER, NOOP_RUM, createMemoryStorage, err, ok } from '../chunk-
|
|
1
|
+
export { KEY_MESSAGE_FORMATTER, NOOP_ACTIVITY_REPORTER, NOOP_NOTIFIER, NOOP_RUM, createMemoryStorage, err, ok } from '../chunk-VTZYJ6OZ.js';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
package/dist/products/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export { canAddDirectly, findProductInCatalog, formatWeight, getProductWeight, getVariantWeight, resolveVariantPrice, toGrams, usePriceRecalculation, useProductDetail, useProductList, useProductPricing, useProductVariant, useProductVariantModifiers, useProductVariantSelector, useProductsByCategory, useRelatedProducts } from '../chunk-
|
|
1
|
+
export { canAddDirectly, findProductInCatalog, formatWeight, getProductWeight, getVariantWeight, resolveVariantPrice, toGrams, usePriceRecalculation, useProductDetail, useProductList, useProductPricing, useProductVariant, useProductVariantModifiers, useProductVariantSelector, useProductsByCategory, useRelatedProducts } from '../chunk-6FVXAF7L.js';
|
|
2
2
|
import '../chunk-XUGXLJZB.js';
|
|
3
3
|
import '../chunk-5QCUH3FF.js';
|
|
4
4
|
import '../chunk-NSOIL7PW.js';
|
|
5
|
-
import '../chunk-
|
|
5
|
+
import '../chunk-WTZXTBF6.js';
|
|
6
|
+
import '../chunk-IOYYCA75.js';
|
|
6
7
|
//# sourceMappingURL=index.js.map
|
|
7
8
|
//# sourceMappingURL=index.js.map
|
package/dist/promotions/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export { usePromotionsChecker } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
3
|
-
import '../chunk-
|
|
1
|
+
export { usePromotionsChecker } from '../chunk-W43D7JYV.js';
|
|
2
|
+
import '../chunk-TE2QPH74.js';
|
|
3
|
+
import '../chunk-WTZXTBF6.js';
|
|
4
|
+
import '../chunk-IOYYCA75.js';
|
|
4
5
|
//# sourceMappingURL=index.js.map
|
|
5
6
|
//# sourceMappingURL=index.js.map
|
package/dist/seo/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { resolveVariantPrice } from '../chunk-32SLDGE5.js';
|
|
1
|
+
import { resolveVariantPrice } from '../chunk-6FVXAF7L.js';
|
|
2
|
+
import { getSocialLinks } from '../chunk-RURXGVWW.js';
|
|
4
3
|
import '../chunk-XUGXLJZB.js';
|
|
5
4
|
import '../chunk-5QCUH3FF.js';
|
|
6
5
|
import '../chunk-NSOIL7PW.js';
|
|
7
|
-
import '../chunk-
|
|
6
|
+
import '../chunk-WTZXTBF6.js';
|
|
7
|
+
import '../chunk-IOYYCA75.js';
|
|
8
|
+
import { minorToMajorString } from '../chunk-2Y6QTHXI.js';
|
|
8
9
|
import { ContactType } from '@zenky/storefront-api';
|
|
9
10
|
|
|
10
11
|
// src/seo/utils.ts
|
package/dist/seo/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/seo/utils.ts","../../src/seo/siteOrigin.ts","../../src/seo/productSchema.ts","../../src/seo/listingSchema.ts","../../src/seo/articleSchema.ts","../../src/seo/organizationSchema.ts","../../src/seo/ogTags.ts","../../src/seo/entitySeo.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { Media, Store } from '@zenky/storefront-api'\n\n/**\n * Resolves the best available URL from a Media object.\n */\nexport function resolveMediaUrl(media: Media): string | null {\n return (\n media.hd?.url ||\n media.xlarge?.url ||\n media.large?.url ||\n media.medium?.url ||\n media.placeholder?.url ||\n null\n )\n}\n\n/**\n * Resolves store logo URL, preferring square logo over rectangular.\n * Tries all available size variants so CDN assets with only hd/xlarge are found too.\n */\nexport function resolveStoreLogoUrl(store: Store | null | undefined): string | null {\n if (!store) return null\n if (store.square_logo) {\n const url = resolveMediaUrl(store.square_logo)\n if (url) return url\n }\n if (store.logo) {\n return resolveMediaUrl(store.logo)\n }\n return null\n}\n\n/**\n * Strips HTML tags from a string.\n */\nexport function stripHtml(html: string): string {\n return html.replace(/<[^>]*>/g, '').trim()\n}\n\n/**\n * Prepares a string for use as og:description or meta description:\n * strips HTML tags and truncates to maxLength (default 160).\n */\nexport function prepareOgDescription(\n text: string | null | undefined,\n maxLength = 160\n): string | null {\n if (!text) return null\n const plain = stripHtml(text)\n if (!plain) return null\n return plain.length > maxLength ? `${plain.slice(0, maxLength - 3)}...` : plain\n}\n","/**\n * Canonical public origin of the site, from `store.domain`. The fallback (request host)\n * is supplied by the consumer — the core doesn't know about `useRequestURL`.\n */\nexport function resolveSiteOrigin(\n storeDomain: string | null | undefined,\n fallbackOrigin: string\n): string {\n const domain = storeDomain?.trim()\n if (domain) {\n const host = domain.replace(/^https?:\\/\\//, '').replace(/\\/+$/, '')\n return `https://${host}`\n }\n return fallbackOrigin\n}\n","import type { Product, ProductVariant } from '@zenky/storefront-api'\n\nimport { minorToMajorString } from '../money'\nimport { resolveVariantPrice } from '../products'\nimport { resolveMediaUrl } from './utils'\n\nfunction isProductInStock(variants: ProductVariant[]): boolean {\n if (!variants.length) return false\n return variants.some((variant) => {\n const remainders = variant.remainders\n if (!remainders?.length) return true\n return remainders.some((remainder) => remainder.quantity > 0)\n })\n}\n\n/**\n * JSON-LD schema.org/Product. Google uses it for Rich Results (price, availability,\n * rating in search listings).\n * Returns an array of 0 or 1 objects — the consumer wraps it in a `<script ld+json>` tag.\n * Spec: https://schema.org/Product\n */\nexport function buildProductSchema(product: Product, currency: string): Record<string, unknown>[] {\n const variants = product.variants ?? []\n\n const firstVariant = variants[0]\n let offerBlock: Record<string, unknown> | undefined\n\n if (firstVariant) {\n const resolved = resolveVariantPrice(firstVariant)\n\n offerBlock = {\n '@type': 'Offer',\n price: minorToMajorString(resolved.price, currency),\n priceCurrency: currency,\n availability: isProductInStock(variants)\n ? 'https://schema.org/InStock'\n : 'https://schema.org/OutOfStock',\n ...(firstVariant.sku ? { sku: firstVariant.sku } : {}),\n }\n }\n\n const images = (product.images ?? []).map(resolveMediaUrl).filter((url): url is string => !!url)\n\n const schema: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': 'Product',\n name: product.name,\n }\n\n if (product.description) schema.description = product.description\n if (images.length) schema.image = images\n if (offerBlock) schema.offers = offerBlock\n\n return [schema]\n}\n","import type { NestedCategory, Product, ProductsCollection } from '@zenky/storefront-api'\n\nimport { resolveMediaUrl } from './utils'\nimport type { BreadcrumbItem } from './types'\n\n/**\n * JSON-LD for a product listing page: BreadcrumbList + ItemList (first 10 products).\n * Returns an array of 0-2 JSON-LD objects. Category and collection pages differ only\n * in the list name, so both go through here.\n * Spec: https://schema.org/ItemList · https://schema.org/BreadcrumbList\n */\nfunction buildListingSchemas(\n name: string | null,\n products: Product[],\n breadcrumbs: BreadcrumbItem[],\n origin: string\n): Record<string, unknown>[] {\n const schemas: Record<string, unknown>[] = []\n\n if (breadcrumbs.length) {\n schemas.push({\n '@context': 'https://schema.org',\n '@type': 'BreadcrumbList',\n itemListElement: breadcrumbs.map((crumb, index) => ({\n '@type': 'ListItem',\n position: index + 1,\n name: crumb.label,\n ...(crumb.to ? { item: `${origin}${crumb.to}` } : {}),\n })),\n })\n }\n\n const items = products.slice(0, 10)\n if (items.length) {\n schemas.push({\n '@context': 'https://schema.org',\n '@type': 'ItemList',\n name,\n numberOfItems: items.length,\n itemListElement: items.map((product, index) => {\n const url = `${origin}/products/${product.short_id}/${product.slug || product.short_id}`\n const firstImage = product.images?.[0]\n const image = firstImage ? resolveMediaUrl(firstImage) : null\n\n const item: Record<string, unknown> = {\n '@type': 'ListItem',\n position: index + 1,\n url,\n name: product.name,\n }\n\n if (image) item.image = image\n\n return item\n }),\n })\n }\n\n return schemas\n}\n\n/** JSON-LD for a category page. */\nexport function buildCategorySchemas(\n category: NestedCategory,\n products: Product[],\n breadcrumbs: BreadcrumbItem[],\n origin: string\n): Record<string, unknown>[] {\n return buildListingSchemas(category.name, products, breadcrumbs, origin)\n}\n\n/** JSON-LD for a products collection page. */\nexport function buildCollectionSchemas(\n collection: ProductsCollection,\n products: Product[],\n breadcrumbs: BreadcrumbItem[],\n origin: string\n): Record<string, unknown>[] {\n return buildListingSchemas(collection.name, products, breadcrumbs, origin)\n}\n","import type { Article, Media, OfferArticle } from '@zenky/storefront-api'\n\nimport { prepareOgDescription, resolveMediaUrl } from './utils'\n\nfunction articleSchema(\n type: 'Article' | 'BlogPosting',\n opts: {\n title: string | null | undefined\n intro: string | null | undefined\n cover: Media | null | undefined\n storeName: string\n origin: string\n }\n): Record<string, unknown> {\n const schema: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': type,\n headline: opts.title ?? '',\n author: {\n '@type': 'Organization',\n name: opts.storeName,\n url: opts.origin,\n },\n }\n\n const description = prepareOgDescription(opts.intro)\n if (description) schema.description = description\n\n const image = opts.cover ? resolveMediaUrl(opts.cover) : null\n if (image) schema.image = image\n\n return schema\n}\n\n/**\n * JSON-LD schema.org/Article for the offer page's editorial copy.\n * Spec: https://schema.org/Article\n */\nexport function buildOfferArticleSchema(\n article: OfferArticle,\n storeName: string,\n origin: string\n): Record<string, unknown>[] {\n if (!article.title) return []\n\n return [\n articleSchema('Article', {\n title: article.title,\n intro: article.intro,\n cover: article.cover,\n storeName,\n origin,\n }),\n ]\n}\n\n/**\n * JSON-LD schema.org/BlogPosting for a blog article — enables Google Rich Results.\n * Spec: https://schema.org/BlogPosting\n */\nexport function buildBlogPostSchema(\n article: Article,\n storeName: string,\n origin: string\n): Record<string, unknown>[] {\n const schema = articleSchema('BlogPosting', {\n title: article.title,\n intro: article.intro,\n cover: article.cover,\n storeName,\n origin,\n })\n\n if (article.updated_at) {\n schema.dateModified = article.updated_at\n // No separate datePublished field is available — reuse updated_at.\n schema.datePublished = article.updated_at\n }\n\n return [schema]\n}\n","import { ContactType } from '@zenky/storefront-api'\n\nimport type { Schedule, Store } from '@zenky/storefront-api'\n\nimport { getSocialLinks } from '../utils'\n\n/** Schedule.days key → schema.org DayOfWeek. */\nconst SCHEMA_DAY: Record<string, string> = {\n monday: 'Monday',\n tuesday: 'Tuesday',\n wednesday: 'Wednesday',\n thursday: 'Thursday',\n friday: 'Friday',\n saturday: 'Saturday',\n sunday: 'Sunday',\n}\n\n/**\n * Schedule (per-day `hours` string like \"10:00-22:00\") → array of schema.org/OpeningHoursSpecification.\n * Days without a recognizable interval (day off / 24 hours / empty) are skipped.\n */\nfunction buildOpeningHours(schedule: Schedule | null | undefined): Record<string, unknown>[] {\n if (!schedule) return []\n\n const result: Record<string, unknown>[] = []\n for (const key of Object.keys(SCHEMA_DAY)) {\n const day = schedule.days[key as keyof typeof schedule.days]\n const match = day?.hours?.match(/(\\d{1,2}:\\d{2}).*?(\\d{1,2}:\\d{2})/)\n if (!match) continue\n\n result.push({\n '@type': 'OpeningHoursSpecification',\n dayOfWeek: SCHEMA_DAY[key],\n opens: match[1],\n closes: match[2],\n })\n }\n\n return result\n}\n\n/**\n * JSON-LD schema.org/Organization (or LocalBusiness if there's a physical location)\n * for the homepage. Feeds Google's Knowledge Panel. All data comes from the\n * already-loaded profile.\n * Spec: https://schema.org/Organization · https://schema.org/LocalBusiness\n */\nexport function buildOrganizationSchema(store: Store, origin: string): Record<string, unknown>[] {\n const logoUrl =\n store.square_logo?.medium?.url ||\n store.square_logo?.large?.url ||\n store.logo?.medium?.url ||\n store.logo?.large?.url ||\n null\n\n const phones = (store.phones ?? []).map((p) => p.number).filter((n): n is string => !!n)\n const email = store.links?.find((c) => c.type === ContactType.Email)?.value ?? null\n const socialLinks = getSocialLinks(store.links ?? []).map((l) => l.url)\n\n const city = (store.cities ?? []).find((c) => c.is_default) ?? store.cities?.[0] ?? null\n const stock = city?.stocks?.find((st) => st.address) ?? city?.stocks?.[0] ?? null\n\n const streetAddress = stock?.address ?? null\n const geo = stock?.coordinates ?? city?.coordinates ?? null\n const openingHours = buildOpeningHours(stock?.schedule ?? city?.schedule)\n\n const isLocalBusiness = Boolean(streetAddress || geo || openingHours.length)\n\n const schema: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': isLocalBusiness ? 'LocalBusiness' : 'Organization',\n name: store.name,\n url: origin,\n }\n\n if (logoUrl) {\n schema.logo = logoUrl\n schema.image = logoUrl\n }\n\n if (phones.length) schema.telephone = phones.length === 1 ? phones[0] : phones\n if (email) schema.email = email\n if (socialLinks.length) schema.sameAs = socialLinks\n\n if (streetAddress || city?.name) {\n schema.address = {\n '@type': 'PostalAddress',\n ...(streetAddress ? { streetAddress } : {}),\n ...(city?.name ? { addressLocality: city.name } : {}),\n addressCountry: 'RU',\n }\n }\n\n if (geo) {\n schema.geo = {\n '@type': 'GeoCoordinates',\n latitude: geo.latitude,\n longitude: geo.longitude,\n }\n }\n\n if (openingHours.length) schema.openingHoursSpecification = openingHours\n\n return [schema]\n}\n","/** Already-resolved OG/Twitter tag values (with the consumer's defaults applied). */\nexport interface OgTagsInput {\n title: string | null\n description?: string | null\n image?: string | null\n type?: string\n url?: string | null\n}\n\n/** Framework-neutral OG + Twitter meta records. The consumer controls head priority. */\nexport function buildOgTags(input: OgTagsInput): Record<string, string>[] {\n const tags: Record<string, string>[] = []\n\n if (input.title) {\n tags.push({ property: 'og:title', content: input.title })\n tags.push({ name: 'twitter:title', content: input.title })\n }\n\n if (input.description) {\n tags.push({ property: 'og:description', content: input.description })\n tags.push({ name: 'twitter:description', content: input.description })\n }\n\n if (input.image) {\n tags.push({ property: 'og:image', content: input.image })\n tags.push({ name: 'twitter:image', content: input.image })\n }\n\n tags.push({ property: 'og:type', content: input.type ?? 'website' })\n\n if (input.url) {\n tags.push({ property: 'og:url', content: input.url })\n }\n\n return tags\n}\n","import type { Seo, SeoMetaTag } from '@zenky/storefront-api'\n\n/** SDK `Seo.meta` tags mapped to framework-neutral head meta records. */\nexport function mapSeoMetaTags(tags: SeoMetaTag[]): Record<string, string>[] {\n return tags.map((tag) => {\n const attrs: Record<string, string> = {\n [tag.name.attribute]: tag.name.value,\n [tag.value.attribute]: tag.value.value,\n }\n\n for (const additional of tag.additional) {\n attrs[additional.attribute] = additional.value\n }\n\n return attrs\n })\n}\n\n/** Meta tags from a `Seo` entity (description + `Seo.meta` mapping). The consumer resolves the title. */\nexport function buildEntitySeoMeta(seo: Seo | null | undefined): Record<string, string>[] {\n const tags: Record<string, string>[] = []\n\n if (seo?.description) {\n tags.push({ name: 'description', content: seo.description })\n }\n\n if (seo?.meta?.length) {\n tags.push(...mapSeoMetaTags(seo.meta))\n }\n\n return tags\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/seo/utils.ts","../../src/seo/siteOrigin.ts","../../src/seo/productSchema.ts","../../src/seo/listingSchema.ts","../../src/seo/articleSchema.ts","../../src/seo/organizationSchema.ts","../../src/seo/ogTags.ts","../../src/seo/entitySeo.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { Media, Store } from '@zenky/storefront-api'\n\n/**\n * Resolves the best available URL from a Media object.\n */\nexport function resolveMediaUrl(media: Media): string | null {\n return (\n media.hd?.url ||\n media.xlarge?.url ||\n media.large?.url ||\n media.medium?.url ||\n media.placeholder?.url ||\n null\n )\n}\n\n/**\n * Resolves store logo URL, preferring square logo over rectangular.\n * Tries all available size variants so CDN assets with only hd/xlarge are found too.\n */\nexport function resolveStoreLogoUrl(store: Store | null | undefined): string | null {\n if (!store) return null\n if (store.square_logo) {\n const url = resolveMediaUrl(store.square_logo)\n if (url) return url\n }\n if (store.logo) {\n return resolveMediaUrl(store.logo)\n }\n return null\n}\n\n/**\n * Strips HTML tags from a string.\n */\nexport function stripHtml(html: string): string {\n return html.replace(/<[^>]*>/g, '').trim()\n}\n\n/**\n * Prepares a string for use as og:description or meta description:\n * strips HTML tags and truncates to maxLength (default 160).\n */\nexport function prepareOgDescription(\n text: string | null | undefined,\n maxLength = 160\n): string | null {\n if (!text) return null\n const plain = stripHtml(text)\n if (!plain) return null\n return plain.length > maxLength ? `${plain.slice(0, maxLength - 3)}...` : plain\n}\n","/**\n * Canonical public origin of the site, from `store.domain`. The fallback (request host)\n * is supplied by the consumer — the core doesn't know about `useRequestURL`.\n */\nexport function resolveSiteOrigin(\n storeDomain: string | null | undefined,\n fallbackOrigin: string\n): string {\n const domain = storeDomain?.trim()\n if (domain) {\n const host = domain.replace(/^https?:\\/\\//, '').replace(/\\/+$/, '')\n return `https://${host}`\n }\n return fallbackOrigin\n}\n","import type { Product, ProductVariant } from '@zenky/storefront-api'\n\nimport { minorToMajorString } from '../money'\nimport { resolveVariantPrice } from '../products'\nimport { resolveMediaUrl } from './utils'\n\nfunction isProductInStock(variants: ProductVariant[]): boolean {\n if (!variants.length) return false\n return variants.some((variant) => {\n const remainders = variant.remainders\n if (!remainders?.length) return true\n return remainders.some((remainder) => remainder.quantity > 0)\n })\n}\n\n/**\n * JSON-LD schema.org/Product. Google uses it for Rich Results (price, availability,\n * rating in search listings).\n * Returns an array of 0 or 1 objects — the consumer wraps it in a `<script ld+json>` tag.\n * Spec: https://schema.org/Product\n */\nexport function buildProductSchema(product: Product, currency: string): Record<string, unknown>[] {\n const variants = product.variants ?? []\n\n const firstVariant = variants[0]\n let offerBlock: Record<string, unknown> | undefined\n\n if (firstVariant) {\n const resolved = resolveVariantPrice(firstVariant)\n\n offerBlock = {\n '@type': 'Offer',\n price: minorToMajorString(resolved.price, currency),\n priceCurrency: currency,\n availability: isProductInStock(variants)\n ? 'https://schema.org/InStock'\n : 'https://schema.org/OutOfStock',\n ...(firstVariant.sku ? { sku: firstVariant.sku } : {}),\n }\n }\n\n const images = (product.images ?? []).map(resolveMediaUrl).filter((url): url is string => !!url)\n\n const schema: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': 'Product',\n name: product.name,\n }\n\n if (product.description) schema.description = product.description\n if (images.length) schema.image = images\n if (offerBlock) schema.offers = offerBlock\n\n return [schema]\n}\n","import type { NestedCategory, Product, ProductsCollection } from '@zenky/storefront-api'\n\nimport { resolveMediaUrl } from './utils'\nimport type { BreadcrumbItem } from './types'\n\n/**\n * JSON-LD for a product listing page: BreadcrumbList + ItemList (first 10 products).\n * Returns an array of 0-2 JSON-LD objects. Category and collection pages differ only\n * in the list name, so both go through here.\n * Spec: https://schema.org/ItemList · https://schema.org/BreadcrumbList\n */\nfunction buildListingSchemas(\n name: string | null,\n products: Product[],\n breadcrumbs: BreadcrumbItem[],\n origin: string\n): Record<string, unknown>[] {\n const schemas: Record<string, unknown>[] = []\n\n if (breadcrumbs.length) {\n schemas.push({\n '@context': 'https://schema.org',\n '@type': 'BreadcrumbList',\n itemListElement: breadcrumbs.map((crumb, index) => ({\n '@type': 'ListItem',\n position: index + 1,\n name: crumb.label,\n ...(crumb.to ? { item: `${origin}${crumb.to}` } : {}),\n })),\n })\n }\n\n const items = products.slice(0, 10)\n if (items.length) {\n schemas.push({\n '@context': 'https://schema.org',\n '@type': 'ItemList',\n name,\n numberOfItems: items.length,\n itemListElement: items.map((product, index) => {\n const url = `${origin}/products/${product.short_id}/${product.slug || product.short_id}`\n const firstImage = product.images?.[0]\n const image = firstImage ? resolveMediaUrl(firstImage) : null\n\n const item: Record<string, unknown> = {\n '@type': 'ListItem',\n position: index + 1,\n url,\n name: product.name,\n }\n\n if (image) item.image = image\n\n return item\n }),\n })\n }\n\n return schemas\n}\n\n/** JSON-LD for a category page. */\nexport function buildCategorySchemas(\n category: NestedCategory,\n products: Product[],\n breadcrumbs: BreadcrumbItem[],\n origin: string\n): Record<string, unknown>[] {\n return buildListingSchemas(category.name, products, breadcrumbs, origin)\n}\n\n/** JSON-LD for a products collection page. */\nexport function buildCollectionSchemas(\n collection: ProductsCollection,\n products: Product[],\n breadcrumbs: BreadcrumbItem[],\n origin: string\n): Record<string, unknown>[] {\n return buildListingSchemas(collection.name, products, breadcrumbs, origin)\n}\n","import type { Article, Media, OfferArticle } from '@zenky/storefront-api'\n\nimport { prepareOgDescription, resolveMediaUrl } from './utils'\n\nfunction articleSchema(\n type: 'Article' | 'BlogPosting',\n opts: {\n title: string | null | undefined\n intro: string | null | undefined\n cover: Media | null | undefined\n storeName: string\n origin: string\n }\n): Record<string, unknown> {\n const schema: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': type,\n headline: opts.title ?? '',\n author: {\n '@type': 'Organization',\n name: opts.storeName,\n url: opts.origin,\n },\n }\n\n const description = prepareOgDescription(opts.intro)\n if (description) schema.description = description\n\n const image = opts.cover ? resolveMediaUrl(opts.cover) : null\n if (image) schema.image = image\n\n return schema\n}\n\n/**\n * JSON-LD schema.org/Article for the offer page's editorial copy.\n * Spec: https://schema.org/Article\n */\nexport function buildOfferArticleSchema(\n article: OfferArticle,\n storeName: string,\n origin: string\n): Record<string, unknown>[] {\n if (!article.title) return []\n\n return [\n articleSchema('Article', {\n title: article.title,\n intro: article.intro,\n cover: article.cover,\n storeName,\n origin,\n }),\n ]\n}\n\n/**\n * JSON-LD schema.org/BlogPosting for a blog article — enables Google Rich Results.\n * Spec: https://schema.org/BlogPosting\n */\nexport function buildBlogPostSchema(\n article: Article,\n storeName: string,\n origin: string\n): Record<string, unknown>[] {\n const schema = articleSchema('BlogPosting', {\n title: article.title,\n intro: article.intro,\n cover: article.cover,\n storeName,\n origin,\n })\n\n if (article.updated_at) {\n schema.dateModified = article.updated_at\n // No separate datePublished field is available — reuse updated_at.\n schema.datePublished = article.updated_at\n }\n\n return [schema]\n}\n","import { ContactType } from '@zenky/storefront-api'\n\nimport type { Schedule, Store } from '@zenky/storefront-api'\n\nimport { getSocialLinks } from '../utils'\n\n/** Schedule.days key → schema.org DayOfWeek. */\nconst SCHEMA_DAY: Record<string, string> = {\n monday: 'Monday',\n tuesday: 'Tuesday',\n wednesday: 'Wednesday',\n thursday: 'Thursday',\n friday: 'Friday',\n saturday: 'Saturday',\n sunday: 'Sunday',\n}\n\n/**\n * Schedule (per-day `hours` string like \"10:00-22:00\") → array of schema.org/OpeningHoursSpecification.\n * Days without a recognizable interval (day off / 24 hours / empty) are skipped.\n */\nfunction buildOpeningHours(schedule: Schedule | null | undefined): Record<string, unknown>[] {\n if (!schedule) return []\n\n const result: Record<string, unknown>[] = []\n for (const key of Object.keys(SCHEMA_DAY)) {\n const day = schedule.days[key as keyof typeof schedule.days]\n const match = day?.hours?.match(/(\\d{1,2}:\\d{2}).*?(\\d{1,2}:\\d{2})/)\n if (!match) continue\n\n result.push({\n '@type': 'OpeningHoursSpecification',\n dayOfWeek: SCHEMA_DAY[key],\n opens: match[1],\n closes: match[2],\n })\n }\n\n return result\n}\n\n/**\n * JSON-LD schema.org/Organization (or LocalBusiness if there's a physical location)\n * for the homepage. Feeds Google's Knowledge Panel. All data comes from the\n * already-loaded profile.\n * Spec: https://schema.org/Organization · https://schema.org/LocalBusiness\n */\nexport function buildOrganizationSchema(store: Store, origin: string): Record<string, unknown>[] {\n const logoUrl =\n store.square_logo?.medium?.url ||\n store.square_logo?.large?.url ||\n store.logo?.medium?.url ||\n store.logo?.large?.url ||\n null\n\n const phones = (store.phones ?? []).map((p) => p.number).filter((n): n is string => !!n)\n const email = store.links?.find((c) => c.type === ContactType.Email)?.value ?? null\n const socialLinks = getSocialLinks(store.links ?? []).map((l) => l.url)\n\n const city = (store.cities ?? []).find((c) => c.is_default) ?? store.cities?.[0] ?? null\n const stock = city?.stocks?.find((st) => st.address) ?? city?.stocks?.[0] ?? null\n\n const streetAddress = stock?.address ?? null\n const geo = stock?.coordinates ?? city?.coordinates ?? null\n const openingHours = buildOpeningHours(stock?.schedule ?? city?.schedule)\n\n const isLocalBusiness = Boolean(streetAddress || geo || openingHours.length)\n\n const schema: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': isLocalBusiness ? 'LocalBusiness' : 'Organization',\n name: store.name,\n url: origin,\n }\n\n if (logoUrl) {\n schema.logo = logoUrl\n schema.image = logoUrl\n }\n\n if (phones.length) schema.telephone = phones.length === 1 ? phones[0] : phones\n if (email) schema.email = email\n if (socialLinks.length) schema.sameAs = socialLinks\n\n if (streetAddress || city?.name) {\n schema.address = {\n '@type': 'PostalAddress',\n ...(streetAddress ? { streetAddress } : {}),\n ...(city?.name ? { addressLocality: city.name } : {}),\n addressCountry: 'RU',\n }\n }\n\n if (geo) {\n schema.geo = {\n '@type': 'GeoCoordinates',\n latitude: geo.latitude,\n longitude: geo.longitude,\n }\n }\n\n if (openingHours.length) schema.openingHoursSpecification = openingHours\n\n return [schema]\n}\n","/** Already-resolved OG/Twitter tag values (with the consumer's defaults applied). */\nexport interface OgTagsInput {\n title: string | null\n description?: string | null\n image?: string | null\n type?: string\n url?: string | null\n}\n\n/** Framework-neutral OG + Twitter meta records. The consumer controls head priority. */\nexport function buildOgTags(input: OgTagsInput): Record<string, string>[] {\n const tags: Record<string, string>[] = []\n\n if (input.title) {\n tags.push({ property: 'og:title', content: input.title })\n tags.push({ name: 'twitter:title', content: input.title })\n }\n\n if (input.description) {\n tags.push({ property: 'og:description', content: input.description })\n tags.push({ name: 'twitter:description', content: input.description })\n }\n\n if (input.image) {\n tags.push({ property: 'og:image', content: input.image })\n tags.push({ name: 'twitter:image', content: input.image })\n }\n\n tags.push({ property: 'og:type', content: input.type ?? 'website' })\n\n if (input.url) {\n tags.push({ property: 'og:url', content: input.url })\n }\n\n return tags\n}\n","import type { Seo, SeoMetaTag } from '@zenky/storefront-api'\n\n/** SDK `Seo.meta` tags mapped to framework-neutral head meta records. */\nexport function mapSeoMetaTags(tags: SeoMetaTag[]): Record<string, string>[] {\n return tags.map((tag) => {\n const attrs: Record<string, string> = {\n [tag.name.attribute]: tag.name.value,\n [tag.value.attribute]: tag.value.value,\n }\n\n for (const additional of tag.additional) {\n attrs[additional.attribute] = additional.value\n }\n\n return attrs\n })\n}\n\n/** Meta tags from a `Seo` entity (description + `Seo.meta` mapping). The consumer resolves the title. */\nexport function buildEntitySeoMeta(seo: Seo | null | undefined): Record<string, string>[] {\n const tags: Record<string, string>[] = []\n\n if (seo?.description) {\n tags.push({ name: 'description', content: seo.description })\n }\n\n if (seo?.meta?.length) {\n tags.push(...mapSeoMetaTags(seo.meta))\n }\n\n return tags\n}\n"]}
|
package/dist/stores/index.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ import { S as StaticCatalogItem } from '../catalogEntity-DgrPsJpf.js';
|
|
|
5
5
|
import * as _zenky_storefront_api from '@zenky/storefront-api';
|
|
6
6
|
import { Product, Store, City, Stock, NestedCategory, DeliveryMethod, Order, OrderDeliveryMethod, OrderSettings, OrderCheckoutTotal, PaymentMethod, OrderPaymentTransaction, OrderProductVariant, OrderPaymentMethod } from '@zenky/storefront-api';
|
|
7
7
|
import { Website } from '../website-settings/index.js';
|
|
8
|
+
import { a as AuthStep, f as CodeContext, g as ConfirmationChannel, e as ChannelIntent, S as SelectableConfirmationChannel } from '../types-CXm2F6Hz.js';
|
|
9
|
+
import { R as RecaptchaPayloadProvider } from '../useCheckoutSubmit-Be-lQd60.js';
|
|
8
10
|
import '@zenky/integrations-sdk';
|
|
9
11
|
import '../snippets/index.js';
|
|
10
12
|
|
|
@@ -57,6 +59,59 @@ interface AuthStore {
|
|
|
57
59
|
}
|
|
58
60
|
declare const useAuthStore: pinia.StoreDefinition<"zenky-auth", Pick<AuthStore, "token" | "phone" | "countryCode">, Pick<AuthStore, "isAuthenticated" | "apiToken" | "initials">, Pick<AuthStore, "setToken" | "clearToken" | "initFromCookie">>;
|
|
59
61
|
|
|
62
|
+
declare const RESEND_TIMER_SECONDS = 60;
|
|
63
|
+
|
|
64
|
+
interface AuthFlowStore {
|
|
65
|
+
currentStep: Ref<AuthStep>;
|
|
66
|
+
phone: Ref<string>;
|
|
67
|
+
countryCode: Ref<string>;
|
|
68
|
+
loading: Ref<boolean>;
|
|
69
|
+
error: Ref<string | null>;
|
|
70
|
+
fieldErrors: Ref<Record<string, string[]>>;
|
|
71
|
+
codeContext: Ref<CodeContext>;
|
|
72
|
+
confirmationMethod: Ref<ConfirmationChannel | null>;
|
|
73
|
+
/** Какой flow привёл на AUTH_STEP.Channel — публичное, часть $state (SSR-гидратируется). */
|
|
74
|
+
channelIntent: Ref<ChannelIntent | null>;
|
|
75
|
+
/**
|
|
76
|
+
* Канал, на котором последний раз получили `auth.otp_request.failed`. UI может
|
|
77
|
+
* скрыть/задизейблить эту кнопку на повторном показе Channel — бэк уже отказал,
|
|
78
|
+
* выбор того же канала снова даст ту же ошибку.
|
|
79
|
+
*/
|
|
80
|
+
lastFailedChannel: Ref<SelectableConfirmationChannel | null>;
|
|
81
|
+
resetCode: Ref<string>;
|
|
82
|
+
registerCode: Ref<string>;
|
|
83
|
+
resendTimerEndsAt: Ref<number>;
|
|
84
|
+
pendingPassword: Ref<string>;
|
|
85
|
+
availableChannels: ComputedRef<SelectableConfirmationChannel[]>;
|
|
86
|
+
checkPhone: (getRecaptchaPayload: RecaptchaPayloadProvider) => Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* @returns true — токен получен, флоу завершён. false — либо ошибка (см. `error`),
|
|
89
|
+
* либо телефон зарегистрирован, но не подтверждён: код уже отправлен и `currentStep`
|
|
90
|
+
* ушёл на Code/Channel — в этом случае `error` пустой, это не сбой.
|
|
91
|
+
*/
|
|
92
|
+
login: (password: string, getRecaptchaPayload: RecaptchaPayloadProvider) => Promise<boolean>;
|
|
93
|
+
/** @returns true — токен получен (сразу или через login-fallback при 0 каналах), false — код отправлен / ошибка / шаг выбора канала */
|
|
94
|
+
register: (password: string, getRecaptchaPayload: RecaptchaPayloadProvider) => Promise<boolean>;
|
|
95
|
+
confirmRegistration: (code: string, password?: string) => Promise<boolean>;
|
|
96
|
+
resendCode: (getRecaptchaPayload: RecaptchaPayloadProvider) => Promise<number>;
|
|
97
|
+
dispatchReset: (getRecaptchaPayload: RecaptchaPayloadProvider) => Promise<void>;
|
|
98
|
+
resetPassword: (code: string, password: string) => Promise<boolean>;
|
|
99
|
+
selectChannel: (channel: SelectableConfirmationChannel, getRecaptchaPayload: RecaptchaPayloadProvider) => Promise<boolean>;
|
|
100
|
+
/** Сбрасывает выбранный канал перед ручным resend — иначе гейт сработает только один раз. */
|
|
101
|
+
changeChannel: () => void;
|
|
102
|
+
goToChannelBack: () => void;
|
|
103
|
+
goToReset: () => void;
|
|
104
|
+
goToPhone: () => void;
|
|
105
|
+
goToMethod: () => void;
|
|
106
|
+
goToLogin: () => void;
|
|
107
|
+
goToNewPassword: (code: string) => void;
|
|
108
|
+
goToResetCode: () => void;
|
|
109
|
+
goToRegisterPassword: (code: string) => void;
|
|
110
|
+
goToRegisterCode: () => void;
|
|
111
|
+
reset: () => void;
|
|
112
|
+
}
|
|
113
|
+
declare const useAuthFlowStore: pinia.StoreDefinition<"zenky-auth-flow", Pick<AuthFlowStore, "error" | "phone" | "countryCode" | "loading" | "currentStep" | "fieldErrors" | "codeContext" | "confirmationMethod" | "channelIntent" | "lastFailedChannel" | "resetCode" | "registerCode" | "resendTimerEndsAt" | "pendingPassword">, Pick<AuthFlowStore, "availableChannels">, Pick<AuthFlowStore, "login" | "register" | "reset" | "checkPhone" | "confirmRegistration" | "resendCode" | "dispatchReset" | "resetPassword" | "selectChannel" | "changeChannel" | "goToChannelBack" | "goToReset" | "goToPhone" | "goToMethod" | "goToLogin" | "goToNewPassword" | "goToResetCode" | "goToRegisterPassword" | "goToRegisterCode">>;
|
|
114
|
+
|
|
60
115
|
interface OrderSettingsStore {
|
|
61
116
|
deliveryMethod: Ref<DeliveryMethod>;
|
|
62
117
|
onPremiseTable: Ref<string | null>;
|
|
@@ -194,4 +249,4 @@ declare function useStorefrontProfile(): {
|
|
|
194
249
|
hasStock: vue.ComputedRef<boolean>;
|
|
195
250
|
};
|
|
196
251
|
|
|
197
|
-
export { type AppliedBonusesIntent, type AuthStore, type CartStore, type MinPriceViolation, type OrderSettingsStore, type PromotionsStore, type RewardSelection, type RewardSelectionContext, type StoreProfileStore, useAuthStore, useCartStore, useCatalogStore, useOrderSettingsStore, useProductsStore, usePromotionsStore, useStore, useStoreProfileStore, useStorefrontProfile };
|
|
252
|
+
export { type AppliedBonusesIntent, type AuthFlowStore, type AuthStore, type CartStore, type MinPriceViolation, type OrderSettingsStore, type PromotionsStore, RESEND_TIMER_SECONDS, type RewardSelection, type RewardSelectionContext, type StoreProfileStore, useAuthFlowStore, useAuthStore, useCartStore, useCatalogStore, useOrderSettingsStore, useProductsStore, usePromotionsStore, useStore, useStoreProfileStore, useStorefrontProfile };
|
package/dist/stores/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
export { useProductsStore, useStore, useStorefrontProfile } from '../chunk-
|
|
2
|
-
|
|
1
|
+
export { RESEND_TIMER_SECONDS, useAuthFlowStore, useProductsStore, useStore, useStorefrontProfile } from '../chunk-KX7KXN3W.js';
|
|
2
|
+
import '../chunk-AK4276ND.js';
|
|
3
|
+
import '../chunk-TE42OCFK.js';
|
|
4
|
+
export { useCartStore, useOrderSettingsStore, usePromotionsStore } from '../chunk-TE2QPH74.js';
|
|
3
5
|
export { useCatalogStore } from '../chunk-NSOIL7PW.js';
|
|
4
|
-
export { useAuthStore
|
|
6
|
+
export { useAuthStore } from '../chunk-WTZXTBF6.js';
|
|
7
|
+
export { useStoreProfileStore } from '../chunk-IOYYCA75.js';
|
|
5
8
|
//# sourceMappingURL=index.js.map
|
|
6
9
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ConfirmationMethod, RequestedConfirmationMethod } from '@zenky/storefront-api';
|
|
2
|
+
|
|
3
|
+
declare const AUTH_STEP: {
|
|
4
|
+
readonly Method: "method";
|
|
5
|
+
readonly Phone: "phone";
|
|
6
|
+
readonly Channel: "channel";
|
|
7
|
+
readonly Login: "login";
|
|
8
|
+
readonly Register: "register";
|
|
9
|
+
readonly Code: "code";
|
|
10
|
+
readonly RegisterPassword: "register_password";
|
|
11
|
+
readonly Reset: "reset";
|
|
12
|
+
readonly NewPassword: "new_password";
|
|
13
|
+
};
|
|
14
|
+
type AuthStep = (typeof AUTH_STEP)[keyof typeof AUTH_STEP];
|
|
15
|
+
declare const CODE_CONTEXT: {
|
|
16
|
+
readonly Register: "register";
|
|
17
|
+
readonly Reset: "reset";
|
|
18
|
+
};
|
|
19
|
+
type CodeContext = (typeof CODE_CONTEXT)[keyof typeof CODE_CONTEXT];
|
|
20
|
+
/**
|
|
21
|
+
* Какой flow привёл на шаг выбора канала (AUTH_STEP.Channel) — нужно чтобы
|
|
22
|
+
* selectChannel() знал, что вызвать повторно, а goToChannelBack() — куда вернуться.
|
|
23
|
+
*/
|
|
24
|
+
declare const CHANNEL_INTENT: {
|
|
25
|
+
readonly Register: "register";
|
|
26
|
+
readonly Resend: "resend";
|
|
27
|
+
readonly DispatchReset: "dispatch_reset";
|
|
28
|
+
};
|
|
29
|
+
type ChannelIntent = (typeof CHANNEL_INTENT)[keyof typeof CHANNEL_INTENT];
|
|
30
|
+
declare const CONFIRMATION_CHANNEL: {
|
|
31
|
+
readonly Sms: "sms";
|
|
32
|
+
readonly Call: "call";
|
|
33
|
+
readonly Telegram: "telegram";
|
|
34
|
+
readonly WhatsApp: "whatsapp";
|
|
35
|
+
};
|
|
36
|
+
type ConfirmationChannel = (typeof CONFIRMATION_CHANNEL)[keyof typeof CONFIRMATION_CHANNEL];
|
|
37
|
+
/**
|
|
38
|
+
* `Call` — легаси, response-only (сообщает, что код это последние 4 цифры входящего
|
|
39
|
+
* звонка); никогда не показывается как кнопка и не может быть явно выбран. Всё, что
|
|
40
|
+
* реально можно выбрать/передать в `selectChannel` — этот более узкий тип.
|
|
41
|
+
*/
|
|
42
|
+
type SelectableConfirmationChannel = Exclude<ConfirmationChannel, typeof CONFIRMATION_CHANNEL.Call>;
|
|
43
|
+
/** Порядок показа в модалке выбора: Telegram, WhatsApp, SMS — всегда последним. */
|
|
44
|
+
declare const CONFIRMATION_CHANNEL_ORDER: readonly SelectableConfirmationChannel[];
|
|
45
|
+
declare function toConfirmationChannel(method: ConfirmationMethod | string | null | undefined): ConfirmationChannel;
|
|
46
|
+
/**
|
|
47
|
+
* `ConfirmationChannel` шире, чем `RequestedConfirmationMethod` (включает `call` —
|
|
48
|
+
* легаси-значение, которое можно получить в ответе, но нельзя запросить явно). Рантайм-
|
|
49
|
+
* инвариант: сюда `call` попасть не может — `availableChannels` строится из
|
|
50
|
+
* `CONFIRMATION_CHANNEL_ORDER` (Telegram/WhatsApp/Sms, без Call), а канал выбирается
|
|
51
|
+
* только из этого списка.
|
|
52
|
+
*/
|
|
53
|
+
declare function toRequestedMethod(channel: ConfirmationChannel | null | undefined): RequestedConfirmationMethod | undefined;
|
|
54
|
+
|
|
55
|
+
export { AUTH_STEP as A, CHANNEL_INTENT as C, type SelectableConfirmationChannel as S, type AuthStep as a, CODE_CONTEXT as b, CONFIRMATION_CHANNEL as c, CONFIRMATION_CHANNEL_ORDER as d, type ChannelIntent as e, type CodeContext as f, type ConfirmationChannel as g, toRequestedMethod as h, toConfirmationChannel as t };
|
|
@@ -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 };
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -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 };
|
package/dist/utils/index.js
CHANGED
|
@@ -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-
|
|
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
|
package/docs/architecture.md
CHANGED
|
@@ -9,6 +9,10 @@ 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
|
|
@@ -35,12 +39,14 @@ section). This list says what each one owns.
|
|
|
35
39
|
into `<head>` tags is the consumer's job (see the README's note on `useHead`).
|
|
36
40
|
- **`snippets/`** — normalizing the snippet records a website carries (tracking scripts,
|
|
37
41
|
custom CSS) into a consistent shape.
|
|
38
|
-
- **`stores/`** — the
|
|
39
|
-
`promotions`, `store-profile`, `order-settings`. State and business logic
|
|
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.
|
|
40
45
|
- **`theme/`** — generating a full color palette from one brand hex value, and the CSS
|
|
41
46
|
variable map the consumer injects into `<head>`.
|
|
42
47
|
- **`utils/`** — small pure helpers with no domain attached: phone/date/address
|
|
43
|
-
formatting,
|
|
48
|
+
formatting, reading a store schedule (today's status, the week, grouped rows),
|
|
49
|
+
debouncing, pluralization, an operation chain for sequencing async work.
|
|
44
50
|
- **`website-settings/`** — only the **runtime** shape of a website (what the storefront
|
|
45
51
|
reads and renders: settings values, pages, snippets, integrations). The website
|
|
46
52
|
builder's admin schema is a different concern — see below.
|
|
@@ -83,3 +89,73 @@ passed as an argument to that one function. `sdk`, `storage`, and the rest of th
|
|
|
83
89
|
are the port case: dozens of composables across every domain need them, so they are
|
|
84
90
|
resolved once through `useRuntime()` instead of being passed down through every caller in
|
|
85
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.
|
|
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
|
|
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
|
|
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",
|