@marktoflow/integrations 2.0.2 → 2.0.4-alpha.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.
- package/README.md +65 -16
- package/dist/adapters/claude-agent-types.d.ts +10 -8
- package/dist/adapters/claude-agent-types.d.ts.map +1 -1
- package/dist/adapters/claude-agent-types.js.map +1 -1
- package/dist/adapters/claude-agent-workflow.d.ts +10 -10
- package/dist/adapters/claude-agent.d.ts +2 -2
- package/dist/adapters/claude-agent.d.ts.map +1 -1
- package/dist/adapters/claude-agent.js +8 -2
- package/dist/adapters/claude-agent.js.map +1 -1
- package/dist/adapters/codex-types.d.ts +6 -6
- package/dist/adapters/codex-workflow.d.ts +16 -16
- package/dist/adapters/github-copilot-types.d.ts +21 -21
- package/dist/adapters/github-copilot-workflow.d.ts +8 -8
- package/dist/adapters/ollama-types.d.ts +42 -42
- package/dist/adapters/openai-types.d.ts +713 -0
- package/dist/adapters/openai-types.d.ts.map +1 -0
- package/dist/adapters/openai-types.js +91 -0
- package/dist/adapters/openai-types.js.map +1 -0
- package/dist/adapters/openai.d.ts +128 -0
- package/dist/adapters/openai.d.ts.map +1 -0
- package/dist/adapters/openai.js +387 -0
- package/dist/adapters/openai.js.map +1 -0
- package/dist/adapters/opencode.d.ts +25 -0
- package/dist/adapters/opencode.d.ts.map +1 -1
- package/dist/adapters/opencode.js +205 -15
- package/dist/adapters/opencode.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -4
- package/dist/index.js.map +1 -1
- package/dist/reliability/circuit-breaker.d.ts +67 -0
- package/dist/reliability/circuit-breaker.d.ts.map +1 -0
- package/dist/reliability/circuit-breaker.js +164 -0
- package/dist/reliability/circuit-breaker.js.map +1 -0
- package/dist/reliability/index.d.ts +3 -1
- package/dist/reliability/index.d.ts.map +1 -1
- package/dist/reliability/index.js +3 -1
- package/dist/reliability/index.js.map +1 -1
- package/dist/reliability/rate-limiter.d.ts +62 -0
- package/dist/reliability/rate-limiter.d.ts.map +1 -0
- package/dist/reliability/rate-limiter.js +194 -0
- package/dist/reliability/rate-limiter.js.map +1 -0
- package/dist/reliability/wrapper.d.ts +9 -0
- package/dist/reliability/wrapper.d.ts.map +1 -1
- package/dist/reliability/wrapper.js +62 -12
- package/dist/reliability/wrapper.js.map +1 -1
- package/dist/services/ai-browser.d.ts +3 -3
- package/dist/services/ai-browser.d.ts.map +1 -1
- package/dist/services/ai-browser.js +1 -1
- package/dist/services/ai-browser.js.map +1 -1
- package/dist/services/base-client.d.ts.map +1 -1
- package/dist/services/base-client.js +25 -3
- package/dist/services/base-client.js.map +1 -1
- package/dist/services/discord.d.ts.map +1 -1
- package/dist/services/discord.js +6 -0
- package/dist/services/discord.js.map +1 -1
- package/dist/services/gmail.d.ts.map +1 -1
- package/dist/services/gmail.js +65 -47
- package/dist/services/gmail.js.map +1 -1
- package/dist/services/google-calendar.js +9 -5
- package/dist/services/google-calendar.js.map +1 -1
- package/dist/services/google-docs.js +9 -5
- package/dist/services/google-docs.js.map +1 -1
- package/dist/services/google-drive.js +9 -5
- package/dist/services/google-drive.js.map +1 -1
- package/dist/services/google-sheets.js +9 -5
- package/dist/services/google-sheets.js.map +1 -1
- package/dist/services/http.d.ts.map +1 -1
- package/dist/services/http.js +15 -1
- package/dist/services/http.js.map +1 -1
- package/dist/services/mailchimp.d.ts.map +1 -1
- package/dist/services/mailchimp.js +3 -0
- package/dist/services/mailchimp.js.map +1 -1
- package/dist/services/outlook.d.ts.map +1 -1
- package/dist/services/outlook.js +14 -11
- package/dist/services/outlook.js.map +1 -1
- package/dist/services/playwright/client.d.ts +110 -0
- package/dist/services/playwright/client.d.ts.map +1 -0
- package/dist/services/playwright/client.js +690 -0
- package/dist/services/playwright/client.js.map +1 -0
- package/dist/services/playwright/index.d.ts +7 -0
- package/dist/services/playwright/index.d.ts.map +1 -0
- package/dist/services/playwright/index.js +7 -0
- package/dist/services/playwright/index.js.map +1 -0
- package/dist/services/playwright/initializer.d.ts +24 -0
- package/dist/services/playwright/initializer.d.ts.map +1 -0
- package/dist/services/playwright/initializer.js +99 -0
- package/dist/services/playwright/initializer.js.map +1 -0
- package/dist/services/playwright/types.d.ts +270 -0
- package/dist/services/playwright/types.d.ts.map +1 -0
- package/dist/services/playwright/types.js +5 -0
- package/dist/services/playwright/types.js.map +1 -0
- package/dist/services/playwright.d.ts +3 -675
- package/dist/services/playwright.d.ts.map +1 -1
- package/dist/services/playwright.js +3 -1138
- package/dist/services/playwright.js.map +1 -1
- package/dist/services/rss.d.ts +57 -0
- package/dist/services/rss.d.ts.map +1 -0
- package/dist/services/rss.js +190 -0
- package/dist/services/rss.js.map +1 -0
- package/dist/services/shopify.d.ts.map +1 -1
- package/dist/services/shopify.js +3 -0
- package/dist/services/shopify.js.map +1 -1
- package/dist/services/trello.d.ts.map +1 -1
- package/dist/services/trello.js +7 -1
- package/dist/services/trello.js.map +1 -1
- package/package.json +30 -8
- package/dist/adapters/claude-code.d.ts +0 -34
- package/dist/adapters/claude-code.d.ts.map +0 -1
- package/dist/adapters/claude-code.js +0 -89
- package/dist/adapters/claude-code.js.map +0 -1
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Circuit Breaker for integration reliability.
|
|
3
|
+
*
|
|
4
|
+
* Prevents cascading failures by tracking error rates per service
|
|
5
|
+
* and short-circuiting requests when a service is unhealthy.
|
|
6
|
+
*
|
|
7
|
+
* States:
|
|
8
|
+
* - CLOSED: Normal operation, requests pass through
|
|
9
|
+
* - OPEN: Service is failing, requests are rejected immediately
|
|
10
|
+
* - HALF_OPEN: Testing if service has recovered (limited requests allowed)
|
|
11
|
+
*/
|
|
12
|
+
import { IntegrationRequestError } from './errors.js';
|
|
13
|
+
const DEFAULT_OPTIONS = {
|
|
14
|
+
failureThreshold: 5,
|
|
15
|
+
resetTimeout: 30_000,
|
|
16
|
+
successThreshold: 2,
|
|
17
|
+
failureWindow: 60_000,
|
|
18
|
+
};
|
|
19
|
+
export class CircuitBreakerRegistry {
|
|
20
|
+
circuits = new Map();
|
|
21
|
+
options;
|
|
22
|
+
onStateChange;
|
|
23
|
+
constructor(options = {}) {
|
|
24
|
+
this.options = {
|
|
25
|
+
failureThreshold: options.failureThreshold ?? DEFAULT_OPTIONS.failureThreshold,
|
|
26
|
+
resetTimeout: options.resetTimeout ?? DEFAULT_OPTIONS.resetTimeout,
|
|
27
|
+
successThreshold: options.successThreshold ?? DEFAULT_OPTIONS.successThreshold,
|
|
28
|
+
failureWindow: options.failureWindow ?? DEFAULT_OPTIONS.failureWindow,
|
|
29
|
+
};
|
|
30
|
+
this.onStateChange = options.onStateChange;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Check if a request to the given service should be allowed.
|
|
34
|
+
* Throws immediately if circuit is open.
|
|
35
|
+
*/
|
|
36
|
+
allowRequest(service) {
|
|
37
|
+
const circuit = this.getCircuit(service);
|
|
38
|
+
const now = Date.now();
|
|
39
|
+
switch (circuit.state) {
|
|
40
|
+
case 'closed':
|
|
41
|
+
return; // Allow
|
|
42
|
+
case 'open': {
|
|
43
|
+
// Check if reset timeout has elapsed
|
|
44
|
+
if (now - circuit.openedAt >= this.options.resetTimeout) {
|
|
45
|
+
this.transition(service, circuit, 'half_open');
|
|
46
|
+
return; // Allow probe request
|
|
47
|
+
}
|
|
48
|
+
throw new IntegrationRequestError({
|
|
49
|
+
service,
|
|
50
|
+
action: 'circuit_breaker',
|
|
51
|
+
message: `Circuit breaker is open for ${service} — too many recent failures. Retry after ${Math.ceil((circuit.openedAt + this.options.resetTimeout - now) / 1000)}s`,
|
|
52
|
+
retryable: true,
|
|
53
|
+
retryAfter: Math.ceil((circuit.openedAt + this.options.resetTimeout - now) / 1000),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
case 'half_open':
|
|
57
|
+
return; // Allow probe request
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Record a successful request.
|
|
62
|
+
*/
|
|
63
|
+
recordSuccess(service) {
|
|
64
|
+
const circuit = this.getCircuit(service);
|
|
65
|
+
if (circuit.state === 'half_open') {
|
|
66
|
+
circuit.successes++;
|
|
67
|
+
if (circuit.successes >= this.options.successThreshold) {
|
|
68
|
+
this.transition(service, circuit, 'closed');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Record a failed request.
|
|
74
|
+
*/
|
|
75
|
+
recordFailure(service) {
|
|
76
|
+
const circuit = this.getCircuit(service);
|
|
77
|
+
const now = Date.now();
|
|
78
|
+
if (circuit.state === 'half_open') {
|
|
79
|
+
// Any failure in half-open reopens the circuit
|
|
80
|
+
this.transition(service, circuit, 'open');
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// Add failure timestamp and prune old ones
|
|
84
|
+
circuit.failures.push(now);
|
|
85
|
+
circuit.failures = circuit.failures.filter((t) => now - t < this.options.failureWindow);
|
|
86
|
+
circuit.lastFailureAt = now;
|
|
87
|
+
if (circuit.failures.length >= this.options.failureThreshold) {
|
|
88
|
+
this.transition(service, circuit, 'open');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get current state of a service's circuit.
|
|
93
|
+
*/
|
|
94
|
+
getState(service) {
|
|
95
|
+
return this.getCircuit(service).state;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get stats for all circuits.
|
|
99
|
+
*/
|
|
100
|
+
getStats() {
|
|
101
|
+
const stats = {};
|
|
102
|
+
const now = Date.now();
|
|
103
|
+
for (const [service, circuit] of this.circuits) {
|
|
104
|
+
const recentFailures = circuit.failures.filter((t) => now - t < this.options.failureWindow).length;
|
|
105
|
+
stats[service] = { state: circuit.state, recentFailures };
|
|
106
|
+
}
|
|
107
|
+
return stats;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Reset a specific service's circuit to closed.
|
|
111
|
+
*/
|
|
112
|
+
reset(service) {
|
|
113
|
+
this.circuits.delete(service);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Reset all circuits.
|
|
117
|
+
*/
|
|
118
|
+
resetAll() {
|
|
119
|
+
this.circuits.clear();
|
|
120
|
+
}
|
|
121
|
+
getCircuit(service) {
|
|
122
|
+
let circuit = this.circuits.get(service);
|
|
123
|
+
if (!circuit) {
|
|
124
|
+
circuit = {
|
|
125
|
+
state: 'closed',
|
|
126
|
+
failures: [],
|
|
127
|
+
successes: 0,
|
|
128
|
+
lastFailureAt: 0,
|
|
129
|
+
openedAt: 0,
|
|
130
|
+
};
|
|
131
|
+
this.circuits.set(service, circuit);
|
|
132
|
+
}
|
|
133
|
+
return circuit;
|
|
134
|
+
}
|
|
135
|
+
transition(service, circuit, to) {
|
|
136
|
+
const from = circuit.state;
|
|
137
|
+
circuit.state = to;
|
|
138
|
+
if (to === 'open') {
|
|
139
|
+
circuit.openedAt = Date.now();
|
|
140
|
+
circuit.successes = 0;
|
|
141
|
+
}
|
|
142
|
+
else if (to === 'closed') {
|
|
143
|
+
circuit.failures = [];
|
|
144
|
+
circuit.successes = 0;
|
|
145
|
+
}
|
|
146
|
+
else if (to === 'half_open') {
|
|
147
|
+
circuit.successes = 0;
|
|
148
|
+
}
|
|
149
|
+
this.onStateChange?.(service, from, to);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/** Global circuit breaker registry shared across all integrations */
|
|
153
|
+
let _globalRegistry;
|
|
154
|
+
export function getCircuitBreakerRegistry(options) {
|
|
155
|
+
if (!_globalRegistry) {
|
|
156
|
+
_globalRegistry = new CircuitBreakerRegistry(options);
|
|
157
|
+
}
|
|
158
|
+
return _globalRegistry;
|
|
159
|
+
}
|
|
160
|
+
export function resetCircuitBreakerRegistry() {
|
|
161
|
+
_globalRegistry?.resetAll();
|
|
162
|
+
_globalRegistry = undefined;
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=circuit-breaker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker.js","sourceRoot":"","sources":["../../src/reliability/circuit-breaker.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
* Provides input validation, retry, timeout, rate limiting,
|
|
5
5
|
* and error normalization for all integrations.
|
|
6
6
|
*/
|
|
7
|
-
export { wrapIntegration, type WrapperOptions, type ActionCallOptions, } from './wrapper.js';
|
|
7
|
+
export { wrapIntegration, createTimeoutSignal, type WrapperOptions, type ActionCallOptions, } from './wrapper.js';
|
|
8
8
|
export { IntegrationRequestError, normalizeError, type IntegrationError, } from './errors.js';
|
|
9
|
+
export { CircuitBreakerRegistry, getCircuitBreakerRegistry, resetCircuitBreakerRegistry, type CircuitState, type CircuitBreakerOptions, } from './circuit-breaker.js';
|
|
10
|
+
export { RateLimiterRegistry, getRateLimiterRegistry, resetRateLimiterRegistry, KNOWN_RATE_LIMITS, type RateLimitConfig, } from './rate-limiter.js';
|
|
9
11
|
export { slackSchemas, githubSchemas, gmailSchemas, notionSchemas, jiraSchemas, discordSchemas, linearSchemas, } from './schemas/index.js';
|
|
10
12
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/reliability/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,eAAe,EACf,KAAK,cAAc,EACnB,KAAK,iBAAiB,GACvB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,aAAa,EACb,WAAW,EACX,cAAc,EACd,aAAa,GACd,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/reliability/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,KAAK,cAAc,EACnB,KAAK,iBAAiB,GACvB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,2BAA2B,EAC3B,KAAK,YAAY,EACjB,KAAK,qBAAqB,GAC3B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,EACxB,iBAAiB,EACjB,KAAK,eAAe,GACrB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,aAAa,EACb,WAAW,EACX,cAAc,EACd,aAAa,GACd,MAAM,oBAAoB,CAAC"}
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
* Provides input validation, retry, timeout, rate limiting,
|
|
5
5
|
* and error normalization for all integrations.
|
|
6
6
|
*/
|
|
7
|
-
export { wrapIntegration, } from './wrapper.js';
|
|
7
|
+
export { wrapIntegration, createTimeoutSignal, } from './wrapper.js';
|
|
8
8
|
export { IntegrationRequestError, normalizeError, } from './errors.js';
|
|
9
|
+
export { CircuitBreakerRegistry, getCircuitBreakerRegistry, resetCircuitBreakerRegistry, } from './circuit-breaker.js';
|
|
10
|
+
export { RateLimiterRegistry, getRateLimiterRegistry, resetRateLimiterRegistry, KNOWN_RATE_LIMITS, } from './rate-limiter.js';
|
|
9
11
|
export { slackSchemas, githubSchemas, gmailSchemas, notionSchemas, jiraSchemas, discordSchemas, linearSchemas, } from './schemas/index.js';
|
|
10
12
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/reliability/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,eAAe,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/reliability/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,eAAe,EACf,mBAAmB,GAGpB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,uBAAuB,EACvB,cAAc,GAEf,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,2BAA2B,GAG5B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,EACxB,iBAAiB,GAElB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,aAAa,EACb,WAAW,EACX,cAAc,EACd,aAAa,GACd,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proactive rate limiter for integration reliability.
|
|
3
|
+
*
|
|
4
|
+
* Prevents 429 errors by tracking request rates per service
|
|
5
|
+
* and queuing/delaying requests that would exceed known limits.
|
|
6
|
+
*
|
|
7
|
+
* Uses a token bucket algorithm with configurable refill rates.
|
|
8
|
+
*/
|
|
9
|
+
export interface RateLimitConfig {
|
|
10
|
+
/** Maximum requests per window */
|
|
11
|
+
maxRequests: number;
|
|
12
|
+
/** Window duration in ms */
|
|
13
|
+
windowMs: number;
|
|
14
|
+
/** Strategy when limit is reached: 'queue' waits, 'reject' throws (default: 'queue') */
|
|
15
|
+
strategy?: 'queue' | 'reject';
|
|
16
|
+
/** Maximum queue size before rejecting (default: 100) */
|
|
17
|
+
maxQueueSize?: number;
|
|
18
|
+
}
|
|
19
|
+
/** Well-known rate limits for popular services */
|
|
20
|
+
export declare const KNOWN_RATE_LIMITS: Record<string, RateLimitConfig>;
|
|
21
|
+
export declare class RateLimiterRegistry {
|
|
22
|
+
private buckets;
|
|
23
|
+
private configs;
|
|
24
|
+
private timers;
|
|
25
|
+
constructor(overrides?: Record<string, RateLimitConfig>);
|
|
26
|
+
/**
|
|
27
|
+
* Configure rate limit for a specific service.
|
|
28
|
+
*/
|
|
29
|
+
configure(service: string, config: RateLimitConfig): void;
|
|
30
|
+
/**
|
|
31
|
+
* Acquire a token for the given service.
|
|
32
|
+
* Resolves immediately if tokens available, queues or rejects otherwise.
|
|
33
|
+
*/
|
|
34
|
+
acquire(service: string): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Release a token (optional — for manual flow control).
|
|
37
|
+
*/
|
|
38
|
+
release(service: string): void;
|
|
39
|
+
/**
|
|
40
|
+
* Get current rate limit status for a service.
|
|
41
|
+
*/
|
|
42
|
+
getStatus(service: string): {
|
|
43
|
+
available: number;
|
|
44
|
+
max: number;
|
|
45
|
+
queued: number;
|
|
46
|
+
refillRate: number;
|
|
47
|
+
} | null;
|
|
48
|
+
/**
|
|
49
|
+
* Update limits based on response headers (Retry-After, X-RateLimit-*).
|
|
50
|
+
*/
|
|
51
|
+
updateFromHeaders(service: string, headers: Record<string, string>): void;
|
|
52
|
+
/**
|
|
53
|
+
* Clean up timers.
|
|
54
|
+
*/
|
|
55
|
+
destroy(): void;
|
|
56
|
+
private refill;
|
|
57
|
+
private drainQueue;
|
|
58
|
+
private ensureDrainTimer;
|
|
59
|
+
}
|
|
60
|
+
export declare function getRateLimiterRegistry(overrides?: Record<string, RateLimitConfig>): RateLimiterRegistry;
|
|
61
|
+
export declare function resetRateLimiterRegistry(): void;
|
|
62
|
+
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/reliability/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,WAAW,eAAe;IAC9B,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,wFAAwF;IACxF,QAAQ,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC9B,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,kDAAkD;AAClD,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAW7D,CAAC;AAUF,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,OAAO,CAAgD;IAC/D,OAAO,CAAC,MAAM,CAAqD;gBAEvD,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC;IAYvD;;OAEG;IACH,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI;IAmBzD;;;OAGG;IACG,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuC7C;;OAEG;IACH,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAO9B;;OAEG;IACH,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG;QAC1B,SAAS,EAAE,MAAM,CAAC;QAClB,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;KACpB,GAAG,IAAI;IAYR;;OAEG;IACH,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAazE;;OAEG;IACH,OAAO,IAAI,IAAI;IAef,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,gBAAgB;CAiBzB;AAKD,wBAAgB,sBAAsB,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,mBAAmB,CAKvG;AAED,wBAAgB,wBAAwB,IAAI,IAAI,CAG/C"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proactive rate limiter for integration reliability.
|
|
3
|
+
*
|
|
4
|
+
* Prevents 429 errors by tracking request rates per service
|
|
5
|
+
* and queuing/delaying requests that would exceed known limits.
|
|
6
|
+
*
|
|
7
|
+
* Uses a token bucket algorithm with configurable refill rates.
|
|
8
|
+
*/
|
|
9
|
+
import { IntegrationRequestError } from './errors.js';
|
|
10
|
+
/** Well-known rate limits for popular services */
|
|
11
|
+
export const KNOWN_RATE_LIMITS = {
|
|
12
|
+
slack: { maxRequests: 50, windowMs: 60_000 },
|
|
13
|
+
github: { maxRequests: 5000, windowMs: 3_600_000 },
|
|
14
|
+
gmail: { maxRequests: 250, windowMs: 1_000 },
|
|
15
|
+
discord: { maxRequests: 50, windowMs: 1_000 },
|
|
16
|
+
notion: { maxRequests: 3, windowMs: 1_000 },
|
|
17
|
+
linear: { maxRequests: 50, windowMs: 60_000 },
|
|
18
|
+
stripe: { maxRequests: 100, windowMs: 1_000 },
|
|
19
|
+
sendgrid: { maxRequests: 600, windowMs: 60_000 },
|
|
20
|
+
trello: { maxRequests: 100, windowMs: 10_000 },
|
|
21
|
+
shopify: { maxRequests: 40, windowMs: 1_000 },
|
|
22
|
+
};
|
|
23
|
+
export class RateLimiterRegistry {
|
|
24
|
+
buckets = new Map();
|
|
25
|
+
configs = new Map();
|
|
26
|
+
timers = new Map();
|
|
27
|
+
constructor(overrides) {
|
|
28
|
+
// Load known defaults, allow overrides
|
|
29
|
+
for (const [service, config] of Object.entries(KNOWN_RATE_LIMITS)) {
|
|
30
|
+
this.configure(service, config);
|
|
31
|
+
}
|
|
32
|
+
if (overrides) {
|
|
33
|
+
for (const [service, config] of Object.entries(overrides)) {
|
|
34
|
+
this.configure(service, config);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Configure rate limit for a specific service.
|
|
40
|
+
*/
|
|
41
|
+
configure(service, config) {
|
|
42
|
+
const full = {
|
|
43
|
+
maxRequests: config.maxRequests,
|
|
44
|
+
windowMs: config.windowMs,
|
|
45
|
+
strategy: config.strategy ?? 'queue',
|
|
46
|
+
maxQueueSize: config.maxQueueSize ?? 100,
|
|
47
|
+
};
|
|
48
|
+
this.configs.set(service, full);
|
|
49
|
+
const refillRate = config.maxRequests / config.windowMs;
|
|
50
|
+
this.buckets.set(service, {
|
|
51
|
+
tokens: config.maxRequests,
|
|
52
|
+
maxTokens: config.maxRequests,
|
|
53
|
+
refillRate,
|
|
54
|
+
lastRefillAt: Date.now(),
|
|
55
|
+
queue: [],
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Acquire a token for the given service.
|
|
60
|
+
* Resolves immediately if tokens available, queues or rejects otherwise.
|
|
61
|
+
*/
|
|
62
|
+
async acquire(service) {
|
|
63
|
+
const bucket = this.buckets.get(service);
|
|
64
|
+
if (!bucket)
|
|
65
|
+
return; // No rate limit configured — allow
|
|
66
|
+
this.refill(bucket);
|
|
67
|
+
if (bucket.tokens >= 1) {
|
|
68
|
+
bucket.tokens -= 1;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const config = this.configs.get(service);
|
|
72
|
+
if (config.strategy === 'reject') {
|
|
73
|
+
throw new IntegrationRequestError({
|
|
74
|
+
service,
|
|
75
|
+
action: 'rate_limiter',
|
|
76
|
+
message: `Rate limit reached for ${service} (${config.maxRequests} requests per ${config.windowMs}ms)`,
|
|
77
|
+
retryable: true,
|
|
78
|
+
retryAfter: Math.ceil((1 / bucket.refillRate) / 1000),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// Queue strategy — wait for a token
|
|
82
|
+
if (bucket.queue.length >= config.maxQueueSize) {
|
|
83
|
+
throw new IntegrationRequestError({
|
|
84
|
+
service,
|
|
85
|
+
action: 'rate_limiter',
|
|
86
|
+
message: `Rate limit queue full for ${service} (${config.maxQueueSize} pending requests)`,
|
|
87
|
+
retryable: true,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return new Promise((resolve, reject) => {
|
|
91
|
+
bucket.queue.push({ resolve, reject });
|
|
92
|
+
this.ensureDrainTimer(service, bucket);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Release a token (optional — for manual flow control).
|
|
97
|
+
*/
|
|
98
|
+
release(service) {
|
|
99
|
+
const bucket = this.buckets.get(service);
|
|
100
|
+
if (!bucket)
|
|
101
|
+
return;
|
|
102
|
+
bucket.tokens = Math.min(bucket.tokens + 1, bucket.maxTokens);
|
|
103
|
+
this.drainQueue(bucket);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Get current rate limit status for a service.
|
|
107
|
+
*/
|
|
108
|
+
getStatus(service) {
|
|
109
|
+
const bucket = this.buckets.get(service);
|
|
110
|
+
if (!bucket)
|
|
111
|
+
return null;
|
|
112
|
+
this.refill(bucket);
|
|
113
|
+
return {
|
|
114
|
+
available: Math.floor(bucket.tokens),
|
|
115
|
+
max: bucket.maxTokens,
|
|
116
|
+
queued: bucket.queue.length,
|
|
117
|
+
refillRate: bucket.refillRate * 1000, // tokens per second
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Update limits based on response headers (Retry-After, X-RateLimit-*).
|
|
122
|
+
*/
|
|
123
|
+
updateFromHeaders(service, headers) {
|
|
124
|
+
const remaining = headers['x-ratelimit-remaining'] ?? headers['X-RateLimit-Remaining'];
|
|
125
|
+
if (remaining !== undefined) {
|
|
126
|
+
const bucket = this.buckets.get(service);
|
|
127
|
+
if (bucket) {
|
|
128
|
+
const count = parseInt(remaining, 10);
|
|
129
|
+
if (!isNaN(count)) {
|
|
130
|
+
bucket.tokens = Math.min(count, bucket.maxTokens);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Clean up timers.
|
|
137
|
+
*/
|
|
138
|
+
destroy() {
|
|
139
|
+
for (const timer of this.timers.values()) {
|
|
140
|
+
clearInterval(timer);
|
|
141
|
+
}
|
|
142
|
+
this.timers.clear();
|
|
143
|
+
// Reject all queued requests
|
|
144
|
+
for (const bucket of this.buckets.values()) {
|
|
145
|
+
for (const waiter of bucket.queue) {
|
|
146
|
+
waiter.reject(new Error('Rate limiter destroyed'));
|
|
147
|
+
}
|
|
148
|
+
bucket.queue = [];
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
refill(bucket) {
|
|
152
|
+
const now = Date.now();
|
|
153
|
+
const elapsed = now - bucket.lastRefillAt;
|
|
154
|
+
const tokensToAdd = elapsed * bucket.refillRate;
|
|
155
|
+
bucket.tokens = Math.min(bucket.tokens + tokensToAdd, bucket.maxTokens);
|
|
156
|
+
bucket.lastRefillAt = now;
|
|
157
|
+
}
|
|
158
|
+
drainQueue(bucket) {
|
|
159
|
+
while (bucket.queue.length > 0 && bucket.tokens >= 1) {
|
|
160
|
+
bucket.tokens -= 1;
|
|
161
|
+
const waiter = bucket.queue.shift();
|
|
162
|
+
waiter.resolve();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
ensureDrainTimer(service, bucket) {
|
|
166
|
+
if (this.timers.has(service))
|
|
167
|
+
return;
|
|
168
|
+
const intervalMs = Math.max(10, Math.ceil(1 / bucket.refillRate));
|
|
169
|
+
const timer = setInterval(() => {
|
|
170
|
+
this.refill(bucket);
|
|
171
|
+
this.drainQueue(bucket);
|
|
172
|
+
if (bucket.queue.length === 0) {
|
|
173
|
+
clearInterval(timer);
|
|
174
|
+
this.timers.delete(service);
|
|
175
|
+
}
|
|
176
|
+
}, intervalMs);
|
|
177
|
+
if (timer.unref)
|
|
178
|
+
timer.unref();
|
|
179
|
+
this.timers.set(service, timer);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/** Global rate limiter registry */
|
|
183
|
+
let _globalLimiter;
|
|
184
|
+
export function getRateLimiterRegistry(overrides) {
|
|
185
|
+
if (!_globalLimiter) {
|
|
186
|
+
_globalLimiter = new RateLimiterRegistry(overrides);
|
|
187
|
+
}
|
|
188
|
+
return _globalLimiter;
|
|
189
|
+
}
|
|
190
|
+
export function resetRateLimiterRegistry() {
|
|
191
|
+
_globalLimiter?.destroy();
|
|
192
|
+
_globalLimiter = undefined;
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/reliability/rate-limiter.ts"],"names":[],"mappings":"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"}
|
|
@@ -24,6 +24,10 @@ export interface WrapperOptions {
|
|
|
24
24
|
maxRetryDelay?: number;
|
|
25
25
|
/** Zod schemas for input validation per action */
|
|
26
26
|
inputSchemas?: Record<string, z.ZodTypeAny>;
|
|
27
|
+
/** Enable circuit breaker for this service (default: true) */
|
|
28
|
+
circuitBreaker?: boolean;
|
|
29
|
+
/** Enable proactive rate limiting (default: true) */
|
|
30
|
+
rateLimiter?: boolean;
|
|
27
31
|
}
|
|
28
32
|
export interface ActionCallOptions {
|
|
29
33
|
/** Override timeout for this call */
|
|
@@ -43,4 +47,9 @@ export interface ActionCallOptions {
|
|
|
43
47
|
* remain accessible. Only function calls get wrapped.
|
|
44
48
|
*/
|
|
45
49
|
export declare function wrapIntegration<T extends object>(service: string, sdk: T, options?: Omit<WrapperOptions, 'service'>): T;
|
|
50
|
+
/**
|
|
51
|
+
* Create an AbortSignal that triggers after the given timeout.
|
|
52
|
+
* Useful for passing to fetch() or other cancellable operations.
|
|
53
|
+
*/
|
|
54
|
+
export declare function createTimeoutSignal(timeoutMs: number): AbortSignal;
|
|
46
55
|
//# sourceMappingURL=wrapper.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrapper.d.ts","sourceRoot":"","sources":["../../src/reliability/wrapper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"wrapper.d.ts","sourceRoot":"","sources":["../../src/reliability/wrapper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AASxB,MAAM,WAAW,cAAc;IAC7B,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iDAAiD;IACjD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC;IAC5C,8DAA8D;IAC9D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,qDAAqD;IACrD,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAgBD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,EAC9C,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,CAAC,EACN,OAAO,GAAE,IAAI,CAAC,cAAc,EAAE,SAAS,CAAM,GAC5C,CAAC,CAcH;AAgOD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,CAMlE"}
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
* - Rate limit awareness
|
|
10
10
|
*/
|
|
11
11
|
import { IntegrationRequestError, normalizeError } from './errors.js';
|
|
12
|
+
import { getCircuitBreakerRegistry } from './circuit-breaker.js';
|
|
13
|
+
import { getRateLimiterRegistry } from './rate-limiter.js';
|
|
12
14
|
// ============================================================================
|
|
13
15
|
// Defaults
|
|
14
16
|
// ============================================================================
|
|
@@ -38,6 +40,8 @@ export function wrapIntegration(service, sdk, options = {}) {
|
|
|
38
40
|
initialRetryDelay: options.initialRetryDelay ?? DEFAULT_INITIAL_RETRY_DELAY,
|
|
39
41
|
maxRetryDelay: options.maxRetryDelay ?? DEFAULT_MAX_RETRY_DELAY,
|
|
40
42
|
inputSchemas: options.inputSchemas ?? {},
|
|
43
|
+
circuitBreaker: options.circuitBreaker ?? true,
|
|
44
|
+
rateLimiter: options.rateLimiter ?? true,
|
|
41
45
|
};
|
|
42
46
|
return createProxy(sdk, opts, '');
|
|
43
47
|
}
|
|
@@ -79,6 +83,11 @@ function createProxy(target, opts, path) {
|
|
|
79
83
|
}
|
|
80
84
|
function createWrappedFunction(thisArg, fn, opts, actionPath) {
|
|
81
85
|
return async (...args) => {
|
|
86
|
+
// 0. Circuit breaker check
|
|
87
|
+
if (opts.circuitBreaker) {
|
|
88
|
+
const registry = getCircuitBreakerRegistry();
|
|
89
|
+
registry.allowRequest(opts.service); // Throws if circuit is open
|
|
90
|
+
}
|
|
82
91
|
// 1. Input validation
|
|
83
92
|
const schema = opts.inputSchemas[actionPath];
|
|
84
93
|
if (schema && args.length > 0 && args[0] && typeof args[0] === 'object') {
|
|
@@ -92,7 +101,13 @@ function createWrappedFunction(thisArg, fn, opts, actionPath) {
|
|
|
92
101
|
});
|
|
93
102
|
}
|
|
94
103
|
}
|
|
95
|
-
// 2.
|
|
104
|
+
// 2. Proactive rate limiting
|
|
105
|
+
if (opts.rateLimiter) {
|
|
106
|
+
await getRateLimiterRegistry().acquire(opts.service);
|
|
107
|
+
}
|
|
108
|
+
// 3. Retry loop with timeout
|
|
109
|
+
// Circuit breaker records are per-request, not per-attempt,
|
|
110
|
+
// to avoid fighting with retry logic.
|
|
96
111
|
let lastError;
|
|
97
112
|
const maxAttempts = opts.maxRetries + 1;
|
|
98
113
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
@@ -103,6 +118,10 @@ function createWrappedFunction(thisArg, fn, opts, actionPath) {
|
|
|
103
118
|
}
|
|
104
119
|
try {
|
|
105
120
|
const result = await withTimeout(fn.apply(thisArg, args), opts.timeout, opts.service, actionPath);
|
|
121
|
+
// Record success once per request (not per attempt)
|
|
122
|
+
if (opts.circuitBreaker) {
|
|
123
|
+
getCircuitBreakerRegistry().recordSuccess(opts.service);
|
|
124
|
+
}
|
|
106
125
|
return result;
|
|
107
126
|
}
|
|
108
127
|
catch (error) {
|
|
@@ -114,10 +133,17 @@ function createWrappedFunction(thisArg, fn, opts, actionPath) {
|
|
|
114
133
|
? opts.retryOn.includes(lastError.statusCode)
|
|
115
134
|
: lastError.retryable;
|
|
116
135
|
if (!shouldRetry) {
|
|
136
|
+
// Non-retryable failure — record and throw
|
|
137
|
+
if (opts.circuitBreaker) {
|
|
138
|
+
getCircuitBreakerRegistry().recordFailure(opts.service);
|
|
139
|
+
}
|
|
117
140
|
throw lastError;
|
|
118
141
|
}
|
|
119
|
-
// Last attempt — throw
|
|
142
|
+
// Last attempt — record failure and throw
|
|
120
143
|
if (attempt === maxAttempts - 1) {
|
|
144
|
+
if (opts.circuitBreaker) {
|
|
145
|
+
getCircuitBreakerRegistry().recordFailure(opts.service);
|
|
146
|
+
}
|
|
121
147
|
throw lastError;
|
|
122
148
|
}
|
|
123
149
|
}
|
|
@@ -153,22 +179,46 @@ async function withTimeout(promise, timeoutMs, service, action) {
|
|
|
153
179
|
if (!promise || typeof promise.then !== 'function') {
|
|
154
180
|
return promise;
|
|
155
181
|
}
|
|
182
|
+
const controller = new AbortController();
|
|
156
183
|
return new Promise((resolve, reject) => {
|
|
184
|
+
let settled = false;
|
|
157
185
|
const timer = setTimeout(() => {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
186
|
+
if (!settled) {
|
|
187
|
+
settled = true;
|
|
188
|
+
controller.abort();
|
|
189
|
+
reject(new IntegrationRequestError({
|
|
190
|
+
service,
|
|
191
|
+
action,
|
|
192
|
+
message: `Request timed out after ${timeoutMs}ms`,
|
|
193
|
+
retryable: true,
|
|
194
|
+
}));
|
|
195
|
+
}
|
|
164
196
|
}, timeoutMs);
|
|
165
197
|
promise.then((result) => {
|
|
166
|
-
|
|
167
|
-
|
|
198
|
+
if (!settled) {
|
|
199
|
+
settled = true;
|
|
200
|
+
clearTimeout(timer);
|
|
201
|
+
resolve(result);
|
|
202
|
+
}
|
|
168
203
|
}, (error) => {
|
|
169
|
-
|
|
170
|
-
|
|
204
|
+
if (!settled) {
|
|
205
|
+
settled = true;
|
|
206
|
+
clearTimeout(timer);
|
|
207
|
+
reject(error);
|
|
208
|
+
}
|
|
171
209
|
});
|
|
172
210
|
});
|
|
173
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* Create an AbortSignal that triggers after the given timeout.
|
|
214
|
+
* Useful for passing to fetch() or other cancellable operations.
|
|
215
|
+
*/
|
|
216
|
+
export function createTimeoutSignal(timeoutMs) {
|
|
217
|
+
const controller = new AbortController();
|
|
218
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
219
|
+
// Prevent timer from keeping the process alive
|
|
220
|
+
if (timer.unref)
|
|
221
|
+
timer.unref();
|
|
222
|
+
return controller.signal;
|
|
223
|
+
}
|
|
174
224
|
//# sourceMappingURL=wrapper.js.map
|