@pakento/cms-sdk 4.0.4 → 4.0.6

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