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