@splashcodex/api-key-manager 4.1.0 → 5.0.2

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 (39) hide show
  1. package/README.md +129 -201
  2. package/bin/cli.js +50 -0
  3. package/dist/index.d.ts +7 -2
  4. package/dist/index.js +16 -4
  5. package/dist/index.js.map +1 -1
  6. package/dist/persistence/file.d.ts +43 -0
  7. package/dist/persistence/file.js +82 -0
  8. package/dist/persistence/file.js.map +1 -0
  9. package/dist/persistence/index.d.ts +7 -0
  10. package/dist/persistence/index.js +12 -0
  11. package/dist/persistence/index.js.map +1 -0
  12. package/dist/persistence/memory.d.ts +30 -0
  13. package/dist/persistence/memory.js +43 -0
  14. package/dist/persistence/memory.js.map +1 -0
  15. package/dist/presets/base.d.ts +138 -0
  16. package/dist/presets/base.js +227 -0
  17. package/dist/presets/base.js.map +1 -0
  18. package/dist/presets/gemini.d.ts +60 -0
  19. package/dist/presets/gemini.js +77 -0
  20. package/dist/presets/gemini.js.map +1 -0
  21. package/dist/presets/index.d.ts +10 -0
  22. package/dist/presets/index.js +16 -0
  23. package/dist/presets/index.js.map +1 -0
  24. package/dist/presets/multi.d.ts +120 -0
  25. package/dist/presets/multi.js +210 -0
  26. package/dist/presets/multi.js.map +1 -0
  27. package/dist/presets/openai.d.ts +45 -0
  28. package/dist/presets/openai.js +62 -0
  29. package/dist/presets/openai.js.map +1 -0
  30. package/package.json +88 -5
  31. package/src/index.ts +24 -3
  32. package/src/persistence/file.ts +89 -0
  33. package/src/persistence/index.ts +7 -0
  34. package/src/persistence/memory.ts +43 -0
  35. package/src/presets/base.ts +323 -0
  36. package/src/presets/gemini.ts +84 -0
  37. package/src/presets/index.ts +10 -0
  38. package/src/presets/multi.ts +280 -0
  39. package/src/presets/openai.ts +69 -0
package/README.md CHANGED
@@ -1,9 +1,18 @@
1
- # @splashcodex/ApiKeyManager v4.1Mastermind Edition
1
+ # @splashcodex/ApiKeyManager v5.0Ecosystem Edition
2
2
 
3
- > Universal API Key Rotation System with Resilience, Load Balancing, Semantic Caching & AI Gateway Features
3
+ > Universal API Key Management Gateway with Provider Presets, Built-in Persistence, and Multi-Provider Vault.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@splashcodex/ApiKeyManager)](https://www.npmjs.com/package/@splashcodex/ApiKeyManager)
6
6
 
7
+ ## New in v5.0 (Ecosystem Edition)
8
+
9
+ - **Provider Presets** — One-line setup for `GeminiManager`, `OpenAIManager`, and `MultiManager`.
10
+ - **Automatic Env Parsing** — Reads `GOOGLE_GEMINI_API_KEY`, `OPENAI_API_KEY`, etc. (supports JSON arrays and comma-separated strings).
11
+ - **Built-in Persistence** — `FileStorage` (survives restarts) and `MemoryStorage` included.
12
+ - **Singleton Pattern** — Thread-safe singletons with `getInstance()` and `Result<T>` pattern.
13
+ - **Multi-Provider Vault** — Manage multiple providers (`gemini`, `openai`, `anthropic`) from a single entry point.
14
+ - **Centralized API Gateway** — Built-in Fastify proxy server to centralize AI requests for multiple apps securely.
15
+
7
16
  ## Features
8
17
 
9
18
  - **Circuit Breaker** — Keys transition through `CLOSED → OPEN → HALF_OPEN → DEAD`
@@ -12,16 +21,9 @@
12
21
  - **`execute()` Wrapper** — Single method: get key → call → latency → retry → fallback
13
22
  - **Event Emitter** — Typed lifecycle hooks for monitoring & alerting
14
23
  - **Auto-Retry with Backoff** — Built-in retry loop with exponential backoff + jitter
15
- - **Request Timeout** — `AbortController`-based timeout per attempt
16
- - **Fallback Function** — Graceful degradation when all keys fail
17
- - **Provider Tagging** — Multi-provider routing (`openai`, `gemini`, etc.)
18
- - **Health Checks** — Periodic key validation and auto-recovery
19
- - **Bulkhead / Concurrency** — Limits concurrent `execute()` calls
20
- - **Semantic Cache** *(v4 NEW)* — Cosine-similarity cache with pluggable embeddings
21
- - **Streaming Support** *(v4.1 NEW)* — `executeStream()` with initial retry + cache replay
22
- - **Recursion Guard** *(v4 NEW)* — Prevents infinite loops when `getEmbedding` calls `execute()`
23
- - **State Persistence** — Survives restarts via pluggable storage
24
- - **100% Backward Compatible** — v1.x, v2.x, and v3.x code works without changes
24
+ - **Semantic Cache** — Cosine-similarity cache with pluggable embeddings
25
+ - **Streaming Support** — `executeStream()` with initial retry + cache replay
26
+ - **100% Backward Compatible** — v1.x through v4.x code works without changes
25
27
 
26
28
  ## Installation
27
29
 
@@ -29,249 +31,175 @@
29
31
  npm install @splashcodex/api-key-manager
30
32
  ```
31
33
 
32
- ## Quick Start
33
-
34
- ```typescript
35
- import { ApiKeyManager } from '@splashcodex/api-key-manager';
36
-
37
- // Simple (v1/v2 compatible)
38
- const manager = new ApiKeyManager(['key1', 'key2', 'key3']);
39
- const key = manager.getKey();
40
- manager.markSuccess(key!);
41
-
42
- // v3+ — Full power
43
- const result = await manager.execute(
44
- (key) => fetch(`https://api.example.com?key=${key}`),
45
- { maxRetries: 3, timeoutMs: 5000 }
46
- );
34
+ ### 🚀 Auto-Scaffold (New!)
35
+ The fastest way to get started is to run the `init` command in your project directory:
36
+ ```bash
37
+ npx @splashcodex/api-key-manager init
47
38
  ```
39
+ This will automatically create a `.env` template and a `demo.ts` file showing the Gemini Preset in action.
48
40
 
49
- ## v4.0 Semantic Cache (Mastermind Edition)
41
+ ## Quick Start (v5 Presets)
50
42
 
51
- Automatically cache API responses by semantic similarity. Identical or near-identical prompts return cached results without consuming API quota.
43
+ The fastest way to get started in any CodeDex repository.
52
44
 
53
- ```typescript
54
- import { ApiKeyManager } from '@splashcodex/api-key-manager';
45
+ ### Gemini Preset
46
+ Reads `GOOGLE_GEMINI_API_KEY` or `GEMINI_API_KEY` from environment.
55
47
 
56
- const manager = new ApiKeyManager(['key1', 'key2'], {
57
- semanticCache: {
58
- threshold: 0.92, // 92% cosine similarity to match
59
- ttlMs: 86400000, // 24-hour TTL (default)
60
- getEmbedding: async (text) => {
61
- // Your embedding function (e.g. OpenAI, Gemini, local model)
62
- return await myEmbeddingModel.embed(text);
63
- }
64
- }
65
- });
48
+ ```typescript
49
+ import { GeminiManager } from '@splashcodex/api-key-manager/presets/gemini';
66
50
 
67
- // First call → API hit, cached
68
- const r1 = await manager.execute(apiFn, { prompt: 'What is the weather?' });
51
+ const result = GeminiManager.getInstance();
52
+ if (!result.success) throw result.error;
69
53
 
70
- // Second call → Semantic Cache HIT (no API call)
71
- const r2 = await manager.execute(apiFn, { prompt: 'How is the weather today?' });
54
+ const gemini = result.data;
55
+ const response = await gemini.execute(async (key) => {
56
+ // result.data is the underlying ApiKeyManager
57
+ return await callGemini(key, "Hello!");
58
+ });
72
59
  ```
73
60
 
74
- ### v4.1 Streaming Support
61
+ ### Centralized API Gateway (New!)
75
62
 
76
- Real-time response handling with the same resilience as `execute()`.
63
+ Run a standalone proxy server that acts as a single point of entry for all your microservices or repositories. It centralizes key management, circuit breaking, and rate limiting across applications.
77
64
 
78
- ```typescript
79
- const stream = await manager.executeStream(async (key) => {
80
- return await gemini.generateContentStream({ prompt: "..." });
81
- }, { prompt: "..." });
65
+ 1. Install global/local and export your keys:
66
+ ```bash
67
+ export GOOGLE_GEMINI_API_KEY="AIzaSy...1,AIzaSy...2"
68
+ ```
82
69
 
83
- for await (const chunk of stream) {
84
- console.log(chunk.text()); // Zero-latency interaction
85
- }
70
+ 2. Start the gateway:
71
+ ```bash
72
+ npm run gateway
73
+ # Server runs on http://localhost:9000
86
74
  ```
87
75
 
88
- - **Smart Retries**: If the initial connection fails, it rotates keys and retries.
89
- - **Cache Replay**: Semantic cache accumulates stream chunks and replays the full stream on a cache hit.
90
- - **Event Driven**: Emits `executeSuccess`, `executeFailed`, and `retry` just like the standard wrapper.
76
+ 3. Call the proxy from ANY language without managing keys in the client app:
77
+ ```bash
78
+ curl -X POST http://localhost:9000/v1/generate \
79
+ -H "Content-Type: application/json" \
80
+ -d '{"provider": "gemini", "prompt": "Hello!"}'
81
+ ```
82
+ Route supports both `/v1/generate` (Standard JSON) and `/v1/stream` (Server-Sent Events). Monitoring available at `/v1/health` and `/v1/providers`.
91
83
 
92
- > **Recursion Guard**: If your `getEmbedding` callback internally calls `execute()`,
93
- > the cache automatically skips on nested calls to prevent infinite recursion.
84
+ ---
94
85
 
95
- ## execute() Wrapper
86
+ ### Multi-Provider Vault
96
87
 
97
- Wraps the entire lifecycle into one method:
88
+ Manage ALL your provider keys across your system from one pool.Perfect for gateways or complex bots handling multiple models.
98
89
 
99
90
  ```typescript
100
- const manager = new ApiKeyManager(keys, {
101
- storage: localStorage,
102
- strategy: new WeightedStrategy(),
103
- fallbackFn: () => cachedResponse,
104
- concurrency: 10
105
- });
106
-
107
- const result = await manager.execute(
108
- async (key, signal) => {
109
- const res = await fetch(url, { headers: { 'x-api-key': key }, signal });
110
- return res.json();
111
- },
112
- { maxRetries: 3, timeoutMs: 10000 }
113
- );
114
- // Handles: key selection → cache → timeout → retry → fallback → latency tracking
115
- ```
116
-
117
- ## Event Emitter
91
+ import { MultiManager } from '@splashcodex/api-key-manager/presets/multi';
118
92
 
119
- Monitor every state change:
93
+ const vault = MultiManager.getInstance({
94
+ providers: {
95
+ gemini: { envKeys: ['GOOGLE_GEMINI_API_KEY'] },
96
+ openai: { envKeys: ['OPENAI_API_KEY'] }
97
+ }
98
+ }).data!;
120
99
 
121
- ```typescript
122
- manager.on('keyDead', (key) => alertTeam(`Key ${key} permanently dead`));
123
- manager.on('circuitOpen', (key) => metrics.increment('circuit_opens'));
124
- manager.on('keyRecovered', (key) => log(`Key ${key} recovered`));
125
- manager.on('retry', (key, attempt, delay) => log(`Retry #${attempt} in ${delay}ms`));
126
- manager.on('fallback', (reason) => log(`Fallback triggered: ${reason}`));
127
- manager.on('allKeysExhausted', () => alert('No healthy keys!'));
128
- manager.on('bulkheadRejected', () => metrics.increment('rejected'));
129
- manager.on('healthCheckPassed', (key) => log(`${key} healthy`));
130
- manager.on('healthCheckFailed', (key, err) => log(`${key} unhealthy`));
100
+ // Route by provider
101
+ const res = await vault.execute(fn, { provider: 'gemini' });
131
102
  ```
132
103
 
133
- ## Load Balancing Strategies
104
+ ---
134
105
 
135
- ### Weighted (Cost Optimization)
106
+ ## v5.0 Architecture & Persistence
136
107
 
137
- ```typescript
138
- import { ApiKeyManager, WeightedStrategy } from '@splashcodex/api-key-manager';
139
-
140
- const manager = new ApiKeyManager(
141
- [
142
- { key: 'free-key-1', weight: 1.0 },
143
- { key: 'free-key-2', weight: 1.0 },
144
- { key: 'paid-backup', weight: 0.1 },
145
- ],
146
- { strategy: new WeightedStrategy() }
147
- );
148
- ```
149
-
150
- ### Latency (Performance)
108
+ ### Built-in Persistence
109
+ State (cooling down keys, dead keys) now survives application restarts by default.
151
110
 
152
111
  ```typescript
153
- import { ApiKeyManager, LatencyStrategy } from '@splashcodex/api-key-manager';
112
+ import { FileStorage } from '@splashcodex/api-key-manager/persistence/file';
154
113
 
155
- const manager = new ApiKeyManager(keys, { strategy: new LatencyStrategy() });
156
- // After execute(), latency is tracked automatically
114
+ const manager = new ApiKeyManager(keys, {
115
+ storage: new FileStorage({
116
+ filePath: './api_state.json'
117
+ })
118
+ });
157
119
  ```
158
120
 
159
- ## Provider Tagging
160
-
161
- Route requests to specific providers:
121
+ ### Subpath Imports
122
+ To keep your bundles small, you can import only what you need:
162
123
 
163
124
  ```typescript
164
- const manager = new ApiKeyManager([
165
- { key: 'sk-openai-1', weight: 1.0, provider: 'openai' },
166
- { key: 'sk-openai-2', weight: 1.0, provider: 'openai' },
167
- { key: 'AIza-gemini', weight: 0.5, provider: 'gemini' },
168
- ]);
169
-
170
- const openaiKey = manager.getKeyByProvider('openai');
171
- const geminiKey = manager.getKeyByProvider('gemini');
125
+ import { GeminiManager } from '@splashcodex/api-key-manager/presets/gemini';
126
+ import { FileStorage } from '@splashcodex/api-key-manager/persistence/file';
172
127
  ```
173
128
 
174
- ## Health Checks
129
+ ---
130
+
131
+ ## v4.0 — Semantic Cache
175
132
 
176
- Proactively detect recovered keys:
133
+ Automatically cache API responses by semantic similarity.
177
134
 
178
135
  ```typescript
179
- manager.setHealthCheck(async (key) => {
180
- const res = await fetch(`https://api.openai.com/v1/models`, {
181
- headers: { Authorization: `Bearer ${key}` }
182
- });
183
- return res.ok;
136
+ const manager = new ApiKeyManager(['key1', 'key2'], {
137
+ semanticCache: {
138
+ threshold: 0.92,
139
+ getEmbedding: async (text) => await myModel.embed(text)
140
+ }
184
141
  });
185
142
 
186
- manager.startHealthChecks(60_000); // Check every 60 seconds
187
- // manager.stopHealthChecks(); // Stop when done
143
+ // Cache HIT if prompt is semantically similar
144
+ const r1 = await manager.execute(apiFn, { prompt: 'What is the weather?' });
145
+ const r2 = await manager.execute(apiFn, { prompt: 'How is the weather today?' });
188
146
  ```
189
147
 
190
- ## Error Handling
148
+ ### v4.1 — Streaming Support
149
+ Real-time response handling with the same resilience as `execute()`.
191
150
 
192
151
  ```typescript
193
- try {
194
- const result = await callApi(key);
195
- manager.markSuccess(key, duration);
196
- } catch (error) {
197
- const classification = manager.classifyError(error);
198
- manager.markFailed(key, classification);
199
-
200
- if (classification.retryable) {
201
- const delay = manager.calculateBackoff(attempt);
202
- await sleep(delay);
203
- }
152
+ const stream = await manager.executeStream(async (key) => {
153
+ return await gemini.generateContentStream({ prompt: "..." });
154
+ }, { prompt: "..." });
155
+
156
+ for await (const chunk of stream) {
157
+ console.log(chunk.text());
204
158
  }
205
159
  ```
206
160
 
207
- ## API Reference
161
+ ---
208
162
 
209
- ### Constructor
163
+ ## execute() Wrapper
164
+
165
+ Wraps the entire lifecycle into one method:
210
166
 
211
167
  ```typescript
212
- // Legacy (v1/v2)
213
- new ApiKeyManager(keys, storage?, strategy?)
214
-
215
- // v3+ Options
216
- new ApiKeyManager(keys, {
217
- storage?, // Pluggable storage { getItem, setItem }
218
- strategy?, // LoadBalancingStrategy instance
219
- fallbackFn?, // () => any — called when all keys exhausted
220
- concurrency?, // Max concurrent execute() calls
221
- semanticCache?, // v4: { threshold, ttlMs, getEmbedding }
222
- })
168
+ const result = await manager.execute(
169
+ async (key, signal) => {
170
+ const res = await fetch(url, { headers: { 'x-api-key': key }, signal });
171
+ return res.json();
172
+ },
173
+ { maxRetries: 3, timeoutMs: 10000 }
174
+ );
223
175
  ```
224
176
 
225
- ### Methods
177
+ ## API Reference
178
+
179
+ ### Presets
180
+
181
+ | Class | Env Vars | Description |
182
+ |-------|----------|-------------|
183
+ | `GeminiManager` | `GOOGLE_GEMINI_API_KEY`, `GEMINI_API_KEY` | Gemini-optimized singleton |
184
+ | `OpenAIManager` | `OPENAI_API_KEY` | OpenAI-optimized singleton |
185
+ | `MultiManager` | Custom | Vault for multiple provider pools |
186
+
187
+ ### Persistence
188
+
189
+ | Class | Description |
190
+ |-------|-------------|
191
+ | `FileStorage` | Persists to a JSON file (recommended for servers) |
192
+ | `MemoryStorage` | In-memory only (best for serverless/short-lived) |
193
+
194
+ ### Core Methods
226
195
 
227
196
  | Method | Description |
228
197
  |--------|-------------|
229
- | `getKey()` | Returns best available key via strategy |
230
- | `getKeyByProvider(provider)` | Get key filtered by provider tag |
231
- | `markSuccess(key, durationMs?)` | Report success + optional latency |
232
- | `markFailed(key, classification)` | Report failure with error type |
233
- | `classifyError(error, finishReason?)` | Classify an error automatically |
234
- | `execute(fn, options?)` | Full lifecycle wrapper with retry/timeout |
235
- | `executeStream(fn, options?)` | Streaming lifecycle wrapper |
236
- | `calculateBackoff(attempt)` | Get backoff delay with jitter |
237
- | `getStats()` | Get pool health statistics |
238
- | `getKeyCount()` | Count of non-DEAD keys |
239
- | `setHealthCheck(fn)` | Set health check function |
240
- | `startHealthChecks(ms)` | Start periodic health checks |
241
- | `stopHealthChecks()` | Stop health checks |
242
-
243
- ### Events
244
-
245
- | Event | Payload | Trigger |
246
- |-------|---------|---------|
247
- | `keyDead` | `key: string` | Key marked as permanently dead |
248
- | `circuitOpen` | `key: string` | Key circuit opened (cooldown) |
249
- | `circuitHalfOpen` | `key: string` | Key entering test phase |
250
- | `keyRecovered` | `key: string` | Key recovered from failure |
251
- | `fallback` | `reason: string` | Fallback function invoked |
252
- | `allKeysExhausted` | — | All keys dead, no fallback |
253
- | `retry` | `key, attempt, delayMs` | Retry attempt starting |
254
- | `executeSuccess` | `key, durationMs` | execute() completed successfully |
255
- | `executeFailed` | `key, error` | execute() attempt failed |
256
- | `bulkheadRejected` | — | Concurrency limit reached |
257
- | `healthCheckPassed` | `key: string` | Health check succeeded |
258
- | `healthCheckFailed` | `key, error` | Health check failed |
259
-
260
- ### Custom Errors
261
-
262
- | Error | When |
263
- |-------|------|
264
- | `TimeoutError` | Request exceeded `timeoutMs` |
265
- | `BulkheadRejectionError` | Concurrency limit exceeded |
266
- | `AllKeysExhaustedError` | All keys dead, no fallback |
267
-
268
- ### Strategies
269
-
270
- | Strategy | Algorithm | Best For |
271
- |----------|-----------|----------|
272
- | `StandardStrategy` | Least Failures → LRU | General use |
273
- | `WeightedStrategy` | Probabilistic by weight | Cost optimization |
274
- | `LatencyStrategy` | Lowest avg latency | Performance |
198
+ | `execute(fn, opts)` | Standard wrapper |
199
+ | `executeStream(fn, opts)` | Streaming wrapper |
200
+ | `getStats()` | Get pool health |
201
+ | `getKey()` | Manual key rotation |
202
+ | `markFailed(key, err)` | Manual failure reporting |
275
203
 
276
204
  ## License
277
205
 
package/bin/cli.js ADDED
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ const args = process.argv.slice(2);
6
+ const command = args[0];
7
+
8
+ function printDocs() {
9
+ console.log(`\x1b[92m🌟 @splashcodex/api-key-manager v5.0\x1b[0m`);
10
+ console.log(`\x1b[96m📚 Documentation: https://www.npmjs.com/package/@splashcodex/ApiKeyManager\x1b[0m\n`);
11
+ console.log(`\x1b[93m🚀 Commands:\x1b[0m`);
12
+ console.log(` npx @splashcodex/api-key-manager init \x1b[90m# Scaffold a demo project in the current directory\x1b[0m`);
13
+ console.log(`\n\x1b[95m💡 Tip: Need help? Run init to see it in action!\x1b[0m\n`);
14
+ }
15
+
16
+ function initProject() {
17
+ const cwd = process.cwd();
18
+ const envPath = path.join(cwd, '.env');
19
+ const demoPath = path.join(cwd, 'demo.ts');
20
+
21
+ console.log(`\n\x1b[96m🚀 Initializing @splashcodex/api-key-manager environment...\x1b[0m\n`);
22
+
23
+ // Create .env
24
+ if (!fs.existsSync(envPath)) {
25
+ fs.writeFileSync(envPath, `GOOGLE_GEMINI_API_KEY="your_api_key_1,your_api_key_2"\nOPENAI_API_KEY="sk-..."\n`);
26
+ console.log(`\x1b[92m[✔] Created .env file\x1b[0m`);
27
+ } else {
28
+ console.log(`\x1b[90m[-] .env already exists. Remember to add GOOGLE_GEMINI_API_KEY!\x1b[0m`);
29
+ }
30
+
31
+ // Create demo.ts
32
+ if (!fs.existsSync(demoPath)) {
33
+ const tsCode = `import 'dotenv/config';\nimport { GeminiManager } from '@splashcodex/api-key-manager/presets/gemini';\n\nasync function main() {\n console.log("Initialize GeminiManager...");\n // Automatically parse GOOGLE_GEMINI_API_KEY from env\n const result = GeminiManager.getInstance();\n if (!result.success) {\n console.error("Failed to initialize:", result.error);\n return;\n }\n \n const gemini = result.data;\n \n console.log("Executing resilient request...");\n // Automatically handles key rotation and 429 quota limits\n try {\n const response = await gemini.execute(async (key) => {\n console.log(\`[Network] Sending request with key: \${key.substring(0, 8)}...\`);\n // Replace with actual fetch: await fetch(..., { headers: { 'x-goog-api-key': key } })\n return "Success: Simulated API Response!";\n });\n console.log("Result:", response);\n } catch (e) {\n console.error("All keys exhausted or network failed.", e.message);\n }\n}\n\nmain();\n`;
34
+ fs.writeFileSync(demoPath, tsCode);
35
+ console.log(`\x1b[92m[✔] Created demo.ts\x1b[0m`);
36
+ } else {
37
+ console.log(`\x1b[90m[-] demo.ts already exists.\x1b[0m`);
38
+ }
39
+
40
+ console.log(`\n\x1b[93m🎉 Setup Complete!\x1b[0m`);
41
+ console.log(`To run the demo:`);
42
+ console.log(`\x1b[36m npm install dotenv\x1b[0m`);
43
+ console.log(`\x1b[36m npx ts-node demo.ts\x1b[0m\n`);
44
+ }
45
+
46
+ if (command === 'init') {
47
+ initProject();
48
+ } else {
49
+ printDocs();
50
+ }
package/dist/index.d.ts CHANGED
@@ -1,11 +1,16 @@
1
1
  /**
2
- * Universal ApiKeyManager v4.0 — Mastermind Edition
2
+ * Universal ApiKeyManager v5.0 — Ecosystem Edition
3
3
  * Implements: Rotation, Circuit Breaker, Persistence, Exponential Backoff, Strategies,
4
4
  * Event Emitter, Fallback, execute(), Timeout, Auto-Retry, Provider Tags,
5
5
  * Health Checks, Bulkhead/Concurrency
6
+ * NEW in v5.0: Provider Presets (GeminiManager, OpenAIManager, MultiManager),
7
+ * Built-in Persistence (FileStorage, MemoryStorage)
6
8
  * Gemini-Specific: finishReason handling, Safety blocks, RECITATION detection
7
9
  */
8
10
  import { EventEmitter } from 'events';
11
+ export { FileStorage } from './persistence/file';
12
+ export type { FileStorageOptions } from './persistence/file';
13
+ export { MemoryStorage } from './persistence/memory';
9
14
  export interface KeyState {
10
15
  key: string;
11
16
  failCount: number;
@@ -103,7 +108,7 @@ export declare class WeightedStrategy implements LoadBalancingStrategy {
103
108
  next(candidates: KeyState[]): KeyState | null;
104
109
  }
105
110
  /**
106
- * Latency Strategy: Pick lowest average latency
111
+ * Latency Strategy: Pick lowest average latency with LRU tie-break
107
112
  */
108
113
  export declare class LatencyStrategy implements LoadBalancingStrategy {
109
114
  next(candidates: KeyState[]): KeyState | null;
package/dist/index.js CHANGED
@@ -1,14 +1,22 @@
1
1
  "use strict";
2
2
  /**
3
- * Universal ApiKeyManager v4.0 — Mastermind Edition
3
+ * Universal ApiKeyManager v5.0 — Ecosystem Edition
4
4
  * Implements: Rotation, Circuit Breaker, Persistence, Exponential Backoff, Strategies,
5
5
  * Event Emitter, Fallback, execute(), Timeout, Auto-Retry, Provider Tags,
6
6
  * Health Checks, Bulkhead/Concurrency
7
+ * NEW in v5.0: Provider Presets (GeminiManager, OpenAIManager, MultiManager),
8
+ * Built-in Persistence (FileStorage, MemoryStorage)
7
9
  * Gemini-Specific: finishReason handling, Safety blocks, RECITATION detection
8
10
  */
9
11
  Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.ApiKeyManager = exports.SemanticCache = exports.LatencyStrategy = exports.WeightedStrategy = exports.StandardStrategy = exports.AllKeysExhaustedError = exports.BulkheadRejectionError = exports.TimeoutError = void 0;
12
+ exports.ApiKeyManager = exports.SemanticCache = exports.LatencyStrategy = exports.WeightedStrategy = exports.StandardStrategy = exports.AllKeysExhaustedError = exports.BulkheadRejectionError = exports.TimeoutError = exports.MemoryStorage = exports.FileStorage = void 0;
11
13
  const events_1 = require("events");
14
+ // ─── Re-exports: Persistence ─────────────────────────────────────────────────
15
+ // Persistence adapters can be imported from root or via subpath
16
+ var file_1 = require("./persistence/file");
17
+ Object.defineProperty(exports, "FileStorage", { enumerable: true, get: function () { return file_1.FileStorage; } });
18
+ var memory_1 = require("./persistence/memory");
19
+ Object.defineProperty(exports, "MemoryStorage", { enumerable: true, get: function () { return memory_1.MemoryStorage; } });
12
20
  // ─── Config ──────────────────────────────────────────────────────────────────
13
21
  const CONFIG = {
14
22
  MAX_CONSECUTIVE_FAILURES: 5,
@@ -83,13 +91,17 @@ class WeightedStrategy {
83
91
  }
84
92
  exports.WeightedStrategy = WeightedStrategy;
85
93
  /**
86
- * Latency Strategy: Pick lowest average latency
94
+ * Latency Strategy: Pick lowest average latency with LRU tie-break
87
95
  */
88
96
  class LatencyStrategy {
89
97
  next(candidates) {
90
98
  if (candidates.length === 0)
91
99
  return null;
92
- candidates.sort((a, b) => a.averageLatency - b.averageLatency);
100
+ candidates.sort((a, b) => {
101
+ if (a.averageLatency !== b.averageLatency)
102
+ return a.averageLatency - b.averageLatency;
103
+ return a.lastUsed - b.lastUsed; // LRU tie-break
104
+ });
93
105
  return candidates[0];
94
106
  }
95
107
  }