@ecomiq/storefront 0.1.2 → 1.0.1

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 (56) hide show
  1. package/README.md +211 -59
  2. package/dist/chunk-PCTSWSTB.js +346 -0
  3. package/dist/chunk-PCTSWSTB.js.map +1 -0
  4. package/dist/chunk-PDK2VYWD.js +1598 -0
  5. package/dist/chunk-PDK2VYWD.js.map +1 -0
  6. package/dist/chunk-Q762YXCQ.js +175 -0
  7. package/dist/chunk-Q762YXCQ.js.map +1 -0
  8. package/dist/chunks/client.d.ts +8563 -0
  9. package/dist/chunks/client.js +2046 -0
  10. package/dist/chunks/client.js.map +1 -0
  11. package/dist/chunks/config.js +3 -0
  12. package/dist/chunks/config.js.map +1 -0
  13. package/dist/chunks/server-nextjs.d.ts +33 -0
  14. package/dist/chunks/server-nextjs.js +30 -0
  15. package/dist/chunks/server-nextjs.js.map +1 -0
  16. package/dist/chunks/server-tanstack.d.ts +22 -0
  17. package/dist/chunks/server-tanstack.js +30 -0
  18. package/dist/chunks/server-tanstack.js.map +1 -0
  19. package/dist/ecomiq-shell-ui-SWIFLQVM.js +8 -0
  20. package/dist/ecomiq-shell-ui-SWIFLQVM.js.map +1 -0
  21. package/dist/format-currency-oJD9jGYW.d.ts +6751 -0
  22. package/dist/index.d.ts +20 -30
  23. package/dist/index.js +11 -103
  24. package/dist/index.js.map +1 -1
  25. package/dist/index.server.d.ts +8 -0
  26. package/dist/index.server.js +7 -0
  27. package/dist/index.server.js.map +1 -0
  28. package/dist/nextjs/api.d.ts +10 -14
  29. package/dist/nextjs/api.js +8 -148
  30. package/dist/nextjs/api.js.map +1 -1
  31. package/dist/nextjs.d.ts +13 -3213
  32. package/dist/nextjs.js +11 -1793
  33. package/dist/nextjs.js.map +1 -1
  34. package/dist/nextjs.server.d.ts +1 -0
  35. package/dist/nextjs.server.js +1 -0
  36. package/dist/nextjs.server.js.map +1 -0
  37. package/dist/store-proxy-handler-CNzKlTFA.d.ts +10 -0
  38. package/dist/tanstack/api.d.ts +41 -0
  39. package/dist/tanstack/api.js +28 -0
  40. package/dist/tanstack/api.js.map +1 -0
  41. package/dist/tanstack.d.ts +13 -0
  42. package/dist/tanstack.js +11 -0
  43. package/dist/tanstack.js.map +1 -0
  44. package/dist/tanstack.server.d.ts +1 -0
  45. package/dist/tanstack.server.js +1 -0
  46. package/dist/tanstack.server.js.map +1 -0
  47. package/package.json +33 -13
  48. package/dist/collection-anMkdWa7.d.ts +0 -4333
  49. package/dist/core.d.ts +0 -3396
  50. package/dist/core.js +0 -1239
  51. package/dist/core.js.map +0 -1
  52. package/dist/find-storefront-cms-page-DNwLo1iV.d.ts +0 -178
  53. package/dist/react.d.ts +0 -964
  54. package/dist/react.js +0 -2670
  55. package/dist/react.js.map +0 -1
  56. /package/dist/{..d.ts → chunks/config.d.ts} +0 -0
package/dist/react.js DELETED
@@ -1,2670 +0,0 @@
1
- import { z } from 'zod';
2
- import { Modal } from '@ecomiq/ui/components/modal';
3
- import { formatCurrency as formatCurrency$1, StorefrontPageDetailSchema as StorefrontPageDetailSchema$1, useCart as useCart$1, useStore as useStore$1, useStorefrontAuthClient as useStorefrontAuthClient$1, useStorefrontSession as useStorefrontSession$1, SignInForm, SignUpForm, signInWithEcomiqCredentials } from '@ecomiq/storefront/react';
4
- import { toast, Toaster as Toaster$1 } from 'sonner';
5
- import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
6
- import { useTheme } from 'next-themes';
7
- import axios from 'axios';
8
- import { existsSync } from 'fs';
9
- import { createRequire } from 'module';
10
- import { join } from 'path';
11
- import '@opennextjs/cloudflare';
12
- import '@ecomiq/storefront/core';
13
- import { QueryClient, hydrate, QueryClientProvider, useMutation, useQuery, useQueryClient, useInfiniteQuery } from '@tanstack/react-query';
14
- import React2, { createContext, useRef, useEffect, useContext, useState, useMemo, useCallback } from 'react';
15
- import { create } from 'zustand';
16
- import { persist } from 'zustand/middleware';
17
- import { createAuthMiddleware, createAuthEndpoint, formCsrfMiddleware } from 'better-auth/api';
18
- import { APIError } from 'better-auth';
19
- import { setSessionCookie } from 'better-auth/cookies';
20
- import { parseUserOutput } from 'better-auth/db';
21
-
22
- var __defProp = Object.defineProperty;
23
- var __getOwnPropNames = Object.getOwnPropertyNames;
24
- var __esm = (fn, res) => function __init() {
25
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
26
- };
27
- var __export = (target, all) => {
28
- for (var name in all)
29
- __defProp(target, name, { get: all[name], enumerable: true });
30
- };
31
-
32
- // src/core/client/store-api-client.ts
33
- function setClientStoreId(storeId) {
34
- if (typeof window === "undefined") return;
35
- clientStoreIdGetter = storeId?.trim() || null;
36
- }
37
- async function getStoreApiBaseUrl() {
38
- if (typeof window !== "undefined") {
39
- return STORE_API_PATH;
40
- }
41
- if (serverRequestOriginGetter) {
42
- return `${serverRequestOriginGetter.replace(/\/$/, "")}/${STORE_API_PATH.replace(/^\//, "")}`;
43
- }
44
- return STORE_API_PATH;
45
- }
46
- async function getStoreApiClient() {
47
- const baseUrl = await getStoreApiBaseUrl();
48
- return new StoreApiClient(baseUrl);
49
- }
50
- var STORE_API_PATH, serverRequestOriginGetter, serverStoreIdGetter, clientStoreIdGetter, StoreApiClient;
51
- var init_store_api_client = __esm({
52
- "src/core/client/store-api-client.ts"() {
53
- STORE_API_PATH = "/api/store";
54
- serverRequestOriginGetter = null;
55
- serverStoreIdGetter = null;
56
- clientStoreIdGetter = null;
57
- StoreApiClient = class {
58
- baseUrl;
59
- constructor(baseUrl = STORE_API_PATH) {
60
- this.baseUrl = baseUrl.replace(/\/$/, "");
61
- }
62
- async request(path, init, options) {
63
- const pathNorm = path.replace(/^\/+/, "");
64
- const url = path.startsWith("http") ? path : `${this.baseUrl}/${pathNorm}`;
65
- const headers = new Headers(init.headers ?? {});
66
- headers.set("Content-Type", "application/json");
67
- headers.set("Accept-Encoding", "identity");
68
- const explicitStoreId = options?.xStoreId?.trim();
69
- const runtimeStoreId = explicitStoreId || (typeof window === "undefined" ? serverStoreIdGetter : clientStoreIdGetter);
70
- if (runtimeStoreId && !headers.has("x-store-id")) {
71
- headers.set("X-Store-Id", runtimeStoreId);
72
- }
73
- if (options?.xStoreDomain) {
74
- headers.set("X-Store-Domain", options.xStoreDomain);
75
- }
76
- if (options?.xCartId) {
77
- headers.set("X-Cart-Token", options.xCartId);
78
- }
79
- const bearer = typeof options?.bearerToken === "string" && options.bearerToken.trim().length > 0 ? options.bearerToken.trim() : void 0;
80
- if (bearer) {
81
- headers.set(
82
- "Authorization",
83
- bearer.toLowerCase().startsWith("bearer ") ? bearer : `Bearer ${bearer}`
84
- );
85
- }
86
- const isServer = typeof window === "undefined";
87
- const res = await fetch(url, {
88
- ...init,
89
- headers,
90
- credentials: isServer ? "same-origin" : "include",
91
- ...options?.noStore ? { cache: "no-store" } : {},
92
- ...options?.cacheKey && isServer && !options?.noStore ? {
93
- next: {
94
- revalidate: 60,
95
- tags: [options.cacheKey]
96
- }
97
- } : {}
98
- });
99
- if (!res.ok) {
100
- let body;
101
- try {
102
- const errorText = await res.text();
103
- body = errorText ? JSON.parse(errorText) : void 0;
104
- } catch {
105
- body = void 0;
106
- }
107
- const msg = typeof body === "object" && body !== null && "title" in body && typeof body.title === "string" ? body.title : res.statusText || "Store API error";
108
- const err = new Error(msg);
109
- err.status = res.status;
110
- err.body = body;
111
- throw err;
112
- }
113
- const text = await res.text();
114
- if (!text) return void 0;
115
- return JSON.parse(text);
116
- }
117
- get(path, options) {
118
- return this.request(path, { method: "GET" }, options);
119
- }
120
- post(path, data, options) {
121
- return this.request(
122
- path,
123
- {
124
- method: "POST",
125
- body: data != null ? JSON.stringify(data) : void 0
126
- },
127
- options
128
- );
129
- }
130
- patch(path, data, options) {
131
- return this.request(path, {
132
- method: "PATCH",
133
- body: data != null ? JSON.stringify(data) : void 0
134
- }, options);
135
- }
136
- put(path, data, options) {
137
- return this.request(
138
- path,
139
- {
140
- method: "PUT",
141
- body: data != null ? JSON.stringify(data) : void 0
142
- },
143
- options
144
- );
145
- }
146
- delete(path, options) {
147
- return this.request(path, { method: "DELETE" }, options);
148
- }
149
- };
150
- new StoreApiClient(STORE_API_PATH);
151
- }
152
- });
153
- function customerAuthOptions(accessToken) {
154
- const token = typeof accessToken === "string" && accessToken.trim().length > 0 ? accessToken.trim() : void 0;
155
- return token ? { bearerToken: token } : {};
156
- }
157
- async function registerCustomer(data) {
158
- const payload = CustomerRegisterRequestSchema.parse(data);
159
- const client = await getStoreApiClient();
160
- return client.post("customer/register", payload);
161
- }
162
- async function getCustomerMe(accessToken) {
163
- const client = await getStoreApiClient();
164
- const response = await client.get("customer", customerAuthOptions(accessToken));
165
- return CustomerMeSchema.parse(response);
166
- }
167
- var CustomerRegisterRequestSchema, CustomerAddressSchema, CustomerMeSchema;
168
- var init_store_api_customer_service = __esm({
169
- "src/core/client/store-api-customer.service.ts"() {
170
- init_store_api_client();
171
- CustomerRegisterRequestSchema = z.object({
172
- email: z.string().email(),
173
- firstName: z.string().trim().min(1),
174
- lastName: z.string().trim().min(1),
175
- countryIsoCode: z.string().trim().length(2),
176
- password: z.string(),
177
- phone: z.string().trim().optional()
178
- });
179
- CustomerAddressSchema = z.object({
180
- /** Presente en listados cuando el upstream expone el id para PUT/DELETE. */
181
- id: z.string().optional(),
182
- addressKey: z.string(),
183
- countryIsoCode: z.string(),
184
- addressLine: z.string(),
185
- addressNumber: z.string(),
186
- department: z.string(),
187
- province: z.string(),
188
- district: z.string(),
189
- phone: z.string(),
190
- cellPhone: z.string(),
191
- postalCode: z.string(),
192
- reference: z.string(),
193
- latitude: z.number().nullable(),
194
- longitude: z.number().nullable(),
195
- isDefault: z.boolean()
196
- });
197
- CustomerAddressSchema.omit({ id: true });
198
- CustomerMeSchema = z.object({
199
- id: z.string(),
200
- storeId: z.string(),
201
- email: z.string().email(),
202
- firstName: z.string(),
203
- lastName: z.string(),
204
- phone: z.string(),
205
- identificationNumber: z.string().nullable(),
206
- identificationType: z.string().nullable(),
207
- entityName: z.string().nullable(),
208
- dateOfBirth: z.string().nullable(),
209
- gender: z.string().nullable(),
210
- countryIsoCode: z.string(),
211
- role: z.string().nullable(),
212
- companyName: z.string().nullable(),
213
- consent: z.boolean(),
214
- userId: z.string(),
215
- customerType: z.string(),
216
- status: z.string(),
217
- entityIdentificationNumber: z.string().nullable(),
218
- defaultAddressId: z.string().nullable(),
219
- metadata: z.unknown().nullable(),
220
- totalOrders: z.number(),
221
- avgOrders: z.number(),
222
- lastOrderId: z.string().nullable(),
223
- lastOrderDate: z.string().nullable(),
224
- loyaltyPoints: z.number(),
225
- addresses: z.array(CustomerAddressSchema),
226
- createdOn: z.string(),
227
- updatedOn: z.string().nullable()
228
- });
229
- z.object({
230
- firstName: z.string().trim().min(1),
231
- lastName: z.string().trim().min(1),
232
- phone: z.string().trim(),
233
- identificationNumber: z.string().nullable(),
234
- identificationType: z.string().nullable(),
235
- entityName: z.string().nullable(),
236
- entityIdentificationNumber: z.string().nullable(),
237
- gender: z.string().nullable(),
238
- role: z.string().nullable(),
239
- companyName: z.string().nullable(),
240
- dateOfBirth: z.string().nullable(),
241
- consent: z.boolean(),
242
- countryIsoCode: z.string().trim().length(2)
243
- });
244
- }
245
- });
246
- var ModalsFactory;
247
- var init_modals_factory = __esm({
248
- "src/react/shell/components/modals-factory.tsx"() {
249
- "use client";
250
- init_store_api_customer_service();
251
- ModalsFactory = () => {
252
- const cart = useCart$1();
253
- const store = useStore$1();
254
- const authClient = useStorefrontAuthClient$1();
255
- const { refetch } = useStorefrontSession$1();
256
- const handlerLogin = async (values) => {
257
- try {
258
- await signInWithEcomiqCredentials(authClient, store.data, values);
259
- const me = await getCustomerMe();
260
- const displayName = `${me.firstName} ${me.lastName}`.trim() || me.email;
261
- const updateUserResponse = await authClient.$fetch("/update-user", {
262
- method: "POST",
263
- body: {
264
- name: displayName,
265
- firstName: me.firstName,
266
- lastName: me.lastName,
267
- phone: me.phone
268
- }
269
- });
270
- if (updateUserResponse.error) {
271
- throw new Error(updateUserResponse.error?.message ?? "No se pudo sincronizar el perfil");
272
- }
273
- if (me.email) {
274
- cart.setCheckoutCustomerLocal({
275
- email: me.email,
276
- firstName: me.firstName,
277
- lastName: me.lastName,
278
- phone: me.phone ?? ""
279
- });
280
- }
281
- await refetch();
282
- toast.success("Sesi\xF3n iniciada correctamente");
283
- store.closeModal("login");
284
- } catch (error) {
285
- console.error("Error al iniciar sesion:", error);
286
- toast.error("Error al iniciar sesion");
287
- }
288
- };
289
- const handlerSignup = async (values) => {
290
- try {
291
- await registerCustomer({
292
- email: values.email,
293
- firstName: values.firstName,
294
- lastName: values.lastName,
295
- countryIsoCode: values.countryIsoCode,
296
- password: values.password,
297
- phone: values.phone
298
- });
299
- toast.success("Cuenta creada correctamente");
300
- store.closeModal("signup");
301
- setTimeout(() => {
302
- store.openModal("login");
303
- }, 1e3);
304
- } catch (error) {
305
- console.error("Error al registrarse:", error);
306
- toast.error("Error al registrarse");
307
- }
308
- };
309
- return /* @__PURE__ */ jsxs(
310
- Modal,
311
- {
312
- isOpen: store.modals.login || store.modals.signup,
313
- onClose: () => store.closeAllModals(),
314
- children: [
315
- store.modals.login && /* @__PURE__ */ jsxs(Fragment, { children: [
316
- /* @__PURE__ */ jsx(SignInForm, { onSubmit: (values) => handlerLogin(values) }),
317
- /* @__PURE__ */ jsxs("div", { className: "flex justify-center items-center mt-4", children: [
318
- /* @__PURE__ */ jsx("p", { className: "text-gray-500", children: "No tienes cuenta?" }),
319
- /* @__PURE__ */ jsx(
320
- "button",
321
- {
322
- type: "button",
323
- onClick: () => store.openModal("signup"),
324
- className: "text-primary hover:underline ml-2 cursor-pointer",
325
- children: "Registrate"
326
- }
327
- )
328
- ] })
329
- ] }),
330
- store.modals.signup && /* @__PURE__ */ jsxs(Fragment, { children: [
331
- /* @__PURE__ */ jsx(SignUpForm, { onSubmit: (values) => handlerSignup(values) }),
332
- /* @__PURE__ */ jsxs("div", { className: "flex justify-center items-center mt-4", children: [
333
- /* @__PURE__ */ jsx("p", { className: "text-gray-500", children: "Ya tienes cuenta?" }),
334
- /* @__PURE__ */ jsx(
335
- "button",
336
- {
337
- type: "button",
338
- onClick: () => store.openModal("login"),
339
- className: "text-primary hover:underline ml-2 cursor-pointer",
340
- children: "Inicia sesion"
341
- }
342
- )
343
- ] })
344
- ] })
345
- ]
346
- }
347
- );
348
- };
349
- }
350
- });
351
- var Toaster;
352
- var init_Toaster = __esm({
353
- "src/react/shell/components/Toaster.tsx"() {
354
- "use client";
355
- Toaster = ({ ...props }) => {
356
- const { theme = "system" } = useTheme();
357
- return /* @__PURE__ */ jsx(
358
- Toaster$1,
359
- {
360
- theme,
361
- className: "toaster group",
362
- style: {
363
- "--normal-bg": "var(--popover)",
364
- "--normal-text": "var(--popover-foreground)",
365
- "--normal-border": "var(--border)"
366
- },
367
- ...props
368
- }
369
- );
370
- };
371
- }
372
- });
373
-
374
- // src/react/shell/providers/ecomiq-shell-ui.tsx
375
- var ecomiq_shell_ui_exports = {};
376
- __export(ecomiq_shell_ui_exports, {
377
- EcomiqShellUi: () => EcomiqShellUi
378
- });
379
- function EcomiqShellUi() {
380
- return /* @__PURE__ */ jsxs(Fragment, { children: [
381
- /* @__PURE__ */ jsx(ModalsFactory, {}),
382
- /* @__PURE__ */ jsx(Toaster, { position: "bottom-right" })
383
- ] });
384
- }
385
- var init_ecomiq_shell_ui = __esm({
386
- "src/react/shell/providers/ecomiq-shell-ui.tsx"() {
387
- "use client";
388
- init_modals_factory();
389
- init_Toaster();
390
- }
391
- });
392
-
393
- // src/core/constants/api-paths.ts
394
- var STOREFRONT_API_PATH_PREFIX = "v1";
395
-
396
- // src/core/constants/storefront-ecomiq-oauth-cookie.ts
397
- var STOREFRONT_ECOMIQ_OAUTH_COOKIE_NAME = "ecomiq_sf_oauth_at";
398
- var STOREFRONT_ECOMIQ_OAUTH_COOKIE_PATH = "/api/store";
399
-
400
- // src/core/constants/currency.ts
401
- var countryCurrency = {
402
- "PEN": "es-PE",
403
- "USD": "en-US",
404
- "COP": "es-CO",
405
- "MXN": "es-MX"
406
- };
407
-
408
- // src/core/utils/format-currency.ts
409
- function formatCurrency(amount, currency = "USD") {
410
- const locale = countryCurrency[currency] ?? "en-US";
411
- return new Intl.NumberFormat(locale, {
412
- style: "currency",
413
- currency
414
- }).format(amount);
415
- }
416
- var StorefrontCheckoutSettingsSchema = z.object({
417
- allowGuestCheckout: z.boolean().optional(),
418
- identityScope: z.enum(["store", "tenant", "global"]).optional(),
419
- signInMethods: z.array(z.string()).optional()
420
- });
421
- var looseRecord = z.record(z.string(), z.unknown());
422
- var StorefrontThemeConfigSchema = z.object({
423
- name: z.string().nullable().optional(),
424
- mode: z.string().nullable().optional(),
425
- colors: looseRecord.optional(),
426
- fonts: looseRecord.optional(),
427
- assets: looseRecord.optional()
428
- }).passthrough();
429
- var StorefrontFeaturesEnabledSchema = z.object({
430
- flags: looseRecord.optional()
431
- }).passthrough();
432
- var StorefrontCmsBrandingSchema = z.object({
433
- logoUrl: z.string().nullable().optional(),
434
- logoDarkUrl: z.string().nullable().optional(),
435
- faviconUrl: z.string().nullable().optional(),
436
- socialImageUrl: z.string().nullable().optional()
437
- });
438
- var StorefrontCmsSeoSchema = z.object({
439
- siteName: z.string().nullable().optional(),
440
- metaTitle: z.string().nullable().optional(),
441
- metaDescription: z.string().nullable().optional(),
442
- titleTemplate: z.string().nullable().optional(),
443
- robotsIndex: z.boolean().optional()
444
- });
445
- var StorefrontCmsSocialLinksSchema = z.object({
446
- instagram: z.string().nullable().optional(),
447
- facebook: z.string().nullable().optional(),
448
- tiktok: z.string().nullable().optional(),
449
- youtube: z.string().nullable().optional(),
450
- x: z.string().nullable().optional()
451
- });
452
- var StorefrontCmsContactSchema = z.object({
453
- supportEmail: z.string().nullable().optional(),
454
- phone: z.string().nullable().optional(),
455
- whatsapp: z.string().nullable().optional(),
456
- address: z.string().nullable().optional(),
457
- socialLinks: StorefrontCmsSocialLinksSchema.optional()
458
- });
459
- var StorefrontCmsTrackingSchema = z.object({
460
- googleAnalyticsMeasurementId: z.string().nullable().optional(),
461
- metaPixelId: z.string().nullable().optional(),
462
- googleTagManagerContainerId: z.string().nullable().optional()
463
- });
464
- var StorefrontCmsCustomScriptsSchema = z.object({
465
- enabled: z.boolean().optional(),
466
- head: z.string().nullable().optional(),
467
- bodyEnd: z.string().nullable().optional()
468
- });
469
- var StorefrontCmsSettingsSchema = z.object({
470
- themeKey: z.string().optional(),
471
- branding: StorefrontCmsBrandingSchema.optional(),
472
- seo: StorefrontCmsSeoSchema.optional(),
473
- contact: StorefrontCmsContactSchema.optional(),
474
- tracking: StorefrontCmsTrackingSchema.optional(),
475
- customScripts: StorefrontCmsCustomScriptsSchema.optional(),
476
- updatedOn: z.string().nullable().optional()
477
- });
478
- var StorefrontCmsPageSchema = z.object({
479
- id: z.string(),
480
- name: z.string(),
481
- slug: z.string(),
482
- layout: z.string().optional(),
483
- version: z.number().optional(),
484
- settings: z.record(z.string(), z.unknown()).optional().nullable(),
485
- sections: z.array(z.unknown()).optional(),
486
- status: z.string().optional(),
487
- hasPublishedVersion: z.boolean().optional(),
488
- publishedOn: z.string().nullable().optional(),
489
- publishedBy: z.string().nullable().optional(),
490
- createdOn: z.string().nullable().optional(),
491
- updatedOn: z.string().nullable().optional(),
492
- createdBy: z.string().nullable().optional(),
493
- updatedBy: z.string().nullable().optional(),
494
- lastModifiedBy: z.string().nullable().optional()
495
- }).passthrough();
496
- var StorefrontSettingsLegacyFlatSchema = z.object({
497
- id: z.coerce.string(),
498
- tenantId: z.string().nullable().optional(),
499
- name: z.string(),
500
- slug: z.coerce.string().optional(),
501
- description: z.string().nullable().optional(),
502
- visibility: z.string().nullable().optional(),
503
- logo: z.string().nullable().optional(),
504
- favicon: z.string().nullable().optional(),
505
- icon: z.string().nullable().optional(),
506
- currency: z.string().nullable().optional(),
507
- language: z.string().nullable().optional(),
508
- themeConfig: z.unknown().optional().nullable(),
509
- featuresEnabled: z.unknown().optional().nullable(),
510
- checkoutSettings: StorefrontCheckoutSettingsSchema.optional().nullable()
511
- }).passthrough();
512
- function unwrapSettingsResponseBody(v) {
513
- if (!v || typeof v !== "object" || Array.isArray(v)) return v;
514
- const o = v;
515
- if ("data" in o && o.data != null && typeof o.data === "object" && !Array.isArray(o.data)) {
516
- return o.data;
517
- }
518
- return v;
519
- }
520
- function isLegacyFlatSettingsPayload(v) {
521
- if (!v || typeof v !== "object" || Array.isArray(v)) return false;
522
- const o = v;
523
- if ("store" in o || "schemaVersion" in o || "cms" in o) return false;
524
- return (typeof o.id === "string" || typeof o.id === "number") && typeof o.name === "string";
525
- }
526
- function normalizeStorefrontSettingsPayload(v) {
527
- const body = unwrapSettingsResponseBody(v);
528
- if (!isLegacyFlatSettingsPayload(body)) return body;
529
- const raw = body;
530
- const parsed = StorefrontSettingsLegacyFlatSchema.safeParse(raw);
531
- if (!parsed.success) return body;
532
- const l = parsed.data;
533
- const currency = l.currency?.trim() ? l.currency : "PEN";
534
- const language = l.language?.trim() ? l.language : "es";
535
- const slug = l.slug?.trim() ? l.slug : l.id;
536
- return {
537
- schemaVersion: 0,
538
- generatedAt: (/* @__PURE__ */ new Date(0)).toISOString(),
539
- store: {
540
- id: l.id,
541
- tenantId: l.tenantId ?? void 0,
542
- name: l.name,
543
- slug,
544
- description: l.description ?? null,
545
- logo: l.logo ?? null,
546
- icon: l.icon ?? null,
547
- visibility: l.visibility ?? void 0,
548
- currency,
549
- language,
550
- themeConfig: l.themeConfig ?? void 0,
551
- featuresEnabled: l.featuresEnabled ?? void 0,
552
- checkoutSettings: l.checkoutSettings ?? void 0
553
- },
554
- cms: {
555
- settings: {
556
- branding: {
557
- faviconUrl: l.favicon ?? null,
558
- logoUrl: l.logo ?? null,
559
- logoDarkUrl: null,
560
- socialImageUrl: null
561
- }
562
- },
563
- pages: []
564
- }
565
- };
566
- }
567
- var StorefrontStoreSchema = z.object({
568
- id: z.string(),
569
- tenantId: z.string().optional(),
570
- sellerId: z.string().optional(),
571
- plan: z.string().optional(),
572
- storageLimitBytes: z.number().optional(),
573
- monthlyTransferLimitBytes: z.number().optional(),
574
- maxUploadSizeBytes: z.number().optional(),
575
- maxStores: z.number().optional(),
576
- maxMembers: z.number().optional(),
577
- maxProducts: z.number().optional(),
578
- maxCategories: z.number().optional(),
579
- maxBrands: z.number().optional(),
580
- maxAttributes: z.number().optional(),
581
- maxCollections: z.number().optional(),
582
- enableApi: z.boolean().optional(),
583
- isActive: z.boolean().optional(),
584
- name: z.string(),
585
- description: z.string().nullable().optional(),
586
- avatarUrl: z.string().nullable().optional(),
587
- logo: z.string().nullable().optional(),
588
- icon: z.string().nullable().optional(),
589
- slug: z.string(),
590
- domain: z.string().nullable().optional(),
591
- domains: z.array(z.string()).optional(),
592
- timeZone: z.string().nullable().optional(),
593
- currency: z.string(),
594
- language: z.string(),
595
- visibility: z.string().optional(),
596
- privateAccessHash: z.string().nullable().optional(),
597
- privateAccessVersion: z.number().optional(),
598
- themeConfig: StorefrontThemeConfigSchema.optional(),
599
- featuresEnabled: StorefrontFeaturesEnabledSchema.optional(),
600
- checkoutSettings: StorefrontCheckoutSettingsSchema.optional().nullable()
601
- }).passthrough();
602
- var StorefrontSettingsEnvelopeSchema = z.object({
603
- schemaVersion: z.number(),
604
- generatedAt: z.string(),
605
- store: StorefrontStoreSchema,
606
- cms: z.object({
607
- settings: StorefrontCmsSettingsSchema,
608
- pages: z.array(StorefrontCmsPageSchema)
609
- })
610
- }).passthrough();
611
- var StorefrontSettingsSchema = z.preprocess(
612
- normalizeStorefrontSettingsPayload,
613
- StorefrontSettingsEnvelopeSchema
614
- );
615
- function storefrontStoreRecord(settings) {
616
- return settings?.store ?? null;
617
- }
618
- function storefrontFaviconUrl(settings) {
619
- const b = settings?.cms?.settings?.branding;
620
- return b?.faviconUrl ?? settings?.store?.logo ?? settings?.store?.icon ?? null;
621
- }
622
- function storefrontOgImageUrl(settings) {
623
- const b = settings?.cms?.settings?.branding;
624
- return b?.socialImageUrl ?? settings?.store?.logo ?? null;
625
- }
626
-
627
- // src/core/lib/ecomiq-oauth-identity.ts
628
- function buildEcomiqOAuthIdentityHeaders(settings) {
629
- const record = storefrontStoreRecord(settings);
630
- if (!record?.id) {
631
- throw new Error("Tienda no disponible para iniciar sesi\xF3n");
632
- }
633
- const checkout = record.checkoutSettings;
634
- const identityScope = checkout?.identityScope === "tenant" ? "tenant" : "store";
635
- const headers = {
636
- "x-Identity-Scope": identityScope,
637
- "x-Store-Id": record.id
638
- };
639
- if (record.tenantId) headers["x-Tenant-Id"] = record.tenantId;
640
- return headers;
641
- }
642
-
643
- // src/core/lib/customer-order-labels.ts
644
- var ORDER_STATUS_LABELS = {
645
- New: "Pedido realizado",
646
- Confirmed: "Confirmado",
647
- Processing: "En preparaci\xF3n",
648
- InTransit: "En camino",
649
- Delivered: "Entregado",
650
- Cancelled: "Cancelado",
651
- Canceled: "Cancelado",
652
- Refunded: "Reembolsado",
653
- Pending: "Pendiente",
654
- Paid: "Pagado",
655
- Captured: "Pago confirmado"
656
- };
657
- function formatCustomerOrderStatus(status) {
658
- const trimmed = status.trim();
659
- if (!trimmed) return "Desconocido";
660
- return ORDER_STATUS_LABELS[trimmed] ?? trimmed.replace(/([A-Z])/g, " $1").trim();
661
- }
662
- function customerOrderStatusTone(status) {
663
- const s = status.toLowerCase();
664
- if (s.includes("cancel") || s.includes("refund")) return "danger";
665
- if (s.includes("deliver")) return "success";
666
- if (s.includes("transit") || s.includes("ship")) return "info";
667
- if (s.includes("confirm") || s.includes("paid") || s.includes("capture")) return "success";
668
- if (s.includes("pending") || s.includes("new") || s.includes("process")) return "warning";
669
- return "default";
670
- }
671
- function formatOrderDate(iso, locale = "es-PE") {
672
- const d = new Date(iso);
673
- if (Number.isNaN(d.getTime())) return iso;
674
- return new Intl.DateTimeFormat(locale, {
675
- day: "numeric",
676
- month: "long",
677
- year: "numeric"
678
- }).format(d);
679
- }
680
- function formatOrderDateTime(iso, locale = "es-PE") {
681
- const d = new Date(iso);
682
- if (Number.isNaN(d.getTime())) return iso;
683
- return new Intl.DateTimeFormat(locale, {
684
- day: "numeric",
685
- month: "short",
686
- year: "numeric",
687
- hour: "2-digit",
688
- minute: "2-digit"
689
- }).format(d);
690
- }
691
-
692
- // src/core/lib/storefront-geo-inei.ts
693
- function toGeoItems(data) {
694
- if (!Array.isArray(data)) return [];
695
- return data.map((raw) => {
696
- if (!raw || typeof raw !== "object") return null;
697
- const item = raw;
698
- return {
699
- code: String(item.code ?? ""),
700
- name: String(item.name ?? "")
701
- };
702
- }).filter((x) => !!x && !!x.code && !!x.name);
703
- }
704
- var departmentsCache = null;
705
- var departmentsRequest = null;
706
- function loadStorefrontGeoDepartments(headers) {
707
- if (departmentsCache) return Promise.resolve(departmentsCache);
708
- if (departmentsRequest) return departmentsRequest;
709
- departmentsRequest = fetch(
710
- `/api/store/geo/departments?countryCode=PE&source=inei&limit=200`,
711
- { cache: "no-store", headers }
712
- ).then((r) => r.json()).then((data) => {
713
- const items = toGeoItems(data);
714
- departmentsCache = items;
715
- return items;
716
- }).catch(() => {
717
- departmentsCache = [];
718
- return [];
719
- }).finally(() => {
720
- departmentsRequest = null;
721
- });
722
- return departmentsRequest;
723
- }
724
- async function fetchStorefrontGeoProvinces(headers, countryCode, departmentCode) {
725
- const res = await fetch(
726
- `/api/store/geo/provinces?countryCode=${encodeURIComponent(
727
- countryCode
728
- )}&departmentCode=${encodeURIComponent(departmentCode)}&source=inei&limit=200`,
729
- { headers }
730
- );
731
- const data = await res.json();
732
- return toGeoItems(data);
733
- }
734
- async function fetchStorefrontGeoDistricts(headers, countryCode, provinceCode) {
735
- const res = await fetch(
736
- `/api/store/geo/districts?countryCode=${encodeURIComponent(
737
- countryCode
738
- )}&provinceCode=${encodeURIComponent(provinceCode)}&source=inei`,
739
- { headers }
740
- );
741
- const data = await res.json();
742
- return toGeoItems(data);
743
- }
744
- async function resolveIneiCodesFromLabels(storeId, labels, countryCode = "PE") {
745
- const id = storeId.trim();
746
- if (!id) return null;
747
- const headers = { "X-Store-Id": id };
748
- const departments = await loadStorefrontGeoDepartments(headers);
749
- const dept = departments.find((d) => d.name.trim() === labels.department.trim());
750
- if (!dept) return null;
751
- const provinces = await fetchStorefrontGeoProvinces(headers, countryCode, dept.code);
752
- const prov = provinces.find((p) => p.name.trim() === labels.province.trim());
753
- if (!prov) {
754
- return { departmentCode: dept.code, provinceCode: "", districtCode: "" };
755
- }
756
- const districts = await fetchStorefrontGeoDistricts(headers, countryCode, prov.code);
757
- const dist = districts.find((d) => d.name.trim() === labels.district.trim());
758
- return {
759
- departmentCode: dept.code,
760
- provinceCode: prov.code,
761
- districtCode: dist?.code ?? ""
762
- };
763
- }
764
- var ApiClient = class {
765
- client;
766
- storeId;
767
- storeDomain;
768
- constructor(baseURL = "https://storefront.ecomiq.pe") {
769
- this.client = axios.create({
770
- baseURL,
771
- timeout: 1e4,
772
- headers: {
773
- "Content-Type": "application/json"
774
- }
775
- });
776
- this.client.interceptors.request.use(
777
- (config) => {
778
- if (config.headers) {
779
- if (this.storeId) config.headers["X-Store-Id"] = this.storeId;
780
- if (this.storeDomain) config.headers["X-Store-Domain"] = this.storeDomain;
781
- }
782
- return config;
783
- }
784
- );
785
- this.client.interceptors.response.use(
786
- (response) => response,
787
- (error) => {
788
- console.error("API Error:", error);
789
- return Promise.reject(error);
790
- }
791
- );
792
- }
793
- setStoreId(storeId) {
794
- this.storeId = storeId;
795
- }
796
- /** Subdominio para endpoints que usan X-Store-Domain (ej. GET /v1/storefront/settings) */
797
- setStoreDomain(domain) {
798
- this.storeDomain = domain;
799
- }
800
- get(url, config) {
801
- return this.client.get(url, config).then((response) => response.data);
802
- }
803
- post(url, data, config) {
804
- return this.client.post(url, data, config).then((response) => response.data);
805
- }
806
- put(url, data, config) {
807
- return this.client.put(url, data, config).then((response) => response.data);
808
- }
809
- patch(url, data, config) {
810
- return this.client.patch(url, data, config).then((response) => response.data);
811
- }
812
- delete(url, config) {
813
- return this.client.delete(url, config).then((response) => response.data);
814
- }
815
- };
816
- var apiClient = new ApiClient();
817
- var ProductVariantSchema = z.object({
818
- id: z.coerce.string(),
819
- name: z.string(),
820
- storeId: z.string(),
821
- productId: z.coerce.string(),
822
- basePrice: z.number().positive().optional().nullable(),
823
- comparePrice: z.number().positive().optional().nullable(),
824
- //TODO: remove this field
825
- originalPrice: z.number().positive().optional().nullable(),
826
- costPrice: z.number().positive().optional().nullable(),
827
- isDefault: z.boolean().optional().nullable(),
828
- reserveStock: z.number().nonnegative().optional().nullable(),
829
- weight: z.number().nonnegative().optional().nullable().default(0),
830
- width: z.number().nonnegative().optional().nullable(),
831
- height: z.number().nonnegative().optional().nullable(),
832
- unitOfMeasure: z.string().optional().nullable(),
833
- order: z.number().nonnegative().optional().nullable().default(0),
834
- sku: z.string().optional().nullable(),
835
- stock: z.number().nonnegative().optional().nullable(),
836
- leadTime: z.string().optional().nullable(),
837
- barCode: z.string().optional().nullable(),
838
- shippingWeight: z.number().nonnegative().optional().nullable(),
839
- images: z.array(z.string()).optional().nullable(),
840
- attributes: z.array(z.string()).optional().nullable(),
841
- variantStatus: z.string().optional().nullable(),
842
- status: z.string().optional().nullable(),
843
- createdOn: z.string().optional().nullable(),
844
- updatedOn: z.string().optional().nullable(),
845
- deletedOn: z.string().optional().nullable()
846
- });
847
- var ProductSchema = z.object({
848
- id: z.coerce.string(),
849
- name: z.string(),
850
- images: z.array(z.object({
851
- url: z.string().url(),
852
- name: z.string().optional(),
853
- isPrimary: z.boolean().optional()
854
- })),
855
- basePrice: z.number().positive().optional(),
856
- salePrice: z.number().positive().optional(),
857
- slug: z.string(),
858
- description: z.string().optional(),
859
- categories: z.array(z.object({
860
- id: z.coerce.string(),
861
- name: z.string(),
862
- slug: z.string(),
863
- logo: z.string().optional(),
864
- icon: z.string().optional()
865
- })).optional(),
866
- tags: z.array(z.string()).optional(),
867
- inStock: z.boolean().optional(),
868
- variants: z.array(ProductVariantSchema).optional()
869
- });
870
- var ProductsResponseSchema = z.object({
871
- items: z.array(ProductSchema),
872
- meta: z.object({
873
- total: z.number().int().positive(),
874
- page: z.number().int().positive(),
875
- pageSize: z.number().int().positive(),
876
- hasNextPage: z.boolean()
877
- })
878
- });
879
- var ProductsFiltersSchema = z.object({
880
- category: z.string().optional(),
881
- search: z.string().optional(),
882
- minPrice: z.number().positive().optional(),
883
- maxPrice: z.number().positive().optional(),
884
- inStock: z.boolean().optional(),
885
- sortBy: z.enum(["name", "price", "createdAt"]).optional(),
886
- sortOrder: z.enum(["asc", "desc"]).optional(),
887
- page: z.number().int().positive().optional(),
888
- limit: z.number().int().positive().optional()
889
- });
890
- var StorefrontProductSchema = z.object({
891
- id: z.coerce.string(),
892
- name: z.string(),
893
- slug: z.string(),
894
- productType: z.string().optional().nullable(),
895
- imageUrl: z.string().optional().nullable(),
896
- price: z.number().nonnegative(),
897
- compareAtPrice: z.number().nonnegative().optional().nullable(),
898
- variantCount: z.number().int().nonnegative().optional().default(0),
899
- isAvailable: z.boolean(),
900
- totalInventory: z.number().int().nonnegative(),
901
- bundleItemsCount: z.number().int().nonnegative().optional().default(0),
902
- brand: z.string().optional().nullable(),
903
- category: z.string().optional().nullable(),
904
- status: z.string().optional().nullable(),
905
- tags: z.array(z.string()).optional().nullable()
906
- });
907
- var StorefrontFacetSchema = z.object({
908
- value: z.string(),
909
- count: z.number().int().nonnegative()
910
- });
911
- var StorefrontProductsResponseSchema = z.object({
912
- pageIndex: z.number().int().positive(),
913
- pageSize: z.number().int().positive(),
914
- totalCount: z.number().int().nonnegative(),
915
- totalPages: z.number().int().nonnegative(),
916
- products: z.array(StorefrontProductSchema),
917
- categories: z.array(StorefrontFacetSchema).optional().default([]),
918
- brands: z.array(StorefrontFacetSchema).optional().default([]),
919
- minPrice: z.number().nonnegative().optional().nullable(),
920
- maxPrice: z.number().nonnegative().optional().nullable()
921
- });
922
- var StorefrontProductsParamsSchema = z.object({
923
- page: z.number().int().positive().optional().default(1),
924
- pageSize: z.number().int().positive().optional().default(20),
925
- sort: z.enum(["price_asc", "price_desc", "name_asc", "name_desc", "newest"]).optional().default("price_asc"),
926
- sellerId: z.string().optional(),
927
- search: z.string().optional(),
928
- collection: z.string().optional(),
929
- category: z.string().optional(),
930
- brand: z.string().optional(),
931
- productType: z.string().optional(),
932
- inStock: z.boolean().optional(),
933
- minPrice: z.number().nonnegative().optional(),
934
- maxPrice: z.number().nonnegative().optional()
935
- });
936
- var StorefrontVariantOptionSchema = z.object({
937
- key: z.string(),
938
- name: z.string(),
939
- value: z.string()
940
- });
941
- var StorefrontProductImageSchema = z.object({
942
- url: z.string(),
943
- name: z.string().optional().nullable(),
944
- order: z.number().int().nonnegative().optional()
945
- });
946
- var StorefrontVariantSchema = z.object({
947
- id: z.coerce.string().optional().default(""),
948
- sku: z.string(),
949
- status: z.string().optional().nullable(),
950
- price: z.number().nonnegative(),
951
- compareAtPrice: z.number().nonnegative().optional().nullable(),
952
- quantity: z.number().int().nonnegative(),
953
- options: z.array(StorefrontVariantOptionSchema).optional().default([]),
954
- images: z.array(StorefrontProductImageSchema).optional().default([])
955
- });
956
- var StorefrontPriceTierSchema = z.object({
957
- variantId: z.coerce.string(),
958
- minQuantity: z.number().int().positive(),
959
- maxQuantity: z.number().int().positive().optional().nullable(),
960
- unitPrice: z.number().nonnegative()
961
- });
962
- var StorefrontBundleItemSchema = z.object({
963
- productId: z.coerce.string(),
964
- variantId: z.coerce.string(),
965
- productName: z.string(),
966
- variantName: z.string().optional().nullable(),
967
- sku: z.string().optional().nullable(),
968
- brand: z.string().optional().nullable(),
969
- imageUrl: z.string().optional().nullable(),
970
- quantity: z.number().int(),
971
- availableStock: z.number().int().optional().default(0),
972
- allowBackorders: z.boolean().optional().default(false),
973
- requiresShipping: z.boolean().optional().default(true)
974
- });
975
- var StorefrontProductDetailSchema = z.object({
976
- id: z.coerce.string(),
977
- name: z.string(),
978
- slug: z.string(),
979
- description: z.string().optional().nullable(),
980
- productType: z.string().optional().nullable(),
981
- status: z.string().optional().nullable(),
982
- imageUrl: z.string().optional().nullable(),
983
- price: z.number().nonnegative(),
984
- compareAtPrice: z.number().nonnegative().optional().nullable(),
985
- isAvailable: z.boolean(),
986
- totalInventory: z.number().int().nonnegative(),
987
- brand: z.string().optional().nullable(),
988
- category: z.string().optional().nullable(),
989
- categoryId: z.coerce.string().optional().nullable(),
990
- tags: z.array(z.string()).optional().nullable(),
991
- specifications: z.array(z.record(z.string(), z.unknown())).optional().default([]),
992
- variants: z.array(StorefrontVariantSchema).optional().default([]),
993
- images: z.array(StorefrontProductImageSchema).optional().default([]),
994
- bundleItems: z.array(StorefrontBundleItemSchema).optional().default([]),
995
- priceTiers: z.array(StorefrontPriceTierSchema).optional().default([]),
996
- createdAt: z.string().optional().nullable(),
997
- updatedAt: z.string().optional().nullable()
998
- });
999
-
1000
- // src/core/services/product.service.ts
1001
- var ProductService = class _ProductService {
1002
- static instance;
1003
- constructor() {
1004
- }
1005
- static getInstance() {
1006
- if (!_ProductService.instance) {
1007
- _ProductService.instance = new _ProductService();
1008
- }
1009
- return _ProductService.instance;
1010
- }
1011
- /**
1012
- * Obtiene todos los productos del store
1013
- */
1014
- async getProducts(filters) {
1015
- if (filters) {
1016
- ProductsFiltersSchema.parse(filters);
1017
- }
1018
- const params = new URLSearchParams();
1019
- if (filters) {
1020
- if (filters.category) params.append("category", filters.category);
1021
- if (filters.search) params.append("search", filters.search);
1022
- if (filters.minPrice) params.append("minPrice", filters.minPrice.toString());
1023
- if (filters.maxPrice) params.append("maxPrice", filters.maxPrice.toString());
1024
- if (filters.sortBy) params.append("sortBy", filters.sortBy);
1025
- if (filters.sortOrder) params.append("sortOrder", filters.sortOrder);
1026
- if (filters.page) params.append("page", filters.page.toString());
1027
- if (filters.limit) params.append("limit", filters.limit.toString());
1028
- }
1029
- const queryString = params.toString();
1030
- const url = `/${STOREFRONT_API_PATH_PREFIX}/products${queryString ? `?${queryString}` : ""}`;
1031
- const response = await apiClient.get(url);
1032
- return ProductsResponseSchema.parse(response);
1033
- }
1034
- /**
1035
- * Obtiene un producto específico por ID
1036
- */
1037
- async getProduct(id) {
1038
- const response = await apiClient.get(`/${STOREFRONT_API_PATH_PREFIX}/products/${id}`);
1039
- return ProductSchema.parse(response);
1040
- }
1041
- /**
1042
- * Obtiene un producto específico por slug
1043
- */
1044
- async getProductBySlug(slug) {
1045
- const response = await apiClient.get(`/${STOREFRONT_API_PATH_PREFIX}/products/${encodeURIComponent(slug)}`);
1046
- return ProductSchema.parse(response);
1047
- }
1048
- /**
1049
- * Busca productos por término de búsqueda
1050
- */
1051
- async searchProducts(query, filters) {
1052
- return this.getProducts({ ...filters, search: query });
1053
- }
1054
- /**
1055
- * Obtiene productos por categoría
1056
- */
1057
- async getProductsByCategory(category, filters) {
1058
- return this.getProducts({ ...filters, category });
1059
- }
1060
- /**
1061
- * Obtiene productos en oferta (con descuento)
1062
- */
1063
- async getProductsOnSale(filters) {
1064
- return this.getProducts({ ...filters, minPrice: 0 });
1065
- }
1066
- /**
1067
- * Obtiene productos en stock
1068
- */
1069
- async getProductsInStock(filters) {
1070
- return this.getProducts({ ...filters, inStock: true });
1071
- }
1072
- /**
1073
- * Lista de productos del storefront (API v1/storefront/products).
1074
- * Soporta paginación, orden y filtros por categoría, marca y precio.
1075
- */
1076
- async getStorefrontProducts(params) {
1077
- const parsed = StorefrontProductsParamsSchema.parse(params ?? {});
1078
- const searchParams = new URLSearchParams();
1079
- searchParams.set("page", String(parsed.page));
1080
- searchParams.set("pageSize", String(parsed.pageSize));
1081
- if (parsed.sort) searchParams.set("sort", parsed.sort);
1082
- if (parsed.sellerId) searchParams.set("sellerId", parsed.sellerId);
1083
- if (parsed.collection) searchParams.set("collection", parsed.collection);
1084
- if (parsed.category) searchParams.set("category", parsed.category);
1085
- if (parsed.brand) searchParams.set("brand", parsed.brand);
1086
- if (parsed.productType) searchParams.set("productType", parsed.productType);
1087
- if (parsed.inStock != null) searchParams.set("inStock", String(parsed.inStock));
1088
- if (parsed.minPrice != null) searchParams.set("minPrice", String(parsed.minPrice));
1089
- if (parsed.maxPrice != null) searchParams.set("maxPrice", String(parsed.maxPrice));
1090
- if (parsed.search) searchParams.set("search", parsed.search);
1091
- const url = `/${STOREFRONT_API_PATH_PREFIX}/products?${searchParams.toString()}`;
1092
- const response = await apiClient.get(url);
1093
- return StorefrontProductsResponseSchema.parse(response);
1094
- }
1095
- /**
1096
- * Obtiene un producto del storefront por slug (API v1/storefront/products/by-slug/:slug).
1097
- */
1098
- async getStorefrontProductBySlug(slug) {
1099
- const response = await apiClient.get(`/${STOREFRONT_API_PATH_PREFIX}/products/by-slug/${encodeURIComponent(slug)}`);
1100
- return StorefrontProductDetailSchema.parse(response);
1101
- }
1102
- };
1103
- var productService = ProductService.getInstance();
1104
- var StorefrontCollectionSchema = z.object({
1105
- id: z.coerce.string(),
1106
- slug: z.string(),
1107
- name: z.string(),
1108
- description: z.string().nullable().optional(),
1109
- imageUrl: z.string().nullable().optional(),
1110
- type: z.string(),
1111
- productCount: z.number().int().nonnegative()
1112
- });
1113
- var StorefrontCollectionsResponseSchema = z.array(StorefrontCollectionSchema);
1114
- var StorefrontCollectionDetailSchema = StorefrontCollectionSchema;
1115
-
1116
- // src/core/services/collection.service.ts
1117
- var CollectionService = class _CollectionService {
1118
- static instance;
1119
- constructor() {
1120
- }
1121
- static getInstance() {
1122
- if (!_CollectionService.instance) {
1123
- _CollectionService.instance = new _CollectionService();
1124
- }
1125
- return _CollectionService.instance;
1126
- }
1127
- /**
1128
- * Lista de colecciones del storefront (GET /v1/collections).
1129
- */
1130
- async getStorefrontCollections() {
1131
- const response = await apiClient.get(
1132
- `/${STOREFRONT_API_PATH_PREFIX}/collections`
1133
- );
1134
- return StorefrontCollectionsResponseSchema.parse(response);
1135
- }
1136
- /**
1137
- * Obtiene una colección del storefront por slug (GET /v1/collections/by-slug/:slug).
1138
- */
1139
- async getStorefrontCollectionBySlug(slug) {
1140
- const response = await apiClient.get(
1141
- `/${STOREFRONT_API_PATH_PREFIX}/collections/by-slug/${encodeURIComponent(slug)}`
1142
- );
1143
- return StorefrontCollectionDetailSchema.parse(response);
1144
- }
1145
- };
1146
- var collectionService = CollectionService.getInstance();
1147
- var StorefrontPageListItemSchema = z.object({
1148
- id: z.string(),
1149
- name: z.string(),
1150
- slug: z.string(),
1151
- layout: z.string()
1152
- });
1153
- var StorefrontPageSeoSchema = z.object({
1154
- title: z.string().optional(),
1155
- description: z.string().optional()
1156
- }).optional();
1157
- var StorefrontPageSettingsSchema = z.object({
1158
- seo: StorefrontPageSeoSchema,
1159
- html: z.string().optional(),
1160
- title: z.string().optional()
1161
- }).catchall(z.unknown());
1162
- var StorefrontPageBlockSchema = z.object({
1163
- id: z.string(),
1164
- type: z.string(),
1165
- order: z.number(),
1166
- settings: z.record(z.unknown()).optional()
1167
- });
1168
- var StorefrontPageSectionSchema = z.object({
1169
- id: z.string(),
1170
- type: z.string(),
1171
- order: z.number(),
1172
- settings: z.record(z.unknown()).optional(),
1173
- blocks: z.array(StorefrontPageBlockSchema).optional().default([])
1174
- });
1175
- var StorefrontPageDetailSchema = z.object({
1176
- id: z.string(),
1177
- name: z.string(),
1178
- slug: z.string(),
1179
- layout: z.string(),
1180
- settings: StorefrontPageSettingsSchema.optional(),
1181
- sections: z.array(StorefrontPageSectionSchema).optional()
1182
- });
1183
-
1184
- // src/core/services/page.service.ts
1185
- var PageService = class _PageService {
1186
- static instance;
1187
- constructor() {
1188
- }
1189
- static getInstance() {
1190
- if (!_PageService.instance) {
1191
- _PageService.instance = new _PageService();
1192
- }
1193
- return _PageService.instance;
1194
- }
1195
- /**
1196
- * Lista de páginas del storefront (GET /v1/pages).
1197
- */
1198
- async getPages() {
1199
- const response = await apiClient.get(
1200
- `/${STOREFRONT_API_PATH_PREFIX}/pages`
1201
- );
1202
- return z.array(StorefrontPageListItemSchema).parse(response);
1203
- }
1204
- /**
1205
- * Obtiene una página por slug (GET /v1/pages/{slug}).
1206
- * sections puede no venir en la respuesta.
1207
- */
1208
- async getPageBySlug(slug) {
1209
- const response = await apiClient.get(
1210
- `/${STOREFRONT_API_PATH_PREFIX}/pages/${encodeURIComponent(slug)}`
1211
- );
1212
- return StorefrontPageDetailSchema.parse(response);
1213
- }
1214
- };
1215
- var pageService = PageService.getInstance();
1216
-
1217
- // src/core/services/store.service.ts
1218
- var StoreService = class _StoreService {
1219
- static instance;
1220
- constructor() {
1221
- }
1222
- static getInstance() {
1223
- if (!_StoreService.instance) {
1224
- _StoreService.instance = new _StoreService();
1225
- }
1226
- return _StoreService.instance;
1227
- }
1228
- /**
1229
- * Obtiene la configuración de la tienda por subdominio (GET /v1/settings).
1230
- * Usa el header X-Store-Domain. Si se pasa domain, se configura antes de la petición.
1231
- */
1232
- async getStorefrontSettings(domain) {
1233
- if (domain) {
1234
- apiClient.setStoreDomain(domain);
1235
- }
1236
- const response = await apiClient.get(`/${STOREFRONT_API_PATH_PREFIX}/settings`);
1237
- return StorefrontSettingsSchema.parse(response);
1238
- }
1239
- };
1240
- var storeService = StoreService.getInstance();
1241
- var CartBuyerRequestSchema = z.object({
1242
- email: z.string().email(),
1243
- firstName: z.string().min(1),
1244
- lastName: z.string().min(1),
1245
- phone: z.string().min(1)
1246
- });
1247
- var CartCreateRequestSchema = z.object({
1248
- currencyCode: z.string(),
1249
- salesChannelId: z.string(),
1250
- email: z.string().email()
1251
- });
1252
- var CartAddressSchema = z.object({
1253
- name: z.string().min(1),
1254
- address1: z.string().min(1),
1255
- city: z.string().min(1),
1256
- countryCode: z.string().min(2),
1257
- company: z.string().nullable().optional(),
1258
- address2: z.string().nullable().optional(),
1259
- province: z.string().nullable().optional(),
1260
- postalCode: z.string().nullable().optional(),
1261
- district: z.string().nullable().optional(),
1262
- phone: z.string().nullable().optional(),
1263
- latitude: z.number().nullable().optional(),
1264
- longitude: z.number().nullable().optional(),
1265
- geo: z.object({
1266
- departmentCode: z.string().nullable().optional(),
1267
- provinceCode: z.string().nullable().optional(),
1268
- districtCode: z.string().nullable().optional()
1269
- }).nullable().optional()
1270
- });
1271
- var CartAddressesRequestSchema = z.object({
1272
- shippingAddress: CartAddressSchema,
1273
- billingAddress: CartAddressSchema
1274
- });
1275
- var CartItemModifierRequestSchema = z.object({
1276
- modifierGroupId: z.coerce.string().min(1),
1277
- optionIds: z.array(z.coerce.string().min(1)).optional().default([])
1278
- });
1279
- var CartItemRequestSchema = z.object({
1280
- variantId: z.coerce.string().min(1),
1281
- quantity: z.number().int().positive(),
1282
- modifiers: z.array(CartItemModifierRequestSchema).optional()
1283
- });
1284
- var CartCouponRequestSchema = z.object({
1285
- code: z.string().min(1)
1286
- });
1287
- z.object({
1288
- subtotal: z.number(),
1289
- discount: z.number(),
1290
- tax: z.number(),
1291
- warranty: z.number().optional().default(0),
1292
- total: z.number()
1293
- });
1294
- z.object({
1295
- name: z.string().nullable().optional(),
1296
- address1: z.string(),
1297
- city: z.string(),
1298
- countryCode: z.string(),
1299
- company: z.string().nullable().optional(),
1300
- address2: z.string().nullable().optional(),
1301
- province: z.string().nullable().optional(),
1302
- postalCode: z.string().nullable().optional(),
1303
- district: z.string().nullable().optional(),
1304
- phone: z.string().nullable().optional(),
1305
- latitude: z.number().nullable().optional(),
1306
- longitude: z.number().nullable().optional(),
1307
- geo: z.object({
1308
- departmentCode: z.string().nullable().optional(),
1309
- provinceCode: z.string().nullable().optional(),
1310
- districtCode: z.string().nullable().optional()
1311
- }).nullable().optional()
1312
- });
1313
- var CartResponseBundleItemSchema = z.object({
1314
- productId: z.coerce.string(),
1315
- variantId: z.coerce.string(),
1316
- productName: z.string(),
1317
- variantName: z.string().nullable().optional(),
1318
- sku: z.string().nullable().optional(),
1319
- brand: z.string().nullable().optional(),
1320
- imageUrl: z.string().nullable().optional(),
1321
- quantity: z.number().int()
1322
- });
1323
- var CartResponseItemSchema = z.object({
1324
- id: z.string(),
1325
- title: z.string(),
1326
- variantLabel: z.string().nullable().optional(),
1327
- thumbnail: z.string().nullable().optional(),
1328
- quantity: z.number().int(),
1329
- variantId: z.coerce.string(),
1330
- variantSku: z.string().nullable().optional(),
1331
- unitPrice: z.number(),
1332
- bundleItems: z.array(CartResponseBundleItemSchema).optional().default([])
1333
- });
1334
- z.object({
1335
- id: z.string(),
1336
- name: z.string(),
1337
- description: z.string().nullable().optional(),
1338
- total: z.number(),
1339
- estimatedDays: z.number().int().nullable().optional(),
1340
- pickupPointId: z.string().nullable().optional(),
1341
- scheduledDate: z.string().nullable().optional(),
1342
- scheduledTime: z.string().nullable().optional()
1343
- });
1344
- var CartResponseSchema = z.object({
1345
- id: z.string(),
1346
- regionId: z.string().nullable().optional(),
1347
- salesChannelId: z.string().nullable().optional(),
1348
- email: z.string().email().nullable().optional(),
1349
- phone: z.string().nullable().optional(),
1350
- firstName: z.string().nullable().optional(),
1351
- lastName: z.string().nullable().optional(),
1352
- currencyCode: z.string(),
1353
- completedAt: z.string().nullable().optional(),
1354
- shippingAddress: z.unknown().nullable().optional(),
1355
- billingAddress: z.unknown().nullable().optional(),
1356
- metadata: z.record(z.string(), z.unknown()).optional().default({}),
1357
- promotionCodes: z.array(z.string()).optional().default([]),
1358
- items: z.array(CartResponseItemSchema).optional().default([]),
1359
- shippingMethods: z.array(z.unknown()).optional().default([]),
1360
- totals: z.object({
1361
- discountTotal: z.number(),
1362
- shippingTotal: z.number(),
1363
- subtotal: z.number(),
1364
- taxTotal: z.number(),
1365
- total: z.number(),
1366
- warrantyTotal: z.number()
1367
- })
1368
- });
1369
- var CartCheckoutRequestSchema = z.object({
1370
- paymentMethodCode: z.string().min(1),
1371
- externalRef: z.string().min(1),
1372
- receiptUrl: z.string().min(1).optional()
1373
- });
1374
- var CartCheckoutPaymentActionSchema = z.object({
1375
- publicConfiguration: z.object({
1376
- publishableKey: z.string().nullable().optional(),
1377
- checkoutMode: z.string().nullable().optional(),
1378
- phoneNumber: z.string().nullable().optional(),
1379
- accountHolder: z.string().nullable().optional(),
1380
- qrImageUrl: z.string().nullable().optional()
1381
- }).passthrough().nullable().optional(),
1382
- url: z.string().nullable().optional(),
1383
- html: z.string().nullable().optional(),
1384
- clientSecret: z.string().nullable().optional(),
1385
- qrCode: z.string().nullable().optional(),
1386
- expiresAt: z.string().nullable().optional()
1387
- }).passthrough();
1388
- var CartCheckoutPaymentSchema = z.object({
1389
- transactionId: z.string(),
1390
- paymentMethodCode: z.string(),
1391
- providerId: z.string().nullable().optional(),
1392
- status: z.string(),
1393
- amount: z.number(),
1394
- reference: z.string(),
1395
- actionType: z.string(),
1396
- receiptUrl: z.string().nullable().optional(),
1397
- action: CartCheckoutPaymentActionSchema.nullable().optional()
1398
- }).passthrough();
1399
- var CartCheckoutResponseSchema = z.object({
1400
- publicKey: z.string().optional(),
1401
- publishableKey: z.string().optional(),
1402
- stripePublicKey: z.string().optional(),
1403
- clientSecret: z.string().optional(),
1404
- intentClientSecret: z.string().optional(),
1405
- orderId: z.coerce.string().optional(),
1406
- orderGroup: z.string().optional(),
1407
- orderIds: z.array(z.coerce.string()).optional(),
1408
- /** Payload de pago (embedded, redirect, etc.) */
1409
- payment: CartCheckoutPaymentSchema.optional(),
1410
- cart: z.unknown().optional()
1411
- }).passthrough();
1412
- var CartPaymentReceiptResponseSchema = z.object({
1413
- id: z.string(),
1414
- url: z.string()
1415
- });
1416
- var CartPaymentMethodSchema = z.object({
1417
- code: z.string(),
1418
- name: z.string(),
1419
- description: z.string().nullable().optional(),
1420
- kind: z.string(),
1421
- isActive: z.boolean(),
1422
- isDefault: z.boolean(),
1423
- isManual: z.boolean(),
1424
- sortOrder: z.number(),
1425
- phoneNumber: z.string().nullable().optional(),
1426
- accountHolder: z.string().nullable().optional(),
1427
- qrImageUrl: z.string().nullable().optional()
1428
- });
1429
- var CartPaymentMethodsResponseSchema = z.object({
1430
- methods: z.array(CartPaymentMethodSchema)
1431
- });
1432
- var CartCheckoutReadResponseSchema = z.object({
1433
- cart: CartResponseSchema,
1434
- paymentMethods: CartPaymentMethodsResponseSchema
1435
- });
1436
- var ShippingRateSchema = z.object({
1437
- shippingMethodId: z.string(),
1438
- name: z.string(),
1439
- displayName: z.string().optional(),
1440
- type: z.string().optional(),
1441
- price: z.number(),
1442
- currency: z.string().optional(),
1443
- sortOrder: z.number().optional(),
1444
- rateId: z.string(),
1445
- description: z.string().nullable().optional(),
1446
- deliveryLocationId: z.string().nullable().optional(),
1447
- deliveryLocationName: z.string().nullable().optional(),
1448
- estimatedDeliveryDays: z.number().nullable().optional(),
1449
- serviceCode: z.string().nullable().optional(),
1450
- metadata: z.record(z.string(), z.unknown()).optional()
1451
- }).passthrough();
1452
- var ShippingOptionGroupSchema = z.object({
1453
- sellerId: z.string(),
1454
- shippingProfileId: z.string().nullable().optional(),
1455
- orderAmount: z.number().optional(),
1456
- totalWeight: z.number().optional(),
1457
- itemIds: z.array(z.string()).optional(),
1458
- rates: z.array(ShippingRateSchema)
1459
- }).passthrough();
1460
- var ShippingOptionsResponseSchema = z.object({
1461
- groups: z.array(ShippingOptionGroupSchema)
1462
- });
1463
- var CartShippingMethodPutRequestSchema = z.object({
1464
- rateId: z.string().min(1),
1465
- shippingMethodId: z.string().min(1),
1466
- serviceCode: z.string().nullable().optional(),
1467
- sellerId: z.string().nullable().optional(),
1468
- shippingProfileId: z.string().nullable().optional(),
1469
- locationId: z.string().nullable().optional(),
1470
- pickupPointId: z.string().nullable().optional(),
1471
- scheduledDate: z.string().nullable().optional(),
1472
- scheduledTime: z.string().nullable().optional()
1473
- });
1474
-
1475
- // src/core/services/cart.service.ts
1476
- var CartService = class _CartService {
1477
- static instance;
1478
- constructor() {
1479
- }
1480
- static getInstance() {
1481
- if (!_CartService.instance) {
1482
- _CartService.instance = new _CartService();
1483
- }
1484
- return _CartService.instance;
1485
- }
1486
- /**
1487
- * Obtiene el carrito actual (GET /v1/carts).
1488
- */
1489
- async getCart() {
1490
- const response = await apiClient.get(`/${STOREFRONT_API_PATH_PREFIX}/carts`);
1491
- return CartResponseSchema.parse(response);
1492
- }
1493
- /**
1494
- * Crea un carrito nuevo (POST /v1/carts).
1495
- */
1496
- async createCart(data) {
1497
- const payload = CartCreateRequestSchema.parse(data);
1498
- const response = await apiClient.post(`/${STOREFRONT_API_PATH_PREFIX}/carts`, payload);
1499
- return CartResponseSchema.parse(response);
1500
- }
1501
- /**
1502
- * Actualiza los datos del comprador del carrito actual (PATCH /v1/carts/buyer).
1503
- * El carrito se resuelve en backend vía contexto (token/guest), por eso no requiere cartId.
1504
- */
1505
- async updateBuyer(data) {
1506
- const payload = CartBuyerRequestSchema.parse(data);
1507
- const response = await apiClient.patch(`/${STOREFRONT_API_PATH_PREFIX}/carts/buyer`, payload);
1508
- return CartResponseSchema.parse(response);
1509
- }
1510
- /**
1511
- * Actualiza direcciones de envío y facturación del carrito (PATCH /v1/carts/addresses).
1512
- */
1513
- async updateAddresses(data) {
1514
- const payload = CartAddressesRequestSchema.parse(data);
1515
- const response = await apiClient.patch(`/${STOREFRONT_API_PATH_PREFIX}/carts/addresses`, payload);
1516
- return CartResponseSchema.parse(response);
1517
- }
1518
- /**
1519
- * Inicializa/obtiene detalles del método de pago para el checkout (POST /v1/carts/checkout).
1520
- * Usado para providers como Stripe (public key, client secret, etc).
1521
- */
1522
- async checkout(data) {
1523
- const payload = CartCheckoutRequestSchema.parse(data);
1524
- const response = await apiClient.post(`/${STOREFRONT_API_PATH_PREFIX}/carts/checkout`, payload);
1525
- return CartCheckoutResponseSchema.parse(response);
1526
- }
1527
- };
1528
- var cartService = CartService.getInstance();
1529
- var ECOMIQ_APP_APEX = "ecomiq.app";
1530
- var ECOMIQ_APP_SUFFIX = [`.${ECOMIQ_APP_APEX}`, `.localhost`];
1531
- function normalizeStoreHost(hostHeader) {
1532
- let h = hostHeader.trim().toLowerCase();
1533
- const colon = h.indexOf(":");
1534
- if (colon !== -1) h = h.slice(0, colon);
1535
- if (h.startsWith("www.")) h = h.slice(4);
1536
- return h;
1537
- }
1538
- function resolveStorefrontSettingsHost(hostHeader) {
1539
- const host = normalizeStoreHost(hostHeader);
1540
- if (!host) {
1541
- throw Object.assign(new Error("Host inv\xE1lido para tienda"), { status: 400 });
1542
- }
1543
- if (host === ECOMIQ_APP_APEX) {
1544
- throw Object.assign(new Error("Host apex ecomiq.app sin slug de tienda"), { status: 400 });
1545
- }
1546
- if (ECOMIQ_APP_SUFFIX.some((suffix) => host.endsWith(suffix))) {
1547
- const slug = host.slice(0, -(ECOMIQ_APP_SUFFIX.find((suffix) => host.endsWith(suffix))?.length ?? 0));
1548
- if (!slug) {
1549
- throw Object.assign(new Error("Host sin slug para ecomiq.app"), { status: 400 });
1550
- }
1551
- return slug;
1552
- }
1553
- return host;
1554
- }
1555
-
1556
- // src/core/config/ecomiq-config.ts
1557
- var CONFIG_FILENAMES = [
1558
- "ecomiq.config.js",
1559
- "ecomiq.config.cjs",
1560
- "ecomiq.config.mjs"
1561
- ];
1562
- var EcomiqConfigSchema = z.object({
1563
- /**
1564
- * Slug de tienda en Ecomiq (p. ej. `mi-tienda` para `mi-tienda.ecomiq.app`).
1565
- * Si está definido, tiene prioridad sobre el `Host` de la request (útil en `localhost`).
1566
- */
1567
- slugStore: z.string().min(1).optional()
1568
- });
1569
- var cachedConfig;
1570
- function parseExportedConfig(raw) {
1571
- const candidate = raw && typeof raw === "object" && "default" in raw ? raw.default : raw;
1572
- const parsed = EcomiqConfigSchema.safeParse(candidate);
1573
- if (!parsed.success) {
1574
- console.warn(
1575
- "[ecomiq] ecomiq.config.* inv\xE1lido:",
1576
- parsed.error.flatten().fieldErrors
1577
- );
1578
- return null;
1579
- }
1580
- const slugStore = parsed.data.slugStore?.trim();
1581
- return slugStore ? { slugStore } : {};
1582
- }
1583
- function loadEcomiqConfig(cwd = process.cwd()) {
1584
- if (cachedConfig !== void 0) return cachedConfig;
1585
- for (const filename of CONFIG_FILENAMES) {
1586
- const configPath = join(cwd, filename);
1587
- if (!existsSync(configPath)) continue;
1588
- try {
1589
- const requireFromApp = createRequire(join(cwd, "package.json"));
1590
- const loaded = requireFromApp(configPath);
1591
- const parsed = parseExportedConfig(loaded);
1592
- cachedConfig = parsed;
1593
- return cachedConfig;
1594
- } catch (error) {
1595
- console.warn(`[ecomiq] No se pudo cargar ${configPath}:`, error);
1596
- }
1597
- }
1598
- cachedConfig = null;
1599
- return null;
1600
- }
1601
- function clearEcomiqConfigCache() {
1602
- cachedConfig = void 0;
1603
- }
1604
- function resolvePublicStoreKey(hostHeader, cwd) {
1605
- const slugFromConfig = loadEcomiqConfig(cwd)?.slugStore?.trim();
1606
- if (slugFromConfig) return slugFromConfig;
1607
- if (!hostHeader?.trim()) {
1608
- throw Object.assign(
1609
- new Error(
1610
- "Host is required, o define slugStore en ecomiq.config.js en la ra\xEDz del proyecto"
1611
- ),
1612
- { status: 400 }
1613
- );
1614
- }
1615
- return resolveStorefrontSettingsHost(hostHeader);
1616
- }
1617
- function hasEcomiqSlugStoreConfig(cwd) {
1618
- return Boolean(loadEcomiqConfig(cwd)?.slugStore?.trim());
1619
- }
1620
- var defaultOptions = {
1621
- queries: {
1622
- staleTime: 60 * 1e3
1623
- }
1624
- };
1625
- function QueryProvider({ children, dehydratedState }) {
1626
- const queryClientRef = useRef(null);
1627
- const hydratedRef = useRef(false);
1628
- if (queryClientRef.current === null) {
1629
- queryClientRef.current = new QueryClient({ defaultOptions });
1630
- }
1631
- const queryClient = queryClientRef.current;
1632
- useEffect(() => {
1633
- if (dehydratedState && !hydratedRef.current) {
1634
- hydrate(queryClient, dehydratedState);
1635
- hydratedRef.current = true;
1636
- }
1637
- }, [queryClient, dehydratedState]);
1638
- return /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children });
1639
- }
1640
- var StorefrontAuthContext = createContext(null);
1641
- function StorefrontAuthProvider({
1642
- authClient,
1643
- children
1644
- }) {
1645
- return /* @__PURE__ */ jsx(StorefrontAuthContext.Provider, { value: authClient, children });
1646
- }
1647
- function useStorefrontAuthClient() {
1648
- const client = useContext(StorefrontAuthContext);
1649
- if (!client) {
1650
- throw new Error(
1651
- "useStorefrontAuthClient debe usarse dentro de StorefrontAuthProvider (o EcomiqProvider con authClient)."
1652
- );
1653
- }
1654
- return client;
1655
- }
1656
- function storefrontSessionEndsSoon(expiresAt, skewMs = 12e4) {
1657
- if (!expiresAt) return false;
1658
- return Date.now() + skewMs >= expiresAt.getTime();
1659
- }
1660
- var storefrontAccessTokenNeedsRefresh = storefrontSessionEndsSoon;
1661
- function parseStorefrontSessionExpires(at) {
1662
- if (!at) return void 0;
1663
- if (at instanceof Date) return Number.isNaN(at.getTime()) ? void 0 : at;
1664
- if (typeof at === "string") {
1665
- const d = new Date(at);
1666
- return Number.isNaN(d.getTime()) ? void 0 : d;
1667
- }
1668
- return void 0;
1669
- }
1670
- function mapSessionUser(raw) {
1671
- if (!raw || typeof raw !== "object") return null;
1672
- const u = raw;
1673
- const explicitFirst = typeof u.firstName === "string" ? u.firstName : typeof u.given_name === "string" ? u.given_name : "";
1674
- const explicitLast = typeof u.lastName === "string" ? u.lastName : typeof u.family_name === "string" ? u.family_name : "";
1675
- if (explicitFirst || explicitLast) {
1676
- return {
1677
- id: String(u.id ?? ""),
1678
- email: String(u.email ?? ""),
1679
- firstName: explicitFirst,
1680
- lastName: explicitLast,
1681
- name: typeof u.name === "string" ? u.name : void 0,
1682
- phone: typeof u.phone === "string" ? u.phone : void 0
1683
- };
1684
- }
1685
- const emailLocal = String(u.email ?? "").split("@")[0] ?? "";
1686
- const full = typeof u.name === "string" ? u.name.trim() : emailLocal.trim();
1687
- const parts = full ? full.split(/\s+/) : [];
1688
- return {
1689
- id: String(u.id ?? ""),
1690
- email: String(u.email ?? ""),
1691
- firstName: parts[0] ?? "",
1692
- lastName: parts.slice(1).join(" "),
1693
- name: typeof u.name === "string" ? u.name : void 0,
1694
- phone: typeof u.phone === "string" ? u.phone : void 0
1695
- };
1696
- }
1697
- function useStorefrontSession() {
1698
- const authClient = useStorefrontAuthClient();
1699
- const session = authClient.useSession();
1700
- const user = mapSessionUser(session.data?.user);
1701
- const storefrontSession = session.data?.session;
1702
- const sessionExpiresAt = parseStorefrontSessionExpires(storefrontSession?.expiresAt);
1703
- return {
1704
- session,
1705
- user,
1706
- sessionExpiresAt,
1707
- isAuthenticated: Boolean(session.data?.user),
1708
- isPending: session.isPending,
1709
- error: session.error,
1710
- refetch: session.refetch,
1711
- signOut: () => authClient.signOut()
1712
- };
1713
- }
1714
-
1715
- // src/react/hooks/use-user.ts
1716
- function useUser() {
1717
- return {};
1718
- }
1719
-
1720
- // src/react/hooks/use-config.ts
1721
- function useConfig() {
1722
- return {};
1723
- }
1724
- var useProducts = (storeId) => {
1725
- if (storeId) {
1726
- apiClient.setStoreId(storeId);
1727
- }
1728
- const useProductsList = (filters) => {
1729
- return useQuery({
1730
- queryKey: ["products", filters],
1731
- queryFn: () => productService.getProducts(filters),
1732
- enabled: !!storeId
1733
- });
1734
- };
1735
- const useInfiniteProducts = (filters) => {
1736
- return useInfiniteQuery({
1737
- queryKey: ["products", "infinite", filters],
1738
- queryFn: ({ pageParam = 1 }) => productService.getProducts({ ...filters, page: pageParam }),
1739
- getNextPageParam: (lastPage) => lastPage.meta.hasNextPage ? lastPage.meta.page + 1 : void 0,
1740
- initialPageParam: 1,
1741
- enabled: !!storeId
1742
- });
1743
- };
1744
- const useProduct = (id) => {
1745
- return useQuery({
1746
- queryKey: ["product", id],
1747
- queryFn: () => productService.getProduct(id),
1748
- enabled: !!id && !!storeId
1749
- });
1750
- };
1751
- const useProductBySlug = (slug) => {
1752
- return useQuery({
1753
- queryKey: ["product", "slug", slug],
1754
- queryFn: () => productService.getProductBySlug(slug),
1755
- enabled: !!slug && !!storeId
1756
- });
1757
- };
1758
- const useSearchProducts = (query, filters) => {
1759
- return useQuery({
1760
- queryKey: ["products", "search", query, filters],
1761
- queryFn: () => productService.searchProducts(query, filters),
1762
- enabled: !!query && !!storeId
1763
- });
1764
- };
1765
- const useProductsByCategory = (category, filters) => {
1766
- return useQuery({
1767
- queryKey: ["products", "category", category, filters],
1768
- queryFn: () => productService.getProductsByCategory(category, filters),
1769
- enabled: !!category && !!storeId
1770
- });
1771
- };
1772
- const useProductsOnSale = (filters) => {
1773
- return useQuery({
1774
- queryKey: ["products", "sale", filters],
1775
- queryFn: () => productService.getProductsOnSale(filters),
1776
- enabled: !!storeId
1777
- });
1778
- };
1779
- const useProductsInStock = (filters) => {
1780
- return useQuery({
1781
- queryKey: ["products", "stock", filters],
1782
- queryFn: () => productService.getProductsInStock(filters),
1783
- enabled: !!storeId
1784
- });
1785
- };
1786
- const useRefreshProducts = () => {
1787
- const queryClient = useQueryClient();
1788
- return useMutation({
1789
- mutationFn: () => productService.getProducts(),
1790
- onSuccess: (data) => {
1791
- queryClient.setQueryData(["products"], data);
1792
- }
1793
- });
1794
- };
1795
- const useStorefrontProductsList = (params) => {
1796
- return useQuery({
1797
- queryKey: ["storefront", "products", params],
1798
- queryFn: () => productService.getStorefrontProducts(params),
1799
- enabled: !!storeId
1800
- });
1801
- };
1802
- const useStorefrontInfiniteProducts = (params) => {
1803
- return useInfiniteQuery({
1804
- queryKey: ["storefront", "products", "infinite", params],
1805
- queryFn: ({ pageParam = 1 }) => productService.getStorefrontProducts({ ...params, page: pageParam }),
1806
- getNextPageParam: (lastPage) => lastPage.pageIndex < lastPage.totalPages ? lastPage.pageIndex + 1 : void 0,
1807
- initialPageParam: 1,
1808
- enabled: !!storeId
1809
- });
1810
- };
1811
- const useRefreshStorefrontProducts = () => {
1812
- const queryClient = useQueryClient();
1813
- return useMutation({
1814
- mutationFn: (params) => productService.getStorefrontProducts(params),
1815
- onSuccess: () => {
1816
- queryClient.invalidateQueries({ queryKey: ["storefront", "products"] });
1817
- }
1818
- });
1819
- };
1820
- return {
1821
- useProductsList,
1822
- useInfiniteProducts,
1823
- useProduct,
1824
- useProductBySlug,
1825
- useSearchProducts,
1826
- useProductsByCategory,
1827
- useProductsOnSale,
1828
- useProductsInStock,
1829
- useRefreshProducts,
1830
- useStorefrontProductsList,
1831
- useStorefrontInfiniteProducts,
1832
- useRefreshStorefrontProducts
1833
- };
1834
- };
1835
- var useCollections = (storeId) => {
1836
- if (storeId) {
1837
- apiClient.setStoreId(storeId);
1838
- }
1839
- const useStorefrontCollectionsList = () => {
1840
- return useQuery({
1841
- queryKey: ["storefront", "collections"],
1842
- queryFn: () => collectionService.getStorefrontCollections(),
1843
- enabled: !!storeId
1844
- });
1845
- };
1846
- const useStorefrontCollectionBySlug = (slug) => {
1847
- return useQuery({
1848
- queryKey: ["storefront", "collection", "slug", slug],
1849
- queryFn: () => collectionService.getStorefrontCollectionBySlug(slug),
1850
- enabled: !!slug && !!storeId
1851
- });
1852
- };
1853
- const useRefreshStorefrontCollections = () => {
1854
- const queryClient = useQueryClient();
1855
- return useMutation({
1856
- mutationFn: () => collectionService.getStorefrontCollections(),
1857
- onSuccess: () => {
1858
- queryClient.invalidateQueries({ queryKey: ["storefront", "collections"] });
1859
- }
1860
- });
1861
- };
1862
- return {
1863
- useStorefrontCollectionsList,
1864
- useStorefrontCollectionBySlug,
1865
- useRefreshStorefrontCollections
1866
- };
1867
- };
1868
- var usePages = (storeId) => {
1869
- if (storeId) {
1870
- apiClient.setStoreId(storeId);
1871
- }
1872
- const usePagesList = () => {
1873
- return useQuery({
1874
- queryKey: ["storefront", "pages"],
1875
- queryFn: () => pageService.getPages(),
1876
- enabled: !!storeId
1877
- });
1878
- };
1879
- const usePageBySlug = (slug) => {
1880
- return useQuery({
1881
- queryKey: ["storefront", "page", "slug", slug],
1882
- queryFn: () => pageService.getPageBySlug(slug),
1883
- enabled: !!slug && !!storeId
1884
- });
1885
- };
1886
- const useRefreshPages = () => {
1887
- const queryClient = useQueryClient();
1888
- return useMutation({
1889
- mutationFn: () => pageService.getPages(),
1890
- onSuccess: () => {
1891
- queryClient.invalidateQueries({ queryKey: ["storefront", "pages"] });
1892
- }
1893
- });
1894
- };
1895
- return {
1896
- usePagesList,
1897
- usePageBySlug,
1898
- useRefreshPages
1899
- };
1900
- };
1901
- var useCartAddresses = () => {
1902
- return useMutation({
1903
- mutationFn: (data) => cartService.updateAddresses(data)
1904
- });
1905
- };
1906
- var useCartGet = () => {
1907
- return useQuery({
1908
- queryKey: ["cart"],
1909
- queryFn: () => cartService.getCart()
1910
- });
1911
- };
1912
- var useCartCreate = () => {
1913
- return useMutation({
1914
- mutationFn: (data) => cartService.createCart(data)
1915
- });
1916
- };
1917
- function useStorefrontGeoInei(storeId, form) {
1918
- const [departments, setDepartments] = useState([]);
1919
- const [provinces, setProvinces] = useState([]);
1920
- const [districts, setDistricts] = useState([]);
1921
- const [loadingDepartments, setLoadingDepartments] = useState(false);
1922
- const [loadingProvinces, setLoadingProvinces] = useState(false);
1923
- const [loadingDistricts, setLoadingDistricts] = useState(false);
1924
- const prevDepartmentCode = useRef(void 0);
1925
- const prevProvinceCode = useRef(void 0);
1926
- const countryCode = (form.watch("countryCode") || "PE").toUpperCase();
1927
- const departmentCode = form.watch("departmentCode");
1928
- const provinceCode = form.watch("provinceCode");
1929
- const districtCode = form.watch("districtCode");
1930
- const storeRequestHeaders = useMemo(() => {
1931
- const id = storeId.trim();
1932
- if (!id) return null;
1933
- return { "X-Store-Id": id };
1934
- }, [storeId]);
1935
- const selectedDepartment = useMemo(
1936
- () => departments.find((d) => d.code === departmentCode),
1937
- [departments, departmentCode]
1938
- );
1939
- const selectedProvince = useMemo(
1940
- () => provinces.find((p) => p.code === provinceCode),
1941
- [provinces, provinceCode]
1942
- );
1943
- const selectedDistrict = useMemo(
1944
- () => districts.find((d) => d.code === districtCode),
1945
- [districts, districtCode]
1946
- );
1947
- useEffect(() => {
1948
- let active = true;
1949
- if (!storeRequestHeaders) {
1950
- setDepartments([]);
1951
- setLoadingDepartments(false);
1952
- return () => {
1953
- active = false;
1954
- };
1955
- }
1956
- setLoadingDepartments(true);
1957
- loadStorefrontGeoDepartments(storeRequestHeaders).then((items) => {
1958
- if (!active) return;
1959
- setDepartments(items);
1960
- }).finally(() => {
1961
- if (!active) return;
1962
- setLoadingDepartments(false);
1963
- });
1964
- return () => {
1965
- active = false;
1966
- };
1967
- }, [storeRequestHeaders]);
1968
- useEffect(() => {
1969
- const prev = prevDepartmentCode.current;
1970
- const dept = departmentCode;
1971
- const departmentChanged = prev !== void 0 && prev !== dept;
1972
- prevDepartmentCode.current = dept;
1973
- if (!dept) {
1974
- setProvinces([]);
1975
- setDistricts([]);
1976
- prevProvinceCode.current = "";
1977
- prevDepartmentCode.current = "";
1978
- return;
1979
- }
1980
- if (!storeRequestHeaders) {
1981
- setProvinces([]);
1982
- return;
1983
- }
1984
- if (departmentChanged) {
1985
- form.setValue("provinceCode", "", { shouldValidate: true });
1986
- form.setValue("provinceName", "", { shouldValidate: true });
1987
- form.setValue("districtCode", "", { shouldValidate: true });
1988
- form.setValue("districtName", "", { shouldValidate: true });
1989
- setDistricts([]);
1990
- prevProvinceCode.current = "";
1991
- }
1992
- if (selectedDepartment?.name) {
1993
- form.setValue("departmentName", selectedDepartment.name, {
1994
- shouldValidate: true
1995
- });
1996
- }
1997
- let active = true;
1998
- setLoadingProvinces(true);
1999
- fetchStorefrontGeoProvinces(storeRequestHeaders, countryCode, dept).then((items) => {
2000
- if (!active) return;
2001
- setProvinces(items);
2002
- }).catch(() => {
2003
- if (!active) return;
2004
- setProvinces([]);
2005
- }).finally(() => {
2006
- if (!active) return;
2007
- setLoadingProvinces(false);
2008
- });
2009
- return () => {
2010
- active = false;
2011
- };
2012
- }, [departmentCode, countryCode, selectedDepartment?.name, form, storeRequestHeaders]);
2013
- useEffect(() => {
2014
- const prev = prevProvinceCode.current;
2015
- const prov = provinceCode;
2016
- const provinceChanged = prev !== void 0 && prev !== prov;
2017
- prevProvinceCode.current = prov;
2018
- if (!prov) {
2019
- setDistricts([]);
2020
- prevProvinceCode.current = "";
2021
- return;
2022
- }
2023
- if (!storeRequestHeaders) {
2024
- setDistricts([]);
2025
- return;
2026
- }
2027
- if (provinceChanged) {
2028
- form.setValue("districtCode", "", { shouldValidate: true });
2029
- form.setValue("districtName", "", { shouldValidate: true });
2030
- }
2031
- if (selectedProvince?.name) {
2032
- form.setValue("provinceName", selectedProvince.name, {
2033
- shouldValidate: true
2034
- });
2035
- }
2036
- let active = true;
2037
- setLoadingDistricts(true);
2038
- fetchStorefrontGeoDistricts(storeRequestHeaders, countryCode, prov).then((items) => {
2039
- if (!active) return;
2040
- setDistricts(items);
2041
- }).catch(() => {
2042
- if (!active) return;
2043
- setDistricts([]);
2044
- }).finally(() => {
2045
- if (!active) return;
2046
- setLoadingDistricts(false);
2047
- });
2048
- return () => {
2049
- active = false;
2050
- };
2051
- }, [provinceCode, countryCode, selectedProvince?.name, form, storeRequestHeaders]);
2052
- useEffect(() => {
2053
- if (selectedDistrict?.name) {
2054
- form.setValue("districtName", selectedDistrict.name, {
2055
- shouldValidate: true
2056
- });
2057
- }
2058
- }, [selectedDistrict?.name, form]);
2059
- return {
2060
- departments,
2061
- provinces,
2062
- districts,
2063
- loadingDepartments,
2064
- loadingProvinces,
2065
- loadingDistricts,
2066
- departmentCode,
2067
- provinceCode,
2068
- districtCode,
2069
- selectedDepartment,
2070
- selectedProvince,
2071
- selectedDistrict,
2072
- storeRequestHeaders
2073
- };
2074
- }
2075
- var useStore = create()(
2076
- (set) => ({
2077
- data: null,
2078
- drawers: {
2079
- cart: false,
2080
- search: false,
2081
- menu: false,
2082
- filter: false
2083
- },
2084
- openDrawer: (drawer) => set((state) => ({
2085
- drawers: {
2086
- ...state.drawers,
2087
- [drawer]: true
2088
- }
2089
- })),
2090
- closeDrawer: (drawer) => set((state) => ({
2091
- drawers: {
2092
- ...state.drawers,
2093
- [drawer]: false
2094
- }
2095
- })),
2096
- toggleDrawer: (drawer) => set((state) => ({
2097
- drawers: {
2098
- ...state.drawers,
2099
- [drawer]: !state.drawers[drawer]
2100
- }
2101
- })),
2102
- closeAllDrawers: () => set((state) => ({
2103
- drawers: Object.keys(state.drawers).reduce(
2104
- (acc, key) => ({ ...acc, [key]: false }),
2105
- {}
2106
- )
2107
- })),
2108
- modals: {
2109
- login: false,
2110
- signup: false
2111
- },
2112
- openModal: (modal) => set((state) => ({
2113
- modals: {
2114
- ...state.modals,
2115
- login: false,
2116
- signup: false,
2117
- [modal]: true
2118
- }
2119
- })),
2120
- closeModal: (modal) => set((state) => ({
2121
- modals: {
2122
- ...state.modals,
2123
- login: false,
2124
- signup: false,
2125
- [modal]: false
2126
- }
2127
- })),
2128
- toggleModal: (modal) => set((state) => ({
2129
- modals: {
2130
- ...state.modals,
2131
- [modal]: !state.modals[modal]
2132
- }
2133
- })),
2134
- closeAllModals: () => set((state) => ({
2135
- modals: Object.keys(state.modals).reduce(
2136
- (acc, key) => ({ ...acc, [key]: false }),
2137
- {}
2138
- )
2139
- }))
2140
- })
2141
- );
2142
- var useCart = create()(
2143
- persist(
2144
- (set) => ({
2145
- count: 0,
2146
- cart: [],
2147
- cartTokenId: null,
2148
- setCartTokenId: (token) => set((state) => ({ ...state, cartTokenId: token })),
2149
- checkoutStep: null,
2150
- setCheckoutStep: (step) => set((state) => ({ ...state, checkoutStep: step })),
2151
- checkoutCustomerLocal: null,
2152
- setCheckoutCustomerLocal: (customer) => set((state) => {
2153
- if (!state.cartTokenId) return state;
2154
- return {
2155
- checkoutCustomerLocal: {
2156
- cartTokenId: state.cartTokenId,
2157
- customer
2158
- }
2159
- };
2160
- }),
2161
- clearCheckoutCustomerLocal: () => set(() => ({ checkoutCustomerLocal: null })),
2162
- addCart: (item) => set((state) => {
2163
- const existingItem = state.cart.find(
2164
- (cartItem) => cartItem.id === item.id
2165
- );
2166
- if (existingItem) {
2167
- const newQuantity = Math.max(0, existingItem.quantity) + 1;
2168
- const cart2 = state.cart.map(
2169
- (cartItem) => cartItem.id === item.id ? { ...cartItem, quantity: newQuantity } : cartItem
2170
- );
2171
- const count2 = cart2.reduce((sum, i) => sum + Math.max(0, i.quantity), 0);
2172
- return { count: count2, cart: cart2 };
2173
- }
2174
- const newItem = {
2175
- ...item,
2176
- quantity: Math.max(1, Math.max(0, item.quantity ?? 1))
2177
- };
2178
- const cart = [...state.cart, newItem];
2179
- const count = cart.reduce((sum, i) => sum + Math.max(0, i.quantity), 0);
2180
- return { count, cart };
2181
- }),
2182
- removeCart: (id) => set((state) => {
2183
- const existingItem = state.cart.find((item) => item.id === id);
2184
- if (!existingItem) return state;
2185
- if (existingItem.quantity > 1) {
2186
- const newQuantity = Math.max(0, existingItem.quantity - 1);
2187
- const cart2 = state.cart.map(
2188
- (cartItem) => cartItem.id === id ? { ...cartItem, quantity: newQuantity } : cartItem
2189
- );
2190
- const count2 = cart2.reduce((sum, i) => sum + Math.max(0, i.quantity), 0);
2191
- return { count: count2, cart: cart2 };
2192
- }
2193
- const cart = state.cart.filter((item) => item.id !== id);
2194
- const count = Math.max(0, cart.reduce((sum, i) => sum + Math.max(0, i.quantity), 0));
2195
- return { count, cart };
2196
- }),
2197
- clearCartSession: () => set(() => ({
2198
- count: 0,
2199
- cart: [],
2200
- cartTokenId: null,
2201
- checkoutStep: null,
2202
- checkoutCustomerLocal: null
2203
- }))
2204
- }),
2205
- { name: "cart-storage" }
2206
- )
2207
- );
2208
- init_store_api_client();
2209
- function EcomiqProvider({
2210
- store,
2211
- authClient,
2212
- children,
2213
- withShellUi = false
2214
- }) {
2215
- useStore.setState({ data: store ?? null });
2216
- useEffect(() => {
2217
- setClientStoreId(store?.store.id ?? null);
2218
- return () => setClientStoreId(null);
2219
- }, [store]);
2220
- const [queryClient] = useState(() => new QueryClient());
2221
- const tree = /* @__PURE__ */ jsxs(QueryClientProvider, { client: queryClient, children: [
2222
- children,
2223
- withShellUi ? /* @__PURE__ */ jsx(EcomiqShellUi2, {}) : null
2224
- ] });
2225
- if (!authClient) return tree;
2226
- return /* @__PURE__ */ jsx(StorefrontAuthProvider, { authClient, children: tree });
2227
- }
2228
- function EcomiqShellUi2() {
2229
- const [ui, setUi] = useState(null);
2230
- useEffect(() => {
2231
- void Promise.resolve().then(() => (init_ecomiq_shell_ui(), ecomiq_shell_ui_exports)).then((mod) => setUi(() => mod.EcomiqShellUi));
2232
- }, []);
2233
- return ui ? React2.createElement(ui) : null;
2234
- }
2235
-
2236
- // src/react/auth/ecomiq-oauth-password-constants.ts
2237
- var PLUGIN_ID = "ecomiq-oauth-password";
2238
- var ECOMIQ_OAUTH_TOKEN_URL = "https://auth.ecomiq.pe/connect/token";
2239
- var ECOMIQ_OAUTH_USERINFO_URL = "https://auth.ecomiq.pe/connect/userinfo";
2240
- var tokenResponseSchema = z.object({
2241
- access_token: z.string().min(1),
2242
- token_type: z.string().optional(),
2243
- /** Segundos hasta expiración (Ecomiq envía ej. `86399`). Sincroniza la sesión Better Auth con el bearer. */
2244
- expires_in: z.coerce.number().int().positive().optional()
2245
- });
2246
- var userInfoSchema = z.object({
2247
- sub: z.string(),
2248
- email: z.string().email().optional(),
2249
- family_name: z.string().optional(),
2250
- given_name: z.string().optional(),
2251
- iss: z.string().optional(),
2252
- aud: z.union([z.string(), z.array(z.string())]).optional()
2253
- });
2254
- var signInBody = z.object({
2255
- email: z.string().email(),
2256
- password: z.string().min(1),
2257
- callbackURL: z.string().optional(),
2258
- rememberMe: z.boolean().optional().default(true)
2259
- });
2260
-
2261
- // src/react/auth/ecomiq-oauth-password-plugin.ts
2262
- function ecomiqOAuthPassword() {
2263
- const secureCookie = true;
2264
- const clearStorefrontOAuthCookieMiddleware = createAuthMiddleware(async (ctx) => {
2265
- ctx.setCookie(STOREFRONT_ECOMIQ_OAUTH_COOKIE_NAME, "", {
2266
- path: STOREFRONT_ECOMIQ_OAUTH_COOKIE_PATH,
2267
- httpOnly: true,
2268
- sameSite: "lax",
2269
- secure: secureCookie,
2270
- maxAge: 0
2271
- });
2272
- });
2273
- return {
2274
- id: PLUGIN_ID,
2275
- schema: {
2276
- user: {
2277
- fields: {
2278
- firstName: { type: "string", required: false },
2279
- lastName: { type: "string", required: false },
2280
- phone: { type: "string", required: false }
2281
- }
2282
- }
2283
- },
2284
- hooks: {
2285
- after: [
2286
- {
2287
- matcher: (hookCtx) => hookCtx.path === "/sign-out",
2288
- handler: clearStorefrontOAuthCookieMiddleware
2289
- }
2290
- ]
2291
- },
2292
- endpoints: {
2293
- signIn: createAuthEndpoint(
2294
- "/ecomiq-oauth/sign-in",
2295
- {
2296
- method: "POST",
2297
- use: [formCsrfMiddleware],
2298
- body: signInBody
2299
- },
2300
- async (ctx) => {
2301
- const { email: loginEmail, password, callbackURL, rememberMe } = ctx.body;
2302
- const tokens = await exchangePasswordGrant(loginEmail, password, {
2303
- identityScope: ctx.getHeader("x-Identity-Scope"),
2304
- tenantId: ctx.getHeader("x-Tenant-Id"),
2305
- storeId: ctx.getHeader("x-Store-Id")
2306
- });
2307
- const profile = await fetchUserInfo(tokens.access_token);
2308
- const profileEmail = profile.email?.toLowerCase().trim();
2309
- const canonicalEmail = profileEmail ?? loginEmail.toLowerCase().trim();
2310
- if (profileEmail && profileEmail !== loginEmail.toLowerCase().trim()) {
2311
- throw APIError.from("UNAUTHORIZED", {
2312
- code: "ECOMIQ_EMAIL_MISMATCH",
2313
- message: "El correo del perfil OAuth no coincide con el inicio de sesi\xF3n"
2314
- });
2315
- }
2316
- const displayName = displayNameFromUserInfo(profile, canonicalEmail);
2317
- const existing = await ctx.context.internalAdapter.findUserByEmail(canonicalEmail);
2318
- let user = existing?.user;
2319
- if (!user) {
2320
- user = await ctx.context.internalAdapter.createUser({
2321
- email: canonicalEmail,
2322
- name: displayName,
2323
- emailVerified: true
2324
- });
2325
- } else if (user.name !== displayName) {
2326
- await ctx.context.internalAdapter.updateUser(user.id, { name: displayName });
2327
- user = { ...user, name: displayName };
2328
- }
2329
- const oauthSessionExpires = tokens.expires_in !== void 0 && tokens.expires_in > 0 ? new Date(Date.now() + tokens.expires_in * 1e3) : void 0;
2330
- const session = await ctx.context.internalAdapter.createSession(
2331
- user.id,
2332
- rememberMe === false,
2333
- oauthSessionExpires ? { expiresAt: oauthSessionExpires } : {},
2334
- Boolean(oauthSessionExpires)
2335
- );
2336
- if (!session) {
2337
- throw APIError.from("INTERNAL_SERVER_ERROR", {
2338
- code: "ECOMIQ_SESSION_FAILED",
2339
- message: "No se pudo crear la sesi\xF3n"
2340
- });
2341
- }
2342
- await setSessionCookie(ctx, { session, user }, rememberMe === false);
2343
- 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;
2344
- await ctx.setCookie(STOREFRONT_ECOMIQ_OAUTH_COOKIE_NAME, tokens.access_token, {
2345
- path: STOREFRONT_ECOMIQ_OAUTH_COOKIE_PATH,
2346
- httpOnly: true,
2347
- sameSite: "lax",
2348
- secure: secureCookie,
2349
- maxAge: oauthMaxAgeSec
2350
- });
2351
- if (callbackURL) ctx.setHeader("Location", callbackURL);
2352
- return ctx.json({
2353
- redirect: !!callbackURL,
2354
- token: session.token,
2355
- url: callbackURL,
2356
- user: parseUserOutput(ctx.context.options, user)
2357
- });
2358
- }
2359
- )
2360
- }
2361
- };
2362
- }
2363
- function displayNameFromUserInfo(profile, fallbackEmail) {
2364
- const parts = [profile.given_name?.trim(), profile.family_name?.trim()].filter(Boolean);
2365
- const joined = parts.join(" ").trim();
2366
- if (joined) return joined;
2367
- return fallbackEmail.split("@")[0] || "Cliente";
2368
- }
2369
- async function fetchUserInfo(accessToken) {
2370
- const res = await fetch(ECOMIQ_OAUTH_USERINFO_URL, {
2371
- method: "GET",
2372
- headers: {
2373
- Authorization: `Bearer ${accessToken}`,
2374
- accept: "application/json"
2375
- }
2376
- });
2377
- const text = await res.text();
2378
- let json;
2379
- try {
2380
- json = text ? JSON.parse(text) : {};
2381
- } catch {
2382
- throw APIError.from("UNAUTHORIZED", {
2383
- code: "ECOMIQ_USERINFO_INVALID_BODY",
2384
- message: "Respuesta inv\xE1lida del endpoint userinfo"
2385
- });
2386
- }
2387
- if (!res.ok) {
2388
- const err = json;
2389
- throw APIError.from("UNAUTHORIZED", {
2390
- code: "ECOMIQ_USERINFO_REJECTED",
2391
- message: err.error_description ?? err.error ?? "No se pudo obtener el perfil del usuario"
2392
- });
2393
- }
2394
- const parsed = userInfoSchema.safeParse(json);
2395
- if (!parsed.success) {
2396
- throw APIError.from("UNAUTHORIZED", {
2397
- code: "ECOMIQ_USERINFO_SHAPE",
2398
- message: "Perfil OAuth con formato inv\xE1lido"
2399
- });
2400
- }
2401
- return parsed.data;
2402
- }
2403
- async function exchangePasswordGrant(username, password, context) {
2404
- const clientId = process.env.ECOMIQ_AUTH_CLIENT_ID ?? "";
2405
- const clientSecret = process.env.ECOMIQ_AUTH_CLIENT_SECRET ?? "";
2406
- const scope = process.env.ECOMIQ_AUTH_SCOPE ?? "";
2407
- if (!clientId || !clientSecret) {
2408
- throw APIError.from("INTERNAL_SERVER_ERROR", {
2409
- code: "ECOMIQ_AUTH_NOT_CONFIGURED",
2410
- message: "OAuth Ecomiq no configurado: ECOMIQ_AUTH_CLIENT_ID y ECOMIQ_AUTH_CLIENT_SECRET."
2411
- });
2412
- }
2413
- const body = new URLSearchParams({
2414
- grant_type: "password",
2415
- username,
2416
- password,
2417
- scope,
2418
- client_id: clientId,
2419
- client_secret: clientSecret
2420
- });
2421
- const identityScope = context.identityScope;
2422
- const tenantId = context.tenantId;
2423
- const storeId = context.storeId;
2424
- const headers = new Headers({
2425
- "content-type": "application/x-www-form-urlencoded",
2426
- accept: "application/json"
2427
- });
2428
- if (identityScope?.trim()) {
2429
- headers.set("x-Identity-Scope", identityScope.trim());
2430
- }
2431
- if (tenantId?.trim()) {
2432
- headers.set("x-Tenant-Id", tenantId.trim());
2433
- }
2434
- if (storeId?.trim()) {
2435
- headers.set("x-Store-Id", storeId.trim());
2436
- }
2437
- let res;
2438
- let text;
2439
- try {
2440
- res = await fetch(ECOMIQ_OAUTH_TOKEN_URL, {
2441
- method: "POST",
2442
- headers,
2443
- body
2444
- });
2445
- text = await res.text();
2446
- } catch (e) {
2447
- throw mapTokenEndpointFetchFailure(e);
2448
- }
2449
- const json = safeParseJson(text);
2450
- if (!res.ok) {
2451
- throwBadRequestFromOAuth(json, res.statusText || text || `HTTP ${res.status}`);
2452
- }
2453
- const parsed = tokenResponseSchema.safeParse(json);
2454
- if (!parsed.success) {
2455
- throwBadRequestFromOAuth(json, "Token de acceso no recibido");
2456
- }
2457
- return parsed.data;
2458
- }
2459
- function safeParseJson(text) {
2460
- try {
2461
- return text ? JSON.parse(text) : {};
2462
- } catch {
2463
- return {};
2464
- }
2465
- }
2466
- function throwBadRequestFromOAuth(payload, fallbackMessage) {
2467
- const err = payload;
2468
- const errorCode = typeof err.error === "string" && err.error.trim() ? err.error.trim() : "invalid_grant";
2469
- 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.";
2470
- throw APIError.from("BAD_REQUEST", {
2471
- code: errorCode,
2472
- message: errorMessage.slice(0, 500)
2473
- });
2474
- }
2475
- function mapTokenEndpointFetchFailure(e) {
2476
- const codes = collectNetworkErrorCodes(e);
2477
- const msg = e instanceof Error ? e.message : String(e);
2478
- const isDnsError = codes.some((c) => c === "ENOTFOUND" || c === "EAI_AGAIN");
2479
- if (isDnsError) {
2480
- throw APIError.from("BAD_GATEWAY", {
2481
- code: "ECOMIQ_AUTH_DNS",
2482
- message: "No se pudo resolver el servidor de autenticacion."
2483
- });
2484
- }
2485
- const isConnectionRefused = codes.includes("ECONNREFUSED");
2486
- if (isConnectionRefused) {
2487
- throw APIError.from("BAD_GATEWAY", {
2488
- code: "ECOMIQ_AUTH_REFUSED",
2489
- message: "Conexion rechazada al servidor de autenticacion."
2490
- });
2491
- }
2492
- const looksLikeAbortOrSocketClose = codes.some(
2493
- (c) => ["UND_ERR_SOCKET", "ECONNRESET", "EPIPE", "ECONNABORTED"].includes(c)
2494
- ) || /terminated|abort|closed|FIN|socket/i.test(msg);
2495
- if (looksLikeAbortOrSocketClose) {
2496
- throwBadRequestFromOAuth({}, "Credenciales invalidas o conexion cerrada por el servidor.");
2497
- }
2498
- throw APIError.from("INTERNAL_SERVER_ERROR", {
2499
- code: "ECOMIQ_AUTH_REQUEST_FAILED",
2500
- message: msg.slice(0, 280) || "Error al contactar el servidor de autenticacion."
2501
- });
2502
- }
2503
- function collectNetworkErrorCodes(e) {
2504
- const out = [];
2505
- const visit = (x) => {
2506
- if (!x || typeof x !== "object") return;
2507
- const code = "code" in x && typeof x.code === "string" ? x.code : void 0;
2508
- if (code) out.push(code);
2509
- if ("cause" in x && x.cause) {
2510
- visit(x.cause);
2511
- }
2512
- };
2513
- visit(e);
2514
- return out;
2515
- }
2516
-
2517
- // src/react/auth/ecomiq-oauth-password-client.ts
2518
- function ecomiqOAuthPasswordClient() {
2519
- return {
2520
- id: PLUGIN_ID,
2521
- pathMethods: {
2522
- "/ecomiq-oauth/sign-in": "POST"
2523
- },
2524
- atomListeners: [
2525
- {
2526
- matcher: (path) => path === "/ecomiq-oauth/sign-in",
2527
- signal: "$sessionSignal"
2528
- }
2529
- ]
2530
- };
2531
- }
2532
-
2533
- // src/react/auth/ecomiq-storefront-sign-in.ts
2534
- async function signInWithEcomiqCredentials2(authClient, settings, credentials) {
2535
- const response = await authClient.$fetch("/ecomiq-oauth/sign-in", {
2536
- method: "POST",
2537
- headers: buildEcomiqOAuthIdentityHeaders(settings),
2538
- body: credentials
2539
- });
2540
- if (response.error) {
2541
- throw new Error(response.error.message ?? "Error al iniciar sesi\xF3n");
2542
- }
2543
- return response;
2544
- }
2545
- function buildOptionGroups(variants) {
2546
- const map = /* @__PURE__ */ new Map();
2547
- for (const v of variants) {
2548
- for (const opt of v.options ?? []) {
2549
- const key = opt.key;
2550
- const name = opt.name ?? key;
2551
- if (!map.has(key)) map.set(key, /* @__PURE__ */ new Map());
2552
- map.get(key).set(opt.value, name);
2553
- }
2554
- }
2555
- return Array.from(map.entries()).map(([key, valueMap]) => ({
2556
- key,
2557
- name: Array.from(valueMap.values())[0] ?? key,
2558
- values: Array.from(valueMap.keys())
2559
- }));
2560
- }
2561
- function findVariant(variants, selected, hasConfigurableOptions) {
2562
- if (!hasConfigurableOptions && variants.length > 0) {
2563
- return variants[0] ?? null;
2564
- }
2565
- if (Object.keys(selected).length === 0) return null;
2566
- for (const v of variants) {
2567
- const opts = v.options ?? [];
2568
- const match = opts.length > 0 && opts.every((o) => selected[o.key] === o.value);
2569
- if (match) return v;
2570
- }
2571
- return null;
2572
- }
2573
- function getInitialOptions(variants, optionGroups) {
2574
- const first = variants[0];
2575
- if (!first) return {};
2576
- const opts = first.options ?? [];
2577
- const selected = {};
2578
- for (const g of optionGroups) {
2579
- const o = opts.find((x) => x.key === g.key);
2580
- if (o) selected[g.key] = o.value;
2581
- }
2582
- return selected;
2583
- }
2584
- function ProductVariantSelector({
2585
- product,
2586
- currency = "USD",
2587
- features = [],
2588
- onVariantChange,
2589
- children
2590
- }) {
2591
- const variants = useMemo(() => product.variants ?? [], [product.variants]);
2592
- const optionGroups = useMemo(() => buildOptionGroups(variants), [variants]);
2593
- const hasConfigurableOptions = optionGroups.length > 0;
2594
- const [selectedOptions, setSelectedOptions] = useState(
2595
- () => getInitialOptions(variants, optionGroups)
2596
- );
2597
- const selectedVariant = useMemo(
2598
- () => findVariant(variants, selectedOptions, hasConfigurableOptions),
2599
- [variants, selectedOptions, hasConfigurableOptions]
2600
- );
2601
- const displayPrice = selectedVariant?.price ?? product.price ?? 0;
2602
- const compareAtPrice = selectedVariant?.compareAtPrice ?? product.compareAtPrice;
2603
- const isAvailable = (selectedVariant ? (selectedVariant.quantity ?? 0) > 0 : product.isAvailable) ?? true;
2604
- useEffect(() => {
2605
- onVariantChange?.(selectedVariant, displayPrice);
2606
- }, [selectedVariant, displayPrice, onVariantChange]);
2607
- const setOption = useCallback((key, value) => {
2608
- setSelectedOptions((prev) => ({ ...prev, [key]: value }));
2609
- }, []);
2610
- const hasVariants = hasConfigurableOptions;
2611
- return /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
2612
- /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-2 flex-wrap", children: [
2613
- /* @__PURE__ */ jsx(
2614
- "span",
2615
- {
2616
- className: "inline-flex items-center rounded-md bg-primary/10 px-2.5 py-0.5 text-base font-semibold text-primary",
2617
- "data-testid": "product-display-price",
2618
- children: formatCurrency$1(displayPrice, currency)
2619
- }
2620
- ),
2621
- compareAtPrice != null && compareAtPrice > displayPrice && /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground line-through", children: formatCurrency$1(compareAtPrice, currency) })
2622
- ] }),
2623
- hasVariants && optionGroups.map((group) => /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2624
- /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold uppercase tracking-wider text-muted-foreground", children: group.name }),
2625
- /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: group.values.map((value) => {
2626
- const isSelected = selectedOptions[group.key] === value;
2627
- const isColorLike = group.key.toLowerCase().includes("color") || group.key.toLowerCase().includes("colour");
2628
- return /* @__PURE__ */ jsx(
2629
- "button",
2630
- {
2631
- type: "button",
2632
- onClick: () => setOption(group.key, value),
2633
- className: isColorLike ? `min-w-[72px] rounded-md border-2 px-3 py-2 text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 ${isSelected ? "border-primary bg-primary text-foreground" : "border-border bg-muted/30 text-foreground hover:border-muted-foreground/50"}` : `rounded-md border px-3 py-2 text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 ${isSelected ? "border-primary bg-muted ring-1 ring-primary" : "border-border bg-transparent text-muted-foreground hover:border-muted-foreground/50 hover:text-foreground"}`,
2634
- style: isColorLike && isSelected && value.toLowerCase() === "black" ? { backgroundColor: "#1a1a1a", borderColor: "#1a1a1a", color: "#fff" } : isColorLike && value.toLowerCase() === "white" ? { backgroundColor: "#fafafa", color: "#1a1a1a", borderColor: "#e5e5e5" } : void 0,
2635
- "data-testid": `variant-option-${group.key}-${value}`,
2636
- children: value
2637
- },
2638
- value
2639
- );
2640
- }) })
2641
- ] }, group.key)),
2642
- features.length > 0 && /* @__PURE__ */ jsx("ul", { className: "list-disc list-inside space-y-1 text-sm text-muted-foreground", children: features.map((f, i) => /* @__PURE__ */ jsx("li", { children: f }, i)) }),
2643
- children?.({
2644
- product,
2645
- selectedVariant,
2646
- displayPrice,
2647
- compareAtPrice,
2648
- isAvailable,
2649
- selectedOptions
2650
- })
2651
- ] });
2652
- }
2653
- function isRenderableCmsPage(p) {
2654
- if (!p.status) return true;
2655
- return p.status === "published";
2656
- }
2657
- function findStorefrontCmsPageBySlug(settings, slug) {
2658
- const pages = settings?.cms?.pages;
2659
- if (!pages?.length) return null;
2660
- const raw = pages.find(
2661
- (p) => p.slug === slug && isRenderableCmsPage(p)
2662
- );
2663
- if (!raw) return null;
2664
- const parsed = StorefrontPageDetailSchema$1.safeParse(raw);
2665
- return parsed.success ? parsed.data : null;
2666
- }
2667
-
2668
- export { CartAddressesRequestSchema, CartBuyerRequestSchema, CartCheckoutPaymentActionSchema, CartCheckoutPaymentSchema, CartCheckoutReadResponseSchema, CartCheckoutRequestSchema, CartCheckoutResponseSchema, CartCouponRequestSchema, CartCreateRequestSchema, CartItemModifierRequestSchema, CartItemRequestSchema, CartPaymentMethodSchema, CartPaymentMethodsResponseSchema, CartPaymentReceiptResponseSchema, CartResponseBundleItemSchema, CartResponseItemSchema, CartResponseSchema, CartShippingMethodPutRequestSchema, EcomiqConfigSchema, EcomiqProvider, ProductSchema, ProductVariantSchema, ProductVariantSelector, ProductsFiltersSchema, ProductsResponseSchema, QueryProvider, STOREFRONT_API_PATH_PREFIX, STOREFRONT_ECOMIQ_OAUTH_COOKIE_NAME, STOREFRONT_ECOMIQ_OAUTH_COOKIE_PATH, ShippingOptionGroupSchema, ShippingOptionsResponseSchema, ShippingRateSchema, StorefrontAuthProvider, StorefrontBundleItemSchema, StorefrontCheckoutSettingsSchema, StorefrontCollectionDetailSchema, StorefrontCollectionSchema, StorefrontCollectionsResponseSchema, StorefrontPageDetailSchema, StorefrontPageListItemSchema, StorefrontPriceTierSchema, StorefrontProductDetailSchema, StorefrontProductSchema, StorefrontProductsParamsSchema, StorefrontProductsResponseSchema, StorefrontSettingsLegacyFlatSchema, StorefrontSettingsSchema, StorefrontStoreSchema, apiClient, buildEcomiqOAuthIdentityHeaders, cartService, clearEcomiqConfigCache, collectionService, countryCurrency, customerOrderStatusTone, ecomiqOAuthPassword, ecomiqOAuthPasswordClient, fetchStorefrontGeoDistricts, fetchStorefrontGeoProvinces, findStorefrontCmsPageBySlug, formatCurrency, formatCustomerOrderStatus, formatOrderDate, formatOrderDateTime, hasEcomiqSlugStoreConfig, loadEcomiqConfig, loadStorefrontGeoDepartments, normalizeStorefrontSettingsPayload, pageService, productService, resolveIneiCodesFromLabels, resolvePublicStoreKey, signInWithEcomiqCredentials2 as signInWithEcomiqCredentials, storeService, storefrontAccessTokenNeedsRefresh, storefrontFaviconUrl, storefrontOgImageUrl, storefrontSessionEndsSoon, storefrontStoreRecord, toGeoItems, useCart, useCartAddresses, useCartCreate, useCartGet, useCollections, useConfig, usePages, useProducts, useStore, useStorefrontAuthClient, useStorefrontGeoInei, useStorefrontSession, useUser };
2669
- //# sourceMappingURL=react.js.map
2670
- //# sourceMappingURL=react.js.map