performance-helpers 1.0.0 → 1.0.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.
Files changed (287) hide show
  1. package/README.md +12 -2
  2. package/package.json +146 -1
  3. package/src/index.js +1 -0
  4. package/.eslintrc.cjs +0 -22
  5. package/.nojekyll +0 -0
  6. package/.prettierrc +0 -6
  7. package/CONTRIBUTING.md +0 -178
  8. package/assets/1_Caching.md +0 -4
  9. package/assets/2_Parallelizing.md +0 -18
  10. package/assets/3_Logging.md +0 -3
  11. package/assets/404.md +0 -3
  12. package/assets/4_Utils.md +0 -10
  13. package/assets/logo.png +0 -0
  14. package/assets/navigation.md +0 -10
  15. package/bench/README.md +0 -97
  16. package/bench/results.json +0 -94
  17. package/bench/results.md +0 -233
  18. package/bench/run.js +0 -2639
  19. package/bench/worker.js +0 -43
  20. package/docs/README.md +0 -38
  21. package/docs/docs-typedoc.json +0 -38714
  22. package/docs/helpers/constants/README.md +0 -34
  23. package/docs/helpers/constants/variables/DEFAULT_AUTOSCALE_BACKOFF_MAX_MULTIPLIER.md +0 -9
  24. package/docs/helpers/constants/variables/DEFAULT_AUTOSCALE_COOLDOWN_MS.md +0 -9
  25. package/docs/helpers/constants/variables/DEFAULT_AUTOSCALE_INTERVAL_MS.md +0 -9
  26. package/docs/helpers/constants/variables/DEFAULT_AUTOSCALE_MIN_INTERVAL_MS.md +0 -9
  27. package/docs/helpers/constants/variables/DEFAULT_BACKPRESSURE_QUEUE_CAPACITY.md +0 -9
  28. package/docs/helpers/constants/variables/DEFAULT_BACKPRESSURE_REFILL_INTERVAL_MS.md +0 -9
  29. package/docs/helpers/constants/variables/DEFAULT_BATCH_MAX_SIZE.md +0 -9
  30. package/docs/helpers/constants/variables/DEFAULT_CACHE_DEFAULT_TTL_MS.md +0 -9
  31. package/docs/helpers/constants/variables/DEFAULT_CACHE_MAX_POOL_SIZE.md +0 -9
  32. package/docs/helpers/constants/variables/DEFAULT_CACHE_MAX_WEIGHT_BYTES.md +0 -9
  33. package/docs/helpers/constants/variables/DEFAULT_HISTOGRAM_BUCKET_COUNT.md +0 -9
  34. package/docs/helpers/constants/variables/DEFAULT_HISTOGRAM_MAX_VALUE.md +0 -9
  35. package/docs/helpers/constants/variables/DEFAULT_MAX_CLEANUP_PER_TICK.md +0 -9
  36. package/docs/helpers/constants/variables/DEFAULT_QUEUE_CAPACITY.md +0 -9
  37. package/docs/helpers/constants/variables/DEFAULT_REAPER_MIN_INTERVAL_MS.md +0 -9
  38. package/docs/helpers/constants/variables/DEFAULT_REFILL_INTERVAL_MS.md +0 -9
  39. package/docs/helpers/constants/variables/DEFAULT_RETRY_BASE_DELAY_MS.md +0 -9
  40. package/docs/helpers/constants/variables/DEFAULT_RETRY_MAX_DELAY_MS.md +0 -9
  41. package/docs/helpers/constants/variables/DEFAULT_TIMEOUT_MS.md +0 -9
  42. package/docs/helpers/constants/variables/ENCODE_CACHE_LARGE_KEY_LENGTH.md +0 -9
  43. package/docs/helpers/constants/variables/MAX_DEEP_EQUAL_DEPTH.md +0 -9
  44. package/docs/helpers/constants/variables/MS_PER_MIN.md +0 -9
  45. package/docs/helpers/constants/variables/MS_PER_SEC.md +0 -9
  46. package/docs/helpers/constants/variables/default.md +0 -103
  47. package/docs/helpers/jsdoc-types/README.md +0 -33
  48. package/docs/helpers/jsdoc-types/interfaces/BufferDecoder.md +0 -23
  49. package/docs/helpers/jsdoc-types/interfaces/BufferEncoder.md +0 -23
  50. package/docs/helpers/jsdoc-types/interfaces/CacheNode.md +0 -43
  51. package/docs/helpers/jsdoc-types/interfaces/CommonPoolOptions.md +0 -31
  52. package/docs/helpers/jsdoc-types/interfaces/PendingResponseEntry.md +0 -51
  53. package/docs/helpers/jsdoc-types/interfaces/PostMessageOptions.md +0 -39
  54. package/docs/helpers/jsdoc-types/interfaces/PowerBatchOptions.md +0 -13
  55. package/docs/helpers/jsdoc-types/interfaces/PowerCacheOptions.md +0 -115
  56. package/docs/helpers/jsdoc-types/interfaces/PowerChunkingOptions.md +0 -31
  57. package/docs/helpers/jsdoc-types/interfaces/PowerCircuitOptions.md +0 -45
  58. package/docs/helpers/jsdoc-types/interfaces/PowerDeadlineOptions.md +0 -101
  59. package/docs/helpers/jsdoc-types/interfaces/PowerDeferOptions.md +0 -13
  60. package/docs/helpers/jsdoc-types/interfaces/PowerEventBusOptions.md +0 -19
  61. package/docs/helpers/jsdoc-types/interfaces/PowerLatchOptions.md +0 -23
  62. package/docs/helpers/jsdoc-types/interfaces/PowerLoggerOptions.md +0 -51
  63. package/docs/helpers/jsdoc-types/interfaces/PowerObserverOptions.md +0 -25
  64. package/docs/helpers/jsdoc-types/interfaces/PowerPoolOptions.md +0 -85
  65. package/docs/helpers/jsdoc-types/interfaces/PowerQueueOptions.md +0 -13
  66. package/docs/helpers/jsdoc-types/interfaces/PowerRetryOptions.md +0 -83
  67. package/docs/helpers/jsdoc-types/interfaces/PowerSlidingWindowOptions.md +0 -19
  68. package/docs/helpers/jsdoc-types/interfaces/PowerTTLMapOptions.md +0 -27
  69. package/docs/helpers/jsdoc-types/interfaces/PowerThrottleOptions.md +0 -31
  70. package/docs/helpers/jsdoc-types/interfaces/WorkerObj.md +0 -55
  71. package/docs/helpers/powerBackpressure/README.md +0 -17
  72. package/docs/helpers/powerBackpressure/classes/PowerBackpressure.md +0 -368
  73. package/docs/helpers/powerBatch/README.md +0 -17
  74. package/docs/helpers/powerBatch/classes/PowerBatch.md +0 -139
  75. package/docs/helpers/powerBuffer/README.md +0 -26
  76. package/docs/helpers/powerBuffer/functions/b2o.md +0 -25
  77. package/docs/helpers/powerBuffer/functions/o2b.md +0 -23
  78. package/docs/helpers/powerBuffer/functions/o2u8.md +0 -33
  79. package/docs/helpers/powerBuffer/functions/u82o.md +0 -30
  80. package/docs/helpers/powerBulkhead/README.md +0 -17
  81. package/docs/helpers/powerBulkhead/classes/PowerBulkhead.md +0 -302
  82. package/docs/helpers/powerCache/README.md +0 -29
  83. package/docs/helpers/powerCache/classes/PowerCache.md +0 -933
  84. package/docs/helpers/powerCache/classes/PowerMemoizer.md +0 -244
  85. package/docs/helpers/powerCache/classes/PowerTimedCache.md +0 -302
  86. package/docs/helpers/powerCache/functions/simpleArgsKey.md +0 -31
  87. package/docs/helpers/powerChunking/README.md +0 -17
  88. package/docs/helpers/powerChunking/classes/PowerChunker.md +0 -78
  89. package/docs/helpers/powerCircuit/README.md +0 -23
  90. package/docs/helpers/powerCircuit/classes/PowerCircuit.md +0 -167
  91. package/docs/helpers/powerDeadline/README.md +0 -23
  92. package/docs/helpers/powerDeadline/classes/PowerDeadline.md +0 -88
  93. package/docs/helpers/powerDefer/README.md +0 -17
  94. package/docs/helpers/powerDefer/classes/PowerDefer.md +0 -134
  95. package/docs/helpers/powerEventBus/README.md +0 -23
  96. package/docs/helpers/powerEventBus/classes/PowerEventBus.md +0 -330
  97. package/docs/helpers/powerHistogram/README.md +0 -17
  98. package/docs/helpers/powerHistogram/classes/PowerHistogram.md +0 -285
  99. package/docs/helpers/powerLatch/README.md +0 -17
  100. package/docs/helpers/powerLatch/classes/PowerLatch.md +0 -264
  101. package/docs/helpers/powerLogger/README.md +0 -17
  102. package/docs/helpers/powerLogger/classes/PowerLogger.md +0 -290
  103. package/docs/helpers/powerObserver/README.md +0 -23
  104. package/docs/helpers/powerObserver/classes/PowerObserver.md +0 -213
  105. package/docs/helpers/powerPermitGate/README.md +0 -11
  106. package/docs/helpers/powerPermitGate/classes/PowerPermitGate.md +0 -248
  107. package/docs/helpers/powerPool/README.md +0 -36
  108. package/docs/helpers/powerPool/classes/PowerPool.md +0 -973
  109. package/docs/helpers/powerPool/classes/PowerPoolShutdownError.md +0 -67
  110. package/docs/helpers/powerQueue/README.md +0 -11
  111. package/docs/helpers/powerQueue/classes/PowerQueue.md +0 -302
  112. package/docs/helpers/powerRateLimit/README.md +0 -17
  113. package/docs/helpers/powerRateLimit/classes/PowerRateLimit.md +0 -187
  114. package/docs/helpers/powerRetry/README.md +0 -23
  115. package/docs/helpers/powerRetry/classes/PowerRetry.md +0 -106
  116. package/docs/helpers/powerScheduler/README.md +0 -11
  117. package/docs/helpers/powerScheduler/classes/PowerScheduler.md +0 -135
  118. package/docs/helpers/powerSemaphore/README.md +0 -17
  119. package/docs/helpers/powerSemaphore/classes/PowerSemaphore.md +0 -173
  120. package/docs/helpers/powerSlidingWindow/README.md +0 -11
  121. package/docs/helpers/powerSlidingWindow/classes/PowerSlidingWindow.md +0 -83
  122. package/docs/helpers/powerSubscriberSet/README.md +0 -15
  123. package/docs/helpers/powerSubscriberSet/classes/PowerSubscriberSet.md +0 -251
  124. package/docs/helpers/powerSubscriberSet/functions/cleanupWeakRefs.md +0 -21
  125. package/docs/helpers/powerTTLMap/README.md +0 -17
  126. package/docs/helpers/powerTTLMap/classes/PowerTTLMap.md +0 -326
  127. package/docs/helpers/powerThrottle/README.md +0 -17
  128. package/docs/helpers/powerThrottle/classes/PowerThrottle.md +0 -216
  129. package/docs/index/README.md +0 -205
  130. package/docs/utils/errors/README.md +0 -12
  131. package/docs/utils/errors/functions/formatErrorObj.md +0 -30
  132. package/docs/utils/errors/functions/normalizeError.md +0 -50
  133. package/docs/utils/now/README.md +0 -19
  134. package/docs/utils/now/functions/measureAsync.md +0 -37
  135. package/docs/utils/now/functions/measureSync.md +0 -54
  136. package/docs/utils/now/functions/nowMs.md +0 -24
  137. package/guides/autoscale.md +0 -80
  138. package/guides/errors.md +0 -41
  139. package/guides/metaGuide.md +0 -440
  140. package/guides/now.md +0 -56
  141. package/guides/powerBackpressure.md +0 -110
  142. package/guides/powerBatch.md +0 -82
  143. package/guides/powerBuffer.md +0 -86
  144. package/guides/powerBulkhead.md +0 -61
  145. package/guides/powerCache.md +0 -269
  146. package/guides/powerChunking.md +0 -130
  147. package/guides/powerCircuit.md +0 -84
  148. package/guides/powerDeadline.md +0 -99
  149. package/guides/powerDefer.md +0 -56
  150. package/guides/powerEventBus.md +0 -89
  151. package/guides/powerHistogram.md +0 -71
  152. package/guides/powerLatch.md +0 -94
  153. package/guides/powerLogger.md +0 -129
  154. package/guides/powerObserver.md +0 -65
  155. package/guides/powerPermitGate.md +0 -52
  156. package/guides/powerPool.md +0 -321
  157. package/guides/powerQueue.md +0 -112
  158. package/guides/powerRateLimit.md +0 -37
  159. package/guides/powerRetry.md +0 -54
  160. package/guides/powerScheduler.md +0 -41
  161. package/guides/powerSemaphore.md +0 -65
  162. package/guides/powerSlidingWindow.md +0 -63
  163. package/guides/powerSubscriberSet.md +0 -48
  164. package/guides/powerTTLMap.md +0 -58
  165. package/guides/powerThrottle.md +0 -152
  166. package/index.html +0 -57
  167. package/results.json +0 -6692
  168. package/scripts/find-missing-jsdoc.js +0 -62
  169. package/scripts/modernize-optional-chaining.cjs +0 -36
  170. package/scripts/pool-debug.mjs +0 -29
  171. package/scripts/repro_powercache.js +0 -14
  172. package/scripts/static-audit-exports.cjs +0 -93
  173. package/scripts/static-audit-exports.json +0 -518
  174. package/test/powerBackpressure.test.js +0 -114
  175. package/test/powerBatch.branches.extra.test.js +0 -122
  176. package/test/powerBatch.test.js +0 -79
  177. package/test/powerBuffer.test.js +0 -125
  178. package/test/powerBulkhead.test.js +0 -210
  179. package/test/powerCache.branches.test.js +0 -233
  180. package/test/powerCache.bulk.test.js +0 -31
  181. package/test/powerCache.getorset.test.js +0 -110
  182. package/test/powerCache.hitRate.test.js +0 -35
  183. package/test/powerCache.inflight.test.js +0 -24
  184. package/test/powerCache.iterator.test.js +0 -18
  185. package/test/powerCache.misses.test.js +0 -52
  186. package/test/powerCache.more.test.js +0 -118
  187. package/test/powerCache.test.js +0 -37
  188. package/test/powerCache.timeout.test.js +0 -25
  189. package/test/powerCache.touch.test.js +0 -46
  190. package/test/powerChunking.branches.extra.test.js +0 -155
  191. package/test/powerChunking.errors.test.js +0 -177
  192. package/test/powerChunking.test.js +0 -39
  193. package/test/powerCircuit.observability.test.js +0 -71
  194. package/test/powerCircuit.test.js +0 -74
  195. package/test/powerDeadline.test.js +0 -140
  196. package/test/powerDefer.test.js +0 -55
  197. package/test/powerErrors.test.js +0 -32
  198. package/test/powerEventBus.branches.extra.test.js +0 -70
  199. package/test/powerEventBus.extra.test.js +0 -72
  200. package/test/powerEventBus.max.test.js +0 -43
  201. package/test/powerEventBus.more.test.js +0 -121
  202. package/test/powerEventBus.once_off.test.js +0 -17
  203. package/test/powerEventBus.test.js +0 -74
  204. package/test/powerEventBus.uncovered.test.js +0 -57
  205. package/test/powerEventBus.weak.test.js +0 -18
  206. package/test/powerHistogram.test.js +0 -73
  207. package/test/powerLatch.branches.extra.test.js +0 -115
  208. package/test/powerLatch.test.js +0 -57
  209. package/test/powerLogger.branches.test.js +0 -98
  210. package/test/powerLogger.formatter.name.test.js +0 -58
  211. package/test/powerLogger.json.test.js +0 -88
  212. package/test/powerLogger.output.test.js +0 -81
  213. package/test/powerLogger.table.debug.test.js +0 -77
  214. package/test/powerLogger.test.js +0 -59
  215. package/test/powerMemoizer.memoize.test.js +0 -100
  216. package/test/powerMemoizer.test.js +0 -85
  217. package/test/powerObserver.test.js +0 -129
  218. package/test/powerPermitGate.test.js +0 -66
  219. package/test/powerPool.autoTransfer.test.js +0 -100
  220. package/test/powerPool.autoscale.extra.test.js +0 -88
  221. package/test/powerPool.autoscale.test.js +0 -136
  222. package/test/powerPool.awaitDefaultTimeout.test.js +0 -52
  223. package/test/powerPool.awaitTimeout.test.js +0 -22
  224. package/test/powerPool.batch.test.js +0 -170
  225. package/test/powerPool.branches.extra2.test.js +0 -42
  226. package/test/powerPool.branches.test.js +0 -102
  227. package/test/powerPool.browser.messageerror.test.js +0 -45
  228. package/test/powerPool.correlation.test.js +0 -26
  229. package/test/powerPool.correlationId.test.js +0 -63
  230. package/test/powerPool.dispose.test.js +0 -49
  231. package/test/powerPool.drain.test.js +0 -57
  232. package/test/powerPool.events.test.js +0 -131
  233. package/test/powerPool.more.extra.test.js +0 -99
  234. package/test/powerPool.more.test.js +0 -283
  235. package/test/powerPool.node.messageerror.test.js +0 -46
  236. package/test/powerPool.postMessage.promise.test.js +0 -83
  237. package/test/powerPool.queueHigh.test.js +0 -55
  238. package/test/powerPool.queueSaturation.test.js +0 -51
  239. package/test/powerPool.rapidResize.test.js +0 -55
  240. package/test/powerPool.resize.overload.test.js +0 -65
  241. package/test/powerPool.resize.test.js +0 -70
  242. package/test/powerPool.shutdown.test.js +0 -38
  243. package/test/powerPool.stats.test.js +0 -40
  244. package/test/powerPool.stopThePress.test.js +0 -94
  245. package/test/powerPool.terminateShutdown.test.js +0 -22
  246. package/test/powerPool.test.js +0 -525
  247. package/test/powerPool.timers.test.js +0 -55
  248. package/test/powerPool.uncovered.test.js +0 -407
  249. package/test/powerPool.workerId.test.js +0 -47
  250. package/test/powerQueue.iterators.test.js +0 -67
  251. package/test/powerQueue.saturation.test.js +0 -18
  252. package/test/powerQueue.test.js +0 -48
  253. package/test/powerQueue.unshiftMany.test.js +0 -49
  254. package/test/powerRateLimit.atomic.test.js +0 -80
  255. package/test/powerRateLimit.extra.test.js +0 -145
  256. package/test/powerRateLimit.functions.test.js +0 -106
  257. package/test/powerRateLimit.test.js +0 -38
  258. package/test/powerRetry.attemptTimeout.test.js +0 -51
  259. package/test/powerRetry.test.js +0 -121
  260. package/test/powerScheduler.test.js +0 -126
  261. package/test/powerSemaphore.test.js +0 -108
  262. package/test/powerSlidingWindow.pool.test.js +0 -55
  263. package/test/powerSlidingWindow.test.js +0 -25
  264. package/test/powerSubscriberSet.test.js +0 -125
  265. package/test/powerTTLMap.test.js +0 -125
  266. package/test/powerThrottle.pool.test.js +0 -54
  267. package/test/powerThrottle.refill.test.js +0 -22
  268. package/test/powerThrottle.reserve.test.js +0 -46
  269. package/test/powerThrottle.test.js +0 -45
  270. package/test/powerTimedCache.test.js +0 -73
  271. package/test/umd.bundle.branches.test.js +0 -100
  272. package/test/umd.bundle.cache-timers.test.js +0 -48
  273. package/test/umd.bundle.exhaustive.test.js +0 -158
  274. package/test/umd.bundle.fuzz.test.js +0 -86
  275. package/test/umd.bundle.hasEqual.more.test.js +0 -68
  276. package/test/umd.bundle.hasEqual.test.js +0 -104
  277. package/test/umd.bundle.logger-extra.test.js +0 -48
  278. package/test/umd.bundle.more-coverage-2.test.js +0 -67
  279. package/test/umd.bundle.pool.test.js +0 -134
  280. package/test/umd.bundle.test.js +0 -265
  281. package/test/utils.measure.test.js +0 -49
  282. package/test/utils.now.extra.test.js +0 -30
  283. package/test/utils.now.more.test.js +0 -57
  284. package/tsconfig.json +0 -16
  285. package/typedoc.json +0 -25
  286. package/vite.config.js +0 -31
  287. package/vitest.config.js +0 -17
@@ -1,100 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { PowerMemoizer } from '../src/helpers/powerCache.js';
3
-
4
- describe('PowerMemoizer.memoize', () => {
5
- it('throws when memoize is called with a non-function', () => {
6
- const pm = new PowerMemoizer();
7
- expect(() => pm.memoize(null)).toThrow(TypeError);
8
- });
9
-
10
- it('memoizes sync function via memoize() and attaches helpers', () => {
11
- let calls = 0;
12
- const fn = (x) => {
13
- calls++;
14
- return x + 1;
15
- };
16
- const pm = new PowerMemoizer();
17
- const memo = pm.memoize(fn);
18
- expect(memo(1)).toBe(2);
19
- expect(calls).toBe(1);
20
- expect(memo(1)).toBe(2);
21
- expect(calls).toBe(1);
22
- // helpers
23
- expect(typeof memo.get).toBe('function');
24
- expect(memo.get(1)).toBe(2);
25
- expect(typeof memo.has).toBe('function');
26
- expect(memo.has(1)).toBe(true);
27
- expect(typeof memo.delete).toBe('function');
28
- expect(memo.delete(1)).toBe(true);
29
- expect(memo.has(1)).toBe(false);
30
- expect(typeof memo.stats).toBe('function');
31
- expect(memo.stats()).toHaveProperty('size');
32
- expect(memo.cache).toBe(pm.cache);
33
- expect(memo.original).toBe(fn);
34
- expect(typeof memo.clear).toBe('function');
35
- memo.clear();
36
- expect(memo(1)).toBe(2);
37
- expect(calls).toBe(2);
38
- });
39
-
40
- it('memoized function is instanceof PowerMemoizer', () => {
41
- const pm = new PowerMemoizer();
42
- const memo = pm.memoize((x) => x);
43
- expect(memo instanceof PowerMemoizer).toBe(true);
44
- });
45
-
46
- it('honors per-wrapper ttl and weight options', async () => {
47
- // TTL override
48
- let calls = 0;
49
- const fn = (x) => {
50
- calls++;
51
- return x;
52
- };
53
- const pm = new PowerMemoizer();
54
- const memo = pm.memoize(fn, { ttl: 5 });
55
- expect(memo(1)).toBe(1);
56
- expect(calls).toBe(1);
57
- expect(memo(1)).toBe(1);
58
- expect(calls).toBe(1);
59
- await new Promise((r) => setTimeout(r, 10));
60
- expect(memo(1)).toBe(1);
61
- expect(calls).toBe(2);
62
-
63
- // Weight override with rejection by cache
64
- let calls2 = 0;
65
- const fn2 = (x) => {
66
- calls2++;
67
- return { v: x };
68
- };
69
- const pm2 = new PowerMemoizer(undefined, {
70
- cacheOptions: { maxWeight: 1, rejectOversized: true },
71
- });
72
- const memo2 = pm2.memoize(fn2, { weight: 2 });
73
- // first call returns value but should not be cached due to oversized weight
74
- const a = memo2(1);
75
- expect(a).toEqual({ v: 1 });
76
- expect(calls2).toBe(1);
77
- // second call should invoke original again because caching was rejected
78
- const b = memo2(1);
79
- expect(b).toEqual({ v: 1 });
80
- expect(calls2).toBe(2);
81
- });
82
-
83
- it('uses constructor default memoize options when per-call ttl/weight are omitted', async () => {
84
- let calls = 0;
85
- const pm = new PowerMemoizer(undefined, { ttl: 5 });
86
- const memo = pm.memoize((value) => {
87
- calls += 1;
88
- return value;
89
- });
90
-
91
- expect(memo('x')).toBe('x');
92
- expect(memo('x')).toBe('x');
93
- expect(calls).toBe(1);
94
-
95
- await new Promise((resolve) => setTimeout(resolve, 10));
96
-
97
- expect(memo('x')).toBe('x');
98
- expect(calls).toBe(2);
99
- });
100
- });
@@ -1,85 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { PowerMemoizer } from '../src/helpers/powerCache.js';
3
-
4
- describe('PowerMemoizer', () => {
5
- it('caches sync function results', () => {
6
- let calls = 0;
7
- const fn = (a) => {
8
- calls++;
9
- return a * 2;
10
- };
11
- const pm = new PowerMemoizer();
12
- const memo = pm.memoize(fn);
13
- expect(memo(2)).toBe(4);
14
- expect(calls).toBe(1);
15
- expect(memo(2)).toBe(4);
16
- expect(calls).toBe(1);
17
- });
18
-
19
- it('dedupes concurrent async calls and caches result', async () => {
20
- let calls = 0;
21
- const fn = async (x) => {
22
- calls++;
23
- await new Promise((r) => setTimeout(r, 10));
24
- return x * 3;
25
- };
26
- const pm = new PowerMemoizer();
27
- const memo = pm.memoize(fn);
28
- const p1 = memo(3);
29
- const p2 = memo(3);
30
- // same in-flight promise
31
- expect(p1).toBe(p2);
32
- const res = await p1;
33
- expect(res).toBe(9);
34
- expect(calls).toBe(1);
35
- // subsequent call returns cached value (not a new invocation)
36
- const r2 = await Promise.resolve(memo(3));
37
- expect(r2).toBe(9);
38
- expect(calls).toBe(1);
39
- });
40
-
41
- it('does not cache rejected promises', async () => {
42
- let calls = 0;
43
- const fn = async () => {
44
- calls++;
45
- await new Promise((r) => setTimeout(r, 1));
46
- throw new Error('boom');
47
- };
48
- const pm = new PowerMemoizer();
49
- const memo = pm.memoize(fn);
50
- await expect(memo()).rejects.toThrow('boom');
51
- await expect(memo()).rejects.toThrow('boom');
52
- expect(calls).toBe(2);
53
- });
54
-
55
- it('honors TTL and expires entries', async () => {
56
- let calls = 0;
57
- const fn = (x) => {
58
- calls++;
59
- return x;
60
- };
61
- const pm = new PowerMemoizer(undefined, { ttl: 5 });
62
- const memo = pm.memoize(fn);
63
- expect(memo(1)).toBe(1);
64
- expect(calls).toBe(1);
65
- await new Promise((r) => setTimeout(r, 10));
66
- expect(memo(1)).toBe(1);
67
- expect(calls).toBe(2);
68
- });
69
-
70
- it('delete and clear remove cached entries', () => {
71
- let calls = 0;
72
- const fn = (x) => {
73
- calls++;
74
- return x;
75
- };
76
- const pm = new PowerMemoizer();
77
- const memo = pm.memoize(fn);
78
- expect(memo(5)).toBe(5);
79
- memo.delete(5);
80
- expect(memo(5)).toBe(5);
81
- expect(calls).toBe(2);
82
- memo.clear();
83
- expect(memo(6)).toBe(6);
84
- });
85
- });
@@ -1,129 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { PowerObserver } from '../src/helpers/powerObserver.js';
3
-
4
- describe('PowerObserver', () => {
5
- it('notifies subscribers asynchronously and returns unsubscribe', async () => {
6
- const obs = new PowerObserver(1);
7
- let called = 0;
8
- const unsub = obs.subscribe((next, prev) => {
9
- expect(prev).toBe(1);
10
- expect(next).toBe(2);
11
- called++;
12
- });
13
- obs.value = 2;
14
- await Promise.resolve();
15
- expect(called).toBe(1);
16
- unsub();
17
- obs.value = 3;
18
- await Promise.resolve();
19
- expect(called).toBe(1);
20
- });
21
-
22
- it('clear removes all subscribers and size reflects count', () => {
23
- const obs = new PowerObserver('a');
24
- const sub = () => {};
25
- obs.subscribe(sub);
26
- obs.subscribe(() => {});
27
- expect(obs.size).toBe(2);
28
- obs.clear();
29
- expect(obs.size).toBe(0);
30
- });
31
-
32
- it('throws on invalid subscriber', () => {
33
- const obs = new PowerObserver(0);
34
- expect(() => obs.subscribe(null)).toThrow();
35
- });
36
-
37
- it('respects distinct and map options', async () => {
38
- const obs = new PowerObserver(2, { distinct: true, map: (v) => v % 2 });
39
- let calls = 0;
40
- obs.subscribe(() => {
41
- calls++;
42
- });
43
- obs.value = 4; // maps 0 -> previous 0 -> no notify
44
- await Promise.resolve();
45
- expect(calls).toBe(0);
46
- obs.value = 5; // maps 1 -> notify
47
- await Promise.resolve();
48
- expect(calls).toBe(1);
49
- });
50
-
51
- it('allows setting map function via .map()', async () => {
52
- const obs = new PowerObserver(1);
53
- obs.map((v) => v * 2);
54
- let called = 0;
55
- obs.subscribe((n, p) => {
56
- expect(n).toBe(4);
57
- expect(p).toBe(2);
58
- called++;
59
- });
60
- obs.value = 2;
61
- await Promise.resolve();
62
- expect(called).toBe(1);
63
- });
64
-
65
- it('supports sync delivery and swallows subscriber errors', () => {
66
- const obs = new PowerObserver(1, { async: false });
67
- const seen = [];
68
- obs.subscribe(() => {
69
- throw new Error('listener failed');
70
- });
71
- obs.subscribe((next, prev) => {
72
- seen.push([prev, next]);
73
- });
74
-
75
- obs.value = 2;
76
-
77
- expect(seen).toEqual([[1, 2]]);
78
- });
79
-
80
- it('coalesces multiple async writes into the latest next value while preserving the first prev', async () => {
81
- const obs = new PowerObserver(1);
82
- const seen = [];
83
- obs.subscribe((next, prev) => {
84
- seen.push([prev, next]);
85
- });
86
-
87
- obs.value = 2;
88
- obs.value = 3;
89
- await Promise.resolve();
90
-
91
- expect(seen).toEqual([[1, 3]]);
92
- });
93
-
94
- it('drain aliases flush and map(null) clears the mapping function', async () => {
95
- const obs = new PowerObserver(2, { async: 'macrotask', map: (v) => v * 2 });
96
- const seen = [];
97
- obs.subscribe((next, prev) => {
98
- seen.push([prev, next]);
99
- });
100
-
101
- obs.map(null);
102
- obs.value = 4;
103
- obs.drain();
104
-
105
- expect(seen).toEqual([[2, 4]]);
106
- });
107
-
108
- it('throws when map is set to a non-function value', () => {
109
- const obs = new PowerObserver(1);
110
- expect(() => obs.map(123)).toThrow('map must be a function');
111
- });
112
-
113
- it('supports macrotask scheduling and flush()', async () => {
114
- const obs = new PowerObserver(1, { async: 'macrotask' });
115
- let called = 0;
116
- obs.subscribe((n, p) => {
117
- expect(p).toBe(1);
118
- expect(n).toBe(2);
119
- called++;
120
- });
121
- obs.value = 2;
122
- // macrotask: microtask await won't observe it
123
- await Promise.resolve();
124
- expect(called).toBe(0);
125
- // flush synchronously
126
- obs.flush();
127
- expect(called).toBe(1);
128
- });
129
- });
@@ -1,66 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { PowerPermitGate } from '../src/helpers/powerPermitGate.js';
3
-
4
- describe('PowerPermitGate', () => {
5
- it('acquires permits immediately when available', async () => {
6
- const gate = new PowerPermitGate({ capacity: 2 });
7
- const release = await gate.acquire();
8
- expect(typeof release).toBe('function');
9
- expect(gate.available).toBe(1);
10
- expect(gate.pending).toBe(0);
11
- release();
12
- expect(gate.available).toBe(2);
13
- });
14
-
15
- it('queues callers when permits are exhausted', async () => {
16
- const gate = new PowerPermitGate({ capacity: 1 });
17
- const firstRelease = await gate.acquire();
18
- const pending = gate.acquire();
19
- expect(gate.pending).toBe(1);
20
- let resolved = false;
21
-
22
- const promise = pending.then((release) => {
23
- resolved = true;
24
- release();
25
- });
26
-
27
- expect(resolved).toBe(false);
28
- firstRelease();
29
- await promise;
30
- expect(gate.pending).toBe(0);
31
- expect(resolved).toBe(true);
32
- expect(gate.available).toBe(1);
33
- });
34
-
35
- it('rejects acquire when the wait queue is full', async () => {
36
- const gate = new PowerPermitGate({ capacity: 1, queueCapacity: 1 });
37
- await gate.acquire();
38
- gate.acquire();
39
- await expect(gate.acquire()).rejects.toThrow('PowerPermitGate queue is full');
40
- });
41
-
42
- it('tryAcquire returns null when no permit is available', () => {
43
- const gate = new PowerPermitGate({ capacity: 1 });
44
- const release = gate.tryAcquire();
45
- expect(typeof release).toBe('function');
46
- expect(gate.tryAcquire()).toBeNull();
47
- release();
48
- expect(gate.tryAcquire()).not.toBeNull();
49
- });
50
-
51
- it('reset clears waiters and restores available permits', async () => {
52
- const gate = new PowerPermitGate({ capacity: 1, queueCapacity: 2 });
53
- const firstRelease = await gate.acquire();
54
- const pending = gate.acquire();
55
- let rejected = false;
56
- pending.catch((err) => {
57
- rejected = err && err.message === 'PowerPermitGate reset';
58
- });
59
-
60
- gate.reset({ available: 1, reason: new Error('PowerPermitGate reset') });
61
- expect(gate.available).toBe(1);
62
- await Promise.resolve();
63
- expect(rejected).toBe(true);
64
- firstRelease();
65
- });
66
- });
@@ -1,100 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
- import { PowerPool } from '../src/helpers/powerPool.js';
3
-
4
- describe('PowerPool auto-transfer behavior', () => {
5
- it('postMessage without transfer encodes object and passes ArrayBuffer in transfer list', async () => {
6
- class MockUnderlying {
7
- constructor() {
8
- this.onmessage = null;
9
- this.postMessage = vi.fn((msg) => {
10
- // echo back after a tick to simulate worker reply
11
- setTimeout(() => {
12
- if (this.onmessage) this.onmessage({ data: msg });
13
- }, 0);
14
- });
15
- this.terminate = vi.fn();
16
- }
17
- }
18
-
19
- const pool = new PowerPool(MockUnderlying, { size: 1, idleTimeout: 1000 });
20
- try {
21
- const sent = { hello: 'x'.repeat(2000) };
22
- const p = new Promise((resolve) => {
23
- pool.onmessage = (e) => resolve(e.data);
24
- });
25
-
26
- const ok = pool.postMessage(sent);
27
- expect(ok).toBe(true);
28
-
29
- // wait for reply
30
- const decoded = await p;
31
- expect(decoded).toEqual(sent);
32
-
33
- const underlying = pool.workers[0].worker._underlying || pool.workers[0]._underlying;
34
- expect(underlying).toBeTruthy();
35
- const call = underlying.postMessage.mock.calls[0];
36
- expect(call).toBeTruthy();
37
- const [argMsg, argTransfer] = call;
38
- // message should be a Uint8Array and transfer should include its buffer
39
- expect(argMsg).toBeInstanceOf(Uint8Array);
40
- expect(Array.isArray(argTransfer)).toBe(true);
41
- expect(argTransfer.length).toBeGreaterThanOrEqual(1);
42
- expect(argTransfer[0]).toBe(argMsg.buffer);
43
- } finally {
44
- pool.terminate();
45
- }
46
- });
47
-
48
- it('postMessage with explicit empty transfer array still forwards the encoded buffer', () => {
49
- class MockUnderlying {
50
- constructor() {
51
- this.onmessage = null;
52
- this.postMessage = vi.fn();
53
- this.terminate = vi.fn();
54
- }
55
- }
56
-
57
- const pool = new PowerPool(MockUnderlying, { size: 1, idleTimeout: 1000 });
58
- try {
59
- const ok = pool.postMessage({ hello: 'world' }, []);
60
- expect(ok).toBe(true);
61
-
62
- const underlying = pool.workers[0].worker._underlying || pool.workers[0]._underlying;
63
- expect(underlying).toBeTruthy();
64
- const call = underlying.postMessage.mock.calls[0];
65
- expect(call).toBeTruthy();
66
- const [argMsg, argTransfer] = call;
67
- expect(argMsg).toBeInstanceOf(Uint8Array);
68
- expect(argTransfer).toEqual([argMsg.buffer]);
69
- } finally {
70
- pool.terminate();
71
- }
72
- });
73
-
74
- it('broadcast without transfer encodes per-worker and provides transferable buffers', () => {
75
- class MockUnderlying {
76
- constructor() {
77
- this.onmessage = null;
78
- this.postMessage = vi.fn();
79
- this.terminate = vi.fn();
80
- }
81
- }
82
-
83
- const pool = new PowerPool(MockUnderlying, { size: 3, idleTimeout: 1000 });
84
- try {
85
- pool.broadcast({ ping: 'pong'.repeat(500) });
86
- for (const w of pool.workers) {
87
- const underlying = w.worker._underlying || w._underlying;
88
- expect(underlying).toBeTruthy();
89
- const call = underlying.postMessage.mock.calls[0];
90
- expect(call).toBeTruthy();
91
- const [argMsg, argTransfer] = call;
92
- expect(argMsg).toBeInstanceOf(Uint8Array);
93
- expect(Array.isArray(argTransfer)).toBe(true);
94
- expect(argTransfer[0]).toBe(argMsg.buffer);
95
- }
96
- } finally {
97
- pool.terminate();
98
- }
99
- });
100
- });
@@ -1,88 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { PowerPool } from '../src/helpers/powerPool.js';
3
-
4
- // Lightweight mock underlying that reports a fixed processing duration
5
- class MockUnderlyingWithDuration {
6
- constructor() {
7
- this.onmessage = null;
8
- this.postMessage = () => {
9
- // respond on next tick with a reported duration
10
- setTimeout(() => {
11
- if (this.onmessage)
12
- this.onmessage({ data: { duration: MockUnderlyingWithDuration.responseDuration } });
13
- }, 1);
14
- };
15
- this.terminate = () => {};
16
- }
17
- }
18
-
19
- describe('PowerPool autoscale - extra behaviors', () => {
20
- it('multi-step scaling: adds up to `stepUp` workers in one tick', async () => {
21
- MockUnderlyingWithDuration.responseDuration = 200;
22
-
23
- const pool = new PowerPool(MockUnderlyingWithDuration, {
24
- size: 1,
25
- minSize: 1,
26
- maxSize: 8,
27
- lazy: false,
28
- taskQueue: true,
29
- autoScale: {
30
- intervalMs: 50,
31
- targetMs: 50,
32
- alpha: 0.5,
33
- cooldownMs: 10,
34
- hysteresis: 0.1,
35
- stepUp: 3,
36
- },
37
- });
38
-
39
- try {
40
- // post many tasks to ensure EWMA rises
41
- for (let i = 0; i < 12; i++) pool.postMessage({ i });
42
-
43
- // wait several ticks
44
- await new Promise((r) => setTimeout(r, 400));
45
-
46
- // should have added at least stepUp workers in a single tick
47
- expect(pool.workers.length).toBeGreaterThanOrEqual(1 + 3);
48
- } finally {
49
- pool.terminate();
50
- }
51
- });
52
-
53
- it('backoff: backoff multiplier increases after scale action', async () => {
54
- MockUnderlyingWithDuration.responseDuration = 10;
55
-
56
- const pool = new PowerPool(MockUnderlyingWithDuration, {
57
- size: 8,
58
- minSize: 1,
59
- maxSize: 8,
60
- lazy: false,
61
- taskQueue: true,
62
- autoScale: {
63
- intervalMs: 50,
64
- targetMs: 50,
65
- alpha: 0.5,
66
- cooldownMs: 50,
67
- hysteresis: 0.1,
68
- backoffFactor: 4,
69
- backoffMaxMultiplier: 8,
70
- backoffResetMs: 1000,
71
- },
72
- });
73
-
74
- try {
75
- // Start at max size so normal auto-growth cannot add workers.
76
- for (let i = 0; i < 8; i++) pool.postMessage({ i });
77
-
78
- // wait enough time for at least one scale action
79
- await new Promise((r) => setTimeout(r, 200));
80
-
81
- // internal multiplier should have increased from 1 when autoscale scaled down
82
- expect(pool._autoScaleBackoffMultiplier).toBeGreaterThanOrEqual(1);
83
- expect(pool._autoScaleBackoffMultiplier).toBeGreaterThanOrEqual(4);
84
- } finally {
85
- pool.terminate();
86
- }
87
- });
88
- });
@@ -1,136 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { PowerPool } from '../src/helpers/powerPool.js';
3
-
4
- // Lightweight mock underlying used for the first autoscale test
5
- class MockUnderlying {
6
- constructor() {
7
- this.onmessage = null;
8
- this.postMessage = () => {};
9
- this.terminate = () => {};
10
- }
11
- addEventListener(type, cb) {
12
- if (type === 'message') this.onmessage = cb;
13
- }
14
- removeEventListener() {}
15
- }
16
-
17
- describe('PowerPool autoscale', () => {
18
- it('does not terminate workers with in-flight tasks during scale-down', () => {
19
- const pool = new PowerPool(MockUnderlying, {
20
- size: 2,
21
- minSize: 0,
22
- maxSize: 2,
23
- lazy: false,
24
- maxTasksPerWorker: 1,
25
- idleTimeout: 1000,
26
- });
27
- try {
28
- // simulate autoscale config without starting an interval
29
- pool._autoScale = {
30
- enabled: true,
31
- intervalMs: 1000,
32
- targetMs: 100,
33
- alpha: 0.2,
34
- cooldownMs: 100,
35
- hysteresis: 0.5,
36
- stepUp: 1,
37
- stepDown: 2,
38
- backoffFactor: 1,
39
- backoffMaxMultiplier: 1,
40
- backoffResetMs: 10000,
41
- };
42
- pool._autoScaleBackoffMultiplier = 1;
43
-
44
- // set EWMA low to request scale-down
45
- pool._ewmaLatency = 1;
46
-
47
- // mark both workers as busy
48
- pool.workers[0].tasks = 1;
49
- pool.workers[1].tasks = 1;
50
-
51
- pool._autoScaleTick();
52
- // no idle workers -> none removed
53
- expect(pool.workers.length).toBe(2);
54
-
55
- // make one worker idle - it should be eligible for removal
56
- pool.workers[1].tasks = 0;
57
- pool._autoScaleTick();
58
- expect(pool.workers.length).toBe(1);
59
- } finally {
60
- pool.terminate();
61
- }
62
- });
63
- });
64
-
65
- // Lightweight mock underlying that reports a fixed processing duration
66
- class MockUnderlyingWithDuration {
67
- constructor() {
68
- this.onmessage = null;
69
- this.postMessage = () => {
70
- // respond on next tick with a reported duration
71
- setTimeout(() => {
72
- if (this.onmessage)
73
- this.onmessage({ data: { duration: MockUnderlyingWithDuration.responseDuration } });
74
- }, 1);
75
- };
76
- this.terminate = () => {};
77
- }
78
- }
79
-
80
- describe('PowerPool autoscale', () => {
81
- it('scales up when observed EWMA latency exceeds target', async () => {
82
- // heavy tasks reported as 200ms each
83
- MockUnderlyingWithDuration.responseDuration = 200;
84
-
85
- const pool = new PowerPool(MockUnderlyingWithDuration, {
86
- size: 1,
87
- minSize: 1,
88
- maxSize: 4,
89
- lazy: false,
90
- taskQueue: true,
91
- // aggressive autoscale tick for tests
92
- autoScale: { intervalMs: 50, targetMs: 50, alpha: 0.5, cooldownMs: 100, hysteresis: 0.1 },
93
- });
94
-
95
- try {
96
- // post multiple tasks to accumulate EWMA
97
- for (let i = 0; i < 6; i++) pool.postMessage({ i });
98
-
99
- // wait enough time for several autoscale ticks
100
- await new Promise((r) => setTimeout(r, 500));
101
-
102
- // pool should have scaled up at least one worker
103
- expect(pool.workers.length).toBeGreaterThan(1);
104
- } finally {
105
- pool.terminate();
106
- }
107
- });
108
-
109
- it('scales down when latency is low and queue is empty', async () => {
110
- // light tasks reported as 1ms each
111
- MockUnderlyingWithDuration.responseDuration = 1;
112
-
113
- const pool = new PowerPool(MockUnderlyingWithDuration, {
114
- size: 3,
115
- minSize: 1,
116
- maxSize: 4,
117
- lazy: false,
118
- taskQueue: true,
119
- autoScale: { intervalMs: 50, targetMs: 50, alpha: 0.5, cooldownMs: 100, hysteresis: 0.1 },
120
- });
121
-
122
- try {
123
- // run a few quick tasks to seed a low EWMA
124
- for (let i = 0; i < 4; i++) pool.postMessage({ i });
125
-
126
- // allow tasks to complete and autoscaler to observe low latencies
127
- await new Promise((r) => setTimeout(r, 400));
128
-
129
- // after cooldown and empty queue, pool should have shrunk toward minSize
130
- expect(pool.workers.length).toBeLessThanOrEqual(2);
131
- expect(pool.workers.length).toBeGreaterThanOrEqual(1);
132
- } finally {
133
- pool.terminate();
134
- }
135
- });
136
- });