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