astro-tokenkit 1.0.18 → 1.0.20

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.
@@ -368,6 +368,7 @@ function isExpired(expiresAt, now, policy = {}) {
368
368
  }
369
369
 
370
370
  // packages/astro-tokenkit/src/utils/fetch.ts
371
+ let sharedInsecureAgent = null;
371
372
  /**
372
373
  * Perform a fetch request with optional certificate validation bypass
373
374
  */
@@ -376,16 +377,26 @@ function safeFetch(url, init, config) {
376
377
  const fetchFn = config.fetch || fetch;
377
378
  const fetchOptions = Object.assign({}, init);
378
379
  if (config.dangerouslyIgnoreCertificateErrors && typeof process !== 'undefined') {
379
- // In Node.js environment
380
380
  try {
381
- // Try to use undici Agent if available (it is built-in in Node 18+)
382
- // However, we might need to import it if we want to create an Agent.
383
- // Since we don't want to depend on undici in package.json, we use dynamic import.
384
- // But wait, undici's Agent is what we need.
385
- // As a fallback and most reliable way for self-signed certs in Node without extra deps:
386
- process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
387
- // NOTE: This affects the whole process. We should ideally only do this if it's not already 0.
388
- // But for a dev tool / specialized library, it's often what's needed.
381
+ // Try to use undici Agent if available to avoid global process.env changes
382
+ if (!sharedInsecureAgent) {
383
+ // @ts-ignore
384
+ const undici = yield import('undici').catch(() => null);
385
+ if (undici && undici.Agent) {
386
+ sharedInsecureAgent = new undici.Agent({
387
+ connect: { rejectUnauthorized: false }
388
+ });
389
+ }
390
+ }
391
+ if (sharedInsecureAgent) {
392
+ fetchOptions.dispatcher = sharedInsecureAgent;
393
+ }
394
+ else {
395
+ // Fallback to global setting (less secure, but only way without undici)
396
+ if (process.env.NODE_TLS_REJECT_UNAUTHORIZED !== '0') {
397
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
398
+ }
399
+ }
389
400
  }
390
401
  catch (e) {
391
402
  // Ignore
@@ -430,21 +441,18 @@ class SingleFlight {
430
441
  const existing = this.inFlight.get(key);
431
442
  if (existing)
432
443
  return existing;
433
- const promise = this.doExecute(key, fn);
444
+ const promise = (() => __awaiter(this, void 0, void 0, function* () {
445
+ try {
446
+ return yield fn();
447
+ }
448
+ finally {
449
+ this.inFlight.delete(key);
450
+ }
451
+ }))();
434
452
  this.inFlight.set(key, promise);
435
453
  return promise;
436
454
  });
437
455
  }
438
- doExecute(key, fn) {
439
- return __awaiter(this, void 0, void 0, function* () {
440
- try {
441
- return yield fn();
442
- }
443
- finally {
444
- this.inFlight.delete(key);
445
- }
446
- });
447
- }
448
456
  }
449
457
  /**
450
458
  * Token Manager handles all token operations
@@ -460,6 +468,7 @@ class TokenManager {
460
468
  */
461
469
  login(ctx, credentials, options) {
462
470
  return __awaiter(this, void 0, void 0, function* () {
471
+ var _a, _b;
463
472
  const url = this.joinURL(this.baseURL, this.config.login);
464
473
  const contentType = this.config.contentType || 'application/json';
465
474
  const headers = Object.assign(Object.assign({ 'Content-Type': contentType }, this.config.headers), options === null || options === void 0 ? void 0 : options.headers);
@@ -471,12 +480,16 @@ class TokenManager {
471
480
  else {
472
481
  requestBody = JSON.stringify(data);
473
482
  }
483
+ const timeout = (_b = (_a = options === null || options === void 0 ? void 0 : options.timeout) !== null && _a !== void 0 ? _a : this.config.timeout) !== null && _b !== void 0 ? _b : 30000;
484
+ const controller = new AbortController();
485
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
474
486
  let response;
475
487
  try {
476
488
  response = yield safeFetch(url, {
477
489
  method: 'POST',
478
490
  headers,
479
491
  body: requestBody,
492
+ signal: controller.signal,
480
493
  }, this.config);
481
494
  }
482
495
  catch (error) {
@@ -485,6 +498,9 @@ class TokenManager {
485
498
  yield options.onError(authError, ctx);
486
499
  throw authError;
487
500
  }
501
+ finally {
502
+ clearTimeout(timeoutId);
503
+ }
488
504
  if (!response.ok) {
489
505
  const authError = new AuthError(`Login failed: ${response.status} ${response.statusText}`, response.status, response);
490
506
  if (options === null || options === void 0 ? void 0 : options.onError)
@@ -540,6 +556,7 @@ class TokenManager {
540
556
  */
541
557
  performRefresh(ctx, refreshToken, options, extraHeaders) {
542
558
  return __awaiter(this, void 0, void 0, function* () {
559
+ var _a, _b;
543
560
  const url = this.joinURL(this.baseURL, this.config.refresh);
544
561
  const contentType = this.config.contentType || 'application/json';
545
562
  const headers = Object.assign(Object.assign({ 'Content-Type': contentType }, this.config.headers), extraHeaders);
@@ -552,17 +569,24 @@ class TokenManager {
552
569
  else {
553
570
  requestBody = JSON.stringify(data);
554
571
  }
572
+ const timeout = (_b = (_a = options === null || options === void 0 ? void 0 : options.timeout) !== null && _a !== void 0 ? _a : this.config.timeout) !== null && _b !== void 0 ? _b : 30000;
573
+ const controller = new AbortController();
574
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
555
575
  let response;
556
576
  try {
557
577
  response = yield safeFetch(url, {
558
578
  method: 'POST',
559
579
  headers,
560
580
  body: requestBody,
581
+ signal: controller.signal,
561
582
  }, this.config);
562
583
  }
563
584
  catch (error) {
564
585
  throw new AuthError(`Refresh request failed: ${error.message}`, undefined, undefined, undefined, error);
565
586
  }
587
+ finally {
588
+ clearTimeout(timeoutId);
589
+ }
566
590
  if (!response.ok) {
567
591
  // 401/403 = invalid refresh token
568
592
  if (response.status === 401 || response.status === 403) {
@@ -594,8 +618,8 @@ class TokenManager {
594
618
  /**
595
619
  * Ensure valid tokens (with automatic refresh)
596
620
  */
597
- ensure(ctx, options, headers) {
598
- return __awaiter(this, void 0, void 0, function* () {
621
+ ensure(ctx_1, options_1, headers_1) {
622
+ return __awaiter(this, arguments, void 0, function* (ctx, options, headers, force = false) {
599
623
  var _a, _b, _c, _d, _e, _f;
600
624
  const now = Math.floor(Date.now() / 1000);
601
625
  const tokens = retrieveTokens(ctx, this.config.cookies);
@@ -603,12 +627,14 @@ class TokenManager {
603
627
  if (!tokens.accessToken || !tokens.refreshToken || !tokens.expiresAt) {
604
628
  return null;
605
629
  }
606
- // Token expired
607
- if (isExpired(tokens.expiresAt, now, this.config.policy)) {
630
+ // Token expired or force refresh
631
+ if (force || isExpired(tokens.expiresAt, now, this.config.policy)) {
608
632
  const flightKey = this.createFlightKey(tokens.refreshToken);
609
633
  const bundle = yield this.singleFlight.execute(flightKey, () => this.refresh(ctx, tokens.refreshToken, options, headers));
610
634
  if (!bundle)
611
635
  return null;
636
+ // Ensure tokens are stored in the current context (in case of shared flight)
637
+ storeTokens(ctx, bundle, this.config.cookies);
612
638
  return {
613
639
  accessToken: bundle.accessToken,
614
640
  expiresAt: bundle.accessExpiresAt,
@@ -621,6 +647,8 @@ class TokenManager {
621
647
  const flightKey = this.createFlightKey(tokens.refreshToken);
622
648
  const bundle = yield this.singleFlight.execute(flightKey, () => this.refresh(ctx, tokens.refreshToken, options, headers));
623
649
  if (bundle) {
650
+ // Ensure tokens are stored in the current context (in case of shared flight)
651
+ storeTokens(ctx, bundle, this.config.cookies);
624
652
  return {
625
653
  accessToken: bundle.accessToken,
626
654
  expiresAt: bundle.accessExpiresAt,
@@ -648,23 +676,33 @@ class TokenManager {
648
676
  */
649
677
  logout(ctx) {
650
678
  return __awaiter(this, void 0, void 0, function* () {
651
- var _a;
679
+ var _a, _b;
652
680
  // Optionally call logout endpoint
653
681
  if (this.config.logout) {
682
+ const timeout = (_a = this.config.timeout) !== null && _a !== void 0 ? _a : 10000;
683
+ const controller = new AbortController();
684
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
654
685
  try {
655
686
  const url = this.joinURL(this.baseURL, this.config.logout);
656
687
  const session = this.getSession(ctx);
657
688
  const headers = {};
658
689
  if (session === null || session === void 0 ? void 0 : session.accessToken) {
659
- const injectFn = (_a = this.config.injectToken) !== null && _a !== void 0 ? _a : ((token, type) => `${type !== null && type !== void 0 ? type : 'Bearer'} ${token}`);
690
+ const injectFn = (_b = this.config.injectToken) !== null && _b !== void 0 ? _b : ((token, type) => `${type !== null && type !== void 0 ? type : 'Bearer'} ${token}`);
660
691
  headers['Authorization'] = injectFn(session.accessToken, session.tokenType);
661
692
  }
662
- yield safeFetch(url, { method: 'POST', headers }, this.config);
693
+ yield safeFetch(url, {
694
+ method: 'POST',
695
+ headers,
696
+ signal: controller.signal,
697
+ }, this.config);
663
698
  }
664
699
  catch (error) {
665
700
  // Ignore logout endpoint errors
666
701
  logger.debug('[TokenKit] Logout endpoint failed:', error);
667
702
  }
703
+ finally {
704
+ clearTimeout(timeoutId);
705
+ }
668
706
  }
669
707
  clearTokens(ctx, this.config.cookies);
670
708
  });