@stimulcross/rate-limiter 0.0.1 → 0.0.2

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 (183) hide show
  1. package/README.md +20 -0
  2. package/lib/core/cancellable.js +1 -0
  3. package/lib/core/clock.js +1 -0
  4. package/lib/core/decision.js +1 -0
  5. package/lib/core/rate-limit-policy.js +1 -0
  6. package/lib/core/rate-limiter-status.js +1 -0
  7. package/lib/core/rate-limiter.js +1 -0
  8. package/lib/core/state-storage.js +1 -0
  9. package/lib/enums/rate-limit-error-code.js +26 -0
  10. package/lib/errors/custom.error.js +12 -0
  11. package/lib/errors/invalid-cost.error.js +25 -0
  12. package/lib/errors/rate-limit.error.js +74 -0
  13. package/{src/errors/rate-limiter-destroyed.error.ts → lib/errors/rate-limiter-destroyed.error.js} +3 -3
  14. package/lib/index.js +4 -0
  15. package/lib/interfaces/rate-limiter-options.js +1 -0
  16. package/lib/interfaces/rate-limiter-queue-options.js +1 -0
  17. package/lib/interfaces/rate-limiter-run-options.js +1 -0
  18. package/lib/limiters/abstract-rate-limiter.js +132 -0
  19. package/lib/limiters/composite.policy.js +72 -0
  20. package/lib/limiters/fixed-window/fixed-window.limiter.js +84 -0
  21. package/lib/limiters/fixed-window/fixed-window.options.js +1 -0
  22. package/lib/limiters/fixed-window/fixed-window.policy.js +120 -0
  23. package/lib/limiters/fixed-window/fixed-window.state.js +1 -0
  24. package/lib/limiters/fixed-window/fixed-window.status.js +1 -0
  25. package/lib/limiters/fixed-window/index.js +1 -0
  26. package/lib/limiters/generic-cell/generic-cell.limiter.js +73 -0
  27. package/lib/limiters/generic-cell/generic-cell.options.js +1 -0
  28. package/lib/limiters/generic-cell/generic-cell.policy.js +86 -0
  29. package/lib/limiters/generic-cell/generic-cell.state.js +1 -0
  30. package/lib/limiters/generic-cell/generic-cell.status.js +1 -0
  31. package/lib/limiters/generic-cell/index.js +1 -0
  32. package/lib/limiters/http-response-based/http-limit-info.extractor.js +1 -0
  33. package/lib/limiters/http-response-based/http-limit.info.js +1 -0
  34. package/lib/limiters/http-response-based/http-response-based-limiter.options.js +1 -0
  35. package/lib/limiters/http-response-based/http-response-based-limiter.state.js +1 -0
  36. package/lib/limiters/http-response-based/http-response-based-limiter.status.js +1 -0
  37. package/lib/limiters/http-response-based/http-response-based.limiter.js +379 -0
  38. package/lib/limiters/http-response-based/index.js +1 -0
  39. package/lib/limiters/leaky-bucket/index.js +1 -0
  40. package/lib/limiters/leaky-bucket/leaky-bucket.limiter.js +74 -0
  41. package/lib/limiters/leaky-bucket/leaky-bucket.options.js +1 -0
  42. package/lib/limiters/leaky-bucket/leaky-bucket.policy.js +100 -0
  43. package/lib/limiters/leaky-bucket/leaky-bucket.state.js +1 -0
  44. package/lib/limiters/leaky-bucket/leaky-bucket.status.js +1 -0
  45. package/lib/limiters/sliding-window-counter/index.js +1 -0
  46. package/lib/limiters/sliding-window-counter/sliding-window-counter.limiter.js +46 -0
  47. package/lib/limiters/sliding-window-counter/sliding-window-counter.options.js +1 -0
  48. package/lib/limiters/sliding-window-counter/sliding-window-counter.policy.js +127 -0
  49. package/lib/limiters/sliding-window-counter/sliding-window-counter.state.js +1 -0
  50. package/lib/limiters/sliding-window-counter/sliding-window-counter.status.js +1 -0
  51. package/lib/limiters/sliding-window-log/index.js +1 -0
  52. package/lib/limiters/sliding-window-log/sliding-window-log.limiter.js +43 -0
  53. package/lib/limiters/sliding-window-log/sliding-window-log.options.js +1 -0
  54. package/lib/limiters/sliding-window-log/sliding-window-log.policy.js +123 -0
  55. package/lib/limiters/sliding-window-log/sliding-window-log.state.js +1 -0
  56. package/lib/limiters/sliding-window-log/sliding-window-log.status.js +1 -0
  57. package/lib/limiters/token-bucket/index.js +1 -0
  58. package/lib/limiters/token-bucket/token-bucket.limiter.js +74 -0
  59. package/lib/limiters/token-bucket/token-bucket.options.js +1 -0
  60. package/lib/limiters/token-bucket/token-bucket.policy.js +115 -0
  61. package/lib/limiters/token-bucket/token-bucket.state.js +1 -0
  62. package/lib/limiters/token-bucket/token-bucket.status.js +1 -0
  63. package/lib/runtime/default-clock.js +6 -0
  64. package/lib/runtime/execution-tickets.js +26 -0
  65. package/lib/runtime/in-memory-state-store.js +96 -0
  66. package/lib/runtime/rate-limiter.executor.js +195 -0
  67. package/lib/runtime/semaphore.js +27 -0
  68. package/lib/runtime/task.js +100 -0
  69. package/lib/types/limit-behavior.js +1 -0
  70. package/lib/utils/generate-random-string.js +12 -0
  71. package/lib/utils/promise-with-resolvers.js +14 -0
  72. package/lib/utils/sanitize-error.js +4 -0
  73. package/lib/utils/sanitize-priority.js +17 -0
  74. package/lib/utils/validate-cost.js +13 -0
  75. package/package.json +12 -2
  76. package/.editorconfig +0 -21
  77. package/.github/workflows/node.yml +0 -87
  78. package/.husky/commit-msg +0 -1
  79. package/.husky/pre-commit +0 -1
  80. package/.megaignore +0 -8
  81. package/.prettierignore +0 -3
  82. package/commitlint.config.js +0 -8
  83. package/eslint.config.js +0 -65
  84. package/lint-staged.config.js +0 -4
  85. package/prettier.config.cjs +0 -1
  86. package/src/core/cancellable.ts +0 -4
  87. package/src/core/clock.ts +0 -9
  88. package/src/core/decision.ts +0 -27
  89. package/src/core/rate-limit-policy.ts +0 -15
  90. package/src/core/rate-limiter-status.ts +0 -14
  91. package/src/core/rate-limiter.ts +0 -37
  92. package/src/core/state-storage.ts +0 -51
  93. package/src/enums/rate-limit-error-code.ts +0 -29
  94. package/src/errors/custom.error.ts +0 -14
  95. package/src/errors/invalid-cost.error.ts +0 -33
  96. package/src/errors/rate-limit.error.ts +0 -91
  97. package/src/index.ts +0 -11
  98. package/src/interfaces/rate-limiter-options.ts +0 -84
  99. package/src/interfaces/rate-limiter-queue-options.ts +0 -45
  100. package/src/interfaces/rate-limiter-run-options.ts +0 -58
  101. package/src/limiters/abstract-rate-limiter.ts +0 -206
  102. package/src/limiters/composite.policy.ts +0 -102
  103. package/src/limiters/fixed-window/fixed-window.limiter.ts +0 -121
  104. package/src/limiters/fixed-window/fixed-window.options.ts +0 -29
  105. package/src/limiters/fixed-window/fixed-window.policy.ts +0 -159
  106. package/src/limiters/fixed-window/fixed-window.state.ts +0 -10
  107. package/src/limiters/fixed-window/fixed-window.status.ts +0 -46
  108. package/src/limiters/fixed-window/index.ts +0 -4
  109. package/src/limiters/generic-cell/generic-cell.limiter.ts +0 -108
  110. package/src/limiters/generic-cell/generic-cell.options.ts +0 -23
  111. package/src/limiters/generic-cell/generic-cell.policy.ts +0 -115
  112. package/src/limiters/generic-cell/generic-cell.state.ts +0 -8
  113. package/src/limiters/generic-cell/generic-cell.status.ts +0 -54
  114. package/src/limiters/generic-cell/index.ts +0 -4
  115. package/src/limiters/http-response-based/http-limit-info.extractor.ts +0 -20
  116. package/src/limiters/http-response-based/http-limit.info.ts +0 -41
  117. package/src/limiters/http-response-based/http-response-based-limiter.options.ts +0 -18
  118. package/src/limiters/http-response-based/http-response-based-limiter.state.ts +0 -13
  119. package/src/limiters/http-response-based/http-response-based-limiter.status.ts +0 -74
  120. package/src/limiters/http-response-based/http-response-based.limiter.ts +0 -512
  121. package/src/limiters/http-response-based/index.ts +0 -6
  122. package/src/limiters/leaky-bucket/index.ts +0 -4
  123. package/src/limiters/leaky-bucket/leaky-bucket.limiter.ts +0 -105
  124. package/src/limiters/leaky-bucket/leaky-bucket.options.ts +0 -23
  125. package/src/limiters/leaky-bucket/leaky-bucket.policy.ts +0 -134
  126. package/src/limiters/leaky-bucket/leaky-bucket.state.ts +0 -9
  127. package/src/limiters/leaky-bucket/leaky-bucket.status.ts +0 -36
  128. package/src/limiters/sliding-window-counter/index.ts +0 -7
  129. package/src/limiters/sliding-window-counter/sliding-window-counter.limiter.ts +0 -76
  130. package/src/limiters/sliding-window-counter/sliding-window-counter.options.ts +0 -20
  131. package/src/limiters/sliding-window-counter/sliding-window-counter.policy.ts +0 -167
  132. package/src/limiters/sliding-window-counter/sliding-window-counter.state.ts +0 -10
  133. package/src/limiters/sliding-window-counter/sliding-window-counter.status.ts +0 -53
  134. package/src/limiters/sliding-window-log/index.ts +0 -4
  135. package/src/limiters/sliding-window-log/sliding-window-log.limiter.ts +0 -65
  136. package/src/limiters/sliding-window-log/sliding-window-log.options.ts +0 -20
  137. package/src/limiters/sliding-window-log/sliding-window-log.policy.ts +0 -166
  138. package/src/limiters/sliding-window-log/sliding-window-log.state.ts +0 -19
  139. package/src/limiters/sliding-window-log/sliding-window-log.status.ts +0 -44
  140. package/src/limiters/token-bucket/index.ts +0 -4
  141. package/src/limiters/token-bucket/token-bucket.limiter.ts +0 -110
  142. package/src/limiters/token-bucket/token-bucket.options.ts +0 -17
  143. package/src/limiters/token-bucket/token-bucket.policy.ts +0 -155
  144. package/src/limiters/token-bucket/token-bucket.state.ts +0 -10
  145. package/src/limiters/token-bucket/token-bucket.status.ts +0 -36
  146. package/src/runtime/default-clock.ts +0 -8
  147. package/src/runtime/execution-tickets.ts +0 -34
  148. package/src/runtime/in-memory-state-store.ts +0 -135
  149. package/src/runtime/rate-limiter.executor.ts +0 -286
  150. package/src/runtime/semaphore.ts +0 -31
  151. package/src/runtime/task.ts +0 -141
  152. package/src/types/limit-behavior.ts +0 -8
  153. package/src/utils/generate-random-string.ts +0 -16
  154. package/src/utils/promise-with-resolvers.ts +0 -23
  155. package/src/utils/sanitize-error.ts +0 -4
  156. package/src/utils/sanitize-priority.ts +0 -22
  157. package/src/utils/validate-cost.ts +0 -16
  158. package/tests/integration/limiters/fixed-window.limiter.spec.ts +0 -371
  159. package/tests/integration/limiters/generic-cell.limiter.spec.ts +0 -361
  160. package/tests/integration/limiters/http-response-based.limiter.spec.ts +0 -833
  161. package/tests/integration/limiters/leaky-bucket.spec.ts +0 -357
  162. package/tests/integration/limiters/sliding-window-counter.limiter.spec.ts +0 -175
  163. package/tests/integration/limiters/sliding-window-log.spec.ts +0 -185
  164. package/tests/integration/limiters/token-bucket.limiter.spec.ts +0 -363
  165. package/tests/tsconfig.json +0 -4
  166. package/tests/unit/policies/composite.policy.spec.ts +0 -244
  167. package/tests/unit/policies/fixed-window.policy.spec.ts +0 -260
  168. package/tests/unit/policies/generic-cell.policy.spec.ts +0 -178
  169. package/tests/unit/policies/leaky-bucket.policy.spec.ts +0 -215
  170. package/tests/unit/policies/sliding-window-counter.policy.spec.ts +0 -209
  171. package/tests/unit/policies/sliding-window-log.policy.spec.ts +0 -285
  172. package/tests/unit/policies/token-bucket.policy.spec.ts +0 -371
  173. package/tests/unit/runtime/execution-tickets.spec.ts +0 -121
  174. package/tests/unit/runtime/in-memory-state-store.spec.ts +0 -238
  175. package/tests/unit/runtime/rate-limiter.executor.spec.ts +0 -353
  176. package/tests/unit/runtime/semaphore.spec.ts +0 -98
  177. package/tests/unit/runtime/task.spec.ts +0 -182
  178. package/tests/unit/utils/generate-random-string.spec.ts +0 -51
  179. package/tests/unit/utils/promise-with-resolvers.spec.ts +0 -57
  180. package/tests/unit/utils/sanitize-priority.spec.ts +0 -46
  181. package/tests/unit/utils/validate-cost.spec.ts +0 -48
  182. package/tsconfig.json +0 -14
  183. package/vitest.config.js +0 -22
@@ -1,182 +0,0 @@
1
- import { Priority } from '@stimulcross/ds-policy-priority-queue';
2
- import { describe, expect, it, vi } from 'vitest';
3
- import { RateLimitError, RateLimitErrorCode } from '../../../src/index.js';
4
- import { Task } from '../../../src/runtime/task.js';
5
-
6
- const defaultOpts = { id: 'task-1', key: 'key-1' };
7
-
8
- describe('Task', () => {
9
- describe('Initialization', () => {
10
- it('should initialize with default values', () => {
11
- const task = new Task(() => 'test', defaultOpts);
12
-
13
- expect(task.id).toBe('task-1');
14
- expect(task.key).toBe('key-1');
15
- expect(task.priority).toBe(Priority.Normal);
16
- expect(task.expiresAt).toBeUndefined();
17
- expect(task.isActive).toBe(true);
18
- expect(task.isAborted).toBe(false);
19
- expect(task.isCancellable).toBe(false);
20
- });
21
-
22
- it('should initialize with provided options', () => {
23
- const task = new Task(() => 'test', {
24
- ...defaultOpts,
25
- priority: Priority.High,
26
- expiresAt: 1000,
27
- });
28
-
29
- expect(task.priority).toBe(Priority.High);
30
- expect(task.expiresAt).toBe(1000);
31
- });
32
-
33
- it('should be cancellable when initialized with an abort signal', () => {
34
- const controller = new AbortController();
35
- const task = new Task(() => 'test', { ...defaultOpts, signal: controller.signal });
36
-
37
- expect(task.isCancellable).toBe(true);
38
- });
39
- });
40
-
41
- describe('Execution', () => {
42
- it('should resolve the promise when the task completes successfully', async () => {
43
- const taskFn = vi.fn().mockResolvedValue('success');
44
- const task = new Task(taskFn, defaultOpts);
45
-
46
- await task.run();
47
-
48
- await expect(task).resolves.toBe('success');
49
- expect(task.isActive).toBe(false);
50
- });
51
-
52
- it('should reject the promise when the task throws an error', async () => {
53
- const error = new Error('Task failed');
54
- const taskFn = vi.fn().mockRejectedValue(error);
55
- const task = new Task(taskFn, defaultOpts);
56
-
57
- await task.run();
58
-
59
- await expect(task).rejects.toThrow(error);
60
- expect(task.isActive).toBe(false);
61
- });
62
-
63
- it('should mark task as inactive immediately when run starts', () => {
64
- const task = new Task(async () => 'test', defaultOpts);
65
-
66
- void task.run();
67
-
68
- expect(task.isActive).toBe(false);
69
- });
70
- });
71
-
72
- describe('Promise API (PromiseLike)', () => {
73
- it('should support .then() chaining', async () => {
74
- const task = new Task(() => 'chaining', defaultOpts);
75
-
76
- const promise = task.then(val => `${val}-success`);
77
-
78
- await task.run();
79
-
80
- await expect(promise).resolves.toBe('chaining-success');
81
- });
82
-
83
- it('should support .catch() chaining', async () => {
84
- const error = new Error('Fail');
85
- const task = new Task(() => Promise.reject(error), defaultOpts);
86
-
87
- const promise = task.catch(e => e.message);
88
-
89
- await task.run();
90
-
91
- await expect(promise).resolves.toBe('Fail');
92
- });
93
-
94
- it('should support .finally() execution', async () => {
95
- const finallyFn = vi.fn();
96
- const task = new Task(() => 'test', defaultOpts);
97
-
98
- const promise = task.finally(finallyFn);
99
-
100
- await task.run();
101
- await promise;
102
-
103
- expect(finallyFn).toHaveBeenCalledOnce();
104
- });
105
- });
106
-
107
- describe('State Management', () => {
108
- it('should allow manual rejection', async () => {
109
- const task = new Task(() => 'test', defaultOpts);
110
- const reason = new Error('Manual rejection');
111
-
112
- task.reject(reason);
113
-
114
- expect(task.isActive).toBe(false);
115
- await expect(task).rejects.toThrow(reason);
116
- });
117
-
118
- it('should mark task as inactive when destroyed', () => {
119
- const task = new Task(() => 'test', defaultOpts);
120
-
121
- task.destroy();
122
-
123
- expect(task.isActive).toBe(false);
124
- });
125
- });
126
-
127
- describe('Abort Signal Handling', () => {
128
- it('should abort task, call handler, and reject with RateLimitError when signal triggers', async () => {
129
- const controller = new AbortController();
130
- const task = new Task(() => 'test', { ...defaultOpts, signal: controller.signal });
131
- const abortHandler = vi.fn();
132
-
133
- task.onAbort(abortHandler);
134
- controller.abort();
135
- // second call should not trigger handler
136
- controller.abort();
137
-
138
- expect(task.isAborted).toBe(true);
139
- expect(task.isActive).toBe(false);
140
- expect(abortHandler).toHaveBeenCalledOnce();
141
-
142
- await expect(task).rejects.toThrow(RateLimitError);
143
- await expect(task).rejects.toMatchObject({
144
- code: RateLimitErrorCode.Cancelled,
145
- message: 'Aborted by client',
146
- });
147
- });
148
-
149
- it('should clean up signal references and event listeners when destroyed', () => {
150
- const controller = new AbortController();
151
- const removeEventListenerSpy = vi.spyOn(controller.signal, 'removeEventListener');
152
- const task = new Task(() => 'test', { ...defaultOpts, signal: controller.signal });
153
-
154
- task.destroy();
155
-
156
- expect(task.isCancellable).toBe(false);
157
- expect(removeEventListenerSpy).toHaveBeenCalledWith('abort', expect.any(Function));
158
- });
159
-
160
- it('should safely destroy even if no abort handler is registered', () => {
161
- const controller = new AbortController();
162
- const task = new Task(() => 'test', { ...defaultOpts, signal: controller.signal });
163
-
164
- expect(() => {
165
- task.destroy();
166
- }).not.toThrow();
167
- });
168
-
169
- it('should not call abort handler if destroyed before signal aborts', () => {
170
- const controller = new AbortController();
171
- const task = new Task(() => 'test', { ...defaultOpts, signal: controller.signal });
172
- const abortHandler = vi.fn();
173
-
174
- task.onAbort(abortHandler);
175
- task.destroy();
176
- controller.abort();
177
-
178
- expect(abortHandler).not.toHaveBeenCalled();
179
- expect(task.isAborted).toBe(false);
180
- });
181
- });
182
- });
@@ -1,51 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { generateRandomString } from '../../../src/utils/generate-random-string.js';
3
-
4
- describe('generateRandomString', () => {
5
- const ALPHANUMERIC_REGEX = /^[A-Za-z\d]*$/u;
6
-
7
- describe('length and boundary conditions', () => {
8
- it('should return a string of length 7 by default', () => {
9
- expect(generateRandomString()).toHaveLength(7);
10
- });
11
-
12
- it('should return a string of the exact specified length', () => {
13
- expect(generateRandomString(5)).toHaveLength(5);
14
- expect(generateRandomString(100)).toHaveLength(100);
15
- });
16
-
17
- it('should return an empty string when length is 0', () => {
18
- expect(generateRandomString(0)).toBe('');
19
- });
20
- });
21
-
22
- describe('validation', () => {
23
- it('should throw a RangeError for negative lengths', () => {
24
- expect(() => generateRandomString(-1)).toThrow(RangeError);
25
- expect(() => generateRandomString(-5)).toThrow(/Invalid length/u);
26
- });
27
-
28
- it('should throw a RangeError for non-integer lengths', () => {
29
- expect(() => generateRandomString(5.5)).toThrow(RangeError);
30
- });
31
-
32
- it('should throw a RangeError for unsafe integers and non-numbers', () => {
33
- expect(() => generateRandomString(Number.NaN)).toThrow(RangeError);
34
- expect(() => generateRandomString(Infinity)).toThrow(RangeError);
35
- expect(() => generateRandomString(Number.MAX_SAFE_INTEGER + 1)).toThrow(RangeError);
36
- });
37
- });
38
-
39
- describe('content and semantics', () => {
40
- it('should contain only allowed alphanumeric characters', () => {
41
- const result = generateRandomString(50);
42
- expect(ALPHANUMERIC_REGEX.test(result)).toBe(true);
43
- });
44
-
45
- it('should generate different strings on subsequent calls', () => {
46
- const string1 = generateRandomString(20);
47
- const string2 = generateRandomString(20);
48
- expect(string1).not.toBe(string2);
49
- });
50
- });
51
- });
@@ -1,57 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { promiseWithResolvers } from '../../../src/utils/promise-with-resolvers.js';
3
-
4
- describe('promiseWithResolvers', () => {
5
- it('should return an object with promise, resolve and reject', () => {
6
- const r = promiseWithResolvers<number>();
7
-
8
- expect(r).toBeTruthy();
9
- expect(r.promise).toBeInstanceOf(Promise);
10
- expect(typeof r.resolve).toBe('function');
11
- expect(typeof r.reject).toBe('function');
12
- });
13
-
14
- it('should resolve the promise with a value', async () => {
15
- const r = promiseWithResolvers<number>();
16
-
17
- r.resolve(42);
18
-
19
- await expect(r.promise).resolves.toBe(42);
20
- });
21
-
22
- it('should resolve the promise with a PromiseLike value', async () => {
23
- const r = promiseWithResolvers<number>();
24
-
25
- r.resolve(Promise.resolve(7));
26
-
27
- await expect(r.promise).resolves.toBe(7);
28
- });
29
-
30
- it('should reject the promise with a reason', async () => {
31
- const r = promiseWithResolvers<number>();
32
- const err = new Error('boom');
33
-
34
- r.reject(err);
35
-
36
- await expect(r.promise).rejects.toBe(err);
37
- });
38
-
39
- it('should settle only once (resolve then reject)', async () => {
40
- const r = promiseWithResolvers<number>();
41
-
42
- r.resolve(1);
43
- r.reject(new Error('should be ignored'));
44
-
45
- await expect(r.promise).resolves.toBe(1);
46
- });
47
-
48
- it('should settle only once (reject then resolve)', async () => {
49
- const r = promiseWithResolvers<number>();
50
- const err = new Error('first');
51
-
52
- r.reject(err);
53
- r.resolve(123);
54
-
55
- await expect(r.promise).rejects.toBe(err);
56
- });
57
- });
@@ -1,46 +0,0 @@
1
- import { Priority } from '@stimulcross/ds-policy-priority-queue';
2
- import { describe, it, expect } from 'vitest';
3
- import { sanitizePriority } from '../../../src/utils/sanitize-priority.js';
4
-
5
- describe('sanitizePriority (updated)', () => {
6
- it('should return Priority.Normal for non-finite numbers', () => {
7
- expect(sanitizePriority(Number.NaN)).toBe(Priority.Normal);
8
- expect(sanitizePriority(Number.POSITIVE_INFINITY)).toBe(Priority.Normal);
9
- expect(sanitizePriority(Number.NEGATIVE_INFINITY)).toBe(Priority.Normal);
10
- });
11
-
12
- it('should clamp below Priority.Lowest to Priority.Lowest (before rounding)', () => {
13
- expect(sanitizePriority(Priority.Lowest - 1)).toBe(Priority.Lowest);
14
- expect(sanitizePriority(Priority.Lowest - 0.1)).toBe(Priority.Lowest);
15
- expect(sanitizePriority(Priority.Lowest - 1000)).toBe(Priority.Lowest);
16
- });
17
-
18
- it('should clamp above Priority.Highest to Priority.Highest (before rounding)', () => {
19
- expect(sanitizePriority(Priority.Highest + 1)).toBe(Priority.Highest);
20
- expect(sanitizePriority(Priority.Highest + 0.1)).toBe(Priority.Highest);
21
- expect(sanitizePriority(Priority.Highest + 1000)).toBe(Priority.Highest);
22
- });
23
-
24
- it('should round non-integer priorities that are already within range', () => {
25
- expect(sanitizePriority(1.4)).toBe(1);
26
- expect(sanitizePriority(1.5)).toBe(2);
27
- expect(sanitizePriority(3.7)).toBe(4);
28
- });
29
-
30
- it('should return the same value for integers within range', () => {
31
- expect(sanitizePriority(Priority.Lowest)).toBe(Priority.Lowest);
32
- expect(sanitizePriority(Priority.Normal)).toBe(Priority.Normal);
33
- expect(sanitizePriority(Priority.Highest)).toBe(Priority.Highest);
34
-
35
- const mid = Math.trunc((Priority.Lowest + Priority.Highest) / 2);
36
- expect(sanitizePriority(mid)).toBe(mid);
37
- });
38
-
39
- it('should not clamp after rounding (behavior change guard)', () => {
40
- expect(sanitizePriority(Priority.Highest - 0.6)).toBe(Priority.Highest - 1);
41
- expect(sanitizePriority(Priority.Highest - 0.4)).toBe(Priority.Highest);
42
-
43
- expect(sanitizePriority(Priority.Lowest + 0.6)).toBe(Priority.Lowest + 1);
44
- expect(sanitizePriority(Priority.Lowest + 0.4)).toBe(Priority.Lowest);
45
- });
46
- });
@@ -1,48 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { InvalidCostError } from '../../../src/errors/invalid-cost.error.js';
3
- import { validateCost } from '../../../src/utils/validate-cost.js';
4
-
5
- describe('validateCost', () => {
6
- it('should not throw for a non-negative safe integer', () => {
7
- expect(() => validateCost(0)).not.toThrow();
8
- expect(() => validateCost(1)).not.toThrow();
9
- expect(() => validateCost(123_456)).not.toThrow();
10
- });
11
-
12
- it('should throw InvalidCostError for negative numbers', () => {
13
- expect(() => validateCost(-1)).toThrow(InvalidCostError);
14
- });
15
-
16
- it('should throw InvalidCostError for non-integers', () => {
17
- expect(() => validateCost(1.1)).toThrow(InvalidCostError);
18
- });
19
-
20
- it('should throw InvalidCostError for NaN', () => {
21
- expect(() => validateCost(Number.NaN)).toThrow(InvalidCostError);
22
- });
23
-
24
- it('should throw InvalidCostError for Infinity', () => {
25
- expect(() => validateCost(Number.POSITIVE_INFINITY)).toThrow(InvalidCostError);
26
- expect(() => validateCost(Number.NEGATIVE_INFINITY)).toThrow(InvalidCostError);
27
- });
28
-
29
- it('should throw InvalidCostError for non-safe integers', () => {
30
- expect(() => validateCost(Number.MAX_SAFE_INTEGER + 1)).toThrow(InvalidCostError);
31
- });
32
-
33
- it('should respect max when provided', () => {
34
- expect(() => validateCost(10, 10)).not.toThrow();
35
- expect(() => validateCost(11, 10)).toThrow(InvalidCostError);
36
- });
37
-
38
- it('should respect min when provided', () => {
39
- expect(() => validateCost(10, undefined, 10)).not.toThrow();
40
- expect(() => validateCost(9, undefined, 10)).toThrow(InvalidCostError);
41
- });
42
-
43
- it('should respect both min and max when provided', () => {
44
- expect(() => validateCost(5, 10, 0)).not.toThrow();
45
- expect(() => validateCost(11, 10, 0)).toThrow(InvalidCostError);
46
- expect(() => validateCost(-1, 10, 0)).toThrow(InvalidCostError);
47
- });
48
- });
package/tsconfig.json DELETED
@@ -1,14 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "Node16",
6
- "lib": ["ES2022", "ES2024.Promise"],
7
- "esModuleInterop": true,
8
- "forceConsistentCasingInFileNames": true,
9
- "strict": true,
10
- "skipLibCheck": true,
11
- "outDir": "lib"
12
- },
13
- "include": ["src"]
14
- }
package/vitest.config.js DELETED
@@ -1,22 +0,0 @@
1
- import { defineConfig } from 'vitest/config';
2
-
3
- export default defineConfig({
4
- test: {
5
- environment: 'node',
6
- include: ['tests/**/*.spec.ts'],
7
- coverage: {
8
- provider: 'v8',
9
- reportsDirectory: './coverage',
10
- include: ['src/limiters', 'src/runtime', 'src/utils'],
11
- exclude: [
12
- 'src/**/index.ts',
13
- 'src/**/*.info.ts',
14
- 'src/**/*.state.ts',
15
- 'src/**/*.status.ts',
16
- 'src/**/*.options.ts',
17
- 'src/runtime/queue/queue*.ts',
18
- 'src/**/http-limit-info.extractor',
19
- ],
20
- },
21
- },
22
- });