@ecomiq/storefront 0.1.1 → 1.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.
package/dist/nextjs.js CHANGED
@@ -1,15 +1,24 @@
1
1
  import { z } from 'zod';
2
2
  import { Modal } from '@ecomiq/ui/components/modal';
3
- import { StorefrontPageDetailSchema as StorefrontPageDetailSchema$1, formatCurrency, useCart, useStore as useStore$1, useStorefrontAuthClient, useStorefrontSession, SignInForm, SignUpForm, storefrontStoreRecord } from '@ecomiq/storefront/react';
3
+ import { StorefrontPageDetailSchema as StorefrontPageDetailSchema$1, formatCurrency, useCart, useStore as useStore$1, useStorefrontAuthClient as useStorefrontAuthClient$1, useStorefrontSession as useStorefrontSession$1, SignInForm, SignUpForm, signInWithEcomiqCredentials } from '@ecomiq/storefront/react';
4
4
  import { toast, Toaster as Toaster$1 } from 'sonner';
5
5
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
6
6
  import { useTheme } from 'next-themes';
7
- import { headers } from 'next/headers';
8
7
  import { StorefrontSettingsSchema, StorefrontProductsParamsSchema, StorefrontProductsResponseSchema, StorefrontProductDetailSchema, StorefrontCollectionsResponseSchema, StorefrontCollectionDetailSchema, StorefrontPageListItemSchema, StorefrontPageDetailSchema, CartCreateRequestSchema, CartResponseSchema, CartBuyerRequestSchema, CartAddressesRequestSchema, CartCouponRequestSchema, CartCheckoutRequestSchema, CartCheckoutResponseSchema, CartPaymentReceiptResponseSchema, CartCheckoutReadResponseSchema, ShippingOptionsResponseSchema, CartShippingMethodPutRequestSchema, CartItemRequestSchema } from '@ecomiq/storefront/core';
9
8
  import { getCloudflareContext } from '@opennextjs/cloudflare';
10
- import React2, { createContext, useEffect, useState, useMemo, useCallback, useRef } from 'react';
11
- import { QueryClient, QueryClientProvider, useMutation, useQuery, useQueryClient, useInfiniteQuery } from '@tanstack/react-query';
9
+ import { QueryClient, hydrate, QueryClientProvider, useMutation, useQuery, useQueryClient, useInfiniteQuery } from '@tanstack/react-query';
10
+ import React2, { createContext, useRef, useEffect, useContext, useState, useMemo, useCallback } from 'react';
12
11
  import { create } from 'zustand';
12
+ import { persist } from 'zustand/middleware';
13
+ import { createAuthMiddleware, createAuthEndpoint, formCsrfMiddleware } from 'better-auth/api';
14
+ import { APIError } from 'better-auth';
15
+ import { setSessionCookie } from 'better-auth/cookies';
16
+ import { parseUserOutput } from 'better-auth/db';
17
+ import axios from 'axios';
18
+ import { existsSync } from 'fs';
19
+ import { createRequire } from 'module';
20
+ import { join } from 'path';
21
+ import { headers } from 'next/headers';
13
22
 
14
23
  var __defProp = Object.defineProperty;
15
24
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -309,34 +318,11 @@ var init_modals_factory = __esm({
309
318
  ModalsFactory = () => {
310
319
  const cart = useCart();
311
320
  const store = useStore$1();
312
- const authClient = useStorefrontAuthClient();
313
- const { refetch } = useStorefrontSession();
321
+ const authClient = useStorefrontAuthClient$1();
322
+ const { refetch } = useStorefrontSession$1();
314
323
  const handlerLogin = async (values) => {
315
- const settings = store.data;
316
- const record = storefrontStoreRecord(settings);
317
- const checkout = record?.checkoutSettings;
318
- if (!record?.id) {
319
- toast.error("Tienda no disponible");
320
- return;
321
- }
322
- const identityScope = checkout?.identityScope === "tenant" ? "tenant" : "store";
323
- const identityHeaders = {
324
- "x-Identity-Scope": identityScope,
325
- "x-Store-Id": record.id
326
- };
327
- if (record.tenantId) {
328
- identityHeaders["x-Tenant-Id"] = record.tenantId;
329
- }
330
324
  try {
331
- const response = await authClient.$fetch("/ecomiq-oauth/sign-in", {
332
- method: "POST",
333
- headers: identityHeaders,
334
- body: { email: values.email, password: values.password }
335
- });
336
- if (response.error) {
337
- toast.error(response.error.message);
338
- return;
339
- }
325
+ await signInWithEcomiqCredentials(authClient, store.data, values);
340
326
  const me = await getCustomerMe();
341
327
  const displayName = `${me.firstName} ${me.lastName}`.trim() || me.email;
342
328
  const updateUserResponse = await authClient.$fetch("/update-user", {
@@ -471,18 +457,19 @@ var init_ecomiq_shell_ui = __esm({
471
457
  }
472
458
  });
473
459
 
474
- // src/react/nextjs/actions/getHostName.ts
475
- init_store_api_client();
476
- async function getHostname() {
477
- const hd = await headers();
478
- const host = hd.get("host");
479
- const hostname = host?.replace(/^www\./, "") ?? null;
480
- const proto = hd.get("x-forwarded-proto") ?? hd.get("x-forwarded-ssl") ?? "http";
481
- const secure = proto === "https" || (hd.get("x-forwarded-ssl") ?? "") === "on";
482
- setServerRequestOrigin(`${secure ? "https" : "http"}://${hostname}`);
483
- return hostname;
460
+ // src/core/client/store-api-errors.ts
461
+ function isCartNotActiveApiError(error) {
462
+ if (!error || typeof error !== "object") return false;
463
+ const e = error;
464
+ if (e.status !== 400) return false;
465
+ const body = e.body;
466
+ const keys = body?.errors ? Object.keys(body.errors) : [];
467
+ return keys.includes("Carts.NotActive");
484
468
  }
485
469
 
470
+ // src/core/client/index.ts
471
+ init_store_api_client();
472
+
486
473
  // src/core/client/store-api-store.service.ts
487
474
  init_store_api_client();
488
475
  async function getStorefrontSettings(subdomain, cacheKey) {
@@ -521,8 +508,17 @@ function resolveStorefrontSettingsHost(hostHeader) {
521
508
  }
522
509
  return host;
523
510
  }
511
+ function buildStorefrontKvSnapshotKeyFromStoreKey(storeKey) {
512
+ const key = storeKey.trim();
513
+ if (!key) {
514
+ throw Object.assign(new Error("Store key vac\xEDo para KV"), { status: 400 });
515
+ }
516
+ return `storefront:snapshots:store:${key}`;
517
+ }
524
518
  function buildStorefrontKvSnapshotKey(hostHeader) {
525
- return `storefront:snapshots:store:${resolveStorefrontSettingsHost(hostHeader)}`;
519
+ return buildStorefrontKvSnapshotKeyFromStoreKey(
520
+ resolveStorefrontSettingsHost(hostHeader)
521
+ );
526
522
  }
527
523
  function getKvNamespace(env) {
528
524
  if (!env || typeof env !== "object") return void 0;
@@ -532,7 +528,7 @@ function getKvNamespace(env) {
532
528
  }
533
529
  return ns;
534
530
  }
535
- async function getStorefrontSettingsFromKv(hostHeader) {
531
+ async function getStorefrontSettingsFromKvByStoreKey(storeKey) {
536
532
  const { env } = await getCloudflareContext({ async: true });
537
533
  const kv = getKvNamespace(env);
538
534
  if (!kv) {
@@ -541,7 +537,7 @@ async function getStorefrontSettingsFromKv(hostHeader) {
541
537
  );
542
538
  throw Object.assign(err, { status: 500 });
543
539
  }
544
- const cacheKey = buildStorefrontKvSnapshotKey(hostHeader);
540
+ const cacheKey = buildStorefrontKvSnapshotKeyFromStoreKey(storeKey);
545
541
  const raw = await kv.get(cacheKey, { type: "json" });
546
542
  if (raw == null) {
547
543
  const err = new Error("Store settings not found in KV");
@@ -549,171 +545,12 @@ async function getStorefrontSettingsFromKv(hostHeader) {
549
545
  }
550
546
  return StorefrontSettingsSchema.parse(raw);
551
547
  }
552
-
553
- // src/react/shell/lib/public-store-settings-source.ts
554
- var ENV_PUBLIC_STORE_SETTINGS_SOURCE = "PUBLIC_STORE_SETTINGS_SOURCE";
555
- function resolvePublicStoreSettingsSource(envValue = process.env[ENV_PUBLIC_STORE_SETTINGS_SOURCE]) {
556
- const v = (envValue ?? "api").trim().toLowerCase();
557
- return v === "kv" ? "kv" : "api";
558
- }
559
-
560
- // src/react/nextjs/actions/getPublicStore.ts
561
- init_store_api_client();
562
- async function getPublicStore(options) {
563
- const headerList = await headers();
564
- const host = headerList.get("host");
565
- if (!host?.trim()) {
566
- return {
567
- data: null,
568
- error: {
569
- message: "Host is required",
570
- statusCode: 400,
571
- isNotFound: true,
572
- subdomain: void 0
573
- }
574
- };
575
- }
576
- const storeHost = normalizeStoreHost(host);
577
- const proto = headerList.get("x-forwarded-proto") ?? headerList.get("x-forwarded-ssl") ?? "http";
578
- const secure = proto === "https" || (headerList.get("x-forwarded-ssl") ?? "") === "on";
579
- setServerRequestOrigin(`${secure ? "https" : "http"}://${host}`);
580
- const source = options?.source ?? resolvePublicStoreSettingsSource();
581
- let storeDomain;
582
- try {
583
- storeDomain = resolveStorefrontSettingsHost(host);
584
- setServerStoreId(null);
585
- const data = source === "kv" ? await getStorefrontSettingsFromKv(storeHost) : await getStorefrontSettings(storeDomain, `storefront-settings-${storeDomain}`);
586
- setServerStoreId(data.store.id);
587
- return { data, error: null };
588
- } catch (error) {
589
- const err = error;
590
- const statusCode = err.status ?? 500;
591
- return {
592
- data: null,
593
- error: {
594
- message: err.message ?? "Error fetching store settings",
595
- statusCode,
596
- isNotFound: statusCode === 404,
597
- subdomain: storeDomain
598
- }
599
- };
600
- }
601
- }
602
- var StorefrontAuthContext = createContext(null);
603
- function StorefrontAuthProvider({
604
- authClient,
605
- children
606
- }) {
607
- return /* @__PURE__ */ jsx(StorefrontAuthContext.Provider, { value: authClient, children });
608
- }
609
- var useStore = create()(
610
- (set) => ({
611
- data: null,
612
- drawers: {
613
- cart: false,
614
- search: false,
615
- menu: false,
616
- filter: false
617
- },
618
- openDrawer: (drawer) => set((state) => ({
619
- drawers: {
620
- ...state.drawers,
621
- [drawer]: true
622
- }
623
- })),
624
- closeDrawer: (drawer) => set((state) => ({
625
- drawers: {
626
- ...state.drawers,
627
- [drawer]: false
628
- }
629
- })),
630
- toggleDrawer: (drawer) => set((state) => ({
631
- drawers: {
632
- ...state.drawers,
633
- [drawer]: !state.drawers[drawer]
634
- }
635
- })),
636
- closeAllDrawers: () => set((state) => ({
637
- drawers: Object.keys(state.drawers).reduce(
638
- (acc, key) => ({ ...acc, [key]: false }),
639
- {}
640
- )
641
- })),
642
- modals: {
643
- login: false,
644
- signup: false
645
- },
646
- openModal: (modal) => set((state) => ({
647
- modals: {
648
- ...state.modals,
649
- login: false,
650
- signup: false,
651
- [modal]: true
652
- }
653
- })),
654
- closeModal: (modal) => set((state) => ({
655
- modals: {
656
- ...state.modals,
657
- login: false,
658
- signup: false,
659
- [modal]: false
660
- }
661
- })),
662
- toggleModal: (modal) => set((state) => ({
663
- modals: {
664
- ...state.modals,
665
- [modal]: !state.modals[modal]
666
- }
667
- })),
668
- closeAllModals: () => set((state) => ({
669
- modals: Object.keys(state.modals).reduce(
670
- (acc, key) => ({ ...acc, [key]: false }),
671
- {}
672
- )
673
- }))
674
- })
675
- );
676
-
677
- // src/react/shell/providers/EcomiqProvider.tsx
678
- init_store_api_client();
679
- function EcomiqProvider({
680
- store,
681
- authClient,
682
- children,
683
- withShellUi = false
684
- }) {
685
- useStore.setState({ data: store ?? null });
686
- useEffect(() => {
687
- setClientStoreId(store?.store.id ?? null);
688
- return () => setClientStoreId(null);
689
- }, [store]);
690
- const [queryClient] = useState(() => new QueryClient());
691
- return /* @__PURE__ */ jsx(StorefrontAuthProvider, { authClient, children: /* @__PURE__ */ jsxs(QueryClientProvider, { client: queryClient, children: [
692
- children,
693
- withShellUi ? /* @__PURE__ */ jsx(EcomiqShellUi2, {}) : null
694
- ] }) });
695
- }
696
- function EcomiqShellUi2() {
697
- const [ui, setUi] = useState(null);
698
- useEffect(() => {
699
- void Promise.resolve().then(() => (init_ecomiq_shell_ui(), ecomiq_shell_ui_exports)).then((mod) => setUi(() => mod.EcomiqShellUi));
700
- }, []);
701
- return ui ? React2.createElement(ui) : null;
702
- }
703
-
704
- // src/core/client/store-api-errors.ts
705
- function isCartNotActiveApiError(error) {
706
- if (!error || typeof error !== "object") return false;
707
- const e = error;
708
- if (e.status !== 400) return false;
709
- const body = e.body;
710
- const keys = body?.errors ? Object.keys(body.errors) : [];
711
- return keys.includes("Carts.NotActive");
548
+ async function getStorefrontSettingsFromKv(hostHeader) {
549
+ return getStorefrontSettingsFromKvByStoreKey(
550
+ resolveStorefrontSettingsHost(hostHeader)
551
+ );
712
552
  }
713
553
 
714
- // src/core/client/index.ts
715
- init_store_api_client();
716
-
717
554
  // src/core/client/store-api-product.service.ts
718
555
  init_store_api_client();
719
556
  async function getStorefrontProducts(params, cacheKey) {
@@ -1154,6 +991,100 @@ async function getSharedOrderDraft(shareToken) {
1154
991
  );
1155
992
  return OrderDraftSchema.parse(response);
1156
993
  }
994
+ var defaultOptions = {
995
+ queries: {
996
+ staleTime: 60 * 1e3
997
+ }
998
+ };
999
+ function QueryProvider({ children, dehydratedState }) {
1000
+ const queryClientRef = useRef(null);
1001
+ const hydratedRef = useRef(false);
1002
+ if (queryClientRef.current === null) {
1003
+ queryClientRef.current = new QueryClient({ defaultOptions });
1004
+ }
1005
+ const queryClient = queryClientRef.current;
1006
+ useEffect(() => {
1007
+ if (dehydratedState && !hydratedRef.current) {
1008
+ hydrate(queryClient, dehydratedState);
1009
+ hydratedRef.current = true;
1010
+ }
1011
+ }, [queryClient, dehydratedState]);
1012
+ return /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children });
1013
+ }
1014
+ var StorefrontAuthContext = createContext(null);
1015
+ function StorefrontAuthProvider({
1016
+ authClient,
1017
+ children
1018
+ }) {
1019
+ return /* @__PURE__ */ jsx(StorefrontAuthContext.Provider, { value: authClient, children });
1020
+ }
1021
+ function useStorefrontAuthClient() {
1022
+ const client = useContext(StorefrontAuthContext);
1023
+ if (!client) {
1024
+ throw new Error(
1025
+ "useStorefrontAuthClient debe usarse dentro de StorefrontAuthProvider (o EcomiqProvider con authClient)."
1026
+ );
1027
+ }
1028
+ return client;
1029
+ }
1030
+ function storefrontSessionEndsSoon(expiresAt, skewMs = 12e4) {
1031
+ if (!expiresAt) return false;
1032
+ return Date.now() + skewMs >= expiresAt.getTime();
1033
+ }
1034
+ var storefrontAccessTokenNeedsRefresh = storefrontSessionEndsSoon;
1035
+ function parseStorefrontSessionExpires(at) {
1036
+ if (!at) return void 0;
1037
+ if (at instanceof Date) return Number.isNaN(at.getTime()) ? void 0 : at;
1038
+ if (typeof at === "string") {
1039
+ const d = new Date(at);
1040
+ return Number.isNaN(d.getTime()) ? void 0 : d;
1041
+ }
1042
+ return void 0;
1043
+ }
1044
+ function mapSessionUser(raw) {
1045
+ if (!raw || typeof raw !== "object") return null;
1046
+ const u = raw;
1047
+ const explicitFirst = typeof u.firstName === "string" ? u.firstName : typeof u.given_name === "string" ? u.given_name : "";
1048
+ const explicitLast = typeof u.lastName === "string" ? u.lastName : typeof u.family_name === "string" ? u.family_name : "";
1049
+ if (explicitFirst || explicitLast) {
1050
+ return {
1051
+ id: String(u.id ?? ""),
1052
+ email: String(u.email ?? ""),
1053
+ firstName: explicitFirst,
1054
+ lastName: explicitLast,
1055
+ name: typeof u.name === "string" ? u.name : void 0,
1056
+ phone: typeof u.phone === "string" ? u.phone : void 0
1057
+ };
1058
+ }
1059
+ const emailLocal = String(u.email ?? "").split("@")[0] ?? "";
1060
+ const full = typeof u.name === "string" ? u.name.trim() : emailLocal.trim();
1061
+ const parts = full ? full.split(/\s+/) : [];
1062
+ return {
1063
+ id: String(u.id ?? ""),
1064
+ email: String(u.email ?? ""),
1065
+ firstName: parts[0] ?? "",
1066
+ lastName: parts.slice(1).join(" "),
1067
+ name: typeof u.name === "string" ? u.name : void 0,
1068
+ phone: typeof u.phone === "string" ? u.phone : void 0
1069
+ };
1070
+ }
1071
+ function useStorefrontSession() {
1072
+ const authClient = useStorefrontAuthClient();
1073
+ const session = authClient.useSession();
1074
+ const user = mapSessionUser(session.data?.user);
1075
+ const storefrontSession = session.data?.session;
1076
+ const sessionExpiresAt = parseStorefrontSessionExpires(storefrontSession?.expiresAt);
1077
+ return {
1078
+ session,
1079
+ user,
1080
+ sessionExpiresAt,
1081
+ isAuthenticated: Boolean(session.data?.user),
1082
+ isPending: session.isPending,
1083
+ error: session.error,
1084
+ refetch: session.refetch,
1085
+ signOut: () => authClient.signOut()
1086
+ };
1087
+ }
1157
1088
  function useStoreProducts() {
1158
1089
  const useStorefrontProductsList = (params) => {
1159
1090
  return useQuery({
@@ -1369,57 +1300,749 @@ function useStoreCart() {
1369
1300
  useRemoveCartCoupon
1370
1301
  };
1371
1302
  }
1372
- function isRenderableCmsPage(p) {
1373
- if (!p.status) return true;
1374
- return p.status === "published";
1375
- }
1376
- function findStorefrontCmsPageBySlug(settings, slug) {
1377
- const pages = settings?.cms?.pages;
1378
- if (!pages?.length) return null;
1379
- const raw = pages.find(
1380
- (p) => p.slug === slug && isRenderableCmsPage(p)
1381
- );
1382
- if (!raw) return null;
1383
- const parsed = StorefrontPageDetailSchema$1.safeParse(raw);
1384
- return parsed.success ? parsed.data : null;
1385
- }
1386
- function buildOptionGroups(variants) {
1387
- const map = /* @__PURE__ */ new Map();
1388
- for (const v of variants) {
1389
- for (const opt of v.options ?? []) {
1390
- const key = opt.key;
1391
- const name = opt.name ?? key;
1392
- if (!map.has(key)) map.set(key, /* @__PURE__ */ new Map());
1393
- map.get(key).set(opt.value, name);
1394
- }
1395
- }
1396
- return Array.from(map.entries()).map(([key, valueMap]) => ({
1397
- key,
1398
- name: Array.from(valueMap.values())[0] ?? key,
1399
- values: Array.from(valueMap.keys())
1400
- }));
1401
- }
1402
- function findVariant(variants, selected, hasConfigurableOptions) {
1403
- if (!hasConfigurableOptions && variants.length > 0) {
1404
- return variants[0] ?? null;
1405
- }
1406
- if (Object.keys(selected).length === 0) return null;
1407
- for (const v of variants) {
1408
- const opts = v.options ?? [];
1409
- const match = opts.length > 0 && opts.every((o) => selected[o.key] === o.value);
1410
- if (match) return v;
1411
- }
1412
- return null;
1413
- }
1414
- function getInitialOptions(variants, optionGroups) {
1415
- const first = variants[0];
1416
- if (!first) return {};
1417
- const opts = first.options ?? [];
1418
- const selected = {};
1419
- for (const g of optionGroups) {
1420
- const o = opts.find((x) => x.key === g.key);
1421
- if (o) selected[g.key] = o.value;
1422
- }
1303
+ var useStore = create()(
1304
+ (set) => ({
1305
+ data: null,
1306
+ drawers: {
1307
+ cart: false,
1308
+ search: false,
1309
+ menu: false,
1310
+ filter: false
1311
+ },
1312
+ openDrawer: (drawer) => set((state) => ({
1313
+ drawers: {
1314
+ ...state.drawers,
1315
+ [drawer]: true
1316
+ }
1317
+ })),
1318
+ closeDrawer: (drawer) => set((state) => ({
1319
+ drawers: {
1320
+ ...state.drawers,
1321
+ [drawer]: false
1322
+ }
1323
+ })),
1324
+ toggleDrawer: (drawer) => set((state) => ({
1325
+ drawers: {
1326
+ ...state.drawers,
1327
+ [drawer]: !state.drawers[drawer]
1328
+ }
1329
+ })),
1330
+ closeAllDrawers: () => set((state) => ({
1331
+ drawers: Object.keys(state.drawers).reduce(
1332
+ (acc, key) => ({ ...acc, [key]: false }),
1333
+ {}
1334
+ )
1335
+ })),
1336
+ modals: {
1337
+ login: false,
1338
+ signup: false
1339
+ },
1340
+ openModal: (modal) => set((state) => ({
1341
+ modals: {
1342
+ ...state.modals,
1343
+ login: false,
1344
+ signup: false,
1345
+ [modal]: true
1346
+ }
1347
+ })),
1348
+ closeModal: (modal) => set((state) => ({
1349
+ modals: {
1350
+ ...state.modals,
1351
+ login: false,
1352
+ signup: false,
1353
+ [modal]: false
1354
+ }
1355
+ })),
1356
+ toggleModal: (modal) => set((state) => ({
1357
+ modals: {
1358
+ ...state.modals,
1359
+ [modal]: !state.modals[modal]
1360
+ }
1361
+ })),
1362
+ closeAllModals: () => set((state) => ({
1363
+ modals: Object.keys(state.modals).reduce(
1364
+ (acc, key) => ({ ...acc, [key]: false }),
1365
+ {}
1366
+ )
1367
+ }))
1368
+ })
1369
+ );
1370
+ var useCart2 = create()(
1371
+ persist(
1372
+ (set) => ({
1373
+ count: 0,
1374
+ cart: [],
1375
+ cartTokenId: null,
1376
+ setCartTokenId: (token) => set((state) => ({ ...state, cartTokenId: token })),
1377
+ checkoutStep: null,
1378
+ setCheckoutStep: (step) => set((state) => ({ ...state, checkoutStep: step })),
1379
+ checkoutCustomerLocal: null,
1380
+ setCheckoutCustomerLocal: (customer) => set((state) => {
1381
+ if (!state.cartTokenId) return state;
1382
+ return {
1383
+ checkoutCustomerLocal: {
1384
+ cartTokenId: state.cartTokenId,
1385
+ customer
1386
+ }
1387
+ };
1388
+ }),
1389
+ clearCheckoutCustomerLocal: () => set(() => ({ checkoutCustomerLocal: null })),
1390
+ addCart: (item) => set((state) => {
1391
+ const existingItem = state.cart.find(
1392
+ (cartItem) => cartItem.id === item.id
1393
+ );
1394
+ if (existingItem) {
1395
+ const newQuantity = Math.max(0, existingItem.quantity) + 1;
1396
+ const cart2 = state.cart.map(
1397
+ (cartItem) => cartItem.id === item.id ? { ...cartItem, quantity: newQuantity } : cartItem
1398
+ );
1399
+ const count2 = cart2.reduce((sum, i) => sum + Math.max(0, i.quantity), 0);
1400
+ return { count: count2, cart: cart2 };
1401
+ }
1402
+ const newItem = {
1403
+ ...item,
1404
+ quantity: Math.max(1, Math.max(0, item.quantity ?? 1))
1405
+ };
1406
+ const cart = [...state.cart, newItem];
1407
+ const count = cart.reduce((sum, i) => sum + Math.max(0, i.quantity), 0);
1408
+ return { count, cart };
1409
+ }),
1410
+ removeCart: (id) => set((state) => {
1411
+ const existingItem = state.cart.find((item) => item.id === id);
1412
+ if (!existingItem) return state;
1413
+ if (existingItem.quantity > 1) {
1414
+ const newQuantity = Math.max(0, existingItem.quantity - 1);
1415
+ const cart2 = state.cart.map(
1416
+ (cartItem) => cartItem.id === id ? { ...cartItem, quantity: newQuantity } : cartItem
1417
+ );
1418
+ const count2 = cart2.reduce((sum, i) => sum + Math.max(0, i.quantity), 0);
1419
+ return { count: count2, cart: cart2 };
1420
+ }
1421
+ const cart = state.cart.filter((item) => item.id !== id);
1422
+ const count = Math.max(0, cart.reduce((sum, i) => sum + Math.max(0, i.quantity), 0));
1423
+ return { count, cart };
1424
+ }),
1425
+ clearCartSession: () => set(() => ({
1426
+ count: 0,
1427
+ cart: [],
1428
+ cartTokenId: null,
1429
+ checkoutStep: null,
1430
+ checkoutCustomerLocal: null
1431
+ }))
1432
+ }),
1433
+ { name: "cart-storage" }
1434
+ )
1435
+ );
1436
+ init_store_api_client();
1437
+ function EcomiqProvider({
1438
+ store,
1439
+ authClient,
1440
+ children,
1441
+ withShellUi = false
1442
+ }) {
1443
+ useStore.setState({ data: store ?? null });
1444
+ useEffect(() => {
1445
+ setClientStoreId(store?.store.id ?? null);
1446
+ return () => setClientStoreId(null);
1447
+ }, [store]);
1448
+ const [queryClient] = useState(() => new QueryClient());
1449
+ const tree = /* @__PURE__ */ jsxs(QueryClientProvider, { client: queryClient, children: [
1450
+ children,
1451
+ withShellUi ? /* @__PURE__ */ jsx(EcomiqShellUi2, {}) : null
1452
+ ] });
1453
+ if (!authClient) return tree;
1454
+ return /* @__PURE__ */ jsx(StorefrontAuthProvider, { authClient, children: tree });
1455
+ }
1456
+ function EcomiqShellUi2() {
1457
+ const [ui, setUi] = useState(null);
1458
+ useEffect(() => {
1459
+ void Promise.resolve().then(() => (init_ecomiq_shell_ui(), ecomiq_shell_ui_exports)).then((mod) => setUi(() => mod.EcomiqShellUi));
1460
+ }, []);
1461
+ return ui ? React2.createElement(ui) : null;
1462
+ }
1463
+
1464
+ // src/core/constants/storefront-ecomiq-oauth-cookie.ts
1465
+ var STOREFRONT_ECOMIQ_OAUTH_COOKIE_NAME = "ecomiq_sf_oauth_at";
1466
+ var STOREFRONT_ECOMIQ_OAUTH_COOKIE_PATH = "/api/store";
1467
+
1468
+ // src/react/auth/ecomiq-oauth-password-constants.ts
1469
+ var PLUGIN_ID = "ecomiq-oauth-password";
1470
+ var ECOMIQ_OAUTH_TOKEN_URL = "https://auth.ecomiq.pe/connect/token";
1471
+ var ECOMIQ_OAUTH_USERINFO_URL = "https://auth.ecomiq.pe/connect/userinfo";
1472
+ var tokenResponseSchema = z.object({
1473
+ access_token: z.string().min(1),
1474
+ token_type: z.string().optional(),
1475
+ /** Segundos hasta expiración (Ecomiq envía ej. `86399`). Sincroniza la sesión Better Auth con el bearer. */
1476
+ expires_in: z.coerce.number().int().positive().optional()
1477
+ });
1478
+ var userInfoSchema = z.object({
1479
+ sub: z.string(),
1480
+ email: z.string().email().optional(),
1481
+ family_name: z.string().optional(),
1482
+ given_name: z.string().optional(),
1483
+ iss: z.string().optional(),
1484
+ aud: z.union([z.string(), z.array(z.string())]).optional()
1485
+ });
1486
+ var signInBody = z.object({
1487
+ email: z.string().email(),
1488
+ password: z.string().min(1),
1489
+ callbackURL: z.string().optional(),
1490
+ rememberMe: z.boolean().optional().default(true)
1491
+ });
1492
+
1493
+ // src/react/auth/ecomiq-oauth-password-plugin.ts
1494
+ function ecomiqOAuthPassword() {
1495
+ const secureCookie = true;
1496
+ const clearStorefrontOAuthCookieMiddleware = createAuthMiddleware(async (ctx) => {
1497
+ ctx.setCookie(STOREFRONT_ECOMIQ_OAUTH_COOKIE_NAME, "", {
1498
+ path: STOREFRONT_ECOMIQ_OAUTH_COOKIE_PATH,
1499
+ httpOnly: true,
1500
+ sameSite: "lax",
1501
+ secure: secureCookie,
1502
+ maxAge: 0
1503
+ });
1504
+ });
1505
+ return {
1506
+ id: PLUGIN_ID,
1507
+ schema: {
1508
+ user: {
1509
+ fields: {
1510
+ firstName: { type: "string", required: false },
1511
+ lastName: { type: "string", required: false },
1512
+ phone: { type: "string", required: false }
1513
+ }
1514
+ }
1515
+ },
1516
+ hooks: {
1517
+ after: [
1518
+ {
1519
+ matcher: (hookCtx) => hookCtx.path === "/sign-out",
1520
+ handler: clearStorefrontOAuthCookieMiddleware
1521
+ }
1522
+ ]
1523
+ },
1524
+ endpoints: {
1525
+ signIn: createAuthEndpoint(
1526
+ "/ecomiq-oauth/sign-in",
1527
+ {
1528
+ method: "POST",
1529
+ use: [formCsrfMiddleware],
1530
+ body: signInBody
1531
+ },
1532
+ async (ctx) => {
1533
+ const { email: loginEmail, password, callbackURL, rememberMe } = ctx.body;
1534
+ const tokens = await exchangePasswordGrant(loginEmail, password, {
1535
+ identityScope: ctx.getHeader("x-Identity-Scope"),
1536
+ tenantId: ctx.getHeader("x-Tenant-Id"),
1537
+ storeId: ctx.getHeader("x-Store-Id")
1538
+ });
1539
+ const profile = await fetchUserInfo(tokens.access_token);
1540
+ const profileEmail = profile.email?.toLowerCase().trim();
1541
+ const canonicalEmail = profileEmail ?? loginEmail.toLowerCase().trim();
1542
+ if (profileEmail && profileEmail !== loginEmail.toLowerCase().trim()) {
1543
+ throw APIError.from("UNAUTHORIZED", {
1544
+ code: "ECOMIQ_EMAIL_MISMATCH",
1545
+ message: "El correo del perfil OAuth no coincide con el inicio de sesi\xF3n"
1546
+ });
1547
+ }
1548
+ const displayName = displayNameFromUserInfo(profile, canonicalEmail);
1549
+ const existing = await ctx.context.internalAdapter.findUserByEmail(canonicalEmail);
1550
+ let user = existing?.user;
1551
+ if (!user) {
1552
+ user = await ctx.context.internalAdapter.createUser({
1553
+ email: canonicalEmail,
1554
+ name: displayName,
1555
+ emailVerified: true
1556
+ });
1557
+ } else if (user.name !== displayName) {
1558
+ await ctx.context.internalAdapter.updateUser(user.id, { name: displayName });
1559
+ user = { ...user, name: displayName };
1560
+ }
1561
+ const oauthSessionExpires = tokens.expires_in !== void 0 && tokens.expires_in > 0 ? new Date(Date.now() + tokens.expires_in * 1e3) : void 0;
1562
+ const session = await ctx.context.internalAdapter.createSession(
1563
+ user.id,
1564
+ rememberMe === false,
1565
+ oauthSessionExpires ? { expiresAt: oauthSessionExpires } : {},
1566
+ Boolean(oauthSessionExpires)
1567
+ );
1568
+ if (!session) {
1569
+ throw APIError.from("INTERNAL_SERVER_ERROR", {
1570
+ code: "ECOMIQ_SESSION_FAILED",
1571
+ message: "No se pudo crear la sesi\xF3n"
1572
+ });
1573
+ }
1574
+ await setSessionCookie(ctx, { session, user }, rememberMe === false);
1575
+ const oauthMaxAgeSec = oauthSessionExpires !== void 0 ? Math.max(60, Math.ceil((oauthSessionExpires.getTime() - Date.now()) / 1e3)) : tokens.expires_in !== void 0 && tokens.expires_in > 0 ? tokens.expires_in : 60 * 60 * 24;
1576
+ await ctx.setCookie(STOREFRONT_ECOMIQ_OAUTH_COOKIE_NAME, tokens.access_token, {
1577
+ path: STOREFRONT_ECOMIQ_OAUTH_COOKIE_PATH,
1578
+ httpOnly: true,
1579
+ sameSite: "lax",
1580
+ secure: secureCookie,
1581
+ maxAge: oauthMaxAgeSec
1582
+ });
1583
+ if (callbackURL) ctx.setHeader("Location", callbackURL);
1584
+ return ctx.json({
1585
+ redirect: !!callbackURL,
1586
+ token: session.token,
1587
+ url: callbackURL,
1588
+ user: parseUserOutput(ctx.context.options, user)
1589
+ });
1590
+ }
1591
+ )
1592
+ }
1593
+ };
1594
+ }
1595
+ function displayNameFromUserInfo(profile, fallbackEmail) {
1596
+ const parts = [profile.given_name?.trim(), profile.family_name?.trim()].filter(Boolean);
1597
+ const joined = parts.join(" ").trim();
1598
+ if (joined) return joined;
1599
+ return fallbackEmail.split("@")[0] || "Cliente";
1600
+ }
1601
+ async function fetchUserInfo(accessToken) {
1602
+ const res = await fetch(ECOMIQ_OAUTH_USERINFO_URL, {
1603
+ method: "GET",
1604
+ headers: {
1605
+ Authorization: `Bearer ${accessToken}`,
1606
+ accept: "application/json"
1607
+ }
1608
+ });
1609
+ const text = await res.text();
1610
+ let json;
1611
+ try {
1612
+ json = text ? JSON.parse(text) : {};
1613
+ } catch {
1614
+ throw APIError.from("UNAUTHORIZED", {
1615
+ code: "ECOMIQ_USERINFO_INVALID_BODY",
1616
+ message: "Respuesta inv\xE1lida del endpoint userinfo"
1617
+ });
1618
+ }
1619
+ if (!res.ok) {
1620
+ const err = json;
1621
+ throw APIError.from("UNAUTHORIZED", {
1622
+ code: "ECOMIQ_USERINFO_REJECTED",
1623
+ message: err.error_description ?? err.error ?? "No se pudo obtener el perfil del usuario"
1624
+ });
1625
+ }
1626
+ const parsed = userInfoSchema.safeParse(json);
1627
+ if (!parsed.success) {
1628
+ throw APIError.from("UNAUTHORIZED", {
1629
+ code: "ECOMIQ_USERINFO_SHAPE",
1630
+ message: "Perfil OAuth con formato inv\xE1lido"
1631
+ });
1632
+ }
1633
+ return parsed.data;
1634
+ }
1635
+ async function exchangePasswordGrant(username, password, context) {
1636
+ const clientId = process.env.ECOMIQ_AUTH_CLIENT_ID ?? "";
1637
+ const clientSecret = process.env.ECOMIQ_AUTH_CLIENT_SECRET ?? "";
1638
+ const scope = process.env.ECOMIQ_AUTH_SCOPE ?? "";
1639
+ if (!clientId || !clientSecret) {
1640
+ throw APIError.from("INTERNAL_SERVER_ERROR", {
1641
+ code: "ECOMIQ_AUTH_NOT_CONFIGURED",
1642
+ message: "OAuth Ecomiq no configurado: ECOMIQ_AUTH_CLIENT_ID y ECOMIQ_AUTH_CLIENT_SECRET."
1643
+ });
1644
+ }
1645
+ const body = new URLSearchParams({
1646
+ grant_type: "password",
1647
+ username,
1648
+ password,
1649
+ scope,
1650
+ client_id: clientId,
1651
+ client_secret: clientSecret
1652
+ });
1653
+ const identityScope = context.identityScope;
1654
+ const tenantId = context.tenantId;
1655
+ const storeId = context.storeId;
1656
+ const headers3 = new Headers({
1657
+ "content-type": "application/x-www-form-urlencoded",
1658
+ accept: "application/json"
1659
+ });
1660
+ if (identityScope?.trim()) {
1661
+ headers3.set("x-Identity-Scope", identityScope.trim());
1662
+ }
1663
+ if (tenantId?.trim()) {
1664
+ headers3.set("x-Tenant-Id", tenantId.trim());
1665
+ }
1666
+ if (storeId?.trim()) {
1667
+ headers3.set("x-Store-Id", storeId.trim());
1668
+ }
1669
+ let res;
1670
+ let text;
1671
+ try {
1672
+ res = await fetch(ECOMIQ_OAUTH_TOKEN_URL, {
1673
+ method: "POST",
1674
+ headers: headers3,
1675
+ body
1676
+ });
1677
+ text = await res.text();
1678
+ } catch (e) {
1679
+ throw mapTokenEndpointFetchFailure(e);
1680
+ }
1681
+ const json = safeParseJson(text);
1682
+ if (!res.ok) {
1683
+ throwBadRequestFromOAuth(json, res.statusText || text || `HTTP ${res.status}`);
1684
+ }
1685
+ const parsed = tokenResponseSchema.safeParse(json);
1686
+ if (!parsed.success) {
1687
+ throwBadRequestFromOAuth(json, "Token de acceso no recibido");
1688
+ }
1689
+ return parsed.data;
1690
+ }
1691
+ function safeParseJson(text) {
1692
+ try {
1693
+ return text ? JSON.parse(text) : {};
1694
+ } catch {
1695
+ return {};
1696
+ }
1697
+ }
1698
+ function throwBadRequestFromOAuth(payload, fallbackMessage) {
1699
+ const err = payload;
1700
+ const errorCode = typeof err.error === "string" && err.error.trim() ? err.error.trim() : "invalid_grant";
1701
+ const errorMessage = typeof err.error_description === "string" && err.error_description.trim() ? err.error_description.trim() : typeof err.error === "string" && err.error.trim() ? err.error.trim() : fallbackMessage.trim() || "Credenciales invalidas.";
1702
+ throw APIError.from("BAD_REQUEST", {
1703
+ code: errorCode,
1704
+ message: errorMessage.slice(0, 500)
1705
+ });
1706
+ }
1707
+ function mapTokenEndpointFetchFailure(e) {
1708
+ const codes = collectNetworkErrorCodes(e);
1709
+ const msg = e instanceof Error ? e.message : String(e);
1710
+ const isDnsError = codes.some((c) => c === "ENOTFOUND" || c === "EAI_AGAIN");
1711
+ if (isDnsError) {
1712
+ throw APIError.from("BAD_GATEWAY", {
1713
+ code: "ECOMIQ_AUTH_DNS",
1714
+ message: "No se pudo resolver el servidor de autenticacion."
1715
+ });
1716
+ }
1717
+ const isConnectionRefused = codes.includes("ECONNREFUSED");
1718
+ if (isConnectionRefused) {
1719
+ throw APIError.from("BAD_GATEWAY", {
1720
+ code: "ECOMIQ_AUTH_REFUSED",
1721
+ message: "Conexion rechazada al servidor de autenticacion."
1722
+ });
1723
+ }
1724
+ const looksLikeAbortOrSocketClose = codes.some(
1725
+ (c) => ["UND_ERR_SOCKET", "ECONNRESET", "EPIPE", "ECONNABORTED"].includes(c)
1726
+ ) || /terminated|abort|closed|FIN|socket/i.test(msg);
1727
+ if (looksLikeAbortOrSocketClose) {
1728
+ throwBadRequestFromOAuth({}, "Credenciales invalidas o conexion cerrada por el servidor.");
1729
+ }
1730
+ throw APIError.from("INTERNAL_SERVER_ERROR", {
1731
+ code: "ECOMIQ_AUTH_REQUEST_FAILED",
1732
+ message: msg.slice(0, 280) || "Error al contactar el servidor de autenticacion."
1733
+ });
1734
+ }
1735
+ function collectNetworkErrorCodes(e) {
1736
+ const out = [];
1737
+ const visit = (x) => {
1738
+ if (!x || typeof x !== "object") return;
1739
+ const code = "code" in x && typeof x.code === "string" ? x.code : void 0;
1740
+ if (code) out.push(code);
1741
+ if ("cause" in x && x.cause) {
1742
+ visit(x.cause);
1743
+ }
1744
+ };
1745
+ visit(e);
1746
+ return out;
1747
+ }
1748
+
1749
+ // src/react/auth/ecomiq-oauth-password-client.ts
1750
+ function ecomiqOAuthPasswordClient() {
1751
+ return {
1752
+ id: PLUGIN_ID,
1753
+ pathMethods: {
1754
+ "/ecomiq-oauth/sign-in": "POST"
1755
+ },
1756
+ atomListeners: [
1757
+ {
1758
+ matcher: (path) => path === "/ecomiq-oauth/sign-in",
1759
+ signal: "$sessionSignal"
1760
+ }
1761
+ ]
1762
+ };
1763
+ }
1764
+ var StorefrontCheckoutSettingsSchema = z.object({
1765
+ allowGuestCheckout: z.boolean().optional(),
1766
+ identityScope: z.enum(["store", "tenant", "global"]).optional(),
1767
+ signInMethods: z.array(z.string()).optional()
1768
+ });
1769
+ var looseRecord = z.record(z.string(), z.unknown());
1770
+ var StorefrontThemeConfigSchema = z.object({
1771
+ name: z.string().nullable().optional(),
1772
+ mode: z.string().nullable().optional(),
1773
+ colors: looseRecord.optional(),
1774
+ fonts: looseRecord.optional(),
1775
+ assets: looseRecord.optional()
1776
+ }).passthrough();
1777
+ var StorefrontFeaturesEnabledSchema = z.object({
1778
+ flags: looseRecord.optional()
1779
+ }).passthrough();
1780
+ var StorefrontCmsBrandingSchema = z.object({
1781
+ logoUrl: z.string().nullable().optional(),
1782
+ logoDarkUrl: z.string().nullable().optional(),
1783
+ faviconUrl: z.string().nullable().optional(),
1784
+ socialImageUrl: z.string().nullable().optional()
1785
+ });
1786
+ var StorefrontCmsSeoSchema = z.object({
1787
+ siteName: z.string().nullable().optional(),
1788
+ metaTitle: z.string().nullable().optional(),
1789
+ metaDescription: z.string().nullable().optional(),
1790
+ titleTemplate: z.string().nullable().optional(),
1791
+ robotsIndex: z.boolean().optional()
1792
+ });
1793
+ var StorefrontCmsSocialLinksSchema = z.object({
1794
+ instagram: z.string().nullable().optional(),
1795
+ facebook: z.string().nullable().optional(),
1796
+ tiktok: z.string().nullable().optional(),
1797
+ youtube: z.string().nullable().optional(),
1798
+ x: z.string().nullable().optional()
1799
+ });
1800
+ var StorefrontCmsContactSchema = z.object({
1801
+ supportEmail: z.string().nullable().optional(),
1802
+ phone: z.string().nullable().optional(),
1803
+ whatsapp: z.string().nullable().optional(),
1804
+ address: z.string().nullable().optional(),
1805
+ socialLinks: StorefrontCmsSocialLinksSchema.optional()
1806
+ });
1807
+ var StorefrontCmsTrackingSchema = z.object({
1808
+ googleAnalyticsMeasurementId: z.string().nullable().optional(),
1809
+ metaPixelId: z.string().nullable().optional(),
1810
+ googleTagManagerContainerId: z.string().nullable().optional()
1811
+ });
1812
+ var StorefrontCmsCustomScriptsSchema = z.object({
1813
+ enabled: z.boolean().optional(),
1814
+ head: z.string().nullable().optional(),
1815
+ bodyEnd: z.string().nullable().optional()
1816
+ });
1817
+ var StorefrontCmsSettingsSchema = z.object({
1818
+ themeKey: z.string().optional(),
1819
+ branding: StorefrontCmsBrandingSchema.optional(),
1820
+ seo: StorefrontCmsSeoSchema.optional(),
1821
+ contact: StorefrontCmsContactSchema.optional(),
1822
+ tracking: StorefrontCmsTrackingSchema.optional(),
1823
+ customScripts: StorefrontCmsCustomScriptsSchema.optional(),
1824
+ updatedOn: z.string().nullable().optional()
1825
+ });
1826
+ var StorefrontCmsPageSchema = z.object({
1827
+ id: z.string(),
1828
+ name: z.string(),
1829
+ slug: z.string(),
1830
+ layout: z.string().optional(),
1831
+ version: z.number().optional(),
1832
+ settings: z.record(z.string(), z.unknown()).optional().nullable(),
1833
+ sections: z.array(z.unknown()).optional(),
1834
+ status: z.string().optional(),
1835
+ hasPublishedVersion: z.boolean().optional(),
1836
+ publishedOn: z.string().nullable().optional(),
1837
+ publishedBy: z.string().nullable().optional(),
1838
+ createdOn: z.string().nullable().optional(),
1839
+ updatedOn: z.string().nullable().optional(),
1840
+ createdBy: z.string().nullable().optional(),
1841
+ updatedBy: z.string().nullable().optional(),
1842
+ lastModifiedBy: z.string().nullable().optional()
1843
+ }).passthrough();
1844
+ var StorefrontSettingsLegacyFlatSchema = z.object({
1845
+ id: z.coerce.string(),
1846
+ tenantId: z.string().nullable().optional(),
1847
+ name: z.string(),
1848
+ slug: z.coerce.string().optional(),
1849
+ description: z.string().nullable().optional(),
1850
+ visibility: z.string().nullable().optional(),
1851
+ logo: z.string().nullable().optional(),
1852
+ favicon: z.string().nullable().optional(),
1853
+ icon: z.string().nullable().optional(),
1854
+ currency: z.string().nullable().optional(),
1855
+ language: z.string().nullable().optional(),
1856
+ themeConfig: z.unknown().optional().nullable(),
1857
+ featuresEnabled: z.unknown().optional().nullable(),
1858
+ checkoutSettings: StorefrontCheckoutSettingsSchema.optional().nullable()
1859
+ }).passthrough();
1860
+ function unwrapSettingsResponseBody(v) {
1861
+ if (!v || typeof v !== "object" || Array.isArray(v)) return v;
1862
+ const o = v;
1863
+ if ("data" in o && o.data != null && typeof o.data === "object" && !Array.isArray(o.data)) {
1864
+ return o.data;
1865
+ }
1866
+ return v;
1867
+ }
1868
+ function isLegacyFlatSettingsPayload(v) {
1869
+ if (!v || typeof v !== "object" || Array.isArray(v)) return false;
1870
+ const o = v;
1871
+ if ("store" in o || "schemaVersion" in o || "cms" in o) return false;
1872
+ return (typeof o.id === "string" || typeof o.id === "number") && typeof o.name === "string";
1873
+ }
1874
+ function normalizeStorefrontSettingsPayload(v) {
1875
+ const body = unwrapSettingsResponseBody(v);
1876
+ if (!isLegacyFlatSettingsPayload(body)) return body;
1877
+ const raw = body;
1878
+ const parsed = StorefrontSettingsLegacyFlatSchema.safeParse(raw);
1879
+ if (!parsed.success) return body;
1880
+ const l = parsed.data;
1881
+ const currency = l.currency?.trim() ? l.currency : "PEN";
1882
+ const language = l.language?.trim() ? l.language : "es";
1883
+ const slug = l.slug?.trim() ? l.slug : l.id;
1884
+ return {
1885
+ schemaVersion: 0,
1886
+ generatedAt: (/* @__PURE__ */ new Date(0)).toISOString(),
1887
+ store: {
1888
+ id: l.id,
1889
+ tenantId: l.tenantId ?? void 0,
1890
+ name: l.name,
1891
+ slug,
1892
+ description: l.description ?? null,
1893
+ logo: l.logo ?? null,
1894
+ icon: l.icon ?? null,
1895
+ visibility: l.visibility ?? void 0,
1896
+ currency,
1897
+ language,
1898
+ themeConfig: l.themeConfig ?? void 0,
1899
+ featuresEnabled: l.featuresEnabled ?? void 0,
1900
+ checkoutSettings: l.checkoutSettings ?? void 0
1901
+ },
1902
+ cms: {
1903
+ settings: {
1904
+ branding: {
1905
+ faviconUrl: l.favicon ?? null,
1906
+ logoUrl: l.logo ?? null,
1907
+ logoDarkUrl: null,
1908
+ socialImageUrl: null
1909
+ }
1910
+ },
1911
+ pages: []
1912
+ }
1913
+ };
1914
+ }
1915
+ var StorefrontStoreSchema = z.object({
1916
+ id: z.string(),
1917
+ tenantId: z.string().optional(),
1918
+ sellerId: z.string().optional(),
1919
+ plan: z.string().optional(),
1920
+ storageLimitBytes: z.number().optional(),
1921
+ monthlyTransferLimitBytes: z.number().optional(),
1922
+ maxUploadSizeBytes: z.number().optional(),
1923
+ maxStores: z.number().optional(),
1924
+ maxMembers: z.number().optional(),
1925
+ maxProducts: z.number().optional(),
1926
+ maxCategories: z.number().optional(),
1927
+ maxBrands: z.number().optional(),
1928
+ maxAttributes: z.number().optional(),
1929
+ maxCollections: z.number().optional(),
1930
+ enableApi: z.boolean().optional(),
1931
+ isActive: z.boolean().optional(),
1932
+ name: z.string(),
1933
+ description: z.string().nullable().optional(),
1934
+ avatarUrl: z.string().nullable().optional(),
1935
+ logo: z.string().nullable().optional(),
1936
+ icon: z.string().nullable().optional(),
1937
+ slug: z.string(),
1938
+ domain: z.string().nullable().optional(),
1939
+ domains: z.array(z.string()).optional(),
1940
+ timeZone: z.string().nullable().optional(),
1941
+ currency: z.string(),
1942
+ language: z.string(),
1943
+ visibility: z.string().optional(),
1944
+ privateAccessHash: z.string().nullable().optional(),
1945
+ privateAccessVersion: z.number().optional(),
1946
+ themeConfig: StorefrontThemeConfigSchema.optional(),
1947
+ featuresEnabled: StorefrontFeaturesEnabledSchema.optional(),
1948
+ checkoutSettings: StorefrontCheckoutSettingsSchema.optional().nullable()
1949
+ }).passthrough();
1950
+ var StorefrontSettingsEnvelopeSchema = z.object({
1951
+ schemaVersion: z.number(),
1952
+ generatedAt: z.string(),
1953
+ store: StorefrontStoreSchema,
1954
+ cms: z.object({
1955
+ settings: StorefrontCmsSettingsSchema,
1956
+ pages: z.array(StorefrontCmsPageSchema)
1957
+ })
1958
+ }).passthrough();
1959
+ var StorefrontSettingsSchema3 = z.preprocess(
1960
+ normalizeStorefrontSettingsPayload,
1961
+ StorefrontSettingsEnvelopeSchema
1962
+ );
1963
+ function storefrontStoreRecord(settings) {
1964
+ return settings?.store ?? null;
1965
+ }
1966
+
1967
+ // src/core/lib/ecomiq-oauth-identity.ts
1968
+ function buildEcomiqOAuthIdentityHeaders(settings) {
1969
+ const record = storefrontStoreRecord(settings);
1970
+ if (!record?.id) {
1971
+ throw new Error("Tienda no disponible para iniciar sesi\xF3n");
1972
+ }
1973
+ const checkout = record.checkoutSettings;
1974
+ const identityScope = checkout?.identityScope === "tenant" ? "tenant" : "store";
1975
+ const headers3 = {
1976
+ "x-Identity-Scope": identityScope,
1977
+ "x-Store-Id": record.id
1978
+ };
1979
+ if (record.tenantId) headers3["x-Tenant-Id"] = record.tenantId;
1980
+ return headers3;
1981
+ }
1982
+
1983
+ // src/react/auth/ecomiq-storefront-sign-in.ts
1984
+ async function signInWithEcomiqCredentials2(authClient, settings, credentials) {
1985
+ const response = await authClient.$fetch("/ecomiq-oauth/sign-in", {
1986
+ method: "POST",
1987
+ headers: buildEcomiqOAuthIdentityHeaders(settings),
1988
+ body: credentials
1989
+ });
1990
+ if (response.error) {
1991
+ throw new Error(response.error.message ?? "Error al iniciar sesi\xF3n");
1992
+ }
1993
+ return response;
1994
+ }
1995
+ function isRenderableCmsPage(p) {
1996
+ if (!p.status) return true;
1997
+ return p.status === "published";
1998
+ }
1999
+ function findStorefrontCmsPageBySlug(settings, slug) {
2000
+ const pages = settings?.cms?.pages;
2001
+ if (!pages?.length) return null;
2002
+ const raw = pages.find(
2003
+ (p) => p.slug === slug && isRenderableCmsPage(p)
2004
+ );
2005
+ if (!raw) return null;
2006
+ const parsed = StorefrontPageDetailSchema$1.safeParse(raw);
2007
+ return parsed.success ? parsed.data : null;
2008
+ }
2009
+ function buildOptionGroups(variants) {
2010
+ const map = /* @__PURE__ */ new Map();
2011
+ for (const v of variants) {
2012
+ for (const opt of v.options ?? []) {
2013
+ const key = opt.key;
2014
+ const name = opt.name ?? key;
2015
+ if (!map.has(key)) map.set(key, /* @__PURE__ */ new Map());
2016
+ map.get(key).set(opt.value, name);
2017
+ }
2018
+ }
2019
+ return Array.from(map.entries()).map(([key, valueMap]) => ({
2020
+ key,
2021
+ name: Array.from(valueMap.values())[0] ?? key,
2022
+ values: Array.from(valueMap.keys())
2023
+ }));
2024
+ }
2025
+ function findVariant(variants, selected, hasConfigurableOptions) {
2026
+ if (!hasConfigurableOptions && variants.length > 0) {
2027
+ return variants[0] ?? null;
2028
+ }
2029
+ if (Object.keys(selected).length === 0) return null;
2030
+ for (const v of variants) {
2031
+ const opts = v.options ?? [];
2032
+ const match = opts.length > 0 && opts.every((o) => selected[o.key] === o.value);
2033
+ if (match) return v;
2034
+ }
2035
+ return null;
2036
+ }
2037
+ function getInitialOptions(variants, optionGroups) {
2038
+ const first = variants[0];
2039
+ if (!first) return {};
2040
+ const opts = first.options ?? [];
2041
+ const selected = {};
2042
+ for (const g of optionGroups) {
2043
+ const o = opts.find((x) => x.key === g.key);
2044
+ if (o) selected[g.key] = o.value;
2045
+ }
1423
2046
  return selected;
1424
2047
  }
1425
2048
  function ProductVariantSelector({
@@ -1492,9 +2115,850 @@ function ProductVariantSelector({
1492
2115
  ] });
1493
2116
  }
1494
2117
 
1495
- // src/npm/nextjs.ts
2118
+ // src/core/constants/api-paths.ts
2119
+ var STOREFRONT_API_PATH_PREFIX = "v1";
2120
+
2121
+ // src/core/constants/currency.ts
2122
+ var countryCurrency = {
2123
+ "PEN": "es-PE",
2124
+ "USD": "en-US",
2125
+ "COP": "es-CO",
2126
+ "MXN": "es-MX"
2127
+ };
2128
+
2129
+ // src/core/utils/format-currency.ts
2130
+ function formatCurrency2(amount, currency = "USD") {
2131
+ const locale = countryCurrency[currency] ?? "en-US";
2132
+ return new Intl.NumberFormat(locale, {
2133
+ style: "currency",
2134
+ currency
2135
+ }).format(amount);
2136
+ }
2137
+ var ApiClient = class {
2138
+ client;
2139
+ storeId;
2140
+ storeDomain;
2141
+ constructor(baseURL = "https://storefront.ecomiq.pe") {
2142
+ this.client = axios.create({
2143
+ baseURL,
2144
+ timeout: 1e4,
2145
+ headers: {
2146
+ "Content-Type": "application/json"
2147
+ }
2148
+ });
2149
+ this.client.interceptors.request.use(
2150
+ (config) => {
2151
+ if (config.headers) {
2152
+ if (this.storeId) config.headers["X-Store-Id"] = this.storeId;
2153
+ if (this.storeDomain) config.headers["X-Store-Domain"] = this.storeDomain;
2154
+ }
2155
+ return config;
2156
+ }
2157
+ );
2158
+ this.client.interceptors.response.use(
2159
+ (response) => response,
2160
+ (error) => {
2161
+ console.error("API Error:", error);
2162
+ return Promise.reject(error);
2163
+ }
2164
+ );
2165
+ }
2166
+ setStoreId(storeId) {
2167
+ this.storeId = storeId;
2168
+ }
2169
+ /** Subdominio para endpoints que usan X-Store-Domain (ej. GET /v1/storefront/settings) */
2170
+ setStoreDomain(domain) {
2171
+ this.storeDomain = domain;
2172
+ }
2173
+ get(url, config) {
2174
+ return this.client.get(url, config).then((response) => response.data);
2175
+ }
2176
+ post(url, data, config) {
2177
+ return this.client.post(url, data, config).then((response) => response.data);
2178
+ }
2179
+ put(url, data, config) {
2180
+ return this.client.put(url, data, config).then((response) => response.data);
2181
+ }
2182
+ patch(url, data, config) {
2183
+ return this.client.patch(url, data, config).then((response) => response.data);
2184
+ }
2185
+ delete(url, config) {
2186
+ return this.client.delete(url, config).then((response) => response.data);
2187
+ }
2188
+ };
2189
+ new ApiClient();
2190
+ var ProductVariantSchema = z.object({
2191
+ id: z.coerce.string(),
2192
+ name: z.string(),
2193
+ storeId: z.string(),
2194
+ productId: z.coerce.string(),
2195
+ basePrice: z.number().positive().optional().nullable(),
2196
+ comparePrice: z.number().positive().optional().nullable(),
2197
+ //TODO: remove this field
2198
+ originalPrice: z.number().positive().optional().nullable(),
2199
+ costPrice: z.number().positive().optional().nullable(),
2200
+ isDefault: z.boolean().optional().nullable(),
2201
+ reserveStock: z.number().nonnegative().optional().nullable(),
2202
+ weight: z.number().nonnegative().optional().nullable().default(0),
2203
+ width: z.number().nonnegative().optional().nullable(),
2204
+ height: z.number().nonnegative().optional().nullable(),
2205
+ unitOfMeasure: z.string().optional().nullable(),
2206
+ order: z.number().nonnegative().optional().nullable().default(0),
2207
+ sku: z.string().optional().nullable(),
2208
+ stock: z.number().nonnegative().optional().nullable(),
2209
+ leadTime: z.string().optional().nullable(),
2210
+ barCode: z.string().optional().nullable(),
2211
+ shippingWeight: z.number().nonnegative().optional().nullable(),
2212
+ images: z.array(z.string()).optional().nullable(),
2213
+ attributes: z.array(z.string()).optional().nullable(),
2214
+ variantStatus: z.string().optional().nullable(),
2215
+ status: z.string().optional().nullable(),
2216
+ createdOn: z.string().optional().nullable(),
2217
+ updatedOn: z.string().optional().nullable(),
2218
+ deletedOn: z.string().optional().nullable()
2219
+ });
2220
+ var ProductSchema = z.object({
2221
+ id: z.coerce.string(),
2222
+ name: z.string(),
2223
+ images: z.array(z.object({
2224
+ url: z.string().url(),
2225
+ name: z.string().optional(),
2226
+ isPrimary: z.boolean().optional()
2227
+ })),
2228
+ basePrice: z.number().positive().optional(),
2229
+ salePrice: z.number().positive().optional(),
2230
+ slug: z.string(),
2231
+ description: z.string().optional(),
2232
+ categories: z.array(z.object({
2233
+ id: z.coerce.string(),
2234
+ name: z.string(),
2235
+ slug: z.string(),
2236
+ logo: z.string().optional(),
2237
+ icon: z.string().optional()
2238
+ })).optional(),
2239
+ tags: z.array(z.string()).optional(),
2240
+ inStock: z.boolean().optional(),
2241
+ variants: z.array(ProductVariantSchema).optional()
2242
+ });
2243
+ z.object({
2244
+ items: z.array(ProductSchema),
2245
+ meta: z.object({
2246
+ total: z.number().int().positive(),
2247
+ page: z.number().int().positive(),
2248
+ pageSize: z.number().int().positive(),
2249
+ hasNextPage: z.boolean()
2250
+ })
2251
+ });
2252
+ z.object({
2253
+ category: z.string().optional(),
2254
+ search: z.string().optional(),
2255
+ minPrice: z.number().positive().optional(),
2256
+ maxPrice: z.number().positive().optional(),
2257
+ inStock: z.boolean().optional(),
2258
+ sortBy: z.enum(["name", "price", "createdAt"]).optional(),
2259
+ sortOrder: z.enum(["asc", "desc"]).optional(),
2260
+ page: z.number().int().positive().optional(),
2261
+ limit: z.number().int().positive().optional()
2262
+ });
2263
+ var StorefrontProductSchema = z.object({
2264
+ id: z.coerce.string(),
2265
+ name: z.string(),
2266
+ slug: z.string(),
2267
+ productType: z.string().optional().nullable(),
2268
+ imageUrl: z.string().optional().nullable(),
2269
+ price: z.number().nonnegative(),
2270
+ compareAtPrice: z.number().nonnegative().optional().nullable(),
2271
+ variantCount: z.number().int().nonnegative().optional().default(0),
2272
+ isAvailable: z.boolean(),
2273
+ totalInventory: z.number().int().nonnegative(),
2274
+ bundleItemsCount: z.number().int().nonnegative().optional().default(0),
2275
+ brand: z.string().optional().nullable(),
2276
+ category: z.string().optional().nullable(),
2277
+ status: z.string().optional().nullable(),
2278
+ tags: z.array(z.string()).optional().nullable()
2279
+ });
2280
+ var StorefrontFacetSchema = z.object({
2281
+ value: z.string(),
2282
+ count: z.number().int().nonnegative()
2283
+ });
2284
+ var StorefrontProductsResponseSchema2 = z.object({
2285
+ pageIndex: z.number().int().positive(),
2286
+ pageSize: z.number().int().positive(),
2287
+ totalCount: z.number().int().nonnegative(),
2288
+ totalPages: z.number().int().nonnegative(),
2289
+ products: z.array(StorefrontProductSchema),
2290
+ categories: z.array(StorefrontFacetSchema).optional().default([]),
2291
+ brands: z.array(StorefrontFacetSchema).optional().default([]),
2292
+ minPrice: z.number().nonnegative().optional().nullable(),
2293
+ maxPrice: z.number().nonnegative().optional().nullable()
2294
+ });
2295
+ z.object({
2296
+ page: z.number().int().positive().optional().default(1),
2297
+ pageSize: z.number().int().positive().optional().default(20),
2298
+ sort: z.enum(["price_asc", "price_desc", "name_asc", "name_desc", "newest"]).optional().default("price_asc"),
2299
+ sellerId: z.string().optional(),
2300
+ search: z.string().optional(),
2301
+ collection: z.string().optional(),
2302
+ category: z.string().optional(),
2303
+ brand: z.string().optional(),
2304
+ productType: z.string().optional(),
2305
+ inStock: z.boolean().optional(),
2306
+ minPrice: z.number().nonnegative().optional(),
2307
+ maxPrice: z.number().nonnegative().optional()
2308
+ });
2309
+ var StorefrontVariantOptionSchema = z.object({
2310
+ key: z.string(),
2311
+ name: z.string(),
2312
+ value: z.string()
2313
+ });
2314
+ var StorefrontProductImageSchema = z.object({
2315
+ url: z.string(),
2316
+ name: z.string().optional().nullable(),
2317
+ order: z.number().int().nonnegative().optional()
2318
+ });
2319
+ var StorefrontVariantSchema = z.object({
2320
+ id: z.coerce.string().optional().default(""),
2321
+ sku: z.string(),
2322
+ status: z.string().optional().nullable(),
2323
+ price: z.number().nonnegative(),
2324
+ compareAtPrice: z.number().nonnegative().optional().nullable(),
2325
+ quantity: z.number().int().nonnegative(),
2326
+ options: z.array(StorefrontVariantOptionSchema).optional().default([]),
2327
+ images: z.array(StorefrontProductImageSchema).optional().default([])
2328
+ });
2329
+ var StorefrontPriceTierSchema = z.object({
2330
+ variantId: z.coerce.string(),
2331
+ minQuantity: z.number().int().positive(),
2332
+ maxQuantity: z.number().int().positive().optional().nullable(),
2333
+ unitPrice: z.number().nonnegative()
2334
+ });
2335
+ var StorefrontBundleItemSchema = z.object({
2336
+ productId: z.coerce.string(),
2337
+ variantId: z.coerce.string(),
2338
+ productName: z.string(),
2339
+ variantName: z.string().optional().nullable(),
2340
+ sku: z.string().optional().nullable(),
2341
+ brand: z.string().optional().nullable(),
2342
+ imageUrl: z.string().optional().nullable(),
2343
+ quantity: z.number().int(),
2344
+ availableStock: z.number().int().optional().default(0),
2345
+ allowBackorders: z.boolean().optional().default(false),
2346
+ requiresShipping: z.boolean().optional().default(true)
2347
+ });
2348
+ z.object({
2349
+ id: z.coerce.string(),
2350
+ name: z.string(),
2351
+ slug: z.string(),
2352
+ description: z.string().optional().nullable(),
2353
+ productType: z.string().optional().nullable(),
2354
+ status: z.string().optional().nullable(),
2355
+ imageUrl: z.string().optional().nullable(),
2356
+ price: z.number().nonnegative(),
2357
+ compareAtPrice: z.number().nonnegative().optional().nullable(),
2358
+ isAvailable: z.boolean(),
2359
+ totalInventory: z.number().int().nonnegative(),
2360
+ brand: z.string().optional().nullable(),
2361
+ category: z.string().optional().nullable(),
2362
+ categoryId: z.coerce.string().optional().nullable(),
2363
+ tags: z.array(z.string()).optional().nullable(),
2364
+ specifications: z.array(z.record(z.string(), z.unknown())).optional().default([]),
2365
+ variants: z.array(StorefrontVariantSchema).optional().default([]),
2366
+ images: z.array(StorefrontProductImageSchema).optional().default([]),
2367
+ bundleItems: z.array(StorefrontBundleItemSchema).optional().default([]),
2368
+ priceTiers: z.array(StorefrontPriceTierSchema).optional().default([]),
2369
+ createdAt: z.string().optional().nullable(),
2370
+ updatedAt: z.string().optional().nullable()
2371
+ });
2372
+ var StorefrontCollectionSchema = z.object({
2373
+ id: z.coerce.string(),
2374
+ slug: z.string(),
2375
+ name: z.string(),
2376
+ description: z.string().nullable().optional(),
2377
+ imageUrl: z.string().nullable().optional(),
2378
+ type: z.string(),
2379
+ productCount: z.number().int().nonnegative()
2380
+ });
2381
+ z.array(StorefrontCollectionSchema);
2382
+ z.object({
2383
+ id: z.string(),
2384
+ name: z.string(),
2385
+ slug: z.string(),
2386
+ layout: z.string()
2387
+ });
2388
+ var StorefrontPageSeoSchema = z.object({
2389
+ title: z.string().optional(),
2390
+ description: z.string().optional()
2391
+ }).optional();
2392
+ var StorefrontPageSettingsSchema = z.object({
2393
+ seo: StorefrontPageSeoSchema,
2394
+ html: z.string().optional(),
2395
+ title: z.string().optional()
2396
+ }).catchall(z.unknown());
2397
+ var StorefrontPageBlockSchema = z.object({
2398
+ id: z.string(),
2399
+ type: z.string(),
2400
+ order: z.number(),
2401
+ settings: z.record(z.unknown()).optional()
2402
+ });
2403
+ var StorefrontPageSectionSchema = z.object({
2404
+ id: z.string(),
2405
+ type: z.string(),
2406
+ order: z.number(),
2407
+ settings: z.record(z.unknown()).optional(),
2408
+ blocks: z.array(StorefrontPageBlockSchema).optional().default([])
2409
+ });
2410
+ z.object({
2411
+ id: z.string(),
2412
+ name: z.string(),
2413
+ slug: z.string(),
2414
+ layout: z.string(),
2415
+ settings: StorefrontPageSettingsSchema.optional(),
2416
+ sections: z.array(StorefrontPageSectionSchema).optional()
2417
+ });
2418
+ z.object({
2419
+ email: z.string().email(),
2420
+ firstName: z.string().min(1),
2421
+ lastName: z.string().min(1),
2422
+ phone: z.string().min(1)
2423
+ });
2424
+ z.object({
2425
+ currencyCode: z.string(),
2426
+ salesChannelId: z.string(),
2427
+ email: z.string().email()
2428
+ });
2429
+ var CartAddressSchema = z.object({
2430
+ name: z.string().min(1),
2431
+ address1: z.string().min(1),
2432
+ city: z.string().min(1),
2433
+ countryCode: z.string().min(2),
2434
+ company: z.string().nullable().optional(),
2435
+ address2: z.string().nullable().optional(),
2436
+ province: z.string().nullable().optional(),
2437
+ postalCode: z.string().nullable().optional(),
2438
+ district: z.string().nullable().optional(),
2439
+ phone: z.string().nullable().optional(),
2440
+ latitude: z.number().nullable().optional(),
2441
+ longitude: z.number().nullable().optional(),
2442
+ geo: z.object({
2443
+ departmentCode: z.string().nullable().optional(),
2444
+ provinceCode: z.string().nullable().optional(),
2445
+ districtCode: z.string().nullable().optional()
2446
+ }).nullable().optional()
2447
+ });
2448
+ z.object({
2449
+ shippingAddress: CartAddressSchema,
2450
+ billingAddress: CartAddressSchema
2451
+ });
2452
+ var CartItemModifierRequestSchema = z.object({
2453
+ modifierGroupId: z.coerce.string().min(1),
2454
+ optionIds: z.array(z.coerce.string().min(1)).optional().default([])
2455
+ });
2456
+ z.object({
2457
+ variantId: z.coerce.string().min(1),
2458
+ quantity: z.number().int().positive(),
2459
+ modifiers: z.array(CartItemModifierRequestSchema).optional()
2460
+ });
2461
+ z.object({
2462
+ code: z.string().min(1)
2463
+ });
2464
+ z.object({
2465
+ subtotal: z.number(),
2466
+ discount: z.number(),
2467
+ tax: z.number(),
2468
+ warranty: z.number().optional().default(0),
2469
+ total: z.number()
2470
+ });
2471
+ z.object({
2472
+ name: z.string().nullable().optional(),
2473
+ address1: z.string(),
2474
+ city: z.string(),
2475
+ countryCode: z.string(),
2476
+ company: z.string().nullable().optional(),
2477
+ address2: z.string().nullable().optional(),
2478
+ province: z.string().nullable().optional(),
2479
+ postalCode: z.string().nullable().optional(),
2480
+ district: z.string().nullable().optional(),
2481
+ phone: z.string().nullable().optional(),
2482
+ latitude: z.number().nullable().optional(),
2483
+ longitude: z.number().nullable().optional(),
2484
+ geo: z.object({
2485
+ departmentCode: z.string().nullable().optional(),
2486
+ provinceCode: z.string().nullable().optional(),
2487
+ districtCode: z.string().nullable().optional()
2488
+ }).nullable().optional()
2489
+ });
2490
+ var CartResponseBundleItemSchema = z.object({
2491
+ productId: z.coerce.string(),
2492
+ variantId: z.coerce.string(),
2493
+ productName: z.string(),
2494
+ variantName: z.string().nullable().optional(),
2495
+ sku: z.string().nullable().optional(),
2496
+ brand: z.string().nullable().optional(),
2497
+ imageUrl: z.string().nullable().optional(),
2498
+ quantity: z.number().int()
2499
+ });
2500
+ var CartResponseItemSchema = z.object({
2501
+ id: z.string(),
2502
+ title: z.string(),
2503
+ variantLabel: z.string().nullable().optional(),
2504
+ thumbnail: z.string().nullable().optional(),
2505
+ quantity: z.number().int(),
2506
+ variantId: z.coerce.string(),
2507
+ variantSku: z.string().nullable().optional(),
2508
+ unitPrice: z.number(),
2509
+ bundleItems: z.array(CartResponseBundleItemSchema).optional().default([])
2510
+ });
2511
+ z.object({
2512
+ id: z.string(),
2513
+ name: z.string(),
2514
+ description: z.string().nullable().optional(),
2515
+ total: z.number(),
2516
+ estimatedDays: z.number().int().nullable().optional(),
2517
+ pickupPointId: z.string().nullable().optional(),
2518
+ scheduledDate: z.string().nullable().optional(),
2519
+ scheduledTime: z.string().nullable().optional()
2520
+ });
2521
+ var CartResponseSchema2 = z.object({
2522
+ id: z.string(),
2523
+ regionId: z.string().nullable().optional(),
2524
+ salesChannelId: z.string().nullable().optional(),
2525
+ email: z.string().email().nullable().optional(),
2526
+ phone: z.string().nullable().optional(),
2527
+ firstName: z.string().nullable().optional(),
2528
+ lastName: z.string().nullable().optional(),
2529
+ currencyCode: z.string(),
2530
+ completedAt: z.string().nullable().optional(),
2531
+ shippingAddress: z.unknown().nullable().optional(),
2532
+ billingAddress: z.unknown().nullable().optional(),
2533
+ metadata: z.record(z.string(), z.unknown()).optional().default({}),
2534
+ promotionCodes: z.array(z.string()).optional().default([]),
2535
+ items: z.array(CartResponseItemSchema).optional().default([]),
2536
+ shippingMethods: z.array(z.unknown()).optional().default([]),
2537
+ totals: z.object({
2538
+ discountTotal: z.number(),
2539
+ shippingTotal: z.number(),
2540
+ subtotal: z.number(),
2541
+ taxTotal: z.number(),
2542
+ total: z.number(),
2543
+ warrantyTotal: z.number()
2544
+ })
2545
+ });
2546
+ z.object({
2547
+ paymentMethodCode: z.string().min(1),
2548
+ externalRef: z.string().min(1),
2549
+ receiptUrl: z.string().min(1).optional()
2550
+ });
2551
+ var CartCheckoutPaymentActionSchema = z.object({
2552
+ publicConfiguration: z.object({
2553
+ publishableKey: z.string().nullable().optional(),
2554
+ checkoutMode: z.string().nullable().optional(),
2555
+ phoneNumber: z.string().nullable().optional(),
2556
+ accountHolder: z.string().nullable().optional(),
2557
+ qrImageUrl: z.string().nullable().optional()
2558
+ }).passthrough().nullable().optional(),
2559
+ url: z.string().nullable().optional(),
2560
+ html: z.string().nullable().optional(),
2561
+ clientSecret: z.string().nullable().optional(),
2562
+ qrCode: z.string().nullable().optional(),
2563
+ expiresAt: z.string().nullable().optional()
2564
+ }).passthrough();
2565
+ var CartCheckoutPaymentSchema = z.object({
2566
+ transactionId: z.string(),
2567
+ paymentMethodCode: z.string(),
2568
+ providerId: z.string().nullable().optional(),
2569
+ status: z.string(),
2570
+ amount: z.number(),
2571
+ reference: z.string(),
2572
+ actionType: z.string(),
2573
+ receiptUrl: z.string().nullable().optional(),
2574
+ action: CartCheckoutPaymentActionSchema.nullable().optional()
2575
+ }).passthrough();
2576
+ z.object({
2577
+ publicKey: z.string().optional(),
2578
+ publishableKey: z.string().optional(),
2579
+ stripePublicKey: z.string().optional(),
2580
+ clientSecret: z.string().optional(),
2581
+ intentClientSecret: z.string().optional(),
2582
+ orderId: z.coerce.string().optional(),
2583
+ orderGroup: z.string().optional(),
2584
+ orderIds: z.array(z.coerce.string()).optional(),
2585
+ /** Payload de pago (embedded, redirect, etc.) */
2586
+ payment: CartCheckoutPaymentSchema.optional(),
2587
+ cart: z.unknown().optional()
2588
+ }).passthrough();
2589
+ z.object({
2590
+ id: z.string(),
2591
+ url: z.string()
2592
+ });
2593
+ var CartPaymentMethodSchema = z.object({
2594
+ code: z.string(),
2595
+ name: z.string(),
2596
+ description: z.string().nullable().optional(),
2597
+ kind: z.string(),
2598
+ isActive: z.boolean(),
2599
+ isDefault: z.boolean(),
2600
+ isManual: z.boolean(),
2601
+ sortOrder: z.number(),
2602
+ phoneNumber: z.string().nullable().optional(),
2603
+ accountHolder: z.string().nullable().optional(),
2604
+ qrImageUrl: z.string().nullable().optional()
2605
+ });
2606
+ var CartPaymentMethodsResponseSchema = z.object({
2607
+ methods: z.array(CartPaymentMethodSchema)
2608
+ });
2609
+ z.object({
2610
+ cart: CartResponseSchema2,
2611
+ paymentMethods: CartPaymentMethodsResponseSchema
2612
+ });
2613
+ var ShippingRateSchema = z.object({
2614
+ shippingMethodId: z.string(),
2615
+ name: z.string(),
2616
+ displayName: z.string().optional(),
2617
+ type: z.string().optional(),
2618
+ price: z.number(),
2619
+ currency: z.string().optional(),
2620
+ sortOrder: z.number().optional(),
2621
+ rateId: z.string(),
2622
+ description: z.string().nullable().optional(),
2623
+ deliveryLocationId: z.string().nullable().optional(),
2624
+ deliveryLocationName: z.string().nullable().optional(),
2625
+ estimatedDeliveryDays: z.number().nullable().optional(),
2626
+ serviceCode: z.string().nullable().optional(),
2627
+ metadata: z.record(z.string(), z.unknown()).optional()
2628
+ }).passthrough();
2629
+ var ShippingOptionGroupSchema = z.object({
2630
+ sellerId: z.string(),
2631
+ shippingProfileId: z.string().nullable().optional(),
2632
+ orderAmount: z.number().optional(),
2633
+ totalWeight: z.number().optional(),
2634
+ itemIds: z.array(z.string()).optional(),
2635
+ rates: z.array(ShippingRateSchema)
2636
+ }).passthrough();
2637
+ z.object({
2638
+ groups: z.array(ShippingOptionGroupSchema)
2639
+ });
2640
+ z.object({
2641
+ rateId: z.string().min(1),
2642
+ shippingMethodId: z.string().min(1),
2643
+ serviceCode: z.string().nullable().optional(),
2644
+ sellerId: z.string().nullable().optional(),
2645
+ shippingProfileId: z.string().nullable().optional(),
2646
+ locationId: z.string().nullable().optional(),
2647
+ pickupPointId: z.string().nullable().optional(),
2648
+ scheduledDate: z.string().nullable().optional(),
2649
+ scheduledTime: z.string().nullable().optional()
2650
+ });
2651
+ var CONFIG_FILENAMES = [
2652
+ "ecomiq.config.js",
2653
+ "ecomiq.config.cjs",
2654
+ "ecomiq.config.mjs"
2655
+ ];
2656
+ var EcomiqConfigSchema = z.object({
2657
+ /**
2658
+ * Slug de tienda en Ecomiq (p. ej. `mi-tienda` para `mi-tienda.ecomiq.app`).
2659
+ * Si está definido, tiene prioridad sobre el `Host` de la request (útil en `localhost`).
2660
+ */
2661
+ slugStore: z.string().min(1).optional()
2662
+ });
2663
+ var cachedConfig;
2664
+ function parseExportedConfig(raw) {
2665
+ const candidate = raw && typeof raw === "object" && "default" in raw ? raw.default : raw;
2666
+ const parsed = EcomiqConfigSchema.safeParse(candidate);
2667
+ if (!parsed.success) {
2668
+ console.warn(
2669
+ "[ecomiq] ecomiq.config.* inv\xE1lido:",
2670
+ parsed.error.flatten().fieldErrors
2671
+ );
2672
+ return null;
2673
+ }
2674
+ const slugStore = parsed.data.slugStore?.trim();
2675
+ return slugStore ? { slugStore } : {};
2676
+ }
2677
+ function loadEcomiqConfig(cwd = process.cwd()) {
2678
+ if (cachedConfig !== void 0) return cachedConfig;
2679
+ for (const filename of CONFIG_FILENAMES) {
2680
+ const configPath = join(cwd, filename);
2681
+ if (!existsSync(configPath)) continue;
2682
+ try {
2683
+ const requireFromApp = createRequire(join(cwd, "package.json"));
2684
+ const loaded = requireFromApp(configPath);
2685
+ const parsed = parseExportedConfig(loaded);
2686
+ cachedConfig = parsed;
2687
+ return cachedConfig;
2688
+ } catch (error) {
2689
+ console.warn(`[ecomiq] No se pudo cargar ${configPath}:`, error);
2690
+ }
2691
+ }
2692
+ cachedConfig = null;
2693
+ return null;
2694
+ }
2695
+ function resolvePublicStoreKey(hostHeader, cwd) {
2696
+ const slugFromConfig = loadEcomiqConfig(cwd)?.slugStore?.trim();
2697
+ if (slugFromConfig) return slugFromConfig;
2698
+ if (!hostHeader?.trim()) {
2699
+ throw Object.assign(
2700
+ new Error(
2701
+ "Host is required, o define slugStore en ecomiq.config.js en la ra\xEDz del proyecto"
2702
+ ),
2703
+ { status: 400 }
2704
+ );
2705
+ }
2706
+ return resolveStorefrontSettingsHost(hostHeader);
2707
+ }
2708
+
2709
+ // src/core/server/request-origin.ts
2710
+ init_store_api_client();
2711
+ function applyServerRequestOriginFromHeaders(headerList) {
2712
+ const host = headerList.get("host")?.trim();
2713
+ if (!host) return;
2714
+ const proto = headerList.get("x-forwarded-proto") ?? headerList.get("x-forwarded-ssl") ?? "http";
2715
+ const secure = proto === "https" || (headerList.get("x-forwarded-ssl") ?? "") === "on";
2716
+ setServerRequestOrigin(`${secure ? "https" : "http"}://${host}`);
2717
+ }
2718
+ function normalizeRequestHostname(headerList) {
2719
+ const host = headerList.get("host");
2720
+ return host?.replace(/^www\./, "") ?? null;
2721
+ }
2722
+
2723
+ // src/core/server/fetch-public-store.ts
2724
+ init_store_api_client();
2725
+
2726
+ // src/react/shell/lib/public-store-settings-source.ts
2727
+ var ENV_PUBLIC_STORE_SETTINGS_SOURCE = "PUBLIC_STORE_SETTINGS_SOURCE";
2728
+ function resolvePublicStoreSettingsSource(envValue = process.env[ENV_PUBLIC_STORE_SETTINGS_SOURCE]) {
2729
+ const v = (envValue ?? "api").trim().toLowerCase();
2730
+ return v === "kv" ? "kv" : "api";
2731
+ }
2732
+
2733
+ // src/core/server/fetch-public-store.ts
2734
+ async function fetchPublicStoreSettings(options) {
2735
+ let storeKey;
2736
+ try {
2737
+ storeKey = resolvePublicStoreKey(options.host);
2738
+ } catch (error) {
2739
+ const err = error;
2740
+ return {
2741
+ data: null,
2742
+ error: {
2743
+ message: err.message ?? "Host is required",
2744
+ statusCode: err.status ?? 400,
2745
+ isNotFound: true,
2746
+ subdomain: void 0
2747
+ }
2748
+ };
2749
+ }
2750
+ const source = options.source ?? resolvePublicStoreSettingsSource();
2751
+ try {
2752
+ setServerStoreId(null);
2753
+ const data = source === "kv" ? await getStorefrontSettingsFromKvByStoreKey(storeKey) : await getStorefrontSettings(
2754
+ storeKey,
2755
+ `storefront-settings-${storeKey}`
2756
+ );
2757
+ setServerStoreId(data.store.id);
2758
+ return { data, error: null };
2759
+ } catch (error) {
2760
+ const err = error;
2761
+ const statusCode = err.status ?? 500;
2762
+ return {
2763
+ data: null,
2764
+ error: {
2765
+ message: err.message ?? "Error fetching store settings",
2766
+ statusCode,
2767
+ isNotFound: statusCode === 404,
2768
+ subdomain: storeKey
2769
+ }
2770
+ };
2771
+ }
2772
+ }
2773
+
2774
+ // src/core/server/store-proxy-handler.ts
2775
+ var DEFAULT_API_BASE_URL = "https://storefront.ecomiq.pe";
2776
+ function jsonError(status, message, error) {
2777
+ return Response.json({ error, message, status }, { status });
2778
+ }
2779
+ async function ensureStoreContext(request) {
2780
+ const xStoreId = request.headers.get("x-store-id")?.trim();
2781
+ const xStoreDomain = request.headers.get("x-store-domain")?.trim();
2782
+ if (xStoreDomain) {
2783
+ return {
2784
+ ok: true,
2785
+ storeId: xStoreId || "",
2786
+ storeDomain: xStoreDomain || null
2787
+ };
2788
+ }
2789
+ if (!xStoreId) {
2790
+ return {
2791
+ ok: false,
2792
+ response: jsonError(
2793
+ 400,
2794
+ "X-Store-Id is required",
2795
+ "Store context required"
2796
+ )
2797
+ };
2798
+ }
2799
+ return {
2800
+ ok: true,
2801
+ storeId: xStoreId || "",
2802
+ storeDomain: xStoreDomain || null
2803
+ };
2804
+ }
2805
+ var SKIP_FORWARD_HEADERS = /* @__PURE__ */ new Set([
2806
+ "host",
2807
+ "connection",
2808
+ "content-length",
2809
+ "transfer-encoding",
2810
+ "x-store-id",
2811
+ "x-store-domain",
2812
+ "authorization",
2813
+ "cookie",
2814
+ "cookie2",
2815
+ "set-cookie",
2816
+ "x-forwarded-for",
2817
+ "x-forwarded-proto",
2818
+ "x-forwarded-host",
2819
+ "x-real-ip",
2820
+ "forwarded",
2821
+ "cf-connecting-ip",
2822
+ "cf-connecting-ipv6",
2823
+ "true-client-ip",
2824
+ "cf-ipcountry",
2825
+ "cf-ray",
2826
+ "cf-visitor"
2827
+ ]);
2828
+ function getClientIp(request) {
2829
+ const candidates = [
2830
+ request.headers.get("cf-connecting-ip"),
2831
+ request.headers.get("true-client-ip"),
2832
+ request.headers.get("x-real-ip")
2833
+ ];
2834
+ for (const value of candidates) {
2835
+ const trimmed = value?.trim();
2836
+ if (trimmed) return trimmed;
2837
+ }
2838
+ const xff = request.headers.get("x-forwarded-for");
2839
+ if (xff?.trim()) {
2840
+ const first = xff.split(",")[0]?.trim();
2841
+ if (first) return first;
2842
+ }
2843
+ return null;
2844
+ }
2845
+ function getCookieValue(request, name) {
2846
+ const cookieHeader = request.headers.get("cookie");
2847
+ if (!cookieHeader) return void 0;
2848
+ for (const part of cookieHeader.split(";")) {
2849
+ const [rawKey, ...rest] = part.trim().split("=");
2850
+ if (rawKey?.trim() === name) {
2851
+ return decodeURIComponent(rest.join("="));
2852
+ }
2853
+ }
2854
+ return void 0;
2855
+ }
2856
+ function buildUpstreamRequest(request, pathSegments, config, storeId, storeDomain) {
2857
+ const requestUrl = new URL(request.url);
2858
+ const base = (config.apiBaseUrl ?? DEFAULT_API_BASE_URL).replace(/\/$/, "");
2859
+ const userPath = pathSegments.length ? pathSegments.join("/") : "";
2860
+ const path = userPath ? `${STOREFRONT_API_PATH_PREFIX}/${userPath}` : STOREFRONT_API_PATH_PREFIX;
2861
+ const search = requestUrl.searchParams.toString();
2862
+ const url = search ? `${base}/${path}?${search}` : `${base}/${path}`;
2863
+ const headers3 = new Headers();
2864
+ request.headers.forEach((value, key) => {
2865
+ const lower = key.toLowerCase();
2866
+ if (!SKIP_FORWARD_HEADERS.has(lower)) {
2867
+ headers3.set(key, value);
2868
+ }
2869
+ });
2870
+ headers3.set("X-Store-Id", storeId);
2871
+ if (storeDomain) {
2872
+ headers3.set("X-Store-domain", storeDomain);
2873
+ }
2874
+ const clientIp = getClientIp(request);
2875
+ if (clientIp) {
2876
+ headers3.set("X-Forwarded-For", clientIp);
2877
+ }
2878
+ const forwardedProto = request.headers.get("cf-visitor")?.match(/"scheme":"(https?)"/)?.[1] ?? requestUrl.protocol.replace(":", "") ?? "https";
2879
+ headers3.set("X-Forwarded-Proto", forwardedProto);
2880
+ const forwardedHost = request.headers.get("host") ?? requestUrl.host;
2881
+ if (forwardedHost) {
2882
+ headers3.set("X-Forwarded-Host", forwardedHost);
2883
+ }
2884
+ const incomingAuth = request.headers.get("authorization")?.trim();
2885
+ const storefrontOAuth = getCookieValue(request, STOREFRONT_ECOMIQ_OAUTH_COOKIE_NAME)?.trim();
2886
+ if (incomingAuth && /^Bearer\s+\S+/i.test(incomingAuth)) {
2887
+ headers3.set("Authorization", incomingAuth);
2888
+ } else if (storefrontOAuth) {
2889
+ headers3.set("Authorization", `Bearer ${storefrontOAuth}`);
2890
+ }
2891
+ headers3.set("Accept-Encoding", "identity");
2892
+ const init = {
2893
+ method: request.method,
2894
+ headers: headers3
2895
+ };
2896
+ const bodyMethods = ["POST", "PUT", "PATCH"];
2897
+ if (bodyMethods.includes(request.method) && request.body) {
2898
+ init.body = request.body;
2899
+ init.duplex = "half";
2900
+ }
2901
+ return { url, init };
2902
+ }
2903
+ function isResponseBodyForbidden(status) {
2904
+ return status === 204 || status === 205 || status === 304;
2905
+ }
2906
+ async function handleStoreProxyRequest(request, pathSegments, config = {}) {
2907
+ const storeContext = await ensureStoreContext(request);
2908
+ if (!storeContext.ok) {
2909
+ return storeContext.response;
2910
+ }
2911
+ const { storeId, storeDomain } = storeContext;
2912
+ const { url, init } = buildUpstreamRequest(
2913
+ request,
2914
+ pathSegments,
2915
+ config,
2916
+ storeId,
2917
+ storeDomain
2918
+ );
2919
+ const upstream = await fetch(url, init);
2920
+ const responseHeaders = new Headers(upstream.headers);
2921
+ const status = upstream.status;
2922
+ if (isResponseBodyForbidden(status)) {
2923
+ responseHeaders.delete("content-length");
2924
+ responseHeaders.delete("content-type");
2925
+ return new Response(null, {
2926
+ status,
2927
+ statusText: upstream.statusText,
2928
+ headers: responseHeaders
2929
+ });
2930
+ }
2931
+ const body = await upstream.text();
2932
+ return new Response(body, {
2933
+ status,
2934
+ statusText: upstream.statusText,
2935
+ headers: responseHeaders
2936
+ });
2937
+ }
2938
+
2939
+ // src/react/frameworks/index.ts
1496
2940
  var usePublicStore = () => useStore.getState().data;
2941
+ async function getHostname() {
2942
+ const headerList = await headers();
2943
+ applyServerRequestOriginFromHeaders(headerList);
2944
+ return normalizeRequestHostname(headerList);
2945
+ }
2946
+ async function getPublicStore(options) {
2947
+ const headerList = await headers();
2948
+ applyServerRequestOriginFromHeaders(headerList);
2949
+ return fetchPublicStoreSettings({
2950
+ host: headerList.get("host"),
2951
+ source: options?.source
2952
+ });
2953
+ }
2954
+ async function getPublicStoreFromHeaders(headerList, options) {
2955
+ applyServerRequestOriginFromHeaders(headerList);
2956
+ return fetchPublicStoreSettings({
2957
+ host: headerList.get("host"),
2958
+ ...options
2959
+ });
2960
+ }
1497
2961
 
1498
- export { CustomerAddressSchema, CustomerAddressUpsertSchema, CustomerMeSchema, CustomerOrderDetailSchema, CustomerOrderListItemSchema, CustomerProfilePatchSchema, CustomerRegisterRequestSchema, ECOMIQ_CLIENTS_STORE_KV_BINDING, EcomiqProvider, ProductVariantSelector, StoreApiClient, addCartCoupon, buildStorefrontKvSnapshotKey, checkoutCart, createCart, createCustomerAddress, createStoreApiClient, customerMeToProfilePatch, deleteCustomerAddress, findStorefrontCmsPageBySlug, getCart, getCartCheckout, getCartPaymentMethods, getCustomerAddresses, getCustomerMe, getCustomerOrder, getCustomerOrders, getHostname, getPublicStore, getSharedOrderDraft, getShippingOptions, getStoreApiBaseUrl, getStoreApiClient, getStorefrontCollectionBySlug, getStorefrontCollections, getStorefrontPageBySlug, getStorefrontPagePreview, getStorefrontPages, getStorefrontProductBySlug, getStorefrontProducts, getStorefrontSettings, getStorefrontSettingsFromKv, isCartNotActiveApiError, normalizeStoreHost, patchCartAddresses, patchCartBuyer, patchCustomer, putCartShippingMethod, registerCustomer, removeCartCoupon, resolvePublicStoreSettingsSource, resolveStorefrontSettingsHost, setClientStoreId, setServerRequestOrigin, setServerStoreId, storeApiClient, updateCustomerAddress, uploadPaymentReceipt, usePublicStore, useStore, useStoreCart, useStoreCollections, useStorePages, useStoreProducts };
2962
+ export { CustomerAddressSchema, CustomerAddressUpsertSchema, CustomerMeSchema, CustomerOrderDetailSchema, CustomerOrderListItemSchema, CustomerProfilePatchSchema, CustomerRegisterRequestSchema, ECOMIQ_CLIENTS_STORE_KV_BINDING, EcomiqProvider, ProductVariantSelector, QueryProvider, StoreApiClient, StorefrontAuthProvider, StorefrontProductSchema, StorefrontProductsResponseSchema2 as StorefrontProductsResponseSchema, StorefrontSettingsSchema3 as StorefrontSettingsSchema, addCartCoupon, applyServerRequestOriginFromHeaders, buildStorefrontKvSnapshotKey, buildStorefrontKvSnapshotKeyFromStoreKey, checkoutCart, createCart, createCustomerAddress, createStoreApiClient, customerMeToProfilePatch, deleteCustomerAddress, ecomiqOAuthPassword, ecomiqOAuthPasswordClient, fetchPublicStoreSettings, findStorefrontCmsPageBySlug, formatCurrency2 as formatCurrency, getCart, getCartCheckout, getCartPaymentMethods, getCustomerAddresses, getCustomerMe, getCustomerOrder, getCustomerOrders, getHostname, getPublicStore, getPublicStoreFromHeaders, getSharedOrderDraft, getShippingOptions, getStoreApiBaseUrl, getStoreApiClient, getStorefrontCollectionBySlug, getStorefrontCollections, getStorefrontPageBySlug, getStorefrontPagePreview, getStorefrontPages, getStorefrontProductBySlug, getStorefrontProducts, getStorefrontSettings, getStorefrontSettingsFromKv, getStorefrontSettingsFromKvByStoreKey, handleStoreProxyRequest, isCartNotActiveApiError, normalizeStoreHost, patchCartAddresses, patchCartBuyer, patchCustomer, putCartShippingMethod, registerCustomer, removeCartCoupon, resolvePublicStoreSettingsSource, resolveStorefrontSettingsHost, setClientStoreId, setServerRequestOrigin, setServerStoreId, signInWithEcomiqCredentials2 as signInWithEcomiqCredentials, storeApiClient, storefrontAccessTokenNeedsRefresh, storefrontSessionEndsSoon, updateCustomerAddress, uploadPaymentReceipt, useCart2 as useCart, usePublicStore, useStore, useStoreCart, useStoreCollections, useStorePages, useStoreProducts, useStorefrontAuthClient, useStorefrontSession };
1499
2963
  //# sourceMappingURL=nextjs.js.map
1500
2964
  //# sourceMappingURL=nextjs.js.map