@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 +1 @@
1
- {"version":3,"sources":["../../src/order/deliveryTime.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type {\n City,\n DeliveryInterval,\n DeliveryIntervalsGroup,\n Schedule,\n Stock,\n} from '@zenky/storefront-api'\n\nconst WEEKDAY_IDS: DeliveryIntervalsGroup['day_id'][] = [\n 'sunday',\n 'monday',\n 'tuesday',\n 'wednesday',\n 'thursday',\n 'friday',\n 'saturday',\n]\n\ninterface TimeParts {\n year: number\n month: number\n day: number\n hour: number\n minute: number\n}\n\ninterface DeliveryTimeWindow {\n startMinutes: number\n endMinutes: number\n}\n\nexport interface GenerateFreeTimeGroupsOptions {\n offsetMinutes: number\n stepMinutes: number\n schedule: Schedule | null | undefined\n storeTimezone?: string | null\n now?: Date\n}\n\n/**\n * Слот free-режима с собственной датой доставки. «Ночной хвост» (00:00–02:00) показывается\n * в той же вкладке, что и начало бизнес-дня, но его `date` — уже следующие сутки, поэтому\n * `deliver_at` строится по `interval.date`, а не по дате вкладки (`group.date`).\n */\nexport interface FreeDeliveryInterval extends DeliveryInterval {\n date: string\n}\n\nconst DEFAULT_MAX_DAYS = 7\nconst FULL_DAY_MINUTES = 24 * 60\n\nexport interface ResolveDeliveryTimeScheduleOptions {\n orderStock?: Stock | null\n selectedStock?: Stock | null\n orderCity?: City | null\n selectedCity?: City | null\n storeCities?: City[]\n}\n\nexport interface ResolvedDeliveryTimeSchedule {\n schedule: Schedule | null\n source: string | null\n}\n\nfunction pad(value: number): string {\n return String(value).padStart(2, '0')\n}\n\nexport function formatYmd(date: Date): string {\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`\n}\n\nfunction formatUtcYmd(date: Date): string {\n return `${date.getUTCFullYear()}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())}`\n}\n\nfunction formatDayName(offset: number, date: Date): string {\n if (offset === 0) return 'Сегодня'\n if (offset === 1) return 'Завтра'\n if (offset === 2) return 'Послезавтра'\n return `${pad(date.getUTCDate())}.${pad(date.getUTCMonth() + 1)}`\n}\n\nfunction ceilToStep(minutes: number, step: number): number {\n return Math.ceil(minutes / step) * step\n}\n\nfunction nowInTimezone(timezone: string | null | undefined, now = new Date()): TimeParts {\n try {\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone: timezone || undefined,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n hour12: false,\n }).formatToParts(now)\n const get = (type: string): string => parts.find(part => part.type === type)?.value ?? ''\n\n return {\n year: Number(get('year')),\n month: Number(get('month')),\n day: Number(get('day')),\n hour: Number(get('hour') === '24' ? '0' : get('hour')),\n minute: Number(get('minute')),\n }\n } catch {\n return {\n year: now.getFullYear(),\n month: now.getMonth() + 1,\n day: now.getDate(),\n hour: now.getHours(),\n minute: now.getMinutes(),\n }\n }\n}\n\n/** Часы:минуты внутри суток в TZ магазина (а не в TZ браузера). */\nfunction minutesOfDayInTimezone(date: Date, timezone: string | null | undefined): number {\n try {\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone: timezone || undefined,\n hour: '2-digit',\n minute: '2-digit',\n hour12: false,\n }).formatToParts(date)\n const get = (type: string): string => parts.find(part => part.type === type)?.value ?? ''\n const hour = Number(get('hour') === '24' ? '0' : get('hour'))\n const minute = Number(get('minute'))\n\n return hour * 60 + minute\n } catch {\n return date.getHours() * 60 + date.getMinutes()\n }\n}\n\nfunction parseTimeToMinutes(value: string): number | null {\n const match = value.trim().match(/^(\\d{1,2}):(\\d{2})/)\n if (!match) return null\n\n const hours = Number(match[1])\n const minutes = Number(match[2])\n if (!Number.isInteger(hours) || !Number.isInteger(minutes)) return null\n if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59) return null\n\n return hours * 60 + minutes\n}\n\n/**\n * Нормализует окно работы из пары «начало/конец» в минутах суток.\n * `endMinutes` может быть > 24:00 — для окон, уходящих за полночь. Раскладку «ночного\n * хвоста» по календарным датам делает {@link generateFreeTimeGroups}.\n * - `00:00–00:00` → круглосуточно (`0 … 24:00`).\n * - Окно через полночь (`10:00–00:00`, `22:00–02:00`, конец < начала) → конец относится к\n * следующим суткам, поэтому добавляем к нему 24 часа (`02:00` → `26:00`).\n * - Нулевое окно (`start === end`, кроме круглосуточного) → расписания нет.\n */\nfunction buildWindow(startMinutes: number, endMinutes: number): DeliveryTimeWindow | null {\n if (startMinutes === 0 && endMinutes === 0) {\n return { startMinutes: 0, endMinutes: FULL_DAY_MINUTES }\n }\n\n if (endMinutes === startMinutes) return null\n\n if (endMinutes < startMinutes) {\n return { startMinutes, endMinutes: endMinutes + FULL_DAY_MINUTES }\n }\n\n return { startMinutes, endMinutes }\n}\n\nfunction parseHoursWindow(hours: string | null | undefined): DeliveryTimeWindow | null {\n if (!hours) return null\n\n const parts = hours.split(/\\s*[-–—]\\s*/)\n if (parts.length < 2) return null\n\n const startMinutes = parseTimeToMinutes(parts[0] ?? '')\n const endMinutes = parseTimeToMinutes(parts[1] ?? '')\n if (startMinutes === null || endMinutes === null) return null\n\n return buildWindow(startMinutes, endMinutes)\n}\n\nfunction getScheduleWindow(\n schedule: Schedule | null | undefined,\n dayId: DeliveryIntervalsGroup['day_id'],\n timezone?: string | null\n): DeliveryTimeWindow | null {\n const dayHours = schedule?.days?.[dayId]?.hours\n const dayWindow = parseHoursWindow(dayHours)\n if (dayWindow) return dayWindow\n\n if (!schedule?.opening_at || !schedule.closing_at) return null\n\n const opening = new Date(schedule.opening_at)\n const closing = new Date(schedule.closing_at)\n if (Number.isNaN(opening.getTime()) || Number.isNaN(closing.getTime())) return null\n\n return buildWindow(\n minutesOfDayInTimezone(opening, timezone),\n minutesOfDayInTimezone(closing, timezone)\n )\n}\n\nfunction hasAvailableScheduleWindow(schedule: Schedule | null | undefined): boolean {\n if (!schedule) return false\n return WEEKDAY_IDS.some(dayId => getScheduleWindow(schedule, dayId) !== null)\n}\n\nfunction findStockSchedule(city: City | null | undefined): Schedule | null {\n return city?.stocks?.find(stock => hasAvailableScheduleWindow(stock.schedule))?.schedule ?? null\n}\n\nexport function resolveDeliveryTimeSchedule(\n options: ResolveDeliveryTimeScheduleOptions\n): Schedule | null {\n return resolveDeliveryTimeScheduleWithSource(options).schedule\n}\n\nexport function resolveDeliveryTimeScheduleWithSource(\n options: ResolveDeliveryTimeScheduleOptions\n): ResolvedDeliveryTimeSchedule {\n const cityId = options.orderCity?.id ?? options.selectedCity?.id ?? null\n const storeCity = cityId ? (options.storeCities ?? []).find(city => city.id === cityId) : null\n const fallbackStoreCity = options.storeCities?.find(city =>\n hasAvailableScheduleWindow(city.schedule)\n )\n const fallbackStoreCityWithStock = options.storeCities?.find(\n city => findStockSchedule(city) !== null\n )\n\n const candidates: ResolvedDeliveryTimeSchedule[] = [\n { schedule: options.orderStock?.schedule ?? null, source: 'order.stock.schedule' },\n { schedule: options.selectedStock?.schedule ?? null, source: 'profile.stock.schedule' },\n { schedule: options.orderCity?.schedule ?? null, source: 'order.city.schedule' },\n { schedule: options.selectedCity?.schedule ?? null, source: 'profile.city.schedule' },\n { schedule: storeCity?.schedule ?? null, source: 'store.cities[current].schedule' },\n { schedule: findStockSchedule(options.orderCity), source: 'order.city.stocks[].schedule' },\n {\n schedule: findStockSchedule(options.selectedCity),\n source: 'profile.city.stocks[].schedule',\n },\n {\n schedule: findStockSchedule(storeCity),\n source: 'store.cities[current].stocks[].schedule',\n },\n { schedule: fallbackStoreCity?.schedule ?? null, source: 'store.cities[].schedule' },\n {\n schedule: findStockSchedule(fallbackStoreCityWithStock),\n source: 'store.cities[].stocks[].schedule',\n },\n ]\n\n return (\n candidates.find(candidate => hasAvailableScheduleWindow(candidate.schedule)) ?? {\n schedule: null,\n source: null,\n }\n )\n}\n\nexport function generateFreeTimeGroups(\n options: GenerateFreeTimeGroupsOptions\n): DeliveryIntervalsGroup[] {\n const safeOffset = Math.max(10, options.offsetMinutes)\n const step = options.stepMinutes >= 15 ? options.stepMinutes : 15\n const nowParts = nowInTimezone(options.storeTimezone, options.now)\n\n const baseYear = nowParts.year\n const baseMonth = nowParts.month - 1\n const baseDay = nowParts.day\n\n // Минимально допустимое время для слота «сегодня»: сейчас + offset, округлённое вверх.\n const earliestTodayMinutes = ceilToStep(nowParts.hour * 60 + nowParts.minute + safeOffset, step)\n\n const groups: DeliveryIntervalsGroup[] = []\n\n // Одна вкладка = один бизнес-день (открытие → закрытие). Окно может уходить за полночь —\n // тогда «ночной хвост» 00:00–02:00 остаётся в этой же вкладке, но дата у его слотов уже\n // следующая (см. FreeDeliveryInterval.date).\n for (let dayOffset = 0; dayOffset < DEFAULT_MAX_DAYS; dayOffset++) {\n const stamp = new Date(Date.UTC(baseYear, baseMonth, baseDay + dayOffset))\n const dayId = WEEKDAY_IDS[stamp.getUTCDay()] ?? 'monday'\n const window = getScheduleWindow(options.schedule, dayId, options.storeTimezone)\n\n if (!window) continue\n\n const intervals: FreeDeliveryInterval[] = []\n const seenTimes = new Set<string>()\n\n for (let minutes = window.startMinutes; minutes <= window.endMinutes; minutes += step) {\n const dayShift = Math.floor(minutes / FULL_DAY_MINUTES)\n const minuteOfDay = minutes - dayShift * FULL_DAY_MINUTES\n const time = `${pad(Math.floor(minuteOfDay / 60))}:${pad(minuteOfDay % 60)}`\n\n // Внутри одной вкладки HH:MM уникален: у круглосуточного окна закрытие 00:00\n // совпало бы с открытием — оставляем первое (своего дня), дубль-закрытие 00:00\n // не эмитим (он же — открытие следующей вкладки). Дедуп ДО today-фильтра, чтобы\n // закрывающий 00:00 отсеялся, даже когда открытие 00:00 отброшено как «в прошлом».\n if (seenTimes.has(time)) continue\n seenTimes.add(time)\n\n // Слот «сегодня» не раньше, чем сейчас + offset.\n if (dayOffset === 0 && minutes < earliestTodayMinutes) continue\n\n const slotStamp = new Date(\n Date.UTC(baseYear, baseMonth, baseDay + dayOffset + dayShift)\n )\n intervals.push({\n id: time,\n date: formatUtcYmd(slotStamp),\n price: null,\n name: time,\n description: null,\n })\n }\n\n if (!intervals.length) continue\n\n groups.push({\n day_id: dayId,\n date: formatUtcYmd(stamp),\n name: formatDayName(dayOffset, stamp),\n intervals,\n })\n }\n\n return groups\n}\n"]}
1
+ {"version":3,"sources":["../../src/order/deliveryTime.ts"],"names":[],"mappings":";AAQA,IAAM,WAAA,GAAkD;AAAA,EACpD,QAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,WAAA;AAAA,EACA,UAAA;AAAA,EACA,QAAA;AAAA,EACA;AACJ,CAAA;AAgCA,IAAM,gBAAA,GAAmB,CAAA;AACzB,IAAM,mBAAmB,EAAA,GAAK,EAAA;AAe9B,SAAS,IAAI,KAAA,EAAuB;AAChC,EAAA,OAAO,MAAA,CAAO,KAAK,CAAA,CAAE,QAAA,CAAS,GAAG,GAAG,CAAA;AACxC;AAEO,SAAS,UAAU,IAAA,EAAoB;AAC1C,EAAA,OAAO,GAAG,IAAA,CAAK,WAAA,EAAa,CAAA,CAAA,EAAI,IAAI,IAAA,CAAK,QAAA,EAAS,GAAI,CAAC,CAAC,CAAA,CAAA,EAAI,GAAA,CAAI,IAAA,CAAK,OAAA,EAAS,CAAC,CAAA,CAAA;AACnF;AAEA,SAAS,aAAa,IAAA,EAAoB;AACtC,EAAA,OAAO,GAAG,IAAA,CAAK,cAAA,EAAgB,CAAA,CAAA,EAAI,IAAI,IAAA,CAAK,WAAA,EAAY,GAAI,CAAC,CAAC,CAAA,CAAA,EAAI,GAAA,CAAI,IAAA,CAAK,UAAA,EAAY,CAAC,CAAA,CAAA;AAC5F;AAEA,SAAS,aAAA,CAAc,QAAgB,IAAA,EAAoB;AACvD,EAAA,IAAI,MAAA,KAAW,GAAG,OAAO,4CAAA;AACzB,EAAA,IAAI,MAAA,KAAW,GAAG,OAAO,sCAAA;AACzB,EAAA,IAAI,MAAA,KAAW,GAAG,OAAO,oEAAA;AACzB,EAAA,OAAO,CAAA,EAAG,GAAA,CAAI,IAAA,CAAK,UAAA,EAAY,CAAC,CAAA,CAAA,EAAI,GAAA,CAAI,IAAA,CAAK,WAAA,EAAY,GAAI,CAAC,CAAC,CAAA,CAAA;AACnE;AAEA,SAAS,UAAA,CAAW,SAAiB,IAAA,EAAsB;AACvD,EAAA,OAAO,IAAA,CAAK,IAAA,CAAK,OAAA,GAAU,IAAI,CAAA,GAAI,IAAA;AACvC;AAEA,SAAS,aAAA,CAAc,QAAA,EAAqC,GAAA,mBAAM,IAAI,MAAK,EAAc;AACrF,EAAA,IAAI;AACA,IAAA,MAAM,KAAA,GAAQ,IAAI,IAAA,CAAK,cAAA,CAAe,OAAA,EAAS;AAAA,MAC3C,UAAU,QAAA,IAAY,KAAA,CAAA;AAAA,MACtB,IAAA,EAAM,SAAA;AAAA,MACN,KAAA,EAAO,SAAA;AAAA,MACP,GAAA,EAAK,SAAA;AAAA,MACL,IAAA,EAAM,SAAA;AAAA,MACN,MAAA,EAAQ,SAAA;AAAA,MACR,MAAA,EAAQ;AAAA,KACX,CAAA,CAAE,aAAA,CAAc,GAAG,CAAA;AACpB,IAAA,MAAM,GAAA,GAAM,CAAC,IAAA,KAAyB,KAAA,CAAM,IAAA,CAAK,CAAC,IAAA,KAAS,IAAA,CAAK,IAAA,KAAS,IAAI,CAAA,EAAG,KAAA,IAAS,EAAA;AAEzF,IAAA,OAAO;AAAA,MACH,IAAA,EAAM,MAAA,CAAO,GAAA,CAAI,MAAM,CAAC,CAAA;AAAA,MACxB,KAAA,EAAO,MAAA,CAAO,GAAA,CAAI,OAAO,CAAC,CAAA;AAAA,MAC1B,GAAA,EAAK,MAAA,CAAO,GAAA,CAAI,KAAK,CAAC,CAAA;AAAA,MACtB,IAAA,EAAM,OAAO,GAAA,CAAI,MAAM,MAAM,IAAA,GAAO,GAAA,GAAM,GAAA,CAAI,MAAM,CAAC,CAAA;AAAA,MACrD,MAAA,EAAQ,MAAA,CAAO,GAAA,CAAI,QAAQ,CAAC;AAAA,KAChC;AAAA,EACJ,CAAA,CAAA,MAAQ;AACJ,IAAA,OAAO;AAAA,MACH,IAAA,EAAM,IAAI,WAAA,EAAY;AAAA,MACtB,KAAA,EAAO,GAAA,CAAI,QAAA,EAAS,GAAI,CAAA;AAAA,MACxB,GAAA,EAAK,IAAI,OAAA,EAAQ;AAAA,MACjB,IAAA,EAAM,IAAI,QAAA,EAAS;AAAA,MACnB,MAAA,EAAQ,IAAI,UAAA;AAAW,KAC3B;AAAA,EACJ;AACJ;AAGA,SAAS,sBAAA,CAAuB,MAAY,QAAA,EAA6C;AACrF,EAAA,IAAI;AACA,IAAA,MAAM,KAAA,GAAQ,IAAI,IAAA,CAAK,cAAA,CAAe,OAAA,EAAS;AAAA,MAC3C,UAAU,QAAA,IAAY,KAAA,CAAA;AAAA,MACtB,IAAA,EAAM,SAAA;AAAA,MACN,MAAA,EAAQ,SAAA;AAAA,MACR,MAAA,EAAQ;AAAA,KACX,CAAA,CAAE,aAAA,CAAc,IAAI,CAAA;AACrB,IAAA,MAAM,GAAA,GAAM,CAAC,IAAA,KAAyB,KAAA,CAAM,IAAA,CAAK,CAAC,IAAA,KAAS,IAAA,CAAK,IAAA,KAAS,IAAI,CAAA,EAAG,KAAA,IAAS,EAAA;AACzF,IAAA,MAAM,IAAA,GAAO,OAAO,GAAA,CAAI,MAAM,MAAM,IAAA,GAAO,GAAA,GAAM,GAAA,CAAI,MAAM,CAAC,CAAA;AAC5D,IAAA,MAAM,MAAA,GAAS,MAAA,CAAO,GAAA,CAAI,QAAQ,CAAC,CAAA;AAEnC,IAAA,OAAO,OAAO,EAAA,GAAK,MAAA;AAAA,EACvB,CAAA,CAAA,MAAQ;AACJ,IAAA,OAAO,IAAA,CAAK,QAAA,EAAS,GAAI,EAAA,GAAK,KAAK,UAAA,EAAW;AAAA,EAClD;AACJ;AAEA,SAAS,mBAAmB,KAAA,EAA8B;AACtD,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,IAAA,EAAK,CAAE,MAAM,oBAAoB,CAAA;AACrD,EAAA,IAAI,CAAC,OAAO,OAAO,IAAA;AAEnB,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,KAAA,CAAM,CAAC,CAAC,CAAA;AAC7B,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,KAAA,CAAM,CAAC,CAAC,CAAA;AAC/B,EAAA,IAAI,CAAC,MAAA,CAAO,SAAA,CAAU,KAAK,CAAA,IAAK,CAAC,MAAA,CAAO,SAAA,CAAU,OAAO,CAAA,EAAG,OAAO,IAAA;AACnE,EAAA,IAAI,KAAA,GAAQ,KAAK,KAAA,GAAQ,EAAA,IAAM,UAAU,CAAA,IAAK,OAAA,GAAU,IAAI,OAAO,IAAA;AAEnE,EAAA,OAAO,QAAQ,EAAA,GAAK,OAAA;AACxB;AAWA,SAAS,WAAA,CAAY,cAAsB,UAAA,EAA+C;AACtF,EAAA,IAAI,YAAA,KAAiB,CAAA,IAAK,UAAA,KAAe,CAAA,EAAG;AACxC,IAAA,OAAO,EAAE,YAAA,EAAc,CAAA,EAAG,UAAA,EAAY,gBAAA,EAAiB;AAAA,EAC3D;AAEA,EAAA,IAAI,UAAA,KAAe,cAAc,OAAO,IAAA;AAExC,EAAA,IAAI,aAAa,YAAA,EAAc;AAC3B,IAAA,OAAO,EAAE,YAAA,EAAc,UAAA,EAAY,UAAA,GAAa,gBAAA,EAAiB;AAAA,EACrE;AAEA,EAAA,OAAO,EAAE,cAAc,UAAA,EAAW;AACtC;AAEA,SAAS,iBAAiB,KAAA,EAA6D;AACnF,EAAA,IAAI,CAAC,OAAO,OAAO,IAAA;AAEnB,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,aAAa,CAAA;AACvC,EAAA,IAAI,KAAA,CAAM,MAAA,GAAS,CAAA,EAAG,OAAO,IAAA;AAE7B,EAAA,MAAM,YAAA,GAAe,kBAAA,CAAmB,KAAA,CAAM,CAAC,KAAK,EAAE,CAAA;AACtD,EAAA,MAAM,UAAA,GAAa,kBAAA,CAAmB,KAAA,CAAM,CAAC,KAAK,EAAE,CAAA;AACpD,EAAA,IAAI,YAAA,KAAiB,IAAA,IAAQ,UAAA,KAAe,IAAA,EAAM,OAAO,IAAA;AAEzD,EAAA,OAAO,WAAA,CAAY,cAAc,UAAU,CAAA;AAC/C;AAEA,SAAS,iBAAA,CACL,QAAA,EACA,KAAA,EACA,QAAA,EACyB;AACzB,EAAA,MAAM,QAAA,GAAW,QAAA,EAAU,IAAA,GAAO,KAAK,CAAA,EAAG,KAAA;AAC1C,EAAA,MAAM,SAAA,GAAY,iBAAiB,QAAQ,CAAA;AAC3C,EAAA,IAAI,WAAW,OAAO,SAAA;AAEtB,EAAA,IAAI,CAAC,QAAA,EAAU,UAAA,IAAc,CAAC,QAAA,CAAS,YAAY,OAAO,IAAA;AAE1D,EAAA,MAAM,OAAA,GAAU,IAAI,IAAA,CAAK,QAAA,CAAS,UAAU,CAAA;AAC5C,EAAA,MAAM,OAAA,GAAU,IAAI,IAAA,CAAK,QAAA,CAAS,UAAU,CAAA;AAC5C,EAAA,IAAI,MAAA,CAAO,KAAA,CAAM,OAAA,CAAQ,OAAA,EAAS,CAAA,IAAK,MAAA,CAAO,KAAA,CAAM,OAAA,CAAQ,OAAA,EAAS,CAAA,EAAG,OAAO,IAAA;AAE/E,EAAA,OAAO,WAAA;AAAA,IACH,sBAAA,CAAuB,SAAS,QAAQ,CAAA;AAAA,IACxC,sBAAA,CAAuB,SAAS,QAAQ;AAAA,GAC5C;AACJ;AAEA,SAAS,2BAA2B,QAAA,EAAgD;AAChF,EAAA,IAAI,CAAC,UAAU,OAAO,KAAA;AACtB,EAAA,OAAO,WAAA,CAAY,KAAK,CAAC,KAAA,KAAU,kBAAkB,QAAA,EAAU,KAAK,MAAM,IAAI,CAAA;AAClF;AAEA,SAAS,kBAAkB,IAAA,EAAgD;AACvE,EAAA,OACI,IAAA,EAAM,MAAA,EAAQ,IAAA,CAAK,CAAC,KAAA,KAAU,2BAA2B,KAAA,CAAM,QAAQ,CAAC,CAAA,EAAG,QAAA,IAAY,IAAA;AAE/F;AAEO,SAAS,4BACZ,OAAA,EACe;AACf,EAAA,OAAO,qCAAA,CAAsC,OAAO,CAAA,CAAE,QAAA;AAC1D;AAEO,SAAS,sCACZ,OAAA,EAC4B;AAC5B,EAAA,MAAM,SAAS,OAAA,CAAQ,SAAA,EAAW,EAAA,IAAM,OAAA,CAAQ,cAAc,EAAA,IAAM,IAAA;AACpE,EAAA,MAAM,SAAA,GAAY,MAAA,GAAA,CAAU,OAAA,CAAQ,WAAA,IAAe,EAAC,EAAG,IAAA,CAAK,CAAC,IAAA,KAAS,IAAA,CAAK,EAAA,KAAO,MAAM,CAAA,GAAI,IAAA;AAC5F,EAAA,MAAM,iBAAA,GAAoB,QAAQ,WAAA,EAAa,IAAA;AAAA,IAAK,CAAC,IAAA,KACjD,0BAAA,CAA2B,IAAA,CAAK,QAAQ;AAAA,GAC5C;AACA,EAAA,MAAM,0BAAA,GAA6B,QAAQ,WAAA,EAAa,IAAA;AAAA,IACpD,CAAC,IAAA,KAAS,iBAAA,CAAkB,IAAI,CAAA,KAAM;AAAA,GAC1C;AAEA,EAAA,MAAM,UAAA,GAA6C;AAAA,IAC/C,EAAE,QAAA,EAAU,OAAA,CAAQ,YAAY,QAAA,IAAY,IAAA,EAAM,QAAQ,sBAAA,EAAuB;AAAA,IACjF,EAAE,QAAA,EAAU,OAAA,CAAQ,eAAe,QAAA,IAAY,IAAA,EAAM,QAAQ,wBAAA,EAAyB;AAAA,IACtF,EAAE,QAAA,EAAU,OAAA,CAAQ,WAAW,QAAA,IAAY,IAAA,EAAM,QAAQ,qBAAA,EAAsB;AAAA,IAC/E,EAAE,QAAA,EAAU,OAAA,CAAQ,cAAc,QAAA,IAAY,IAAA,EAAM,QAAQ,uBAAA,EAAwB;AAAA,IACpF,EAAE,QAAA,EAAU,SAAA,EAAW,QAAA,IAAY,IAAA,EAAM,QAAQ,gCAAA,EAAiC;AAAA,IAClF,EAAE,QAAA,EAAU,iBAAA,CAAkB,QAAQ,SAAS,CAAA,EAAG,QAAQ,8BAAA,EAA+B;AAAA,IACzF;AAAA,MACI,QAAA,EAAU,iBAAA,CAAkB,OAAA,CAAQ,YAAY,CAAA;AAAA,MAChD,MAAA,EAAQ;AAAA,KACZ;AAAA,IACA;AAAA,MACI,QAAA,EAAU,kBAAkB,SAAS,CAAA;AAAA,MACrC,MAAA,EAAQ;AAAA,KACZ;AAAA,IACA,EAAE,QAAA,EAAU,iBAAA,EAAmB,QAAA,IAAY,IAAA,EAAM,QAAQ,yBAAA,EAA0B;AAAA,IACnF;AAAA,MACI,QAAA,EAAU,kBAAkB,0BAA0B,CAAA;AAAA,MACtD,MAAA,EAAQ;AAAA;AACZ,GACJ;AAEA,EAAA,OACI,UAAA,CAAW,KAAK,CAAC,SAAA,KAAc,2BAA2B,SAAA,CAAU,QAAQ,CAAC,CAAA,IAAK;AAAA,IAC9E,QAAA,EAAU,IAAA;AAAA,IACV,MAAA,EAAQ;AAAA,GACZ;AAER;AAEO,SAAS,uBACZ,OAAA,EACwB;AACxB,EAAA,MAAM,UAAA,GAAa,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,QAAQ,aAAa,CAAA;AACrD,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,WAAA,IAAe,EAAA,GAAK,QAAQ,WAAA,GAAc,EAAA;AAC/D,EAAA,MAAM,QAAA,GAAW,aAAA,CAAc,OAAA,CAAQ,aAAA,EAAe,QAAQ,GAAG,CAAA;AAEjE,EAAA,MAAM,WAAW,QAAA,CAAS,IAAA;AAC1B,EAAA,MAAM,SAAA,GAAY,SAAS,KAAA,GAAQ,CAAA;AACnC,EAAA,MAAM,UAAU,QAAA,CAAS,GAAA;AAGzB,EAAA,MAAM,oBAAA,GAAuB,WAAW,QAAA,CAAS,IAAA,GAAO,KAAK,QAAA,CAAS,MAAA,GAAS,YAAY,IAAI,CAAA;AAE/F,EAAA,MAAM,SAAmC,EAAC;AAK1C,EAAA,KAAA,IAAS,SAAA,GAAY,CAAA,EAAG,SAAA,GAAY,gBAAA,EAAkB,SAAA,EAAA,EAAa;AAC/D,IAAA,MAAM,KAAA,GAAQ,IAAI,IAAA,CAAK,IAAA,CAAK,IAAI,QAAA,EAAU,SAAA,EAAW,OAAA,GAAU,SAAS,CAAC,CAAA;AACzE,IAAA,MAAM,KAAA,GAAQ,WAAA,CAAY,KAAA,CAAM,SAAA,EAAW,CAAA,IAAK,QAAA;AAChD,IAAA,MAAM,SAAS,iBAAA,CAAkB,OAAA,CAAQ,QAAA,EAAU,KAAA,EAAO,QAAQ,aAAa,CAAA;AAE/E,IAAA,IAAI,CAAC,MAAA,EAAQ;AAEb,IAAA,MAAM,YAAoC,EAAC;AAC3C,IAAA,MAAM,SAAA,uBAAgB,GAAA,EAAY;AAElC,IAAA,KAAA,IAAS,UAAU,MAAA,CAAO,YAAA,EAAc,WAAW,MAAA,CAAO,UAAA,EAAY,WAAW,IAAA,EAAM;AACnF,MAAA,MAAM,QAAA,GAAW,IAAA,CAAK,KAAA,CAAM,OAAA,GAAU,gBAAgB,CAAA;AACtD,MAAA,MAAM,WAAA,GAAc,UAAU,QAAA,GAAW,gBAAA;AACzC,MAAA,MAAM,IAAA,GAAO,CAAA,EAAG,GAAA,CAAI,IAAA,CAAK,KAAA,CAAM,WAAA,GAAc,EAAE,CAAC,CAAC,CAAA,CAAA,EAAI,GAAA,CAAI,WAAA,GAAc,EAAE,CAAC,CAAA,CAAA;AAM1E,MAAA,IAAI,SAAA,CAAU,GAAA,CAAI,IAAI,CAAA,EAAG;AACzB,MAAA,SAAA,CAAU,IAAI,IAAI,CAAA;AAGlB,MAAA,IAAI,SAAA,KAAc,CAAA,IAAK,OAAA,GAAU,oBAAA,EAAsB;AAEvD,MAAA,MAAM,YAAY,IAAI,IAAA;AAAA,QAClB,KAAK,GAAA,CAAI,QAAA,EAAU,SAAA,EAAW,OAAA,GAAU,YAAY,QAAQ;AAAA,OAChE;AACA,MAAA,SAAA,CAAU,IAAA,CAAK;AAAA,QACX,EAAA,EAAI,IAAA;AAAA,QACJ,IAAA,EAAM,aAAa,SAAS,CAAA;AAAA,QAC5B,KAAA,EAAO,IAAA;AAAA,QACP,IAAA,EAAM,IAAA;AAAA,QACN,WAAA,EAAa;AAAA,OAChB,CAAA;AAAA,IACL;AAEA,IAAA,IAAI,CAAC,UAAU,MAAA,EAAQ;AAEvB,IAAA,MAAA,CAAO,IAAA,CAAK;AAAA,MACR,MAAA,EAAQ,KAAA;AAAA,MACR,IAAA,EAAM,aAAa,KAAK,CAAA;AAAA,MACxB,IAAA,EAAM,aAAA,CAAc,SAAA,EAAW,KAAK,CAAA;AAAA,MACpC;AAAA,KACH,CAAA;AAAA,EACL;AAEA,EAAA,OAAO,MAAA;AACX","file":"index.js","sourcesContent":["import type {\n City,\n DeliveryInterval,\n DeliveryIntervalsGroup,\n Schedule,\n Stock,\n} from '@zenky/storefront-api'\n\nconst WEEKDAY_IDS: DeliveryIntervalsGroup['day_id'][] = [\n 'sunday',\n 'monday',\n 'tuesday',\n 'wednesday',\n 'thursday',\n 'friday',\n 'saturday',\n]\n\ninterface TimeParts {\n year: number\n month: number\n day: number\n hour: number\n minute: number\n}\n\ninterface DeliveryTimeWindow {\n startMinutes: number\n endMinutes: number\n}\n\nexport interface GenerateFreeTimeGroupsOptions {\n offsetMinutes: number\n stepMinutes: number\n schedule: Schedule | null | undefined\n storeTimezone?: string | null\n now?: Date\n}\n\n/**\n * Слот free-режима с собственной датой доставки. «Ночной хвост» (00:00–02:00) показывается\n * в той же вкладке, что и начало бизнес-дня, но его `date` — уже следующие сутки, поэтому\n * `deliver_at` строится по `interval.date`, а не по дате вкладки (`group.date`).\n */\nexport interface FreeDeliveryInterval extends DeliveryInterval {\n date: string\n}\n\nconst DEFAULT_MAX_DAYS = 7\nconst FULL_DAY_MINUTES = 24 * 60\n\nexport interface ResolveDeliveryTimeScheduleOptions {\n orderStock?: Stock | null\n selectedStock?: Stock | null\n orderCity?: City | null\n selectedCity?: City | null\n storeCities?: City[]\n}\n\nexport interface ResolvedDeliveryTimeSchedule {\n schedule: Schedule | null\n source: string | null\n}\n\nfunction pad(value: number): string {\n return String(value).padStart(2, '0')\n}\n\nexport function formatYmd(date: Date): string {\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`\n}\n\nfunction formatUtcYmd(date: Date): string {\n return `${date.getUTCFullYear()}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())}`\n}\n\nfunction formatDayName(offset: number, date: Date): string {\n if (offset === 0) return 'Сегодня'\n if (offset === 1) return 'Завтра'\n if (offset === 2) return 'Послезавтра'\n return `${pad(date.getUTCDate())}.${pad(date.getUTCMonth() + 1)}`\n}\n\nfunction ceilToStep(minutes: number, step: number): number {\n return Math.ceil(minutes / step) * step\n}\n\nfunction nowInTimezone(timezone: string | null | undefined, now = new Date()): TimeParts {\n try {\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone: timezone || undefined,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n hour12: false,\n }).formatToParts(now)\n const get = (type: string): string => parts.find((part) => part.type === type)?.value ?? ''\n\n return {\n year: Number(get('year')),\n month: Number(get('month')),\n day: Number(get('day')),\n hour: Number(get('hour') === '24' ? '0' : get('hour')),\n minute: Number(get('minute')),\n }\n } catch {\n return {\n year: now.getFullYear(),\n month: now.getMonth() + 1,\n day: now.getDate(),\n hour: now.getHours(),\n minute: now.getMinutes(),\n }\n }\n}\n\n/** Часы:минуты внутри суток в TZ магазина (а не в TZ браузера). */\nfunction minutesOfDayInTimezone(date: Date, timezone: string | null | undefined): number {\n try {\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone: timezone || undefined,\n hour: '2-digit',\n minute: '2-digit',\n hour12: false,\n }).formatToParts(date)\n const get = (type: string): string => parts.find((part) => part.type === type)?.value ?? ''\n const hour = Number(get('hour') === '24' ? '0' : get('hour'))\n const minute = Number(get('minute'))\n\n return hour * 60 + minute\n } catch {\n return date.getHours() * 60 + date.getMinutes()\n }\n}\n\nfunction parseTimeToMinutes(value: string): number | null {\n const match = value.trim().match(/^(\\d{1,2}):(\\d{2})/)\n if (!match) return null\n\n const hours = Number(match[1])\n const minutes = Number(match[2])\n if (!Number.isInteger(hours) || !Number.isInteger(minutes)) return null\n if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59) return null\n\n return hours * 60 + minutes\n}\n\n/**\n * Нормализует окно работы из пары «начало/конец» в минутах суток.\n * `endMinutes` может быть > 24:00 — для окон, уходящих за полночь. Раскладку «ночного\n * хвоста» по календарным датам делает {@link generateFreeTimeGroups}.\n * - `00:00–00:00` → круглосуточно (`0 … 24:00`).\n * - Окно через полночь (`10:00–00:00`, `22:00–02:00`, конец < начала) → конец относится к\n * следующим суткам, поэтому добавляем к нему 24 часа (`02:00` → `26:00`).\n * - Нулевое окно (`start === end`, кроме круглосуточного) → расписания нет.\n */\nfunction buildWindow(startMinutes: number, endMinutes: number): DeliveryTimeWindow | null {\n if (startMinutes === 0 && endMinutes === 0) {\n return { startMinutes: 0, endMinutes: FULL_DAY_MINUTES }\n }\n\n if (endMinutes === startMinutes) return null\n\n if (endMinutes < startMinutes) {\n return { startMinutes, endMinutes: endMinutes + FULL_DAY_MINUTES }\n }\n\n return { startMinutes, endMinutes }\n}\n\nfunction parseHoursWindow(hours: string | null | undefined): DeliveryTimeWindow | null {\n if (!hours) return null\n\n const parts = hours.split(/\\s*[-–—]\\s*/)\n if (parts.length < 2) return null\n\n const startMinutes = parseTimeToMinutes(parts[0] ?? '')\n const endMinutes = parseTimeToMinutes(parts[1] ?? '')\n if (startMinutes === null || endMinutes === null) return null\n\n return buildWindow(startMinutes, endMinutes)\n}\n\nfunction getScheduleWindow(\n schedule: Schedule | null | undefined,\n dayId: DeliveryIntervalsGroup['day_id'],\n timezone?: string | null\n): DeliveryTimeWindow | null {\n const dayHours = schedule?.days?.[dayId]?.hours\n const dayWindow = parseHoursWindow(dayHours)\n if (dayWindow) return dayWindow\n\n if (!schedule?.opening_at || !schedule.closing_at) return null\n\n const opening = new Date(schedule.opening_at)\n const closing = new Date(schedule.closing_at)\n if (Number.isNaN(opening.getTime()) || Number.isNaN(closing.getTime())) return null\n\n return buildWindow(\n minutesOfDayInTimezone(opening, timezone),\n minutesOfDayInTimezone(closing, timezone)\n )\n}\n\nfunction hasAvailableScheduleWindow(schedule: Schedule | null | undefined): boolean {\n if (!schedule) return false\n return WEEKDAY_IDS.some((dayId) => getScheduleWindow(schedule, dayId) !== null)\n}\n\nfunction findStockSchedule(city: City | null | undefined): Schedule | null {\n return (\n city?.stocks?.find((stock) => hasAvailableScheduleWindow(stock.schedule))?.schedule ?? null\n )\n}\n\nexport function resolveDeliveryTimeSchedule(\n options: ResolveDeliveryTimeScheduleOptions\n): Schedule | null {\n return resolveDeliveryTimeScheduleWithSource(options).schedule\n}\n\nexport function resolveDeliveryTimeScheduleWithSource(\n options: ResolveDeliveryTimeScheduleOptions\n): ResolvedDeliveryTimeSchedule {\n const cityId = options.orderCity?.id ?? options.selectedCity?.id ?? null\n const storeCity = cityId ? (options.storeCities ?? []).find((city) => city.id === cityId) : null\n const fallbackStoreCity = options.storeCities?.find((city) =>\n hasAvailableScheduleWindow(city.schedule)\n )\n const fallbackStoreCityWithStock = options.storeCities?.find(\n (city) => findStockSchedule(city) !== null\n )\n\n const candidates: ResolvedDeliveryTimeSchedule[] = [\n { schedule: options.orderStock?.schedule ?? null, source: 'order.stock.schedule' },\n { schedule: options.selectedStock?.schedule ?? null, source: 'profile.stock.schedule' },\n { schedule: options.orderCity?.schedule ?? null, source: 'order.city.schedule' },\n { schedule: options.selectedCity?.schedule ?? null, source: 'profile.city.schedule' },\n { schedule: storeCity?.schedule ?? null, source: 'store.cities[current].schedule' },\n { schedule: findStockSchedule(options.orderCity), source: 'order.city.stocks[].schedule' },\n {\n schedule: findStockSchedule(options.selectedCity),\n source: 'profile.city.stocks[].schedule',\n },\n {\n schedule: findStockSchedule(storeCity),\n source: 'store.cities[current].stocks[].schedule',\n },\n { schedule: fallbackStoreCity?.schedule ?? null, source: 'store.cities[].schedule' },\n {\n schedule: findStockSchedule(fallbackStoreCityWithStock),\n source: 'store.cities[].stocks[].schedule',\n },\n ]\n\n return (\n candidates.find((candidate) => hasAvailableScheduleWindow(candidate.schedule)) ?? {\n schedule: null,\n source: null,\n }\n )\n}\n\nexport function generateFreeTimeGroups(\n options: GenerateFreeTimeGroupsOptions\n): DeliveryIntervalsGroup[] {\n const safeOffset = Math.max(10, options.offsetMinutes)\n const step = options.stepMinutes >= 15 ? options.stepMinutes : 15\n const nowParts = nowInTimezone(options.storeTimezone, options.now)\n\n const baseYear = nowParts.year\n const baseMonth = nowParts.month - 1\n const baseDay = nowParts.day\n\n // Минимально допустимое время для слота «сегодня»: сейчас + offset, округлённое вверх.\n const earliestTodayMinutes = ceilToStep(nowParts.hour * 60 + nowParts.minute + safeOffset, step)\n\n const groups: DeliveryIntervalsGroup[] = []\n\n // Одна вкладка = один бизнес-день (открытие → закрытие). Окно может уходить за полночь —\n // тогда «ночной хвост» 00:00–02:00 остаётся в этой же вкладке, но дата у его слотов уже\n // следующая (см. FreeDeliveryInterval.date).\n for (let dayOffset = 0; dayOffset < DEFAULT_MAX_DAYS; dayOffset++) {\n const stamp = new Date(Date.UTC(baseYear, baseMonth, baseDay + dayOffset))\n const dayId = WEEKDAY_IDS[stamp.getUTCDay()] ?? 'monday'\n const window = getScheduleWindow(options.schedule, dayId, options.storeTimezone)\n\n if (!window) continue\n\n const intervals: FreeDeliveryInterval[] = []\n const seenTimes = new Set<string>()\n\n for (let minutes = window.startMinutes; minutes <= window.endMinutes; minutes += step) {\n const dayShift = Math.floor(minutes / FULL_DAY_MINUTES)\n const minuteOfDay = minutes - dayShift * FULL_DAY_MINUTES\n const time = `${pad(Math.floor(minuteOfDay / 60))}:${pad(minuteOfDay % 60)}`\n\n // Внутри одной вкладки HH:MM уникален: у круглосуточного окна закрытие 00:00\n // совпало бы с открытием — оставляем первое (своего дня), дубль-закрытие 00:00\n // не эмитим (он же — открытие следующей вкладки). Дедуп ДО today-фильтра, чтобы\n // закрывающий 00:00 отсеялся, даже когда открытие 00:00 отброшено как «в прошлом».\n if (seenTimes.has(time)) continue\n seenTimes.add(time)\n\n // Слот «сегодня» не раньше, чем сейчас + offset.\n if (dayOffset === 0 && minutes < earliestTodayMinutes) continue\n\n const slotStamp = new Date(\n Date.UTC(baseYear, baseMonth, baseDay + dayOffset + dayShift)\n )\n intervals.push({\n id: time,\n date: formatUtcYmd(slotStamp),\n price: null,\n name: time,\n description: null,\n })\n }\n\n if (!intervals.length) continue\n\n groups.push({\n day_id: dayId,\n date: formatUtcYmd(stamp),\n name: formatDayName(dayOffset, stamp),\n intervals,\n })\n }\n\n return groups\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pagination/usePaginatedList.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { computed, ref, type ComputedRef, type Ref } from 'vue'\n\nimport type { PaginatedResponse } from '@zenky/storefront-api'\n\n/**\n * Pure composable пагинации списка (товары, статьи блога и т.д.).\n *\n * Универсальный — фетчер и тип элемента передаются снаружи через дженерик `T` и\n * замыкание, поэтому можно использовать для категории, коллекции, блога и т.п.\n * Не зависит от Nuxt — можно вынести в библиотеку.\n *\n * Поддерживает два режима навигации:\n * - `loadPage(N)` — заменяет всё содержимое элементами N-й страницы (для прямого\n * ?page=N и для классической пагинации цифрами)\n * - `loadMore()` — добавляет следующую страницу к текущему списку (load-more / infinite)\n *\n * Хранит данные не как плоский массив, а как **чанки по страницам** (`pageChunks`).\n * Это позволяет UI рендерить каждую страницу отдельной секцией с `data-page` атрибутом,\n * что нужно для `content-visibility: auto` (оптимизация рендера скрытых секций).\n *\n * Параллельные вызовы игнорируются (одна загрузка за раз).\n */\n\nexport type PaginatedFetcher<T> = (page: number, count: number) => Promise<PaginatedResponse<T>>\n\nexport interface PageChunk<T> {\n page: number\n items: T[]\n}\n\nexport interface UsePaginatedListOptions<T> {\n /** Размер страницы. По умолчанию 20 — баланс для 2/4/5-колоночных сеток. */\n perPage?: number\n /** С какой страницы начинать. Используется для инициализации из ?page=N. */\n initialPage?: number\n /**\n * Готовые данные первой загрузки (для SSR-friendly инициализации без повторного\n * запроса на клиенте). Если переданы — composable не делает первичный fetch.\n */\n initialData?: PaginatedResponse<T> | null\n /** Функция загрузки страницы. Замыкание родителя — может включать фильтры, категорию и т.д. */\n fetcher: PaginatedFetcher<T>\n}\n\nexport interface UsePaginatedList<T> {\n /**\n * Чанки страниц — основной источник данных. Каждый элемент — это загруженная\n * страница со своими элементами. При `loadPage` — массив заменяется одним чанком.\n * При `loadMore` — новый чанк добавляется в конец.\n */\n pageChunks: Ref<PageChunk<T>[]>\n /** Плоский массив всех загруженных элементов (computed из pageChunks для удобства). */\n items: ComputedRef<T[]>\n /** Самая последняя загруженная страница (не обязательно «текущая» для пагинации). */\n currentPage: Ref<number>\n /** Диапазон страниц, чьи элементы сейчас в `pageChunks`. */\n pageWindow: Ref<{ from: number; to: number }>\n /** Всего страниц по данным API. */\n totalPages: Ref<number>\n /** Всего элементов по данным API. */\n totalItems: Ref<number>\n /** Есть ли следующая страница для load-more. */\n hasMore: Ref<boolean>\n /** Идёт ли запрос. */\n loading: Ref<boolean>\n /** Произошла ли ошибка при последнем запросе. */\n error: Ref<unknown | null>\n\n /** Заменить state элементами страницы N. Используется для прямого перехода и кликов на цифру. */\n loadPage(page: number): Promise<void>\n /** Догрузить следующую страницу (append). */\n loadMore(): Promise<void>\n /**\n * Применить готовые данные страницы без запроса (используется когда initialData\n * приходит асинхронно через `useAsyncData({ lazy: true })` на клиенте).\n */\n applyInitialData(response: PaginatedResponse<T>, page: number): void\n /**\n * Принудительно включить loading-флаг и очистить pageChunks. Используется\n * когда SSR вернул пустые initialData и мы ждём клиентскую загрузку.\n */\n startLoading(): void\n /**\n * Выключить loading-флаг без применения данных (при ошибке, чтобы скрыть скелетоны).\n */\n stopLoading(): void\n}\n\nconst DEFAULT_PER_PAGE = 20\n\nexport function usePaginatedList<T>(options: UsePaginatedListOptions<T>): UsePaginatedList<T> {\n const perPage = options.perPage ?? DEFAULT_PER_PAGE\n const fetcher = options.fetcher\n\n const pageChunks = ref<PageChunk<T>[]>([]) as Ref<PageChunk<T>[]>\n const currentPage = ref<number>(options.initialPage ?? 1)\n const pageWindow = ref<{ from: number; to: number }>({\n from: options.initialPage ?? 1,\n to: options.initialPage ?? 1,\n })\n const totalPages = ref<number>(1)\n const totalItems = ref<number>(0)\n const hasMore = ref<boolean>(false)\n const loading = ref<boolean>(false)\n const error = ref<unknown | null>(null)\n\n /** Плоский массив для обратной совместимости — просто flatMap по чанкам. */\n const items = computed<T[]>(() => pageChunks.value.flatMap(chunk => chunk.items))\n\n /**\n * Применить ответ API в replace-режиме (loadPage) — один чанк заменяет всё.\n */\n function applyReplaceResponse(response: PaginatedResponse<T>, page: number): void {\n pageChunks.value = [{ page, items: response.items }]\n currentPage.value = page\n pageWindow.value = { from: page, to: page }\n totalPages.value = response.pagination.total_pages\n totalItems.value = response.pagination.total\n hasMore.value = response.pagination.has_next_page\n }\n\n // Если переданы готовые данные первой страницы — заиспользуем без запроса\n if (options.initialData) {\n applyReplaceResponse(options.initialData, options.initialPage ?? 1)\n }\n\n async function loadPage(page: number): Promise<void> {\n if (loading.value) return\n\n loading.value = true\n error.value = null\n // Очищаем старые данные, чтобы UI показал skeleton вместо застывшей старой страницы.\n pageChunks.value = []\n\n try {\n const response = await fetcher(page, perPage)\n applyReplaceResponse(response, page)\n } catch (e) {\n error.value = e\n console.error('[paginated-list] loadPage failed', e)\n } finally {\n loading.value = false\n }\n }\n\n async function loadMore(): Promise<void> {\n if (loading.value || !hasMore.value) return\n\n const nextPage = currentPage.value + 1\n loading.value = true\n error.value = null\n\n try {\n const response = await fetcher(nextPage, perPage)\n\n // Append — добавляем новый чанк вместо мержа в плоский массив\n pageChunks.value = [...pageChunks.value, { page: nextPage, items: response.items }]\n currentPage.value = nextPage\n pageWindow.value = { from: pageWindow.value.from, to: nextPage }\n totalPages.value = response.pagination.total_pages\n totalItems.value = response.pagination.total\n hasMore.value = response.pagination.has_next_page\n } catch (e) {\n error.value = e\n console.error('[paginated-list] loadMore failed', e)\n } finally {\n loading.value = false\n }\n }\n\n function applyInitialData(response: PaginatedResponse<T>, page: number): void {\n applyReplaceResponse(response, page)\n loading.value = false\n }\n\n function startLoading(): void {\n loading.value = true\n pageChunks.value = []\n }\n\n function stopLoading(): void {\n loading.value = false\n }\n\n return {\n pageChunks,\n items,\n currentPage,\n pageWindow,\n totalPages,\n totalItems,\n hasMore,\n loading,\n error,\n loadPage,\n loadMore,\n applyInitialData,\n startLoading,\n stopLoading,\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/pagination/usePaginatedList.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { computed, ref, type ComputedRef, type Ref } from 'vue'\n\nimport type { PaginatedResponse } from '@zenky/storefront-api'\n\n/**\n * Pure composable пагинации списка (товары, статьи блога и т.д.).\n *\n * Универсальный — фетчер и тип элемента передаются снаружи через дженерик `T` и\n * замыкание, поэтому можно использовать для категории, коллекции, блога и т.п.\n * Не зависит от Nuxt — можно вынести в библиотеку.\n *\n * Поддерживает два режима навигации:\n * - `loadPage(N)` — заменяет всё содержимое элементами N-й страницы (для прямого\n * ?page=N и для классической пагинации цифрами)\n * - `loadMore()` — добавляет следующую страницу к текущему списку (load-more / infinite)\n *\n * Хранит данные не как плоский массив, а как **чанки по страницам** (`pageChunks`).\n * Это позволяет UI рендерить каждую страницу отдельной секцией с `data-page` атрибутом,\n * что нужно для `content-visibility: auto` (оптимизация рендера скрытых секций).\n *\n * Параллельные вызовы игнорируются (одна загрузка за раз).\n */\n\nexport type PaginatedFetcher<T> = (page: number, count: number) => Promise<PaginatedResponse<T>>\n\nexport interface PageChunk<T> {\n page: number\n items: T[]\n}\n\nexport interface UsePaginatedListOptions<T> {\n /** Размер страницы. По умолчанию 20 — баланс для 2/4/5-колоночных сеток. */\n perPage?: number\n /** С какой страницы начинать. Используется для инициализации из ?page=N. */\n initialPage?: number\n /**\n * Готовые данные первой загрузки (для SSR-friendly инициализации без повторного\n * запроса на клиенте). Если переданы — composable не делает первичный fetch.\n */\n initialData?: PaginatedResponse<T> | null\n /** Функция загрузки страницы. Замыкание родителя — может включать фильтры, категорию и т.д. */\n fetcher: PaginatedFetcher<T>\n}\n\nexport interface UsePaginatedList<T> {\n /**\n * Чанки страниц — основной источник данных. Каждый элемент — это загруженная\n * страница со своими элементами. При `loadPage` — массив заменяется одним чанком.\n * При `loadMore` — новый чанк добавляется в конец.\n */\n pageChunks: Ref<PageChunk<T>[]>\n /** Плоский массив всех загруженных элементов (computed из pageChunks для удобства). */\n items: ComputedRef<T[]>\n /** Самая последняя загруженная страница (не обязательно «текущая» для пагинации). */\n currentPage: Ref<number>\n /** Диапазон страниц, чьи элементы сейчас в `pageChunks`. */\n pageWindow: Ref<{ from: number; to: number }>\n /** Всего страниц по данным API. */\n totalPages: Ref<number>\n /** Всего элементов по данным API. */\n totalItems: Ref<number>\n /** Есть ли следующая страница для load-more. */\n hasMore: Ref<boolean>\n /** Идёт ли запрос. */\n loading: Ref<boolean>\n /** Произошла ли ошибка при последнем запросе. */\n error: Ref<unknown | null>\n\n /** Заменить state элементами страницы N. Используется для прямого перехода и кликов на цифру. */\n loadPage(page: number): Promise<void>\n /** Догрузить следующую страницу (append). */\n loadMore(): Promise<void>\n /**\n * Применить готовые данные страницы без запроса (используется когда initialData\n * приходит асинхронно через `useAsyncData({ lazy: true })` на клиенте).\n */\n applyInitialData(response: PaginatedResponse<T>, page: number): void\n /**\n * Принудительно включить loading-флаг и очистить pageChunks. Используется\n * когда SSR вернул пустые initialData и мы ждём клиентскую загрузку.\n */\n startLoading(): void\n /**\n * Выключить loading-флаг без применения данных (при ошибке, чтобы скрыть скелетоны).\n */\n stopLoading(): void\n}\n\nconst DEFAULT_PER_PAGE = 20\n\nexport function usePaginatedList<T>(options: UsePaginatedListOptions<T>): UsePaginatedList<T> {\n const perPage = options.perPage ?? DEFAULT_PER_PAGE\n const fetcher = options.fetcher\n\n const pageChunks = ref<PageChunk<T>[]>([]) as Ref<PageChunk<T>[]>\n const currentPage = ref<number>(options.initialPage ?? 1)\n const pageWindow = ref<{ from: number; to: number }>({\n from: options.initialPage ?? 1,\n to: options.initialPage ?? 1,\n })\n const totalPages = ref<number>(1)\n const totalItems = ref<number>(0)\n const hasMore = ref<boolean>(false)\n const loading = ref<boolean>(false)\n const error = ref<unknown | null>(null)\n\n /** Плоский массив для обратной совместимости — просто flatMap по чанкам. */\n const items = computed<T[]>(() => pageChunks.value.flatMap((chunk) => chunk.items))\n\n /**\n * Применить ответ API в replace-режиме (loadPage) — один чанк заменяет всё.\n */\n function applyReplaceResponse(response: PaginatedResponse<T>, page: number): void {\n pageChunks.value = [{ page, items: response.items }]\n currentPage.value = page\n pageWindow.value = { from: page, to: page }\n totalPages.value = response.pagination.total_pages\n totalItems.value = response.pagination.total\n hasMore.value = response.pagination.has_next_page\n }\n\n // Если переданы готовые данные первой страницы — заиспользуем без запроса\n if (options.initialData) {\n applyReplaceResponse(options.initialData, options.initialPage ?? 1)\n }\n\n async function loadPage(page: number): Promise<void> {\n if (loading.value) return\n\n loading.value = true\n error.value = null\n // Очищаем старые данные, чтобы UI показал skeleton вместо застывшей старой страницы.\n pageChunks.value = []\n\n try {\n const response = await fetcher(page, perPage)\n applyReplaceResponse(response, page)\n } catch (e) {\n error.value = e\n console.error('[paginated-list] loadPage failed', e)\n } finally {\n loading.value = false\n }\n }\n\n async function loadMore(): Promise<void> {\n if (loading.value || !hasMore.value) return\n\n const nextPage = currentPage.value + 1\n loading.value = true\n error.value = null\n\n try {\n const response = await fetcher(nextPage, perPage)\n\n // Append — добавляем новый чанк вместо мержа в плоский массив\n pageChunks.value = [...pageChunks.value, { page: nextPage, items: response.items }]\n currentPage.value = nextPage\n pageWindow.value = { from: pageWindow.value.from, to: nextPage }\n totalPages.value = response.pagination.total_pages\n totalItems.value = response.pagination.total\n hasMore.value = response.pagination.has_next_page\n } catch (e) {\n error.value = e\n console.error('[paginated-list] loadMore failed', e)\n } finally {\n loading.value = false\n }\n }\n\n function applyInitialData(response: PaginatedResponse<T>, page: number): void {\n applyReplaceResponse(response, page)\n loading.value = false\n }\n\n function startLoading(): void {\n loading.value = true\n pageChunks.value = []\n }\n\n function stopLoading(): void {\n loading.value = false\n }\n\n return {\n pageChunks,\n items,\n currentPage,\n pageWindow,\n totalPages,\n totalItems,\n hasMore,\n loading,\n error,\n loadPage,\n loadMore,\n applyInitialData,\n startLoading,\n stopLoading,\n }\n}\n"]}
@@ -1,3 +1,3 @@
1
- export { NOOP_ACTIVITY_REPORTER, NOOP_NOTIFIER, NOOP_RUM, createMemoryStorage, err, ok } from '../chunk-AL5W5JSD.js';
1
+ export { NOOP_ACTIVITY_REPORTER, NOOP_NOTIFIER, NOOP_RUM, createMemoryStorage, err, ok } from '../chunk-GLTOJTZO.js';
2
2
  //# sourceMappingURL=index.js.map
3
3
  //# sourceMappingURL=index.js.map
@@ -1,6 +1,6 @@
1
- export { canAddDirectly, findProductInCatalog, formatWeight, getProductWeight, getVariantWeight, resolveVariantPrice, toGrams, usePriceRecalculation, useProductPricing, useProductVariant, useProductVariantModifiers, useProductVariantSelector, useProductsByCategory, useRelatedProducts } from '../chunk-IGDDWGS4.js';
1
+ export { canAddDirectly, findProductInCatalog, formatWeight, getProductWeight, getVariantWeight, resolveVariantPrice, toGrams, usePriceRecalculation, useProductPricing, useProductVariant, useProductVariantModifiers, useProductVariantSelector, useProductsByCategory, useRelatedProducts } from '../chunk-JLFBAAFT.js';
2
2
  import '../chunk-5QCUH3FF.js';
3
- import '../chunk-PWHIDGKW.js';
3
+ import '../chunk-TEG7EL4N.js';
4
4
  import '../chunk-XUGXLJZB.js';
5
5
  import '../chunk-V3VVDQIR.js';
6
6
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,7 @@
1
- export { usePromotionsChecker } from '../chunk-4MJKN7PL.js';
1
+ export { usePromotionsChecker } from '../chunk-N3NOTJUC.js';
2
2
  import '../chunk-BWUKMWHL.js';
3
- import '../chunk-PWHIDGKW.js';
4
- import '../chunk-VL3T36VM.js';
3
+ import '../chunk-TEG7EL4N.js';
4
+ import '../chunk-FFBZKQRV.js';
5
5
  import '../chunk-V3VVDQIR.js';
6
6
  //# sourceMappingURL=index.js.map
7
7
  //# sourceMappingURL=index.js.map
package/dist/seo/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { resolveVariantPrice } from '../chunk-IGDDWGS4.js';
1
+ import { resolveVariantPrice } from '../chunk-JLFBAAFT.js';
2
2
  import '../chunk-5QCUH3FF.js';
3
- import '../chunk-PWHIDGKW.js';
4
- import { getSocialLinks } from '../chunk-PJ2LEKW6.js';
3
+ import '../chunk-TEG7EL4N.js';
4
+ import { getSocialLinks } from '../chunk-MZ66ARDL.js';
5
5
  import '../chunk-XUGXLJZB.js';
6
6
  import '../chunk-V3VVDQIR.js';
7
7
  import { minorToMajorString } from '../chunk-RO25KNM5.js';
@@ -35,7 +35,7 @@ function prepareOgDescription(text, maxLength = 160) {
35
35
  if (!text) return null;
36
36
  const plain = stripHtml(text);
37
37
  if (!plain) return null;
38
- return plain.length > maxLength ? plain.slice(0, maxLength - 3) + "..." : plain;
38
+ return plain.length > maxLength ? `${plain.slice(0, maxLength - 3)}...` : plain;
39
39
  }
40
40
 
41
41
  // src/seo/siteOrigin.ts
@@ -53,7 +53,7 @@ function isProductInStock(variants) {
53
53
  if (!variants.length) return false;
54
54
  return variants.some((variant) => {
55
55
  const remainders = variant.remainders;
56
- if (!remainders || !remainders.length) return true;
56
+ if (!remainders?.length) return true;
57
57
  return remainders.some((remainder) => remainder.quantity > 0);
58
58
  });
59
59
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/seo/headPriority.ts","../../src/seo/utils.ts","../../src/seo/siteOrigin.ts","../../src/seo/productSchema.ts","../../src/seo/categorySchema.ts","../../src/seo/articleSchema.ts","../../src/seo/organizationSchema.ts","../../src/seo/ogTags.ts","../../src/seo/entitySeo.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,IAAM,qBAAA,GAAwB,UAAA;AAIvB,SAAS,oBACZ,GAAA,EACiD;AACjD,EAAA,OAAO,EAAE,GAAG,GAAA,EAAK,WAAA,EAAa,qBAAA,EAAsB;AACxD;;;ACHO,SAAS,gBAAgB,KAAA,EAA6B;AACzD,EAAA,OACI,KAAA,CAAM,EAAA,EAAI,GAAA,IACV,KAAA,CAAM,QAAQ,GAAA,IACd,KAAA,CAAM,KAAA,EAAO,GAAA,IACb,KAAA,CAAM,MAAA,EAAQ,GAAA,IACd,KAAA,CAAM,aAAa,GAAA,IACnB,IAAA;AAER;AAMO,SAAS,oBAAoB,KAAA,EAAgD;AAChF,EAAA,IAAI,CAAC,OAAO,OAAO,IAAA;AACnB,EAAA,IAAI,MAAM,WAAA,EAAa;AACnB,IAAA,MAAM,GAAA,GAAM,eAAA,CAAgB,KAAA,CAAM,WAAW,CAAA;AAC7C,IAAA,IAAI,KAAK,OAAO,GAAA;AAAA,EACpB;AACA,EAAA,IAAI,MAAM,IAAA,EAAM;AACZ,IAAA,OAAO,eAAA,CAAgB,MAAM,IAAI,CAAA;AAAA,EACrC;AACA,EAAA,OAAO,IAAA;AACX;AAKO,SAAS,UAAU,IAAA,EAAsB;AAC5C,EAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,UAAA,EAAY,EAAE,EAAE,IAAA,EAAK;AAC7C;AAMO,SAAS,oBAAA,CACZ,IAAA,EACA,SAAA,GAAY,GAAA,EACC;AACb,EAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,EAAA,MAAM,KAAA,GAAQ,UAAU,IAAI,CAAA;AAC5B,EAAA,IAAI,CAAC,OAAO,OAAO,IAAA;AACnB,EAAA,OAAO,KAAA,CAAM,SAAS,SAAA,GAAY,KAAA,CAAM,MAAM,CAAA,EAAG,SAAA,GAAY,CAAC,CAAA,GAAI,KAAA,GAAQ,KAAA;AAC9E;;;AC/CO,SAAS,iBAAA,CACZ,aACA,cAAA,EACM;AACN,EAAA,MAAM,MAAA,GAAS,aAAa,IAAA,EAAK;AACjC,EAAA,IAAI,MAAA,EAAQ;AACR,IAAA,MAAM,IAAA,GAAO,OAAO,OAAA,CAAQ,cAAA,EAAgB,EAAE,CAAA,CAAE,OAAA,CAAQ,QAAQ,EAAE,CAAA;AAClE,IAAA,OAAO,WAAW,IAAI,CAAA,CAAA;AAAA,EAC1B;AACA,EAAA,OAAO,cAAA;AACX;;;ACRA,SAAS,iBAAiB,QAAA,EAAqC;AAC3D,EAAA,IAAI,CAAC,QAAA,CAAS,MAAA,EAAQ,OAAO,KAAA;AAC7B,EAAA,OAAO,QAAA,CAAS,IAAA,CAAK,CAAC,OAAA,KAAY;AAC9B,IAAA,MAAM,aAAa,OAAA,CAAQ,UAAA;AAC3B,IAAA,IAAI,CAAC,UAAA,IAAc,CAAC,UAAA,CAAW,QAAQ,OAAO,IAAA;AAC9C,IAAA,OAAO,WAAW,IAAA,CAAK,CAAC,SAAA,KAAc,SAAA,CAAU,WAAW,CAAC,CAAA;AAAA,EAChE,CAAC,CAAA;AACL;AAOO,SAAS,kBAAA,CAAmB,SAAkB,QAAA,EAA6C;AAC9F,EAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,QAAA,IAAY,EAAC;AAEtC,EAAA,MAAM,YAAA,GAAe,SAAS,CAAC,CAAA;AAC/B,EAAA,IAAI,UAAA;AAEJ,EAAA,IAAI,YAAA,EAAc;AACd,IAAA,MAAM,QAAA,GAAW,oBAAoB,YAAY,CAAA;AAEjD,IAAA,UAAA,GAAa;AAAA,MACT,OAAA,EAAS,OAAA;AAAA,MACT,KAAA,EAAO,kBAAA,CAAmB,QAAA,CAAS,KAAA,EAAO,QAAQ,CAAA;AAAA,MAClD,aAAA,EAAe,QAAA;AAAA,MACf,YAAA,EAAc,gBAAA,CAAiB,QAAQ,CAAA,GACjC,4BAAA,GACA,+BAAA;AAAA,MACN,GAAI,aAAa,GAAA,GAAM,EAAE,KAAK,YAAA,CAAa,GAAA,KAAQ;AAAC,KACxD;AAAA,EACJ;AAEA,EAAA,MAAM,MAAA,GAAA,CAAU,OAAA,CAAQ,MAAA,IAAU,EAAC,EAC9B,GAAA,CAAI,eAAe,CAAA,CACnB,MAAA,CAAO,CAAC,GAAA,KAAuB,CAAC,CAAC,GAAG,CAAA;AAEzC,EAAA,MAAM,MAAA,GAAkC;AAAA,IACpC,UAAA,EAAY,oBAAA;AAAA,IACZ,OAAA,EAAS,SAAA;AAAA,IACT,MAAM,OAAA,CAAQ;AAAA,GAClB;AAEA,EAAA,IAAI,OAAA,CAAQ,WAAA,EAAa,MAAA,CAAO,WAAA,GAAc,OAAA,CAAQ,WAAA;AACtD,EAAA,IAAI,MAAA,CAAO,MAAA,EAAQ,MAAA,CAAO,KAAA,GAAQ,MAAA;AAClC,EAAA,IAAI,UAAA,SAAmB,MAAA,GAAS,UAAA;AAEhC,EAAA,OAAO,CAAC,MAAM,CAAA;AAClB;;;AC7CO,SAAS,oBAAA,CACZ,QAAA,EACA,QAAA,EACA,WAAA,EACA,MAAA,EACyB;AACzB,EAAA,MAAM,UAAqC,EAAC;AAE5C,EAAA,IAAI,YAAY,MAAA,EAAQ;AACpB,IAAA,OAAA,CAAQ,IAAA,CAAK;AAAA,MACT,UAAA,EAAY,oBAAA;AAAA,MACZ,OAAA,EAAS,gBAAA;AAAA,MACT,eAAA,EAAiB,WAAA,CAAY,GAAA,CAAI,CAAC,OAAO,KAAA,MAAW;AAAA,QAChD,OAAA,EAAS,UAAA;AAAA,QACT,UAAU,KAAA,GAAQ,CAAA;AAAA,QAClB,MAAM,KAAA,CAAM,KAAA;AAAA,QACZ,GAAI,KAAA,CAAM,EAAA,GAAK,EAAE,IAAA,EAAM,CAAA,EAAG,MAAM,CAAA,EAAG,KAAA,CAAM,EAAE,CAAA,CAAA,EAAG,GAAI;AAAC,OACvD,CAAE;AAAA,KACL,CAAA;AAAA,EACL;AAEA,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA;AAClC,EAAA,IAAI,MAAM,MAAA,EAAQ;AACd,IAAA,OAAA,CAAQ,IAAA,CAAK;AAAA,MACT,UAAA,EAAY,oBAAA;AAAA,MACZ,OAAA,EAAS,UAAA;AAAA,MACT,MAAM,QAAA,CAAS,IAAA;AAAA,MACf,eAAe,KAAA,CAAM,MAAA;AAAA,MACrB,eAAA,EAAiB,KAAA,CAAM,GAAA,CAAI,CAAC,SAAS,KAAA,KAAU;AAC3C,QAAA,MAAM,GAAA,GAAM,CAAA,EAAG,MAAM,CAAA,UAAA,EAAa,OAAA,CAAQ,QAAQ,CAAA,CAAA,EAAI,OAAA,CAAQ,IAAA,IAAQ,OAAA,CAAQ,QAAQ,CAAA,CAAA;AACtF,QAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,MAAA,GAAS,CAAC,CAAA;AACrC,QAAA,MAAM,KAAA,GAAQ,UAAA,GAAa,eAAA,CAAgB,UAAU,CAAA,GAAI,IAAA;AAEzD,QAAA,MAAM,IAAA,GAAgC;AAAA,UAClC,OAAA,EAAS,UAAA;AAAA,UACT,UAAU,KAAA,GAAQ,CAAA;AAAA,UAClB,GAAA;AAAA,UACA,MAAM,OAAA,CAAQ;AAAA,SAClB;AAEA,QAAA,IAAI,KAAA,OAAY,KAAA,GAAQ,KAAA;AAExB,QAAA,OAAO,IAAA;AAAA,MACX,CAAC;AAAA,KACJ,CAAA;AAAA,EACL;AAEA,EAAA,OAAO,OAAA;AACX;;;ACtDA,SAAS,aAAA,CACL,MACA,IAAA,EAOuB;AACvB,EAAA,MAAM,MAAA,GAAkC;AAAA,IACpC,UAAA,EAAY,oBAAA;AAAA,IACZ,OAAA,EAAS,IAAA;AAAA,IACT,QAAA,EAAU,KAAK,KAAA,IAAS,EAAA;AAAA,IACxB,MAAA,EAAQ;AAAA,MACJ,OAAA,EAAS,cAAA;AAAA,MACT,MAAM,IAAA,CAAK,SAAA;AAAA,MACX,KAAK,IAAA,CAAK;AAAA;AACd,GACJ;AAEA,EAAA,MAAM,WAAA,GAAc,oBAAA,CAAqB,IAAA,CAAK,KAAK,CAAA;AACnD,EAAA,IAAI,WAAA,SAAoB,WAAA,GAAc,WAAA;AAEtC,EAAA,MAAM,QAAQ,IAAA,CAAK,KAAA,GAAQ,eAAA,CAAgB,IAAA,CAAK,KAAK,CAAA,GAAI,IAAA;AACzD,EAAA,IAAI,KAAA,SAAc,KAAA,GAAQ,KAAA;AAE1B,EAAA,OAAO,MAAA;AACX;AAMO,SAAS,uBAAA,CACZ,OAAA,EACA,SAAA,EACA,MAAA,EACyB;AACzB,EAAA,IAAI,CAAC,OAAA,CAAQ,KAAA,EAAO,OAAO,EAAC;AAE5B,EAAA,OAAO;AAAA,IACH,cAAc,SAAA,EAAW;AAAA,MACrB,OAAO,OAAA,CAAQ,KAAA;AAAA,MACf,OAAO,OAAA,CAAQ,KAAA;AAAA,MACf,OAAO,OAAA,CAAQ,KAAA;AAAA,MACf,SAAA;AAAA,MACA;AAAA,KACH;AAAA,GACL;AACJ;AAMO,SAAS,mBAAA,CACZ,OAAA,EACA,SAAA,EACA,MAAA,EACyB;AACzB,EAAA,MAAM,MAAA,GAAS,cAAc,aAAA,EAAe;AAAA,IACxC,OAAO,OAAA,CAAQ,KAAA;AAAA,IACf,OAAO,OAAA,CAAQ,KAAA;AAAA,IACf,OAAO,OAAA,CAAQ,KAAA;AAAA,IACf,SAAA;AAAA,IACA;AAAA,GACH,CAAA;AAED,EAAA,IAAI,QAAQ,UAAA,EAAY;AACpB,IAAA,MAAA,CAAO,eAAe,OAAA,CAAQ,UAAA;AAE9B,IAAA,MAAA,CAAO,gBAAgB,OAAA,CAAQ,UAAA;AAAA,EACnC;AAEA,EAAA,OAAO,CAAC,MAAM,CAAA;AAClB;ACzEA,IAAM,UAAA,GAAqC;AAAA,EACvC,MAAA,EAAQ,QAAA;AAAA,EACR,OAAA,EAAS,SAAA;AAAA,EACT,SAAA,EAAW,WAAA;AAAA,EACX,QAAA,EAAU,UAAA;AAAA,EACV,MAAA,EAAQ,QAAA;AAAA,EACR,QAAA,EAAU,UAAA;AAAA,EACV,MAAA,EAAQ;AACZ,CAAA;AAMA,SAAS,kBAAkB,QAAA,EAAkE;AACzF,EAAA,IAAI,CAAC,QAAA,EAAU,OAAO,EAAC;AAEvB,EAAA,MAAM,SAAoC,EAAC;AAC3C,EAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,UAAU,CAAA,EAAG;AACvC,IAAA,MAAM,GAAA,GAAM,QAAA,CAAS,IAAA,CAAK,GAAiC,CAAA;AAC3D,IAAA,MAAM,KAAA,GAAQ,GAAA,EAAK,KAAA,EAAO,KAAA,CAAM,mCAAmC,CAAA;AACnE,IAAA,IAAI,CAAC,KAAA,EAAO;AAEZ,IAAA,MAAA,CAAO,IAAA,CAAK;AAAA,MACR,OAAA,EAAS,2BAAA;AAAA,MACT,SAAA,EAAW,WAAW,GAAG,CAAA;AAAA,MACzB,KAAA,EAAO,MAAM,CAAC,CAAA;AAAA,MACd,MAAA,EAAQ,MAAM,CAAC;AAAA,KAClB,CAAA;AAAA,EACL;AAEA,EAAA,OAAO,MAAA;AACX;AAOO,SAAS,uBAAA,CAAwB,OAAc,MAAA,EAA2C;AAC7F,EAAA,MAAM,UACF,KAAA,CAAM,WAAA,EAAa,MAAA,EAAQ,GAAA,IAC3B,MAAM,WAAA,EAAa,KAAA,EAAO,GAAA,IAC1B,KAAA,CAAM,MAAM,MAAA,EAAQ,GAAA,IACpB,KAAA,CAAM,IAAA,EAAM,OAAO,GAAA,IACnB,IAAA;AAEJ,EAAA,MAAM,UAAU,KAAA,CAAM,MAAA,IAAU,EAAC,EAAG,IAAI,CAAC,CAAA,KAAM,CAAA,CAAE,MAAM,EAAE,MAAA,CAAO,CAAC,CAAA,KAAmB,CAAC,CAAC,CAAC,CAAA;AACvF,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,EAAO,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,WAAA,CAAY,KAAK,CAAA,EAAG,KAAA,IAAS,IAAA;AAC/E,EAAA,MAAM,WAAA,GAAc,cAAA,CAAe,KAAA,CAAM,KAAA,IAAS,EAAE,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,GAAG,CAAA;AAEtE,EAAA,MAAM,IAAA,GAAA,CAAQ,KAAA,CAAM,MAAA,IAAU,IAAI,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,UAAU,CAAA,IAAK,KAAA,CAAM,MAAA,GAAS,CAAC,CAAA,IAAK,IAAA;AACpF,EAAA,MAAM,KAAA,GAAQ,IAAA,EAAM,MAAA,EAAQ,IAAA,CAAK,CAAC,EAAA,KAAO,EAAA,CAAG,OAAO,CAAA,IAAK,IAAA,EAAM,MAAA,GAAS,CAAC,CAAA,IAAK,IAAA;AAE7E,EAAA,MAAM,aAAA,GAAgB,OAAO,OAAA,IAAW,IAAA;AACxC,EAAA,MAAM,GAAA,GAAM,KAAA,EAAO,WAAA,IAAe,IAAA,EAAM,WAAA,IAAe,IAAA;AACvD,EAAA,MAAM,YAAA,GAAe,iBAAA,CAAkB,KAAA,EAAO,QAAA,IAAY,MAAM,QAAQ,CAAA;AAExE,EAAA,MAAM,eAAA,GAAkB,OAAA,CAAQ,aAAA,IAAiB,GAAA,IAAO,aAAa,MAAM,CAAA;AAE3E,EAAA,MAAM,MAAA,GAAkC;AAAA,IACpC,UAAA,EAAY,oBAAA;AAAA,IACZ,OAAA,EAAS,kBAAkB,eAAA,GAAkB,cAAA;AAAA,IAC7C,MAAM,KAAA,CAAM,IAAA;AAAA,IACZ,GAAA,EAAK;AAAA,GACT;AAEA,EAAA,IAAI,OAAA,EAAS;AACT,IAAA,MAAA,CAAO,IAAA,GAAO,OAAA;AACd,IAAA,MAAA,CAAO,KAAA,GAAQ,OAAA;AAAA,EACnB;AAEA,EAAA,IAAI,MAAA,CAAO,QAAQ,MAAA,CAAO,SAAA,GAAY,OAAO,MAAA,KAAW,CAAA,GAAI,MAAA,CAAO,CAAC,CAAA,GAAI,MAAA;AACxE,EAAA,IAAI,KAAA,SAAc,KAAA,GAAQ,KAAA;AAC1B,EAAA,IAAI,WAAA,CAAY,MAAA,EAAQ,MAAA,CAAO,MAAA,GAAS,WAAA;AAExC,EAAA,IAAI,aAAA,IAAiB,MAAM,IAAA,EAAM;AAC7B,IAAA,MAAA,CAAO,OAAA,GAAU;AAAA,MACb,OAAA,EAAS,eAAA;AAAA,MACT,GAAI,aAAA,GAAgB,EAAE,aAAA,KAAkB,EAAC;AAAA,MACzC,GAAI,MAAM,IAAA,GAAO,EAAE,iBAAiB,IAAA,CAAK,IAAA,KAAS,EAAC;AAAA,MACnD,cAAA,EAAgB;AAAA,KACpB;AAAA,EACJ;AAEA,EAAA,IAAI,GAAA,EAAK;AACL,IAAA,MAAA,CAAO,GAAA,GAAM;AAAA,MACT,OAAA,EAAS,gBAAA;AAAA,MACT,UAAU,GAAA,CAAI,QAAA;AAAA,MACd,WAAW,GAAA,CAAI;AAAA,KACnB;AAAA,EACJ;AAEA,EAAA,IAAI,YAAA,CAAa,MAAA,EAAQ,MAAA,CAAO,yBAAA,GAA4B,YAAA;AAE5D,EAAA,OAAO,CAAC,MAAM,CAAA;AAClB;;;AC3FO,SAAS,YAAY,KAAA,EAA8C;AACtE,EAAA,MAAM,OAAiC,EAAC;AAExC,EAAA,IAAI,MAAM,KAAA,EAAO;AACb,IAAA,IAAA,CAAK,IAAA,CAAK,oBAAoB,EAAE,QAAA,EAAU,YAAY,OAAA,EAAS,KAAA,CAAM,KAAA,EAAO,CAAC,CAAA;AAC7E,IAAA,IAAA,CAAK,IAAA,CAAK,oBAAoB,EAAE,IAAA,EAAM,iBAAiB,OAAA,EAAS,KAAA,CAAM,KAAA,EAAO,CAAC,CAAA;AAAA,EAClF;AAEA,EAAA,IAAI,MAAM,WAAA,EAAa;AACnB,IAAA,IAAA,CAAK,IAAA,CAAK,oBAAoB,EAAE,QAAA,EAAU,kBAAkB,OAAA,EAAS,KAAA,CAAM,WAAA,EAAa,CAAC,CAAA;AACzF,IAAA,IAAA,CAAK,IAAA,CAAK,oBAAoB,EAAE,IAAA,EAAM,uBAAuB,OAAA,EAAS,KAAA,CAAM,WAAA,EAAa,CAAC,CAAA;AAAA,EAC9F;AAEA,EAAA,IAAI,MAAM,KAAA,EAAO;AACb,IAAA,IAAA,CAAK,IAAA,CAAK,oBAAoB,EAAE,QAAA,EAAU,YAAY,OAAA,EAAS,KAAA,CAAM,KAAA,EAAO,CAAC,CAAA;AAC7E,IAAA,IAAA,CAAK,IAAA,CAAK,oBAAoB,EAAE,IAAA,EAAM,iBAAiB,OAAA,EAAS,KAAA,CAAM,KAAA,EAAO,CAAC,CAAA;AAAA,EAClF;AAEA,EAAA,IAAA,CAAK,IAAA,CAAK,mBAAA,CAAoB,EAAE,QAAA,EAAU,SAAA,EAAW,SAAS,KAAA,CAAM,IAAA,IAAQ,SAAA,EAAW,CAAC,CAAA;AAExF,EAAA,IAAI,MAAM,GAAA,EAAK;AACX,IAAA,IAAA,CAAK,IAAA,CAAK,oBAAoB,EAAE,QAAA,EAAU,UAAU,OAAA,EAAS,KAAA,CAAM,GAAA,EAAK,CAAC,CAAA;AAAA,EAC7E;AAEA,EAAA,OAAO,IAAA;AACX;;;AChCO,SAAS,eAAe,IAAA,EAA8C;AACzE,EAAA,OAAO,IAAA,CAAK,GAAA,CAAI,CAAC,GAAA,KAAQ;AACrB,IAAA,MAAM,KAAA,GAAgC;AAAA,MAClC,CAAC,GAAA,CAAI,IAAA,CAAK,SAAS,GAAG,IAAI,IAAA,CAAK,KAAA;AAAA,MAC/B,CAAC,GAAA,CAAI,KAAA,CAAM,SAAS,GAAG,IAAI,KAAA,CAAM;AAAA,KACrC;AAEA,IAAA,KAAA,MAAW,UAAA,IAAc,IAAI,UAAA,EAAY;AACrC,MAAA,KAAA,CAAM,UAAA,CAAW,SAAS,CAAA,GAAI,UAAA,CAAW,KAAA;AAAA,IAC7C;AAEA,IAAA,IACI,KAAA,CAAM,IAAA,KAAS,aAAA,IACf,KAAA,CAAM,IAAA,EAAM,UAAA,CAAW,UAAU,CAAA,IACjC,KAAA,CAAM,QAAA,EAAU,UAAA,CAAW,KAAK,CAAA,EAClC;AACE,MAAA,OAAO,oBAAoB,KAAK,CAAA;AAAA,IACpC;AAEA,IAAA,OAAO,KAAA;AAAA,EACX,CAAC,CAAA;AACL;AAGO,SAAS,mBAAmB,GAAA,EAAuD;AACtF,EAAA,MAAM,OAAiC,EAAC;AAExC,EAAA,IAAI,KAAK,WAAA,EAAa;AAClB,IAAA,IAAA,CAAK,IAAA,CAAK,oBAAoB,EAAE,IAAA,EAAM,eAAe,OAAA,EAAS,GAAA,CAAI,WAAA,EAAa,CAAC,CAAA;AAAA,EACpF;AAEA,EAAA,IAAI,GAAA,EAAK,MAAM,MAAA,EAAQ;AACnB,IAAA,IAAA,CAAK,IAAA,CAAK,GAAG,cAAA,CAAe,GAAA,CAAI,IAAI,CAAC,CAAA;AAAA,EACzC;AAEA,EAAA,OAAO,IAAA;AACX","file":"index.js","sourcesContent":["const SEO_META_TAG_PRIORITY = 'critical'\n\ntype SeoMetaTag = Record<string, string>\n\nexport function withSeoMetaPriority<T extends SeoMetaTag>(\n tag: T\n): T & { tagPriority: typeof SEO_META_TAG_PRIORITY } {\n return { ...tag, tagPriority: SEO_META_TAG_PRIORITY }\n}\n","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 * Каноничный публичный origin сайта из `store.domain`. Fallback (хост запроса)\n * приносит потребитель — ядро не знает про 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 || !remainders.length) return true\n return remainders.some((remainder) => remainder.quantity > 0)\n })\n}\n\n/**\n * JSON-LD schema.org/Product. Google → Rich Results (цена/наличие/рейтинг в выдаче).\n * Возвращает массив (0 или 1 объект) — потребитель оборачивает в `<script ld+json>`.\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 ?? [])\n .map(resolveMediaUrl)\n .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 } from '@zenky/storefront-api'\n\nimport { resolveMediaUrl } from './utils'\nimport type { BreadcrumbItem } from './types'\n\n/**\n * JSON-LD для страницы категории: BreadcrumbList (хлебные крошки) + ItemList\n * (первые 10 товаров). Возвращает массив JSON-LD объектов (0–2).\n * Spec: https://schema.org/ItemList · https://schema.org/BreadcrumbList\n */\nexport function buildCategorySchemas(\n category: NestedCategory,\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: category.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","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 для страницы акции (редакционный контент).\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 для статьи блога (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 // Нет отдельного datePublished — используем 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 → 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 (по дням `hours` — «10:00–22:00») → массив schema.org/OpeningHoursSpecification.\n * Дни без распознаваемого интервала (выходной / круглосуточно / пусто) пропускаем.\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 (или LocalBusiness при наличии физической точки)\n * для главной. Google → Knowledge Panel. Все данные из уже загруженного профиля.\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","import { withSeoMetaPriority } from './headPriority'\n\n/** Уже разрешённые (с учётом дефолтов потребителя) значения OG/Twitter-тегов. */\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/** OG + Twitter meta-теги (critical-приоритет). Telegram читает twitter:image. */\nexport function buildOgTags(input: OgTagsInput): Record<string, string>[] {\n const tags: Record<string, string>[] = []\n\n if (input.title) {\n tags.push(withSeoMetaPriority({ property: 'og:title', content: input.title }))\n tags.push(withSeoMetaPriority({ name: 'twitter:title', content: input.title }))\n }\n\n if (input.description) {\n tags.push(withSeoMetaPriority({ property: 'og:description', content: input.description }))\n tags.push(withSeoMetaPriority({ name: 'twitter:description', content: input.description }))\n }\n\n if (input.image) {\n tags.push(withSeoMetaPriority({ property: 'og:image', content: input.image }))\n tags.push(withSeoMetaPriority({ name: 'twitter:image', content: input.image }))\n }\n\n tags.push(withSeoMetaPriority({ property: 'og:type', content: input.type ?? 'website' }))\n\n if (input.url) {\n tags.push(withSeoMetaPriority({ property: 'og:url', content: input.url }))\n }\n\n return tags\n}\n","import type { Seo, SeoMetaTag } from '@zenky/storefront-api'\n\nimport { withSeoMetaPriority } from './headPriority'\n\n/** SDK-теги `Seo.meta` → head-meta records (с critical-приоритетом для og/twitter/description). */\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 if (\n attrs.name === 'description' ||\n attrs.name?.startsWith('twitter:') ||\n attrs.property?.startsWith('og:')\n ) {\n return withSeoMetaPriority(attrs)\n }\n\n return attrs\n })\n}\n\n/** meta-теги из `Seo`-сущности (description + маппинг `Seo.meta`). 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(withSeoMetaPriority({ 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/headPriority.ts","../../src/seo/utils.ts","../../src/seo/siteOrigin.ts","../../src/seo/productSchema.ts","../../src/seo/categorySchema.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":["const SEO_META_TAG_PRIORITY = 'critical'\n\ntype SeoMetaTag = Record<string, string>\n\nexport function withSeoMetaPriority<T extends SeoMetaTag>(\n tag: T\n): T & { tagPriority: typeof SEO_META_TAG_PRIORITY } {\n return { ...tag, tagPriority: SEO_META_TAG_PRIORITY }\n}\n","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 * Каноничный публичный origin сайта из `store.domain`. Fallback (хост запроса)\n * приносит потребитель — ядро не знает про 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 → Rich Results (цена/наличие/рейтинг в выдаче).\n * Возвращает массив (0 или 1 объект) — потребитель оборачивает в `<script ld+json>`.\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 } from '@zenky/storefront-api'\n\nimport { resolveMediaUrl } from './utils'\nimport type { BreadcrumbItem } from './types'\n\n/**\n * JSON-LD для страницы категории: BreadcrumbList (хлебные крошки) + ItemList\n * (первые 10 товаров). Возвращает массив JSON-LD объектов (0–2).\n * Spec: https://schema.org/ItemList · https://schema.org/BreadcrumbList\n */\nexport function buildCategorySchemas(\n category: NestedCategory,\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: category.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","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 для страницы акции (редакционный контент).\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 для статьи блога (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 // Нет отдельного datePublished — используем 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 → 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 (по дням `hours` — «10:00–22:00») → массив schema.org/OpeningHoursSpecification.\n * Дни без распознаваемого интервала (выходной / круглосуточно / пусто) пропускаем.\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 (или LocalBusiness при наличии физической точки)\n * для главной. Google → Knowledge Panel. Все данные из уже загруженного профиля.\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","import { withSeoMetaPriority } from './headPriority'\n\n/** Уже разрешённые (с учётом дефолтов потребителя) значения OG/Twitter-тегов. */\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/** OG + Twitter meta-теги (critical-приоритет). Telegram читает twitter:image. */\nexport function buildOgTags(input: OgTagsInput): Record<string, string>[] {\n const tags: Record<string, string>[] = []\n\n if (input.title) {\n tags.push(withSeoMetaPriority({ property: 'og:title', content: input.title }))\n tags.push(withSeoMetaPriority({ name: 'twitter:title', content: input.title }))\n }\n\n if (input.description) {\n tags.push(withSeoMetaPriority({ property: 'og:description', content: input.description }))\n tags.push(withSeoMetaPriority({ name: 'twitter:description', content: input.description }))\n }\n\n if (input.image) {\n tags.push(withSeoMetaPriority({ property: 'og:image', content: input.image }))\n tags.push(withSeoMetaPriority({ name: 'twitter:image', content: input.image }))\n }\n\n tags.push(withSeoMetaPriority({ property: 'og:type', content: input.type ?? 'website' }))\n\n if (input.url) {\n tags.push(withSeoMetaPriority({ property: 'og:url', content: input.url }))\n }\n\n return tags\n}\n","import type { Seo, SeoMetaTag } from '@zenky/storefront-api'\n\nimport { withSeoMetaPriority } from './headPriority'\n\n/** SDK-теги `Seo.meta` → head-meta records (с critical-приоритетом для og/twitter/description). */\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 if (\n attrs.name === 'description' ||\n attrs.name?.startsWith('twitter:') ||\n attrs.property?.startsWith('og:')\n ) {\n return withSeoMetaPriority(attrs)\n }\n\n return attrs\n })\n}\n\n/** meta-теги из `Seo`-сущности (description + маппинг `Seo.meta`). 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(withSeoMetaPriority({ 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,3 +1,3 @@
1
- export { buildSnippetHead, normalizeSnippets } from '../chunk-DKWDKVXQ.js';
1
+ export { buildSnippetHead, normalizeSnippets } from '../chunk-7FRMMCR5.js';
2
2
  //# sourceMappingURL=index.js.map
3
3
  //# sourceMappingURL=index.js.map
@@ -1,8 +1,8 @@
1
- export { useProductsStore, useStore, useStorefrontProfile } from '../chunk-BIGTKVC6.js';
1
+ export { useProductsStore, useStore, useStorefrontProfile } from '../chunk-57IOMDAM.js';
2
2
  export { useCatalogStore } from '../chunk-Z3IW4ODZ.js';
3
3
  export { usePromotionsStore } from '../chunk-BWUKMWHL.js';
4
- export { useAuthStore, useStoreProfileStore } from '../chunk-PWHIDGKW.js';
5
- export { useCartStore, useOrderSettingsStore } from '../chunk-VL3T36VM.js';
4
+ export { useAuthStore, useStoreProfileStore } from '../chunk-TEG7EL4N.js';
5
+ export { useCartStore, useOrderSettingsStore } from '../chunk-FFBZKQRV.js';
6
6
  import '../chunk-V3VVDQIR.js';
7
7
  //# sourceMappingURL=index.js.map
8
8
  //# sourceMappingURL=index.js.map
@@ -21,8 +21,8 @@ declare function formatPhoneSpaced(formatted: string | null | undefined): string
21
21
  * Генерирует URL картинки QR-кода через Zenky media API.
22
22
  *
23
23
  * Контент кодируется в base64 и передаётся в query `content`.
24
- * `btoa` работает только с Latin-1 — для юникода предварительно прогоняем через
25
- * `encodeURIComponent` + `unescape`. На сервере используем Buffer.
24
+ * Реализация не зависит от `window`, `btoa` или Node.js `Buffer`, поэтому один
25
+ * и тот же код работает в браузере, Node SSR и edge runtimes.
26
26
  */
27
27
  declare function buildQrCodeUrl(target: string, apiBaseUrl: string, size?: number): string;
28
28
 
@@ -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-PJ2LEKW6.js';
1
+ export { buildQrCodeUrl, createOperationChain, formatAddressCityLast, formatDate, formatDateTime, formatPhoneNational, formatPhoneSpaced, formatScheduleStatus, getApiErrorMessage, getApiFieldErrors, getEmailLink, getPhoneTelLink, getSocialLinks, getTodayInfo, getWebsiteLink, getWeeklySchedule, pluralize, toDateInputValue, todayInTimezone } from '../chunk-MZ66ARDL.js';
2
2
  export { createDebounce } from '../chunk-XUGXLJZB.js';
3
3
  //# sourceMappingURL=index.js.map
4
4
  //# sourceMappingURL=index.js.map