@pakento/cms-sdk 4.0.5 → 4.0.7

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);
@@ -108,11 +71,13 @@ var PakentoCMSAPI = class {
108
71
  }
109
72
  }
110
73
  serializeForCache(value) {
111
- return typeof value === "string" ? value : JSON.stringify(value);
74
+ return JSON.stringify(value);
112
75
  }
113
76
  async getCachedOrFetch(key, fetcher, ttl, skipCache = false) {
114
77
  if (!this.redis || skipCache) {
115
- console.log(`[PakentoSDK] ${!this.redis ? "Redis no configurado" : "Saltando cache"} - llamando a API`);
78
+ console.log(
79
+ `[PakentoSDK] ${!this.redis ? "Redis no configurado" : "Saltando cache"} - llamando a API`
80
+ );
116
81
  return fetcher();
117
82
  }
118
83
  try {
@@ -138,18 +103,191 @@ var PakentoCMSAPI = class {
138
103
  await this.redis.set(key, this.serializeForCache(result), {
139
104
  ex: ttl ?? this.defaultTTL
140
105
  });
141
- console.log(`[PakentoSDK] \u2705 Guardado en cache: ${key} (TTL: ${ttl ?? this.defaultTTL}s)`);
106
+ console.log(
107
+ `[PakentoSDK] \u2705 Guardado en cache: ${key} (TTL: ${ttl ?? this.defaultTTL}s)`
108
+ );
142
109
  } catch (err) {
143
110
  console.warn("[PakentoSDK] Error escribiendo cache Redis", err);
144
111
  }
145
112
  return result;
146
113
  }
114
+ async hasCache(functionName, params = {}) {
115
+ if (!this.redis) {
116
+ return false;
117
+ }
118
+ try {
119
+ const key = this.buildCacheKey(functionName, params);
120
+ const cached = await this.redis.get(key);
121
+ return cached !== null;
122
+ } catch (err) {
123
+ console.warn("[PakentoSDK] Error verificando cache", err);
124
+ return false;
125
+ }
126
+ }
127
+ getCacheKey(functionName, params = {}) {
128
+ return this.buildCacheKey(functionName, params);
129
+ }
130
+ async clearCache(functionName, params = {}) {
131
+ if (!this.redis) {
132
+ return false;
133
+ }
134
+ try {
135
+ const key = this.buildCacheKey(functionName, params);
136
+ await this.redis.del(key);
137
+ console.log(`[PakentoSDK] \u2705 Cache limpiado para: ${key}`);
138
+ return true;
139
+ } catch (err) {
140
+ console.warn("[PakentoSDK] Error limpiando cache", err);
141
+ return false;
142
+ }
143
+ }
144
+ async clearAllCache() {
145
+ if (!this.redis) {
146
+ return false;
147
+ }
148
+ try {
149
+ const pattern = `pakento:${this.apiKeyHash}:*`;
150
+ const keys = await this.redis.keys(pattern);
151
+ if (keys.length > 0) {
152
+ await this.redis.del(...keys);
153
+ console.log(`[PakentoSDK] \u2705 Cache limpiado para ${keys.length} keys`);
154
+ }
155
+ return true;
156
+ } catch (err) {
157
+ console.warn("[PakentoSDK] Error limpiando todo el cache", err);
158
+ return false;
159
+ }
160
+ }
161
+ async getCacheInfo(functionName, params = {}) {
162
+ if (!this.redis) {
163
+ return {
164
+ exists: false,
165
+ key: this.buildCacheKey(functionName, params)
166
+ };
167
+ }
168
+ try {
169
+ const key = this.buildCacheKey(functionName, params);
170
+ const exists = await this.redis.exists(key);
171
+ const ttl = exists ? await this.redis.ttl(key) : void 0;
172
+ return {
173
+ exists: exists === 1,
174
+ key,
175
+ ttl: ttl === -1 ? void 0 : ttl
176
+ };
177
+ } catch (err) {
178
+ console.warn("[PakentoSDK] Error obteniendo info del cache", err);
179
+ return {
180
+ exists: false,
181
+ key: this.buildCacheKey(functionName, params)
182
+ };
183
+ }
184
+ }
185
+ };
186
+
187
+ // src/services/api.ts
188
+ var import_zod = require("zod");
189
+ var PakentoCMSAPI = class {
190
+ constructor(config) {
191
+ this.defaultTTL = 86400;
192
+ this.baseURL = process.env.PAKENTO_CMS_BASE_URL || "";
193
+ this.apiKey = process.env.PAKENTO_API_KEY || "";
194
+ if (config?.cacheTTL && typeof config.cacheTTL === "number") {
195
+ this.defaultTTL = config.cacheTTL;
196
+ }
197
+ this.cache = new CacheService(this.defaultTTL, this.apiKey);
198
+ this.client = import_axios.default.create({
199
+ baseURL: this.baseURL,
200
+ headers: {
201
+ "Content-Type": "application/json",
202
+ Authorization: `users API-Key ${this.apiKey}`
203
+ }
204
+ });
205
+ this.client.interceptors.request.use(
206
+ (config2) => {
207
+ const fullUrl = `${config2.baseURL}${config2.url}`;
208
+ console.log(`\u{1F310} API Call: ${config2.method?.toUpperCase()} ${fullUrl}`);
209
+ return config2;
210
+ },
211
+ (error) => {
212
+ return Promise.reject(error);
213
+ }
214
+ );
215
+ }
216
+ // Centralized error handler
217
+ handleApiError(error, context) {
218
+ if (error instanceof import_axios.AxiosError) {
219
+ const status = error.response?.status;
220
+ const responseMessage = error.response?.data?.message;
221
+ if (status === 401) return "API Key inv\xE1lida o expirada";
222
+ if (status === 404) return `${context} no encontrado`;
223
+ if (status && status >= 500) return "Error del servidor CMS";
224
+ return responseMessage || `Error de conexi\xF3n en ${context}: ${error.message}`;
225
+ } else if (error instanceof import_zod.z.ZodError) {
226
+ return `Validaci\xF3n fallida: ${error.issues.map((e) => e.message).join(", ")}`;
227
+ } else if (error instanceof Error) {
228
+ return error.message;
229
+ }
230
+ return "Error desconocido";
231
+ }
232
+ // Generic GraphQL fetcher
233
+ async fetchGraphQL(query, variables, extractData) {
234
+ try {
235
+ const response = await this.client.post("/api/graphql", {
236
+ query,
237
+ variables
238
+ });
239
+ if (response.data.errors) {
240
+ const errorMessage = `GraphQL Error: ${response.data.errors[0].message}`;
241
+ return { data: null, error: true, errorMessage };
242
+ }
243
+ const data = extractData(response.data.data);
244
+ return { data, error: false, errorMessage: null };
245
+ } catch (error) {
246
+ const errorMessage = this.handleApiError(error, "GraphQL query");
247
+ return { data: null, error: true, errorMessage };
248
+ }
249
+ }
147
250
  async getItems(params = {}) {
148
- const { skipCache = false, cacheTTL, ...rest } = params;
149
- const cacheKey = this.buildCacheKey("GetEcommerceItems", rest);
150
- return this.getCachedOrFetch(
251
+ const itemsSchema = import_zod.z.object({
252
+ where: import_zod.z.object({
253
+ item_category_id: import_zod.z.object({ equals: import_zod.z.string().optional() }).optional(),
254
+ item_super_category_id: import_zod.z.object({ equals: import_zod.z.string().optional() }).optional(),
255
+ brand_id: import_zod.z.object({ equals: import_zod.z.string().optional() }).optional(),
256
+ id: import_zod.z.object({ equals: import_zod.z.string().optional() }).optional()
257
+ }).optional(),
258
+ onlyOffers: import_zod.z.boolean().optional(),
259
+ onlyEcommerce: import_zod.z.boolean().optional(),
260
+ limit: import_zod.z.number().optional(),
261
+ page: import_zod.z.number().optional(),
262
+ search: import_zod.z.string().optional(),
263
+ sort: import_zod.z.string().optional(),
264
+ minPrice: import_zod.z.number().optional(),
265
+ maxPrice: import_zod.z.number().optional(),
266
+ skipCache: import_zod.z.boolean().optional(),
267
+ cacheTTL: import_zod.z.number().optional()
268
+ });
269
+ const validatedParams = itemsSchema.safeParse(params);
270
+ if (!validatedParams.success) {
271
+ const errorMessage = this.handleApiError(
272
+ validatedParams.error,
273
+ "getItems validation"
274
+ );
275
+ return {
276
+ data: null,
277
+ items: [],
278
+ totalDocs: 0,
279
+ totalPages: 0,
280
+ prevPage: null,
281
+ nextPage: null,
282
+ error: true,
283
+ errorMessage
284
+ };
285
+ }
286
+ const { skipCache = false, cacheTTL, ...rest } = validatedParams.data;
287
+ const cacheKey = this.cache.buildCacheKey("GetEcommerceItems", rest);
288
+ return this.cache.getCachedOrFetch(
151
289
  cacheKey,
152
- () => this.fetchItemsFromAPI(params),
290
+ () => this.fetchItemsFromAPI(rest),
153
291
  cacheTTL,
154
292
  skipCache
155
293
  );
@@ -167,136 +305,84 @@ var PakentoCMSAPI = class {
167
305
  errorMessage: "SDK no configurado: PAKENTO_CMS_BASE_URL o PAKENTO_API_KEY faltantes."
168
306
  };
169
307
  }
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
308
+ const query = `
309
+ query GetEcommerceItems(
310
+ $where: EcommerceItemsWhere
311
+ $onlyOffers: Boolean
312
+ $onlyEcommerce: Boolean
313
+ $limit: Int
314
+ $page: Int
315
+ $search: String
316
+ $sort: String
317
+ $minPrice: Float
318
+ $maxPrice: Float
319
+ ) {
320
+ GetEcommerceItems(
321
+ where: $where
322
+ onlyOffers: $onlyOffers
323
+ onlyEcommerce: $onlyEcommerce
324
+ limit: $limit
325
+ page: $page
326
+ search: $search
327
+ sort: $sort
328
+ minPrice: $minPrice
329
+ maxPrice: $maxPrice
216
330
  ) {
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
- }
331
+ totalDocs
332
+ totalPages
333
+ prevPage
334
+ nextPage
335
+ docs {
336
+ id
337
+ name
338
+ featured
339
+ old_price
340
+ currency_prefix
341
+ description
342
+ price_text
343
+ price
344
+ brand_id
345
+ brand_name
346
+ cover_image_url
347
+ cover_image_thumbnail_url
348
+ item_category_name
349
+ item_category_id
350
+ images {
351
+ alt
352
+ url
353
+ thumbnail_url
252
354
  }
253
355
  }
254
356
  }
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
357
  }
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
358
+ `;
359
+ const whereConditions = {};
360
+ if (params.where?.item_category_id?.equals) {
361
+ whereConditions.item_category_id = {
362
+ equals: params.where.item_category_id.equals
283
363
  };
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
- }
364
+ }
365
+ if (params.where?.item_super_category_id?.equals) {
366
+ whereConditions.item_super_category_id = {
367
+ equals: params.where.item_super_category_id.equals
368
+ };
369
+ }
370
+ if (params.where?.brand_id?.equals) {
371
+ whereConditions.brand_id = { equals: params.where.brand_id.equals };
372
+ }
373
+ if (params.where?.id?.equals) {
374
+ whereConditions.id = { equals: params.where.id.equals };
375
+ }
376
+ const variables = {
377
+ ...params,
378
+ where: Object.keys(whereConditions).length > 0 ? whereConditions : void 0
379
+ };
380
+ const { data, error, errorMessage } = await this.fetchGraphQL(
381
+ query,
382
+ variables,
383
+ (responseData) => responseData.GetEcommerceItems
384
+ );
385
+ if (error) {
300
386
  return {
301
387
  data: null,
302
388
  items: [],
@@ -308,6 +394,16 @@ var PakentoCMSAPI = class {
308
394
  errorMessage
309
395
  };
310
396
  }
397
+ return {
398
+ data,
399
+ items: data.docs,
400
+ totalDocs: data.totalDocs,
401
+ totalPages: data.totalPages,
402
+ prevPage: data.prevPage,
403
+ nextPage: data.nextPage,
404
+ error: false,
405
+ errorMessage: null
406
+ };
311
407
  }
312
408
  async getCategories(params = {}) {
313
409
  const {
@@ -315,8 +411,8 @@ var PakentoCMSAPI = class {
315
411
  cacheTTL,
316
412
  ...rest
317
413
  } = params;
318
- const cacheKey = this.buildCacheKey("GetEcommerceCategories", rest);
319
- return this.getCachedOrFetch(
414
+ const cacheKey = this.cache.buildCacheKey("GetEcommerceCategories", rest);
415
+ return this.cache.getCachedOrFetch(
320
416
  cacheKey,
321
417
  () => this.fetchCategoriesFromAPI(rest),
322
418
  cacheTTL,
@@ -332,64 +428,33 @@ var PakentoCMSAPI = class {
332
428
  errorMessage: "SDK no configurado: PAKENTO_CMS_BASE_URL o PAKENTO_API_KEY faltantes."
333
429
  };
334
430
  }
335
- try {
336
- const query = `
337
- query GetEcommerceCategories(
338
- $where: EcommerceCategoriesWhere
339
- $limit: Int
340
- $page: Int
341
- $sort: String
431
+ const query = `
432
+ query GetEcommerceCategories(
433
+ $where: EcommerceCategoriesWhere
434
+ $limit: Int
435
+ $page: Int
436
+ $sort: String
437
+ ) {
438
+ GetEcommerceCategories(
439
+ where: $where
440
+ limit: $limit
441
+ page: $page
442
+ sort: $sort
342
443
  ) {
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
- }
444
+ docs {
445
+ id
446
+ name
447
+ image_url
354
448
  }
355
449
  }
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
450
  }
451
+ `;
452
+ const { data, error, errorMessage } = await this.fetchGraphQL(
453
+ query,
454
+ params,
455
+ (responseData) => responseData.GetEcommerceCategories
456
+ );
457
+ if (error) {
393
458
  return {
394
459
  data: null,
395
460
  categories: [],
@@ -397,11 +462,17 @@ var PakentoCMSAPI = class {
397
462
  errorMessage
398
463
  };
399
464
  }
465
+ return {
466
+ data: data.docs,
467
+ categories: data.docs,
468
+ error: false,
469
+ errorMessage: null
470
+ };
400
471
  }
401
472
  async getBrands(params = {}) {
402
473
  const { skipCache = false, cacheTTL, ...rest } = params;
403
- const cacheKey = this.buildCacheKey("GetEcommerceBrands", rest);
404
- return this.getCachedOrFetch(
474
+ const cacheKey = this.cache.buildCacheKey("GetEcommerceBrands", rest);
475
+ return this.cache.getCachedOrFetch(
405
476
  cacheKey,
406
477
  () => this.fetchBrandsFromAPI(rest),
407
478
  cacheTTL,
@@ -427,96 +498,45 @@ var PakentoCMSAPI = class {
427
498
  errorMessage: "SDK no configurado: PAKENTO_CMS_BASE_URL o PAKENTO_API_KEY faltantes."
428
499
  };
429
500
  }
430
- try {
431
- const query = `
432
- query GetEcommerceBrands(
433
- $limit: Int
434
- $page: Int
435
- $sort: String
501
+ const query = `
502
+ query GetEcommerceBrands(
503
+ $limit: Int
504
+ $page: Int
505
+ $sort: String
506
+ ) {
507
+ GetEcommerceBrands(
508
+ limit: $limit
509
+ page: $page
510
+ sort: $sort
436
511
  ) {
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
512
+ docs {
513
+ id
514
+ name
515
+ description
516
+ items_count
517
+ image_url
518
+ image_thumbnail_url
519
+ image_alt
461
520
  }
521
+ hasNextPage
522
+ hasPrevPage
523
+ limit
524
+ nextPage
525
+ offset
526
+ page
527
+ pagingCounter
528
+ prevPage
529
+ totalDocs
530
+ totalPages
462
531
  }
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
532
  }
533
+ `;
534
+ const { data, error, errorMessage } = await this.fetchGraphQL(
535
+ query,
536
+ params,
537
+ (responseData) => responseData.GetEcommerceBrands
538
+ );
539
+ if (error) {
520
540
  return {
521
541
  data: null,
522
542
  brands: [],
@@ -534,11 +554,27 @@ var PakentoCMSAPI = class {
534
554
  errorMessage
535
555
  };
536
556
  }
557
+ return {
558
+ data,
559
+ brands: data.docs,
560
+ hasNextPage: data.hasNextPage,
561
+ hasPrevPage: data.hasPrevPage,
562
+ limit: data.limit,
563
+ nextPage: data.nextPage,
564
+ offset: data.offset,
565
+ page: data.page,
566
+ pagingCounter: data.pagingCounter,
567
+ prevPage: data.prevPage,
568
+ totalDocs: data.totalDocs,
569
+ totalPages: data.totalPages,
570
+ error: false,
571
+ errorMessage: null
572
+ };
537
573
  }
538
574
  async getEntity(params = {}) {
539
575
  const { skipCache = false, cacheTTL, ...rest } = params;
540
- const cacheKey = this.buildCacheKey("GetEntity", rest);
541
- return this.getCachedOrFetch(
576
+ const cacheKey = this.cache.buildCacheKey("GetEntity", rest);
577
+ return this.cache.getCachedOrFetch(
542
578
  cacheKey,
543
579
  () => this.fetchEntityFromAPI(rest),
544
580
  cacheTTL,
@@ -554,81 +590,50 @@ var PakentoCMSAPI = class {
554
590
  errorMessage: "SDK no configurado: PAKENTO_CMS_BASE_URL o PAKENTO_API_KEY faltantes."
555
591
  };
556
592
  }
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}`;
593
+ const query = `
594
+ query GetEntity {
595
+ GetEntity {
596
+ id
597
+ tin
598
+ name
599
+ web
600
+ address
601
+ country
602
+ city
603
+ currency_id
604
+ currency_name
605
+ currency_prefix
606
+ currency_suffix
607
+ logo_url
608
+ logo_alt
609
+ logo_thumbnail_url
610
+ logo_sizes_thumbnail_filename
611
+ logo_filename
612
+ logo_width
613
+ logo_height
614
+ logo_2_url
615
+ logo_2_alt
616
+ logo_2_thumbnail_url
617
+ logo_2_sizes_thumbnail_filename
618
+ logo_2_filename
619
+ logo_2_width
620
+ logo_2_height
621
+ featured_image_url
622
+ featured_image_alt
623
+ featured_image_thumbnail_url
624
+ featured_image_sizes_thumbnail_filename
625
+ featured_image_filename
626
+ featured_image_width
627
+ featured_image_height
628
628
  }
629
- } else if (error instanceof Error) {
630
- errorMessage = error.message;
631
629
  }
630
+ `;
631
+ const { data, error, errorMessage } = await this.fetchGraphQL(
632
+ query,
633
+ params,
634
+ (responseData) => responseData.GetEntity
635
+ );
636
+ if (error) {
632
637
  return {
633
638
  data: null,
634
639
  entity: null,
@@ -636,49 +641,49 @@ var PakentoCMSAPI = class {
636
641
  errorMessage
637
642
  };
638
643
  }
644
+ return {
645
+ data,
646
+ entity: data,
647
+ error: false,
648
+ errorMessage: null
649
+ };
639
650
  }
640
651
  async createEcommerceOrder(params) {
641
652
  if (!this.baseURL || !this.apiKey) {
642
653
  return {
643
- message: "SDK no configurado: PAKENTO_CMS_BASE_URL o PAKENTO_API_KEY faltantes.",
644
- error: true,
645
- errorMessage: "SDK no configurado: PAKENTO_CMS_BASE_URL o PAKENTO_API_KEY faltantes."
646
- };
647
- }
648
- if (!params.name || !params.email) {
649
- return {
650
- message: "Faltan campos requeridos: name y email son obligatorios",
654
+ message: "SDK no configurado",
651
655
  error: true,
652
- errorMessage: "Faltan campos requeridos: name y email son obligatorios"
656
+ errorMessage: "SDK no configurado"
653
657
  };
654
658
  }
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
- }
659
+ const orderSchema = import_zod.z.object({
660
+ name: import_zod.z.string().min(1),
661
+ email: import_zod.z.string().email(),
662
+ phone: import_zod.z.string().optional(),
663
+ notes: import_zod.z.string().optional(),
664
+ tin: import_zod.z.string().optional(),
665
+ items: import_zod.z.array(
666
+ import_zod.z.object({
667
+ id: import_zod.z.number().positive(),
668
+ quantity: import_zod.z.number().positive()
669
+ })
670
+ ).min(1),
671
+ delivery_address: import_zod.z.string().optional(),
672
+ delivery_instructions: import_zod.z.string().optional(),
673
+ payment_method: import_zod.z.enum(["cash", "transfer"]).optional()
674
+ });
675
+ const validated = orderSchema.safeParse(params);
676
+ if (!validated.success) {
677
+ const errorMessage = this.handleApiError(
678
+ validated.error,
679
+ "createEcommerceOrder validation"
680
+ );
681
+ return { message: errorMessage, error: true, errorMessage };
677
682
  }
678
683
  try {
679
684
  const response = await this.client.post(
680
685
  "/api/orders/create-ecommerce-order",
681
- params
686
+ validated.data
682
687
  );
683
688
  if (response.status === 200) {
684
689
  return {
@@ -688,36 +693,12 @@ var PakentoCMSAPI = class {
688
693
  errorMessage: null
689
694
  };
690
695
  } 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
- };
696
+ const errorMessage = response.data.message || "Error al crear la orden";
697
+ return { message: errorMessage, error: true, errorMessage };
696
698
  }
697
699
  } 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
- };
700
+ const errorMessage = this.handleApiError(error, "createEcommerceOrder");
701
+ return { message: errorMessage, error: true, errorMessage };
721
702
  }
722
703
  }
723
704
  async executeCustomQuery(params) {
@@ -786,25 +767,24 @@ var PakentoCMSAPI = class {
786
767
  errorMessage: "SDK no configurado: PAKENTO_CMS_BASE_URL o PAKENTO_API_KEY faltantes."
787
768
  };
788
769
  }
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
- };
770
+ const contactUsSchema = import_zod.z.object({
771
+ name: import_zod.z.string().min(1),
772
+ email: import_zod.z.string().email(),
773
+ subject: import_zod.z.string().optional(),
774
+ message: import_zod.z.string().min(1)
775
+ });
776
+ const validated = contactUsSchema.safeParse(params);
777
+ if (!validated.success) {
778
+ const errorMessage = this.handleApiError(
779
+ validated.error,
780
+ "sendContactUsEmail validation"
781
+ );
782
+ return { message: errorMessage, error: true, errorMessage };
803
783
  }
804
784
  try {
805
785
  const response = await this.client.post(
806
786
  "/api/entities/send-contact-us-email",
807
- params
787
+ validated.data
808
788
  );
809
789
  if (response.status === 200) {
810
790
  return {
@@ -813,124 +793,43 @@ var PakentoCMSAPI = class {
813
793
  errorMessage: null
814
794
  };
815
795
  } 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
- };
796
+ const errorMessage = response.data.message || "Error al enviar el mensaje";
797
+ return { message: errorMessage, error: true, errorMessage };
821
798
  }
822
799
  } 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
- };
800
+ const errorMessage = this.handleApiError(error, "sendContactUsEmail");
801
+ return { message: errorMessage, error: true, errorMessage };
846
802
  }
847
803
  }
848
804
  /**
849
805
  * Verifica si existe cache para una función y parámetros específicos
850
806
  */
851
807
  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
- }
808
+ return this.cache.hasCache(functionName, params);
863
809
  }
864
810
  /**
865
811
  * Obtiene la key de cache para una función y parámetros específicos
866
812
  */
867
813
  getCacheKey(functionName, params = {}) {
868
- return this.buildCacheKey(functionName, params);
814
+ return this.cache.getCacheKey(functionName, params);
869
815
  }
870
816
  /**
871
817
  * Limpia el cache para una función y parámetros específicos
872
818
  */
873
819
  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
- }
820
+ return this.cache.clearCache(functionName, params);
886
821
  }
887
822
  /**
888
823
  * Limpia todo el cache relacionado con este API Key
889
824
  */
890
825
  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
- }
826
+ return this.cache.clearAllCache();
907
827
  }
908
828
  /**
909
829
  * Obtiene información del cache (útil para debugging)
910
830
  */
911
831
  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
- }
832
+ return this.cache.getCacheInfo(functionName, params);
934
833
  }
935
834
  };
936
835
  var pakentoCMSAPI = new PakentoCMSAPI();