@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,51 @@
1
+ import { currentConsent, installConsent, onConsent } from "./consent-B13rBpBs.js";
2
+ // src/analytics/tracker.ts
3
+ const QUEUE_LIMIT = 50;
4
+ let config = null;
5
+ let queue = [];
6
+ let loading = null;
7
+ let destinations = null;
8
+ function granted(consent) {
9
+ return consent.analytics || consent.marketing;
10
+ }
11
+ function flush(settings, consent, ready) {
12
+ ready.load(settings, consent);
13
+ const pending = queue;
14
+ queue = [];
15
+ if (granted(consent)) for (const event of pending) ready.send(settings, consent, event);
16
+ }
17
+ function apply(consent) {
18
+ const settings = config;
19
+ if (settings === null || consent === null) return;
20
+ if (destinations !== null) {
21
+ flush(settings, consent, destinations);
22
+ return;
23
+ }
24
+ if (!granted(consent)) {
25
+ queue = [];
26
+ return;
27
+ }
28
+ loading ??= import("./destinations-iECrlLIr.js");
29
+ loading.then((ready) => {
30
+ destinations = ready;
31
+ const latest = currentConsent();
32
+ if (config !== null && latest !== null) flush(config, latest, ready);
33
+ });
34
+ }
35
+ function configureTracker(settings) {
36
+ config = settings;
37
+ installConsent(settings);
38
+ }
39
+ function startTracker() {
40
+ const stop = onConsent(apply);
41
+ apply(currentConsent());
42
+ return stop;
43
+ }
44
+ function track(event) {
45
+ if (config === null) return;
46
+ const consent = currentConsent();
47
+ if (consent !== null && !granted(consent)) return;
48
+ if (consent !== null && destinations !== null) destinations.send(config, consent, event);
49
+ else if (queue.length < QUEUE_LIMIT) queue.push(event);
50
+ }
51
+ export { configureTracker, startTracker, track };
@@ -0,0 +1,159 @@
1
+ import { STOREFRONT_SETTINGS_SCHEMA_VERSION } from "@ecomiq/core";
2
+ import { z } from "zod";
3
+ import { STOREFRONT_CACHE_TTL_SECONDS } from "@ecomiq/core/storefront-cache-revision";
4
+ // src/api-cache.ts
5
+ const CACHE_NAME = "ecomiq-api";
6
+ const GEO_CACHE_SECONDS = 43200;
7
+ const STORED_AT = "x-ecomiq-stored-at";
8
+ const PUBLIC_READS = [
9
+ "settings",
10
+ "collections",
11
+ "products",
12
+ "categories",
13
+ "urls/resolve",
14
+ "pages",
15
+ "payments/methods",
16
+ "sellers",
17
+ "articles",
18
+ "geo",
19
+ "sitemap"
20
+ ];
21
+ const PRIVATE_HEADERS = [
22
+ "authorization",
23
+ "cookie",
24
+ "x-customer-session"
25
+ ];
26
+ const NO_SHARED_CACHE = /(?:^|,)\s*(?:private|no-store|no-cache|max-age\s*=\s*0|s-maxage\s*=\s*0)\b/i;
27
+ const GraphQLErrorsSchema = z.object({ errors: z.array(z.object({ extensions: z.object({ code: z.string().optional() }).optional() })) });
28
+ const REVOKED_GRAPHQL_CODES = /* @__PURE__ */ new Set([
29
+ "NOT_FOUND",
30
+ "FORBIDDEN",
31
+ "UNAUTHENTICATED",
32
+ "AUTH_NOT_AUTHENTICATED",
33
+ "AUTH_NOT_AUTHORIZED",
34
+ "STORE_CONTEXT_REQUIRED"
35
+ ]);
36
+ function graphQLRead(request) {
37
+ return request.method === "POST" && new URL(request.url).pathname.endsWith("/graphql");
38
+ }
39
+ function catalogLinksRead(request) {
40
+ return request.method === "POST" && new URL(request.url).pathname.endsWith("/v1/urls/links");
41
+ }
42
+ function publicRead(request) {
43
+ if (PRIVATE_HEADERS.some((header) => request.headers.has(header))) return false;
44
+ if (NO_SHARED_CACHE.test(request.headers.get("cache-control") ?? "") || request.cache === "no-store" || request.cache === "no-cache") return false;
45
+ if (graphQLRead(request) || catalogLinksRead(request)) return true;
46
+ if (request.method !== "GET") return false;
47
+ const path = new URL(request.url).pathname.split("/v1/")[1] ?? "";
48
+ if (path === "reviews") return true;
49
+ if (path.endsWith("/reviews")) return false;
50
+ return PUBLIC_READS.some((read) => path === read || path.startsWith(`${read}/`));
51
+ }
52
+ function geoRead(request) {
53
+ const path = new URL(request.url).pathname.split("/v1/")[1] ?? "";
54
+ return path === "geo" || path.startsWith("geo/");
55
+ }
56
+ async function cacheKey(request, store, cacheRevision) {
57
+ const url = new URL(request.url);
58
+ url.searchParams.set("__ecomiq_store", store);
59
+ if (!geoRead(request)) url.searchParams.set("__ecomiq_revision", cacheRevision);
60
+ const storeId = request.headers.get("x-store-id");
61
+ if (storeId !== null) url.searchParams.set("__ecomiq_store_id", storeId);
62
+ if (url.pathname.endsWith("/v1/settings")) url.searchParams.set("__ecomiq_settings_schema", String(STOREFRONT_SETTINGS_SCHEMA_VERSION));
63
+ if (request.method === "POST") {
64
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(await request.clone().text()));
65
+ url.searchParams.set("__ecomiq_query", [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join(""));
66
+ }
67
+ return new Request(`https://ecomiq-api.cache/${url.host}${url.pathname}${url.search}`);
68
+ }
69
+ function countedFetch(count) {
70
+ return (input, init) => {
71
+ count();
72
+ return fetch(input, init);
73
+ };
74
+ }
75
+ function uncachedFetch(count) {
76
+ return (input, init) => {
77
+ const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : void 0));
78
+ headers.set("cache-control", "no-store");
79
+ count();
80
+ return fetch(input, {
81
+ ...init,
82
+ headers,
83
+ cache: "no-store"
84
+ });
85
+ };
86
+ }
87
+ async function load(cache, key, request, maxAge, count, ctx, hasCachedResponse = false) {
88
+ count();
89
+ const response = await fetch(request);
90
+ const shareable = response.status === 200 && !response.headers.has("set-cookie") && !NO_SHARED_CACHE.test(response.headers.get("cache-control") ?? "") && (response.headers.get("vary") ?? "").split(",").every((header) => ["", "accept-encoding"].includes(header.trim().toLowerCase()));
91
+ if ([
92
+ 401,
93
+ 403,
94
+ 404,
95
+ 410
96
+ ].includes(response.status) || response.status === 200 && !shareable) {
97
+ if (hasCachedResponse) {
98
+ count();
99
+ await cache.delete(key).catch(() => void 0);
100
+ }
101
+ return response;
102
+ }
103
+ if (!shareable) return response;
104
+ const headers = new Headers(response.headers);
105
+ headers.set("cache-control", `public, max-age=${maxAge}`);
106
+ headers.set(STORED_AT, String(Date.now()));
107
+ const copy = response.clone();
108
+ const write = (async () => {
109
+ if (graphQLRead(request)) {
110
+ const body = await copy.arrayBuffer();
111
+ const result = JSON.parse(new TextDecoder().decode(body));
112
+ if (result === null || typeof result !== "object") return;
113
+ if ("errors" in result) {
114
+ const parsed = GraphQLErrorsSchema.safeParse(result);
115
+ if (hasCachedResponse && parsed.success && parsed.data.errors.some((error) => error.extensions?.code !== void 0 && REVOKED_GRAPHQL_CODES.has(error.extensions.code))) {
116
+ count();
117
+ await cache.delete(key).catch(() => void 0);
118
+ }
119
+ return;
120
+ }
121
+ count();
122
+ await cache.put(key, new Response(body, {
123
+ status: 200,
124
+ headers
125
+ }));
126
+ } else {
127
+ count();
128
+ await cache.put(key, new Response(copy.body, {
129
+ status: 200,
130
+ headers
131
+ }));
132
+ }
133
+ })().catch(() => void 0);
134
+ if (ctx) ctx.waitUntil(write);
135
+ else await write;
136
+ return response;
137
+ }
138
+ function apiCacheFetch(ctx, store, count, cacheRevision) {
139
+ if (typeof caches === "undefined") return void 0;
140
+ return async (input, init) => {
141
+ const request = new Request(input, init);
142
+ if (!publicRead(request)) return countedFetch(count)(request);
143
+ const maxAge = geoRead(request) ? GEO_CACHE_SECONDS : STOREFRONT_CACHE_TTL_SECONDS;
144
+ let cache;
145
+ try {
146
+ cache = await caches.open(CACHE_NAME);
147
+ } catch {
148
+ return uncachedFetch(count)(request);
149
+ }
150
+ const key = await cacheKey(request, store, cacheRevision);
151
+ count();
152
+ const cached = await cache.match(key).catch(() => void 0);
153
+ if (!cached) return load(cache, key, request, maxAge, count, ctx);
154
+ const age = (Date.now() - Number(cached.headers.get(STORED_AT) ?? 0)) / 1e3;
155
+ if (!(age >= 0 && age < maxAge)) return load(cache, key, request, maxAge, count, ctx, true);
156
+ return cached;
157
+ };
158
+ }
159
+ export { apiCacheFetch, countedFetch, uncachedFetch };
@@ -0,0 +1,81 @@
1
+ import { useStorefrontText } from "./storefront-language-DrMjVbmk.js";
2
+ import { useState } from "react";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ // src/bank-accounts.tsx
5
+ const ACCOUNT_TYPES = {
6
+ savings: "Ahorros",
7
+ checking: "Corriente"
8
+ };
9
+ const CURRENCIES = {
10
+ PEN: "soles",
11
+ USD: "dólares"
12
+ };
13
+ function CopyValue({ label, value }) {
14
+ const storefrontTranslate = useStorefrontText();
15
+ const [copied, setCopied] = useState(false);
16
+ return /* @__PURE__ */ jsxs("span", {
17
+ className: "flex items-center justify-between gap-3",
18
+ children: [/* @__PURE__ */ jsxs("span", {
19
+ className: "min-w-0 break-all",
20
+ children: [
21
+ label,
22
+ " ",
23
+ /* @__PURE__ */ jsx("span", {
24
+ className: "font-medium tabular-nums",
25
+ children: value
26
+ })
27
+ ]
28
+ }), /* @__PURE__ */ jsx("button", {
29
+ type: "button",
30
+ className: "shrink-0 text-xs font-medium underline underline-offset-2",
31
+ onClick: () => {
32
+ navigator.clipboard?.writeText(value).then(() => setCopied(true));
33
+ },
34
+ children: copied ? storefrontTranslate("Copiado") : storefrontTranslate("Copiar")
35
+ })]
36
+ });
37
+ }
38
+ function BankAccountList({ accounts }) {
39
+ const storefrontTranslate = useStorefrontText();
40
+ if (accounts.length === 0) return null;
41
+ return /* @__PURE__ */ jsx("ul", {
42
+ className: "grid gap-2",
43
+ children: accounts.map((account) => {
44
+ const type = storefrontTranslate(ACCOUNT_TYPES[account.accountType] ?? account.accountType);
45
+ const currency = storefrontTranslate(CURRENCIES[account.currencyCode] ?? account.currencyCode);
46
+ return /* @__PURE__ */ jsxs("li", {
47
+ className: "grid gap-1 rounded-md border border-input bg-background px-3 py-2",
48
+ children: [
49
+ /* @__PURE__ */ jsxs("span", {
50
+ className: "font-medium",
51
+ children: [
52
+ account.bankName,
53
+ " · ",
54
+ storefrontTranslate("{value0} en {value1}", {
55
+ value0: type,
56
+ value1: currency
57
+ })
58
+ ]
59
+ }),
60
+ /* @__PURE__ */ jsx(CopyValue, {
61
+ label: storefrontTranslate("Cuenta"),
62
+ value: account.accountNumber
63
+ }),
64
+ account.interbankAccountNumber ? /* @__PURE__ */ jsx(CopyValue, {
65
+ label: "CCI",
66
+ value: account.interbankAccountNumber
67
+ }) : null,
68
+ /* @__PURE__ */ jsxs("span", {
69
+ className: "text-muted-foreground",
70
+ children: [
71
+ storefrontTranslate("Titular:"),
72
+ " ",
73
+ account.accountHolder
74
+ ]
75
+ })
76
+ ]
77
+ }, `${account.bankName}-${account.accountNumber}`);
78
+ })
79
+ });
80
+ }
81
+ export { BankAccountList };
@@ -0,0 +1,4 @@
1
+ import { PaymentBankAccount } from "@ecomiq/core";
2
+ export declare function BankAccountList({ accounts }: {
3
+ accounts: readonly PaymentBankAccount[];
4
+ }): import("react").JSX.Element | null;
@@ -0,0 +1,117 @@
1
+ import { useStorefrontText } from "./storefront-language-DrMjVbmk.js";
2
+ import { text } from "./form-BtQcbXlJ.js";
3
+ import { useEffect, useRef, useState } from "react";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ // src/captcha.tsx
6
+ const CAPTCHA_FIELDS = {
7
+ turnstile: "cf-turnstile-response",
8
+ recaptcha: "g-recaptcha-response"
9
+ };
10
+ const captchaScripts = /* @__PURE__ */ new Map();
11
+ function captchaResponse(form, provider) {
12
+ if (provider === "off") return "";
13
+ return text(form, CAPTCHA_FIELDS[provider]);
14
+ }
15
+ function loadCaptcha(provider) {
16
+ const saved = captchaScripts.get(provider);
17
+ if (saved) return saved;
18
+ const loading = new Promise((resolve, reject) => {
19
+ const script = document.createElement("script");
20
+ script.src = provider === "turnstile" ? "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit" : "https://www.google.com/recaptcha/api.js?render=explicit";
21
+ script.async = true;
22
+ script.defer = true;
23
+ script.onload = () => resolve();
24
+ script.onerror = () => {
25
+ captchaScripts.delete(provider);
26
+ script.remove();
27
+ reject(/* @__PURE__ */ new Error("Captcha unavailable"));
28
+ };
29
+ document.head.appendChild(script);
30
+ });
31
+ captchaScripts.set(provider, loading);
32
+ return loading;
33
+ }
34
+ function Captcha({ captcha, action, errorClass, busy }) {
35
+ const storefrontTranslate = useStorefrontText();
36
+ const element = useRef(null);
37
+ const widget = useRef(null);
38
+ const submitted = useRef(false);
39
+ const [token, setToken] = useState("");
40
+ const [failed, setFailed] = useState(false);
41
+ useEffect(() => {
42
+ if (captcha.provider === "off" || !captcha.siteKey || !captcha.isConfigured) return;
43
+ const provider = captcha.provider;
44
+ const siteKey = captcha.siteKey;
45
+ let active = true;
46
+ loadCaptcha(provider).then(async () => {
47
+ if (!active || !element.current) return;
48
+ const api = provider === "turnstile" ? window.turnstile : window.grecaptcha;
49
+ if (!api) throw new Error("Captcha unavailable");
50
+ if (api.ready) await new Promise((resolve) => api.ready(resolve));
51
+ if (!active || !element.current) return;
52
+ const id = api.render(element.current, {
53
+ sitekey: siteKey,
54
+ action: provider === "turnstile" ? action : void 0,
55
+ size: element.current.clientWidth < 304 ? "compact" : "normal",
56
+ callback: (value) => {
57
+ setToken(value);
58
+ setFailed(false);
59
+ },
60
+ "expired-callback": () => setToken(""),
61
+ "error-callback": () => {
62
+ setToken("");
63
+ setFailed(true);
64
+ }
65
+ });
66
+ widget.current = {
67
+ api,
68
+ id
69
+ };
70
+ }).catch(() => {
71
+ if (active) setFailed(true);
72
+ });
73
+ return () => {
74
+ active = false;
75
+ const rendered = widget.current;
76
+ if (rendered?.api.remove) rendered.api.remove(rendered.id);
77
+ else if (rendered) rendered.api.reset(rendered.id);
78
+ widget.current = null;
79
+ };
80
+ }, [
81
+ captcha.provider,
82
+ captcha.siteKey,
83
+ captcha.isConfigured,
84
+ action
85
+ ]);
86
+ useEffect(() => {
87
+ if (busy) {
88
+ submitted.current = true;
89
+ return;
90
+ }
91
+ if (!submitted.current) return;
92
+ submitted.current = false;
93
+ setToken("");
94
+ const rendered = widget.current;
95
+ if (rendered) rendered.api.reset(rendered.id);
96
+ }, [busy]);
97
+ if (captcha.provider === "off") return null;
98
+ if (!captcha.isConfigured) return /* @__PURE__ */ jsx("p", {
99
+ className: errorClass,
100
+ role: "alert",
101
+ children: storefrontTranslate("La tienda debe completar la configuración de verificación. Intenta más tarde.")
102
+ });
103
+ return /* @__PURE__ */ jsxs("div", { children: [
104
+ /* @__PURE__ */ jsx("div", { ref: element }),
105
+ /* @__PURE__ */ jsx("input", {
106
+ type: "hidden",
107
+ name: CAPTCHA_FIELDS[captcha.provider],
108
+ value: token
109
+ }),
110
+ failed ? /* @__PURE__ */ jsx("p", {
111
+ className: errorClass,
112
+ role: "alert",
113
+ children: storefrontTranslate("No se pudo cargar la verificación. Recarga la página e intenta de nuevo.")
114
+ }) : null
115
+ ] });
116
+ }
117
+ export { Captcha, captchaResponse };
@@ -0,0 +1,34 @@
1
+ export type CaptchaProvider = "off" | "turnstile" | "recaptcha";
2
+ export type CaptchaConfiguration = {
3
+ provider: CaptchaProvider;
4
+ siteKey: string | null;
5
+ isConfigured: boolean;
6
+ };
7
+ export type CaptchaProps = {
8
+ captcha: CaptchaConfiguration;
9
+ action: string;
10
+ errorClass: string;
11
+ busy: boolean;
12
+ };
13
+ type CaptchaOptions = {
14
+ sitekey: string;
15
+ action?: string;
16
+ size: "normal" | "compact";
17
+ callback: (token: string) => void;
18
+ "expired-callback": () => void;
19
+ "error-callback": () => void;
20
+ };
21
+ type CaptchaApi = {
22
+ ready?: (callback: () => void) => void;
23
+ render: (container: HTMLElement, options: CaptchaOptions) => string | number;
24
+ remove?: (id: string | number) => void;
25
+ reset: (id: string | number) => void;
26
+ };
27
+ declare global {
28
+ interface Window {
29
+ turnstile?: CaptchaApi;
30
+ grecaptcha?: CaptchaApi;
31
+ }
32
+ }
33
+ export declare function Captcha({ captcha, action, errorClass, busy }: CaptchaProps): import("react").JSX.Element | null;
34
+ export {};
@@ -0,0 +1,30 @@
1
+ import { createCookie } from "react-router";
2
+ // src/cart.ts
3
+ const cartCookie = createCookie("__Host-ecomiq-cart", {
4
+ httpOnly: true,
5
+ sameSite: "lax",
6
+ secure: true,
7
+ path: "/",
8
+ maxAge: 2592e3
9
+ });
10
+ async function cartToken(request) {
11
+ const value = await cartCookie.parse(request.headers.get("Cookie"));
12
+ return typeof value === "string" && value !== "" ? value : null;
13
+ }
14
+ const CLOSED = /* @__PURE__ */ new Set([
15
+ "completed",
16
+ "cancelled",
17
+ "expired",
18
+ "failed"
19
+ ]);
20
+ async function readCart(request, client) {
21
+ const token = await cartToken(request);
22
+ if (token === null) return null;
23
+ try {
24
+ const cart = await client.getCart({ cartToken: token });
25
+ return CLOSED.has(cart.status) ? null : cart;
26
+ } catch {
27
+ return null;
28
+ }
29
+ }
30
+ export { cartCookie, cartToken, readCart };
@@ -0,0 +1,116 @@
1
+ import { cartCookie, cartToken, readCart } from "./cart-D7DxZ2lD.js";
2
+ import { text } from "./form-BtQcbXlJ.js";
3
+ import { CART_INTENTS, isIntent } from "./form-contracts-BNpHaTGJ.js";
4
+ import { store } from "./store-8Q-_LFrQ.js";
5
+ import { data, redirect } from "react-router";
6
+ import { ApiError } from "@ecomiq/core";
7
+ // src/cart-actions.ts
8
+ const MAX_CART_QUANTITY = 999;
9
+ const MAX_REORDER_LINES = 20;
10
+ const ERRORS = {
11
+ "Catalog.InsufficientStock": "No hay stock suficiente para esa cantidad.",
12
+ "Inventory.InsufficientStock": "No hay stock suficiente para esa cantidad.",
13
+ "Catalog.VariantNotFound": "Este producto ya no está disponible.",
14
+ "Carts.QuantityOutOfRange": "Esa cantidad supera el máximo por pedido.",
15
+ "Carts.ItemNotFound": "Ese producto ya no está en tu carrito.",
16
+ "Checkout.InProgress": "Tu compra se está pagando. Termina el pago antes de cambiar el carrito."
17
+ };
18
+ const CLOSED = /* @__PURE__ */ new Set(["Carts.NotActive", "Carts.NotFound"]);
19
+ function failure(message, headers) {
20
+ return data({
21
+ ok: false,
22
+ message
23
+ }, {
24
+ status: 400,
25
+ headers
26
+ });
27
+ }
28
+ async function apiFailure(error) {
29
+ if (error.code !== void 0 && CLOSED.has(error.code)) return failure("Tu carrito anterior ya se cerró. Vuelve a agregar el producto.", { "Set-Cookie": await cartCookie.serialize("", { maxAge: 0 }) });
30
+ if (error.code?.startsWith("Modifiers.")) return failure("Revisa las opciones elegidas.");
31
+ return failure((error.code === void 0 ? void 0 : ERRORS[error.code]) ?? "No pudimos actualizar el carrito. Intenta de nuevo.");
32
+ }
33
+ function quantity(form, minimum) {
34
+ const value = Math.floor(Number(text(form, "quantity")));
35
+ if (!Number.isFinite(value) || value < minimum) return minimum;
36
+ return Math.min(999, value);
37
+ }
38
+ function reorderLines(form) {
39
+ const quantities = form.getAll("quantity");
40
+ return form.getAll("variantId").map((value, index) => {
41
+ const amount = Math.floor(Number(quantities[index]));
42
+ return {
43
+ variantId: typeof value === "string" ? value.trim() : "",
44
+ quantity: Number.isFinite(amount) && amount > 0 ? Math.min(999, amount) : 1
45
+ };
46
+ }).filter((line) => line.variantId !== "").slice(0, MAX_REORDER_LINES);
47
+ }
48
+ async function availableVariant(client, slug) {
49
+ if (!slug) return null;
50
+ return (await client.getProductBySlug(slug)).variants.find((candidate) => candidate.id !== null && candidate.isAvailable !== false)?.id ?? null;
51
+ }
52
+ function goesToCheckout(intent, form) {
53
+ if (intent === "buy") return true;
54
+ if (intent !== "add" && intent !== "quick") return false;
55
+ const next = text(form, "next");
56
+ return next === "/checkout" || next === "checkout";
57
+ }
58
+ async function loadCart(request, context) {
59
+ return { cart: await readCart(request, store(context)) };
60
+ }
61
+ async function cartAction(request, context) {
62
+ const client = store(context);
63
+ const form = await request.formData();
64
+ const intent = text(form, "intent");
65
+ if (!isIntent(CART_INTENTS, intent)) return failure("Acción no válida.");
66
+ try {
67
+ let cart = await readCart(request, client);
68
+ if (cart === null) {
69
+ const settings = await client.getSettings();
70
+ cart = await client.createCart({ currencyCode: settings.store.currency });
71
+ }
72
+ if (intent === "add" || intent === "buy" || intent === "quick") {
73
+ const variantId = text(form, "variantId") || await availableVariant(client, text(form, "product"));
74
+ if (!variantId) return failure("Este producto no está disponible.");
75
+ const added = intent === "quick" ? 1 : quantity(form, 1);
76
+ cart = await client.addCartItem({
77
+ cartToken: cart.token,
78
+ variantId,
79
+ quantity: added
80
+ });
81
+ } else if (intent === "reorder") {
82
+ let added = 0;
83
+ for (const line of reorderLines(form)) try {
84
+ cart = await client.addCartItem({
85
+ cartToken: cart.token,
86
+ variantId: line.variantId,
87
+ quantity: line.quantity
88
+ });
89
+ added += 1;
90
+ } catch (error) {
91
+ if (!(error instanceof ApiError) || error.status < 400 || error.status >= 500 || error.code !== void 0 && CLOSED.has(error.code)) throw error;
92
+ }
93
+ if (added === 0) return failure("Estos productos ya no están disponibles.");
94
+ } else if (intent === "update") cart = await client.updateCartItemQuantity({
95
+ cartToken: cart.token,
96
+ cartItemId: text(form, "itemId"),
97
+ quantity: quantity(form, 0)
98
+ });
99
+ else cart = await client.removeCartItem({
100
+ cartToken: cart.token,
101
+ cartItemId: text(form, "itemId")
102
+ });
103
+ let headers;
104
+ if (await cartToken(request) !== cart.token) headers = { "Set-Cookie": await cartCookie.serialize(cart.token) };
105
+ if (goesToCheckout(intent, form)) return redirect("/checkout", { headers });
106
+ if (intent === "reorder") return redirect("/cart", { headers });
107
+ return data({
108
+ ok: true,
109
+ message: ""
110
+ }, { headers });
111
+ } catch (error) {
112
+ if (error instanceof ApiError && error.status >= 400 && error.status < 500) return apiFailure(error);
113
+ return failure("No pudimos actualizar el carrito. Intenta de nuevo.");
114
+ }
115
+ }
116
+ export { MAX_CART_QUANTITY, cartAction, loadCart };
@@ -0,0 +1,15 @@
1
+ import { RouterContextProvider } from "react-router";
2
+ import { StorefrontCart } from "@ecomiq/core";
3
+ export declare const MAX_CART_QUANTITY = 999;
4
+ export type CartData = {
5
+ cart: StorefrontCart | null;
6
+ };
7
+ export type CartActionData = {
8
+ ok: true;
9
+ message: "";
10
+ } | {
11
+ ok: false;
12
+ message: string;
13
+ };
14
+ export declare function loadCart(request: Request, context: Readonly<RouterContextProvider>): Promise<CartData>;
15
+ export declare function cartAction(request: Request, context: Readonly<RouterContextProvider>): Promise<import("react-router").UNSAFE_DataWithResponseInit<CartActionData> | Response>;
@@ -0,0 +1,22 @@
1
+ import { createContext, useContext, useMemo, useState } from "react";
2
+ import { jsx } from "react/jsx-runtime";
3
+ // src/cart-drawer.tsx
4
+ const CartDrawerContext = createContext({
5
+ open: false,
6
+ setOpen: () => void 0
7
+ });
8
+ function CartDrawerProvider({ children }) {
9
+ const [open, setOpen] = useState(false);
10
+ const drawer = useMemo(() => ({
11
+ open,
12
+ setOpen
13
+ }), [open]);
14
+ return /* @__PURE__ */ jsx(CartDrawerContext.Provider, {
15
+ value: drawer,
16
+ children
17
+ });
18
+ }
19
+ function useCartDrawer() {
20
+ return useContext(CartDrawerContext);
21
+ }
22
+ export { CartDrawerProvider, useCartDrawer };
@@ -0,0 +1,9 @@
1
+ import { ReactNode } from "react";
2
+ export type CartDrawer = {
3
+ open: boolean;
4
+ setOpen: (open: boolean) => void;
5
+ };
6
+ export declare function CartDrawerProvider({ children }: {
7
+ children: ReactNode;
8
+ }): import("react").JSX.Element;
9
+ export declare function useCartDrawer(): CartDrawer;