@pakento/cms-sdk 4.0.5 → 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,47 +1,20 @@
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
6
  import { createHash } from "crypto";
5
- var PakentoCMSAPI = class {
6
- constructor(config) {
7
- this.defaultTTL = 86400;
8
- this.baseURL = process.env.PAKENTO_CMS_BASE_URL || "";
9
- this.apiKey = process.env.PAKENTO_API_KEY || "";
10
- if (config?.cacheTTL && typeof config.cacheTTL === "number") {
11
- this.defaultTTL = config.cacheTTL;
12
- }
7
+ var CacheService = class {
8
+ constructor(defaultTTL, apiKey) {
9
+ this.defaultTTL = defaultTTL;
10
+ this.apiKeyHash = createHash("sha256").update(apiKey).digest("hex").substring(0, 8);
13
11
  if (process.env.UPSTASH_REDIS_REST_URL && process.env.UPSTASH_REDIS_REST_TOKEN) {
14
12
  this.redis = new Redis({
15
13
  url: process.env.UPSTASH_REDIS_REST_URL,
16
14
  token: process.env.UPSTASH_REDIS_REST_TOKEN
17
15
  });
18
16
  }
19
- if (!this.baseURL || !this.apiKey) {
20
- console.warn(
21
- "[PakentoSDK] Variables de entorno PAKENTO_CMS_BASE_URL o PAKENTO_API_KEY faltantes. Se validar\xE1n al hacer la petici\xF3n."
22
- );
23
- }
24
- this.client = axios.create({
25
- baseURL: this.baseURL,
26
- headers: {
27
- "Content-Type": "application/json",
28
- Authorization: `users API-Key ${this.apiKey}`
29
- }
30
- });
31
- this.client.interceptors.request.use(
32
- (config2) => {
33
- const fullUrl = `${config2.baseURL}${config2.url}`;
34
- console.log(`\u{1F310} API Call: ${config2.method?.toUpperCase()} ${fullUrl}`);
35
- return config2;
36
- },
37
- (error) => {
38
- return Promise.reject(error);
39
- }
40
- );
41
17
  }
42
- /**
43
- * Genera un hash único para los parámetros de búsqueda
44
- */
45
18
  generateParamsHash(params) {
46
19
  const sortedParams = Object.keys(params).sort().reduce((acc, key) => {
47
20
  acc[key] = params[key];
@@ -50,20 +23,10 @@ var PakentoCMSAPI = class {
50
23
  const paramsString = JSON.stringify(sortedParams);
51
24
  return createHash("sha256").update(paramsString).digest("hex").substring(0, 16);
52
25
  }
53
- /**
54
- * Genera una key única y calculable para el cache basada en:
55
- * - API Token (para namespacing entre proyectos)
56
- * - Nombre de la función
57
- * - Hash de los parámetros de búsqueda
58
- */
59
26
  buildCacheKey(functionName, params = {}) {
60
27
  const paramsHash = this.generateParamsHash(params);
61
- const apiKeyHash = createHash("sha256").update(this.apiKey).digest("hex").substring(0, 8);
62
- return `pakento:${apiKeyHash}:${functionName}:${paramsHash}`;
28
+ return `pakento:${this.apiKeyHash}:${functionName}:${paramsHash}`;
63
29
  }
64
- /**
65
- * Obtiene datos desde cache si existen; de lo contrario llama a fetcher() y guarda.
66
- */
67
30
  safeJsonParse(data) {
68
31
  try {
69
32
  return JSON.parse(data);
@@ -108,12 +71,183 @@ var PakentoCMSAPI = class {
108
71
  }
109
72
  return result;
110
73
  }
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
+ }
209
+ }
111
210
  async getItems(params = {}) {
112
- const { skipCache = false, cacheTTL, ...rest } = params;
113
- const cacheKey = this.buildCacheKey("GetEcommerceItems", rest);
114
- 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(
115
249
  cacheKey,
116
- () => this.fetchItemsFromAPI(params),
250
+ () => this.fetchItemsFromAPI(rest),
117
251
  cacheTTL,
118
252
  skipCache
119
253
  );
@@ -131,136 +265,84 @@ var PakentoCMSAPI = class {
131
265
  errorMessage: "SDK no configurado: PAKENTO_CMS_BASE_URL o PAKENTO_API_KEY faltantes."
132
266
  };
133
267
  }
134
- try {
135
- const whereConditions = {};
136
- if (params.where?.item_category_id?.equals) {
137
- whereConditions.item_category_id = {
138
- equals: params.where.item_category_id.equals
139
- };
140
- }
141
- if (params.where?.item_super_category_id?.equals) {
142
- whereConditions.item_super_category_id = {
143
- equals: params.where.item_super_category_id.equals
144
- };
145
- }
146
- if (params.where?.brand_id?.equals) {
147
- whereConditions.brand_id = {
148
- equals: params.where.brand_id.equals
149
- };
150
- }
151
- if (params.where?.id?.equals) {
152
- whereConditions.id = {
153
- equals: params.where.id.equals
154
- };
155
- }
156
- const variables = {
157
- onlyOffers: params.onlyOffers,
158
- onlyEcommerce: params.onlyEcommerce,
159
- limit: params.limit,
160
- page: params.page,
161
- search: params.search,
162
- sort: params.sort,
163
- minPrice: params.minPrice,
164
- maxPrice: params.maxPrice
165
- };
166
- if (Object.keys(whereConditions).length > 0) {
167
- variables.where = whereConditions;
168
- }
169
- const query = `
170
- query GetEcommerceItems(
171
- $where: EcommerceItemsWhere
172
- $onlyOffers: Boolean
173
- $onlyEcommerce: Boolean
174
- $limit: Int
175
- $page: Int
176
- $search: String
177
- $sort: String
178
- $minPrice: Float
179
- $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
180
290
  ) {
181
- GetEcommerceItems(
182
- where: $where
183
- onlyOffers: $onlyOffers
184
- onlyEcommerce: $onlyEcommerce
185
- limit: $limit
186
- page: $page
187
- search: $search
188
- sort: $sort
189
- minPrice: $minPrice
190
- maxPrice: $maxPrice
191
- ) {
192
- totalDocs
193
- totalPages
194
- prevPage
195
- nextPage
196
- docs {
197
- id
198
- name
199
- featured
200
- old_price
201
- currency_prefix
202
- description
203
- price_text
204
- price
205
- brand_id
206
- brand_name
207
- cover_image_url
208
- cover_image_thumbnail_url
209
- item_category_name
210
- item_category_id
211
- images {
212
- alt
213
- url
214
- thumbnail_url
215
- }
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
216
314
  }
217
315
  }
218
316
  }
219
- `;
220
- const response = await this.client.post("/api/graphql", {
221
- query,
222
- variables
223
- });
224
- if (response.data.errors) {
225
- const errorMessage = `GraphQL Error: ${response.data.errors[0].message}`;
226
- return {
227
- data: null,
228
- items: [],
229
- totalDocs: 0,
230
- totalPages: 0,
231
- prevPage: null,
232
- nextPage: null,
233
- error: true,
234
- errorMessage
235
- };
236
317
  }
237
- const rawData = response.data.data.GetEcommerceItems;
238
- return {
239
- data: rawData,
240
- items: rawData.docs,
241
- totalDocs: rawData.totalDocs,
242
- totalPages: rawData.totalPages,
243
- prevPage: rawData.prevPage,
244
- nextPage: rawData.nextPage,
245
- error: false,
246
- 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
247
323
  };
248
- } catch (error) {
249
- let errorMessage = "Error desconocido";
250
- if (error instanceof AxiosError) {
251
- const status = error.response?.status;
252
- if (status === 401) {
253
- errorMessage = "API Key inv\xE1lida o expirada";
254
- } else if (status === 404) {
255
- errorMessage = "Endpoint no encontrado";
256
- } else if (status && status >= 500) {
257
- errorMessage = "Error del servidor CMS";
258
- } else {
259
- errorMessage = `Error de conexi\xF3n: ${error.message}`;
260
- }
261
- } else if (error instanceof Error) {
262
- errorMessage = error.message;
263
- }
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) {
264
346
  return {
265
347
  data: null,
266
348
  items: [],
@@ -272,6 +354,16 @@ var PakentoCMSAPI = class {
272
354
  errorMessage
273
355
  };
274
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
+ };
275
367
  }
276
368
  async getCategories(params = {}) {
277
369
  const {
@@ -279,8 +371,8 @@ var PakentoCMSAPI = class {
279
371
  cacheTTL,
280
372
  ...rest
281
373
  } = params;
282
- const cacheKey = this.buildCacheKey("GetEcommerceCategories", rest);
283
- return this.getCachedOrFetch(
374
+ const cacheKey = this.cache.buildCacheKey("GetEcommerceCategories", rest);
375
+ return this.cache.getCachedOrFetch(
284
376
  cacheKey,
285
377
  () => this.fetchCategoriesFromAPI(rest),
286
378
  cacheTTL,
@@ -296,64 +388,33 @@ var PakentoCMSAPI = class {
296
388
  errorMessage: "SDK no configurado: PAKENTO_CMS_BASE_URL o PAKENTO_API_KEY faltantes."
297
389
  };
298
390
  }
299
- try {
300
- const query = `
301
- query GetEcommerceCategories(
302
- $where: EcommerceCategoriesWhere
303
- $limit: Int
304
- $page: Int
305
- $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
306
403
  ) {
307
- GetEcommerceCategories(
308
- where: $where
309
- limit: $limit
310
- page: $page
311
- sort: $sort
312
- ) {
313
- docs {
314
- id
315
- name
316
- image_url
317
- }
404
+ docs {
405
+ id
406
+ name
407
+ image_url
318
408
  }
319
409
  }
320
- `;
321
- const response = await this.client.post("/api/graphql", {
322
- query,
323
- variables: params
324
- });
325
- if (response.data.errors) {
326
- const errorMessage = `GraphQL Error: ${response.data.errors[0].message}`;
327
- return {
328
- data: null,
329
- categories: [],
330
- error: true,
331
- errorMessage
332
- };
333
- }
334
- const rawData = response.data.data.GetEcommerceCategories;
335
- return {
336
- data: rawData.docs,
337
- categories: rawData.docs,
338
- error: false,
339
- errorMessage: null
340
- };
341
- } catch (error) {
342
- let errorMessage = "Error desconocido";
343
- if (error instanceof AxiosError) {
344
- const status = error.response?.status;
345
- if (status === 401) {
346
- errorMessage = "API Key inv\xE1lida o expirada";
347
- } else if (status === 404) {
348
- errorMessage = "Endpoint no encontrado";
349
- } else if (status && status >= 500) {
350
- errorMessage = "Error del servidor CMS";
351
- } else {
352
- errorMessage = `Error de conexi\xF3n: ${error.message}`;
353
- }
354
- } else if (error instanceof Error) {
355
- errorMessage = error.message;
356
410
  }
411
+ `;
412
+ const { data, error, errorMessage } = await this.fetchGraphQL(
413
+ query,
414
+ params,
415
+ (responseData) => responseData.GetEcommerceCategories
416
+ );
417
+ if (error) {
357
418
  return {
358
419
  data: null,
359
420
  categories: [],
@@ -361,11 +422,17 @@ var PakentoCMSAPI = class {
361
422
  errorMessage
362
423
  };
363
424
  }
425
+ return {
426
+ data: data.docs,
427
+ categories: data.docs,
428
+ error: false,
429
+ errorMessage: null
430
+ };
364
431
  }
365
432
  async getBrands(params = {}) {
366
433
  const { skipCache = false, cacheTTL, ...rest } = params;
367
- const cacheKey = this.buildCacheKey("GetEcommerceBrands", rest);
368
- return this.getCachedOrFetch(
434
+ const cacheKey = this.cache.buildCacheKey("GetEcommerceBrands", rest);
435
+ return this.cache.getCachedOrFetch(
369
436
  cacheKey,
370
437
  () => this.fetchBrandsFromAPI(rest),
371
438
  cacheTTL,
@@ -391,96 +458,45 @@ var PakentoCMSAPI = class {
391
458
  errorMessage: "SDK no configurado: PAKENTO_CMS_BASE_URL o PAKENTO_API_KEY faltantes."
392
459
  };
393
460
  }
394
- try {
395
- const query = `
396
- query GetEcommerceBrands(
397
- $limit: Int
398
- $page: Int
399
- $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
400
471
  ) {
401
- GetEcommerceBrands(
402
- limit: $limit
403
- page: $page
404
- sort: $sort
405
- ) {
406
- docs {
407
- id
408
- name
409
- description
410
- items_count
411
- image_url
412
- image_thumbnail_url
413
- image_alt
414
- }
415
- hasNextPage
416
- hasPrevPage
417
- limit
418
- nextPage
419
- offset
420
- page
421
- pagingCounter
422
- prevPage
423
- totalDocs
424
- totalPages
472
+ docs {
473
+ id
474
+ name
475
+ description
476
+ items_count
477
+ image_url
478
+ image_thumbnail_url
479
+ image_alt
425
480
  }
481
+ hasNextPage
482
+ hasPrevPage
483
+ limit
484
+ nextPage
485
+ offset
486
+ page
487
+ pagingCounter
488
+ prevPage
489
+ totalDocs
490
+ totalPages
426
491
  }
427
- `;
428
- const response = await this.client.post("/api/graphql", {
429
- query,
430
- variables: params
431
- });
432
- if (response.data.errors) {
433
- const errorMessage = `GraphQL Error: ${response.data.errors[0].message}`;
434
- return {
435
- data: null,
436
- brands: [],
437
- hasNextPage: false,
438
- hasPrevPage: false,
439
- limit: 0,
440
- nextPage: 0,
441
- offset: 0,
442
- page: 0,
443
- pagingCounter: 0,
444
- prevPage: 0,
445
- totalDocs: 0,
446
- totalPages: 0,
447
- error: true,
448
- errorMessage
449
- };
450
- }
451
- const rawData = response.data.data.GetEcommerceBrands;
452
- return {
453
- data: rawData,
454
- brands: rawData.docs,
455
- hasNextPage: rawData.hasNextPage,
456
- hasPrevPage: rawData.hasPrevPage,
457
- limit: rawData.limit,
458
- nextPage: rawData.nextPage,
459
- offset: rawData.offset,
460
- page: rawData.page,
461
- pagingCounter: rawData.pagingCounter,
462
- prevPage: rawData.prevPage,
463
- totalDocs: rawData.totalDocs,
464
- totalPages: rawData.totalPages,
465
- error: false,
466
- errorMessage: null
467
- };
468
- } catch (error) {
469
- let errorMessage = "Error desconocido";
470
- if (error instanceof AxiosError) {
471
- const status = error.response?.status;
472
- if (status === 401) {
473
- errorMessage = "API Key inv\xE1lida o expirada";
474
- } else if (status === 404) {
475
- errorMessage = "Endpoint no encontrado";
476
- } else if (status && status >= 500) {
477
- errorMessage = "Error del servidor CMS";
478
- } else {
479
- errorMessage = `Error de conexi\xF3n: ${error.message}`;
480
- }
481
- } else if (error instanceof Error) {
482
- errorMessage = error.message;
483
492
  }
493
+ `;
494
+ const { data, error, errorMessage } = await this.fetchGraphQL(
495
+ query,
496
+ params,
497
+ (responseData) => responseData.GetEcommerceBrands
498
+ );
499
+ if (error) {
484
500
  return {
485
501
  data: null,
486
502
  brands: [],
@@ -498,11 +514,27 @@ var PakentoCMSAPI = class {
498
514
  errorMessage
499
515
  };
500
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
+ };
501
533
  }
502
534
  async getEntity(params = {}) {
503
535
  const { skipCache = false, cacheTTL, ...rest } = params;
504
- const cacheKey = this.buildCacheKey("GetEntity", rest);
505
- return this.getCachedOrFetch(
536
+ const cacheKey = this.cache.buildCacheKey("GetEntity", rest);
537
+ return this.cache.getCachedOrFetch(
506
538
  cacheKey,
507
539
  () => this.fetchEntityFromAPI(rest),
508
540
  cacheTTL,
@@ -518,81 +550,50 @@ var PakentoCMSAPI = class {
518
550
  errorMessage: "SDK no configurado: PAKENTO_CMS_BASE_URL o PAKENTO_API_KEY faltantes."
519
551
  };
520
552
  }
521
- try {
522
- const query = `
523
- query GetEntity {
524
- GetEntity {
525
- id
526
- tin
527
- name
528
- web
529
- address
530
- country
531
- city
532
- currency_id
533
- currency_name
534
- currency_prefix
535
- currency_suffix
536
- logo_url
537
- logo_alt
538
- logo_thumbnail_url
539
- logo_sizes_thumbnail_filename
540
- logo_filename
541
- logo_width
542
- logo_height
543
- logo_2_url
544
- logo_2_alt
545
- logo_2_thumbnail_url
546
- logo_2_sizes_thumbnail_filename
547
- logo_2_filename
548
- logo_2_width
549
- logo_2_height
550
- featured_image_url
551
- featured_image_alt
552
- featured_image_thumbnail_url
553
- featured_image_sizes_thumbnail_filename
554
- featured_image_filename
555
- featured_image_width
556
- featured_image_height
557
- }
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
558
588
  }
559
- `;
560
- const response = await this.client.post("/api/graphql", {
561
- query,
562
- variables: params
563
- });
564
- if (response.data.errors) {
565
- const errorMessage = `GraphQL Error: ${response.data.errors[0].message}`;
566
- return {
567
- data: null,
568
- entity: null,
569
- error: true,
570
- errorMessage
571
- };
572
- }
573
- const entityData = response.data.data.GetEntity;
574
- return {
575
- data: entityData,
576
- entity: entityData,
577
- error: false,
578
- errorMessage: null
579
- };
580
- } catch (error) {
581
- let errorMessage = "Error desconocido";
582
- if (error instanceof AxiosError) {
583
- const status = error.response?.status;
584
- if (status === 401) {
585
- errorMessage = "API Key inv\xE1lida o expirada";
586
- } else if (status === 404) {
587
- errorMessage = "Endpoint no encontrado";
588
- } else if (status && status >= 500) {
589
- errorMessage = "Error del servidor CMS";
590
- } else {
591
- errorMessage = `Error de conexi\xF3n: ${error.message}`;
592
- }
593
- } else if (error instanceof Error) {
594
- errorMessage = error.message;
595
589
  }
590
+ `;
591
+ const { data, error, errorMessage } = await this.fetchGraphQL(
592
+ query,
593
+ params,
594
+ (responseData) => responseData.GetEntity
595
+ );
596
+ if (error) {
596
597
  return {
597
598
  data: null,
598
599
  entity: null,
@@ -600,49 +601,49 @@ var PakentoCMSAPI = class {
600
601
  errorMessage
601
602
  };
602
603
  }
604
+ return {
605
+ data,
606
+ entity: data,
607
+ error: false,
608
+ errorMessage: null
609
+ };
603
610
  }
604
611
  async createEcommerceOrder(params) {
605
612
  if (!this.baseURL || !this.apiKey) {
606
613
  return {
607
- message: "SDK no configurado: PAKENTO_CMS_BASE_URL o PAKENTO_API_KEY faltantes.",
608
- error: true,
609
- errorMessage: "SDK no configurado: PAKENTO_CMS_BASE_URL o PAKENTO_API_KEY faltantes."
610
- };
611
- }
612
- if (!params.name || !params.email) {
613
- return {
614
- message: "Faltan campos requeridos: name y email son obligatorios",
615
- error: true,
616
- errorMessage: "Faltan campos requeridos: name y email son obligatorios"
617
- };
618
- }
619
- if (!params.items || !Array.isArray(params.items) || params.items.length === 0) {
620
- return {
621
- message: "Se requiere al menos un item en la orden",
622
- error: true,
623
- errorMessage: "Se requiere al menos un item en la orden"
624
- };
625
- }
626
- if (params.payment_method && params.payment_method !== "cash" && params.payment_method !== "transfer") {
627
- return {
628
- message: "M\xE9todo de pago no v\xE1lido",
614
+ message: "SDK no configurado",
629
615
  error: true,
630
- errorMessage: "M\xE9todo de pago no v\xE1lido"
616
+ errorMessage: "SDK no configurado"
631
617
  };
632
618
  }
633
- for (const item of params.items) {
634
- if (!item.id || !item.quantity || item.quantity <= 0) {
635
- return {
636
- message: "Cada item debe tener un id v\xE1lido y una cantidad mayor a 0",
637
- error: true,
638
- errorMessage: "Cada item debe tener un id v\xE1lido y una cantidad mayor a 0"
639
- };
640
- }
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 };
641
642
  }
642
643
  try {
643
644
  const response = await this.client.post(
644
645
  "/api/orders/create-ecommerce-order",
645
- params
646
+ validated.data
646
647
  );
647
648
  if (response.status === 200) {
648
649
  return {
@@ -652,36 +653,12 @@ var PakentoCMSAPI = class {
652
653
  errorMessage: null
653
654
  };
654
655
  } else {
655
- return {
656
- message: response.data.message || "Error al crear la orden",
657
- error: true,
658
- errorMessage: response.data.message || "Error al crear la orden"
659
- };
656
+ const errorMessage = response.data.message || "Error al crear la orden";
657
+ return { message: errorMessage, error: true, errorMessage };
660
658
  }
661
659
  } catch (error) {
662
- let errorMessage = "Error desconocido";
663
- if (error instanceof AxiosError) {
664
- const status = error.response?.status;
665
- const responseMessage = error.response?.data?.message;
666
- if (status === 400) {
667
- errorMessage = responseMessage || "Datos de entrada inv\xE1lidos";
668
- } else if (status === 401) {
669
- errorMessage = "API Key inv\xE1lida o expirada";
670
- } else if (status === 404) {
671
- errorMessage = "Endpoint no encontrado";
672
- } else if (status && status >= 500) {
673
- errorMessage = "Error del servidor CMS";
674
- } else {
675
- errorMessage = responseMessage || `Error de conexi\xF3n: ${error.message}`;
676
- }
677
- } else if (error instanceof Error) {
678
- errorMessage = error.message;
679
- }
680
- return {
681
- message: errorMessage,
682
- error: true,
683
- errorMessage
684
- };
660
+ const errorMessage = this.handleApiError(error, "createEcommerceOrder");
661
+ return { message: errorMessage, error: true, errorMessage };
685
662
  }
686
663
  }
687
664
  async executeCustomQuery(params) {
@@ -750,25 +727,24 @@ var PakentoCMSAPI = class {
750
727
  errorMessage: "SDK no configurado: PAKENTO_CMS_BASE_URL o PAKENTO_API_KEY faltantes."
751
728
  };
752
729
  }
753
- if (!params.name || !params.email) {
754
- return {
755
- message: "Faltan campos requeridos: name y email son obligatorios",
756
- error: true,
757
- errorMessage: "Faltan campos requeridos: name y email son obligatorios"
758
- };
759
- }
760
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
761
- if (!emailRegex.test(params.email)) {
762
- return {
763
- message: "Formato de email inv\xE1lido",
764
- error: true,
765
- errorMessage: "Formato de email inv\xE1lido"
766
- };
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 };
767
743
  }
768
744
  try {
769
745
  const response = await this.client.post(
770
746
  "/api/entities/send-contact-us-email",
771
- params
747
+ validated.data
772
748
  );
773
749
  if (response.status === 200) {
774
750
  return {
@@ -777,124 +753,43 @@ var PakentoCMSAPI = class {
777
753
  errorMessage: null
778
754
  };
779
755
  } else {
780
- return {
781
- message: response.data.message || "Error al enviar el mensaje",
782
- error: true,
783
- errorMessage: response.data.message || "Error al enviar el mensaje"
784
- };
756
+ const errorMessage = response.data.message || "Error al enviar el mensaje";
757
+ return { message: errorMessage, error: true, errorMessage };
785
758
  }
786
759
  } catch (error) {
787
- let errorMessage = "Error desconocido";
788
- if (error instanceof AxiosError) {
789
- const status = error.response?.status;
790
- const responseMessage = error.response?.data?.message;
791
- if (status === 400) {
792
- errorMessage = responseMessage || "Datos de entrada inv\xE1lidos";
793
- } else if (status === 401) {
794
- errorMessage = "API Key inv\xE1lida o expirada";
795
- } else if (status === 404) {
796
- errorMessage = "Endpoint no encontrado";
797
- } else if (status && status >= 500) {
798
- errorMessage = "Error del servidor CMS";
799
- } else {
800
- errorMessage = responseMessage || `Error de conexi\xF3n: ${error.message}`;
801
- }
802
- } else if (error instanceof Error) {
803
- errorMessage = error.message;
804
- }
805
- return {
806
- message: errorMessage,
807
- error: true,
808
- errorMessage
809
- };
760
+ const errorMessage = this.handleApiError(error, "sendContactUsEmail");
761
+ return { message: errorMessage, error: true, errorMessage };
810
762
  }
811
763
  }
812
764
  /**
813
765
  * Verifica si existe cache para una función y parámetros específicos
814
766
  */
815
767
  async hasCache(functionName, params = {}) {
816
- if (!this.redis) {
817
- return false;
818
- }
819
- try {
820
- const key = this.buildCacheKey(functionName, params);
821
- const cached = await this.redis.get(key);
822
- return cached !== null;
823
- } catch (err) {
824
- console.warn("[PakentoSDK] Error verificando cache", err);
825
- return false;
826
- }
768
+ return this.cache.hasCache(functionName, params);
827
769
  }
828
770
  /**
829
771
  * Obtiene la key de cache para una función y parámetros específicos
830
772
  */
831
773
  getCacheKey(functionName, params = {}) {
832
- return this.buildCacheKey(functionName, params);
774
+ return this.cache.getCacheKey(functionName, params);
833
775
  }
834
776
  /**
835
777
  * Limpia el cache para una función y parámetros específicos
836
778
  */
837
779
  async clearCache(functionName, params = {}) {
838
- if (!this.redis) {
839
- return false;
840
- }
841
- try {
842
- const key = this.buildCacheKey(functionName, params);
843
- await this.redis.del(key);
844
- console.log(`[PakentoSDK] \u2705 Cache limpiado para: ${key}`);
845
- return true;
846
- } catch (err) {
847
- console.warn("[PakentoSDK] Error limpiando cache", err);
848
- return false;
849
- }
780
+ return this.cache.clearCache(functionName, params);
850
781
  }
851
782
  /**
852
783
  * Limpia todo el cache relacionado con este API Key
853
784
  */
854
785
  async clearAllCache() {
855
- if (!this.redis) {
856
- return false;
857
- }
858
- try {
859
- const apiKeyHash = createHash("sha256").update(this.apiKey).digest("hex").substring(0, 8);
860
- const pattern = `pakento:${apiKeyHash}:*`;
861
- const keys = await this.redis.keys(pattern);
862
- if (keys.length > 0) {
863
- await this.redis.del(...keys);
864
- console.log(`[PakentoSDK] \u2705 Cache limpiado para ${keys.length} keys`);
865
- }
866
- return true;
867
- } catch (err) {
868
- console.warn("[PakentoSDK] Error limpiando todo el cache", err);
869
- return false;
870
- }
786
+ return this.cache.clearAllCache();
871
787
  }
872
788
  /**
873
789
  * Obtiene información del cache (útil para debugging)
874
790
  */
875
791
  async getCacheInfo(functionName, params = {}) {
876
- if (!this.redis) {
877
- return {
878
- exists: false,
879
- key: this.buildCacheKey(functionName, params)
880
- };
881
- }
882
- try {
883
- const key = this.buildCacheKey(functionName, params);
884
- const exists = await this.redis.exists(key);
885
- const ttl = exists ? await this.redis.ttl(key) : void 0;
886
- return {
887
- exists: exists === 1,
888
- key,
889
- ttl: ttl === -1 ? void 0 : ttl
890
- };
891
- } catch (err) {
892
- console.warn("[PakentoSDK] Error obteniendo info del cache", err);
893
- return {
894
- exists: false,
895
- key: this.buildCacheKey(functionName, params)
896
- };
897
- }
792
+ return this.cache.getCacheInfo(functionName, params);
898
793
  }
899
794
  };
900
795
  var pakentoCMSAPI = new PakentoCMSAPI();