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,46 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { PowerCache } from '../src/helpers/powerCache.js';
3
-
4
- function delay(ms) {
5
- return new Promise((res) => setTimeout(res, ms));
6
- }
7
-
8
- describe('PowerCache.touch()', () => {
9
- it('updates recency without changing value', () => {
10
- const c = new PowerCache();
11
- c.set('a', 1);
12
- c.set('b', 2);
13
- // MRU order should be b, a
14
- const before = Array.from(c.entries('MRU')).map(([k]) => k);
15
- expect(before).toEqual(['b', 'a']);
16
-
17
- const ok = c.touch('a');
18
- expect(ok).toBe(true);
19
- const after = Array.from(c.entries('MRU')).map(([k]) => k);
20
- expect(after).toEqual(['a', 'b']);
21
- // value unchanged
22
- expect(c.get('a')).toBe(1);
23
- });
24
-
25
- it('returns false and removes expired entries', async () => {
26
- const c = new PowerCache();
27
- // set with very short TTL
28
- c.set('x', 42, { ttl: 5 });
29
- await delay(20);
30
- // now expired
31
- const ok = c.touch('x');
32
- expect(ok).toBe(false);
33
- expect(c.get('x')).toBeUndefined();
34
- });
35
-
36
- it('refreshes TTL when provided', async () => {
37
- const c = new PowerCache();
38
- c.set('k', 'v', { ttl: 10 });
39
- // touch with longer ttl
40
- const ok = c.touch('k', 1000);
41
- expect(ok).toBe(true);
42
- // wait for original TTL to have passed but before refreshed TTL
43
- await delay(50);
44
- expect(c.get('k')).toBe('v');
45
- });
46
- });
@@ -1,155 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { PowerChunker } from '../src/helpers/powerChunking.js';
3
- import { PowerPool } from '../src/helpers/powerPool.js';
4
- import { o2u8 } from '../src/helpers/powerBuffer.js';
5
-
6
- describe('PowerChunker branches extra', () => {
7
- it('throws when missing iterable or fn not function', () => {
8
- expect(() => new PowerChunker(null, () => {})).toThrow();
9
- expect(() => new PowerChunker([1, 2, 3], null)).toThrow();
10
- });
11
-
12
- it('processes array and emits error objects for thrown items', async () => {
13
- const items = [1, 2, 3];
14
- const messages = [];
15
- const pool = new PowerChunker(
16
- items,
17
- (item) => {
18
- if (item === 2) throw new Error('boom');
19
- return item * 2;
20
- },
21
- { chunkSize: 2 }
22
- );
23
- pool.onmessage = (e) => messages.push(e.data);
24
- await pool.drain();
25
- // At least one message should contain an error result for the thrown item
26
- const allResults = messages.flatMap((m) => m.results || []);
27
- expect(allResults.some((r) => r && r.error)).toBe(true);
28
- });
29
-
30
- it('streams an iterator and returns the pool instance when iterable throws', async () => {
31
- function* badGen() {
32
- yield 1;
33
- yield 2;
34
- throw new Error('stream fail');
35
- }
36
- const pool = new PowerChunker(badGen(), (n) => n + 1);
37
- // ensure we get a pool back and drain resolves without throwing
38
- expect(pool).toBeTruthy();
39
- await pool.drain();
40
- });
41
-
42
- it('handles async item functions and preserves correlationId on direct pool posts', async () => {
43
- const pool = new PowerChunker([], async (item) => item * 2, { chunkSize: 2 });
44
-
45
- const payload = await new Promise((resolve) => {
46
- pool.onmessage = (e) => resolve(e.data);
47
- pool.postMessage({ chunk: [2, 4], correlationId: 'cid-1' });
48
- });
49
-
50
- expect(payload.processed).toBe(2);
51
- expect(payload.results).toEqual([4, 8]);
52
- expect(payload.correlationId).toBe('cid-1');
53
- pool.terminate();
54
- });
55
-
56
- it('continues streaming when pool.postMessage throws for a chunk', async () => {
57
- const original = PowerPool.prototype.postMessage;
58
- let calls = 0;
59
-
60
- PowerPool.prototype.postMessage = function patchedPostMessage(...args) {
61
- calls += 1;
62
- if (calls === 1) throw new Error('chunk dispatch failed');
63
- return original.apply(this, args);
64
- };
65
-
66
- try {
67
- const values = function* () {
68
- yield 1;
69
- yield 2;
70
- yield 3;
71
- };
72
-
73
- const seen = [];
74
- const pool = new PowerChunker(values(), (value) => value + 1, { chunkSize: 1 });
75
- pool.onmessage = (e) => {
76
- seen.push(...(e.data.results || []));
77
- };
78
-
79
- await pool.drain();
80
- expect(seen.length).toBeGreaterThan(0);
81
- expect(seen).toContain(3);
82
- expect(seen).toContain(4);
83
- pool.terminate();
84
- } finally {
85
- PowerPool.prototype.postMessage = original;
86
- }
87
- });
88
-
89
- it('decodes transferable chunk payloads and supports inline worker add/remove listeners', async () => {
90
- const pool = new PowerChunker([], (value) => value + 1, {
91
- poolOptions: { size: 1, minSize: 1, lazy: false },
92
- });
93
-
94
- try {
95
- const worker = pool.workers[0].worker._underlying;
96
- const seen = [];
97
- const onMessage = (e) => seen.push(e.data);
98
- worker.addEventListener('message', onMessage);
99
-
100
- worker.postMessage(o2u8({ chunk: [2], correlationId: 'buf-1' }));
101
- await new Promise((resolve) => setTimeout(resolve, 10));
102
-
103
- expect(seen[0]).toMatchObject({
104
- processed: 1,
105
- results: [3],
106
- correlationId: 'buf-1',
107
- });
108
-
109
- worker.removeEventListener('message', onMessage);
110
- worker.postMessage(o2u8({ chunk: [5] }));
111
- await new Promise((resolve) => setTimeout(resolve, 10));
112
-
113
- expect(seen).toHaveLength(1);
114
- } finally {
115
- pool.terminate();
116
- }
117
- });
118
-
119
- it('routes inline worker failures to onerror and stops processing after terminate()', async () => {
120
- const pool = new PowerChunker([], (value) => value + 1, {
121
- poolOptions: { size: 1, minSize: 1, lazy: false },
122
- });
123
-
124
- try {
125
- const worker = pool.workers[0].worker._underlying;
126
- const errors = [];
127
- const messages = [];
128
- const onError = (err) => errors.push(err);
129
- worker.addEventListener('error', onError);
130
- worker.onmessage = () => {
131
- throw new Error('message handler failed');
132
- };
133
-
134
- worker.postMessage({ chunk: [1] });
135
- await new Promise((resolve) => setTimeout(resolve, 10));
136
- expect(errors.some((err) => /message handler failed/.test(String(err && err.message)))).toBe(
137
- true
138
- );
139
-
140
- worker.onmessage = (e) => messages.push(e.data);
141
- worker.postMessage(o2u8(null));
142
- await new Promise((resolve) => setTimeout(resolve, 10));
143
- expect(errors.length).toBeGreaterThanOrEqual(2);
144
-
145
- worker.terminate();
146
- worker.postMessage({ chunk: [9] });
147
- await new Promise((resolve) => setTimeout(resolve, 10));
148
- expect(messages).toHaveLength(0);
149
-
150
- worker.removeEventListener('error', onError);
151
- } finally {
152
- pool.terminate();
153
- }
154
- });
155
- });
@@ -1,177 +0,0 @@
1
- import { expect } from 'chai';
2
- import { describe, it } from 'vitest';
3
- import { PowerChunker } from '../src/helpers/powerChunking.js';
4
-
5
- describe('PowerChunking error handling', () => {
6
- it('emits per-item normalized error objects for sync throws and async rejections', async () => {
7
- const items = [1, 2, 3, 4];
8
- // 2 throws sync with code, 3 rejects async with code
9
- const fn = (item) => {
10
- if (item === 2) {
11
- const e = new Error('sync fail');
12
- e.code = 'SYNC_CODE';
13
- throw e;
14
- }
15
- if (item === 3) {
16
- const e = new Error('async fail');
17
- e.code = 'ASYNC_CODE';
18
- return Promise.reject(e);
19
- }
20
- return item * 10;
21
- };
22
-
23
- const pool = new PowerChunker(items, fn, { poolOptions: { size: 2 }, chunkSize: 2 });
24
-
25
- const chunkResults = [];
26
- pool.onmessage = (e) => {
27
- if (e && e.data) chunkResults.push(e.data);
28
- };
29
-
30
- await pool.drain();
31
-
32
- // flatten per-item results
33
- const all = chunkResults.flatMap((c) => c.results || []);
34
-
35
- // should contain two errors (SYNC_CODE and ASYNC_CODE) and two numeric results
36
- const codes = all
37
- .filter((r) => r && r.error)
38
- .map((r) => r.code)
39
- .sort();
40
- expect(codes).to.include('SYNC_CODE');
41
- expect(codes).to.include('ASYNC_CODE');
42
-
43
- const numbers = all.filter((r) => !r || !r.error);
44
- // two successful items 1 and 4 -> 10 and 40
45
- expect(numbers).to.include(10);
46
- expect(numbers).to.include(40);
47
-
48
- pool.terminate();
49
- });
50
-
51
- it('surfaces batch dispatch failures via pool error listeners', async () => {
52
- const pool = new PowerChunker([1, 2, 3], (item) => item, {
53
- chunkSize: 1,
54
- poolOptions: {
55
- size: 1,
56
- minSize: 1,
57
- maxSize: 1,
58
- maxTasksPerWorker: 0,
59
- taskQueue: true,
60
- queuePolicy: 'reject',
61
- lazy: false,
62
- },
63
- });
64
-
65
- const errors = [];
66
- pool.addEventListener('error', (err) => errors.push(err));
67
-
68
- await Promise.resolve();
69
- await new Promise((resolve) => setTimeout(resolve, 0));
70
-
71
- expect(errors.length).to.be.greaterThan(0);
72
- expect(errors[0]).to.include({ code: 'ECHUNKDISPATCH', mode: 'batch' });
73
-
74
- pool.terminate();
75
- });
76
-
77
- it('reports global failed chunk indexes across array dispatch windows', async () => {
78
- const items = Array.from({ length: 20 }, (_, i) => i + 1);
79
- const pool = new PowerChunker(items, (item) => item, {
80
- chunkSize: 1,
81
- poolOptions: {
82
- size: 1,
83
- minSize: 1,
84
- maxSize: 1,
85
- maxTasksPerWorker: 0,
86
- taskQueue: true,
87
- queuePolicy: 'reject',
88
- lazy: false,
89
- },
90
- });
91
-
92
- const errors = [];
93
- pool.addEventListener('error', (err) => errors.push(err));
94
-
95
- await Promise.resolve();
96
- await new Promise((resolve) => setTimeout(resolve, 0));
97
-
98
- const batchErrors = errors.filter(
99
- (err) => err && err.code === 'ECHUNKDISPATCH' && err.mode === 'batch'
100
- );
101
- expect(batchErrors.length).to.be.greaterThan(0);
102
- for (const err of batchErrors) {
103
- expect(Array.isArray(err.failedChunks)).to.equal(true);
104
- for (const idx of err.failedChunks) {
105
- expect(Number.isInteger(idx)).to.equal(true);
106
- expect(idx).to.be.at.least(0);
107
- expect(idx).to.be.below(items.length);
108
- }
109
- }
110
-
111
- pool.terminate();
112
- });
113
-
114
- it('surfaces stream dispatch failures via pool error listeners', async () => {
115
- function* items() {
116
- yield 1;
117
- yield 2;
118
- yield 3;
119
- }
120
-
121
- const pool = new PowerChunker(items(), (item) => item, {
122
- chunkSize: 1,
123
- poolOptions: {
124
- size: 1,
125
- minSize: 1,
126
- maxSize: 1,
127
- maxTasksPerWorker: 0,
128
- taskQueue: true,
129
- queuePolicy: 'reject',
130
- lazy: false,
131
- },
132
- });
133
-
134
- const errors = [];
135
- pool.addEventListener('error', (err) => errors.push(err));
136
-
137
- await Promise.resolve();
138
- await new Promise((resolve) => setTimeout(resolve, 0));
139
-
140
- expect(
141
- errors.some((err) => err && err.code === 'ECHUNKDISPATCH' && err.mode === 'stream')
142
- ).to.equal(true);
143
-
144
- pool.terminate();
145
- });
146
-
147
- it('emits stream-iterate errors when the input iterable throws', async () => {
148
- function* badItems() {
149
- yield 1;
150
- throw new Error('iter boom');
151
- }
152
-
153
- const pool = new PowerChunker(badItems(), (item) => item, {
154
- chunkSize: 1,
155
- poolOptions: {
156
- size: 1,
157
- minSize: 1,
158
- maxSize: 1,
159
- taskQueue: true,
160
- queuePolicy: 'enqueue',
161
- lazy: false,
162
- },
163
- });
164
-
165
- const errors = [];
166
- pool.addEventListener('error', (err) => errors.push(err));
167
-
168
- await Promise.resolve();
169
- await new Promise((resolve) => setTimeout(resolve, 0));
170
-
171
- expect(
172
- errors.some((err) => err && err.code === 'ECHUNKDISPATCH' && err.mode === 'stream-iterate')
173
- ).to.equal(true);
174
-
175
- pool.terminate();
176
- });
177
- });
@@ -1,39 +0,0 @@
1
- import { expect } from 'chai';
2
- import { describe, it } from 'vitest';
3
- import { PowerChunker } from '../src/helpers/powerChunking.js';
4
-
5
- describe('PowerChunking helper', () => {
6
- it('processes all items in chunks and returns processed counts (awaitResponse)', async () => {
7
- const items = Array.from({ length: 50 }, (_, i) => i + 1);
8
- const processed = [];
9
- const fn = (item) => {
10
- processed.push(item);
11
- };
12
- const pool = new PowerChunker(items, fn, { poolOptions: { size: 2 } });
13
- expect(pool && typeof pool.postMessageBatch === 'function').to.equal(true);
14
- // wait until the pool has finished processing all chunks
15
- await pool.drain();
16
- // ensure the fn ran for each item
17
- expect(processed.length).to.equal(items.length);
18
- pool.terminate();
19
- });
20
-
21
- it('uses explicit chunkSize when provided', () => {
22
- const items = Array.from({ length: 10 }, (_, i) => i);
23
- const fn = () => {};
24
- const pool = new PowerChunker(items, fn, { poolOptions: { size: 2 }, chunkSize: 3 });
25
- // Each processed chunk emits one `message` event; wait for drain and verify
26
- let messageCount = 0;
27
- let totalProcessed = 0;
28
- pool.onmessage = (e) => {
29
- messageCount++;
30
- if (e && e.data && e.data.processed) totalProcessed += e.data.processed;
31
- };
32
- return pool.drain().then(() => {
33
- // Ensure all items were processed and at least the expected number of chunk messages occurred
34
- expect(totalProcessed).to.equal(items.length);
35
- expect(messageCount).to.be.at.least(Math.ceil(items.length / 3));
36
- pool.terminate();
37
- });
38
- });
39
- });
@@ -1,71 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import PowerCircuit from '../src/helpers/powerCircuit.js';
3
- import { PowerEventBus } from '../src/helpers/powerEventBus.js';
4
-
5
- describe('PowerCircuit observability', () => {
6
- it('calls onStateChange callback when state transitions occur', async () => {
7
- const calls = [];
8
- const cb = new PowerCircuit({
9
- threshold: 1,
10
- timeout: 10,
11
- onStateChange: (s, r) => calls.push([s, r]),
12
- });
13
-
14
- // cause a failure to open the circuit
15
- await cb.call(() => Promise.reject(new Error('fail'))).catch(() => {});
16
-
17
- // after one failure threshold=1 -> should open
18
- expect(calls.length).toBeGreaterThanOrEqual(1);
19
- expect(calls[0][0]).toBe('open');
20
- expect(calls[0][1]).toBe('thresholdExceeded');
21
-
22
- // advance through timeout by waiting
23
- await new Promise((res) => setTimeout(res, 15));
24
-
25
- // next call should cause half-open then success -> closed
26
- await cb.call(() => Promise.resolve('ok'));
27
-
28
- // find closed event
29
- const closed = calls.find(([s]) => s === 'closed');
30
- expect(closed).toBeTruthy();
31
- });
32
-
33
- it('swallows errors thrown by onStateChange and event bus observers', async () => {
34
- const bus = new PowerEventBus();
35
- bus.on('stateChange', () => {
36
- throw new Error('bus observer failed');
37
- });
38
-
39
- const cb = new PowerCircuit({
40
- threshold: 1,
41
- timeout: 10,
42
- eventBus: bus,
43
- onStateChange() {
44
- throw new Error('callback failed');
45
- },
46
- });
47
-
48
- await expect(cb.call(() => Promise.reject(new Error('fail')))).rejects.toThrow('fail');
49
- expect(cb.state).toBe('open');
50
- });
51
-
52
- it('emits stateChange on provided PowerEventBus', async () => {
53
- const bus = new PowerEventBus();
54
- const events = [];
55
- bus.on('stateChange', (payload) => events.push(payload));
56
-
57
- const cb = new PowerCircuit({ threshold: 1, timeout: 10, eventBus: bus });
58
-
59
- await cb.call(() => Promise.reject(new Error('boom'))).catch(() => {});
60
-
61
- expect(events.length).toBeGreaterThanOrEqual(1);
62
- expect(events[0].state).toBe('open');
63
- expect(events[0].reason).toBe('thresholdExceeded');
64
-
65
- await new Promise((res) => setTimeout(res, 15));
66
- await cb.call(() => Promise.resolve('ok'));
67
-
68
- const closedEvent = events.find((e) => e.state === 'closed');
69
- expect(closedEvent).toBeTruthy();
70
- });
71
- });
@@ -1,74 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { PowerCircuit } from '../src/helpers/powerCircuit.js';
3
-
4
- describe('PowerCircuit', () => {
5
- it('throws when fn is not a function', async () => {
6
- const cb = new PowerCircuit();
7
- await expect(cb.call(null)).rejects.toThrow('fn must be a function');
8
- });
9
-
10
- it('opens after threshold failures and short-circuits', async () => {
11
- const cb = new PowerCircuit({ threshold: 2, timeout: 50 });
12
- let fail = true;
13
- const f = async () => {
14
- if (fail) throw new Error('boom');
15
- return 'ok';
16
- };
17
-
18
- await expect(cb.call(f)).rejects.toThrow('boom');
19
- expect(cb.failures).toBe(1);
20
- // second failure triggers open
21
- await expect(cb.call(f)).rejects.toThrow('boom');
22
- expect(cb.state).toBe('open');
23
- expect(cb.failures).toBe(0);
24
- expect(cb.lastError).toBeInstanceOf(Error);
25
- // subsequent calls short-circuit
26
- await expect(cb.call(f)).rejects.toHaveProperty('code', 'ECIRCUITOPEN');
27
- });
28
-
29
- it('allows trial after timeout and recovers on success', async () => {
30
- const cb = new PowerCircuit({ threshold: 1, timeout: 50 });
31
- // cause one failure to open
32
- await expect(cb.call(() => Promise.reject(new Error('e1')))).rejects.toThrow();
33
- expect(cb.state).toBe('open');
34
- // wait for timeout to allow half-open
35
- await new Promise((r) => setTimeout(r, 60));
36
- expect(cb.state).toBe('half-open');
37
- // now succeed
38
- const res = await cb.call(() => Promise.resolve('ok'));
39
- expect(res).toBe('ok');
40
- expect(cb.state).toBe('closed');
41
- expect(cb.failures).toBe(0);
42
- expect(cb.lastError).toBe(null);
43
- });
44
-
45
- it('reopens when the half-open trial fails', async () => {
46
- const cb = new PowerCircuit({ threshold: 1, timeout: 10 });
47
-
48
- await expect(cb.call(() => Promise.reject(new Error('first fail')))).rejects.toThrow(
49
- 'first fail'
50
- );
51
- expect(cb.state).toBe('open');
52
-
53
- await new Promise((r) => setTimeout(r, 15));
54
- expect(cb.state).toBe('half-open');
55
-
56
- await expect(cb.call(() => Promise.reject(new Error('trial fail')))).rejects.toThrow(
57
- 'trial fail'
58
- );
59
- expect(cb.state).toBe('open');
60
- expect(cb.failures).toBe(0);
61
- });
62
-
63
- it('reset closes the circuit and clears failure state', async () => {
64
- const cb = new PowerCircuit({ threshold: 1, timeout: 50 });
65
- await expect(cb.call(() => Promise.reject(new Error('boom')))).rejects.toThrow('boom');
66
- expect(cb.state).toBe('open');
67
-
68
- cb.reset();
69
-
70
- expect(cb.state).toBe('closed');
71
- expect(cb.failures).toBe(0);
72
- expect(cb.lastError).toBe(null);
73
- });
74
- });
@@ -1,140 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { PowerDeadline } from '../src/helpers/powerDeadline.js';
3
-
4
- const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
5
-
6
- describe('PowerDeadline', () => {
7
- it('resolves when the function completes before timeout', async () => {
8
- const value = await PowerDeadline.run(
9
- async () => {
10
- await delay(10);
11
- return 'ok';
12
- },
13
- { attemptTimeout: 100 }
14
- );
15
-
16
- expect(value).toBe('ok');
17
- });
18
-
19
- it('rejects with ETIMEOUT when an attempt exceeds attemptTimeout', async () => {
20
- await expect(
21
- PowerDeadline.run(
22
- async () => {
23
- await delay(50);
24
- },
25
- { attemptTimeout: 10 }
26
- )
27
- ).rejects.toHaveProperty('code', 'ETIMEOUT');
28
- });
29
-
30
- it('rejects with EDEADLINE when totalTimeout is exceeded', async () => {
31
- await expect(
32
- PowerDeadline.run(
33
- async () => {
34
- await delay(100);
35
- },
36
- { maxAttempts: 3, totalTimeout: 50, retryDelay: 10 }
37
- )
38
- ).rejects.toHaveProperty('code', 'EDEADLINE');
39
- });
40
-
41
- it('retries failed attempts when retryIf returns true', async () => {
42
- let calls = 0;
43
- const value = await PowerDeadline.run(
44
- async () => {
45
- calls += 1;
46
- if (calls < 2) throw new Error('try again');
47
- return 'done';
48
- },
49
- { maxAttempts: 3, retryDelay: 5, retryIf: () => true }
50
- );
51
-
52
- expect(value).toBe('done');
53
- expect(calls).toBe(2);
54
- });
55
-
56
- it('supports AbortSignal cancellation', async () => {
57
- const controller = new AbortController();
58
- const promise = PowerDeadline.run(
59
- async () => {
60
- await delay(50);
61
- },
62
- { signal: controller.signal, attemptTimeout: 100 }
63
- );
64
-
65
- controller.abort();
66
- await expect(promise).rejects.toHaveProperty('code', 'EABORT');
67
- });
68
-
69
- it('propagates attempts metadata on rejection', async () => {
70
- await expect(
71
- PowerDeadline.run(
72
- async () => {
73
- throw new Error('bad');
74
- },
75
- { maxAttempts: 2, retryIf: () => false }
76
- )
77
- ).rejects.toMatchObject({ attempts: 1, attemptTimeout: null });
78
- });
79
-
80
- it('throws TypeError when fn is not callable', async () => {
81
- await expect(PowerDeadline.run(null)).rejects.toThrow(TypeError);
82
- });
83
-
84
- it('rejects immediately when the abort signal is already aborted', async () => {
85
- const controller = new AbortController();
86
- controller.abort('user cancelled');
87
-
88
- await expect(
89
- PowerDeadline.run(async () => 'never', { signal: controller.signal, totalTimeout: 25 })
90
- ).rejects.toMatchObject({
91
- code: 'EABORT',
92
- reason: 'user cancelled',
93
- attempts: 1,
94
- totalTimeout: 25,
95
- });
96
- });
97
-
98
- it('treats boolean retryIf values as retry predicates', async () => {
99
- let calls = 0;
100
- const value = await PowerDeadline.run(
101
- async () => {
102
- calls += 1;
103
- if (calls === 1) throw new Error('retry once');
104
- return 'ok';
105
- },
106
- { maxAttempts: 2, retryIf: true }
107
- );
108
-
109
- expect(value).toBe('ok');
110
- expect(calls).toBe(2);
111
- });
112
-
113
- it('merges instance defaults with per-call overrides', async () => {
114
- let calls = 0;
115
- const deadline = new PowerDeadline({ maxAttempts: 3, retryIf: true });
116
- const value = await deadline.run(
117
- async () => {
118
- calls += 1;
119
- if (calls < 2) throw new Error('retry from instance');
120
- return 'done';
121
- },
122
- { retryDelay: 1 }
123
- );
124
-
125
- expect(value).toBe('done');
126
- expect(calls).toBe(2);
127
- });
128
-
129
- it('stops before an attempt starts when the total deadline is already exhausted', async () => {
130
- await expect(
131
- PowerDeadline.run(
132
- async () => {
133
- await delay(10);
134
- throw new Error('too late');
135
- },
136
- { maxAttempts: 3, totalTimeout: 1, retryDelay: 5, retryIf: true }
137
- )
138
- ).rejects.toMatchObject({ code: 'EDEADLINE' });
139
- });
140
- });