@zenky/storefront-vue 0.10.0 → 0.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +52 -2
  3. package/dist/cart/index.d.ts +31 -2
  4. package/dist/cart/index.js +5 -6
  5. package/dist/catalog/index.js +2 -2
  6. package/dist/{chunk-BIGTKVC6.js → chunk-57IOMDAM.js} +3 -3
  7. package/dist/{chunk-BIGTKVC6.js.map → chunk-57IOMDAM.js.map} +1 -1
  8. package/dist/{chunk-DKWDKVXQ.js → chunk-7FRMMCR5.js} +12 -10
  9. package/dist/chunk-7FRMMCR5.js.map +1 -0
  10. package/dist/{chunk-EYRCAUUP.js → chunk-E3G7NGCH.js} +2 -2
  11. package/dist/chunk-E3G7NGCH.js.map +1 -0
  12. package/dist/{chunk-VL3T36VM.js → chunk-FFBZKQRV.js} +14 -7
  13. package/dist/chunk-FFBZKQRV.js.map +1 -0
  14. package/dist/{chunk-OQLU7FSR.js → chunk-G2X4SBCO.js} +199 -29
  15. package/dist/chunk-G2X4SBCO.js.map +1 -0
  16. package/dist/{chunk-AL5W5JSD.js → chunk-GLTOJTZO.js} +2 -2
  17. package/dist/{chunk-AL5W5JSD.js.map → chunk-GLTOJTZO.js.map} +1 -1
  18. package/dist/{chunk-IGDDWGS4.js → chunk-JLFBAAFT.js} +20 -11
  19. package/dist/chunk-JLFBAAFT.js.map +1 -0
  20. package/dist/{chunk-PJ2LEKW6.js → chunk-MZ66ARDL.js} +18 -3
  21. package/dist/chunk-MZ66ARDL.js.map +1 -0
  22. package/dist/{chunk-4MJKN7PL.js → chunk-N3NOTJUC.js} +4 -4
  23. package/dist/{chunk-4MJKN7PL.js.map → chunk-N3NOTJUC.js.map} +1 -1
  24. package/dist/{chunk-2OD7CLKD.js → chunk-RYYY7MVP.js} +2 -2
  25. package/dist/chunk-RYYY7MVP.js.map +1 -0
  26. package/dist/{chunk-PWHIDGKW.js → chunk-TEG7EL4N.js} +12 -3
  27. package/dist/chunk-TEG7EL4N.js.map +1 -0
  28. package/dist/index.d.ts +1 -1
  29. package/dist/index.js +11 -11
  30. package/dist/layout/index.js +8 -4
  31. package/dist/layout/index.js.map +1 -1
  32. package/dist/order/index.js.map +1 -1
  33. package/dist/pagination/index.js.map +1 -1
  34. package/dist/ports/index.js +1 -1
  35. package/dist/products/index.js +2 -2
  36. package/dist/promotions/index.js +3 -3
  37. package/dist/seo/index.js +5 -5
  38. package/dist/seo/index.js.map +1 -1
  39. package/dist/snippets/index.js +1 -1
  40. package/dist/stores/index.js +3 -3
  41. package/dist/utils/index.d.ts +2 -2
  42. package/dist/utils/index.js +1 -1
  43. package/docs/consumer-setup.md +275 -0
  44. package/docs/ports.md +144 -0
  45. package/docs/releasing.md +46 -0
  46. package/package.json +26 -4
  47. package/dist/chunk-2OD7CLKD.js.map +0 -1
  48. package/dist/chunk-DKWDKVXQ.js.map +0 -1
  49. package/dist/chunk-EYRCAUUP.js.map +0 -1
  50. package/dist/chunk-IGDDWGS4.js.map +0 -1
  51. package/dist/chunk-OQLU7FSR.js.map +0 -1
  52. package/dist/chunk-PJ2LEKW6.js.map +0 -1
  53. package/dist/chunk-PWHIDGKW.js.map +0 -1
  54. package/dist/chunk-VL3T36VM.js.map +0 -1
@@ -1,14 +1,14 @@
1
- import { useCartCredentials, usePromotionsChecker } from './chunk-4MJKN7PL.js';
1
+ import { useCartCredentials, usePromotionsChecker } from './chunk-N3NOTJUC.js';
2
2
  import { createRuntimeState } from './chunk-5QCUH3FF.js';
3
3
  import { useCatalogStore } from './chunk-Z3IW4ODZ.js';
4
4
  import { usePromotionsStore } from './chunk-BWUKMWHL.js';
5
- import { useStoreProfileStore, useAuthStore } from './chunk-PWHIDGKW.js';
6
- import { getRemoteCatalog } from './chunk-EYRCAUUP.js';
7
- import { useCartStore, useOrderSettingsStore } from './chunk-VL3T36VM.js';
5
+ import { useStoreProfileStore, useAuthStore } from './chunk-TEG7EL4N.js';
6
+ import { getRemoteCatalog } from './chunk-E3G7NGCH.js';
7
+ import { useCartStore, useOrderSettingsStore } from './chunk-FFBZKQRV.js';
8
8
  import { useRuntime } from './chunk-V3VVDQIR.js';
9
9
  import { minorToMajor, majorToMinor } from './chunk-RO25KNM5.js';
10
10
  import { PaymentMethod, OrderAuthenticationMethod, DeliveryMethod, OrderCartCheckerResultAction, RecaptchaAction } from '@zenky/storefront-api';
11
- import { computed, ref, watch, watchEffect, getCurrentScope, onScopeDispose, toValue } from 'vue';
11
+ import { ref, computed, watch, watchEffect, getCurrentScope, onScopeDispose, toValue } from 'vue';
12
12
  import { storeToRefs } from 'pinia';
13
13
 
14
14
  // src/cart/constants.ts
@@ -95,16 +95,70 @@ function buildCashChangePresets({
95
95
  }
96
96
 
97
97
  // src/cart/logCartError.ts
98
+ var REDACTED = "[REDACTED]";
99
+ var MAX_LOG_DEPTH = 6;
100
+ var MAX_ARRAY_ITEMS = 25;
101
+ var MAX_OBJECT_KEYS = 50;
102
+ var SENSITIVE_KEY_PARTS = [
103
+ "authorization",
104
+ "cookie",
105
+ "credential",
106
+ "password",
107
+ "recaptcha",
108
+ "secret",
109
+ "token"
110
+ ];
111
+ var SENSITIVE_EXACT_KEYS = /* @__PURE__ */ new Set(["cvc", "cvv", "otp", "pan", "pin"]);
112
+ var SENSITIVE_ASSIGNMENT = /((?:authorization|cookie|password|recaptcha|secret|(?:api|access|auth|card|order|refresh)[_-]?token|token)\s*[=:]\s*["']?)([^\s,"';&}\]]+)/gi;
113
+ var SENSITIVE_QUERY_PARAM = /([?&#](?:authorization|password|recaptcha|secret|(?:api|access|auth|card|order|refresh)[_-]?token|token)=)([^&#\s]+)/gi;
114
+ var BEARER_TOKEN = /(bearer\s+)[a-z0-9._~+/=-]+/gi;
115
+ function isSensitiveKey(key) {
116
+ const normalized = key.replace(/[^a-z0-9]/gi, "").toLowerCase();
117
+ return SENSITIVE_EXACT_KEYS.has(normalized) || normalized.includes("confirmationcode") || normalized.includes("verificationcode") || SENSITIVE_KEY_PARTS.some((part) => normalized.includes(part));
118
+ }
119
+ function sanitizeString(value) {
120
+ return value.replace(BEARER_TOKEN, `$1${REDACTED}`).replace(SENSITIVE_QUERY_PARAM, `$1${REDACTED}`).replace(SENSITIVE_ASSIGNMENT, `$1${REDACTED}`);
121
+ }
122
+ function sanitizeLogValue(value, depth = 0, seen = /* @__PURE__ */ new WeakSet()) {
123
+ if (typeof value === "string") return sanitizeString(value);
124
+ if (value === null || typeof value !== "object") return value;
125
+ if (depth >= MAX_LOG_DEPTH) return "[TRUNCATED]";
126
+ if (seen.has(value)) return "[CIRCULAR]";
127
+ seen.add(value);
128
+ if (Array.isArray(value)) {
129
+ const sanitized = value.slice(0, MAX_ARRAY_ITEMS).map((item) => sanitizeLogValue(item, depth + 1, seen));
130
+ if (value.length > MAX_ARRAY_ITEMS) sanitized.push("[TRUNCATED]");
131
+ return sanitized;
132
+ }
133
+ const result = {};
134
+ if (value instanceof Error) {
135
+ result.name = sanitizeString(value.name);
136
+ result.message = sanitizeString(value.message);
137
+ }
138
+ let allEntries;
139
+ try {
140
+ allEntries = Object.entries(value);
141
+ } catch {
142
+ return "[UNSERIALIZABLE]";
143
+ }
144
+ const entries = allEntries.slice(0, MAX_OBJECT_KEYS);
145
+ for (const [key, nestedValue] of entries) {
146
+ if (key === "name" || key === "message") continue;
147
+ result[key] = isSensitiveKey(key) ? REDACTED : sanitizeLogValue(nestedValue, depth + 1, seen);
148
+ }
149
+ if (allEntries.length > MAX_OBJECT_KEYS) result.__truncated__ = true;
150
+ return result;
151
+ }
98
152
  function logCartError({ scope, context }, e) {
99
153
  const err = e?.err;
154
+ const safeContext = sanitizeLogValue(context);
100
155
  console.error(`[${scope}] failed`, {
101
- ...context ?? {},
156
+ ...safeContext ?? {},
102
157
  httpCode: err?.http_code,
103
158
  errorCode: err?.error_code,
104
- message: err?.message,
105
- meta: err?.meta,
106
- // raw — полный объект ошибки на случай нестандартных полей
107
- raw: e
159
+ message: sanitizeLogValue(err?.message),
160
+ meta: sanitizeLogValue(err?.meta),
161
+ error: sanitizeLogValue(e)
108
162
  });
109
163
  }
110
164
  function getCartErrorCode(e) {
@@ -115,7 +169,7 @@ function getCartErrorHttpCode(e) {
115
169
  }
116
170
  function getValidationErrors(e) {
117
171
  const err = e?.err;
118
- if (!err || err.error_code !== "validation") return {};
172
+ if (err?.error_code !== "validation") return {};
119
173
  const meta = err.meta;
120
174
  return meta?.errors ?? {};
121
175
  }
@@ -337,7 +391,7 @@ function useCartAddressSync() {
337
391
  const response = await sdk.customer.getAddresses(storeId, void 0, requestedToken);
338
392
  addresses = response.items ?? [];
339
393
  } catch (e) {
340
- console.error("[address-sync] failed to fetch saved addresses", e);
394
+ logCartError({ scope: "address-sync", context: { storeId } }, e);
341
395
  return;
342
396
  }
343
397
  if (!authStore.isAuthenticated || authStore.token !== requestedToken) return;
@@ -381,9 +435,13 @@ var ORDER_INCLUDES = [
381
435
  "variants.product.images",
382
436
  "variants.product.categories",
383
437
  "variants.modifiers.modifier",
384
- "variants.modifiers.modifiers_group"
438
+ "variants.modifiers.modifiers_group",
439
+ // Ограничения позиций (#32): is_blocking для сообщений в корзине и блокировки чекаута.
440
+ "variants.restrictions"
385
441
  ].join(",");
386
442
  var ORDER_WITH = { with: ORDER_INCLUDES };
443
+ var RUM_MARK_CART_INIT = "rum:cart:init";
444
+ var RUM_METRIC_CART_INIT = "rum_cart_init_seconds";
387
445
  var getInitializationState = createRuntimeState(() => ({ promise: null }));
388
446
  function useCartInitialization(credentials, sdk) {
389
447
  const cartStore = useCartStore();
@@ -406,10 +464,17 @@ function useCartInitialization(credentials, sdk) {
406
464
  const settings = await sdk.orders.getOrderSettings(storeId, orderCredentials);
407
465
  cartStore.setOrderSettings(settings);
408
466
  } catch (e) {
409
- console.error("[cart] Failed to load order settings", e);
467
+ logCartError(
468
+ {
469
+ scope: "cart-load-order-settings",
470
+ context: { orderId: cartStore.credentials?.id }
471
+ },
472
+ e
473
+ );
410
474
  }
411
475
  }
412
476
  async function _doInitialize() {
477
+ runtime.rum?.mark(RUM_MARK_CART_INIT);
413
478
  cartStore.setInitializing(true);
414
479
  try {
415
480
  if (credentials.orderIdCookie.value && credentials.orderTokenCookie.value) {
@@ -448,6 +513,7 @@ function useCartInitialization(credentials, sdk) {
448
513
  }
449
514
  }
450
515
  cartStore.setInitializing(false);
516
+ runtime.rum?.measure(RUM_METRIC_CART_INIT, RUM_MARK_CART_INIT);
451
517
  }
452
518
  }
453
519
  function initializeOrder() {
@@ -493,9 +559,13 @@ function useOrderBonuses(deps = {}) {
493
559
  activity.begin("bonuses");
494
560
  try {
495
561
  const orderCredentials = credentials.getCredentials();
496
- const updatedOrder = await sdk.orders.setOrderPayments(storeId, orderCredentials, {
497
- payments: [...additionalPayments, bonusesPayment]
498
- });
562
+ const updatedOrder = await sdk.orders.setOrderPayments(
563
+ storeId,
564
+ orderCredentials,
565
+ {
566
+ payments: [...additionalPayments, bonusesPayment]
567
+ }
568
+ );
499
569
  if (isStale()) return;
500
570
  if (Array.isArray(updatedOrder)) {
501
571
  cartStore.setOrderPaymentsState(updatedOrder);
@@ -527,9 +597,13 @@ function useOrderBonuses(deps = {}) {
527
597
  activity.begin("bonuses");
528
598
  try {
529
599
  const orderCredentials = credentials.getCredentials();
530
- const updatedOrder = await sdk.orders.setOrderPayments(storeId, orderCredentials, {
531
- payments: mainPayment ? [mainPayment] : []
532
- });
600
+ const updatedOrder = await sdk.orders.setOrderPayments(
601
+ storeId,
602
+ orderCredentials,
603
+ {
604
+ payments: mainPayment ? [mainPayment] : []
605
+ }
606
+ );
533
607
  if (isStale()) return;
534
608
  if (Array.isArray(updatedOrder)) {
535
609
  cartStore.setOrderPaymentsState(updatedOrder);
@@ -807,7 +881,6 @@ function useCartCustomerAttachment(cache) {
807
881
  await sdk.orders.setOrderCustomer(storeId, credentials, {
808
882
  phone: { number: authStore.phone ?? "", country: authStore.countryCode }
809
883
  });
810
- console.log("[cart] customer attached to order");
811
884
  const fresh = await sdk.orders.getOrder(storeId, credentials, ORDER_WITH);
812
885
  if (!authStore.isAuthenticated || authStore.token !== requestedToken) return;
813
886
  if (cartStore.order?.id !== requestedOrderId) return;
@@ -823,7 +896,13 @@ function useCartCustomerAttachment(cache) {
823
896
  }
824
897
  }
825
898
  } catch (e) {
826
- console.error("[cart] failed to attach customer to order", e);
899
+ logCartError(
900
+ {
901
+ scope: "cart-customer-attach",
902
+ context: { orderId: requestedOrderId }
903
+ },
904
+ e
905
+ );
827
906
  if (notifyOnPhoneChange) {
828
907
  notifier.error("\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u043A\u043E\u043D\u0442\u0430\u043A\u0442\u043D\u044B\u0435 \u0434\u0430\u043D\u043D\u044B\u0435 \u043F\u043E\u0441\u043B\u0435 \u0432\u0445\u043E\u0434\u0430");
829
908
  }
@@ -843,7 +922,13 @@ function useCartCustomerAttachment(cache) {
843
922
  cartStore.setOrder(fresh);
844
923
  cache.clear();
845
924
  } catch (e) {
846
- console.error("[cart] failed to refresh order after logout", e);
925
+ logCartError(
926
+ {
927
+ scope: "cart-customer-refresh-after-logout",
928
+ context: { orderId: requestedOrderId }
929
+ },
930
+ e
931
+ );
847
932
  }
848
933
  }
849
934
  watch(
@@ -1162,7 +1247,7 @@ function useOrderPromocode(deps = {}) {
1162
1247
  logCartError(
1163
1248
  {
1164
1249
  scope: "promocode-apply",
1165
- context: { code: trimmed, orderId: cartStore.credentials?.id }
1250
+ context: { codeLength: trimmed.length, orderId: cartStore.credentials?.id }
1166
1251
  },
1167
1252
  e
1168
1253
  );
@@ -1195,7 +1280,7 @@ function useOrderPromocode(deps = {}) {
1195
1280
  {
1196
1281
  scope: "promocode-remove",
1197
1282
  context: {
1198
- code: applied.value?.promocode,
1283
+ codeLength: applied.value?.promocode?.length,
1199
1284
  orderId: cartStore.credentials?.id
1200
1285
  }
1201
1286
  },
@@ -1293,7 +1378,13 @@ function useCartDeliveryAttachment() {
1293
1378
  if (!recovered) orderSettings.setSelectedAddress(null, null, null);
1294
1379
  return;
1295
1380
  }
1296
- console.error("[cart-delivery-attach] failed", result.error);
1381
+ logCartError(
1382
+ {
1383
+ scope: "cart-delivery-attach",
1384
+ context: { orderId: cartStore.credentials?.id }
1385
+ },
1386
+ result.error
1387
+ );
1297
1388
  }
1298
1389
  }
1299
1390
  watch(
@@ -1835,7 +1926,86 @@ function useOrderChannel() {
1835
1926
  });
1836
1927
  }
1837
1928
  }
1929
+ var getGateState = createRuntimeState(() => ({
1930
+ isOpen: ref(false),
1931
+ blocking: ref([]),
1932
+ product: ref(null),
1933
+ resolver: null
1934
+ }));
1935
+ function useRestrictionGate() {
1936
+ const state = getGateState(useRuntime());
1937
+ function settle(confirmed) {
1938
+ state.isOpen.value = false;
1939
+ const resolve = state.resolver;
1940
+ state.resolver = null;
1941
+ resolve?.(confirmed);
1942
+ }
1943
+ function confirmBlocking(items, gateProduct) {
1944
+ settle(false);
1945
+ state.blocking.value = items;
1946
+ state.product.value = gateProduct;
1947
+ state.isOpen.value = true;
1948
+ return new Promise((resolve) => {
1949
+ state.resolver = resolve;
1950
+ });
1951
+ }
1952
+ return {
1953
+ isOpen: state.isOpen,
1954
+ blocking: state.blocking,
1955
+ product: state.product,
1956
+ confirmBlocking,
1957
+ confirm: () => settle(true),
1958
+ cancel: () => settle(false)
1959
+ };
1960
+ }
1961
+
1962
+ // src/cart/useRestrictedAddToCart.ts
1963
+ function useRestrictedAddToCart() {
1964
+ const { sdk } = useRuntime();
1965
+ const profileStore = useStoreProfileStore();
1966
+ const { addToCart } = useStorefrontCart();
1967
+ const { confirmBlocking, isOpen: gateIsOpen } = useRestrictionGate();
1968
+ async function checkRestrictionsAllowed(product, alreadyInCart) {
1969
+ if (alreadyInCart || !product.restrictions?.length) return true;
1970
+ const storeId = profileStore.store?.id;
1971
+ const cityId = profileStore.city?.id;
1972
+ if (!storeId || !cityId) return true;
1973
+ let blocking = [];
1974
+ try {
1975
+ const url = sdk.client.getStoreUrl(storeId, `/products/${product.id}/restrictions`, {
1976
+ city_id: cityId,
1977
+ stock_id: profileStore.stock?.id ?? void 0
1978
+ });
1979
+ const response = await sdk.client.request("GET", url);
1980
+ blocking = (response?.data ?? []).filter((item) => item.is_blocking);
1981
+ } catch (error) {
1982
+ logCartError({ scope: "restrictions-check", context: { productId: product.id } }, error);
1983
+ return true;
1984
+ }
1985
+ if (!blocking.length) return true;
1986
+ if (gateIsOpen.value) return false;
1987
+ return confirmBlocking(blocking, {
1988
+ name: product.name ?? "",
1989
+ image: product.images?.[0] ?? null
1990
+ });
1991
+ }
1992
+ async function addWithRestrictionCheck(product, request, alreadyInCart) {
1993
+ if (await checkRestrictionsAllowed(product, alreadyInCart)) {
1994
+ await addToCart(request);
1995
+ }
1996
+ }
1997
+ return { checkRestrictionsAllowed, addWithRestrictionCheck };
1998
+ }
1999
+ function useVisibleCartVariants() {
2000
+ const cartStore = useCartStore();
2001
+ return computed(
2002
+ () => cartStore.variants.filter((variant) => {
2003
+ const delta = cartStore.lineOptimistic[variantLineKey(variant)] ?? 0;
2004
+ return variant.quantity + delta > 0;
2005
+ })
2006
+ );
2007
+ }
1838
2008
 
1839
- export { CASH_DENOMINATIONS_MAJOR, DEFAULT_ROUND_STEPS_MAJOR, ORDER_EVENTS, ORDER_WITH, buildCartLineKey, buildCashChangePresets, buildMainPaymentRequest, getCartErrorCode, getCartErrorHttpCode, getCartErrorMessage, getCashDenominationsMinor, getDefaultRoundStepsMinor, getValidationErrors, logCartError, normalizeAddress, useBonusesReconciliation, useCartAddressSync, useCartCustomerAttachment, useCartDeliveryAttachment, useCartInitialization, useCartMutation, useCartOperations, useCartQuantities, useCartRecalculation, useCartStockWatcher, useCheckoutFlow, useCheckoutSubmit, useCustomerAttach, useOnlinePaymentRetry, useOrderBonuses, useOrderChannel, useOrderDelivery, useOrderPromocode, useOrderSettings, useOrderTotal, usePaymentSelection, useStorefrontCart, variantLineKey };
1840
- //# sourceMappingURL=chunk-OQLU7FSR.js.map
1841
- //# sourceMappingURL=chunk-OQLU7FSR.js.map
2009
+ export { CASH_DENOMINATIONS_MAJOR, DEFAULT_ROUND_STEPS_MAJOR, ORDER_EVENTS, ORDER_WITH, buildCartLineKey, buildCashChangePresets, buildMainPaymentRequest, getCartErrorCode, getCartErrorHttpCode, getCartErrorMessage, getCashDenominationsMinor, getDefaultRoundStepsMinor, getValidationErrors, logCartError, normalizeAddress, useBonusesReconciliation, useCartAddressSync, useCartCustomerAttachment, useCartDeliveryAttachment, useCartInitialization, useCartMutation, useCartOperations, useCartQuantities, useCartRecalculation, useCartStockWatcher, useCheckoutFlow, useCheckoutSubmit, useCustomerAttach, useOnlinePaymentRetry, useOrderBonuses, useOrderChannel, useOrderDelivery, useOrderPromocode, useOrderSettings, useOrderTotal, usePaymentSelection, useRestrictedAddToCart, useRestrictionGate, useStorefrontCart, useVisibleCartVariants, variantLineKey };
2010
+ //# sourceMappingURL=chunk-G2X4SBCO.js.map
2011
+ //# sourceMappingURL=chunk-G2X4SBCO.js.map