@dereekb/calcom 13.10.9 → 13.11.1

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.
@@ -3,19 +3,18 @@
3
3
  var common = require('@nestjs/common');
4
4
  var calcom = require('@dereekb/calcom');
5
5
  var util = require('@dereekb/util');
6
+ var nestjs = require('@dereekb/nestjs');
6
7
  var node_crypto = require('node:crypto');
7
8
  var node_path = require('node:path');
8
- var node_fs = require('node:fs');
9
9
  var config = require('@nestjs/config');
10
- var nestjs = require('@dereekb/nestjs');
11
10
 
12
- function _type_of(obj) {
11
+ function _type_of$1(obj) {
13
12
  "@swc/helpers - typeof";
14
13
  return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
15
14
  }
16
15
  function __decorate(decorators, target, key, desc) {
17
16
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
18
- if ((typeof Reflect === "undefined" ? "undefined" : _type_of(Reflect)) === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
17
+ if ((typeof Reflect === "undefined" ? "undefined" : _type_of$1(Reflect)) === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
19
18
  else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
20
19
  return c > 3 && r && Object.defineProperty(target, key, r), r;
21
20
  }
@@ -47,7 +46,7 @@ function _create_class$6(Constructor, protoProps, staticProps) {
47
46
  if (staticProps) _defineProperties$6(Constructor, staticProps);
48
47
  return Constructor;
49
48
  }
50
- function _define_property$7(obj, key, value) {
49
+ function _define_property$8(obj, key, value) {
51
50
  if (key in obj) {
52
51
  Object.defineProperty(obj, key, {
53
52
  value: value,
@@ -70,8 +69,8 @@ var CALCOM_API_KEY_CONFIG_KEY = 'CALCOM_API_KEY';
70
69
  */ var CalcomOAuthServiceConfig = /*#__PURE__*/ function() {
71
70
  function CalcomOAuthServiceConfig() {
72
71
  _class_call_check$8(this, CalcomOAuthServiceConfig);
73
- _define_property$7(this, "calcomOAuth", void 0);
74
- _define_property$7(this, "factoryConfig", void 0);
72
+ _define_property$8(this, "calcomOAuth", void 0);
73
+ _define_property$8(this, "factoryConfig", void 0);
75
74
  }
76
75
  _create_class$6(CalcomOAuthServiceConfig, null, [
77
76
  {
@@ -158,6 +157,27 @@ function _class_call_check$7(instance, Constructor) {
158
157
  throw new TypeError("Cannot call a class as a function");
159
158
  }
160
159
  }
160
+ function _define_property$7(obj, key, value) {
161
+ if (key in obj) {
162
+ Object.defineProperty(obj, key, {
163
+ value: value,
164
+ enumerable: true,
165
+ configurable: true,
166
+ writable: true
167
+ });
168
+ } else {
169
+ obj[key] = value;
170
+ }
171
+ return obj;
172
+ }
173
+ function _instanceof(left, right) {
174
+ "@swc/helpers - instanceof";
175
+ if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
176
+ return !!right[Symbol.hasInstance](left);
177
+ } else {
178
+ return left instanceof right;
179
+ }
180
+ }
161
181
  function _iterable_to_array$3(iter) {
162
182
  if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
163
183
  }
@@ -191,12 +211,50 @@ function _non_iterable_rest() {
191
211
  function _non_iterable_spread$3() {
192
212
  throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
193
213
  }
214
+ function _object_spread$2(target) {
215
+ for(var i = 1; i < arguments.length; i++){
216
+ var source = arguments[i] != null ? arguments[i] : {};
217
+ var ownKeys = Object.keys(source);
218
+ if (typeof Object.getOwnPropertySymbols === "function") {
219
+ ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
220
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
221
+ }));
222
+ }
223
+ ownKeys.forEach(function(key) {
224
+ _define_property$7(target, key, source[key]);
225
+ });
226
+ }
227
+ return target;
228
+ }
229
+ function ownKeys$2(object, enumerableOnly) {
230
+ var keys = Object.keys(object);
231
+ if (Object.getOwnPropertySymbols) {
232
+ var symbols = Object.getOwnPropertySymbols(object);
233
+ keys.push.apply(keys, symbols);
234
+ }
235
+ return keys;
236
+ }
237
+ function _object_spread_props$2(target, source) {
238
+ source = source != null ? source : {};
239
+ if (Object.getOwnPropertyDescriptors) {
240
+ Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
241
+ } else {
242
+ ownKeys$2(Object(source)).forEach(function(key) {
243
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
244
+ });
245
+ }
246
+ return target;
247
+ }
194
248
  function _sliced_to_array(arr, i) {
195
249
  return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array$3(arr, i) || _non_iterable_rest();
196
250
  }
197
251
  function _to_consumable_array$3(arr) {
198
252
  return _array_without_holes$3(arr) || _iterable_to_array$3(arr) || _unsupported_iterable_to_array$3(arr) || _non_iterable_spread$3();
199
253
  }
254
+ function _type_of(obj) {
255
+ "@swc/helpers - typeof";
256
+ return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
257
+ }
200
258
  function _unsupported_iterable_to_array$3(o, minLen) {
201
259
  if (!o) return;
202
260
  if (typeof o === "string") return _array_like_to_array$3(o, minLen);
@@ -326,6 +384,56 @@ exports.CalcomOAuthAccessTokenCacheService = __decorate([
326
384
  */ function calcomRefreshTokenCacheKey(refreshToken) {
327
385
  return node_crypto.createHash('sha256').update(refreshToken).digest('hex').substring(0, 16);
328
386
  }
387
+ // MARK: Merge
388
+ function buildCalcomReadAdapter(cache) {
389
+ return {
390
+ load: function load() {
391
+ return _async_to_generator$2(function() {
392
+ var value;
393
+ return _ts_generator$2(this, function(_state) {
394
+ switch(_state.label){
395
+ case 0:
396
+ return [
397
+ 4,
398
+ cache.loadCachedToken().catch(function() {
399
+ return undefined;
400
+ })
401
+ ];
402
+ case 1:
403
+ value = _state.sent();
404
+ return [
405
+ 2,
406
+ value != null && !util.isExpired(value) ? value : undefined
407
+ ];
408
+ }
409
+ });
410
+ })();
411
+ },
412
+ update: function update(token) {
413
+ return cache.updateCachedToken(token);
414
+ },
415
+ clear: function clear() {
416
+ return _async_to_generator$2(function() {
417
+ return _ts_generator$2(this, function(_state) {
418
+ return [
419
+ 2
420
+ ];
421
+ });
422
+ // CalcomAccessTokenCache does not expose a clear method.
423
+ })();
424
+ }
425
+ };
426
+ }
427
+ function updateCalcomCacheCapturingError(cache, accessToken) {
428
+ return cache.updateCachedToken(accessToken).then(function() {
429
+ return null;
430
+ }).catch(function(e) {
431
+ return [
432
+ cache,
433
+ e
434
+ ];
435
+ });
436
+ }
329
437
  /**
330
438
  * Default error logging function for {@link mergeCalcomOAuthAccessTokenCacheServices}.
331
439
  * Logs a warning for each cache that failed to update.
@@ -344,6 +452,11 @@ exports.CalcomOAuthAccessTokenCacheService = __decorate([
344
452
  * If one source fails retrieval, the next will be tried.
345
453
  * When updating a cached token, it will update the token across all services.
346
454
  *
455
+ * Read fall-through is delegated to {@link mergeAsyncValueCaches} after wrapping each
456
+ * underlying cache with an {@link isExpired}-aware filter, so an expired cached token
457
+ * never short-circuits the lookup. Updates run across all services in parallel via
458
+ * `Promise.allSettled`, mirroring the previous behavior, with optional error logging.
459
+ *
347
460
  * @param inputServicesToMerge Must include at least one service. Empty arrays will throw an error.
348
461
  * @param logError - optional error logging configuration; pass a function, true for default logging, or false to disable
349
462
  * @returns a merged CalcomOAuthAccessTokenCacheService that delegates across all input services
@@ -353,60 +466,44 @@ exports.CalcomOAuthAccessTokenCacheService = __decorate([
353
466
  if (allServices.length === 0) {
354
467
  throw new Error('mergeCalcomOAuthAccessTokenCacheServices() input cannot be empty.');
355
468
  }
356
- var loadCalcomAccessTokenCache = function loadCalcomAccessTokenCache(accessCachesForServices) {
357
- var loadCachedTokenFromFirstService = util.tryWithPromiseFactoriesFunction({
358
- promiseFactories: accessCachesForServices.map(function(x) {
359
- return function() {
360
- return x.loadCachedToken().catch(function() {
361
- return null;
362
- }).then(function(x) {
363
- var result = undefined;
364
- if (x && !util.isPast(x.expiresAt)) {
365
- result = x; // only return from cache if it is not expired
366
- }
367
- return result;
368
- });
369
- };
370
- }),
371
- successOnMaybe: false,
372
- throwErrors: false
373
- });
374
- var cacheForService = {
469
+ function mergeCachesForService(accessCachesForServices) {
470
+ var readAdapters = accessCachesForServices.map(buildCalcomReadAdapter);
471
+ var merged = util.mergeAsyncValueCaches(readAdapters);
472
+ return {
375
473
  loadCachedToken: function loadCachedToken() {
376
- return loadCachedTokenFromFirstService();
474
+ return merged.load();
377
475
  },
378
476
  updateCachedToken: function updateCachedToken(accessToken) {
379
477
  return _async_to_generator$2(function() {
478
+ var settled, failedUpdates;
380
479
  return _ts_generator$2(this, function(_state) {
381
- return [
382
- 2,
383
- Promise.allSettled(accessCachesForServices.map(function(x) {
384
- return x.updateCachedToken(accessToken).then(function() {
385
- return null;
386
- }).catch(function(e) {
387
- return [
388
- x,
389
- e
390
- ];
391
- });
392
- })).then(function(x) {
393
- // only find the failures if we're logging
480
+ switch(_state.label){
481
+ case 0:
482
+ return [
483
+ 4,
484
+ Promise.allSettled(accessCachesForServices.map(function(cache) {
485
+ return updateCalcomCacheCapturingError(cache, accessToken);
486
+ }))
487
+ ];
488
+ case 1:
489
+ settled = _state.sent();
394
490
  if (logErrorFunction != null) {
395
- var failedUpdates = util.filterMaybeArrayValues(x.map(function(y) {
491
+ failedUpdates = util.filterMaybeArrayValues(settled.map(function(y) {
396
492
  return y.value;
397
493
  }));
398
494
  if (failedUpdates.length) {
399
495
  logErrorFunction(failedUpdates);
400
496
  }
401
497
  }
402
- })
403
- ];
498
+ return [
499
+ 2
500
+ ];
501
+ }
404
502
  });
405
503
  })();
406
504
  }
407
505
  };
408
- return cacheForService;
409
- };
506
+ }
410
507
  var allServiceAccessTokenCaches = allServices.map(function(x) {
411
508
  return x.loadCalcomAccessTokenCache();
412
509
  });
@@ -417,206 +514,204 @@ exports.CalcomOAuthAccessTokenCacheService = __decorate([
417
514
  var allCaches = allServicesWithCacheForRefreshToken.map(function(x) {
418
515
  return x.cacheForRefreshToken(refreshToken);
419
516
  });
420
- return loadCalcomAccessTokenCache(allCaches);
517
+ return mergeCachesForService(allCaches);
421
518
  } : undefined;
422
519
  var service = {
423
- loadCalcomAccessTokenCache: function loadCalcomAccessTokenCache1() {
424
- return loadCalcomAccessTokenCache(allServiceAccessTokenCaches);
520
+ loadCalcomAccessTokenCache: function loadCalcomAccessTokenCache() {
521
+ return mergeCachesForService(allServiceAccessTokenCaches);
425
522
  },
426
523
  cacheForRefreshToken: cacheForRefreshToken
427
524
  };
428
525
  return service;
429
526
  }
430
527
  // MARK: Memory Access Token Cache
528
+ /**
529
+ * Adapts an {@link AsyncValueCache} to a {@link CalcomAccessTokenCache}, optionally logging cache reads/writes to the console.
530
+ *
531
+ * @param cache - underlying single-value cache for the access token
532
+ * @param logAccessToConsole - when true, logs reads and writes to the console
533
+ * @returns the CalcomAccessTokenCache backed by the given async cache
534
+ */ function calcomAccessTokenCacheFromAsyncValueCache(cache, logAccessToConsole) {
535
+ return {
536
+ loadCachedToken: function loadCachedToken() {
537
+ return _async_to_generator$2(function() {
538
+ var token;
539
+ return _ts_generator$2(this, function(_state) {
540
+ switch(_state.label){
541
+ case 0:
542
+ return [
543
+ 4,
544
+ cache.load()
545
+ ];
546
+ case 1:
547
+ token = _state.sent();
548
+ if (logAccessToConsole) {
549
+ console.log('retrieving access token from memory: ', {
550
+ hit: token != null,
551
+ expiresAt: token === null || token === void 0 ? void 0 : token.expiresAt
552
+ });
553
+ }
554
+ return [
555
+ 2,
556
+ token
557
+ ];
558
+ }
559
+ });
560
+ })();
561
+ },
562
+ updateCachedToken: function updateCachedToken(accessToken) {
563
+ return _async_to_generator$2(function() {
564
+ return _ts_generator$2(this, function(_state) {
565
+ switch(_state.label){
566
+ case 0:
567
+ return [
568
+ 4,
569
+ cache.update(accessToken)
570
+ ];
571
+ case 1:
572
+ _state.sent();
573
+ if (logAccessToConsole) {
574
+ console.log('updating access token in memory: ', {
575
+ expiresAt: accessToken === null || accessToken === void 0 ? void 0 : accessToken.expiresAt
576
+ });
577
+ }
578
+ return [
579
+ 2
580
+ ];
581
+ }
582
+ });
583
+ })();
584
+ }
585
+ };
586
+ }
431
587
  /**
432
588
  * Creates a CalcomOAuthAccessTokenCacheService that uses in-memory storage.
433
- * Per-user caches are stored in a Map keyed by the md5 hash of the refresh token.
589
+ *
590
+ * The server-level token is held in a single-slot {@link inMemoryAsyncValueCache};
591
+ * per-user tokens are held in an {@link inMemoryAsyncKeyedValueCache} keyed by the
592
+ * sha256-truncated refresh token hash.
434
593
  *
435
594
  * @param existingToken - optional pre-existing server-level access token to seed the cache
436
595
  * @param logAccessToConsole - when true, logs all cache reads and writes to console
437
- * @returns a CalcomOAuthAccessTokenCacheService backed by in-memory Maps
596
+ * @returns a CalcomOAuthAccessTokenCacheService backed by in-memory caches
438
597
  */ function memoryCalcomOAuthAccessTokenCacheService(existingToken, logAccessToConsole) {
439
- var serverToken = existingToken;
440
- var userTokens = new Map();
441
- function makeCache(getToken, setToken) {
598
+ var serverCache = util.inMemoryAsyncValueCache(existingToken);
599
+ var userCache = util.inMemoryAsyncKeyedValueCache();
600
+ function userCacheView(key) {
442
601
  return {
443
- loadCachedToken: function loadCachedToken() {
444
- return _async_to_generator$2(function() {
445
- var token;
446
- return _ts_generator$2(this, function(_state) {
447
- token = getToken();
448
- if (logAccessToConsole) {
449
- console.log('retrieving access token from memory: ', {
450
- token: token
451
- });
452
- }
453
- return [
454
- 2,
455
- token
456
- ];
457
- });
458
- })();
602
+ load: function load() {
603
+ return userCache.get(key);
459
604
  },
460
- updateCachedToken: function updateCachedToken(accessToken) {
461
- return _async_to_generator$2(function() {
462
- return _ts_generator$2(this, function(_state) {
463
- setToken(accessToken);
464
- if (logAccessToConsole) {
465
- console.log('updating access token in memory: ', {
466
- accessToken: accessToken
467
- });
468
- }
469
- return [
470
- 2
471
- ];
472
- });
473
- })();
605
+ update: function update(token) {
606
+ return userCache.set(key, token);
607
+ },
608
+ clear: function clear() {
609
+ return userCache.remove(key);
474
610
  }
475
611
  };
476
612
  }
477
613
  return {
478
614
  loadCalcomAccessTokenCache: function loadCalcomAccessTokenCache() {
479
- return makeCache(function() {
480
- return serverToken;
481
- }, function(t) {
482
- serverToken = t;
483
- });
615
+ return calcomAccessTokenCacheFromAsyncValueCache(serverCache, logAccessToConsole);
484
616
  },
485
617
  cacheForRefreshToken: function cacheForRefreshToken(refreshToken) {
486
618
  var key = calcomRefreshTokenCacheKey(refreshToken);
487
- return makeCache(function() {
488
- return userTokens.get(key);
489
- }, function(t) {
490
- return userTokens.set(key, t);
491
- });
619
+ return calcomAccessTokenCacheFromAsyncValueCache(userCacheView(key), logAccessToConsole);
492
620
  }
493
621
  };
494
622
  }
495
623
  // MARK: File System Access Token Cache
496
624
  var DEFAULT_FILE_CALCOM_ACCESS_TOKEN_CACHE_DIR = '.tmp/calcom-tokens';
497
625
  var CALCOM_SERVER_TOKEN_FILE_KEY = 'server';
626
+ /**
627
+ * Reviver applied to the cached file payload on load so `expiresAt` is always a `Date`
628
+ * regardless of how it was serialized.
629
+ *
630
+ * @param raw - the raw JSON-parsed file payload
631
+ * @returns the revived CalcomAccessToken, or undefined when the payload is empty/invalid
632
+ */ function reviveCalcomAccessTokenFile(raw) {
633
+ if (raw == null || (typeof raw === "undefined" ? "undefined" : _type_of(raw)) !== 'object') {
634
+ return undefined;
635
+ }
636
+ var wrapper = raw;
637
+ var token = wrapper.token;
638
+ if (token == null) {
639
+ return undefined;
640
+ }
641
+ var rawExpiresAt = token.expiresAt;
642
+ var expiresAt = rawExpiresAt != null && !_instanceof(rawExpiresAt, Date) ? new Date(rawExpiresAt) : rawExpiresAt;
643
+ return _object_spread_props$2(_object_spread$2({}, token), {
644
+ expiresAt: expiresAt
645
+ });
646
+ }
498
647
  /**
499
648
  * Creates a CalcomOAuthAccessTokenCacheService that reads and writes access tokens
500
- * to the file system. Each user gets their own file, keyed by an md5 hash of their refresh token.
649
+ * to the file system. Each user gets their own file, keyed by an sha256 hash of their refresh token.
650
+ *
651
+ * Backed by {@link createMemoizedJsonFileAsyncValueCache} for each key, so reads after
652
+ * the first hit memory.
501
653
  *
502
654
  * File structure:
503
655
  * ```
504
656
  * <cacheDir>/
505
657
  * server.json — server-level token
506
- * user-<md5hash>.json — per-user tokens (hash of initial refresh token)
658
+ * user-<sha256hash>.json — per-user tokens (hash of initial refresh token)
507
659
  * ```
508
660
  *
509
661
  * @param cacheDir Directory to store token files. Defaults to `.tmp/calcom-tokens`.
510
662
  * @returns a CalcomOAuthAccessTokenCacheService backed by the file system
511
663
  */ function fileCalcomOAuthAccessTokenCacheService() {
512
664
  var cacheDir = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : DEFAULT_FILE_CALCOM_ACCESS_TOKEN_CACHE_DIR;
513
- var memoryTokens = new Map();
514
- function filePathForKey(key) {
515
- return node_path.join(cacheDir, "".concat(key, ".json"));
516
- }
517
- function readTokenFile(filePath) {
518
- return new Promise(function(resolve) {
519
- node_fs.mkdirSync(node_path.dirname(filePath), {
520
- recursive: true
521
- });
522
- node_fs.readFile(filePath, {}, function(err, data) {
523
- var result = undefined;
524
- if (!err) {
525
- try {
526
- result = JSON.parse(data.toString());
527
- if (result === null || result === void 0 ? void 0 : result.token) {
528
- result.token.expiresAt = new Date(result.token.expiresAt);
529
- }
530
- } catch (e) {
531
- console.error("Failed reading token file ".concat(filePath, ": "), e);
532
- }
533
- }
534
- resolve(result);
535
- });
536
- });
537
- }
538
- function writeTokenFile(filePath, content) {
539
- return new Promise(function(resolve, reject) {
540
- node_fs.mkdirSync(node_path.dirname(filePath), {
541
- recursive: true
542
- });
543
- node_fs.writeFile(filePath, JSON.stringify(content, null, 2), {}, function(err) {
544
- if (!err) {
545
- resolve();
546
- } else {
547
- reject(err);
665
+ var cachesByKey = new Map();
666
+ function cacheForKey(fileKey) {
667
+ var cache = cachesByKey.get(fileKey);
668
+ if (cache == null) {
669
+ cache = nestjs.createMemoizedJsonFileAsyncValueCache({
670
+ filePath: node_path.join(cacheDir, "".concat(fileKey, ".json")),
671
+ reviver: reviveCalcomAccessTokenFile,
672
+ replacer: function replacer(token) {
673
+ return {
674
+ token: token
675
+ };
548
676
  }
549
677
  });
550
- });
678
+ cachesByKey.set(fileKey, cache);
679
+ }
680
+ return cache;
551
681
  }
552
682
  function makeCacheForKey(fileKey) {
553
- var filePath = filePathForKey(fileKey);
683
+ var cache = cacheForKey(fileKey);
554
684
  return {
555
685
  loadCachedToken: function loadCachedToken() {
556
- return _async_to_generator$2(function() {
557
- var memoryEntry, fileContent;
558
- return _ts_generator$2(this, function(_state) {
559
- switch(_state.label){
560
- case 0:
561
- // Check memory first
562
- memoryEntry = memoryTokens.get(fileKey);
563
- if (memoryEntry !== undefined) {
564
- return [
565
- 2,
566
- memoryEntry === null || memoryEntry === void 0 ? void 0 : memoryEntry.token
567
- ];
568
- }
569
- return [
570
- 4,
571
- readTokenFile(filePath)
572
- ];
573
- case 1:
574
- fileContent = _state.sent();
575
- memoryTokens.set(fileKey, fileContent !== null && fileContent !== void 0 ? fileContent : null);
576
- return [
577
- 2,
578
- fileContent === null || fileContent === void 0 ? void 0 : fileContent.token
579
- ];
580
- }
581
- });
582
- })();
686
+ return cache.load();
583
687
  },
584
688
  updateCachedToken: function updateCachedToken(accessToken) {
585
689
  return _async_to_generator$2(function() {
586
- var content, e;
690
+ var e;
587
691
  return _ts_generator$2(this, function(_state) {
588
692
  switch(_state.label){
589
693
  case 0:
590
- content = {
591
- token: accessToken
592
- };
593
- memoryTokens.set(fileKey, content);
594
- _state.label = 1;
595
- case 1:
596
694
  _state.trys.push([
597
- 1,
598
- 3,
695
+ 0,
696
+ 2,
599
697
  ,
600
- 4
698
+ 3
601
699
  ]);
602
700
  return [
603
701
  4,
604
- writeTokenFile(filePath, content)
702
+ cache.update(accessToken)
605
703
  ];
606
- case 2:
704
+ case 1:
607
705
  _state.sent();
608
706
  return [
609
707
  3,
610
- 4
708
+ 3
611
709
  ];
612
- case 3:
710
+ case 2:
613
711
  e = _state.sent();
614
- console.error("Failed updating token file ".concat(filePath, ": "), e);
615
- return [
616
- 3,
617
- 4
618
- ];
619
- case 4:
712
+ console.error("Failed updating token file for ".concat(fileKey, ": "), e);
713
+ throw e;
714
+ case 3:
620
715
  return [
621
716
  2
622
717
  ];
@@ -1,19 +1,18 @@
1
1
  import { Injectable, Inject, Logger, Post, Res, Req, Controller, Module } from '@nestjs/common';
2
2
  import { calcomOAuthFactory, exchangeAuthorizationCode, calcomFactory, getMe, getSchedules, createBooking, getBooking, cancelBooking, getEventTypes, createEventType, updateEventType, deleteEventType, getCalendars, getBusyTimes, createWebhook, getWebhooks, getWebhook, updateWebhook, deleteWebhook, getAvailableSlots } from '@dereekb/calcom';
3
- import { tryWithPromiseFactoriesFunction, isPast, filterMaybeArrayValues, cachedGetter, handlerFactory, handlerConfigurerFactory, handlerMappedSetFunctionFactory } from '@dereekb/util';
3
+ import { inMemoryAsyncValueCache, inMemoryAsyncKeyedValueCache, mergeAsyncValueCaches, isExpired, filterMaybeArrayValues, cachedGetter, handlerFactory, handlerConfigurerFactory, handlerMappedSetFunctionFactory } from '@dereekb/util';
4
+ import { createMemoizedJsonFileAsyncValueCache, RawBody } from '@dereekb/nestjs';
4
5
  import { createHash, createHmac } from 'node:crypto';
5
- import { join, dirname } from 'node:path';
6
- import { mkdirSync, writeFile, readFile } from 'node:fs';
6
+ import { join } from 'node:path';
7
7
  import { ConfigService, ConfigModule } from '@nestjs/config';
8
- import { RawBody } from '@dereekb/nestjs';
9
8
 
10
- function _type_of(obj) {
9
+ function _type_of$1(obj) {
11
10
  "@swc/helpers - typeof";
12
11
  return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
13
12
  }
14
13
  function __decorate(decorators, target, key, desc) {
15
14
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
16
- if ((typeof Reflect === "undefined" ? "undefined" : _type_of(Reflect)) === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
15
+ if ((typeof Reflect === "undefined" ? "undefined" : _type_of$1(Reflect)) === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
17
16
  else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
18
17
  return c > 3 && r && Object.defineProperty(target, key, r), r;
19
18
  }
@@ -45,7 +44,7 @@ function _create_class$6(Constructor, protoProps, staticProps) {
45
44
  if (staticProps) _defineProperties$6(Constructor, staticProps);
46
45
  return Constructor;
47
46
  }
48
- function _define_property$7(obj, key, value) {
47
+ function _define_property$8(obj, key, value) {
49
48
  if (key in obj) {
50
49
  Object.defineProperty(obj, key, {
51
50
  value: value,
@@ -68,8 +67,8 @@ var CALCOM_API_KEY_CONFIG_KEY = 'CALCOM_API_KEY';
68
67
  */ var CalcomOAuthServiceConfig = /*#__PURE__*/ function() {
69
68
  function CalcomOAuthServiceConfig() {
70
69
  _class_call_check$8(this, CalcomOAuthServiceConfig);
71
- _define_property$7(this, "calcomOAuth", void 0);
72
- _define_property$7(this, "factoryConfig", void 0);
70
+ _define_property$8(this, "calcomOAuth", void 0);
71
+ _define_property$8(this, "factoryConfig", void 0);
73
72
  }
74
73
  _create_class$6(CalcomOAuthServiceConfig, null, [
75
74
  {
@@ -156,6 +155,27 @@ function _class_call_check$7(instance, Constructor) {
156
155
  throw new TypeError("Cannot call a class as a function");
157
156
  }
158
157
  }
158
+ function _define_property$7(obj, key, value) {
159
+ if (key in obj) {
160
+ Object.defineProperty(obj, key, {
161
+ value: value,
162
+ enumerable: true,
163
+ configurable: true,
164
+ writable: true
165
+ });
166
+ } else {
167
+ obj[key] = value;
168
+ }
169
+ return obj;
170
+ }
171
+ function _instanceof(left, right) {
172
+ "@swc/helpers - instanceof";
173
+ if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
174
+ return !!right[Symbol.hasInstance](left);
175
+ } else {
176
+ return left instanceof right;
177
+ }
178
+ }
159
179
  function _iterable_to_array$3(iter) {
160
180
  if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
161
181
  }
@@ -189,12 +209,50 @@ function _non_iterable_rest() {
189
209
  function _non_iterable_spread$3() {
190
210
  throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
191
211
  }
212
+ function _object_spread$2(target) {
213
+ for(var i = 1; i < arguments.length; i++){
214
+ var source = arguments[i] != null ? arguments[i] : {};
215
+ var ownKeys = Object.keys(source);
216
+ if (typeof Object.getOwnPropertySymbols === "function") {
217
+ ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
218
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
219
+ }));
220
+ }
221
+ ownKeys.forEach(function(key) {
222
+ _define_property$7(target, key, source[key]);
223
+ });
224
+ }
225
+ return target;
226
+ }
227
+ function ownKeys$2(object, enumerableOnly) {
228
+ var keys = Object.keys(object);
229
+ if (Object.getOwnPropertySymbols) {
230
+ var symbols = Object.getOwnPropertySymbols(object);
231
+ keys.push.apply(keys, symbols);
232
+ }
233
+ return keys;
234
+ }
235
+ function _object_spread_props$2(target, source) {
236
+ source = source != null ? source : {};
237
+ if (Object.getOwnPropertyDescriptors) {
238
+ Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
239
+ } else {
240
+ ownKeys$2(Object(source)).forEach(function(key) {
241
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
242
+ });
243
+ }
244
+ return target;
245
+ }
192
246
  function _sliced_to_array(arr, i) {
193
247
  return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array$3(arr, i) || _non_iterable_rest();
194
248
  }
195
249
  function _to_consumable_array$3(arr) {
196
250
  return _array_without_holes$3(arr) || _iterable_to_array$3(arr) || _unsupported_iterable_to_array$3(arr) || _non_iterable_spread$3();
197
251
  }
252
+ function _type_of(obj) {
253
+ "@swc/helpers - typeof";
254
+ return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
255
+ }
198
256
  function _unsupported_iterable_to_array$3(o, minLen) {
199
257
  if (!o) return;
200
258
  if (typeof o === "string") return _array_like_to_array$3(o, minLen);
@@ -324,6 +382,56 @@ CalcomOAuthAccessTokenCacheService = __decorate([
324
382
  */ function calcomRefreshTokenCacheKey(refreshToken) {
325
383
  return createHash('sha256').update(refreshToken).digest('hex').substring(0, 16);
326
384
  }
385
+ // MARK: Merge
386
+ function buildCalcomReadAdapter(cache) {
387
+ return {
388
+ load: function load() {
389
+ return _async_to_generator$2(function() {
390
+ var value;
391
+ return _ts_generator$2(this, function(_state) {
392
+ switch(_state.label){
393
+ case 0:
394
+ return [
395
+ 4,
396
+ cache.loadCachedToken().catch(function() {
397
+ return undefined;
398
+ })
399
+ ];
400
+ case 1:
401
+ value = _state.sent();
402
+ return [
403
+ 2,
404
+ value != null && !isExpired(value) ? value : undefined
405
+ ];
406
+ }
407
+ });
408
+ })();
409
+ },
410
+ update: function update(token) {
411
+ return cache.updateCachedToken(token);
412
+ },
413
+ clear: function clear() {
414
+ return _async_to_generator$2(function() {
415
+ return _ts_generator$2(this, function(_state) {
416
+ return [
417
+ 2
418
+ ];
419
+ });
420
+ // CalcomAccessTokenCache does not expose a clear method.
421
+ })();
422
+ }
423
+ };
424
+ }
425
+ function updateCalcomCacheCapturingError(cache, accessToken) {
426
+ return cache.updateCachedToken(accessToken).then(function() {
427
+ return null;
428
+ }).catch(function(e) {
429
+ return [
430
+ cache,
431
+ e
432
+ ];
433
+ });
434
+ }
327
435
  /**
328
436
  * Default error logging function for {@link mergeCalcomOAuthAccessTokenCacheServices}.
329
437
  * Logs a warning for each cache that failed to update.
@@ -342,6 +450,11 @@ CalcomOAuthAccessTokenCacheService = __decorate([
342
450
  * If one source fails retrieval, the next will be tried.
343
451
  * When updating a cached token, it will update the token across all services.
344
452
  *
453
+ * Read fall-through is delegated to {@link mergeAsyncValueCaches} after wrapping each
454
+ * underlying cache with an {@link isExpired}-aware filter, so an expired cached token
455
+ * never short-circuits the lookup. Updates run across all services in parallel via
456
+ * `Promise.allSettled`, mirroring the previous behavior, with optional error logging.
457
+ *
345
458
  * @param inputServicesToMerge Must include at least one service. Empty arrays will throw an error.
346
459
  * @param logError - optional error logging configuration; pass a function, true for default logging, or false to disable
347
460
  * @returns a merged CalcomOAuthAccessTokenCacheService that delegates across all input services
@@ -351,60 +464,44 @@ CalcomOAuthAccessTokenCacheService = __decorate([
351
464
  if (allServices.length === 0) {
352
465
  throw new Error('mergeCalcomOAuthAccessTokenCacheServices() input cannot be empty.');
353
466
  }
354
- var loadCalcomAccessTokenCache = function loadCalcomAccessTokenCache(accessCachesForServices) {
355
- var loadCachedTokenFromFirstService = tryWithPromiseFactoriesFunction({
356
- promiseFactories: accessCachesForServices.map(function(x) {
357
- return function() {
358
- return x.loadCachedToken().catch(function() {
359
- return null;
360
- }).then(function(x) {
361
- var result = undefined;
362
- if (x && !isPast(x.expiresAt)) {
363
- result = x; // only return from cache if it is not expired
364
- }
365
- return result;
366
- });
367
- };
368
- }),
369
- successOnMaybe: false,
370
- throwErrors: false
371
- });
372
- var cacheForService = {
467
+ function mergeCachesForService(accessCachesForServices) {
468
+ var readAdapters = accessCachesForServices.map(buildCalcomReadAdapter);
469
+ var merged = mergeAsyncValueCaches(readAdapters);
470
+ return {
373
471
  loadCachedToken: function loadCachedToken() {
374
- return loadCachedTokenFromFirstService();
472
+ return merged.load();
375
473
  },
376
474
  updateCachedToken: function updateCachedToken(accessToken) {
377
475
  return _async_to_generator$2(function() {
476
+ var settled, failedUpdates;
378
477
  return _ts_generator$2(this, function(_state) {
379
- return [
380
- 2,
381
- Promise.allSettled(accessCachesForServices.map(function(x) {
382
- return x.updateCachedToken(accessToken).then(function() {
383
- return null;
384
- }).catch(function(e) {
385
- return [
386
- x,
387
- e
388
- ];
389
- });
390
- })).then(function(x) {
391
- // only find the failures if we're logging
478
+ switch(_state.label){
479
+ case 0:
480
+ return [
481
+ 4,
482
+ Promise.allSettled(accessCachesForServices.map(function(cache) {
483
+ return updateCalcomCacheCapturingError(cache, accessToken);
484
+ }))
485
+ ];
486
+ case 1:
487
+ settled = _state.sent();
392
488
  if (logErrorFunction != null) {
393
- var failedUpdates = filterMaybeArrayValues(x.map(function(y) {
489
+ failedUpdates = filterMaybeArrayValues(settled.map(function(y) {
394
490
  return y.value;
395
491
  }));
396
492
  if (failedUpdates.length) {
397
493
  logErrorFunction(failedUpdates);
398
494
  }
399
495
  }
400
- })
401
- ];
496
+ return [
497
+ 2
498
+ ];
499
+ }
402
500
  });
403
501
  })();
404
502
  }
405
503
  };
406
- return cacheForService;
407
- };
504
+ }
408
505
  var allServiceAccessTokenCaches = allServices.map(function(x) {
409
506
  return x.loadCalcomAccessTokenCache();
410
507
  });
@@ -415,206 +512,204 @@ CalcomOAuthAccessTokenCacheService = __decorate([
415
512
  var allCaches = allServicesWithCacheForRefreshToken.map(function(x) {
416
513
  return x.cacheForRefreshToken(refreshToken);
417
514
  });
418
- return loadCalcomAccessTokenCache(allCaches);
515
+ return mergeCachesForService(allCaches);
419
516
  } : undefined;
420
517
  var service = {
421
- loadCalcomAccessTokenCache: function loadCalcomAccessTokenCache1() {
422
- return loadCalcomAccessTokenCache(allServiceAccessTokenCaches);
518
+ loadCalcomAccessTokenCache: function loadCalcomAccessTokenCache() {
519
+ return mergeCachesForService(allServiceAccessTokenCaches);
423
520
  },
424
521
  cacheForRefreshToken: cacheForRefreshToken
425
522
  };
426
523
  return service;
427
524
  }
428
525
  // MARK: Memory Access Token Cache
526
+ /**
527
+ * Adapts an {@link AsyncValueCache} to a {@link CalcomAccessTokenCache}, optionally logging cache reads/writes to the console.
528
+ *
529
+ * @param cache - underlying single-value cache for the access token
530
+ * @param logAccessToConsole - when true, logs reads and writes to the console
531
+ * @returns the CalcomAccessTokenCache backed by the given async cache
532
+ */ function calcomAccessTokenCacheFromAsyncValueCache(cache, logAccessToConsole) {
533
+ return {
534
+ loadCachedToken: function loadCachedToken() {
535
+ return _async_to_generator$2(function() {
536
+ var token;
537
+ return _ts_generator$2(this, function(_state) {
538
+ switch(_state.label){
539
+ case 0:
540
+ return [
541
+ 4,
542
+ cache.load()
543
+ ];
544
+ case 1:
545
+ token = _state.sent();
546
+ if (logAccessToConsole) {
547
+ console.log('retrieving access token from memory: ', {
548
+ hit: token != null,
549
+ expiresAt: token === null || token === void 0 ? void 0 : token.expiresAt
550
+ });
551
+ }
552
+ return [
553
+ 2,
554
+ token
555
+ ];
556
+ }
557
+ });
558
+ })();
559
+ },
560
+ updateCachedToken: function updateCachedToken(accessToken) {
561
+ return _async_to_generator$2(function() {
562
+ return _ts_generator$2(this, function(_state) {
563
+ switch(_state.label){
564
+ case 0:
565
+ return [
566
+ 4,
567
+ cache.update(accessToken)
568
+ ];
569
+ case 1:
570
+ _state.sent();
571
+ if (logAccessToConsole) {
572
+ console.log('updating access token in memory: ', {
573
+ expiresAt: accessToken === null || accessToken === void 0 ? void 0 : accessToken.expiresAt
574
+ });
575
+ }
576
+ return [
577
+ 2
578
+ ];
579
+ }
580
+ });
581
+ })();
582
+ }
583
+ };
584
+ }
429
585
  /**
430
586
  * Creates a CalcomOAuthAccessTokenCacheService that uses in-memory storage.
431
- * Per-user caches are stored in a Map keyed by the md5 hash of the refresh token.
587
+ *
588
+ * The server-level token is held in a single-slot {@link inMemoryAsyncValueCache};
589
+ * per-user tokens are held in an {@link inMemoryAsyncKeyedValueCache} keyed by the
590
+ * sha256-truncated refresh token hash.
432
591
  *
433
592
  * @param existingToken - optional pre-existing server-level access token to seed the cache
434
593
  * @param logAccessToConsole - when true, logs all cache reads and writes to console
435
- * @returns a CalcomOAuthAccessTokenCacheService backed by in-memory Maps
594
+ * @returns a CalcomOAuthAccessTokenCacheService backed by in-memory caches
436
595
  */ function memoryCalcomOAuthAccessTokenCacheService(existingToken, logAccessToConsole) {
437
- var serverToken = existingToken;
438
- var userTokens = new Map();
439
- function makeCache(getToken, setToken) {
596
+ var serverCache = inMemoryAsyncValueCache(existingToken);
597
+ var userCache = inMemoryAsyncKeyedValueCache();
598
+ function userCacheView(key) {
440
599
  return {
441
- loadCachedToken: function loadCachedToken() {
442
- return _async_to_generator$2(function() {
443
- var token;
444
- return _ts_generator$2(this, function(_state) {
445
- token = getToken();
446
- if (logAccessToConsole) {
447
- console.log('retrieving access token from memory: ', {
448
- token: token
449
- });
450
- }
451
- return [
452
- 2,
453
- token
454
- ];
455
- });
456
- })();
600
+ load: function load() {
601
+ return userCache.get(key);
457
602
  },
458
- updateCachedToken: function updateCachedToken(accessToken) {
459
- return _async_to_generator$2(function() {
460
- return _ts_generator$2(this, function(_state) {
461
- setToken(accessToken);
462
- if (logAccessToConsole) {
463
- console.log('updating access token in memory: ', {
464
- accessToken: accessToken
465
- });
466
- }
467
- return [
468
- 2
469
- ];
470
- });
471
- })();
603
+ update: function update(token) {
604
+ return userCache.set(key, token);
605
+ },
606
+ clear: function clear() {
607
+ return userCache.remove(key);
472
608
  }
473
609
  };
474
610
  }
475
611
  return {
476
612
  loadCalcomAccessTokenCache: function loadCalcomAccessTokenCache() {
477
- return makeCache(function() {
478
- return serverToken;
479
- }, function(t) {
480
- serverToken = t;
481
- });
613
+ return calcomAccessTokenCacheFromAsyncValueCache(serverCache, logAccessToConsole);
482
614
  },
483
615
  cacheForRefreshToken: function cacheForRefreshToken(refreshToken) {
484
616
  var key = calcomRefreshTokenCacheKey(refreshToken);
485
- return makeCache(function() {
486
- return userTokens.get(key);
487
- }, function(t) {
488
- return userTokens.set(key, t);
489
- });
617
+ return calcomAccessTokenCacheFromAsyncValueCache(userCacheView(key), logAccessToConsole);
490
618
  }
491
619
  };
492
620
  }
493
621
  // MARK: File System Access Token Cache
494
622
  var DEFAULT_FILE_CALCOM_ACCESS_TOKEN_CACHE_DIR = '.tmp/calcom-tokens';
495
623
  var CALCOM_SERVER_TOKEN_FILE_KEY = 'server';
624
+ /**
625
+ * Reviver applied to the cached file payload on load so `expiresAt` is always a `Date`
626
+ * regardless of how it was serialized.
627
+ *
628
+ * @param raw - the raw JSON-parsed file payload
629
+ * @returns the revived CalcomAccessToken, or undefined when the payload is empty/invalid
630
+ */ function reviveCalcomAccessTokenFile(raw) {
631
+ if (raw == null || (typeof raw === "undefined" ? "undefined" : _type_of(raw)) !== 'object') {
632
+ return undefined;
633
+ }
634
+ var wrapper = raw;
635
+ var token = wrapper.token;
636
+ if (token == null) {
637
+ return undefined;
638
+ }
639
+ var rawExpiresAt = token.expiresAt;
640
+ var expiresAt = rawExpiresAt != null && !_instanceof(rawExpiresAt, Date) ? new Date(rawExpiresAt) : rawExpiresAt;
641
+ return _object_spread_props$2(_object_spread$2({}, token), {
642
+ expiresAt: expiresAt
643
+ });
644
+ }
496
645
  /**
497
646
  * Creates a CalcomOAuthAccessTokenCacheService that reads and writes access tokens
498
- * to the file system. Each user gets their own file, keyed by an md5 hash of their refresh token.
647
+ * to the file system. Each user gets their own file, keyed by an sha256 hash of their refresh token.
648
+ *
649
+ * Backed by {@link createMemoizedJsonFileAsyncValueCache} for each key, so reads after
650
+ * the first hit memory.
499
651
  *
500
652
  * File structure:
501
653
  * ```
502
654
  * <cacheDir>/
503
655
  * server.json — server-level token
504
- * user-<md5hash>.json — per-user tokens (hash of initial refresh token)
656
+ * user-<sha256hash>.json — per-user tokens (hash of initial refresh token)
505
657
  * ```
506
658
  *
507
659
  * @param cacheDir Directory to store token files. Defaults to `.tmp/calcom-tokens`.
508
660
  * @returns a CalcomOAuthAccessTokenCacheService backed by the file system
509
661
  */ function fileCalcomOAuthAccessTokenCacheService() {
510
662
  var cacheDir = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : DEFAULT_FILE_CALCOM_ACCESS_TOKEN_CACHE_DIR;
511
- var memoryTokens = new Map();
512
- function filePathForKey(key) {
513
- return join(cacheDir, "".concat(key, ".json"));
514
- }
515
- function readTokenFile(filePath) {
516
- return new Promise(function(resolve) {
517
- mkdirSync(dirname(filePath), {
518
- recursive: true
519
- });
520
- readFile(filePath, {}, function(err, data) {
521
- var result = undefined;
522
- if (!err) {
523
- try {
524
- result = JSON.parse(data.toString());
525
- if (result === null || result === void 0 ? void 0 : result.token) {
526
- result.token.expiresAt = new Date(result.token.expiresAt);
527
- }
528
- } catch (e) {
529
- console.error("Failed reading token file ".concat(filePath, ": "), e);
530
- }
531
- }
532
- resolve(result);
533
- });
534
- });
535
- }
536
- function writeTokenFile(filePath, content) {
537
- return new Promise(function(resolve, reject) {
538
- mkdirSync(dirname(filePath), {
539
- recursive: true
540
- });
541
- writeFile(filePath, JSON.stringify(content, null, 2), {}, function(err) {
542
- if (!err) {
543
- resolve();
544
- } else {
545
- reject(err);
663
+ var cachesByKey = new Map();
664
+ function cacheForKey(fileKey) {
665
+ var cache = cachesByKey.get(fileKey);
666
+ if (cache == null) {
667
+ cache = createMemoizedJsonFileAsyncValueCache({
668
+ filePath: join(cacheDir, "".concat(fileKey, ".json")),
669
+ reviver: reviveCalcomAccessTokenFile,
670
+ replacer: function replacer(token) {
671
+ return {
672
+ token: token
673
+ };
546
674
  }
547
675
  });
548
- });
676
+ cachesByKey.set(fileKey, cache);
677
+ }
678
+ return cache;
549
679
  }
550
680
  function makeCacheForKey(fileKey) {
551
- var filePath = filePathForKey(fileKey);
681
+ var cache = cacheForKey(fileKey);
552
682
  return {
553
683
  loadCachedToken: function loadCachedToken() {
554
- return _async_to_generator$2(function() {
555
- var memoryEntry, fileContent;
556
- return _ts_generator$2(this, function(_state) {
557
- switch(_state.label){
558
- case 0:
559
- // Check memory first
560
- memoryEntry = memoryTokens.get(fileKey);
561
- if (memoryEntry !== undefined) {
562
- return [
563
- 2,
564
- memoryEntry === null || memoryEntry === void 0 ? void 0 : memoryEntry.token
565
- ];
566
- }
567
- return [
568
- 4,
569
- readTokenFile(filePath)
570
- ];
571
- case 1:
572
- fileContent = _state.sent();
573
- memoryTokens.set(fileKey, fileContent !== null && fileContent !== void 0 ? fileContent : null);
574
- return [
575
- 2,
576
- fileContent === null || fileContent === void 0 ? void 0 : fileContent.token
577
- ];
578
- }
579
- });
580
- })();
684
+ return cache.load();
581
685
  },
582
686
  updateCachedToken: function updateCachedToken(accessToken) {
583
687
  return _async_to_generator$2(function() {
584
- var content, e;
688
+ var e;
585
689
  return _ts_generator$2(this, function(_state) {
586
690
  switch(_state.label){
587
691
  case 0:
588
- content = {
589
- token: accessToken
590
- };
591
- memoryTokens.set(fileKey, content);
592
- _state.label = 1;
593
- case 1:
594
692
  _state.trys.push([
595
- 1,
596
- 3,
693
+ 0,
694
+ 2,
597
695
  ,
598
- 4
696
+ 3
599
697
  ]);
600
698
  return [
601
699
  4,
602
- writeTokenFile(filePath, content)
700
+ cache.update(accessToken)
603
701
  ];
604
- case 2:
702
+ case 1:
605
703
  _state.sent();
606
704
  return [
607
705
  3,
608
- 4
706
+ 3
609
707
  ];
610
- case 3:
708
+ case 2:
611
709
  e = _state.sent();
612
- console.error("Failed updating token file ".concat(filePath, ": "), e);
613
- return [
614
- 3,
615
- 4
616
- ];
617
- case 4:
710
+ console.error("Failed updating token file for ".concat(fileKey, ": "), e);
711
+ throw e;
712
+ case 3:
618
713
  return [
619
714
  2
620
715
  ];
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@dereekb/calcom/nestjs",
3
- "version": "13.10.9",
3
+ "version": "13.11.1",
4
4
  "peerDependencies": {
5
- "@dereekb/nestjs": "13.10.9",
6
- "@dereekb/rxjs": "13.10.9",
7
- "@dereekb/util": "13.10.9",
8
- "@dereekb/calcom": "13.10.9",
5
+ "@dereekb/nestjs": "13.11.1",
6
+ "@dereekb/rxjs": "13.11.1",
7
+ "@dereekb/util": "13.11.1",
8
+ "@dereekb/calcom": "13.11.1",
9
9
  "@nestjs/common": "^11.1.19",
10
10
  "@nestjs/config": "^4.0.4",
11
11
  "express": "^5.2.1"
@@ -45,6 +45,11 @@ export declare function logMergeCalcomOAuthAccessTokenCacheServiceErrorFunction(
45
45
  * If one source fails retrieval, the next will be tried.
46
46
  * When updating a cached token, it will update the token across all services.
47
47
  *
48
+ * Read fall-through is delegated to {@link mergeAsyncValueCaches} after wrapping each
49
+ * underlying cache with an {@link isExpired}-aware filter, so an expired cached token
50
+ * never short-circuits the lookup. Updates run across all services in parallel via
51
+ * `Promise.allSettled`, mirroring the previous behavior, with optional error logging.
52
+ *
48
53
  * @param inputServicesToMerge Must include at least one service. Empty arrays will throw an error.
49
54
  * @param logError - optional error logging configuration; pass a function, true for default logging, or false to disable
50
55
  * @returns a merged CalcomOAuthAccessTokenCacheService that delegates across all input services
@@ -52,11 +57,14 @@ export declare function logMergeCalcomOAuthAccessTokenCacheServiceErrorFunction(
52
57
  export declare function mergeCalcomOAuthAccessTokenCacheServices(inputServicesToMerge: CalcomOAuthAccessTokenCacheService[], logError?: Maybe<boolean | LogMergeCalcomOAuthAccessTokenCacheServiceErrorFunction>): CalcomOAuthAccessTokenCacheService;
53
58
  /**
54
59
  * Creates a CalcomOAuthAccessTokenCacheService that uses in-memory storage.
55
- * Per-user caches are stored in a Map keyed by the md5 hash of the refresh token.
60
+ *
61
+ * The server-level token is held in a single-slot {@link inMemoryAsyncValueCache};
62
+ * per-user tokens are held in an {@link inMemoryAsyncKeyedValueCache} keyed by the
63
+ * sha256-truncated refresh token hash.
56
64
  *
57
65
  * @param existingToken - optional pre-existing server-level access token to seed the cache
58
66
  * @param logAccessToConsole - when true, logs all cache reads and writes to console
59
- * @returns a CalcomOAuthAccessTokenCacheService backed by in-memory Maps
67
+ * @returns a CalcomOAuthAccessTokenCacheService backed by in-memory caches
60
68
  */
61
69
  export declare function memoryCalcomOAuthAccessTokenCacheService(existingToken?: Maybe<CalcomAccessToken>, logAccessToConsole?: boolean): CalcomOAuthAccessTokenCacheService;
62
70
  export declare const DEFAULT_FILE_CALCOM_ACCESS_TOKEN_CACHE_DIR = ".tmp/calcom-tokens";
@@ -69,13 +77,16 @@ export interface FileSystemCalcomOAuthAccessTokenCacheService extends CalcomOAut
69
77
  }
70
78
  /**
71
79
  * Creates a CalcomOAuthAccessTokenCacheService that reads and writes access tokens
72
- * to the file system. Each user gets their own file, keyed by an md5 hash of their refresh token.
80
+ * to the file system. Each user gets their own file, keyed by an sha256 hash of their refresh token.
81
+ *
82
+ * Backed by {@link createMemoizedJsonFileAsyncValueCache} for each key, so reads after
83
+ * the first hit memory.
73
84
  *
74
85
  * File structure:
75
86
  * ```
76
87
  * <cacheDir>/
77
88
  * server.json — server-level token
78
- * user-<md5hash>.json — per-user tokens (hash of initial refresh token)
89
+ * user-<sha256hash>.json — per-user tokens (hash of initial refresh token)
79
90
  * ```
80
91
  *
81
92
  * @param cacheDir Directory to store token files. Defaults to `.tmp/calcom-tokens`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dereekb/calcom",
3
- "version": "13.10.9",
3
+ "version": "13.11.1",
4
4
  "exports": {
5
5
  "./nestjs": {
6
6
  "module": "./nestjs/index.esm.js",
@@ -17,9 +17,9 @@
17
17
  }
18
18
  },
19
19
  "peerDependencies": {
20
- "@dereekb/nestjs": "13.10.9",
21
- "@dereekb/rxjs": "13.10.9",
22
- "@dereekb/util": "13.10.9",
20
+ "@dereekb/nestjs": "13.11.1",
21
+ "@dereekb/rxjs": "13.11.1",
22
+ "@dereekb/util": "13.11.1",
23
23
  "@nestjs/common": "^11.1.19",
24
24
  "@nestjs/config": "^4.0.4",
25
25
  "express": "^5.2.1",