@ecomiq/storefront 2.1.4 → 3.0.0

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 (310) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +150 -103
  3. package/dist/account-Dx4e3syi.js +258 -0
  4. package/dist/account-forms-AVk71ko7.js +39 -0
  5. package/dist/account-forms.d.ts +13 -0
  6. package/dist/account.d.ts +240 -0
  7. package/dist/address-defaults-BqjRhS82.js +14 -0
  8. package/dist/address-defaults.d.ts +11 -0
  9. package/dist/analytics/analytics-CG1Qagwl.js +148 -0
  10. package/dist/analytics/analytics.d.ts +1 -0
  11. package/dist/analytics/config-DJr-GRhs.js +62 -0
  12. package/dist/analytics/config.d.ts +22 -0
  13. package/dist/analytics/consent-B13rBpBs.js +57 -0
  14. package/dist/analytics/consent-banner-9QvRRy8v.js +239 -0
  15. package/dist/analytics/consent-banner.d.ts +20 -0
  16. package/dist/analytics/cookies-pLozllyB.js +100 -0
  17. package/dist/analytics/cookies.d.ts +6 -0
  18. package/dist/analytics/destinations-iECrlLIr.js +144 -0
  19. package/dist/analytics/events-Dt81tIp1.js +102 -0
  20. package/dist/analytics/events.d.ts +51 -0
  21. package/dist/analytics/tracker-p4yaoOyk.js +51 -0
  22. package/dist/api-cache-DsuIn5hX.js +159 -0
  23. package/dist/bank-accounts-DlEczQQ5.js +81 -0
  24. package/dist/bank-accounts.d.ts +4 -0
  25. package/dist/captcha-DAqaIx8m.js +117 -0
  26. package/dist/captcha.d.ts +34 -0
  27. package/dist/cart-D7DxZ2lD.js +30 -0
  28. package/dist/cart-actions-Bsub_FFG.js +116 -0
  29. package/dist/cart-actions.d.ts +15 -0
  30. package/dist/cart-drawer-B7PPsR69.js +22 -0
  31. package/dist/cart-drawer.d.ts +9 -0
  32. package/dist/cart-form-y9P1r_EX.js +81 -0
  33. package/dist/cart-form.d.ts +24 -0
  34. package/dist/cart-summary-ur2VWKr-.js +89 -0
  35. package/dist/cart-summary.d.ts +31 -0
  36. package/dist/cart.d.ts +4 -0
  37. package/dist/catalog-7gJ-l5hW.js +67 -0
  38. package/dist/catalog-hooks-B7ZP4wYY.js +312 -0
  39. package/dist/catalog-hooks.d.ts +111 -0
  40. package/dist/catalog.d.ts +5 -0
  41. package/dist/checkout-DVWSXANi.js +474 -0
  42. package/dist/checkout-form-CbOIdENC.js +131 -0
  43. package/dist/checkout-form.d.ts +20 -0
  44. package/dist/checkout.d.ts +86 -0
  45. package/dist/complaint-book-BvwLvfx0.js +117 -0
  46. package/dist/complaint-book.d.ts +62 -0
  47. package/dist/culqi-authentication-BGr20vAJ.js +204 -0
  48. package/dist/culqi-authentication.d.ts +15 -0
  49. package/dist/culqi-checkout-CTXpKgp_.js +55 -0
  50. package/dist/culqi-three-ds-HP3mfrvV.js +33 -0
  51. package/dist/culqi-three-ds.d.ts +29 -0
  52. package/dist/customer-avatar-CJXSDroK.js +12 -0
  53. package/dist/customer-avatar.d.ts +5 -0
  54. package/dist/customer-cookies-DHOgFAMV.js +45 -0
  55. package/dist/customer-csrf-C1oDX-Py.js +17 -0
  56. package/dist/customer-csrf.d.ts +3 -0
  57. package/dist/customer-document-fields-C1K-_DAD.js +70 -0
  58. package/dist/customer-document-fields.d.ts +9 -0
  59. package/dist/customer-login-form-C7AC6vXd.js +144 -0
  60. package/dist/customer-login-form.d.ts +17 -0
  61. package/dist/customer-registration-form-BEm_NEfP.js +93 -0
  62. package/dist/customer-registration-form.d.ts +8 -0
  63. package/dist/document-url-CGsAl5Tf.js +9 -0
  64. package/dist/documents-T1mzc_j8.js +38 -0
  65. package/dist/documents.d.ts +16 -0
  66. package/dist/entries/account.d.ts +11 -0
  67. package/dist/entries/account.js +10 -0
  68. package/dist/entries/analytics.d.ts +6 -0
  69. package/dist/entries/analytics.js +5 -0
  70. package/dist/entries/cart.d.ts +8 -0
  71. package/dist/entries/cart.js +7 -0
  72. package/dist/entries/checkout.d.ts +12 -0
  73. package/dist/entries/checkout.js +11 -0
  74. package/dist/entries/order.d.ts +11 -0
  75. package/dist/entries/order.js +11 -0
  76. package/dist/favicon-YpyhVJ9H.js +28 -0
  77. package/dist/favicon.d.ts +4 -0
  78. package/dist/form-BtQcbXlJ.js +18 -0
  79. package/dist/form-contracts-BNpHaTGJ.js +166 -0
  80. package/dist/form-contracts.d.ts +42 -0
  81. package/dist/form.d.ts +3 -0
  82. package/dist/hidden-CCWtOWKR.js +3 -0
  83. package/dist/hidden.d.ts +4 -0
  84. package/dist/index.d.ts +50 -0
  85. package/dist/index.js +50 -0
  86. package/dist/invoice-fields-BG0HjL6T.js +103 -0
  87. package/dist/invoice-fields.d.ts +39 -0
  88. package/dist/links-kFxBZfdb.js +96 -0
  89. package/dist/links.d.ts +21 -0
  90. package/dist/mercadopago-checkout-DC7RXa8k.js +27 -0
  91. package/dist/mercadopago-dialog-UTQev0MG.js +180 -0
  92. package/dist/mercadopago-dialog.d.ts +21 -0
  93. package/dist/money-DtGOtDri.js +32 -0
  94. package/dist/money.d.ts +1 -0
  95. package/dist/optimistic-cart-BveVOHuh.js +53 -0
  96. package/dist/optimistic-cart.d.ts +16 -0
  97. package/dist/order-display-DTt5WBbc.js +112 -0
  98. package/dist/order-display.d.ts +20 -0
  99. package/dist/order-draft-DxV1Uhm3.js +38 -0
  100. package/dist/order-draft.d.ts +16 -0
  101. package/dist/order-pages-Dm6l5m0m.js +38 -0
  102. package/dist/order-pages.d.ts +12 -0
  103. package/dist/order-payment-continuation-D_d5PGA4.js +212 -0
  104. package/dist/order-payment-continuation.d.ts +10 -0
  105. package/dist/order-payment-in-review-x4gdLFHZ.js +65 -0
  106. package/dist/order-payment-in-review.d.ts +5 -0
  107. package/dist/order-status-BJXLJwxq.js +162 -0
  108. package/dist/order-status.d.ts +89 -0
  109. package/dist/order-view-9IlBg5AF.js +62 -0
  110. package/dist/order-view.d.ts +12 -0
  111. package/dist/page-action-Ctkf4xC6.js +7 -0
  112. package/dist/page-seo-a5FJl8x5.js +262 -0
  113. package/dist/page-seo.d.ts +34 -0
  114. package/dist/pages-Ca_Gt6mq.js +150 -0
  115. package/dist/pages.d.ts +45 -0
  116. package/dist/payment-dialog-frame-B-Q_xQ94.js +57 -0
  117. package/dist/payment-logo-BoPaGJc_.js +46 -0
  118. package/dist/payment-logo-svgs-By9lDTLK.js +20 -0
  119. package/dist/payment-logo.d.ts +11 -0
  120. package/dist/payment-logos-BYvdwYyJ.js +23 -0
  121. package/dist/payment-methods-B8ocTgFZ.js +73 -0
  122. package/dist/payment-methods.d.ts +58 -0
  123. package/dist/payment-return-5cjypwIw.js +7 -0
  124. package/dist/place-CmW4hBWY.js +13 -0
  125. package/dist/place.d.ts +4 -0
  126. package/dist/preview-BiIBif3C.js +427 -0
  127. package/dist/preview-address-Cf0sJ-9p.js +21 -0
  128. package/dist/preview-cart-Dg2CVktS.js +376 -0
  129. package/dist/product-preview-BspMAecJ.js +51 -0
  130. package/dist/product-preview.d.ts +5 -0
  131. package/dist/product-variants-BNDHvs1y.js +46 -0
  132. package/dist/product-variants.d.ts +12 -0
  133. package/dist/public-pages-XtwlaLwt.js +194 -0
  134. package/dist/public-pages.d.ts +67 -0
  135. package/dist/registration-CpDcv8ql.js +91 -0
  136. package/dist/registration.d.ts +12 -0
  137. package/dist/request-security-CRsRMgwj.js +76 -0
  138. package/dist/rich-text-M-Ju8RL4.js +9 -0
  139. package/dist/rich-text.d.ts +5 -0
  140. package/dist/route-text-DcJw2SsK.js +9 -0
  141. package/dist/route-text.d.ts +4 -0
  142. package/dist/section-preview-CmsMl3yu.js +83 -0
  143. package/dist/section-preview.d.ts +12 -0
  144. package/dist/sections/account-addresses.d.ts +18 -0
  145. package/dist/sections/account-addresses.js +375 -0
  146. package/dist/sections/account-order.d.ts +12 -0
  147. package/dist/sections/account-order.js +592 -0
  148. package/dist/sections/account-orders.d.ts +8 -0
  149. package/dist/sections/account-orders.js +359 -0
  150. package/dist/sections/account-profile.d.ts +16 -0
  151. package/dist/sections/account-profile.js +123 -0
  152. package/dist/sections/checkout.d.ts +24 -0
  153. package/dist/sections/checkout.js +963 -0
  154. package/dist/sections/complaint-book.d.ts +8 -0
  155. package/dist/sections/complaint-book.js +538 -0
  156. package/dist/sections/design-system.d.ts +1 -0
  157. package/dist/sections/design-system.js +310 -0
  158. package/dist/sections/draft-invoice.d.ts +20 -0
  159. package/dist/sections/draft-invoice.js +158 -0
  160. package/dist/sections/order-tracking.d.ts +19 -0
  161. package/dist/sections/order-tracking.js +141 -0
  162. package/dist/sections-CNUMWTF_.js +77 -0
  163. package/dist/sections.d.ts +19 -0
  164. package/dist/seo-GpDRhAAA.js +78 -0
  165. package/dist/session-CKvmFJyg.js +379 -0
  166. package/dist/session.d.ts +24 -0
  167. package/dist/settings-B0NO2A6Y.js +78 -0
  168. package/dist/settings.d.ts +51 -0
  169. package/dist/source-marker-DhqSkW7n.js +4 -0
  170. package/dist/sources-B-3JFJfb.js +46 -0
  171. package/dist/sources.d.ts +7 -0
  172. package/dist/store-8Q-_LFrQ.js +152 -0
  173. package/dist/store-contact--hY6CoFb.js +19 -0
  174. package/dist/store-contact.d.ts +6 -0
  175. package/dist/store.d.ts +17 -0
  176. package/dist/storefront-language-DrMjVbmk.js +29 -0
  177. package/dist/storefront-language.d.ts +14 -0
  178. package/dist/storefront-text-D25ae1qf.js +11 -0
  179. package/dist/storefront-text-en-ljjjSS4q.js +1030 -0
  180. package/dist/storefront-text-pt-BtHaJLcp.js +1030 -0
  181. package/dist/storefront-text.d.ts +4 -0
  182. package/dist/stripe-dialog-Ev9s3Z62.js +396 -0
  183. package/dist/stripe-dialog.d.ts +30 -0
  184. package/dist/telemetry-HuZXWBPi.js +114 -0
  185. package/dist/telemetry.d.ts +10 -0
  186. package/dist/templates-CAqoNyTW.js +40 -0
  187. package/dist/templates.d.ts +15 -0
  188. package/dist/urls-CpGQPxvf.js +71 -0
  189. package/dist/urls.d.ts +18 -0
  190. package/dist/use-checkout-CZcxXJXi.js +42 -0
  191. package/dist/use-checkout-payment-9R-zacrJ.js +80 -0
  192. package/dist/use-checkout-payment.d.ts +18 -0
  193. package/dist/use-checkout.d.ts +36 -0
  194. package/dist/use-culqi-checkout-CrjLAipU.js +109 -0
  195. package/dist/use-order-payment-BKIz_IO0.js +45 -0
  196. package/dist/use-order-payment.d.ts +18 -0
  197. package/dist/use-payment-token-CErx-tN-.js +105 -0
  198. package/dist/vite-payment-logos-kziu7wKb.js +27 -0
  199. package/dist/vite-payment-logos.d.ts +24 -0
  200. package/dist/vite-public-assets-DW2fgtVb.js +209 -0
  201. package/dist/vite-public-assets.d.ts +29 -0
  202. package/dist/vite-sections-DTBr0-Kg.js +464 -0
  203. package/dist/vite-sections.d.ts +21 -0
  204. package/dist/vite-theme-ui-BMO-CDi3.js +41 -0
  205. package/dist/vite-theme-ui.d.ts +10 -0
  206. package/dist/vite.d.ts +36 -0
  207. package/dist/vite.js +127 -0
  208. package/dist/voucher-payment-CVFDZ1L8.js +66 -0
  209. package/dist/worker-CNi-gzCu.js +86 -0
  210. package/dist/worker.d.ts +27 -0
  211. package/package.json +100 -76
  212. package/src/sections/account-addresses.tsx +215 -0
  213. package/src/sections/account-order.tsx +424 -0
  214. package/src/sections/account-orders.tsx +259 -0
  215. package/src/sections/account-profile.tsx +70 -0
  216. package/src/sections/checkout.tsx +586 -0
  217. package/src/sections/complaint-book.tsx +325 -0
  218. package/src/sections/design-system.tsx +146 -0
  219. package/src/sections/draft-invoice.tsx +105 -0
  220. package/src/sections/order-tracking.tsx +81 -0
  221. package/src/vite-client.d.ts +4 -0
  222. package/bin/ecomiq.mjs +0 -179
  223. package/public/design-bridge.js +0 -318
  224. package/src/api-proxy.ts +0 -194
  225. package/src/auth/create-storefront-auth.ts +0 -94
  226. package/src/auth/ecomiq-oauth-password-client.ts +0 -26
  227. package/src/auth/ecomiq-oauth-password-constants.ts +0 -24
  228. package/src/auth/ecomiq-oauth-password-plugin.ts +0 -294
  229. package/src/auth/ecomiq-oauth-password-types.ts +0 -24
  230. package/src/auth/ecomiq-storefront-sign-in.ts +0 -44
  231. package/src/auth/env.ts +0 -7
  232. package/src/auth/secure-cookies.ts +0 -23
  233. package/src/auth/storefront-auth-base-url.ts +0 -117
  234. package/src/auth/storefront-auth.ts +0 -7
  235. package/src/cart-session.ts +0 -239
  236. package/src/client.ts +0 -48
  237. package/src/codegen.mjs +0 -801
  238. package/src/components/account/customer-addresses-panel.tsx +0 -678
  239. package/src/components/account/customer-order-detail-panel.tsx +0 -285
  240. package/src/components/account/customer-orders-panel.tsx +0 -192
  241. package/src/components/account/customer-profile-panel.tsx +0 -379
  242. package/src/components/checkout/cart-sync.ts +0 -143
  243. package/src/components/checkout/checkout-contact-section.tsx +0 -76
  244. package/src/components/checkout/checkout-external-update-banner.tsx +0 -15
  245. package/src/components/checkout/checkout-form-controls.tsx +0 -139
  246. package/src/components/checkout/checkout-line-items.tsx +0 -43
  247. package/src/components/checkout/checkout-order-summary.tsx +0 -165
  248. package/src/components/checkout/checkout-payment-section.tsx +0 -212
  249. package/src/components/checkout/checkout-session-view.tsx +0 -564
  250. package/src/components/checkout/checkout-shipping-address-section.tsx +0 -400
  251. package/src/components/checkout/checkout-shipping-section.tsx +0 -98
  252. package/src/components/checkout/checkout-states.tsx +0 -73
  253. package/src/components/checkout/checkout-step-card.tsx +0 -101
  254. package/src/components/checkout/checkout-steps.ts +0 -111
  255. package/src/components/checkout/form-schema.ts +0 -281
  256. package/src/components/checkout/format.ts +0 -15
  257. package/src/components/checkout/index.ts +0 -5
  258. package/src/components/checkout/map-customer-to-checkout.ts +0 -188
  259. package/src/components/checkout/types.ts +0 -41
  260. package/src/components/checkout/use-checkout-customer-prefill.ts +0 -409
  261. package/src/components/checkout/use-checkout-geo.ts +0 -50
  262. package/src/components/checkout/use-checkout-session.ts +0 -640
  263. package/src/components/hero.tsx +0 -291
  264. package/src/config.d.ts +0 -94
  265. package/src/config.mjs +0 -146
  266. package/src/format-money.ts +0 -31
  267. package/src/health.ts +0 -90
  268. package/src/hooks/index.ts +0 -9
  269. package/src/hooks/recently-viewed.ts +0 -125
  270. package/src/index.ts +0 -349
  271. package/src/link.tsx +0 -2
  272. package/src/pages/account/account-page-skeleton.tsx +0 -69
  273. package/src/pages/account/account-page-view.tsx +0 -270
  274. package/src/pages/account/account-shell.tsx +0 -101
  275. package/src/pages/account/parse-account-route.ts +0 -36
  276. package/src/pages/account.tsx +0 -16
  277. package/src/pages/api.$.ts +0 -20
  278. package/src/pages/api.auth.$.ts +0 -16
  279. package/src/pages/checkout.$checkoutId.tsx +0 -60
  280. package/src/pages/checkout.tsx +0 -31
  281. package/src/pages/pages.$pageSlug.tsx +0 -74
  282. package/src/pages/thank-you.tsx +0 -53
  283. package/src/providers/auth.tsx +0 -96
  284. package/src/providers/cart.tsx +0 -275
  285. package/src/providers/ecomiq.ts +0 -137
  286. package/src/providers/index.ts +0 -26
  287. package/src/proxy-api.ts +0 -20
  288. package/src/public-config.ts +0 -24
  289. package/src/review-submission.ts +0 -89
  290. package/src/runtime/context.ts +0 -2
  291. package/src/runtime/default-layout.tsx +0 -64
  292. package/src/runtime/head.ts +0 -149
  293. package/src/runtime/page.tsx +0 -143
  294. package/src/runtime/postcss-browser-stub.mjs +0 -5
  295. package/src/server-config-store.ts +0 -35
  296. package/src/server.ts +0 -286
  297. package/src/styles.css +0 -76
  298. package/src/ui/badge.tsx +0 -32
  299. package/src/ui/button.tsx +0 -31
  300. package/src/ui/card.tsx +0 -31
  301. package/src/ui/carousel.tsx +0 -218
  302. package/src/ui/cn.ts +0 -6
  303. package/src/ui/index.ts +0 -8
  304. package/src/ui/separator.tsx +0 -6
  305. package/src/ui/skeleton.tsx +0 -6
  306. package/src/ui/storefront-error.tsx +0 -49
  307. package/src/utils.ts +0 -36
  308. package/src/vite-inline.mjs +0 -263
  309. package/templates/__root.tsx +0 -83
  310. package/templates/router.tsx +0 -19
@@ -0,0 +1,262 @@
1
+ import { loadStorefrontTranslations, storefrontText } from "./storefront-text-D25ae1qf.js";
2
+ import { store } from "./store-8Q-_LFrQ.js";
3
+ import { absoluteUrl, canonicalOrigin, requestUrl } from "./urls-CpGQPxvf.js";
4
+ // src/page-seo.ts
5
+ const DESCRIPTION_LENGTH = 160;
6
+ const TITLE_LENGTH = 65;
7
+ function plainText(value) {
8
+ if (!value) return "";
9
+ return value.replace(/<[^>]*>/g, " ").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, "\"").replace(/&#39;/g, "'").replace(/\s+/g, " ").trim();
10
+ }
11
+ function clip(value, length) {
12
+ if (value.length <= length) return value;
13
+ const cut = value.slice(0, length - 1);
14
+ const space = cut.lastIndexOf(" ");
15
+ if (space > length / 2) return `${cut.slice(0, space)}…`;
16
+ return `${cut}…`;
17
+ }
18
+ function describe(...candidates) {
19
+ const parts = [];
20
+ for (const candidate of candidates) {
21
+ const text = plainText(candidate);
22
+ if (!text) continue;
23
+ parts.push(text);
24
+ if (parts.join(" ").length >= 70) break;
25
+ }
26
+ return clip(parts.join(" "), DESCRIPTION_LENGTH);
27
+ }
28
+ async function shop(request, context) {
29
+ const settings = await store(context).getSettings();
30
+ const name = settings.cms.settings.seo.siteName?.trim() || settings.store.name;
31
+ const about = settings.cms.settings.seo.metaDescription?.trim() || settings.store.description;
32
+ return {
33
+ settings,
34
+ translations: await loadStorefrontTranslations(settings.store.language),
35
+ origin: canonicalOrigin(request, settings),
36
+ name,
37
+ about
38
+ };
39
+ }
40
+ function titled(current, name) {
41
+ const template = current.settings.cms.settings.seo.titleTemplate?.trim();
42
+ let title = `${name} | ${current.name}`;
43
+ if (template && template.includes("{page}")) title = template.replaceAll("{page}", name).replaceAll("{site}", current.name);
44
+ if (title.length > TITLE_LENGTH) return name;
45
+ return title;
46
+ }
47
+ function crumbs(current, items) {
48
+ return {
49
+ "@context": "https://schema.org",
50
+ "@type": "BreadcrumbList",
51
+ itemListElement: [{
52
+ name: storefrontText(current.translations, "Inicio"),
53
+ path: "/"
54
+ }, ...items].map((item, index) => ({
55
+ "@type": "ListItem",
56
+ position: index + 1,
57
+ name: item.name,
58
+ item: absoluteUrl(current.origin, item.path)
59
+ }))
60
+ };
61
+ }
62
+ function seo(current, page, noindex = false) {
63
+ const { branding } = current.settings.cms.settings;
64
+ const { store: shop } = current.settings;
65
+ const image = page.image || branding.socialImageUrl || shop.logo || shop.icon || null;
66
+ return {
67
+ ...page,
68
+ image,
69
+ siteName: current.name,
70
+ noindex
71
+ };
72
+ }
73
+ async function homeSeo(request, context) {
74
+ const current = await shop(request, context);
75
+ const { settings } = current;
76
+ const description = describe(current.about, `Compra en ${current.name}.`);
77
+ let title = settings.cms.settings.seo.metaTitle?.trim() || current.name;
78
+ if (title.length < 15 && current.about) title = clip(`${title} | ${plainText(current.about)}`, TITLE_LENGTH);
79
+ const logo = settings.store.logo ?? settings.store.icon;
80
+ const organization = {
81
+ "@context": "https://schema.org",
82
+ "@type": "Organization",
83
+ name: current.name,
84
+ url: current.origin
85
+ };
86
+ if (logo) organization.logo = logo;
87
+ return seo(current, {
88
+ title,
89
+ description,
90
+ canonical: absoluteUrl(current.origin, "/"),
91
+ image: logo,
92
+ type: "website",
93
+ jsonLd: [organization, {
94
+ "@context": "https://schema.org",
95
+ "@type": "WebSite",
96
+ name: current.name,
97
+ url: current.origin,
98
+ potentialAction: {
99
+ "@type": "SearchAction",
100
+ target: {
101
+ "@type": "EntryPoint",
102
+ urlTemplate: `${current.origin}/products?q={search_term_string}`
103
+ },
104
+ "query-input": "required name=search_term_string"
105
+ }
106
+ }]
107
+ });
108
+ }
109
+ async function productSeo(request, context, product, rating = null) {
110
+ const current = await shop(request, context);
111
+ const canonical = absoluteUrl(current.origin, product.url);
112
+ const images = [product.imageUrl, ...product.images.filter((image) => image.type === "image").map((image) => image.url)].filter((url, index, all) => url && all.indexOf(url) === index);
113
+ const offer = {
114
+ "@type": "Offer",
115
+ url: canonical,
116
+ price: product.price,
117
+ priceCurrency: current.settings.store.currency,
118
+ availability: product.isAvailable ? "https://schema.org/InStock" : "https://schema.org/OutOfStock",
119
+ itemCondition: "https://schema.org/NewCondition"
120
+ };
121
+ const data = {
122
+ "@context": "https://schema.org",
123
+ "@type": "Product",
124
+ name: product.name,
125
+ description: clip(plainText(product.description), 5e3),
126
+ image: images,
127
+ offers: offer
128
+ };
129
+ const sku = product.variants[0]?.sku;
130
+ if (sku) data.sku = sku;
131
+ if (product.brand) data.brand = {
132
+ "@type": "Brand",
133
+ name: product.brand
134
+ };
135
+ if (rating && rating.averageRating !== null && rating.reviewCount > 0) data.aggregateRating = {
136
+ "@type": "AggregateRating",
137
+ ratingValue: Number(rating.averageRating.toFixed(1)),
138
+ reviewCount: rating.reviewCount
139
+ };
140
+ return seo(current, {
141
+ title: titled(current, product.name),
142
+ description: describe(product.description, storefrontText(current.translations, "{value0} en {value1}.", {
143
+ value0: product.name,
144
+ value1: current.name
145
+ }), current.about),
146
+ canonical,
147
+ image: images[0] ?? null,
148
+ type: "product",
149
+ jsonLd: [data, crumbs(current, [{
150
+ name: product.name,
151
+ path: product.url
152
+ }])]
153
+ });
154
+ }
155
+ async function categorySeo(request, context, category) {
156
+ const current = await shop(request, context);
157
+ const trail = [...category.ancestors.map((ancestor) => ({
158
+ name: ancestor.name,
159
+ path: ancestor.url
160
+ })), {
161
+ name: category.name,
162
+ path: category.url
163
+ }];
164
+ return seo(current, {
165
+ title: category.metaTitle?.trim() || titled(current, category.name),
166
+ description: describe(category.metaDescription, category.description, storefrontText(current.translations, "{value0} en {value1}.", {
167
+ value0: category.name,
168
+ value1: current.name
169
+ }), current.about),
170
+ canonical: absoluteUrl(current.origin, category.url),
171
+ image: category.imageUrl,
172
+ type: "website",
173
+ jsonLd: [crumbs(current, trail)]
174
+ });
175
+ }
176
+ async function collectionSeo(request, context, collection) {
177
+ const current = await shop(request, context);
178
+ const path = `/collections/${collection.slug}`;
179
+ return seo(current, {
180
+ title: titled(current, collection.name),
181
+ description: describe(collection.description, storefrontText(current.translations, "{value0} en {value1}.", {
182
+ value0: collection.name,
183
+ value1: current.name
184
+ }), current.about),
185
+ canonical: absoluteUrl(current.origin, path),
186
+ image: collection.imageUrl ?? null,
187
+ type: "website",
188
+ jsonLd: [crumbs(current, [{
189
+ name: collection.name,
190
+ path
191
+ }])]
192
+ });
193
+ }
194
+ async function pageSeo(request, context, input) {
195
+ const current = await shop(request, context);
196
+ const path = input.path ?? requestUrl(request).pathname;
197
+ const title = input.systemTitle ? storefrontText(current.translations, input.title, input.titleValues) : input.title;
198
+ return seo(current, {
199
+ title: titled(current, title),
200
+ description: describe(input.description, storefrontText(current.translations, "{value0} en {value1}.", {
201
+ value0: title,
202
+ value1: current.name
203
+ }), current.about),
204
+ canonical: absoluteUrl(current.origin, path),
205
+ image: input.image ?? null,
206
+ type: input.type ?? "website",
207
+ jsonLd: [crumbs(current, [{
208
+ name: title,
209
+ path
210
+ }])]
211
+ }, input.noindex);
212
+ }
213
+ function seoMeta(page) {
214
+ if (!page) return [];
215
+ const tags = [
216
+ { title: page.title },
217
+ {
218
+ name: "description",
219
+ content: page.description
220
+ },
221
+ {
222
+ tagName: "link",
223
+ rel: "canonical",
224
+ href: page.canonical
225
+ },
226
+ {
227
+ property: "og:type",
228
+ content: page.type
229
+ },
230
+ {
231
+ property: "og:title",
232
+ content: page.title
233
+ },
234
+ {
235
+ property: "og:description",
236
+ content: page.description
237
+ },
238
+ {
239
+ property: "og:url",
240
+ content: page.canonical
241
+ },
242
+ {
243
+ property: "og:site_name",
244
+ content: page.siteName
245
+ },
246
+ {
247
+ name: "twitter:card",
248
+ content: page.image ? "summary_large_image" : "summary"
249
+ }
250
+ ];
251
+ if (page.image) tags.push({
252
+ property: "og:image",
253
+ content: page.image
254
+ });
255
+ if (page.noindex) tags.push({
256
+ name: "robots",
257
+ content: "noindex, follow"
258
+ });
259
+ for (const data of page.jsonLd) tags.push({ "script:ld+json": data });
260
+ return tags;
261
+ }
262
+ export { categorySeo, collectionSeo, homeSeo, pageSeo, productSeo, seoMeta };
@@ -0,0 +1,34 @@
1
+ import { StorefrontTextValues } from "./storefront-text.js";
2
+ import { MetaDescriptor, RouterContextProvider } from "react-router";
3
+ import { CategoryDetail, Collection, ProductDetail } from "@ecomiq/core";
4
+ type JsonLd = Record<string, unknown>;
5
+ export type PageSeo = {
6
+ title: string;
7
+ description: string;
8
+ canonical: string;
9
+ image: string | null;
10
+ type: "website" | "product" | "article";
11
+ siteName: string;
12
+ noindex: boolean;
13
+ jsonLd: JsonLd[];
14
+ };
15
+ export type PageSeoInput = {
16
+ title: string;
17
+ systemTitle?: boolean;
18
+ titleValues?: StorefrontTextValues;
19
+ description?: string | null;
20
+ image?: string | null;
21
+ path?: string;
22
+ type?: "website" | "article";
23
+ noindex?: boolean;
24
+ };
25
+ export declare function homeSeo(request: Request, context: Readonly<RouterContextProvider>): Promise<PageSeo>;
26
+ export declare function productSeo(request: Request, context: Readonly<RouterContextProvider>, product: ProductDetail, rating?: {
27
+ averageRating: number | null;
28
+ reviewCount: number;
29
+ } | null): Promise<PageSeo>;
30
+ export declare function categorySeo(request: Request, context: Readonly<RouterContextProvider>, category: CategoryDetail): Promise<PageSeo>;
31
+ export declare function collectionSeo(request: Request, context: Readonly<RouterContextProvider>, collection: Collection): Promise<PageSeo>;
32
+ export declare function pageSeo(request: Request, context: Readonly<RouterContextProvider>, input: PageSeoInput): Promise<PageSeo>;
33
+ export declare function seoMeta(page: PageSeo | null | undefined): MetaDescriptor[];
34
+ export {};
@@ -0,0 +1,150 @@
1
+ import { categoryAnalytics, collectionAnalytics, productAnalytics } from "./analytics/events-Dt81tIp1.js";
2
+ import { readCatalog } from "./catalog-7gJ-l5hW.js";
3
+ import { store } from "./store-8Q-_LFrQ.js";
4
+ import { requestUrl, requireCanonical } from "./urls-CpGQPxvf.js";
5
+ import { worker } from "./worker-CNi-gzCu.js";
6
+ import { data, redirect } from "react-router";
7
+ import { CategoryDetailSchema, CollectionSchema, GraphQLResponseError, ProductDetailSchema, ProductSummarySchema, ProductsResponseSchema, graphQLSelection, sanitizeHtml } from "@ecomiq/core";
8
+ import { z } from "zod";
9
+ // src/pages.ts
10
+ const RELATED_LIMIT = 8;
11
+ const RELATED_MAX = 12;
12
+ const ReviewSummarySchema = z.object({
13
+ averageRating: z.number().nullable(),
14
+ reviewCount: z.number().int().nonnegative()
15
+ });
16
+ const RouteSchema = z.object({ route: z.object({
17
+ kind: z.enum([
18
+ "product",
19
+ "category",
20
+ "redirect"
21
+ ]),
22
+ url: z.string(),
23
+ product: ProductDetailSchema.extend({
24
+ related: z.array(ProductSummarySchema),
25
+ reviewSummary: ReviewSummarySchema
26
+ }).nullable(),
27
+ category: CategoryDetailSchema.extend({ products: ProductsResponseSchema }).nullable()
28
+ }).nullable() });
29
+ const ROUTE = `query Route($path: String!, $previewKey: String, $related: Int!, $catalog: StorefrontProductsInput) {
30
+ route(path: $path, previewKey: $previewKey) {
31
+ kind
32
+ url
33
+ product { ${graphQLSelection(ProductDetailSchema)} related(limit: $related) { ${graphQLSelection(ProductSummarySchema)} } reviewSummary { averageRating reviewCount } }
34
+ category { ${graphQLSelection(CategoryDetailSchema)} products(input: $catalog) { ${graphQLSelection(ProductsResponseSchema)} } }
35
+ }
36
+ }`;
37
+ const CollectionPageSchema = z.object({
38
+ collection: CollectionSchema,
39
+ products: ProductsResponseSchema
40
+ });
41
+ const COLLECTION = `query Collection($handle: String!, $catalog: StorefrontProductsInput) {
42
+ collection(slug: $handle) { ${graphQLSelection(CollectionSchema)} }
43
+ products(input: $catalog) { ${graphQLSelection(ProductsResponseSchema)} }
44
+ }`;
45
+ function productsInput(query) {
46
+ const { includeFilters: _includeFilters, categoryId: _categoryId, sellers, sort, productTypes, ...rest } = query;
47
+ return {
48
+ ...rest,
49
+ ...sellers ? { sellerSlugs: sellers } : {},
50
+ ...sort ? { sort: sort.toUpperCase() } : {},
51
+ ...productTypes ? { productTypes: productTypes.map((type) => type.toUpperCase()) } : {}
52
+ };
53
+ }
54
+ function withoutFilter(result, id) {
55
+ return {
56
+ ...result,
57
+ filters: result.filters.filter((filter) => filter.id !== id)
58
+ };
59
+ }
60
+ function moved(current, url) {
61
+ const target = new URL(current);
62
+ target.pathname = url;
63
+ return redirect(`/${target.pathname.replace(/^\/+/, "")}${target.search}`, 301);
64
+ }
65
+ async function resolvePage(request, context, options = {}) {
66
+ const current = requestUrl(request);
67
+ const query = readCatalog(current);
68
+ delete query.categories;
69
+ const { productPreview } = context.get(worker);
70
+ const { route } = await store(context).graphql(ROUTE, RouteSchema, {
71
+ path: current.pathname,
72
+ previewKey: productPreview?.key ?? null,
73
+ related: Math.min(RELATED_MAX, Math.max(1, Math.trunc(options.related ?? RELATED_LIMIT))),
74
+ catalog: productsInput(query)
75
+ });
76
+ if (route === null) throw data(null, { status: 404 });
77
+ if (route.kind === "redirect") throw moved(current, route.url);
78
+ requireCanonical(request, route.url);
79
+ if (route.kind === "product" && route.product) {
80
+ const { related, reviewSummary, ...product } = route.product;
81
+ if (product.isPreview && productPreview) productPreview.shown = true;
82
+ return {
83
+ kind: "product",
84
+ product,
85
+ related: related.filter((item) => item.id !== product.id),
86
+ reviewSummary,
87
+ analytics: productAnalytics(product)
88
+ };
89
+ }
90
+ if (route.kind === "category" && route.category) {
91
+ const { products, ...category } = route.category;
92
+ const description = category.description === null ? null : sanitizeHtml(category.description);
93
+ return {
94
+ kind: "category",
95
+ category: {
96
+ ...category,
97
+ description
98
+ },
99
+ page: query.page ?? 1,
100
+ result: withoutFilter(products, "category"),
101
+ analytics: categoryAnalytics(category, products.products)
102
+ };
103
+ }
104
+ throw data(null, { status: 404 });
105
+ }
106
+ async function productPage(request, context, options = {}) {
107
+ const page = await resolvePage(request, context, options);
108
+ if (page.kind !== "product") throw data(null, { status: 404 });
109
+ return page;
110
+ }
111
+ async function collectionPage(request, context, handle) {
112
+ const query = {
113
+ ...readCatalog(requestUrl(request)),
114
+ collections: [handle]
115
+ };
116
+ try {
117
+ const { collection, products } = await store(context).graphql(COLLECTION, CollectionPageSchema, {
118
+ handle,
119
+ catalog: productsInput(query)
120
+ });
121
+ return {
122
+ collection,
123
+ page: query.page ?? 1,
124
+ result: withoutFilter(products, "collection"),
125
+ analytics: collectionAnalytics(collection, products.products)
126
+ };
127
+ } catch (error) {
128
+ if (error instanceof GraphQLResponseError && error.errors.some((item) => item.extensions?.code === "NOT_FOUND")) throw data(null, { status: 404 });
129
+ throw error;
130
+ }
131
+ }
132
+ function productReviews(context, options) {
133
+ if (options.reviewCount === 0) return Promise.resolve({
134
+ reviews: [],
135
+ reviewCount: 0
136
+ });
137
+ return store(context).listProductReviews({
138
+ productId: options.productId,
139
+ limit: 200,
140
+ sortBy: "createdOn",
141
+ sortDirection: "desc"
142
+ }).then((result) => ({
143
+ reviews: result.data,
144
+ reviewCount: result.totalCount ?? result.data.length
145
+ })).catch(() => ({
146
+ reviews: [],
147
+ reviewCount: 0
148
+ }));
149
+ }
150
+ export { collectionPage, productPage, productReviews, resolvePage };
@@ -0,0 +1,45 @@
1
+ import { PageAnalytics } from "./analytics/events.js";
2
+ import { RouterContextProvider } from "react-router";
3
+ import { CategoryDetail, Collection, ProductDetail, ProductSummary, ProductsResponse, StorefrontReview } from "@ecomiq/core";
4
+ import { z } from "zod";
5
+ declare const ReviewSummarySchema: z.ZodObject<{
6
+ averageRating: z.ZodNullable<z.ZodNumber>;
7
+ reviewCount: z.ZodNumber;
8
+ }, z.core.$strip>;
9
+ export type ReviewSummary = z.infer<typeof ReviewSummarySchema>;
10
+ export type ProductPage = {
11
+ kind: "product";
12
+ product: ProductDetail;
13
+ related: ProductSummary[];
14
+ reviewSummary: ReviewSummary;
15
+ analytics: PageAnalytics;
16
+ };
17
+ export type CategoryPage = {
18
+ kind: "category";
19
+ category: CategoryDetail;
20
+ page: number;
21
+ result: ProductsResponse;
22
+ analytics: PageAnalytics;
23
+ };
24
+ export type ResolvedPage = ProductPage | CategoryPage;
25
+ export type CollectionPage = {
26
+ collection: Collection;
27
+ page: number;
28
+ result: ProductsResponse;
29
+ analytics: PageAnalytics;
30
+ };
31
+ export type ProductReviews = {
32
+ reviews: StorefrontReview[];
33
+ reviewCount: number;
34
+ };
35
+ export type PageOptions = {
36
+ related?: number;
37
+ };
38
+ export declare function resolvePage(request: Request, context: Readonly<RouterContextProvider>, options?: PageOptions): Promise<ResolvedPage>;
39
+ export declare function productPage(request: Request, context: Readonly<RouterContextProvider>, options?: PageOptions): Promise<ProductPage>;
40
+ export declare function collectionPage(request: Request, context: Readonly<RouterContextProvider>, handle: string): Promise<CollectionPage>;
41
+ export declare function productReviews(context: Readonly<RouterContextProvider>, options: {
42
+ productId: string;
43
+ reviewCount: number;
44
+ }): Promise<ProductReviews>;
45
+ export {};
@@ -0,0 +1,57 @@
1
+ import { useStorefrontText } from "./storefront-language-DrMjVbmk.js";
2
+ import { useEffect, useRef } from "react";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ // src/payment-dialog-frame.tsx
5
+ function PaymentDialogFrame({ title, logo, onClose, note, children }) {
6
+ const storefrontTranslate = useStorefrontText();
7
+ const close = useRef(onClose);
8
+ close.current = onClose;
9
+ const modal = useRef(null);
10
+ useEffect(() => {
11
+ const dialog = modal.current;
12
+ if (dialog === null) return;
13
+ const trigger = document.activeElement;
14
+ dialog.showModal();
15
+ return () => {
16
+ dialog.close();
17
+ if (trigger instanceof HTMLElement && trigger.isConnected) trigger.focus();
18
+ };
19
+ }, []);
20
+ return /* @__PURE__ */ jsx("dialog", {
21
+ ref: modal,
22
+ role: "dialog",
23
+ "aria-label": title,
24
+ onCancel: (event) => {
25
+ event.preventDefault();
26
+ close.current();
27
+ },
28
+ className: "fixed inset-0 m-auto max-h-[calc(100%-2rem)] w-[calc(100%-2rem)] max-w-md rounded-lg bg-background p-0 text-foreground shadow-xl backdrop:bg-black/50",
29
+ children: /* @__PURE__ */ jsxs("div", {
30
+ className: "relative max-h-full w-full max-w-md overflow-y-auto rounded-lg bg-background p-4 text-foreground shadow-xl",
31
+ children: [
32
+ /* @__PURE__ */ jsxs("div", {
33
+ className: "mb-2 flex items-center justify-between gap-4",
34
+ children: [/* @__PURE__ */ jsxs("div", {
35
+ className: "flex items-center gap-2",
36
+ children: [logo, /* @__PURE__ */ jsx("p", {
37
+ className: "text-sm font-medium",
38
+ children: title
39
+ })]
40
+ }), /* @__PURE__ */ jsx("button", {
41
+ type: "button",
42
+ onClick: () => close.current(),
43
+ "aria-label": storefrontTranslate("Cerrar"),
44
+ className: "rounded-md px-2 text-lg leading-none text-muted-foreground hover:text-foreground",
45
+ children: "×"
46
+ })]
47
+ }),
48
+ children,
49
+ /* @__PURE__ */ jsx("p", {
50
+ className: "mt-2 text-center text-xs text-muted-foreground",
51
+ children: note
52
+ })
53
+ ]
54
+ })
55
+ });
56
+ }
57
+ export { PaymentDialogFrame };
@@ -0,0 +1,46 @@
1
+ import { paymentLogoNames, paymentLogoPath } from "./payment-logos-BYvdwYyJ.js";
2
+ import { jsx } from "react/jsx-runtime";
3
+ // src/payment-logo.tsx
4
+ const paymentLogoAliases = /* @__PURE__ */ new Map([
5
+ ["amex", "american_express"],
6
+ ["diners_club", "diners"],
7
+ ["mercado_pago", "mercadopago"],
8
+ ["master", "mastercard"],
9
+ ["manual_yape", "yape"],
10
+ ["manual_plin", "plin"]
11
+ ]);
12
+ function paymentLogoCode(code) {
13
+ const normalized = code.trim().toLowerCase().replaceAll("-", "_");
14
+ return paymentLogoAliases.get(normalized) ?? normalized;
15
+ }
16
+ function hasPaymentLogo(code) {
17
+ return paymentLogoNames.has(paymentLogoCode(code));
18
+ }
19
+ function PaymentLogo({ code, className }) {
20
+ const logoCode = paymentLogoCode(code);
21
+ const name = paymentLogoNames.get(logoCode);
22
+ if (name === void 0) return null;
23
+ return /* @__PURE__ */ jsx("img", {
24
+ src: paymentLogoPath(logoCode),
25
+ alt: name,
26
+ width: 38,
27
+ height: 24,
28
+ className,
29
+ loading: "lazy",
30
+ decoding: "async"
31
+ });
32
+ }
33
+ function addLogoCode(codes, code) {
34
+ const logoCode = paymentLogoCode(code);
35
+ if (paymentLogoNames.has(logoCode)) codes.add(logoCode);
36
+ }
37
+ function paymentLogoCodes(methods) {
38
+ const codes = /* @__PURE__ */ new Set();
39
+ const ordered = [...methods].sort((left, right) => left.sortOrder - right.sortOrder);
40
+ for (const method of ordered) {
41
+ for (const brand of method.brands) addLogoCode(codes, brand);
42
+ if (method.kind === "card" || method.brands.length === 0) addLogoCode(codes, method.code);
43
+ }
44
+ return [...codes];
45
+ }
46
+ export { PaymentLogo, hasPaymentLogo, paymentLogoCodes };