accessio 1.0.0 → 1.1.0

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 (40) hide show
  1. package/README.md +121 -391
  2. package/cjs/accessio.cjs +23 -52
  3. package/cjs/accessio.cjs.map +1 -1
  4. package/cjs/core/accessioError.cjs +1 -7
  5. package/cjs/core/accessioError.cjs.map +1 -1
  6. package/cjs/core/buildURL.cjs +1 -5
  7. package/cjs/core/buildURL.cjs.map +1 -1
  8. package/cjs/core/mergeConfig.cjs +2 -4
  9. package/cjs/core/mergeConfig.cjs.map +1 -1
  10. package/cjs/core/request.cjs +44 -66
  11. package/cjs/core/request.cjs.map +1 -1
  12. package/cjs/core/retry.cjs +2 -8
  13. package/cjs/core/retry.cjs.map +1 -1
  14. package/cjs/defaults/index.cjs.map +1 -1
  15. package/cjs/defaults/transforms.cjs.map +1 -1
  16. package/cjs/helpers/debug.cjs +1 -3
  17. package/cjs/helpers/debug.cjs.map +1 -1
  18. package/cjs/helpers/parseHeaders.cjs.map +1 -1
  19. package/cjs/helpers/rateLimiter.cjs +22 -14
  20. package/cjs/helpers/rateLimiter.cjs.map +1 -1
  21. package/cjs/helpers/transformData.cjs.map +1 -1
  22. package/cjs/index.cjs.map +1 -1
  23. package/cjs/interceptors/interceptorManager.cjs +40 -3
  24. package/cjs/interceptors/interceptorManager.cjs.map +1 -1
  25. package/package.json +6 -14
  26. package/src/accessio.ts +28 -72
  27. package/src/core/accessioError.ts +1 -7
  28. package/src/core/buildURL.ts +4 -13
  29. package/src/core/mergeConfig.ts +6 -16
  30. package/src/core/request.ts +44 -74
  31. package/src/core/retry.ts +4 -15
  32. package/src/defaults/index.ts +1 -3
  33. package/src/defaults/transforms.ts +1 -4
  34. package/src/helpers/debug.ts +12 -22
  35. package/src/helpers/parseHeaders.ts +1 -3
  36. package/src/helpers/rateLimiter.ts +28 -23
  37. package/src/helpers/transformData.ts +1 -4
  38. package/src/index.ts +3 -11
  39. package/src/interceptors/interceptorManager.ts +50 -2
  40. package/src/types.ts +7 -68
@@ -36,10 +36,7 @@ function flattenHeaders(
36
36
  }
37
37
 
38
38
  for (const key in headers) {
39
- if (
40
- Object.prototype.hasOwnProperty.call(headers, key) &&
41
- !METHOD_KEYS.has(key)
42
- ) {
39
+ if (Object.prototype.hasOwnProperty.call(headers, key) && !METHOD_KEYS.has(key)) {
43
40
  merged[key] = headers[key] as unknown as string;
44
41
  }
45
42
  }
@@ -48,9 +45,7 @@ function flattenHeaders(
48
45
  }
49
46
 
50
47
  function removeContentType(headers: Record<string, string>): void {
51
- const key = Object.keys(headers).find(
52
- (k) => k.toLowerCase() === 'content-type',
53
- );
48
+ const key = Object.keys(headers).find((k) => k.toLowerCase() === 'content-type');
54
49
  if (key) {
55
50
  delete headers[key];
56
51
  }
@@ -64,9 +59,35 @@ function buildTransformArray(
64
59
  return [transform];
65
60
  }
66
61
 
67
- export default function dispatchRequest(
68
- config: AccessioRequestConfig,
69
- ): Promise<AccessioResponse> {
62
+ function setBasicAuth(config: AccessioRequestConfig, headers: Record<string, string>): void {
63
+ if (!config.auth) return;
64
+ const username = config.auth.username || '';
65
+ const password = config.auth.password || '';
66
+ const credentials = `${username}:${password}`;
67
+
68
+ let encoded: string;
69
+ if (typeof Buffer !== 'undefined') {
70
+ encoded = Buffer.from(credentials).toString('base64');
71
+ } else {
72
+ const bytes = new TextEncoder().encode(credentials);
73
+ const binString = Array.from(bytes, (x) => String.fromCodePoint(x)).join('');
74
+ encoded = btoa(binString);
75
+ }
76
+ headers['Authorization'] = `Basic ${encoded}`;
77
+ }
78
+
79
+ async function readResponseData(fetchResponse: Response, responseType: string): Promise<unknown> {
80
+ switch (responseType) {
81
+ case 'arraybuffer': return await fetchResponse.arrayBuffer();
82
+ case 'blob': return await fetchResponse.blob();
83
+ case 'text': return await fetchResponse.text();
84
+ case 'stream': return fetchResponse.body;
85
+ case 'json':
86
+ default: return await fetchResponse.text();
87
+ }
88
+ }
89
+
90
+ export default function dispatchRequest(config: AccessioRequestConfig): Promise<AccessioResponse> {
70
91
  const fullURL =
71
92
  config._builtUrl ||
72
93
  buildURL(
@@ -76,19 +97,11 @@ export default function dispatchRequest(
76
97
  config.paramsSerializer,
77
98
  );
78
99
 
79
- const flatHeaders = flattenHeaders(
80
- config.headers as HeadersConfig | undefined,
81
- config.method,
82
- );
100
+ const flatHeaders = flattenHeaders(config.headers as HeadersConfig | undefined, config.method);
83
101
 
84
102
  const requestTransforms = buildTransformArray(config.transformRequest);
85
103
 
86
- const requestData = transformData(
87
- requestTransforms,
88
- config.data,
89
- flatHeaders,
90
- config,
91
- );
104
+ const requestData = transformData(requestTransforms, config.data, flatHeaders, config);
92
105
 
93
106
  if (
94
107
  requestData === null ||
@@ -98,24 +111,7 @@ export default function dispatchRequest(
98
111
  removeContentType(flatHeaders);
99
112
  }
100
113
 
101
- if (config.auth) {
102
- const username = config.auth.username || '';
103
- const password = config.auth.password || '';
104
- const credentials = `${username}:${password}`;
105
-
106
- let encoded: string;
107
- if (typeof Buffer !== 'undefined') {
108
- encoded = Buffer.from(credentials).toString('base64');
109
- } else {
110
- const bytes = new TextEncoder().encode(credentials);
111
- const binString = Array.from(bytes, (x) =>
112
- String.fromCodePoint(x),
113
- ).join('');
114
- encoded = btoa(binString);
115
- }
116
-
117
- flatHeaders['Authorization'] = `Basic ${encoded}`;
118
- }
114
+ setBasicAuth(config, flatHeaders);
119
115
 
120
116
  const fetchOptions: RequestInit = {
121
117
  method: (config.method || 'GET').toUpperCase(),
@@ -158,10 +154,7 @@ export default function dispatchRequest(
158
154
 
159
155
  if (config.signal) {
160
156
  if (typeof AbortSignal.any === 'function') {
161
- fetchOptions.signal = AbortSignal.any([
162
- config.signal,
163
- abortController.signal,
164
- ]);
157
+ fetchOptions.signal = AbortSignal.any([config.signal, abortController.signal]);
165
158
  } else {
166
159
  if (config.signal.aborted) {
167
160
  abortController.abort(config.signal.reason);
@@ -190,24 +183,7 @@ export default function dispatchRequest(
190
183
  const responseType = config.responseType || 'json';
191
184
 
192
185
  try {
193
- switch (responseType) {
194
- case 'arraybuffer':
195
- responseData = await fetchResponse.arrayBuffer();
196
- break;
197
- case 'blob':
198
- responseData = await fetchResponse.blob();
199
- break;
200
- case 'text':
201
- responseData = await fetchResponse.text();
202
- break;
203
- case 'stream':
204
- responseData = fetchResponse.body;
205
- break;
206
- case 'json':
207
- default:
208
- responseData = await fetchResponse.text();
209
- break;
210
- }
186
+ responseData = await readResponseData(fetchResponse, responseType);
211
187
  } catch (readError) {
212
188
  throw AccessioError.from(
213
189
  readError as Error,
@@ -222,12 +198,7 @@ export default function dispatchRequest(
222
198
 
223
199
  const responseTransforms = buildTransformArray(config.transformResponse);
224
200
 
225
- responseData = transformData(
226
- responseTransforms,
227
- responseData,
228
- responseHeaders,
229
- config,
230
- );
201
+ responseData = transformData(responseTransforms, responseData, responseHeaders, config);
231
202
 
232
203
  const response: AccessioResponse = {
233
204
  data: responseData,
@@ -240,7 +211,12 @@ export default function dispatchRequest(
240
211
  };
241
212
 
242
213
  return new Promise<AccessioResponse>((resolve, reject) => {
243
- settle(resolve as (value: AccessioResponse) => void, reject as (reason: AccessioError) => void, response, config);
214
+ settle(
215
+ resolve as (value: AccessioResponse) => void,
216
+ reject as (reason: AccessioError) => void,
217
+ response,
218
+ config,
219
+ );
244
220
  });
245
221
  })
246
222
  .catch((error) => {
@@ -258,13 +234,7 @@ export default function dispatchRequest(
258
234
  null,
259
235
  );
260
236
  }
261
- throw new AccessioError(
262
- 'Request aborted',
263
- AccessioError.ERR_CANCELED,
264
- config,
265
- null,
266
- null,
267
- );
237
+ throw new AccessioError('Request aborted', AccessioError.ERR_CANCELED, config, null, null);
268
238
  }
269
239
 
270
240
  throw AccessioError.from(
package/src/core/retry.ts CHANGED
@@ -1,8 +1,4 @@
1
- import {
2
- ERR_CANCELED,
3
- ERR_NETWORK,
4
- ETIMEDOUT,
5
- } from '../constants/errorCodes';
1
+ import { ERR_CANCELED, ERR_NETWORK, ETIMEDOUT } from '../constants/errorCodes';
6
2
  import type {
7
3
  AccessioRequestConfig,
8
4
  AccessioResponse,
@@ -51,9 +47,7 @@ function sleep(ms: number, options?: { signal?: AbortSignal }): Promise<void> {
51
47
  if (options?.signal) {
52
48
  if (options.signal.aborted) {
53
49
  clearTimeout(timeoutId);
54
- return reject(
55
- options.signal.reason || new Error('Sleep aborted'),
56
- );
50
+ return reject(options.signal.reason || new Error('Sleep aborted'));
57
51
  }
58
52
 
59
53
  onAbort = () => {
@@ -77,8 +71,7 @@ async function retryRequest(
77
71
  }
78
72
 
79
73
  const retryDelay = config.retryDelay ?? 1000;
80
- const retryCondition: RetryConditionFunction =
81
- config.retryCondition ?? defaultRetryCondition;
74
+ const retryCondition: RetryConditionFunction = config.retryCondition ?? defaultRetryCondition;
82
75
 
83
76
  let lastError: any;
84
77
 
@@ -99,11 +92,7 @@ async function retryRequest(
99
92
  const delay = calculateDelay(attempt, retryDelay);
100
93
 
101
94
  if (typeof config.onRetry === 'function') {
102
- (config.onRetry as OnRetryFunction)(
103
- attempt + 1,
104
- error as AccessioError,
105
- config,
106
- );
95
+ (config.onRetry as OnRetryFunction)(attempt + 1, error as AccessioError, config);
107
96
  }
108
97
 
109
98
  await sleep(delay, { signal: config.signal });
@@ -24,9 +24,7 @@ const defaults: AccessioRequestConfig = {
24
24
  },
25
25
  transformRequest: [defaultTransformRequest],
26
26
  transformResponse: [defaultTransformResponse],
27
- validateStatus: function defaultValidateStatus(
28
- status: number,
29
- ): boolean {
27
+ validateStatus: function defaultValidateStatus(status: number): boolean {
30
28
  return status >= 200 && status < 300;
31
29
  },
32
30
  responseType: 'json',
@@ -1,7 +1,4 @@
1
- export function defaultTransformRequest(
2
- data: unknown,
3
- headers: Record<string, string>,
4
- ): unknown {
1
+ export function defaultTransformRequest(data: unknown, headers: Record<string, string>): unknown {
5
2
  if (data === null || data === undefined) {
6
3
  return data;
7
4
  }
@@ -8,9 +8,11 @@ function formatBytes(bytes: number): string {
8
8
  return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${sizes[i]}`;
9
9
  }
10
10
 
11
- function sanitizeConfigForLog(
12
- config: AccessioRequestConfig,
13
- ): { params: Record<string, unknown> | undefined; timeout: number | undefined; retry: number | undefined } {
11
+ function sanitizeConfigForLog(config: AccessioRequestConfig): {
12
+ params: Record<string, unknown> | undefined;
13
+ timeout: number | undefined;
14
+ retry: number | undefined;
15
+ } {
14
16
  return {
15
17
  params: config.params,
16
18
  timeout: config.timeout,
@@ -18,10 +20,7 @@ function sanitizeConfigForLog(
18
20
  };
19
21
  }
20
22
 
21
- export function logRequest(
22
- config: AccessioRequestConfig,
23
- fullUrl: string,
24
- ): void {
23
+ export function logRequest(config: AccessioRequestConfig, fullUrl: string): void {
25
24
  if (!config.debug) return;
26
25
 
27
26
  const safe = sanitizeConfigForLog(config);
@@ -37,8 +36,7 @@ export function logRequest(
37
36
 
38
37
  if (config.data && typeof config.data === 'object') {
39
38
  const preview = JSON.stringify(config.data);
40
- const truncated =
41
- preview.length > 200 ? `${preview.substring(0, 200)}...` : preview;
39
+ const truncated = preview.length > 200 ? `${preview.substring(0, 200)}...` : preview;
42
40
  parts.push(` Body: ${truncated}`);
43
41
  }
44
42
 
@@ -57,19 +55,11 @@ export function logResponse(response: AccessioResponse): void {
57
55
  if (!response.config || !response.config.debug) return;
58
56
  const status = response.status;
59
57
  const statusText = response.statusText || '';
60
- const duration =
61
- response.duration != null ? `${response.duration}ms` : '??';
62
-
63
- const statusIcon =
64
- status >= 200 && status < 300
65
- ? '✅'
66
- : status >= 400
67
- ? '❌'
68
- : '⚠️';
69
-
70
- const parts: string[] = [
71
- `🐦‍⬛ [Accessio] ← ${statusIcon} ${status} ${statusText} (${duration})`,
72
- ];
58
+ const duration = response.duration != null ? `${response.duration}ms` : '??';
59
+
60
+ const statusIcon = status >= 200 && status < 300 ? '✅' : status >= 400 ? '❌' : '⚠️';
61
+
62
+ const parts: string[] = [`🐦‍⬛ [Accessio] ← ${statusIcon} ${status} ${statusText} (${duration})`];
73
63
 
74
64
  if (response.data) {
75
65
  try {
@@ -1,6 +1,4 @@
1
- export default function parseHeaders(
2
- headers: any,
3
- ): Record<string, string> {
1
+ export default function parseHeaders(headers: any): Record<string, string> {
4
2
  const parsed: Record<string, string> = {};
5
3
 
6
4
  if (!headers) return parsed;
@@ -1,32 +1,33 @@
1
- import type {
2
- RateLimiter,
3
- AccessioRequestConfig,
4
- AccessioResponse,
5
- } from '../types';
1
+ import type { RateLimiter, AccessioRequestConfig, AccessioResponse } from '../types';
6
2
 
7
3
  interface QueueItem {
8
4
  resolve: () => void;
9
5
  reject: (reason: Error) => void;
10
6
  }
11
7
 
12
- export function createRateLimiter(maxConcurrent: number = Infinity): RateLimiter {
13
- if (
14
- maxConcurrent !== Infinity &&
15
- (!Number.isInteger(maxConcurrent) || maxConcurrent < 1)
16
- ) {
8
+ export function createRateLimiter(
9
+ maxConcurrent: number = Infinity,
10
+ maxQueueSize: number = Infinity,
11
+ ): RateLimiter {
12
+ if (maxConcurrent !== Infinity && (!Number.isInteger(maxConcurrent) || maxConcurrent < 1)) {
17
13
  throw new RangeError(
18
14
  `[Accessio] maxConcurrent must be a positive integer or Infinity, got: ${maxConcurrent}`,
19
15
  );
20
16
  }
17
+ if (maxQueueSize !== Infinity && (!Number.isInteger(maxQueueSize) || maxQueueSize < 1)) {
18
+ throw new RangeError(
19
+ `[Accessio] maxQueueSize must be a positive integer or Infinity, got: ${maxQueueSize}`,
20
+ );
21
+ }
21
22
  let active = 0;
22
23
  let destroyed = false;
23
- const queue: QueueItem[] = [];
24
+ let headIndex = 0;
25
+ let tailIndex = 0;
26
+ const queue: Record<number, QueueItem> = {};
24
27
 
25
28
  function acquire(): Promise<void> {
26
29
  if (destroyed) {
27
- return Promise.reject(
28
- new Error('[Accessio] Rate limiter has been destroyed'),
29
- );
30
+ return Promise.reject(new Error('[Accessio] Rate limiter has been destroyed'));
30
31
  }
31
32
 
32
33
  if (active < maxConcurrent) {
@@ -34,8 +35,12 @@ export function createRateLimiter(maxConcurrent: number = Infinity): RateLimiter
34
35
  return Promise.resolve();
35
36
  }
36
37
 
38
+ if (tailIndex - headIndex >= maxQueueSize) {
39
+ return Promise.reject(new Error(`[Accessio] Rate limiter queue size exceeded maxQueueSize (${maxQueueSize})`));
40
+ }
41
+
37
42
  return new Promise((resolve, reject) => {
38
- queue.push({ resolve, reject });
43
+ queue[tailIndex++] = { resolve, reject };
39
44
  });
40
45
  }
41
46
 
@@ -46,20 +51,20 @@ export function createRateLimiter(maxConcurrent: number = Infinity): RateLimiter
46
51
 
47
52
  active--;
48
53
 
49
- if (queue.length > 0 && active < maxConcurrent) {
54
+ if (tailIndex - headIndex > 0 && active < maxConcurrent) {
50
55
  active++;
51
- const next = queue.shift();
56
+ const next = queue[headIndex];
57
+ delete queue[headIndex++];
52
58
  next?.resolve();
53
59
  }
54
60
  }
55
61
 
56
62
  function destroy(): void {
57
63
  destroyed = true;
58
- const reason = new Error(
59
- '[Accessio] Rate limiter destroyed pending request cancelled',
60
- );
61
- while (queue.length > 0) {
62
- const next = queue.shift();
64
+ const reason = new Error('[Accessio] Rate limiter destroyed — pending request cancelled');
65
+ while (tailIndex - headIndex > 0) {
66
+ const next = queue[headIndex];
67
+ delete queue[headIndex++];
63
68
  next?.reject(reason);
64
69
  }
65
70
  }
@@ -69,7 +74,7 @@ export function createRateLimiter(maxConcurrent: number = Infinity): RateLimiter
69
74
  release,
70
75
  destroy,
71
76
  get pending() {
72
- return queue.length;
77
+ return tailIndex - headIndex;
73
78
  },
74
79
  get active() {
75
80
  return active;
@@ -1,8 +1,5 @@
1
1
  import AccessioError from '../core/accessioError';
2
- import type {
3
- TransformFunction,
4
- AccessioRequestConfig,
5
- } from '../types';
2
+ import type { TransformFunction, AccessioRequestConfig } from '../types';
6
3
 
7
4
  export default function transformData(
8
5
  transforms: TransformFunction | TransformFunction[] | undefined,
package/src/index.ts CHANGED
@@ -46,23 +46,15 @@ function createInstance(defaultConfig: AccessioRequestConfig) {
46
46
  instance.all = function all(promises: any[]): Promise<any[]> {
47
47
  return Promise.all(promises);
48
48
  };
49
- instance.spread = function spread<T>(
50
- callback: (...args: any[]) => T,
51
- ): (arr: any[]) => T {
49
+ instance.spread = function spread<T>(callback: (...args: any[]) => T): (arr: any[]) => T {
52
50
  return function wrap(arr: any[]): T {
53
51
  return callback(...arr);
54
52
  };
55
53
  };
56
54
  instance.isCancel = function isCancel(value: any): boolean {
57
- return !!(
58
- value &&
59
- value.isAccessioError &&
60
- value.code === ERR_CANCELED
61
- );
55
+ return !!(value && value.isAccessioError && value.code === ERR_CANCELED);
62
56
  };
63
- instance.isAccessioError = function isAccessioError(
64
- value: any,
65
- ): boolean {
57
+ instance.isAccessioError = function isAccessioError(value: any): boolean {
66
58
  return (
67
59
  value instanceof AccessioError ||
68
60
  !!(value && typeof value === 'object' && value.isAccessioError === true)
@@ -1,5 +1,53 @@
1
- import { InterceptorManager } from '../types';
1
+ import type { TransformFunction, InterceptorHandler, InterceptorOptions } from '../types';
2
2
 
3
- export { InterceptorManager };
3
+ export class InterceptorManager {
4
+ handlers: Array<InterceptorHandler | null>;
5
+ private _activeCount: number;
6
+
7
+ constructor() {
8
+ this.handlers = [];
9
+ this._activeCount = 0;
10
+ }
11
+
12
+ use(
13
+ fulfilled: TransformFunction | null,
14
+ rejected?: ((error: unknown) => unknown) | null,
15
+ options: InterceptorOptions = {},
16
+ ): number {
17
+ this.handlers.push({
18
+ fulfilled: fulfilled || null,
19
+ rejected: rejected || null,
20
+ synchronous: options.synchronous || false,
21
+ runWhen: options.runWhen || null,
22
+ });
23
+
24
+ this._activeCount++;
25
+ return this.handlers.length - 1;
26
+ }
27
+
28
+ eject(id: number): void {
29
+ if (this.handlers[id]) {
30
+ this.handlers[id] = null;
31
+ this._activeCount--;
32
+ }
33
+ }
34
+
35
+ clear(): void {
36
+ this.handlers = [];
37
+ this._activeCount = 0;
38
+ }
39
+
40
+ forEach(fn: (handler: InterceptorHandler) => void): void {
41
+ for (const handler of this.handlers) {
42
+ if (handler !== null) {
43
+ fn(handler);
44
+ }
45
+ }
46
+ }
47
+
48
+ get size(): number {
49
+ return this._activeCount;
50
+ }
51
+ }
4
52
 
5
53
  export default InterceptorManager;
package/src/types.ts CHANGED
@@ -1,18 +1,8 @@
1
- export type Method =
2
- | 'get'
3
- | 'delete'
4
- | 'head'
5
- | 'options'
6
- | 'post'
7
- | 'put'
8
- | 'patch';
9
-
10
- export type ResponseType =
11
- | 'json'
12
- | 'text'
13
- | 'blob'
14
- | 'arraybuffer'
15
- | 'stream';
1
+ import type InterceptorManager from './interceptors/interceptorManager';
2
+
3
+ export type Method = 'get' | 'delete' | 'head' | 'options' | 'post' | 'put' | 'patch';
4
+
5
+ export type ResponseType = 'json' | 'text' | 'blob' | 'arraybuffer' | 'stream';
16
6
 
17
7
  export interface AuthConfig {
18
8
  username: string;
@@ -21,10 +11,7 @@ export interface AuthConfig {
21
11
 
22
12
  export type ParamsSerializer = (params: Record<string, unknown>) => string;
23
13
 
24
- export type TransformFunction = (
25
- data: unknown,
26
- headers: Record<string, string>,
27
- ) => unknown;
14
+ export type TransformFunction = (data: unknown, headers: Record<string, string>) => unknown;
28
15
 
29
16
  export type RetryConditionFunction = (error: AccessioError) => boolean;
30
17
 
@@ -99,55 +86,7 @@ export interface AccessioError extends Error {
99
86
  toJSON(): Record<string, unknown>;
100
87
  }
101
88
 
102
- export class InterceptorManager {
103
- handlers: Array<InterceptorHandler | null>;
104
- private _activeCount: number;
105
-
106
- constructor() {
107
- this.handlers = [];
108
- this._activeCount = 0;
109
- }
110
-
111
- use(
112
- fulfilled: TransformFunction,
113
- rejected?: (error: unknown) => unknown,
114
- options: InterceptorOptions = {},
115
- ): number {
116
- this.handlers.push({
117
- fulfilled: fulfilled || null,
118
- rejected: rejected || null,
119
- synchronous: options.synchronous || false,
120
- runWhen: options.runWhen || null,
121
- });
122
-
123
- this._activeCount++;
124
- return this.handlers.length - 1;
125
- }
126
-
127
- eject(id: number): void {
128
- if (this.handlers[id]) {
129
- this.handlers[id] = null;
130
- this._activeCount--;
131
- }
132
- }
133
-
134
- clear(): void {
135
- this.handlers = [];
136
- this._activeCount = 0;
137
- }
138
-
139
- forEach(fn: (handler: InterceptorHandler) => void): void {
140
- for (const handler of this.handlers) {
141
- if (handler !== null) {
142
- fn(handler);
143
- }
144
- }
145
- }
146
-
147
- get size(): number {
148
- return this._activeCount;
149
- }
150
- }
89
+
151
90
 
152
91
  export interface RateLimiter {
153
92
  acquire: () => Promise<void>;