@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.
Files changed (111) hide show
  1. package/README.md +65 -16
  2. package/dist/adapters/claude-agent-types.d.ts +10 -8
  3. package/dist/adapters/claude-agent-types.d.ts.map +1 -1
  4. package/dist/adapters/claude-agent-types.js.map +1 -1
  5. package/dist/adapters/claude-agent-workflow.d.ts +10 -10
  6. package/dist/adapters/claude-agent.d.ts +2 -2
  7. package/dist/adapters/claude-agent.d.ts.map +1 -1
  8. package/dist/adapters/claude-agent.js +8 -2
  9. package/dist/adapters/claude-agent.js.map +1 -1
  10. package/dist/adapters/codex-types.d.ts +6 -6
  11. package/dist/adapters/codex-workflow.d.ts +16 -16
  12. package/dist/adapters/github-copilot-types.d.ts +21 -21
  13. package/dist/adapters/github-copilot-workflow.d.ts +8 -8
  14. package/dist/adapters/ollama-types.d.ts +42 -42
  15. package/dist/adapters/openai-types.d.ts +713 -0
  16. package/dist/adapters/openai-types.d.ts.map +1 -0
  17. package/dist/adapters/openai-types.js +91 -0
  18. package/dist/adapters/openai-types.js.map +1 -0
  19. package/dist/adapters/openai.d.ts +128 -0
  20. package/dist/adapters/openai.d.ts.map +1 -0
  21. package/dist/adapters/openai.js +387 -0
  22. package/dist/adapters/openai.js.map +1 -0
  23. package/dist/adapters/opencode.d.ts +25 -0
  24. package/dist/adapters/opencode.d.ts.map +1 -1
  25. package/dist/adapters/opencode.js +205 -15
  26. package/dist/adapters/opencode.js.map +1 -1
  27. package/dist/index.d.ts +3 -1
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +10 -4
  30. package/dist/index.js.map +1 -1
  31. package/dist/reliability/circuit-breaker.d.ts +67 -0
  32. package/dist/reliability/circuit-breaker.d.ts.map +1 -0
  33. package/dist/reliability/circuit-breaker.js +164 -0
  34. package/dist/reliability/circuit-breaker.js.map +1 -0
  35. package/dist/reliability/index.d.ts +3 -1
  36. package/dist/reliability/index.d.ts.map +1 -1
  37. package/dist/reliability/index.js +3 -1
  38. package/dist/reliability/index.js.map +1 -1
  39. package/dist/reliability/rate-limiter.d.ts +62 -0
  40. package/dist/reliability/rate-limiter.d.ts.map +1 -0
  41. package/dist/reliability/rate-limiter.js +194 -0
  42. package/dist/reliability/rate-limiter.js.map +1 -0
  43. package/dist/reliability/wrapper.d.ts +9 -0
  44. package/dist/reliability/wrapper.d.ts.map +1 -1
  45. package/dist/reliability/wrapper.js +62 -12
  46. package/dist/reliability/wrapper.js.map +1 -1
  47. package/dist/services/ai-browser.d.ts +3 -3
  48. package/dist/services/ai-browser.d.ts.map +1 -1
  49. package/dist/services/ai-browser.js +1 -1
  50. package/dist/services/ai-browser.js.map +1 -1
  51. package/dist/services/base-client.d.ts.map +1 -1
  52. package/dist/services/base-client.js +25 -3
  53. package/dist/services/base-client.js.map +1 -1
  54. package/dist/services/discord.d.ts.map +1 -1
  55. package/dist/services/discord.js +6 -0
  56. package/dist/services/discord.js.map +1 -1
  57. package/dist/services/gmail.d.ts.map +1 -1
  58. package/dist/services/gmail.js +65 -47
  59. package/dist/services/gmail.js.map +1 -1
  60. package/dist/services/google-calendar.js +9 -5
  61. package/dist/services/google-calendar.js.map +1 -1
  62. package/dist/services/google-docs.js +9 -5
  63. package/dist/services/google-docs.js.map +1 -1
  64. package/dist/services/google-drive.js +9 -5
  65. package/dist/services/google-drive.js.map +1 -1
  66. package/dist/services/google-sheets.js +9 -5
  67. package/dist/services/google-sheets.js.map +1 -1
  68. package/dist/services/http.d.ts.map +1 -1
  69. package/dist/services/http.js +15 -1
  70. package/dist/services/http.js.map +1 -1
  71. package/dist/services/mailchimp.d.ts.map +1 -1
  72. package/dist/services/mailchimp.js +3 -0
  73. package/dist/services/mailchimp.js.map +1 -1
  74. package/dist/services/outlook.d.ts.map +1 -1
  75. package/dist/services/outlook.js +14 -11
  76. package/dist/services/outlook.js.map +1 -1
  77. package/dist/services/playwright/client.d.ts +110 -0
  78. package/dist/services/playwright/client.d.ts.map +1 -0
  79. package/dist/services/playwright/client.js +690 -0
  80. package/dist/services/playwright/client.js.map +1 -0
  81. package/dist/services/playwright/index.d.ts +7 -0
  82. package/dist/services/playwright/index.d.ts.map +1 -0
  83. package/dist/services/playwright/index.js +7 -0
  84. package/dist/services/playwright/index.js.map +1 -0
  85. package/dist/services/playwright/initializer.d.ts +24 -0
  86. package/dist/services/playwright/initializer.d.ts.map +1 -0
  87. package/dist/services/playwright/initializer.js +99 -0
  88. package/dist/services/playwright/initializer.js.map +1 -0
  89. package/dist/services/playwright/types.d.ts +270 -0
  90. package/dist/services/playwright/types.d.ts.map +1 -0
  91. package/dist/services/playwright/types.js +5 -0
  92. package/dist/services/playwright/types.js.map +1 -0
  93. package/dist/services/playwright.d.ts +3 -675
  94. package/dist/services/playwright.d.ts.map +1 -1
  95. package/dist/services/playwright.js +3 -1138
  96. package/dist/services/playwright.js.map +1 -1
  97. package/dist/services/rss.d.ts +57 -0
  98. package/dist/services/rss.d.ts.map +1 -0
  99. package/dist/services/rss.js +190 -0
  100. package/dist/services/rss.js.map +1 -0
  101. package/dist/services/shopify.d.ts.map +1 -1
  102. package/dist/services/shopify.js +3 -0
  103. package/dist/services/shopify.js.map +1 -1
  104. package/dist/services/trello.d.ts.map +1 -1
  105. package/dist/services/trello.js +7 -1
  106. package/dist/services/trello.js.map +1 -1
  107. package/package.json +30 -8
  108. package/dist/adapters/claude-code.d.ts +0 -34
  109. package/dist/adapters/claude-code.d.ts.map +0 -1
  110. package/dist/adapters/claude-code.js +0 -89
  111. 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,GAGhB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,uBAAuB,EACvB,cAAc,GAEf,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.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;AAOxB,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;CAC7C;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,CAYH"}
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. Retry loop with timeout
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
- reject(new IntegrationRequestError({
159
- service,
160
- action,
161
- message: `Request timed out after ${timeoutMs}ms`,
162
- retryable: true,
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
- clearTimeout(timer);
167
- resolve(result);
198
+ if (!settled) {
199
+ settled = true;
200
+ clearTimeout(timer);
201
+ resolve(result);
202
+ }
168
203
  }, (error) => {
169
- clearTimeout(timer);
170
- reject(error);
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