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