caching.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.makeStrongCache = makeStrongCache;
  6. exports.makeWeakCache = makeWeakCache;
  7. exports.assertSimpleType = assertSimpleType;
  8. function makeStrongCache(handler) {
  9. return makeCachedFunction(new Map(), handler);
  10. }
  11. function makeWeakCache(handler) {
  12. return makeCachedFunction(new WeakMap(), handler);
  13. }
  14. function makeCachedFunction(callCache, handler) {
  15. return function cachedFunction(arg, data) {
  16. let cachedValue = callCache.get(arg);
  17. if (cachedValue) {
  18. for (const _ref of cachedValue) {
  19. const {
  20. value,
  21. valid
  22. } = _ref;
  23. if (valid(data)) return value;
  24. }
  25. }
  26. const cache = new CacheConfigurator(data);
  27. const value = handler(arg, cache);
  28. if (!cache.configured()) cache.forever();
  29. cache.deactivate();
  30. switch (cache.mode()) {
  31. case "forever":
  32. cachedValue = [{
  33. value,
  34. valid: () => true
  35. }];
  36. callCache.set(arg, cachedValue);
  37. break;
  38. case "invalidate":
  39. cachedValue = [{
  40. value,
  41. valid: cache.validator()
  42. }];
  43. callCache.set(arg, cachedValue);
  44. break;
  45. case "valid":
  46. if (cachedValue) {
  47. cachedValue.push({
  48. value,
  49. valid: cache.validator()
  50. });
  51. } else {
  52. cachedValue = [{
  53. value,
  54. valid: cache.validator()
  55. }];
  56. callCache.set(arg, cachedValue);
  57. }
  58. }
  59. return value;
  60. };
  61. }
  62. class CacheConfigurator {
  63. constructor(data) {
  64. this._active = true;
  65. this._never = false;
  66. this._forever = false;
  67. this._invalidate = false;
  68. this._configured = false;
  69. this._pairs = [];
  70. this._data = data;
  71. }
  72. simple() {
  73. return makeSimpleConfigurator(this);
  74. }
  75. mode() {
  76. if (this._never) return "never";
  77. if (this._forever) return "forever";
  78. if (this._invalidate) return "invalidate";
  79. return "valid";
  80. }
  81. forever() {
  82. if (!this._active) {
  83. throw new Error("Cannot change caching after evaluation has completed.");
  84. }
  85. if (this._never) {
  86. throw new Error("Caching has already been configured with .never()");
  87. }
  88. this._forever = true;
  89. this._configured = true;
  90. }
  91. never() {
  92. if (!this._active) {
  93. throw new Error("Cannot change caching after evaluation has completed.");
  94. }
  95. if (this._forever) {
  96. throw new Error("Caching has already been configured with .forever()");
  97. }
  98. this._never = true;
  99. this._configured = true;
  100. }
  101. using(handler) {
  102. if (!this._active) {
  103. throw new Error("Cannot change caching after evaluation has completed.");
  104. }
  105. if (this._never || this._forever) {
  106. throw new Error("Caching has already been configured with .never or .forever()");
  107. }
  108. this._configured = true;
  109. const key = handler(this._data);
  110. this._pairs.push([key, handler]);
  111. return key;
  112. }
  113. invalidate(handler) {
  114. if (!this._active) {
  115. throw new Error("Cannot change caching after evaluation has completed.");
  116. }
  117. if (this._never || this._forever) {
  118. throw new Error("Caching has already been configured with .never or .forever()");
  119. }
  120. this._invalidate = true;
  121. this._configured = true;
  122. const key = handler(this._data);
  123. this._pairs.push([key, handler]);
  124. return key;
  125. }
  126. validator() {
  127. const pairs = this._pairs;
  128. return data => pairs.every(([key, fn]) => key === fn(data));
  129. }
  130. deactivate() {
  131. this._active = false;
  132. }
  133. configured() {
  134. return this._configured;
  135. }
  136. }
  137. function makeSimpleConfigurator(cache) {
  138. function cacheFn(val) {
  139. if (typeof val === "boolean") {
  140. if (val) cache.forever();else cache.never();
  141. return;
  142. }
  143. return cache.using(() => assertSimpleType(val()));
  144. }
  145. cacheFn.forever = () => cache.forever();
  146. cacheFn.never = () => cache.never();
  147. cacheFn.using = cb => cache.using(() => assertSimpleType(cb()));
  148. cacheFn.invalidate = cb => cache.invalidate(() => assertSimpleType(cb()));
  149. return cacheFn;
  150. }
  151. function assertSimpleType(value) {
  152. if (value != null && typeof value !== "string" && typeof value !== "boolean" && typeof value !== "number") {
  153. throw new Error("Cache keys must be either string, boolean, number, null, or undefined.");
  154. }
  155. return value;
  156. }