falconhub-apilibrary 1.1.0

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 ADDED
@@ -0,0 +1,825 @@
1
+ // src/types/ErrorResponse.ts
2
+ var ErrorResponse = class extends Error {
3
+ constructor(message, statusCode, responseData) {
4
+ super(message);
5
+ this.statusCode = statusCode;
6
+ this.responseData = responseData;
7
+ this.name = "FalconHUB API Error";
8
+ }
9
+ };
10
+
11
+ // src/core/API.ts
12
+ var API = class {
13
+ constructor(properties, cryptoService, authInterceptor, getAccessToken, getRefreshToken) {
14
+ this.properties = properties;
15
+ this.cryptoService = cryptoService;
16
+ this.getAccessToken = getAccessToken;
17
+ this.getRefreshToken = getRefreshToken;
18
+ this.authInterceptor = authInterceptor;
19
+ }
20
+ /**
21
+ * Ejecuta una request HTTP al API
22
+ *
23
+ * @param options - Opciones de la request
24
+ * @returns Respuesta parseada del tipo esperado
25
+ */
26
+ async apiExecute(options) {
27
+ const {
28
+ endpoint,
29
+ method,
30
+ body,
31
+ requiresSignature,
32
+ requiresAuth,
33
+ timeStamp
34
+ } = options;
35
+ const context = {
36
+ requestId: this.cryptoService.generateUniqueId(),
37
+ startTime: Date.now(),
38
+ retryCount: 0,
39
+ metadata: {}
40
+ };
41
+ try {
42
+ const url = `${this.properties.url}/${endpoint}`;
43
+ const headers = {};
44
+ if (body && method !== "GET") {
45
+ headers["Content-Type"] = "application/json";
46
+ }
47
+ ;
48
+ const accessToken = this.getAccessToken();
49
+ if (accessToken) {
50
+ headers["Authorization"] = `Bearer ${accessToken}`;
51
+ }
52
+ ;
53
+ const newTimeStamp = timeStamp || (/* @__PURE__ */ new Date()).toString();
54
+ headers["X-Timestamp"] = newTimeStamp;
55
+ let bodyString;
56
+ if (body && method !== "GET") {
57
+ bodyString = JSON.stringify(body);
58
+ }
59
+ ;
60
+ if (requiresSignature && bodyString) {
61
+ try {
62
+ const refreshToken = this.getRefreshToken();
63
+ if (refreshToken) {
64
+ const signature = this.cryptoService.genSignature(
65
+ bodyString,
66
+ newTimeStamp,
67
+ refreshToken
68
+ );
69
+ if (signature) {
70
+ headers["X-Signature"] = signature;
71
+ }
72
+ ;
73
+ }
74
+ ;
75
+ } catch (error) {
76
+ throw new ErrorResponse(
77
+ "Failed to generate request signature.",
78
+ 401
79
+ );
80
+ }
81
+ ;
82
+ }
83
+ ;
84
+ const requestConfig = {
85
+ url,
86
+ method,
87
+ headers,
88
+ body: bodyString,
89
+ requiresAuth,
90
+ requiresSignature
91
+ };
92
+ const modifiedConfig = this.authInterceptor ? await this.authInterceptor.onRequest(requestConfig, context) : requestConfig;
93
+ const requestInit = {
94
+ method: modifiedConfig.method,
95
+ headers: modifiedConfig.headers,
96
+ mode: "cors",
97
+ credentials: "omit"
98
+ };
99
+ if (bodyString) {
100
+ requestInit.body = bodyString;
101
+ }
102
+ ;
103
+ const response = await fetch(
104
+ modifiedConfig.url,
105
+ requestInit
106
+ );
107
+ if (response.ok) {
108
+ const newAccessToken = response.headers.get("X-New-Access-Token");
109
+ const newRefreshToken = response.headers.get("X-New-Refresh-Token");
110
+ if (newAccessToken || newRefreshToken) {
111
+ this.properties.onTokensRefreshed?.({
112
+ accessToken: newAccessToken,
113
+ refreshToken: newRefreshToken
114
+ });
115
+ }
116
+ ;
117
+ const contentType = response.headers.get("Content-Type");
118
+ if (contentType && contentType.includes("application/json")) {
119
+ const responseText = await response.text();
120
+ return JSON.parse(responseText);
121
+ }
122
+ ;
123
+ return void 0;
124
+ } else {
125
+ await this.handleErrorResponse(response);
126
+ }
127
+ ;
128
+ } catch (error) {
129
+ if (error instanceof ErrorResponse) {
130
+ throw error;
131
+ }
132
+ ;
133
+ throw new ErrorResponse(
134
+ "Unexpected error occurred during API request.",
135
+ 500
136
+ );
137
+ }
138
+ ;
139
+ }
140
+ /**
141
+ * Maneja errores HTTP y lanza excepciones apropiadas
142
+ */
143
+ async handleErrorResponse(response) {
144
+ const statusCode = response.status;
145
+ let errorMessage = `Error ${statusCode}`;
146
+ let responseData = null;
147
+ try {
148
+ const contentType = response.headers.get("Content-Type");
149
+ if (contentType && contentType.includes("application/json")) {
150
+ responseData = await response.json();
151
+ errorMessage = responseData.message || responseData.error || errorMessage;
152
+ } else {
153
+ errorMessage = await response.text();
154
+ }
155
+ ;
156
+ } catch (error) {
157
+ }
158
+ ;
159
+ switch (statusCode) {
160
+ case 401:
161
+ const authError = new ErrorResponse(
162
+ "Unauthorized access.",
163
+ 401,
164
+ responseData
165
+ );
166
+ this.properties.onAuthError?.(authError);
167
+ throw authError;
168
+ case 403:
169
+ throw new ErrorResponse(
170
+ "No tienes permisos para realizar esta acci\xF3n.",
171
+ 403,
172
+ responseData
173
+ );
174
+ case 404:
175
+ throw new ErrorResponse(
176
+ "Recurso no encontrado.",
177
+ 404,
178
+ responseData
179
+ );
180
+ case 422:
181
+ throw new ErrorResponse(
182
+ errorMessage || "Datos de entrada inv\xE1lidos.",
183
+ 422,
184
+ responseData
185
+ );
186
+ case 500:
187
+ throw new ErrorResponse(
188
+ "Error interno del servidor.",
189
+ 500,
190
+ responseData
191
+ );
192
+ default:
193
+ throw new ErrorResponse(
194
+ errorMessage,
195
+ statusCode,
196
+ responseData
197
+ );
198
+ }
199
+ ;
200
+ }
201
+ //#region REQUEST METHODS
202
+ async executeGET(endpoint, requiresAuth = false) {
203
+ return this.apiExecute({
204
+ endpoint,
205
+ method: "GET",
206
+ requiresAuth,
207
+ requiresSignature: false
208
+ });
209
+ }
210
+ async executePOST(endpoint, body, requiresAuth = true, requiresSignature = true) {
211
+ return this.apiExecute({
212
+ endpoint,
213
+ method: "POST",
214
+ body,
215
+ requiresAuth,
216
+ requiresSignature
217
+ });
218
+ }
219
+ async executePATCH(endpoint, body, requiresAuth = true, requiresSignature = true) {
220
+ return this.apiExecute({
221
+ endpoint,
222
+ method: "PATCH",
223
+ body,
224
+ requiresAuth,
225
+ requiresSignature
226
+ });
227
+ }
228
+ async executeDELETE(endpoint, body, requiresAuth = true, requiresSignature = false) {
229
+ return this.apiExecute({
230
+ endpoint,
231
+ method: "DELETE",
232
+ body,
233
+ requiresAuth,
234
+ requiresSignature
235
+ });
236
+ }
237
+ async executePUT(endpoint, body, requiresAuth = true, requiresSignature = true) {
238
+ return this.apiExecute({
239
+ endpoint,
240
+ method: "PUT",
241
+ body,
242
+ requiresAuth,
243
+ requiresSignature
244
+ });
245
+ }
246
+ async executePublicRequest(endpoint, method, body, timestamp) {
247
+ return this.apiExecute({
248
+ endpoint,
249
+ method,
250
+ body,
251
+ requiresAuth: false,
252
+ requiresSignature: false,
253
+ timeStamp: timestamp
254
+ });
255
+ }
256
+ //#endregion
257
+ };
258
+
259
+ // src/core/CryptoService.ts
260
+ import * as CryptoJS from "crypto-js";
261
+ var CryptoService = class {
262
+ /**
263
+ * Encripta el texto plano utilizando AES con una clave derivada del secretWord y el timestamp
264
+ *
265
+ * @param plaintText -- Texto plano a encriptar
266
+ * @param secretWord - Palabra secreta utilizada para derivar la clave
267
+ * @param timestamp - Timestamp actual
268
+ * @returns Texto encriptado en formato Base64
269
+ */
270
+ encrypt(plaintText, secretWord, timestamp) {
271
+ const fullKey = `${secretWord}:${timestamp}`;
272
+ const key = CryptoJS.SHA256(fullKey);
273
+ const iv = CryptoJS.lib.WordArray.random(16);
274
+ const encrypted = CryptoJS.AES.encrypt(plaintText, key, {
275
+ iv,
276
+ mode: CryptoJS.mode.CBC,
277
+ padding: CryptoJS.pad.Pkcs7
278
+ });
279
+ const ivAndCipher = iv.concat(encrypted.ciphertext);
280
+ return CryptoJS.enc.Base64.stringify(ivAndCipher);
281
+ }
282
+ /**
283
+ * Genera la firma Signature para las operaciones de tipo POST
284
+ *
285
+ * @param body - Cuerpo de la solicitud
286
+ * @param timestamp - Timestamp actual
287
+ * @param refreshToken - Refresh token del usuario
288
+ * @returns Firma signature en formato Base64
289
+ */
290
+ genSignature(body, timestamp, refreshToken) {
291
+ const message = `${body}:${timestamp}`;
292
+ const hmac = CryptoJS.HmacSHA256(message, refreshToken);
293
+ return CryptoJS.enc.Base64.stringify(hmac);
294
+ }
295
+ /**
296
+ * Desencripta texto usando AES-256-CBC
297
+ *
298
+ * @param encryptedBase64 - Texto encriptado en base64
299
+ * @param secret - Clave secreta
300
+ * @param timestamp - Timestamp usado en la encriptación
301
+ * @returns Texto desencriptado
302
+ */
303
+ decrypt(encryptedText, secretWord, timestamp) {
304
+ try {
305
+ const fullKey = `${secretWord}:${timestamp}`;
306
+ const key = CryptoJS.SHA256(fullKey);
307
+ const ivAndCipher = CryptoJS.enc.Base64.parse(encryptedText);
308
+ const iv = CryptoJS.lib.WordArray.create(ivAndCipher.words.slice(0, 4));
309
+ const ciphertext = CryptoJS.lib.WordArray.create(
310
+ ivAndCipher.words.slice(4),
311
+ ivAndCipher.sigBytes - 16
312
+ );
313
+ const decrypted = CryptoJS.AES.decrypt(
314
+ { ciphertext },
315
+ key,
316
+ {
317
+ iv,
318
+ mode: CryptoJS.mode.CBC,
319
+ padding: CryptoJS.pad.Pkcs7
320
+ }
321
+ );
322
+ return CryptoJS.enc.Utf8.stringify(decrypted);
323
+ } catch (error) {
324
+ throw new Error("Decryption failed, ex:" + error);
325
+ }
326
+ }
327
+ /**
328
+ * Verifica una firma HMAC-SHA256 (útil para testing o validación)
329
+ *
330
+ * @param body - Cuerpo original
331
+ * @param timestamp - Timestamp original
332
+ * @param refreshToken - Token usado como clave
333
+ * @param signature - Firma a verificar
334
+ * @returns true si la firma es válida
335
+ */
336
+ verifySignature(body, timestamp, refreshToken, signature) {
337
+ try {
338
+ const expectedSignature = this.genSignature(body, timestamp, refreshToken);
339
+ return expectedSignature === signature;
340
+ } catch {
341
+ return false;
342
+ }
343
+ }
344
+ /**
345
+ * Genera un hash SHA256 de un texto (útil para fingerprints)
346
+ *
347
+ * @param text - Texto a hashear
348
+ * @returns Hash en formato hexadecimal
349
+ */
350
+ sha256(text) {
351
+ return CryptoJS.SHA256(text).toString(CryptoJS.enc.Hex);
352
+ }
353
+ /**
354
+ * Genera un identificador único basado en timestamp y random
355
+ * (útil para request IDs)
356
+ *
357
+ * @returns ID único
358
+ */
359
+ generateUniqueId() {
360
+ const timestamp = Date.now().toString(36);
361
+ const randomPart = Math.random().toString(36).substring(2, 15);
362
+ return `${timestamp}-${randomPart}`;
363
+ }
364
+ };
365
+
366
+ // src/core/TokenManager.ts
367
+ var TokenManager = class {
368
+ constructor(onTokensChanged, onSessionExpiring, onSessionExpired) {
369
+ this.tokens = null;
370
+ this.sessionCheckInterval = null;
371
+ this.hasNotifiedExpiring = false;
372
+ this.onTokensChanged = onTokensChanged;
373
+ this.onSessionExpiring = onSessionExpiring;
374
+ this.onSessionExpired = onSessionExpired;
375
+ }
376
+ /**
377
+ * Guarda los tokens de autenticación y dispara el callback onTokensChanged.
378
+ */
379
+ setTokens(tokens) {
380
+ this.tokens = tokens;
381
+ this.hasNotifiedExpiring = false;
382
+ this.onTokensChanged?.(this.tokens);
383
+ if (!tokens.rememberMe) {
384
+ this.startExpirationCheck();
385
+ } else {
386
+ this.stopExpirationCheck();
387
+ }
388
+ ;
389
+ }
390
+ /**
391
+ * Obtiene los tokens de autenticación almacenados.
392
+ */
393
+ getTokens() {
394
+ return this.tokens;
395
+ }
396
+ /**
397
+ * Limpia los tokens de autenticación almacenados y dispara el callback onTokensChanged.
398
+ */
399
+ clearTokens() {
400
+ this.stopExpirationCheck();
401
+ this.tokens = null;
402
+ this.onTokensChanged?.(this.tokens);
403
+ }
404
+ /**
405
+ * Valida si existen tokens y si el access token no ha expirado.
406
+ */
407
+ hasValidTokens() {
408
+ if (!this.tokens) return false;
409
+ const now = Date.now();
410
+ const expiresAt = this.tokens.expiresAt.getTime();
411
+ return now < expiresAt;
412
+ }
413
+ /**
414
+ * Valida si el access token expirará dentro del umbral especificado (en minutos).
415
+ */
416
+ isTokenExpiringSoon(thresholdMinutes) {
417
+ if (!this.tokens) return false;
418
+ const now = Date.now();
419
+ const expiresAt = this.tokens.expiresAt.getTime();
420
+ const thresholdMs = thresholdMinutes * 60 * 1e3;
421
+ const timeLeft = expiresAt - now;
422
+ return timeLeft > 0 && timeLeft <= thresholdMs;
423
+ }
424
+ /**
425
+ * Obtiene el tiempo restante hasta la expiración del access token en milisegundos.
426
+ */
427
+ getTimeUntilExpiry() {
428
+ if (!this.tokens) return null;
429
+ const now = Date.now();
430
+ const expiresAt = this.tokens.expiresAt.getTime();
431
+ const timeLeft = Math.max(0, expiresAt - now);
432
+ return timeLeft;
433
+ }
434
+ /**
435
+ * Obtiene el access token almacenado.
436
+ */
437
+ getAccessToken() {
438
+ return this.tokens ? this.tokens.accessToken : null;
439
+ }
440
+ /**
441
+ * Obtiene el refresh token almacenado.
442
+ */
443
+ getRefreshToken() {
444
+ return this.tokens ? this.tokens.refreshToken : null;
445
+ }
446
+ /**
447
+ * Verifica si el auto-refresh debe ejecutarse
448
+ * Solo si el usuario activó "rememberMe" durante el login
449
+ */
450
+ shouldAutoRefresh() {
451
+ if (!this.tokens) return false;
452
+ return this.tokens.rememberMe === true;
453
+ }
454
+ /**
455
+ * Inicia el monitoreo de expiración para sesiones sin rememberMe
456
+ * - Notifica cuando quedan 5 minutos (onSessionExpiring)
457
+ * - Ejecuta logout cuando queda 1 minuto (onSessionExpired)
458
+ */
459
+ startExpirationCheck() {
460
+ this.stopExpirationCheck();
461
+ this.sessionCheckInterval = setInterval(async () => {
462
+ if (!this.tokens) {
463
+ this.stopExpirationCheck();
464
+ return;
465
+ }
466
+ ;
467
+ const timeLeft = this.getTimeUntilExpiry();
468
+ if (timeLeft === null) return;
469
+ const minutesLeft = Math.floor(timeLeft / (60 * 1e3));
470
+ if (minutesLeft <= 5 && minutesLeft > 1 && !this.hasNotifiedExpiring) {
471
+ this.hasNotifiedExpiring = true;
472
+ this.onSessionExpiring?.(minutesLeft);
473
+ }
474
+ ;
475
+ if (minutesLeft <= 1) {
476
+ this.stopExpirationCheck();
477
+ if (this.onSessionExpired) {
478
+ await this.onSessionExpired();
479
+ }
480
+ ;
481
+ }
482
+ ;
483
+ }, 6e4);
484
+ }
485
+ /**
486
+ * Detiene el monitoreo de expiración
487
+ */
488
+ stopExpirationCheck() {
489
+ if (this.sessionCheckInterval) {
490
+ clearInterval(this.sessionCheckInterval);
491
+ this.sessionCheckInterval = null;
492
+ }
493
+ ;
494
+ }
495
+ };
496
+
497
+ // src/core/interceptors/AuthInterceptor.ts
498
+ var AuthInterceptor = class {
499
+ constructor(config) {
500
+ this.name = "AuthInterceptor";
501
+ this.refreshPromise = null;
502
+ this.isRefreshing = false;
503
+ this.config = {
504
+ enabled: config.enabled ?? true,
505
+ thresholdMinutes: config.thresholdMinutes ?? 5,
506
+ isTokenExpiringSoon: config.isTokenExpiringSoon,
507
+ hasValidTokens: config.hasValidTokens,
508
+ shouldAutoRefresh: config.shouldAutoRefresh,
509
+ refreshSession: config.refreshSession
510
+ };
511
+ }
512
+ async onRequest(config, context) {
513
+ if (!config.requiresAuth || !this.config.enabled) {
514
+ return config;
515
+ }
516
+ ;
517
+ if (!this.config.hasValidTokens()) {
518
+ return config;
519
+ }
520
+ ;
521
+ if (!this.config.shouldAutoRefresh()) {
522
+ return config;
523
+ }
524
+ ;
525
+ if (this.config.isTokenExpiringSoon()) {
526
+ try {
527
+ if (this.isRefreshing && this.refreshPromise) {
528
+ await this.refreshPromise;
529
+ } else {
530
+ this.isRefreshing = true;
531
+ this.refreshPromise = this.config.refreshSession();
532
+ await this.refreshPromise;
533
+ this.isRefreshing = false;
534
+ this.refreshPromise = null;
535
+ }
536
+ ;
537
+ } catch (error) {
538
+ this.isRefreshing = false;
539
+ this.refreshPromise = null;
540
+ }
541
+ ;
542
+ }
543
+ ;
544
+ return config;
545
+ }
546
+ updateConfig(config) {
547
+ this.config = {
548
+ ...this.config,
549
+ ...config
550
+ };
551
+ }
552
+ reset() {
553
+ this.isRefreshing = false;
554
+ this.refreshPromise = null;
555
+ }
556
+ };
557
+
558
+ // src/services/AuthService.ts
559
+ var AuthService = class {
560
+ constructor(serviceProperties, tokenManager, cryptoService, api) {
561
+ this.api = api;
562
+ this.crytpoService = cryptoService;
563
+ this.tokenManager = tokenManager;
564
+ this.serviceProperties = serviceProperties;
565
+ }
566
+ /**
567
+ * Login con las credenciales proporcionadas
568
+ * @param credentials - Objeto con email, password y rememberMe
569
+ * @returns Retorna el cuerpo de respuesta del login.
570
+ */
571
+ async login(credentials) {
572
+ const timestamp = Date.now().toString();
573
+ const encryptedEmail = this.crytpoService.encrypt(
574
+ credentials.email,
575
+ `${this.serviceProperties.originRequest}_${this.serviceProperties.domain}`,
576
+ timestamp
577
+ );
578
+ const encryptedPassword = this.crytpoService.encrypt(
579
+ credentials.password,
580
+ `${this.serviceProperties.originRequest}_${this.serviceProperties.domain}`,
581
+ timestamp
582
+ );
583
+ const loginData = {
584
+ email: encryptedEmail,
585
+ password: encryptedPassword,
586
+ rememberMe: credentials.rememberMe
587
+ };
588
+ const response = await this.api.executePublicRequest(
589
+ "oauth/login",
590
+ "POST",
591
+ loginData,
592
+ timestamp
593
+ );
594
+ if (!response) {
595
+ throw new ErrorResponse("No response from server");
596
+ }
597
+ ;
598
+ if (response.success) {
599
+ const data = response.data;
600
+ const expiresIn = parseInt(data.expiresIn.replace("s", ""));
601
+ const expiresAt = new Date(Date.now() + expiresIn * 1e3);
602
+ const tokens = {
603
+ accessToken: data.accessToken,
604
+ refreshToken: data.refreshToken,
605
+ expiresAt,
606
+ rememberMe: credentials.rememberMe
607
+ };
608
+ await this.tokenManager.setTokens(tokens);
609
+ this.serviceProperties.onTokensUpdated?.(tokens);
610
+ const responseData = {
611
+ success: data.accessToken ? true : false,
612
+ data,
613
+ message: response.message,
614
+ responseTime: "3s"
615
+ };
616
+ return responseData;
617
+ }
618
+ return response;
619
+ }
620
+ /**
621
+ * Valida la sesión actual y retorna información de expiración
622
+ *
623
+ * El servidor puede responder de 2 formas:
624
+ * 1. Sesión renovada (sliding window): retorna nuevos tokens
625
+ * 2. Sesión válida: retorna info de expiración sin renovar
626
+ */
627
+ async validateSession() {
628
+ const response = await this.api.executePOST(
629
+ "oauth/validateSession"
630
+ );
631
+ if (!response) {
632
+ throw new ErrorResponse("No response from server");
633
+ }
634
+ ;
635
+ if (response.success && response.data) {
636
+ const respData = response.data;
637
+ if ("expiresIn" in respData && respData.expiresIn) {
638
+ const sessionData = respData;
639
+ const expiresIn = parseInt(sessionData.expiresIn.replace("s", ""));
640
+ const expiresAt = new Date(Date.now() + expiresIn * 1e3);
641
+ const currentTokens = this.tokenManager.getTokens();
642
+ const rememberMe = currentTokens?.rememberMe ?? sessionData.isRemembered;
643
+ const tokens = {
644
+ accessToken: sessionData.accessToken,
645
+ refreshToken: sessionData.refreshToken,
646
+ expiresAt,
647
+ rememberMe
648
+ };
649
+ this.tokenManager.setTokens(tokens);
650
+ this.serviceProperties.onTokensUpdated?.(tokens);
651
+ } else if ("expiresAt" in respData && respData.expiresAt) {
652
+ const sessionData = respData;
653
+ if (sessionData.remainingMinutes <= 5 && sessionData.remainingMinutes > 1) {
654
+ this.serviceProperties.onSessionExpiring?.(sessionData.remainingMinutes);
655
+ } else if (sessionData.remainingMinutes <= 1) {
656
+ if (this.serviceProperties.onSessionExpired) {
657
+ await this.serviceProperties.onSessionExpired();
658
+ }
659
+ }
660
+ }
661
+ }
662
+ ;
663
+ return response;
664
+ }
665
+ /**
666
+ * Refresca los tokens usando el refresh token
667
+ */
668
+ async refreshTokens(rememberMe) {
669
+ const refreshToken = this.tokenManager.getRefreshToken();
670
+ if (!refreshToken) {
671
+ throw new ErrorResponse("No refresh token available");
672
+ }
673
+ ;
674
+ const response = await this.api.executePublicRequest(
675
+ "oauth/refresh",
676
+ "POST",
677
+ { refreshToken }
678
+ );
679
+ if (!response) {
680
+ throw new ErrorResponse("No response from server");
681
+ }
682
+ ;
683
+ if (response.success) {
684
+ const sessionData = response.data;
685
+ const currentTokens = this.tokenManager.getTokens();
686
+ const shouldRemember = rememberMe ?? currentTokens?.rememberMe ?? false;
687
+ const expiresIn = parseInt(sessionData.expiresIn.replace("s", ""));
688
+ const expiresAt = new Date(Date.now() + expiresIn * 1e3);
689
+ const tokens = {
690
+ accessToken: sessionData.accessToken,
691
+ refreshToken: sessionData.refreshToken,
692
+ expiresAt,
693
+ rememberMe: shouldRemember
694
+ };
695
+ this.tokenManager.setTokens(tokens);
696
+ this.serviceProperties.onTokensUpdated?.(tokens);
697
+ }
698
+ ;
699
+ return response;
700
+ }
701
+ /**
702
+ * Cierra sesión: notifica al servidor y limpia tokens
703
+ */
704
+ async logout() {
705
+ try {
706
+ await this.api.executePOST("oauth/logout");
707
+ } catch (error) {
708
+ }
709
+ ;
710
+ this.tokenManager.clearTokens();
711
+ this.serviceProperties.onTokensUpdated?.(null);
712
+ }
713
+ /**
714
+ * Verifica si el usuario está autenticado
715
+ */
716
+ isAuthenticated() {
717
+ return this.tokenManager.hasValidTokens();
718
+ }
719
+ /**
720
+ * Obtiene los tokens actuales
721
+ */
722
+ getTokens() {
723
+ return this.tokenManager.getTokens();
724
+ }
725
+ };
726
+
727
+ // src/FalconHUBSDK.ts
728
+ var FalconHUBSDK = class {
729
+ constructor(serviceProperties) {
730
+ this.serviceProperties = serviceProperties;
731
+ this.cryptoService = new CryptoService();
732
+ this.tokenManager = new TokenManager(
733
+ this.serviceProperties.onTokensUpdated,
734
+ this.serviceProperties.onSessionExpiring,
735
+ async () => {
736
+ await this.auth.logout();
737
+ }
738
+ );
739
+ const autoRefreshConfig = this.normalizeAutoRefreshConfig(this.serviceProperties.autoRefresh);
740
+ if (autoRefreshConfig.enabled) {
741
+ this.authInterceptor = new AuthInterceptor({
742
+ enabled: true,
743
+ thresholdMinutes: autoRefreshConfig.thresholdMinutes,
744
+ isTokenExpiringSoon: () => this.tokenManager.isTokenExpiringSoon(autoRefreshConfig.thresholdMinutes),
745
+ hasValidTokens: () => this.tokenManager.hasValidTokens(),
746
+ shouldAutoRefresh: () => this.tokenManager.shouldAutoRefresh(),
747
+ refreshSession: async () => {
748
+ await this.auth.validateSession();
749
+ }
750
+ });
751
+ } else {
752
+ this.authInterceptor = null;
753
+ }
754
+ this.api = new API(
755
+ this.serviceProperties,
756
+ this.cryptoService,
757
+ this.authInterceptor,
758
+ () => this.tokenManager.getAccessToken(),
759
+ () => this.tokenManager.getRefreshToken()
760
+ );
761
+ this.auth = new AuthService(
762
+ this.serviceProperties,
763
+ this.tokenManager,
764
+ this.cryptoService,
765
+ this.api
766
+ );
767
+ }
768
+ normalizeAutoRefreshConfig(config) {
769
+ if (typeof config === "boolean") {
770
+ return { enabled: config, thresholdMinutes: 5 };
771
+ }
772
+ if (typeof config === "object" && config !== null) {
773
+ return {
774
+ enabled: config.enabled,
775
+ thresholdMinutes: config.thresholdMinutes ?? 5
776
+ };
777
+ }
778
+ return { enabled: true, thresholdMinutes: 5 };
779
+ }
780
+ /**
781
+ * Inyecta tokens desde storage externo (cookies/localStorage/etc)
782
+ */
783
+ setTokensFromExternal(accessToken, refreshToken, expiresAt, rememberMe = false) {
784
+ const tokens = {
785
+ accessToken,
786
+ refreshToken,
787
+ expiresAt,
788
+ rememberMe
789
+ };
790
+ this.tokenManager.setTokens(tokens);
791
+ }
792
+ /**
793
+ * Obtiene los tokens actuales
794
+ */
795
+ getTokens() {
796
+ return this.tokenManager.getTokens();
797
+ }
798
+ /**
799
+ * Verifica si el usuario está autenticado
800
+ */
801
+ isAuthenticated() {
802
+ return this.tokenManager.hasValidTokens();
803
+ }
804
+ /**
805
+ * Verifica si la sesión está por expirar
806
+ */
807
+ isSessionExpiringSoon(thresholdMinutes = 5) {
808
+ return this.tokenManager.isTokenExpiringSoon(thresholdMinutes);
809
+ }
810
+ /**
811
+ * Obtiene el tiempo restante hasta la expiración en milisegundos
812
+ */
813
+ getTimeUntilExpiry() {
814
+ return this.tokenManager.getTimeUntilExpiry();
815
+ }
816
+ };
817
+ export {
818
+ API,
819
+ AuthInterceptor,
820
+ AuthService,
821
+ CryptoService,
822
+ FalconHUBSDK,
823
+ TokenManager
824
+ };
825
+ //# sourceMappingURL=index.mjs.map