@defai.digital/resilience-domain 13.0.3

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.
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Loop Guard Implementation
3
+ *
4
+ * Prevents infinite loops and runaway execution.
5
+ */
6
+ import { LoopGuardErrorCodes, createDefaultLoopGuardConfig, createLoopGuardContext, } from './_contracts.js';
7
+ /**
8
+ * Loop guard error
9
+ */
10
+ export class LoopGuardError extends Error {
11
+ code;
12
+ context;
13
+ constructor(code, message, context) {
14
+ super(message);
15
+ this.code = code;
16
+ this.context = context;
17
+ this.name = 'LoopGuardError';
18
+ }
19
+ static maxIterations(context) {
20
+ return new LoopGuardError(LoopGuardErrorCodes.MAX_ITERATIONS, `Maximum iterations (${context.iterations}) exceeded`, context);
21
+ }
22
+ static maxDuration(context) {
23
+ return new LoopGuardError(LoopGuardErrorCodes.MAX_DURATION, `Maximum duration (${context.elapsedMs}ms) exceeded`, context);
24
+ }
25
+ static contextNotFound(contextId) {
26
+ return new LoopGuardError(LoopGuardErrorCodes.CONTEXT_NOT_FOUND, `Loop guard context not found: ${contextId}`);
27
+ }
28
+ }
29
+ /**
30
+ * Creates a loop guard
31
+ */
32
+ export function createLoopGuard(config) {
33
+ const cfg = { ...createDefaultLoopGuardConfig(), ...config };
34
+ const contexts = new Map();
35
+ const listeners = new Set();
36
+ function emitWarning(contextId, result) {
37
+ for (const listener of listeners) {
38
+ try {
39
+ listener(contextId, result);
40
+ }
41
+ catch {
42
+ // Ignore listener errors
43
+ }
44
+ }
45
+ }
46
+ return {
47
+ startContext(contextId, metadata) {
48
+ const context = createLoopGuardContext(contextId);
49
+ contexts.set(contextId, {
50
+ ...context,
51
+ metadata,
52
+ startTime: Date.now(),
53
+ });
54
+ },
55
+ checkIteration(contextId) {
56
+ const context = contexts.get(contextId);
57
+ if (!context) {
58
+ throw LoopGuardError.contextNotFound(contextId);
59
+ }
60
+ // Update iteration and elapsed time
61
+ context.iterations++;
62
+ context.elapsedMs = Date.now() - context.startTime;
63
+ // Check max iterations
64
+ if (context.iterations >= cfg.maxIterations) {
65
+ return {
66
+ status: 'blocked',
67
+ iteration: context.iterations,
68
+ elapsedMs: context.elapsedMs,
69
+ reason: `Maximum iterations (${cfg.maxIterations}) exceeded`,
70
+ blockType: 'max-iterations',
71
+ };
72
+ }
73
+ // Check max duration
74
+ if (context.elapsedMs >= cfg.maxDurationMs) {
75
+ return {
76
+ status: 'blocked',
77
+ iteration: context.iterations,
78
+ elapsedMs: context.elapsedMs,
79
+ reason: `Maximum duration (${cfg.maxDurationMs}ms) exceeded`,
80
+ blockType: 'max-duration',
81
+ };
82
+ }
83
+ // Check iteration warning
84
+ if (!context.warningIssued && context.iterations >= cfg.warnAtIterations) {
85
+ context.warningIssued = true;
86
+ const result = {
87
+ status: 'warning',
88
+ iteration: context.iterations,
89
+ elapsedMs: context.elapsedMs,
90
+ message: `Approaching iteration limit (${context.iterations}/${cfg.maxIterations})`,
91
+ warningType: 'iteration',
92
+ };
93
+ emitWarning(contextId, result);
94
+ return result;
95
+ }
96
+ // Check duration warning
97
+ if (!context.warningIssued && context.elapsedMs >= cfg.warnAtDurationMs) {
98
+ context.warningIssued = true;
99
+ const result = {
100
+ status: 'warning',
101
+ iteration: context.iterations,
102
+ elapsedMs: context.elapsedMs,
103
+ message: `Approaching duration limit (${context.elapsedMs}ms/${cfg.maxDurationMs}ms)`,
104
+ warningType: 'duration',
105
+ };
106
+ emitWarning(contextId, result);
107
+ return result;
108
+ }
109
+ return {
110
+ status: 'ok',
111
+ iteration: context.iterations,
112
+ elapsedMs: context.elapsedMs,
113
+ };
114
+ },
115
+ endContext(contextId) {
116
+ const context = contexts.get(contextId);
117
+ if (context) {
118
+ context.elapsedMs = Date.now() - context.startTime;
119
+ contexts.delete(contextId);
120
+ // Return without internal startTime property
121
+ const { startTime: _startTime, ...result } = context;
122
+ return result;
123
+ }
124
+ return undefined;
125
+ },
126
+ getContext(contextId) {
127
+ const context = contexts.get(contextId);
128
+ if (context) {
129
+ const { startTime: _startTime, ...result } = context;
130
+ return {
131
+ ...result,
132
+ elapsedMs: Date.now() - context.startTime,
133
+ };
134
+ }
135
+ return undefined;
136
+ },
137
+ onWarning(listener) {
138
+ listeners.add(listener);
139
+ return () => listeners.delete(listener);
140
+ },
141
+ };
142
+ }
143
+ /**
144
+ * Creates a loop guard that throws on blocked
145
+ */
146
+ export function createStrictLoopGuard(config) {
147
+ const guard = createLoopGuard(config);
148
+ const originalCheck = guard.checkIteration.bind(guard);
149
+ guard.checkIteration = (contextId) => {
150
+ const result = originalCheck(contextId);
151
+ if (result.status === 'blocked') {
152
+ const context = guard.getContext(contextId);
153
+ if (result.blockType === 'max-iterations') {
154
+ throw LoopGuardError.maxIterations(context);
155
+ }
156
+ else {
157
+ throw LoopGuardError.maxDuration(context);
158
+ }
159
+ }
160
+ return result;
161
+ };
162
+ return guard;
163
+ }
164
+ //# sourceMappingURL=loop-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop-guard.js","sourceRoot":"","sources":["../src/loop-guard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Metrics Collector Implementation
3
+ *
4
+ * Basic observability metrics for monitoring system health.
5
+ */
6
+ import { type MetricsTimeRange, type RequestMetric, type ErrorMetric, type MetricsSnapshot } from './_contracts.js';
7
+ /**
8
+ * Metrics collector interface
9
+ */
10
+ export interface MetricsCollector {
11
+ /** Record a request metric */
12
+ recordRequest(metric: RequestMetric): void;
13
+ /** Record an error metric */
14
+ recordError(metric: ErrorMetric): void;
15
+ /** Get metrics snapshot */
16
+ getStats(timeRange?: MetricsTimeRange): MetricsSnapshot;
17
+ /** Reset all metrics */
18
+ reset(): void;
19
+ /** Get raw request metrics */
20
+ getRequestMetrics(limit?: number): RequestMetric[];
21
+ /** Get raw error metrics */
22
+ getErrorMetrics(limit?: number): ErrorMetric[];
23
+ }
24
+ /**
25
+ * Metrics collector configuration
26
+ */
27
+ export interface MetricsCollectorConfig {
28
+ /** Maximum number of request metrics to retain */
29
+ maxRequestMetrics: number;
30
+ /** Maximum number of error metrics to retain */
31
+ maxErrorMetrics: number;
32
+ /** Default time range for stats (e.g., '-1h') */
33
+ defaultTimeRange: string;
34
+ }
35
+ /**
36
+ * Creates a metrics collector
37
+ */
38
+ export declare function createMetricsCollector(config?: Partial<MetricsCollectorConfig>): MetricsCollector;
39
+ export declare function getGlobalMetrics(): MetricsCollector;
40
+ export declare function resetGlobalMetrics(): void;
41
+ //# sourceMappingURL=metrics-collector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics-collector.d.ts","sourceRoot":"","sources":["../src/metrics-collector.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,WAAW,EAEhB,KAAK,eAAe,EAGrB,MAAM,iBAAiB,CAAC;AAEzB;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,8BAA8B;IAC9B,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;IAE3C,6BAA6B;IAC7B,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;IAEvC,2BAA2B;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,gBAAgB,GAAG,eAAe,CAAC;IAExD,wBAAwB;IACxB,KAAK,IAAI,IAAI,CAAC;IAEd,8BAA8B;IAC9B,iBAAiB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,aAAa,EAAE,CAAC;IAEnD,4BAA4B;IAC5B,eAAe,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE,CAAC;CAChD;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,kDAAkD;IAClD,iBAAiB,EAAE,MAAM,CAAC;IAE1B,gDAAgD;IAChD,eAAe,EAAE,MAAM,CAAC;IAExB,iDAAiD;IACjD,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAQD;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,GACvC,gBAAgB,CAyMlB;AAOD,wBAAgB,gBAAgB,IAAI,gBAAgB,CAKnD;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Metrics Collector Implementation
3
+ *
4
+ * Basic observability metrics for monitoring system health.
5
+ */
6
+ import { TIME_MULTIPLIERS, DEFAULT_TIME_RANGE_MS, } from './_contracts.js';
7
+ const DEFAULT_CONFIG = {
8
+ maxRequestMetrics: 10000,
9
+ maxErrorMetrics: 1000,
10
+ defaultTimeRange: '-1h',
11
+ };
12
+ /**
13
+ * Creates a metrics collector
14
+ */
15
+ export function createMetricsCollector(config) {
16
+ const cfg = { ...DEFAULT_CONFIG, ...config };
17
+ const requestMetrics = [];
18
+ const errorMetrics = [];
19
+ function parseTimeRange(timeRange) {
20
+ const end = timeRange.end === 'now' ? new Date() : new Date(timeRange.end);
21
+ let start;
22
+ if (timeRange.start.startsWith('-')) {
23
+ // Relative time like '-1h', '-30m', '-1d'
24
+ const match = /^-(\d+)([smhd])$/.exec(timeRange.start);
25
+ if (match) {
26
+ const value = parseInt(match[1], 10);
27
+ const unit = match[2];
28
+ const ms = value * (TIME_MULTIPLIERS[unit] ?? DEFAULT_TIME_RANGE_MS);
29
+ start = new Date(end.getTime() - ms);
30
+ }
31
+ else {
32
+ start = new Date(end.getTime() - DEFAULT_TIME_RANGE_MS);
33
+ }
34
+ }
35
+ else {
36
+ start = new Date(timeRange.start);
37
+ }
38
+ return { start, end };
39
+ }
40
+ function filterByTimeRange(items, timeRange) {
41
+ const { start, end } = parseTimeRange(timeRange);
42
+ return items.filter((item) => {
43
+ const ts = new Date(item.timestamp);
44
+ return ts >= start && ts <= end;
45
+ });
46
+ }
47
+ function calculateLatencyStats(latencies) {
48
+ if (latencies.length === 0) {
49
+ return { min: 0, max: 0, mean: 0, p50: 0, p95: 0, p99: 0, count: 0 };
50
+ }
51
+ const sorted = [...latencies].sort((a, b) => a - b);
52
+ const count = sorted.length;
53
+ const percentile = (p) => {
54
+ const index = Math.ceil((p / 100) * count) - 1;
55
+ return sorted[Math.max(0, Math.min(index, count - 1))];
56
+ };
57
+ return {
58
+ min: sorted[0],
59
+ max: sorted[count - 1],
60
+ mean: Math.round(latencies.reduce((a, b) => a + b, 0) / count),
61
+ p50: percentile(50),
62
+ p95: percentile(95),
63
+ p99: percentile(99),
64
+ count,
65
+ };
66
+ }
67
+ return {
68
+ recordRequest(metric) {
69
+ requestMetrics.push(metric);
70
+ // Trim if over limit
71
+ if (requestMetrics.length > cfg.maxRequestMetrics) {
72
+ requestMetrics.splice(0, requestMetrics.length - cfg.maxRequestMetrics);
73
+ }
74
+ },
75
+ recordError(metric) {
76
+ errorMetrics.push(metric);
77
+ // Trim if over limit
78
+ if (errorMetrics.length > cfg.maxErrorMetrics) {
79
+ errorMetrics.splice(0, errorMetrics.length - cfg.maxErrorMetrics);
80
+ }
81
+ },
82
+ getStats(timeRange) {
83
+ const range = timeRange ?? {
84
+ start: cfg.defaultTimeRange,
85
+ end: 'now',
86
+ };
87
+ const filteredRequests = filterByTimeRange(requestMetrics, range);
88
+ const filteredErrors = filterByTimeRange(errorMetrics, range);
89
+ // Calculate request stats
90
+ const successCount = filteredRequests.filter((r) => r.success).length;
91
+ const failureCount = filteredRequests.length - successCount;
92
+ const successRate = filteredRequests.length > 0
93
+ ? successCount / filteredRequests.length
94
+ : 1;
95
+ // Calculate latency stats
96
+ const latencies = filteredRequests.map((r) => r.durationMs);
97
+ const latencyStats = calculateLatencyStats(latencies);
98
+ // Calculate token stats
99
+ const inputTokens = filteredRequests.reduce((sum, r) => sum + (r.inputTokens ?? 0), 0);
100
+ const outputTokens = filteredRequests.reduce((sum, r) => sum + (r.outputTokens ?? 0), 0);
101
+ // Calculate cost stats
102
+ const totalCost = filteredRequests.reduce((sum, r) => sum + (r.estimatedCost ?? 0), 0);
103
+ // Group errors by code
104
+ const errorsByCode = {};
105
+ for (const error of filteredErrors) {
106
+ errorsByCode[error.code] = (errorsByCode[error.code] ?? 0) + 1;
107
+ }
108
+ // Group by provider
109
+ const byProvider = {};
110
+ for (const req of filteredRequests) {
111
+ if (!byProvider[req.providerId]) {
112
+ byProvider[req.providerId] = {
113
+ requests: 0,
114
+ failures: 0,
115
+ latencyP50: 0,
116
+ tokens: 0,
117
+ };
118
+ }
119
+ const p = byProvider[req.providerId];
120
+ p.requests++;
121
+ if (!req.success)
122
+ p.failures++;
123
+ p.tokens += (req.inputTokens ?? 0) + (req.outputTokens ?? 0);
124
+ }
125
+ // Calculate per-provider p50
126
+ for (const providerId of Object.keys(byProvider)) {
127
+ const providerLatencies = filteredRequests
128
+ .filter((r) => r.providerId === providerId)
129
+ .map((r) => r.durationMs);
130
+ byProvider[providerId].latencyP50 =
131
+ calculateLatencyStats(providerLatencies).p50;
132
+ }
133
+ return {
134
+ timeRange: range,
135
+ requests: {
136
+ total: filteredRequests.length,
137
+ success: successCount,
138
+ failure: failureCount,
139
+ successRate: Math.round(successRate * 1000) / 1000,
140
+ },
141
+ latency: latencyStats,
142
+ tokens: {
143
+ input: inputTokens,
144
+ output: outputTokens,
145
+ total: inputTokens + outputTokens,
146
+ },
147
+ cost: {
148
+ estimated: Math.round(totalCost * 10000) / 10000,
149
+ perRequest: filteredRequests.length > 0
150
+ ? Math.round((totalCost / filteredRequests.length) * 10000) / 10000
151
+ : 0,
152
+ },
153
+ errors: errorsByCode,
154
+ byProvider,
155
+ generatedAt: new Date().toISOString(),
156
+ };
157
+ },
158
+ reset() {
159
+ requestMetrics.length = 0;
160
+ errorMetrics.length = 0;
161
+ },
162
+ getRequestMetrics(limit) {
163
+ if (limit) {
164
+ return requestMetrics.slice(-limit);
165
+ }
166
+ return [...requestMetrics];
167
+ },
168
+ getErrorMetrics(limit) {
169
+ if (limit) {
170
+ return errorMetrics.slice(-limit);
171
+ }
172
+ return [...errorMetrics];
173
+ },
174
+ };
175
+ }
176
+ /**
177
+ * Global metrics collector singleton
178
+ */
179
+ let globalMetrics = null;
180
+ export function getGlobalMetrics() {
181
+ if (!globalMetrics) {
182
+ globalMetrics = createMetricsCollector();
183
+ }
184
+ return globalMetrics;
185
+ }
186
+ export function resetGlobalMetrics() {
187
+ globalMetrics = null;
188
+ }
189
+ //# sourceMappingURL=metrics-collector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics-collector.js","sourceRoot":"","sources":["../src/metrics-collector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Rate Limiter Implementation
3
+ *
4
+ * Token bucket rate limiting to prevent API limit violations.
5
+ */
6
+ import { type RateLimiterConfig, type RateLimiterStats, type RateLimiterAcquireResult } from './_contracts.js';
7
+ /**
8
+ * Rate limiter error
9
+ */
10
+ export declare class RateLimiterError extends Error {
11
+ readonly code: string;
12
+ readonly retryAfterMs?: number | undefined;
13
+ constructor(code: string, message: string, retryAfterMs?: number | undefined);
14
+ static rateLimited(retryAfterMs: number): RateLimiterError;
15
+ static queueFull(): RateLimiterError;
16
+ static timeout(): RateLimiterError;
17
+ }
18
+ /**
19
+ * Rate limiter interface
20
+ */
21
+ export interface RateLimiter {
22
+ /** Acquire capacity (waits if needed) */
23
+ acquire(tokens?: number): Promise<RateLimiterAcquireResult>;
24
+ /** Try to acquire capacity (non-blocking) */
25
+ tryAcquire(tokens?: number): boolean;
26
+ /** Get current statistics */
27
+ getStats(): RateLimiterStats;
28
+ /** Reset the limiter */
29
+ reset(): void;
30
+ }
31
+ /**
32
+ * Creates a rate limiter
33
+ */
34
+ export declare function createRateLimiter(config?: Partial<RateLimiterConfig>): RateLimiter;
35
+ //# sourceMappingURL=rate-limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,wBAAwB,EAK9B,MAAM,iBAAiB,CAAC;AAEzB;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;aAEvB,IAAI,EAAE,MAAM;aAEZ,YAAY,CAAC,EAAE,MAAM;gBAFrB,IAAI,EAAE,MAAM,EAC5B,OAAO,EAAE,MAAM,EACC,YAAY,CAAC,EAAE,MAAM,YAAA;IAMvC,MAAM,CAAC,WAAW,CAAC,YAAY,EAAE,MAAM,GAAG,gBAAgB;IAQ1D,MAAM,CAAC,SAAS,IAAI,gBAAgB;IAOpC,MAAM,CAAC,OAAO,IAAI,gBAAgB;CAMnC;AAWD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,yCAAyC;IACzC,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAE5D,6CAA6C;IAC7C,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAErC,6BAA6B;IAC7B,QAAQ,IAAI,gBAAgB,CAAC;IAE7B,wBAAwB;IACxB,KAAK,IAAI,IAAI,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAClC,WAAW,CAqLb"}
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Rate Limiter Implementation
3
+ *
4
+ * Token bucket rate limiting to prevent API limit violations.
5
+ */
6
+ import { RateLimiterErrorCodes, createDefaultRateLimiterConfig, DEFAULT_TOKEN_WINDOW_MS, DEFAULT_CHECK_INTERVAL_MS, } from './_contracts.js';
7
+ /**
8
+ * Rate limiter error
9
+ */
10
+ export class RateLimiterError extends Error {
11
+ code;
12
+ retryAfterMs;
13
+ constructor(code, message, retryAfterMs) {
14
+ super(message);
15
+ this.code = code;
16
+ this.retryAfterMs = retryAfterMs;
17
+ this.name = 'RateLimiterError';
18
+ }
19
+ static rateLimited(retryAfterMs) {
20
+ return new RateLimiterError(RateLimiterErrorCodes.RATE_LIMITED, `Rate limit exceeded. Retry after ${retryAfterMs}ms`, retryAfterMs);
21
+ }
22
+ static queueFull() {
23
+ return new RateLimiterError(RateLimiterErrorCodes.QUEUE_FULL, 'Rate limiter queue is full');
24
+ }
25
+ static timeout() {
26
+ return new RateLimiterError(RateLimiterErrorCodes.TIMEOUT, 'Rate limiter queue timeout');
27
+ }
28
+ }
29
+ /**
30
+ * Creates a rate limiter
31
+ */
32
+ export function createRateLimiter(config) {
33
+ const cfg = { ...createDefaultRateLimiterConfig(), ...config };
34
+ // Token bucket state
35
+ let tokens = cfg.requestsPerMinute * cfg.burstMultiplier;
36
+ const maxTokens = cfg.requestsPerMinute * cfg.burstMultiplier;
37
+ const refillRate = cfg.requestsPerMinute / 60000; // tokens per ms
38
+ let lastRefillTime = Date.now();
39
+ // Token tracking for LLM APIs
40
+ let llmTokensUsed = 0;
41
+ let llmTokenWindowStart = Date.now();
42
+ // Statistics
43
+ let requestsAllowed = 0;
44
+ let requestsRejected = 0;
45
+ const windowStart = new Date().toISOString();
46
+ // Queue
47
+ const queue = [];
48
+ let processingQueue = false;
49
+ function refillTokens() {
50
+ const now = Date.now();
51
+ const elapsed = now - lastRefillTime;
52
+ const refill = elapsed * refillRate;
53
+ tokens = Math.min(maxTokens, tokens + refill);
54
+ lastRefillTime = now;
55
+ // Reset LLM token window if minute elapsed
56
+ if (cfg.tokensPerMinute && now - llmTokenWindowStart >= DEFAULT_TOKEN_WINDOW_MS) {
57
+ llmTokensUsed = 0;
58
+ llmTokenWindowStart = now;
59
+ }
60
+ }
61
+ function calculateWaitTime(requestTokens) {
62
+ if (tokens >= requestTokens)
63
+ return 0;
64
+ const deficit = requestTokens - tokens;
65
+ return Math.ceil(deficit / refillRate);
66
+ }
67
+ function processQueue() {
68
+ if (processingQueue || queue.length === 0)
69
+ return;
70
+ processingQueue = true;
71
+ const processNext = () => {
72
+ if (queue.length === 0) {
73
+ processingQueue = false;
74
+ return;
75
+ }
76
+ const request = queue[0];
77
+ if (!request) {
78
+ processingQueue = false;
79
+ return;
80
+ }
81
+ const now = Date.now();
82
+ // Check timeout
83
+ if (now - request.enqueuedAt >= cfg.queueTimeoutMs) {
84
+ queue.shift();
85
+ request.resolve({
86
+ acquired: false,
87
+ reason: 'timeout',
88
+ });
89
+ setTimeout(processNext, 0);
90
+ return;
91
+ }
92
+ refillTokens();
93
+ if (tokens >= request.tokens) {
94
+ queue.shift();
95
+ tokens -= request.tokens;
96
+ requestsAllowed++;
97
+ request.resolve({
98
+ acquired: true,
99
+ waitedMs: now - request.enqueuedAt,
100
+ });
101
+ setTimeout(processNext, 0);
102
+ }
103
+ else {
104
+ // Wait for tokens
105
+ const waitTime = calculateWaitTime(request.tokens);
106
+ setTimeout(processNext, Math.min(waitTime, DEFAULT_CHECK_INTERVAL_MS));
107
+ }
108
+ };
109
+ processNext();
110
+ }
111
+ return {
112
+ async acquire(requestTokens = 1) {
113
+ refillTokens();
114
+ // Check LLM token limit
115
+ if (cfg.tokensPerMinute && llmTokensUsed >= cfg.tokensPerMinute) {
116
+ const retryAfterMs = DEFAULT_TOKEN_WINDOW_MS - (Date.now() - llmTokenWindowStart);
117
+ requestsRejected++;
118
+ return {
119
+ acquired: false,
120
+ reason: 'limit-exceeded',
121
+ retryAfterMs,
122
+ };
123
+ }
124
+ // Try immediate acquisition
125
+ if (tokens >= requestTokens && queue.length === 0) {
126
+ tokens -= requestTokens;
127
+ requestsAllowed++;
128
+ return { acquired: true, waitedMs: 0 };
129
+ }
130
+ // Check queue capacity
131
+ if (queue.length >= cfg.maxQueueSize) {
132
+ requestsRejected++;
133
+ return { acquired: false, reason: 'queue-full' };
134
+ }
135
+ // Queue the request
136
+ return new Promise((resolve) => {
137
+ queue.push({
138
+ tokens: requestTokens,
139
+ resolve,
140
+ enqueuedAt: Date.now(),
141
+ });
142
+ processQueue();
143
+ });
144
+ },
145
+ tryAcquire(requestTokens = 1) {
146
+ refillTokens();
147
+ // Check LLM token limit
148
+ if (cfg.tokensPerMinute && llmTokensUsed >= cfg.tokensPerMinute) {
149
+ requestsRejected++;
150
+ return false;
151
+ }
152
+ if (tokens >= requestTokens && queue.length === 0) {
153
+ tokens -= requestTokens;
154
+ requestsAllowed++;
155
+ return true;
156
+ }
157
+ requestsRejected++;
158
+ return false;
159
+ },
160
+ getStats() {
161
+ refillTokens();
162
+ return {
163
+ requestsAllowed,
164
+ requestsRejected,
165
+ queueSize: queue.length,
166
+ tokensUsed: llmTokensUsed,
167
+ availableCapacity: tokens,
168
+ nextRefillMs: tokens >= maxTokens ? 0 : Math.ceil((maxTokens - tokens) / refillRate),
169
+ windowStart,
170
+ };
171
+ },
172
+ reset() {
173
+ tokens = maxTokens;
174
+ lastRefillTime = Date.now();
175
+ llmTokensUsed = 0;
176
+ llmTokenWindowStart = Date.now();
177
+ requestsAllowed = 0;
178
+ requestsRejected = 0;
179
+ // Clear queue
180
+ while (queue.length > 0) {
181
+ const request = queue.shift();
182
+ if (request) {
183
+ request.resolve({ acquired: false, reason: 'timeout' });
184
+ }
185
+ }
186
+ },
187
+ };
188
+ }
189
+ //# sourceMappingURL=rate-limiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"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"}