@pakento/cms-sdk 4.0.4 → 4.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,46 +1,32 @@
1
1
  // src/services/api.ts
2
2
  import axios, { AxiosError } from "axios";
3
+
4
+ // src/services/cache.ts
3
5
  import { Redis } from "@upstash/redis";
4
- var PakentoCMSAPI = class {
5
- constructor(config) {
6
- this.defaultTTL = 86400;
7
- this.baseURL = process.env.PAKENTO_CMS_BASE_URL || "";
8
- this.apiKey = process.env.PAKENTO_API_KEY || "";
9
- if (config?.cacheTTL && typeof config.cacheTTL === "number") {
10
- this.defaultTTL = config.cacheTTL;
11
- }
6
+ import { createHash } from "crypto";
7
+ var CacheService = class {
8
+ constructor(defaultTTL, apiKey) {
9
+ this.defaultTTL = defaultTTL;
10
+ this.apiKeyHash = createHash("sha256").update(apiKey).digest("hex").substring(0, 8);
12
11
  if (process.env.UPSTASH_REDIS_REST_URL && process.env.UPSTASH_REDIS_REST_TOKEN) {
13
12
  this.redis = new Redis({
14
13
  url: process.env.UPSTASH_REDIS_REST_URL,
15
14
  token: process.env.UPSTASH_REDIS_REST_TOKEN
16
15
  });
17
16
  }
18
- if (!this.baseURL || !this.apiKey) {
19
- console.warn(
20
- "[PakentoSDK] Variables de entorno PAKENTO_CMS_BASE_URL o PAKENTO_API_KEY faltantes. Se validar\xE1n al hacer la petici\xF3n."
21
- );
22
- }
23
- this.client = axios.create({
24
- baseURL: this.baseURL,
25
- headers: {
26
- "Content-Type": "application/json",
27
- Authorization: `users API-Key ${this.apiKey}`
28
- }
29
- });
30
- this.client.interceptors.request.use(
31
- (config2) => {
32
- const fullUrl = `${config2.baseURL}${config2.url}`;
33
- console.log(`\u{1F310} API Call: ${config2.method?.toUpperCase()} ${fullUrl}`);
34
- return config2;
35
- },
36
- (error) => {
37
- return Promise.reject(error);
38
- }
39
- );
40
17
  }
41
- /**
42
- * Obtiene datos desde cache si existen; de lo contrario llama a fetcher() y guarda.
43
- */
18
+ generateParamsHash(params) {
19
+ const sortedParams = Object.keys(params).sort().reduce((acc, key) => {
20
+ acc[key] = params[key];
21
+ return acc;
22
+ }, {});
23
+ const paramsString = JSON.stringify(sortedParams);
24
+ return createHash("sha256").update(paramsString).digest("hex").substring(0, 16);
25
+ }
26
+ buildCacheKey(functionName, params = {}) {
27
+ const paramsHash = this.generateParamsHash(params);
28
+ return `pakento:${this.apiKeyHash}:${functionName}:${paramsHash}`;
29
+ }
44
30
  safeJsonParse(data) {
45
31
  try {
46
32
  return JSON.parse(data);
@@ -53,43 +39,215 @@ var PakentoCMSAPI = class {
53
39
  }
54
40
  async getCachedOrFetch(key, fetcher, ttl, skipCache = false) {
55
41
  if (!this.redis || skipCache) {
42
+ console.log(`[PakentoSDK] ${!this.redis ? "Redis no configurado" : "Saltando cache"} - llamando a API`);
56
43
  return fetcher();
57
44
  }
58
45
  try {
46
+ console.log(`[PakentoSDK] Buscando en cache: ${key}`);
59
47
  const cached = await this.redis.get(key);
60
48
  if (cached) {
49
+ console.log(`[PakentoSDK] \u2705 Cache hit para: ${key}`);
61
50
  const parsed = this.safeJsonParse(cached);
62
51
  if (parsed !== null) {
63
52
  return parsed;
53
+ } else {
54
+ console.warn(`[PakentoSDK] Error parseando cache para: ${key}`);
64
55
  }
56
+ } else {
57
+ console.log(`[PakentoSDK] \u274C Cache miss para: ${key}`);
65
58
  }
66
59
  } catch (err) {
67
60
  console.warn("[PakentoSDK] Error leyendo cache Redis", err);
68
61
  }
62
+ console.log(`[PakentoSDK] Llamando a API para: ${key}`);
69
63
  const result = await fetcher();
70
64
  try {
71
65
  await this.redis.set(key, this.serializeForCache(result), {
72
66
  ex: ttl ?? this.defaultTTL
73
67
  });
68
+ console.log(`[PakentoSDK] \u2705 Guardado en cache: ${key} (TTL: ${ttl ?? this.defaultTTL}s)`);
74
69
  } catch (err) {
75
70
  console.warn("[PakentoSDK] Error escribiendo cache Redis", err);
76
71
  }
77
72
  return result;
78
73
  }
79
- /**
80
- * Construye una llave única para el cache a partir del nombre de la
81
- * consulta y los parámetros.
82
- */
83
- buildCacheKey(queryName, variables) {
84
- const vars = typeof variables === "string" ? variables : JSON.stringify(variables ?? {});
85
- return `${this.apiKey}-${queryName}:${vars}`;
74
+ async hasCache(functionName, params = {}) {
75
+ if (!this.redis) {
76
+ return false;
77
+ }
78
+ try {
79
+ const key = this.buildCacheKey(functionName, params);
80
+ const cached = await this.redis.get(key);
81
+ return cached !== null;
82
+ } catch (err) {
83
+ console.warn("[PakentoSDK] Error verificando cache", err);
84
+ return false;
85
+ }
86
+ }
87
+ getCacheKey(functionName, params = {}) {
88
+ return this.buildCacheKey(functionName, params);
89
+ }
90
+ async clearCache(functionName, params = {}) {
91
+ if (!this.redis) {
92
+ return false;
93
+ }
94
+ try {
95
+ const key = this.buildCacheKey(functionName, params);
96
+ await this.redis.del(key);
97
+ console.log(`[PakentoSDK] \u2705 Cache limpiado para: ${key}`);
98
+ return true;
99
+ } catch (err) {
100
+ console.warn("[PakentoSDK] Error limpiando cache", err);
101
+ return false;
102
+ }
103
+ }
104
+ async clearAllCache() {
105
+ if (!this.redis) {
106
+ return false;
107
+ }
108
+ try {
109
+ const pattern = `pakento:${this.apiKeyHash}:*`;
110
+ const keys = await this.redis.keys(pattern);
111
+ if (keys.length > 0) {
112
+ await this.redis.del(...keys);
113
+ console.log(`[PakentoSDK] \u2705 Cache limpiado para ${keys.length} keys`);
114
+ }
115
+ return true;
116
+ } catch (err) {
117
+ console.warn("[PakentoSDK] Error limpiando todo el cache", err);
118
+ return false;
119
+ }
120
+ }
121
+ async getCacheInfo(functionName, params = {}) {
122
+ if (!this.redis) {
123
+ return {
124
+ exists: false,
125
+ key: this.buildCacheKey(functionName, params)
126
+ };
127
+ }
128
+ try {
129
+ const key = this.buildCacheKey(functionName, params);
130
+ const exists = await this.redis.exists(key);
131
+ const ttl = exists ? await this.redis.ttl(key) : void 0;
132
+ return {
133
+ exists: exists === 1,
134
+ key,
135
+ ttl: ttl === -1 ? void 0 : ttl
136
+ };
137
+ } catch (err) {
138
+ console.warn("[PakentoSDK] Error obteniendo info del cache", err);
139
+ return {
140
+ exists: false,
141
+ key: this.buildCacheKey(functionName, params)
142
+ };
143
+ }
144
+ }
145
+ };
146
+
147
+ // src/services/api.ts
148
+ import { z } from "zod";
149
+ var PakentoCMSAPI = class {
150
+ constructor(config) {
151
+ this.defaultTTL = 86400;
152
+ this.baseURL = process.env.PAKENTO_CMS_BASE_URL || "";
153
+ this.apiKey = process.env.PAKENTO_API_KEY || "";
154
+ if (config?.cacheTTL && typeof config.cacheTTL === "number") {
155
+ this.defaultTTL = config.cacheTTL;
156
+ }
157
+ this.cache = new CacheService(this.defaultTTL, this.apiKey);
158
+ this.client = axios.create({
159
+ baseURL: this.baseURL,
160
+ headers: {
161
+ "Content-Type": "application/json",
162
+ Authorization: `users API-Key ${this.apiKey}`
163
+ }
164
+ });
165
+ this.client.interceptors.request.use(
166
+ (config2) => {
167
+ const fullUrl = `${config2.baseURL}${config2.url}`;
168
+ console.log(`\u{1F310} API Call: ${config2.method?.toUpperCase()} ${fullUrl}`);
169
+ return config2;
170
+ },
171
+ (error) => {
172
+ return Promise.reject(error);
173
+ }
174
+ );
175
+ }
176
+ // Centralized error handler
177
+ handleApiError(error, context) {
178
+ if (error instanceof AxiosError) {
179
+ const status = error.response?.status;
180
+ const responseMessage = error.response?.data?.message;
181
+ if (status === 401) return "API Key inv\xE1lida o expirada";
182
+ if (status === 404) return `${context} no encontrado`;
183
+ if (status && status >= 500) return "Error del servidor CMS";
184
+ return responseMessage || `Error de conexi\xF3n en ${context}: ${error.message}`;
185
+ } else if (error instanceof z.ZodError) {
186
+ return `Validaci\xF3n fallida: ${error.issues.map((e) => e.message).join(", ")}`;
187
+ } else if (error instanceof Error) {
188
+ return error.message;
189
+ }
190
+ return "Error desconocido";
191
+ }
192
+ // Generic GraphQL fetcher
193
+ async fetchGraphQL(query, variables, extractData) {
194
+ try {
195
+ const response = await this.client.post("/api/graphql", {
196
+ query,
197
+ variables
198
+ });
199
+ if (response.data.errors) {
200
+ const errorMessage = `GraphQL Error: ${response.data.errors[0].message}`;
201
+ return { data: null, error: true, errorMessage };
202
+ }
203
+ const data = extractData(response.data.data);
204
+ return { data, error: false, errorMessage: null };
205
+ } catch (error) {
206
+ const errorMessage = this.handleApiError(error, "GraphQL query");
207
+ return { data: null, error: true, errorMessage };
208
+ }
86
209
  }
87
210
  async getItems(params = {}) {
88
- const { skipCache = false, cacheTTL, ...rest } = params;
89
- const cacheKey = this.buildCacheKey("GetEcommerceItems", rest);
90
- return this.getCachedOrFetch(
211
+ const itemsSchema = z.object({
212
+ where: z.object({
213
+ item_category_id: z.object({ equals: z.string().optional() }).optional(),
214
+ item_super_category_id: z.object({ equals: z.string().optional() }).optional(),
215
+ brand_id: z.object({ equals: z.string().optional() }).optional(),
216
+ id: z.object({ equals: z.string().optional() }).optional()
217
+ }).optional(),
218
+ onlyOffers: z.boolean().optional(),
219
+ onlyEcommerce: z.boolean().optional(),
220
+ limit: z.number().optional(),
221
+ page: z.number().optional(),
222
+ search: z.string().optional(),
223
+ sort: z.string().optional(),
224
+ minPrice: z.number().optional(),
225
+ maxPrice: z.number().optional(),
226
+ skipCache: z.boolean().optional(),
227
+ cacheTTL: z.number().optional()
228
+ });
229
+ const validatedParams = itemsSchema.safeParse(params);
230
+ if (!validatedParams.success) {
231
+ const errorMessage = this.handleApiError(
232
+ validatedParams.error,
233
+ "getItems validation"
234
+ );
235
+ return {
236
+ data: null,
237
+ items: [],
238
+ totalDocs: 0,
239
+ totalPages: 0,
240
+ prevPage: null,
241
+ nextPage: null,
242
+ error: true,
243
+ errorMessage
244
+ };
245
+ }
246
+ const { skipCache = false, cacheTTL, ...rest } = validatedParams.data;
247
+ const cacheKey = this.cache.buildCacheKey("GetEcommerceItems", rest);
248
+ return this.cache.getCachedOrFetch(
91
249
  cacheKey,
92
- () => this.fetchItemsFromAPI(params),
250
+ () => this.fetchItemsFromAPI(rest),
93
251
  cacheTTL,
94
252
  skipCache
95
253
  );
@@ -107,136 +265,84 @@ var PakentoCMSAPI = class {
107
265
  errorMessage: "SDK no configurado: PAKENTO_CMS_BASE_URL o PAKENTO_API_KEY faltantes."
108
266
  };
109
267
  }
110
- try {
111
- const whereConditions = {};
112
- if (params.where?.item_category_id?.equals) {
113
- whereConditions.item_category_id = {
114
- equals: params.where.item_category_id.equals
115
- };
116
- }
117
- if (params.where?.item_super_category_id?.equals) {
118
- whereConditions.item_super_category_id = {
119
- equals: params.where.item_super_category_id.equals
120
- };
121
- }
122
- if (params.where?.brand_id?.equals) {
123
- whereConditions.brand_id = {
124
- equals: params.where.brand_id.equals
125
- };
126
- }
127
- if (params.where?.id?.equals) {
128
- whereConditions.id = {
129
- equals: params.where.id.equals
130
- };
131
- }
132
- const variables = {
133
- onlyOffers: params.onlyOffers,
134
- onlyEcommerce: params.onlyEcommerce,
135
- limit: params.limit,
136
- page: params.page,
137
- search: params.search,
138
- sort: params.sort,
139
- minPrice: params.minPrice,
140
- maxPrice: params.maxPrice
141
- };
142
- if (Object.keys(whereConditions).length > 0) {
143
- variables.where = whereConditions;
144
- }
145
- const query = `
146
- query GetEcommerceItems(
147
- $where: EcommerceItemsWhere
148
- $onlyOffers: Boolean
149
- $onlyEcommerce: Boolean
150
- $limit: Int
151
- $page: Int
152
- $search: String
153
- $sort: String
154
- $minPrice: Float
155
- $maxPrice: Float
268
+ const query = `
269
+ query GetEcommerceItems(
270
+ $where: EcommerceItemsWhere
271
+ $onlyOffers: Boolean
272
+ $onlyEcommerce: Boolean
273
+ $limit: Int
274
+ $page: Int
275
+ $search: String
276
+ $sort: String
277
+ $minPrice: Float
278
+ $maxPrice: Float
279
+ ) {
280
+ GetEcommerceItems(
281
+ where: $where
282
+ onlyOffers: $onlyOffers
283
+ onlyEcommerce: $onlyEcommerce
284
+ limit: $limit
285
+ page: $page
286
+ search: $search
287
+ sort: $sort
288
+ minPrice: $minPrice
289
+ maxPrice: $maxPrice
156
290
  ) {
157
- GetEcommerceItems(
158
- where: $where
159
- onlyOffers: $onlyOffers
160
- onlyEcommerce: $onlyEcommerce
161
- limit: $limit
162
- page: $page
163
- search: $search
164
- sort: $sort
165
- minPrice: $minPrice
166
- maxPrice: $maxPrice
167
- ) {
168
- totalDocs
169
- totalPages
170
- prevPage
171
- nextPage
172
- docs {
173
- id
174
- name
175
- featured
176
- old_price
177
- currency_prefix
178
- description
179
- price_text
180
- price
181
- brand_id
182
- brand_name
183
- cover_image_url
184
- cover_image_thumbnail_url
185
- item_category_name
186
- item_category_id
187
- images {
188
- alt
189
- url
190
- thumbnail_url
191
- }
291
+ totalDocs
292
+ totalPages
293
+ prevPage
294
+ nextPage
295
+ docs {
296
+ id
297
+ name
298
+ featured
299
+ old_price
300
+ currency_prefix
301
+ description
302
+ price_text
303
+ price
304
+ brand_id
305
+ brand_name
306
+ cover_image_url
307
+ cover_image_thumbnail_url
308
+ item_category_name
309
+ item_category_id
310
+ images {
311
+ alt
312
+ url
313
+ thumbnail_url
192
314
  }
193
315
  }
194
316
  }
195
- `;
196
- const response = await this.client.post("/api/graphql", {
197
- query,
198
- variables
199
- });
200
- if (response.data.errors) {
201
- const errorMessage = `GraphQL Error: ${response.data.errors[0].message}`;
202
- return {
203
- data: null,
204
- items: [],
205
- totalDocs: 0,
206
- totalPages: 0,
207
- prevPage: null,
208
- nextPage: null,
209
- error: true,
210
- errorMessage
211
- };
212
317
  }
213
- const rawData = response.data.data.GetEcommerceItems;
214
- return {
215
- data: rawData,
216
- items: rawData.docs,
217
- totalDocs: rawData.totalDocs,
218
- totalPages: rawData.totalPages,
219
- prevPage: rawData.prevPage,
220
- nextPage: rawData.nextPage,
221
- error: false,
222
- errorMessage: null
318
+ `;
319
+ const whereConditions = {};
320
+ if (params.where?.item_category_id?.equals) {
321
+ whereConditions.item_category_id = {
322
+ equals: params.where.item_category_id.equals
223
323
  };
224
- } catch (error) {
225
- let errorMessage = "Error desconocido";
226
- if (error instanceof AxiosError) {
227
- const status = error.response?.status;
228
- if (status === 401) {
229
- errorMessage = "API Key inv\xE1lida o expirada";
230
- } else if (status === 404) {
231
- errorMessage = "Endpoint no encontrado";
232
- } else if (status && status >= 500) {
233
- errorMessage = "Error del servidor CMS";
234
- } else {
235
- errorMessage = `Error de conexi\xF3n: ${error.message}`;
236
- }
237
- } else if (error instanceof Error) {
238
- errorMessage = error.message;
239
- }
324
+ }
325
+ if (params.where?.item_super_category_id?.equals) {
326
+ whereConditions.item_super_category_id = {
327
+ equals: params.where.item_super_category_id.equals
328
+ };
329
+ }
330
+ if (params.where?.brand_id?.equals) {
331
+ whereConditions.brand_id = { equals: params.where.brand_id.equals };
332
+ }
333
+ if (params.where?.id?.equals) {
334
+ whereConditions.id = { equals: params.where.id.equals };
335
+ }
336
+ const variables = {
337
+ ...params,
338
+ where: Object.keys(whereConditions).length > 0 ? whereConditions : void 0
339
+ };
340
+ const { data, error, errorMessage } = await this.fetchGraphQL(
341
+ query,
342
+ variables,
343
+ (responseData) => responseData.GetEcommerceItems
344
+ );
345
+ if (error) {
240
346
  return {
241
347
  data: null,
242
348
  items: [],
@@ -248,6 +354,16 @@ var PakentoCMSAPI = class {
248
354
  errorMessage
249
355
  };
250
356
  }
357
+ return {
358
+ data,
359
+ items: data.docs,
360
+ totalDocs: data.totalDocs,
361
+ totalPages: data.totalPages,
362
+ prevPage: data.prevPage,
363
+ nextPage: data.nextPage,
364
+ error: false,
365
+ errorMessage: null
366
+ };
251
367
  }
252
368
  async getCategories(params = {}) {
253
369
  const {
@@ -255,8 +371,8 @@ var PakentoCMSAPI = class {
255
371
  cacheTTL,
256
372
  ...rest
257
373
  } = params;
258
- const cacheKey = this.buildCacheKey("GetEcommerceCategories", rest);
259
- return this.getCachedOrFetch(
374
+ const cacheKey = this.cache.buildCacheKey("GetEcommerceCategories", rest);
375
+ return this.cache.getCachedOrFetch(
260
376
  cacheKey,
261
377
  () => this.fetchCategoriesFromAPI(rest),
262
378
  cacheTTL,
@@ -272,64 +388,33 @@ var PakentoCMSAPI = class {
272
388
  errorMessage: "SDK no configurado: PAKENTO_CMS_BASE_URL o PAKENTO_API_KEY faltantes."
273
389
  };
274
390
  }
275
- try {
276
- const query = `
277
- query GetEcommerceCategories(
278
- $where: EcommerceCategoriesWhere
279
- $limit: Int
280
- $page: Int
281
- $sort: String
391
+ const query = `
392
+ query GetEcommerceCategories(
393
+ $where: EcommerceCategoriesWhere
394
+ $limit: Int
395
+ $page: Int
396
+ $sort: String
397
+ ) {
398
+ GetEcommerceCategories(
399
+ where: $where
400
+ limit: $limit
401
+ page: $page
402
+ sort: $sort
282
403
  ) {
283
- GetEcommerceCategories(
284
- where: $where
285
- limit: $limit
286
- page: $page
287
- sort: $sort
288
- ) {
289
- docs {
290
- id
291
- name
292
- image_url
293
- }
404
+ docs {
405
+ id
406
+ name
407
+ image_url
294
408
  }
295
409
  }
296
- `;
297
- const response = await this.client.post("/api/graphql", {
298
- query,
299
- variables: params
300
- });
301
- if (response.data.errors) {
302
- const errorMessage = `GraphQL Error: ${response.data.errors[0].message}`;
303
- return {
304
- data: null,
305
- categories: [],
306
- error: true,
307
- errorMessage
308
- };
309
- }
310
- const rawData = response.data.data.GetEcommerceCategories;
311
- return {
312
- data: rawData.docs,
313
- categories: rawData.docs,
314
- error: false,
315
- errorMessage: null
316
- };
317
- } catch (error) {
318
- let errorMessage = "Error desconocido";
319
- if (error instanceof AxiosError) {
320
- const status = error.response?.status;
321
- if (status === 401) {
322
- errorMessage = "API Key inv\xE1lida o expirada";
323
- } else if (status === 404) {
324
- errorMessage = "Endpoint no encontrado";
325
- } else if (status && status >= 500) {
326
- errorMessage = "Error del servidor CMS";
327
- } else {
328
- errorMessage = `Error de conexi\xF3n: ${error.message}`;
329
- }
330
- } else if (error instanceof Error) {
331
- errorMessage = error.message;
332
410
  }
411
+ `;
412
+ const { data, error, errorMessage } = await this.fetchGraphQL(
413
+ query,
414
+ params,
415
+ (responseData) => responseData.GetEcommerceCategories
416
+ );
417
+ if (error) {
333
418
  return {
334
419
  data: null,
335
420
  categories: [],
@@ -337,11 +422,17 @@ var PakentoCMSAPI = class {
337
422
  errorMessage
338
423
  };
339
424
  }
425
+ return {
426
+ data: data.docs,
427
+ categories: data.docs,
428
+ error: false,
429
+ errorMessage: null
430
+ };
340
431
  }
341
432
  async getBrands(params = {}) {
342
433
  const { skipCache = false, cacheTTL, ...rest } = params;
343
- const cacheKey = this.buildCacheKey("GetEcommerceBrands", rest);
344
- return this.getCachedOrFetch(
434
+ const cacheKey = this.cache.buildCacheKey("GetEcommerceBrands", rest);
435
+ return this.cache.getCachedOrFetch(
345
436
  cacheKey,
346
437
  () => this.fetchBrandsFromAPI(rest),
347
438
  cacheTTL,
@@ -367,96 +458,45 @@ var PakentoCMSAPI = class {
367
458
  errorMessage: "SDK no configurado: PAKENTO_CMS_BASE_URL o PAKENTO_API_KEY faltantes."
368
459
  };
369
460
  }
370
- try {
371
- const query = `
372
- query GetEcommerceBrands(
373
- $limit: Int
374
- $page: Int
375
- $sort: String
461
+ const query = `
462
+ query GetEcommerceBrands(
463
+ $limit: Int
464
+ $page: Int
465
+ $sort: String
466
+ ) {
467
+ GetEcommerceBrands(
468
+ limit: $limit
469
+ page: $page
470
+ sort: $sort
376
471
  ) {
377
- GetEcommerceBrands(
378
- limit: $limit
379
- page: $page
380
- sort: $sort
381
- ) {
382
- docs {
383
- id
384
- name
385
- description
386
- items_count
387
- image_url
388
- image_thumbnail_url
389
- image_alt
390
- }
391
- hasNextPage
392
- hasPrevPage
393
- limit
394
- nextPage
395
- offset
396
- page
397
- pagingCounter
398
- prevPage
399
- totalDocs
400
- totalPages
472
+ docs {
473
+ id
474
+ name
475
+ description
476
+ items_count
477
+ image_url
478
+ image_thumbnail_url
479
+ image_alt
401
480
  }
481
+ hasNextPage
482
+ hasPrevPage
483
+ limit
484
+ nextPage
485
+ offset
486
+ page
487
+ pagingCounter
488
+ prevPage
489
+ totalDocs
490
+ totalPages
402
491
  }
403
- `;
404
- const response = await this.client.post("/api/graphql", {
405
- query,
406
- variables: params
407
- });
408
- if (response.data.errors) {
409
- const errorMessage = `GraphQL Error: ${response.data.errors[0].message}`;
410
- return {
411
- data: null,
412
- brands: [],
413
- hasNextPage: false,
414
- hasPrevPage: false,
415
- limit: 0,
416
- nextPage: 0,
417
- offset: 0,
418
- page: 0,
419
- pagingCounter: 0,
420
- prevPage: 0,
421
- totalDocs: 0,
422
- totalPages: 0,
423
- error: true,
424
- errorMessage
425
- };
426
- }
427
- const rawData = response.data.data.GetEcommerceBrands;
428
- return {
429
- data: rawData,
430
- brands: rawData.docs,
431
- hasNextPage: rawData.hasNextPage,
432
- hasPrevPage: rawData.hasPrevPage,
433
- limit: rawData.limit,
434
- nextPage: rawData.nextPage,
435
- offset: rawData.offset,
436
- page: rawData.page,
437
- pagingCounter: rawData.pagingCounter,
438
- prevPage: rawData.prevPage,
439
- totalDocs: rawData.totalDocs,
440
- totalPages: rawData.totalPages,
441
- error: false,
442
- errorMessage: null
443
- };
444
- } catch (error) {
445
- let errorMessage = "Error desconocido";
446
- if (error instanceof AxiosError) {
447
- const status = error.response?.status;
448
- if (status === 401) {
449
- errorMessage = "API Key inv\xE1lida o expirada";
450
- } else if (status === 404) {
451
- errorMessage = "Endpoint no encontrado";
452
- } else if (status && status >= 500) {
453
- errorMessage = "Error del servidor CMS";
454
- } else {
455
- errorMessage = `Error de conexi\xF3n: ${error.message}`;
456
- }
457
- } else if (error instanceof Error) {
458
- errorMessage = error.message;
459
492
  }
493
+ `;
494
+ const { data, error, errorMessage } = await this.fetchGraphQL(
495
+ query,
496
+ params,
497
+ (responseData) => responseData.GetEcommerceBrands
498
+ );
499
+ if (error) {
460
500
  return {
461
501
  data: null,
462
502
  brands: [],
@@ -474,11 +514,27 @@ var PakentoCMSAPI = class {
474
514
  errorMessage
475
515
  };
476
516
  }
517
+ return {
518
+ data,
519
+ brands: data.docs,
520
+ hasNextPage: data.hasNextPage,
521
+ hasPrevPage: data.hasPrevPage,
522
+ limit: data.limit,
523
+ nextPage: data.nextPage,
524
+ offset: data.offset,
525
+ page: data.page,
526
+ pagingCounter: data.pagingCounter,
527
+ prevPage: data.prevPage,
528
+ totalDocs: data.totalDocs,
529
+ totalPages: data.totalPages,
530
+ error: false,
531
+ errorMessage: null
532
+ };
477
533
  }
478
534
  async getEntity(params = {}) {
479
535
  const { skipCache = false, cacheTTL, ...rest } = params;
480
- const cacheKey = this.buildCacheKey("GetEntity", rest);
481
- return this.getCachedOrFetch(
536
+ const cacheKey = this.cache.buildCacheKey("GetEntity", rest);
537
+ return this.cache.getCachedOrFetch(
482
538
  cacheKey,
483
539
  () => this.fetchEntityFromAPI(rest),
484
540
  cacheTTL,
@@ -494,81 +550,50 @@ var PakentoCMSAPI = class {
494
550
  errorMessage: "SDK no configurado: PAKENTO_CMS_BASE_URL o PAKENTO_API_KEY faltantes."
495
551
  };
496
552
  }
497
- try {
498
- const query = `
499
- query GetEntity {
500
- GetEntity {
501
- id
502
- tin
503
- name
504
- web
505
- address
506
- country
507
- city
508
- currency_id
509
- currency_name
510
- currency_prefix
511
- currency_suffix
512
- logo_url
513
- logo_alt
514
- logo_thumbnail_url
515
- logo_sizes_thumbnail_filename
516
- logo_filename
517
- logo_width
518
- logo_height
519
- logo_2_url
520
- logo_2_alt
521
- logo_2_thumbnail_url
522
- logo_2_sizes_thumbnail_filename
523
- logo_2_filename
524
- logo_2_width
525
- logo_2_height
526
- featured_image_url
527
- featured_image_alt
528
- featured_image_thumbnail_url
529
- featured_image_sizes_thumbnail_filename
530
- featured_image_filename
531
- featured_image_width
532
- featured_image_height
533
- }
534
- }
535
- `;
536
- const response = await this.client.post("/api/graphql", {
537
- query,
538
- variables: params
539
- });
540
- if (response.data.errors) {
541
- const errorMessage = `GraphQL Error: ${response.data.errors[0].message}`;
542
- return {
543
- data: null,
544
- entity: null,
545
- error: true,
546
- errorMessage
547
- };
548
- }
549
- const entityData = response.data.data.GetEntity;
550
- return {
551
- data: entityData,
552
- entity: entityData,
553
- error: false,
554
- errorMessage: null
555
- };
556
- } catch (error) {
557
- let errorMessage = "Error desconocido";
558
- if (error instanceof AxiosError) {
559
- const status = error.response?.status;
560
- if (status === 401) {
561
- errorMessage = "API Key inv\xE1lida o expirada";
562
- } else if (status === 404) {
563
- errorMessage = "Endpoint no encontrado";
564
- } else if (status && status >= 500) {
565
- errorMessage = "Error del servidor CMS";
566
- } else {
567
- errorMessage = `Error de conexi\xF3n: ${error.message}`;
553
+ const query = `
554
+ query GetEntity {
555
+ GetEntity {
556
+ id
557
+ tin
558
+ name
559
+ web
560
+ address
561
+ country
562
+ city
563
+ currency_id
564
+ currency_name
565
+ currency_prefix
566
+ currency_suffix
567
+ logo_url
568
+ logo_alt
569
+ logo_thumbnail_url
570
+ logo_sizes_thumbnail_filename
571
+ logo_filename
572
+ logo_width
573
+ logo_height
574
+ logo_2_url
575
+ logo_2_alt
576
+ logo_2_thumbnail_url
577
+ logo_2_sizes_thumbnail_filename
578
+ logo_2_filename
579
+ logo_2_width
580
+ logo_2_height
581
+ featured_image_url
582
+ featured_image_alt
583
+ featured_image_thumbnail_url
584
+ featured_image_sizes_thumbnail_filename
585
+ featured_image_filename
586
+ featured_image_width
587
+ featured_image_height
568
588
  }
569
- } else if (error instanceof Error) {
570
- errorMessage = error.message;
571
589
  }
590
+ `;
591
+ const { data, error, errorMessage } = await this.fetchGraphQL(
592
+ query,
593
+ params,
594
+ (responseData) => responseData.GetEntity
595
+ );
596
+ if (error) {
572
597
  return {
573
598
  data: null,
574
599
  entity: null,
@@ -576,49 +601,49 @@ var PakentoCMSAPI = class {
576
601
  errorMessage
577
602
  };
578
603
  }
604
+ return {
605
+ data,
606
+ entity: data,
607
+ error: false,
608
+ errorMessage: null
609
+ };
579
610
  }
580
611
  async createEcommerceOrder(params) {
581
612
  if (!this.baseURL || !this.apiKey) {
582
613
  return {
583
- message: "SDK no configurado: PAKENTO_CMS_BASE_URL o PAKENTO_API_KEY faltantes.",
584
- error: true,
585
- errorMessage: "SDK no configurado: PAKENTO_CMS_BASE_URL o PAKENTO_API_KEY faltantes."
586
- };
587
- }
588
- if (!params.name || !params.email) {
589
- return {
590
- message: "Faltan campos requeridos: name y email son obligatorios",
591
- error: true,
592
- errorMessage: "Faltan campos requeridos: name y email son obligatorios"
593
- };
594
- }
595
- if (!params.items || !Array.isArray(params.items) || params.items.length === 0) {
596
- return {
597
- message: "Se requiere al menos un item en la orden",
614
+ message: "SDK no configurado",
598
615
  error: true,
599
- errorMessage: "Se requiere al menos un item en la orden"
616
+ errorMessage: "SDK no configurado"
600
617
  };
601
618
  }
602
- if (params.payment_method && params.payment_method !== "cash" && params.payment_method !== "transfer") {
603
- return {
604
- message: "M\xE9todo de pago no v\xE1lido",
605
- error: true,
606
- errorMessage: "M\xE9todo de pago no v\xE1lido"
607
- };
608
- }
609
- for (const item of params.items) {
610
- if (!item.id || !item.quantity || item.quantity <= 0) {
611
- return {
612
- message: "Cada item debe tener un id v\xE1lido y una cantidad mayor a 0",
613
- error: true,
614
- errorMessage: "Cada item debe tener un id v\xE1lido y una cantidad mayor a 0"
615
- };
616
- }
619
+ const orderSchema = z.object({
620
+ name: z.string().min(1),
621
+ email: z.string().email(),
622
+ phone: z.string().optional(),
623
+ notes: z.string().optional(),
624
+ tin: z.string().optional(),
625
+ items: z.array(
626
+ z.object({
627
+ id: z.number().positive(),
628
+ quantity: z.number().positive()
629
+ })
630
+ ).min(1),
631
+ delivery_address: z.string().optional(),
632
+ delivery_instructions: z.string().optional(),
633
+ payment_method: z.enum(["cash", "transfer"]).optional()
634
+ });
635
+ const validated = orderSchema.safeParse(params);
636
+ if (!validated.success) {
637
+ const errorMessage = this.handleApiError(
638
+ validated.error,
639
+ "createEcommerceOrder validation"
640
+ );
641
+ return { message: errorMessage, error: true, errorMessage };
617
642
  }
618
643
  try {
619
644
  const response = await this.client.post(
620
645
  "/api/orders/create-ecommerce-order",
621
- params
646
+ validated.data
622
647
  );
623
648
  if (response.status === 200) {
624
649
  return {
@@ -628,36 +653,12 @@ var PakentoCMSAPI = class {
628
653
  errorMessage: null
629
654
  };
630
655
  } else {
631
- return {
632
- message: response.data.message || "Error al crear la orden",
633
- error: true,
634
- errorMessage: response.data.message || "Error al crear la orden"
635
- };
656
+ const errorMessage = response.data.message || "Error al crear la orden";
657
+ return { message: errorMessage, error: true, errorMessage };
636
658
  }
637
659
  } catch (error) {
638
- let errorMessage = "Error desconocido";
639
- if (error instanceof AxiosError) {
640
- const status = error.response?.status;
641
- const responseMessage = error.response?.data?.message;
642
- if (status === 400) {
643
- errorMessage = responseMessage || "Datos de entrada inv\xE1lidos";
644
- } else if (status === 401) {
645
- errorMessage = "API Key inv\xE1lida o expirada";
646
- } else if (status === 404) {
647
- errorMessage = "Endpoint no encontrado";
648
- } else if (status && status >= 500) {
649
- errorMessage = "Error del servidor CMS";
650
- } else {
651
- errorMessage = responseMessage || `Error de conexi\xF3n: ${error.message}`;
652
- }
653
- } else if (error instanceof Error) {
654
- errorMessage = error.message;
655
- }
656
- return {
657
- message: errorMessage,
658
- error: true,
659
- errorMessage
660
- };
660
+ const errorMessage = this.handleApiError(error, "createEcommerceOrder");
661
+ return { message: errorMessage, error: true, errorMessage };
661
662
  }
662
663
  }
663
664
  async executeCustomQuery(params) {
@@ -726,25 +727,24 @@ var PakentoCMSAPI = class {
726
727
  errorMessage: "SDK no configurado: PAKENTO_CMS_BASE_URL o PAKENTO_API_KEY faltantes."
727
728
  };
728
729
  }
729
- if (!params.name || !params.email) {
730
- return {
731
- message: "Faltan campos requeridos: name y email son obligatorios",
732
- error: true,
733
- errorMessage: "Faltan campos requeridos: name y email son obligatorios"
734
- };
735
- }
736
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
737
- if (!emailRegex.test(params.email)) {
738
- return {
739
- message: "Formato de email inv\xE1lido",
740
- error: true,
741
- errorMessage: "Formato de email inv\xE1lido"
742
- };
730
+ const contactUsSchema = z.object({
731
+ name: z.string().min(1),
732
+ email: z.string().email(),
733
+ subject: z.string().optional(),
734
+ message: z.string().min(1)
735
+ });
736
+ const validated = contactUsSchema.safeParse(params);
737
+ if (!validated.success) {
738
+ const errorMessage = this.handleApiError(
739
+ validated.error,
740
+ "sendContactUsEmail validation"
741
+ );
742
+ return { message: errorMessage, error: true, errorMessage };
743
743
  }
744
744
  try {
745
745
  const response = await this.client.post(
746
746
  "/api/entities/send-contact-us-email",
747
- params
747
+ validated.data
748
748
  );
749
749
  if (response.status === 200) {
750
750
  return {
@@ -753,38 +753,44 @@ var PakentoCMSAPI = class {
753
753
  errorMessage: null
754
754
  };
755
755
  } else {
756
- return {
757
- message: response.data.message || "Error al enviar el mensaje",
758
- error: true,
759
- errorMessage: response.data.message || "Error al enviar el mensaje"
760
- };
756
+ const errorMessage = response.data.message || "Error al enviar el mensaje";
757
+ return { message: errorMessage, error: true, errorMessage };
761
758
  }
762
759
  } catch (error) {
763
- let errorMessage = "Error desconocido";
764
- if (error instanceof AxiosError) {
765
- const status = error.response?.status;
766
- const responseMessage = error.response?.data?.message;
767
- if (status === 400) {
768
- errorMessage = responseMessage || "Datos de entrada inv\xE1lidos";
769
- } else if (status === 401) {
770
- errorMessage = "API Key inv\xE1lida o expirada";
771
- } else if (status === 404) {
772
- errorMessage = "Endpoint no encontrado";
773
- } else if (status && status >= 500) {
774
- errorMessage = "Error del servidor CMS";
775
- } else {
776
- errorMessage = responseMessage || `Error de conexi\xF3n: ${error.message}`;
777
- }
778
- } else if (error instanceof Error) {
779
- errorMessage = error.message;
780
- }
781
- return {
782
- message: errorMessage,
783
- error: true,
784
- errorMessage
785
- };
760
+ const errorMessage = this.handleApiError(error, "sendContactUsEmail");
761
+ return { message: errorMessage, error: true, errorMessage };
786
762
  }
787
763
  }
764
+ /**
765
+ * Verifica si existe cache para una función y parámetros específicos
766
+ */
767
+ async hasCache(functionName, params = {}) {
768
+ return this.cache.hasCache(functionName, params);
769
+ }
770
+ /**
771
+ * Obtiene la key de cache para una función y parámetros específicos
772
+ */
773
+ getCacheKey(functionName, params = {}) {
774
+ return this.cache.getCacheKey(functionName, params);
775
+ }
776
+ /**
777
+ * Limpia el cache para una función y parámetros específicos
778
+ */
779
+ async clearCache(functionName, params = {}) {
780
+ return this.cache.clearCache(functionName, params);
781
+ }
782
+ /**
783
+ * Limpia todo el cache relacionado con este API Key
784
+ */
785
+ async clearAllCache() {
786
+ return this.cache.clearAllCache();
787
+ }
788
+ /**
789
+ * Obtiene información del cache (útil para debugging)
790
+ */
791
+ async getCacheInfo(functionName, params = {}) {
792
+ return this.cache.getCacheInfo(functionName, params);
793
+ }
788
794
  };
789
795
  var pakentoCMSAPI = new PakentoCMSAPI();
790
796
  export {