paygate-mcp 9.5.0 → 9.7.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.
package/README.md CHANGED
@@ -11,7 +11,7 @@ Monetize any MCP server with one command. Add API key auth, per-tool pricing, ra
11
11
  - [Quick Start](#quick-start)
12
12
  - [What It Does](#what-it-does)
13
13
  - [Usage](#usage) — Local stdio, remote HTTP, multi-server, client SDK
14
- - [API Reference](#api-reference) — All 154+ endpoints
14
+ - [API Reference](#api-reference) — All 166+ endpoints
15
15
  - [CLI Options](#cli-options)
16
16
  - [Deployment](#deployment) — Docker, docker-compose, systemd, PM2
17
17
  - [Load Testing](#load-testing) — k6 benchmarking for production
@@ -66,7 +66,7 @@ Agent → PayGate (auth + billing) → Your MCP Server (stdio or HTTP)
66
66
  - **SSE Streaming** — Full MCP Streamable HTTP transport (POST SSE, GET notifications, DELETE sessions)
67
67
  - **Audit Log** — Structured audit trail with retention policies, query API, CSV/JSON export
68
68
  - **Registry/Discovery** — Agent-discoverable pricing via `/.well-known/mcp-payment`, `/pricing`, and `/.well-known/mcp.json` identity card
69
- - **OpenAPI 3.1 + Interactive Docs** — Auto-generated spec at `/openapi.json`, Swagger UI at `/docs` — all 154+ endpoints documented
69
+ - **OpenAPI 3.1 + Interactive Docs** — Auto-generated spec at `/openapi.json`, Swagger UI at `/docs` — all 166+ endpoints documented
70
70
  - **Public Endpoint Rate Limiting** — Configurable per-IP rate limit (default 300/min) on `/health`, `/info`, `/pricing`, `/docs`, `/openapi.json`, `/.well-known/*`, `/robots.txt`, `/` — 429 with Retry-After header
71
71
  - **Robots.txt + HEAD Support** — Standard `/robots.txt` (allow public, disallow admin/keys), HEAD method on all public endpoints for uptime monitoring
72
72
  - **Prometheus Metrics** — `/metrics` endpoint with counters, gauges, and uptime in standard text format
@@ -162,6 +162,12 @@ Agent → PayGate (auth + billing) → Your MCP Server (stdio or HTTP)
162
162
  - **Concurrency Limiter** — Per-key and per-tool inflight request caps — distinct from rate limiting, limits simultaneous active requests to protect backends from burst parallelism, error code `-32005` with `Retry-After` header, runtime-adjustable via `GET/POST /admin/concurrency`
163
163
  - **Traffic Mirroring** — Fire-and-forget request duplication to a shadow backend for A/B testing MCP server versions — percentage-based sampling, configurable timeout, zero impact on primary response path, stats/management via `GET/POST/DELETE /admin/mirror`
164
164
  - **Tool Aliasing + Deprecation** — Tool renaming with RFC 8594 compliance — map old tool names to new ones with `Deprecation`, `Sunset`, and `Link` headers, chain prevention, per-alias call counts, CRUD via `GET/POST/DELETE /admin/tool-aliases`
165
+ - **Usage Plans** — Tiered key policies (free/pro/enterprise) — bundle rate limits, quotas, credit multipliers, and tool ACL into reusable templates, assign keys to plans via `POST /admin/keys/plan`, denied tools rejected with error code `-32403`, CRUD via `GET/POST/DELETE /admin/plans`
166
+ - **Tool Input Schema Validation** — Per-tool JSON Schema validation at the gateway — register schemas to reject invalid payloads before they reach downstream, zero-dependency JSON Schema subset (type, required, enum, minLength, pattern, items), error code `-32602` with detailed errors, manage via `GET/POST/DELETE /admin/tools/schema`
167
+ - **Canary Routing** — Weighted traffic splitting between primary and canary MCP servers — enable zero-downtime upgrades with percentage-based routing (0-100%), unbiased `crypto.randomInt` decisions, per-backend call/error tracking, weight updates without restart, manage via `GET/POST/DELETE /admin/canary`
168
+ - **Request/Response Transforms** — Declarative rewriting of tool call arguments and responses — inject defaults, strip fields, rename keys, and template `{{variables}}` from context, wildcard tool matching, priority ordering, deep clone on apply, import/export for backup, manage via `GET/POST/PUT/DELETE /admin/transforms`
169
+ - **Backend Retry Policy** — Automatic retry with exponential backoff for transient failures — configurable max retries, base/max backoff, full jitter, retry budget (max % of traffic as retries with cold-start grace), per-tool stats, retryable error pattern matching, manage via `GET/POST /admin/retry-policy`
170
+ - **Adaptive Rate Limiting** — Dynamic rate adjustment based on key behavior — auto-tighten for high error rates, auto-boost for good actors, cooldown periods, configurable thresholds, per-key behavior tracking, LRU eviction, batch evaluation, manage via `GET/POST /admin/adaptive-rates`
165
171
  - **Anomaly Detection** — `GET /admin/anomalies` identifies unusual patterns: keys with high denial rates, rapid credit depletion, low remaining credits, with severity ratings and detailed descriptions
166
172
  - **Usage Forecasting** — `GET /admin/forecast` predicts future credit consumption with per-key depletion estimates, calls remaining, at-risk key identification, system-wide consumption aggregates, and per-tool cost breakdown
167
173
  - **Compliance Report** — `GET /admin/compliance` generates compliance-ready report with key governance (expiry coverage), access control (ACL/IP/spending limit coverage), audit trail completeness, weighted overall score, and actionable recommendations
@@ -475,7 +481,7 @@ A real-time admin UI for managing keys, viewing usage, and monitoring tool calls
475
481
  | `/.well-known/mcp-payment` | GET | None | Server payment metadata (SEP-2007) |
476
482
  | `/.well-known/mcp.json` | GET | None | MCP Server Identity card (discovery) |
477
483
  | `/pricing` | GET | None | Full per-tool pricing breakdown |
478
- | `/openapi.json` | GET | None | OpenAPI 3.1 spec (all 154+ endpoints) |
484
+ | `/openapi.json` | GET | None | OpenAPI 3.1 spec (all 166+ endpoints) |
479
485
  | `/docs` | GET | None | Interactive API docs (Swagger UI) |
480
486
  | `/robots.txt` | GET | None | Crawler directives (allow public, disallow admin/keys) |
481
487
  | `/portal` | GET | None | Self-service API key portal (browser UI, auth via X-API-Key prompt) |
@@ -0,0 +1,126 @@
1
+ /**
2
+ * AdaptiveRateLimiter — Dynamic rate limit adjustment based on traffic behavior.
3
+ *
4
+ * Automatically tightens limits for misbehaving keys (high error rates,
5
+ * excessive credit velocity) and loosens limits for well-behaved keys
6
+ * (zero denials, steady usage). Wraps the existing static RateLimiter
7
+ * with a per-key multiplier.
8
+ *
9
+ * Inspired by Apigee adaptive rate limiting and Tyk request throttling.
10
+ * Zero external dependencies.
11
+ */
12
+ export interface AdaptiveRateConfig {
13
+ /** Enable adaptive rate limiting. */
14
+ enabled: boolean;
15
+ /** Sliding window size in seconds for evaluation. Default 300 (5 min). */
16
+ evaluationWindowSeconds: number;
17
+ /** Error rate threshold (0-1) above which rate is tightened. Default 0.3. */
18
+ errorRateThreshold: number;
19
+ /** If credit spend rate exceeds this × average, tighten. Default 1.5. */
20
+ creditVelocityMultiplier: number;
21
+ /** Never reduce below this % of configured limit. Default 25. */
22
+ minRatePercent: number;
23
+ /** Allow burst up to this % for good behavior. Default 200. */
24
+ maxRatePercent: number;
25
+ /** Hold adjusted rate for at least this many seconds. Default 60. */
26
+ cooldownSeconds: number;
27
+ }
28
+ export interface KeyBehavior {
29
+ totalCalls: number;
30
+ errorCalls: number;
31
+ deniedCalls: number;
32
+ creditsSpent: number;
33
+ /** Current effective multiplier (1.0 = nominal). */
34
+ multiplier: number;
35
+ /** Last time the multiplier was adjusted. */
36
+ lastAdjusted: number;
37
+ /** Sliding window of call timestamps. */
38
+ callTimestamps: number[];
39
+ /** Sliding window of error timestamps. */
40
+ errorTimestamps: number[];
41
+ }
42
+ export interface AdaptiveRateStats {
43
+ enabled: boolean;
44
+ config: AdaptiveRateConfig;
45
+ totalKeys: number;
46
+ tightenedKeys: number;
47
+ boostedKeys: number;
48
+ normalKeys: number;
49
+ keyDetails: Array<{
50
+ key: string;
51
+ multiplier: number;
52
+ effectiveRate: string;
53
+ recentCalls: number;
54
+ recentErrors: number;
55
+ errorRate: number;
56
+ }>;
57
+ }
58
+ export interface AdaptiveRateAdjustment {
59
+ key: string;
60
+ previousMultiplier: number;
61
+ newMultiplier: number;
62
+ reason: string;
63
+ effectiveRate: number;
64
+ baseRate: number;
65
+ }
66
+ export declare class AdaptiveRateLimiter {
67
+ private config;
68
+ private readonly keyBehaviors;
69
+ private readonly maxTrackedKeys;
70
+ constructor(config?: Partial<AdaptiveRateConfig>);
71
+ /**
72
+ * Update configuration at runtime.
73
+ */
74
+ configure(updates: Partial<AdaptiveRateConfig>): AdaptiveRateConfig;
75
+ /**
76
+ * Record a call for a key.
77
+ */
78
+ recordCall(apiKey: string): void;
79
+ /**
80
+ * Record an error for a key.
81
+ */
82
+ recordError(apiKey: string): void;
83
+ /**
84
+ * Record a denied request for a key.
85
+ */
86
+ recordDenied(apiKey: string): void;
87
+ /**
88
+ * Record credits spent by a key.
89
+ */
90
+ recordCredits(apiKey: string, amount: number): void;
91
+ /**
92
+ * Get the effective rate multiplier for a key.
93
+ * Returns 1.0 if adaptive limiting is disabled.
94
+ */
95
+ getMultiplier(apiKey: string): number;
96
+ /**
97
+ * Get the effective rate limit for a key.
98
+ */
99
+ getEffectiveRate(apiKey: string, baseRate: number): number;
100
+ /**
101
+ * Evaluate and adjust the rate multiplier for a key.
102
+ * Call this periodically (e.g., every minute) or on every Nth request.
103
+ */
104
+ evaluate(apiKey: string): AdaptiveRateAdjustment | null;
105
+ /**
106
+ * Evaluate all tracked keys.
107
+ */
108
+ evaluateAll(): AdaptiveRateAdjustment[];
109
+ private pruneTimestamps;
110
+ private getOrCreate;
111
+ /**
112
+ * Get adaptive rate statistics.
113
+ */
114
+ stats(): AdaptiveRateStats;
115
+ /**
116
+ * Reset tracking for a specific key.
117
+ */
118
+ resetKey(apiKey: string): boolean;
119
+ /**
120
+ * Clear all tracking data.
121
+ */
122
+ clear(): void;
123
+ /** Number of tracked keys. */
124
+ get size(): number;
125
+ }
126
+ //# sourceMappingURL=adaptive-rate-limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adaptive-rate-limiter.d.ts","sourceRoot":"","sources":["../src/adaptive-rate-limiter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,260 @@
1
+ "use strict";
2
+ /**
3
+ * AdaptiveRateLimiter — Dynamic rate limit adjustment based on traffic behavior.
4
+ *
5
+ * Automatically tightens limits for misbehaving keys (high error rates,
6
+ * excessive credit velocity) and loosens limits for well-behaved keys
7
+ * (zero denials, steady usage). Wraps the existing static RateLimiter
8
+ * with a per-key multiplier.
9
+ *
10
+ * Inspired by Apigee adaptive rate limiting and Tyk request throttling.
11
+ * Zero external dependencies.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.AdaptiveRateLimiter = void 0;
15
+ // ─── Default config ─────────────────────────────────────────────────────────
16
+ const DEFAULT_ADAPTIVE_CONFIG = {
17
+ enabled: false,
18
+ evaluationWindowSeconds: 300,
19
+ errorRateThreshold: 0.3,
20
+ creditVelocityMultiplier: 1.5,
21
+ minRatePercent: 25,
22
+ maxRatePercent: 200,
23
+ cooldownSeconds: 60,
24
+ };
25
+ // ─── AdaptiveRateLimiter Class ──────────────────────────────────────────────
26
+ class AdaptiveRateLimiter {
27
+ config;
28
+ keyBehaviors = new Map();
29
+ maxTrackedKeys = 5000;
30
+ constructor(config) {
31
+ this.config = { ...DEFAULT_ADAPTIVE_CONFIG, ...config };
32
+ }
33
+ /**
34
+ * Update configuration at runtime.
35
+ */
36
+ configure(updates) {
37
+ if (updates.enabled !== undefined)
38
+ this.config.enabled = updates.enabled;
39
+ if (updates.evaluationWindowSeconds !== undefined)
40
+ this.config.evaluationWindowSeconds = Math.max(30, updates.evaluationWindowSeconds);
41
+ if (updates.errorRateThreshold !== undefined)
42
+ this.config.errorRateThreshold = Math.min(1, Math.max(0, updates.errorRateThreshold));
43
+ if (updates.creditVelocityMultiplier !== undefined)
44
+ this.config.creditVelocityMultiplier = Math.max(1, updates.creditVelocityMultiplier);
45
+ if (updates.minRatePercent !== undefined)
46
+ this.config.minRatePercent = Math.min(100, Math.max(1, updates.minRatePercent));
47
+ if (updates.maxRatePercent !== undefined)
48
+ this.config.maxRatePercent = Math.max(100, updates.maxRatePercent);
49
+ if (updates.cooldownSeconds !== undefined)
50
+ this.config.cooldownSeconds = Math.max(0, updates.cooldownSeconds);
51
+ return { ...this.config };
52
+ }
53
+ /**
54
+ * Record a call for a key.
55
+ */
56
+ recordCall(apiKey) {
57
+ if (!this.config.enabled)
58
+ return;
59
+ const behavior = this.getOrCreate(apiKey);
60
+ behavior.totalCalls++;
61
+ behavior.callTimestamps.push(Date.now());
62
+ }
63
+ /**
64
+ * Record an error for a key.
65
+ */
66
+ recordError(apiKey) {
67
+ if (!this.config.enabled)
68
+ return;
69
+ const behavior = this.getOrCreate(apiKey);
70
+ behavior.errorCalls++;
71
+ behavior.errorTimestamps.push(Date.now());
72
+ }
73
+ /**
74
+ * Record a denied request for a key.
75
+ */
76
+ recordDenied(apiKey) {
77
+ if (!this.config.enabled)
78
+ return;
79
+ const behavior = this.getOrCreate(apiKey);
80
+ behavior.deniedCalls++;
81
+ }
82
+ /**
83
+ * Record credits spent by a key.
84
+ */
85
+ recordCredits(apiKey, amount) {
86
+ if (!this.config.enabled)
87
+ return;
88
+ const behavior = this.getOrCreate(apiKey);
89
+ behavior.creditsSpent += amount;
90
+ }
91
+ /**
92
+ * Get the effective rate multiplier for a key.
93
+ * Returns 1.0 if adaptive limiting is disabled.
94
+ */
95
+ getMultiplier(apiKey) {
96
+ if (!this.config.enabled)
97
+ return 1.0;
98
+ const behavior = this.keyBehaviors.get(apiKey);
99
+ if (!behavior)
100
+ return 1.0;
101
+ return behavior.multiplier;
102
+ }
103
+ /**
104
+ * Get the effective rate limit for a key.
105
+ */
106
+ getEffectiveRate(apiKey, baseRate) {
107
+ return Math.round(baseRate * this.getMultiplier(apiKey));
108
+ }
109
+ /**
110
+ * Evaluate and adjust the rate multiplier for a key.
111
+ * Call this periodically (e.g., every minute) or on every Nth request.
112
+ */
113
+ evaluate(apiKey) {
114
+ if (!this.config.enabled)
115
+ return null;
116
+ const behavior = this.keyBehaviors.get(apiKey);
117
+ if (!behavior)
118
+ return null;
119
+ // Check cooldown
120
+ const now = Date.now();
121
+ const cooldownMs = this.config.cooldownSeconds * 1000;
122
+ if (now - behavior.lastAdjusted < cooldownMs)
123
+ return null;
124
+ // Prune old timestamps
125
+ this.pruneTimestamps(behavior);
126
+ const recentCalls = behavior.callTimestamps.length;
127
+ const recentErrors = behavior.errorTimestamps.length;
128
+ // Need minimum traffic to evaluate
129
+ if (recentCalls < 5)
130
+ return null;
131
+ const previousMultiplier = behavior.multiplier;
132
+ let newMultiplier = 1.0;
133
+ let reason = 'normal behavior';
134
+ // Check error rate
135
+ const errorRate = recentErrors / recentCalls;
136
+ if (errorRate > this.config.errorRateThreshold) {
137
+ // Tighten: reduce to proportional amount
138
+ const tightenFactor = 1 - (errorRate * 0.5); // 30% errors → 0.85x, 60% → 0.7x
139
+ newMultiplier = Math.max(this.config.minRatePercent / 100, tightenFactor);
140
+ reason = `high error rate: ${(errorRate * 100).toFixed(1)}%`;
141
+ }
142
+ else if (errorRate < 0.05 && behavior.deniedCalls === 0) {
143
+ // Boost: reward good behavior
144
+ const boostFactor = 1 + ((1 - errorRate) * 0.3); // up to 1.3x for perfect keys
145
+ newMultiplier = Math.min(this.config.maxRatePercent / 100, boostFactor);
146
+ reason = `good behavior: ${(errorRate * 100).toFixed(1)}% error rate`;
147
+ }
148
+ // Clamp
149
+ newMultiplier = Math.max(this.config.minRatePercent / 100, Math.min(this.config.maxRatePercent / 100, newMultiplier));
150
+ // Only update if significantly different (>5% change)
151
+ if (Math.abs(newMultiplier - previousMultiplier) < 0.05)
152
+ return null;
153
+ behavior.multiplier = newMultiplier;
154
+ behavior.lastAdjusted = now;
155
+ return {
156
+ key: apiKey.slice(0, 8) + '...',
157
+ previousMultiplier,
158
+ newMultiplier,
159
+ reason,
160
+ effectiveRate: 0, // caller fills in with base rate
161
+ baseRate: 0,
162
+ };
163
+ }
164
+ /**
165
+ * Evaluate all tracked keys.
166
+ */
167
+ evaluateAll() {
168
+ const adjustments = [];
169
+ for (const key of this.keyBehaviors.keys()) {
170
+ const adj = this.evaluate(key);
171
+ if (adj)
172
+ adjustments.push(adj);
173
+ }
174
+ return adjustments;
175
+ }
176
+ pruneTimestamps(behavior) {
177
+ const cutoff = Date.now() - (this.config.evaluationWindowSeconds * 1000);
178
+ behavior.callTimestamps = behavior.callTimestamps.filter(t => t > cutoff);
179
+ behavior.errorTimestamps = behavior.errorTimestamps.filter(t => t > cutoff);
180
+ }
181
+ getOrCreate(apiKey) {
182
+ let behavior = this.keyBehaviors.get(apiKey);
183
+ if (!behavior) {
184
+ // Evict oldest if at capacity
185
+ if (this.keyBehaviors.size >= this.maxTrackedKeys) {
186
+ const firstKey = this.keyBehaviors.keys().next().value;
187
+ if (firstKey)
188
+ this.keyBehaviors.delete(firstKey);
189
+ }
190
+ behavior = {
191
+ totalCalls: 0,
192
+ errorCalls: 0,
193
+ deniedCalls: 0,
194
+ creditsSpent: 0,
195
+ multiplier: 1.0,
196
+ lastAdjusted: 0,
197
+ callTimestamps: [],
198
+ errorTimestamps: [],
199
+ };
200
+ this.keyBehaviors.set(apiKey, behavior);
201
+ }
202
+ return behavior;
203
+ }
204
+ /**
205
+ * Get adaptive rate statistics.
206
+ */
207
+ stats() {
208
+ const keyDetails = [];
209
+ let tightened = 0;
210
+ let boosted = 0;
211
+ let normal = 0;
212
+ for (const [key, behavior] of this.keyBehaviors) {
213
+ this.pruneTimestamps(behavior);
214
+ const recentCalls = behavior.callTimestamps.length;
215
+ const recentErrors = behavior.errorTimestamps.length;
216
+ const errorRate = recentCalls > 0 ? recentErrors / recentCalls : 0;
217
+ if (behavior.multiplier < 0.95)
218
+ tightened++;
219
+ else if (behavior.multiplier > 1.05)
220
+ boosted++;
221
+ else
222
+ normal++;
223
+ keyDetails.push({
224
+ key: key.slice(0, 8) + '...',
225
+ multiplier: Math.round(behavior.multiplier * 100) / 100,
226
+ effectiveRate: `${Math.round(behavior.multiplier * 100)}%`,
227
+ recentCalls,
228
+ recentErrors,
229
+ errorRate: Math.round(errorRate * 100) / 100,
230
+ });
231
+ }
232
+ return {
233
+ enabled: this.config.enabled,
234
+ config: { ...this.config },
235
+ totalKeys: this.keyBehaviors.size,
236
+ tightenedKeys: tightened,
237
+ boostedKeys: boosted,
238
+ normalKeys: normal,
239
+ keyDetails,
240
+ };
241
+ }
242
+ /**
243
+ * Reset tracking for a specific key.
244
+ */
245
+ resetKey(apiKey) {
246
+ return this.keyBehaviors.delete(apiKey);
247
+ }
248
+ /**
249
+ * Clear all tracking data.
250
+ */
251
+ clear() {
252
+ this.keyBehaviors.clear();
253
+ }
254
+ /** Number of tracked keys. */
255
+ get size() {
256
+ return this.keyBehaviors.size;
257
+ }
258
+ }
259
+ exports.AdaptiveRateLimiter = AdaptiveRateLimiter;
260
+ //# sourceMappingURL=adaptive-rate-limiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adaptive-rate-limiter.js","sourceRoot":"","sources":["../src/adaptive-rate-limiter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * CanaryRouter — Weighted traffic splitting between primary and canary backends.
3
+ *
4
+ * Enables zero-downtime MCP server upgrades by splitting tool-call traffic
5
+ * between a primary and canary MCP server process. The canary weight (0-100%)
6
+ * determines the percentage of requests routed to the canary.
7
+ *
8
+ * Uses crypto.randomInt for unbiased routing decisions.
9
+ * Tracks per-backend call counts and error rates.
10
+ *
11
+ * Zero external dependencies.
12
+ */
13
+ import { EventEmitter } from 'events';
14
+ export interface CanaryConfig {
15
+ /** Command to spawn the canary MCP server. */
16
+ serverCommand: string;
17
+ /** Args for the canary server command. */
18
+ serverArgs: string[];
19
+ /** Percentage of traffic to route to canary (0-100). */
20
+ weight: number;
21
+ }
22
+ export interface CanaryStats {
23
+ enabled: boolean;
24
+ weight: number;
25
+ canaryCommand: string | null;
26
+ primaryCalls: number;
27
+ canaryCalls: number;
28
+ primaryErrors: number;
29
+ canaryErrors: number;
30
+ createdAt: string | null;
31
+ }
32
+ export type CanaryBackend = 'primary' | 'canary';
33
+ export interface CanaryDecision {
34
+ backend: CanaryBackend;
35
+ weight: number;
36
+ }
37
+ export declare class CanaryRouter extends EventEmitter {
38
+ private config;
39
+ private primaryCalls;
40
+ private canaryCalls;
41
+ private primaryErrors;
42
+ private canaryErrors;
43
+ private createdAt;
44
+ constructor();
45
+ /**
46
+ * Enable canary routing with the given config.
47
+ * The actual canary process spawning is handled by the server — this class
48
+ * only manages the routing decision and stats.
49
+ */
50
+ enable(config: CanaryConfig): void;
51
+ /**
52
+ * Disable canary routing.
53
+ */
54
+ disable(): void;
55
+ /**
56
+ * Update canary weight without restart.
57
+ */
58
+ setWeight(weight: number): void;
59
+ /**
60
+ * Route a request to primary or canary based on weight.
61
+ * Uses crypto.randomInt for unbiased routing.
62
+ */
63
+ route(): CanaryDecision;
64
+ /**
65
+ * Record an error for a backend.
66
+ */
67
+ recordError(backend: CanaryBackend): void;
68
+ /**
69
+ * Get canary statistics.
70
+ */
71
+ stats(): CanaryStats;
72
+ /**
73
+ * Check if canary routing is enabled.
74
+ */
75
+ get enabled(): boolean;
76
+ /**
77
+ * Get current weight.
78
+ */
79
+ get weight(): number;
80
+ /**
81
+ * Get canary config (if enabled).
82
+ */
83
+ get canaryConfig(): CanaryConfig | null;
84
+ }
85
+ //# sourceMappingURL=canary-router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canary-router.d.ts","sourceRoot":"","sources":["../src/canary-router.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAItC,MAAM,WAAW,YAAY;IAC3B,8CAA8C;IAC9C,aAAa,EAAE,MAAM,CAAC;IACtB,0CAA0C;IAC1C,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEjD,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,aAAa,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAID,qBAAa,YAAa,SAAQ,YAAY;IAC5C,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,SAAS,CAAuB;;IAMxC;;;;OAIG;IACH,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAiBlC;;OAEG;IACH,OAAO,IAAI,IAAI;IAKf;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAM/B;;;OAGG;IACH,KAAK,IAAI,cAAc;IAsBvB;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAQzC;;OAEG;IACH,KAAK,IAAI,WAAW;IAepB;;OAEG;IACH,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;OAEG;IACH,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED;;OAEG;IACH,IAAI,YAAY,IAAI,YAAY,GAAG,IAAI,CAEtC;CACF"}