moflo 4.8.19 → 4.8.20
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/.claude/guidance/shipped/moflo.md +45 -0
- package/.claude/helpers/statusline.cjs +1 -1
- package/.claude/workflow-state.json +9 -0
- package/package.json +2 -2
- package/src/@claude-flow/cli/dist/src/init/statusline-generator.js +1 -1
- package/src/@claude-flow/cli/dist/src/services/agentic-flow-bridge.js +5 -3
- package/src/@claude-flow/cli/package.json +1 -1
- package/src/@claude-flow/memory/dist/agent-memory-scope.d.ts +131 -0
- package/src/@claude-flow/memory/dist/agent-memory-scope.js +223 -0
- package/src/@claude-flow/memory/dist/agent-memory-scope.test.d.ts +8 -0
- package/src/@claude-flow/memory/dist/agent-memory-scope.test.js +466 -0
- package/src/@claude-flow/memory/dist/agentdb-adapter.d.ts +165 -0
- package/src/@claude-flow/memory/dist/agentdb-adapter.js +806 -0
- package/src/@claude-flow/memory/dist/agentdb-backend.d.ts +212 -0
- package/src/@claude-flow/memory/dist/agentdb-backend.js +842 -0
- package/src/@claude-flow/memory/dist/agentdb-backend.test.d.ts +7 -0
- package/src/@claude-flow/memory/dist/agentdb-backend.test.js +258 -0
- package/src/@claude-flow/memory/dist/application/commands/delete-memory.command.d.ts +65 -0
- package/src/@claude-flow/memory/dist/application/commands/delete-memory.command.js +129 -0
- package/src/@claude-flow/memory/dist/application/commands/store-memory.command.d.ts +48 -0
- package/src/@claude-flow/memory/dist/application/commands/store-memory.command.js +72 -0
- package/src/@claude-flow/memory/dist/application/index.d.ts +12 -0
- package/src/@claude-flow/memory/dist/application/index.js +15 -0
- package/src/@claude-flow/memory/dist/application/queries/search-memory.query.d.ts +72 -0
- package/src/@claude-flow/memory/dist/application/queries/search-memory.query.js +143 -0
- package/src/@claude-flow/memory/dist/application/services/memory-application-service.d.ts +121 -0
- package/src/@claude-flow/memory/dist/application/services/memory-application-service.js +190 -0
- package/src/@claude-flow/memory/dist/auto-memory-bridge.d.ts +226 -0
- package/src/@claude-flow/memory/dist/auto-memory-bridge.js +709 -0
- package/src/@claude-flow/memory/dist/auto-memory-bridge.test.d.ts +8 -0
- package/src/@claude-flow/memory/dist/auto-memory-bridge.test.js +757 -0
- package/src/@claude-flow/memory/dist/benchmark.test.d.ts +2 -0
- package/src/@claude-flow/memory/dist/benchmark.test.js +277 -0
- package/src/@claude-flow/memory/dist/cache-manager.d.ts +134 -0
- package/src/@claude-flow/memory/dist/cache-manager.js +407 -0
- package/src/@claude-flow/memory/dist/controller-registry.d.ts +216 -0
- package/src/@claude-flow/memory/dist/controller-registry.js +893 -0
- package/src/@claude-flow/memory/dist/controller-registry.test.d.ts +14 -0
- package/src/@claude-flow/memory/dist/controller-registry.test.js +593 -0
- package/src/@claude-flow/memory/dist/database-provider.d.ts +87 -0
- package/src/@claude-flow/memory/dist/database-provider.js +372 -0
- package/src/@claude-flow/memory/dist/database-provider.test.d.ts +7 -0
- package/src/@claude-flow/memory/dist/database-provider.test.js +287 -0
- package/src/@claude-flow/memory/dist/domain/entities/memory-entry.d.ts +143 -0
- package/src/@claude-flow/memory/dist/domain/entities/memory-entry.js +226 -0
- package/src/@claude-flow/memory/dist/domain/index.d.ts +11 -0
- package/src/@claude-flow/memory/dist/domain/index.js +12 -0
- package/src/@claude-flow/memory/dist/domain/repositories/memory-repository.interface.d.ts +102 -0
- package/src/@claude-flow/memory/dist/domain/repositories/memory-repository.interface.js +11 -0
- package/src/@claude-flow/memory/dist/domain/services/memory-domain-service.d.ts +105 -0
- package/src/@claude-flow/memory/dist/domain/services/memory-domain-service.js +297 -0
- package/src/@claude-flow/memory/dist/hnsw-index.d.ts +111 -0
- package/src/@claude-flow/memory/dist/hnsw-index.js +781 -0
- package/src/@claude-flow/memory/dist/hnsw-lite.d.ts +23 -0
- package/src/@claude-flow/memory/dist/hnsw-lite.js +168 -0
- package/src/@claude-flow/memory/dist/index.d.ts +204 -0
- package/src/@claude-flow/memory/dist/index.js +358 -0
- package/src/@claude-flow/memory/dist/infrastructure/index.d.ts +17 -0
- package/src/@claude-flow/memory/dist/infrastructure/index.js +16 -0
- package/src/@claude-flow/memory/dist/infrastructure/repositories/hybrid-memory-repository.d.ts +66 -0
- package/src/@claude-flow/memory/dist/infrastructure/repositories/hybrid-memory-repository.js +409 -0
- package/src/@claude-flow/memory/dist/learning-bridge.d.ts +137 -0
- package/src/@claude-flow/memory/dist/learning-bridge.js +335 -0
- package/src/@claude-flow/memory/dist/learning-bridge.test.d.ts +8 -0
- package/src/@claude-flow/memory/dist/learning-bridge.test.js +578 -0
- package/src/@claude-flow/memory/dist/memory-graph.d.ts +100 -0
- package/src/@claude-flow/memory/dist/memory-graph.js +333 -0
- package/src/@claude-flow/memory/dist/memory-graph.test.d.ts +8 -0
- package/src/@claude-flow/memory/dist/memory-graph.test.js +609 -0
- package/src/@claude-flow/memory/dist/migration.d.ts +68 -0
- package/src/@claude-flow/memory/dist/migration.js +513 -0
- package/src/@claude-flow/memory/dist/persistent-sona.d.ts +144 -0
- package/src/@claude-flow/memory/dist/persistent-sona.js +332 -0
- package/src/@claude-flow/memory/dist/query-builder.d.ts +211 -0
- package/src/@claude-flow/memory/dist/query-builder.js +438 -0
- package/src/@claude-flow/memory/dist/rvf-backend.d.ts +51 -0
- package/src/@claude-flow/memory/dist/rvf-backend.js +481 -0
- package/src/@claude-flow/memory/dist/rvf-learning-store.d.ts +139 -0
- package/src/@claude-flow/memory/dist/rvf-learning-store.js +295 -0
- package/src/@claude-flow/memory/dist/rvf-migration.d.ts +45 -0
- package/src/@claude-flow/memory/dist/rvf-migration.js +234 -0
- package/src/@claude-flow/memory/dist/sqljs-backend.d.ts +127 -0
- package/src/@claude-flow/memory/dist/sqljs-backend.js +600 -0
- package/src/@claude-flow/memory/dist/types.d.ts +484 -0
- package/src/@claude-flow/memory/dist/types.js +58 -0
- package/src/@claude-flow/shared/dist/core/config/defaults.d.ts +41 -0
- package/src/@claude-flow/shared/dist/core/config/defaults.js +186 -0
- package/src/@claude-flow/shared/dist/core/config/index.d.ts +8 -0
- package/src/@claude-flow/shared/dist/core/config/index.js +12 -0
- package/src/@claude-flow/shared/dist/core/config/loader.d.ts +45 -0
- package/src/@claude-flow/shared/dist/core/config/loader.js +222 -0
- package/src/@claude-flow/shared/dist/core/config/schema.d.ts +1134 -0
- package/src/@claude-flow/shared/dist/core/config/schema.js +158 -0
- package/src/@claude-flow/shared/dist/core/config/validator.d.ts +92 -0
- package/src/@claude-flow/shared/dist/core/config/validator.js +147 -0
- package/src/@claude-flow/shared/dist/core/event-bus.d.ts +31 -0
- package/src/@claude-flow/shared/dist/core/event-bus.js +197 -0
- package/src/@claude-flow/shared/dist/core/index.d.ts +15 -0
- package/src/@claude-flow/shared/dist/core/index.js +19 -0
- package/src/@claude-flow/shared/dist/core/interfaces/agent.interface.d.ts +200 -0
- package/src/@claude-flow/shared/dist/core/interfaces/agent.interface.js +6 -0
- package/src/@claude-flow/shared/dist/core/interfaces/coordinator.interface.d.ts +310 -0
- package/src/@claude-flow/shared/dist/core/interfaces/coordinator.interface.js +7 -0
- package/src/@claude-flow/shared/dist/core/interfaces/event.interface.d.ts +224 -0
- package/src/@claude-flow/shared/dist/core/interfaces/event.interface.js +46 -0
- package/src/@claude-flow/shared/dist/core/interfaces/index.d.ts +10 -0
- package/src/@claude-flow/shared/dist/core/interfaces/index.js +15 -0
- package/src/@claude-flow/shared/dist/core/interfaces/memory.interface.d.ts +298 -0
- package/src/@claude-flow/shared/dist/core/interfaces/memory.interface.js +7 -0
- package/src/@claude-flow/shared/dist/core/interfaces/task.interface.d.ts +185 -0
- package/src/@claude-flow/shared/dist/core/interfaces/task.interface.js +6 -0
- package/src/@claude-flow/shared/dist/core/orchestrator/event-coordinator.d.ts +35 -0
- package/src/@claude-flow/shared/dist/core/orchestrator/event-coordinator.js +101 -0
- package/src/@claude-flow/shared/dist/core/orchestrator/health-monitor.d.ts +60 -0
- package/src/@claude-flow/shared/dist/core/orchestrator/health-monitor.js +166 -0
- package/src/@claude-flow/shared/dist/core/orchestrator/index.d.ts +46 -0
- package/src/@claude-flow/shared/dist/core/orchestrator/index.js +64 -0
- package/src/@claude-flow/shared/dist/core/orchestrator/lifecycle-manager.d.ts +56 -0
- package/src/@claude-flow/shared/dist/core/orchestrator/lifecycle-manager.js +195 -0
- package/src/@claude-flow/shared/dist/core/orchestrator/session-manager.d.ts +83 -0
- package/src/@claude-flow/shared/dist/core/orchestrator/session-manager.js +193 -0
- package/src/@claude-flow/shared/dist/core/orchestrator/task-manager.d.ts +49 -0
- package/src/@claude-flow/shared/dist/core/orchestrator/task-manager.js +253 -0
- package/src/@claude-flow/shared/dist/events/domain-events.d.ts +282 -0
- package/src/@claude-flow/shared/dist/events/domain-events.js +165 -0
- package/src/@claude-flow/shared/dist/events/event-store.d.ts +126 -0
- package/src/@claude-flow/shared/dist/events/event-store.js +432 -0
- package/src/@claude-flow/shared/dist/events/event-store.test.d.ts +8 -0
- package/src/@claude-flow/shared/dist/events/event-store.test.js +297 -0
- package/src/@claude-flow/shared/dist/events/example-usage.d.ts +10 -0
- package/src/@claude-flow/shared/dist/events/example-usage.js +193 -0
- package/src/@claude-flow/shared/dist/events/index.d.ts +21 -0
- package/src/@claude-flow/shared/dist/events/index.js +22 -0
- package/src/@claude-flow/shared/dist/events/projections.d.ts +177 -0
- package/src/@claude-flow/shared/dist/events/projections.js +421 -0
- package/src/@claude-flow/shared/dist/events/rvf-event-log.d.ts +82 -0
- package/src/@claude-flow/shared/dist/events/rvf-event-log.js +340 -0
- package/src/@claude-flow/shared/dist/events/state-reconstructor.d.ts +101 -0
- package/src/@claude-flow/shared/dist/events/state-reconstructor.js +263 -0
- package/src/@claude-flow/shared/dist/events.d.ts +80 -0
- package/src/@claude-flow/shared/dist/events.js +249 -0
- package/src/@claude-flow/shared/dist/hooks/example-usage.d.ts +42 -0
- package/src/@claude-flow/shared/dist/hooks/example-usage.js +351 -0
- package/src/@claude-flow/shared/dist/hooks/executor.d.ts +100 -0
- package/src/@claude-flow/shared/dist/hooks/executor.js +267 -0
- package/src/@claude-flow/shared/dist/hooks/hooks.test.d.ts +9 -0
- package/src/@claude-flow/shared/dist/hooks/hooks.test.js +322 -0
- package/src/@claude-flow/shared/dist/hooks/index.d.ts +52 -0
- package/src/@claude-flow/shared/dist/hooks/index.js +51 -0
- package/src/@claude-flow/shared/dist/hooks/registry.d.ts +133 -0
- package/src/@claude-flow/shared/dist/hooks/registry.js +277 -0
- package/src/@claude-flow/shared/dist/hooks/safety/bash-safety.d.ts +105 -0
- package/src/@claude-flow/shared/dist/hooks/safety/bash-safety.js +481 -0
- package/src/@claude-flow/shared/dist/hooks/safety/file-organization.d.ts +144 -0
- package/src/@claude-flow/shared/dist/hooks/safety/file-organization.js +328 -0
- package/src/@claude-flow/shared/dist/hooks/safety/git-commit.d.ts +158 -0
- package/src/@claude-flow/shared/dist/hooks/safety/git-commit.js +450 -0
- package/src/@claude-flow/shared/dist/hooks/safety/index.d.ts +17 -0
- package/src/@claude-flow/shared/dist/hooks/safety/index.js +17 -0
- package/src/@claude-flow/shared/dist/hooks/session-hooks.d.ts +234 -0
- package/src/@claude-flow/shared/dist/hooks/session-hooks.js +334 -0
- package/src/@claude-flow/shared/dist/hooks/task-hooks.d.ts +163 -0
- package/src/@claude-flow/shared/dist/hooks/task-hooks.js +326 -0
- package/src/@claude-flow/shared/dist/hooks/types.d.ts +267 -0
- package/src/@claude-flow/shared/dist/hooks/types.js +62 -0
- package/src/@claude-flow/shared/dist/hooks/verify-exports.test.d.ts +9 -0
- package/src/@claude-flow/shared/dist/hooks/verify-exports.test.js +93 -0
- package/src/@claude-flow/shared/dist/index.d.ts +20 -0
- package/src/@claude-flow/shared/dist/index.js +50 -0
- package/src/@claude-flow/shared/dist/mcp/connection-pool.d.ts +98 -0
- package/src/@claude-flow/shared/dist/mcp/connection-pool.js +364 -0
- package/src/@claude-flow/shared/dist/mcp/index.d.ts +69 -0
- package/src/@claude-flow/shared/dist/mcp/index.js +84 -0
- package/src/@claude-flow/shared/dist/mcp/server.d.ts +166 -0
- package/src/@claude-flow/shared/dist/mcp/server.js +593 -0
- package/src/@claude-flow/shared/dist/mcp/session-manager.d.ts +136 -0
- package/src/@claude-flow/shared/dist/mcp/session-manager.js +335 -0
- package/src/@claude-flow/shared/dist/mcp/tool-registry.d.ts +178 -0
- package/src/@claude-flow/shared/dist/mcp/tool-registry.js +439 -0
- package/src/@claude-flow/shared/dist/mcp/transport/http.d.ts +104 -0
- package/src/@claude-flow/shared/dist/mcp/transport/http.js +476 -0
- package/src/@claude-flow/shared/dist/mcp/transport/index.d.ts +102 -0
- package/src/@claude-flow/shared/dist/mcp/transport/index.js +238 -0
- package/src/@claude-flow/shared/dist/mcp/transport/stdio.d.ts +104 -0
- package/src/@claude-flow/shared/dist/mcp/transport/stdio.js +263 -0
- package/src/@claude-flow/shared/dist/mcp/transport/websocket.d.ts +133 -0
- package/src/@claude-flow/shared/dist/mcp/transport/websocket.js +396 -0
- package/src/@claude-flow/shared/dist/mcp/types.d.ts +438 -0
- package/src/@claude-flow/shared/dist/mcp/types.js +54 -0
- package/src/@claude-flow/shared/dist/plugin-interface.d.ts +544 -0
- package/src/@claude-flow/shared/dist/plugin-interface.js +23 -0
- package/src/@claude-flow/shared/dist/plugin-loader.d.ts +139 -0
- package/src/@claude-flow/shared/dist/plugin-loader.js +434 -0
- package/src/@claude-flow/shared/dist/plugin-registry.d.ts +183 -0
- package/src/@claude-flow/shared/dist/plugin-registry.js +457 -0
- package/src/@claude-flow/shared/dist/plugins/index.d.ts +10 -0
- package/src/@claude-flow/shared/dist/plugins/index.js +10 -0
- package/src/@claude-flow/shared/dist/plugins/official/hive-mind-plugin.d.ts +106 -0
- package/src/@claude-flow/shared/dist/plugins/official/hive-mind-plugin.js +241 -0
- package/src/@claude-flow/shared/dist/plugins/official/index.d.ts +10 -0
- package/src/@claude-flow/shared/dist/plugins/official/index.js +10 -0
- package/src/@claude-flow/shared/dist/plugins/official/maestro-plugin.d.ts +121 -0
- package/src/@claude-flow/shared/dist/plugins/official/maestro-plugin.js +355 -0
- package/src/@claude-flow/shared/dist/plugins/types.d.ts +93 -0
- package/src/@claude-flow/shared/dist/plugins/types.js +9 -0
- package/src/@claude-flow/shared/dist/resilience/bulkhead.d.ts +105 -0
- package/src/@claude-flow/shared/dist/resilience/bulkhead.js +206 -0
- package/src/@claude-flow/shared/dist/resilience/circuit-breaker.d.ts +132 -0
- package/src/@claude-flow/shared/dist/resilience/circuit-breaker.js +233 -0
- package/src/@claude-flow/shared/dist/resilience/index.d.ts +19 -0
- package/src/@claude-flow/shared/dist/resilience/index.js +19 -0
- package/src/@claude-flow/shared/dist/resilience/rate-limiter.d.ts +168 -0
- package/src/@claude-flow/shared/dist/resilience/rate-limiter.js +314 -0
- package/src/@claude-flow/shared/dist/resilience/retry.d.ts +91 -0
- package/src/@claude-flow/shared/dist/resilience/retry.js +159 -0
- package/src/@claude-flow/shared/dist/security/index.d.ts +10 -0
- package/src/@claude-flow/shared/dist/security/index.js +12 -0
- package/src/@claude-flow/shared/dist/security/input-validation.d.ts +73 -0
- package/src/@claude-flow/shared/dist/security/input-validation.js +201 -0
- package/src/@claude-flow/shared/dist/security/secure-random.d.ts +92 -0
- package/src/@claude-flow/shared/dist/security/secure-random.js +142 -0
- package/src/@claude-flow/shared/dist/services/index.d.ts +7 -0
- package/src/@claude-flow/shared/dist/services/index.js +7 -0
- package/src/@claude-flow/shared/dist/services/v3-progress.service.d.ts +124 -0
- package/src/@claude-flow/shared/dist/services/v3-progress.service.js +402 -0
- package/src/@claude-flow/shared/dist/types/agent.types.d.ts +137 -0
- package/src/@claude-flow/shared/dist/types/agent.types.js +6 -0
- package/src/@claude-flow/shared/dist/types/index.d.ts +11 -0
- package/src/@claude-flow/shared/dist/types/index.js +17 -0
- package/src/@claude-flow/shared/dist/types/mcp.types.d.ts +266 -0
- package/src/@claude-flow/shared/dist/types/mcp.types.js +7 -0
- package/src/@claude-flow/shared/dist/types/memory.types.d.ts +236 -0
- package/src/@claude-flow/shared/dist/types/memory.types.js +7 -0
- package/src/@claude-flow/shared/dist/types/swarm.types.d.ts +186 -0
- package/src/@claude-flow/shared/dist/types/swarm.types.js +65 -0
- package/src/@claude-flow/shared/dist/types/task.types.d.ts +178 -0
- package/src/@claude-flow/shared/dist/types/task.types.js +32 -0
- package/src/@claude-flow/shared/dist/types.d.ts +197 -0
- package/src/@claude-flow/shared/dist/types.js +21 -0
- package/src/@claude-flow/shared/dist/utils/secure-logger.d.ts +69 -0
- package/src/@claude-flow/shared/dist/utils/secure-logger.js +208 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rate Limiter
|
|
3
|
+
*
|
|
4
|
+
* Production-ready rate limiting implementations.
|
|
5
|
+
*
|
|
6
|
+
* @module v3/shared/resilience/rate-limiter
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Rate limiter options
|
|
10
|
+
*/
|
|
11
|
+
export interface RateLimiterOptions {
|
|
12
|
+
/** Maximum requests allowed in the window */
|
|
13
|
+
maxRequests: number;
|
|
14
|
+
/** Time window in milliseconds */
|
|
15
|
+
windowMs: number;
|
|
16
|
+
/** Enable sliding window (vs fixed window) */
|
|
17
|
+
slidingWindow?: boolean;
|
|
18
|
+
/** Key generator for per-key limiting */
|
|
19
|
+
keyGenerator?: (context: unknown) => string;
|
|
20
|
+
/** Skip limiter for certain requests */
|
|
21
|
+
skip?: (context: unknown) => boolean;
|
|
22
|
+
/** Handler when rate limit is exceeded */
|
|
23
|
+
onRateLimited?: (key: string, remaining: number, resetAt: Date) => void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Rate limit result
|
|
27
|
+
*/
|
|
28
|
+
export interface RateLimitResult {
|
|
29
|
+
allowed: boolean;
|
|
30
|
+
remaining: number;
|
|
31
|
+
resetAt: Date;
|
|
32
|
+
retryAfter: number;
|
|
33
|
+
total: number;
|
|
34
|
+
used: number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Base Rate Limiter interface
|
|
38
|
+
*/
|
|
39
|
+
export interface RateLimiter {
|
|
40
|
+
/** Check if request is allowed */
|
|
41
|
+
check(key?: string): RateLimitResult;
|
|
42
|
+
/** Consume a request token */
|
|
43
|
+
consume(key?: string): RateLimitResult;
|
|
44
|
+
/** Reset a specific key or all keys */
|
|
45
|
+
reset(key?: string): void;
|
|
46
|
+
/** Get current status */
|
|
47
|
+
status(key?: string): RateLimitResult;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Sliding Window Rate Limiter
|
|
51
|
+
*
|
|
52
|
+
* Uses sliding window algorithm for smooth rate limiting.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* const limiter = new SlidingWindowRateLimiter({
|
|
56
|
+
* maxRequests: 100,
|
|
57
|
+
* windowMs: 60000, // 100 requests per minute
|
|
58
|
+
* });
|
|
59
|
+
*
|
|
60
|
+
* const result = limiter.consume('user-123');
|
|
61
|
+
* if (!result.allowed) {
|
|
62
|
+
* throw new Error(`Rate limited. Retry in ${result.retryAfter}ms`);
|
|
63
|
+
* }
|
|
64
|
+
*/
|
|
65
|
+
export declare class SlidingWindowRateLimiter implements RateLimiter {
|
|
66
|
+
private readonly options;
|
|
67
|
+
private readonly requests;
|
|
68
|
+
private cleanupInterval?;
|
|
69
|
+
constructor(options: RateLimiterOptions);
|
|
70
|
+
/**
|
|
71
|
+
* Check if a request would be allowed without consuming
|
|
72
|
+
*/
|
|
73
|
+
check(key?: string): RateLimitResult;
|
|
74
|
+
/**
|
|
75
|
+
* Consume a request token
|
|
76
|
+
*/
|
|
77
|
+
consume(key?: string): RateLimitResult;
|
|
78
|
+
/**
|
|
79
|
+
* Reset rate limit for a key
|
|
80
|
+
*/
|
|
81
|
+
reset(key?: string): void;
|
|
82
|
+
/**
|
|
83
|
+
* Get current status
|
|
84
|
+
*/
|
|
85
|
+
status(key?: string): RateLimitResult;
|
|
86
|
+
/**
|
|
87
|
+
* Cleanup resources
|
|
88
|
+
*/
|
|
89
|
+
destroy(): void;
|
|
90
|
+
/**
|
|
91
|
+
* Clean old entries for a specific key
|
|
92
|
+
*/
|
|
93
|
+
private cleanupKey;
|
|
94
|
+
/**
|
|
95
|
+
* Clean all old entries
|
|
96
|
+
*/
|
|
97
|
+
private cleanup;
|
|
98
|
+
/**
|
|
99
|
+
* Get reset time based on oldest entry
|
|
100
|
+
*/
|
|
101
|
+
private getResetTime;
|
|
102
|
+
/**
|
|
103
|
+
* Get retry after time in ms
|
|
104
|
+
*/
|
|
105
|
+
private getRetryAfter;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Token Bucket Rate Limiter
|
|
109
|
+
*
|
|
110
|
+
* Uses token bucket algorithm for burst-friendly rate limiting.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* const limiter = new TokenBucketRateLimiter({
|
|
114
|
+
* maxRequests: 10, // bucket size
|
|
115
|
+
* windowMs: 1000, // refill interval
|
|
116
|
+
* });
|
|
117
|
+
*/
|
|
118
|
+
export declare class TokenBucketRateLimiter implements RateLimiter {
|
|
119
|
+
private readonly options;
|
|
120
|
+
private readonly buckets;
|
|
121
|
+
private cleanupInterval?;
|
|
122
|
+
constructor(options: RateLimiterOptions);
|
|
123
|
+
/**
|
|
124
|
+
* Check if a request would be allowed
|
|
125
|
+
*/
|
|
126
|
+
check(key?: string): RateLimitResult;
|
|
127
|
+
/**
|
|
128
|
+
* Consume a token
|
|
129
|
+
*/
|
|
130
|
+
consume(key?: string): RateLimitResult;
|
|
131
|
+
/**
|
|
132
|
+
* Reset bucket for a key
|
|
133
|
+
*/
|
|
134
|
+
reset(key?: string): void;
|
|
135
|
+
/**
|
|
136
|
+
* Get current status
|
|
137
|
+
*/
|
|
138
|
+
status(key?: string): RateLimitResult;
|
|
139
|
+
/**
|
|
140
|
+
* Cleanup resources
|
|
141
|
+
*/
|
|
142
|
+
destroy(): void;
|
|
143
|
+
/**
|
|
144
|
+
* Get or create bucket for key
|
|
145
|
+
*/
|
|
146
|
+
private getBucket;
|
|
147
|
+
/**
|
|
148
|
+
* Refill tokens based on elapsed time
|
|
149
|
+
*/
|
|
150
|
+
private refill;
|
|
151
|
+
/**
|
|
152
|
+
* Clean inactive buckets
|
|
153
|
+
*/
|
|
154
|
+
private cleanup;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Rate limiter middleware for Express-like frameworks
|
|
158
|
+
*/
|
|
159
|
+
export declare function createRateLimiterMiddleware(limiter: RateLimiter): (req: {
|
|
160
|
+
ip?: string;
|
|
161
|
+
headers?: Record<string, string>;
|
|
162
|
+
}, res: {
|
|
163
|
+
status: (code: number) => {
|
|
164
|
+
json: (body: unknown) => void;
|
|
165
|
+
};
|
|
166
|
+
setHeader: (name: string, value: string) => void;
|
|
167
|
+
}, next: () => void) => void;
|
|
168
|
+
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rate Limiter
|
|
3
|
+
*
|
|
4
|
+
* Production-ready rate limiting implementations.
|
|
5
|
+
*
|
|
6
|
+
* @module v3/shared/resilience/rate-limiter
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Sliding Window Rate Limiter
|
|
10
|
+
*
|
|
11
|
+
* Uses sliding window algorithm for smooth rate limiting.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* const limiter = new SlidingWindowRateLimiter({
|
|
15
|
+
* maxRequests: 100,
|
|
16
|
+
* windowMs: 60000, // 100 requests per minute
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* const result = limiter.consume('user-123');
|
|
20
|
+
* if (!result.allowed) {
|
|
21
|
+
* throw new Error(`Rate limited. Retry in ${result.retryAfter}ms`);
|
|
22
|
+
* }
|
|
23
|
+
*/
|
|
24
|
+
export class SlidingWindowRateLimiter {
|
|
25
|
+
options;
|
|
26
|
+
requests = new Map();
|
|
27
|
+
cleanupInterval;
|
|
28
|
+
constructor(options) {
|
|
29
|
+
this.options = {
|
|
30
|
+
slidingWindow: true,
|
|
31
|
+
...options,
|
|
32
|
+
};
|
|
33
|
+
// Periodic cleanup of old entries
|
|
34
|
+
this.cleanupInterval = setInterval(() => {
|
|
35
|
+
this.cleanup();
|
|
36
|
+
}, this.options.windowMs);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Check if a request would be allowed without consuming
|
|
40
|
+
*/
|
|
41
|
+
check(key = 'default') {
|
|
42
|
+
this.cleanupKey(key);
|
|
43
|
+
const entries = this.requests.get(key) || [];
|
|
44
|
+
return {
|
|
45
|
+
allowed: entries.length < this.options.maxRequests,
|
|
46
|
+
remaining: Math.max(0, this.options.maxRequests - entries.length),
|
|
47
|
+
resetAt: this.getResetTime(entries),
|
|
48
|
+
retryAfter: this.getRetryAfter(entries),
|
|
49
|
+
total: this.options.maxRequests,
|
|
50
|
+
used: entries.length,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Consume a request token
|
|
55
|
+
*/
|
|
56
|
+
consume(key = 'default') {
|
|
57
|
+
// Clean old entries first
|
|
58
|
+
this.cleanupKey(key);
|
|
59
|
+
let entries = this.requests.get(key);
|
|
60
|
+
if (!entries) {
|
|
61
|
+
entries = [];
|
|
62
|
+
this.requests.set(key, entries);
|
|
63
|
+
}
|
|
64
|
+
// Check if allowed
|
|
65
|
+
if (entries.length >= this.options.maxRequests) {
|
|
66
|
+
const result = {
|
|
67
|
+
allowed: false,
|
|
68
|
+
remaining: 0,
|
|
69
|
+
resetAt: this.getResetTime(entries),
|
|
70
|
+
retryAfter: this.getRetryAfter(entries),
|
|
71
|
+
total: this.options.maxRequests,
|
|
72
|
+
used: entries.length,
|
|
73
|
+
};
|
|
74
|
+
this.options.onRateLimited?.(key, 0, result.resetAt);
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
// Add new entry
|
|
78
|
+
entries.push({ timestamp: Date.now(), key });
|
|
79
|
+
return {
|
|
80
|
+
allowed: true,
|
|
81
|
+
remaining: this.options.maxRequests - entries.length,
|
|
82
|
+
resetAt: this.getResetTime(entries),
|
|
83
|
+
retryAfter: 0,
|
|
84
|
+
total: this.options.maxRequests,
|
|
85
|
+
used: entries.length,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Reset rate limit for a key
|
|
90
|
+
*/
|
|
91
|
+
reset(key) {
|
|
92
|
+
if (key) {
|
|
93
|
+
this.requests.delete(key);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
this.requests.clear();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get current status
|
|
101
|
+
*/
|
|
102
|
+
status(key = 'default') {
|
|
103
|
+
return this.check(key);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Cleanup resources
|
|
107
|
+
*/
|
|
108
|
+
destroy() {
|
|
109
|
+
if (this.cleanupInterval) {
|
|
110
|
+
clearInterval(this.cleanupInterval);
|
|
111
|
+
this.cleanupInterval = undefined;
|
|
112
|
+
}
|
|
113
|
+
this.requests.clear();
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Clean old entries for a specific key
|
|
117
|
+
*/
|
|
118
|
+
cleanupKey(key) {
|
|
119
|
+
const entries = this.requests.get(key);
|
|
120
|
+
if (!entries)
|
|
121
|
+
return;
|
|
122
|
+
const cutoff = Date.now() - this.options.windowMs;
|
|
123
|
+
const filtered = entries.filter((e) => e.timestamp >= cutoff);
|
|
124
|
+
if (filtered.length === 0) {
|
|
125
|
+
this.requests.delete(key);
|
|
126
|
+
}
|
|
127
|
+
else if (filtered.length !== entries.length) {
|
|
128
|
+
this.requests.set(key, filtered);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Clean all old entries
|
|
133
|
+
*/
|
|
134
|
+
cleanup() {
|
|
135
|
+
for (const key of this.requests.keys()) {
|
|
136
|
+
this.cleanupKey(key);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Get reset time based on oldest entry
|
|
141
|
+
*/
|
|
142
|
+
getResetTime(entries) {
|
|
143
|
+
if (entries.length === 0) {
|
|
144
|
+
return new Date(Date.now() + this.options.windowMs);
|
|
145
|
+
}
|
|
146
|
+
const oldest = entries[0];
|
|
147
|
+
return new Date(oldest.timestamp + this.options.windowMs);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Get retry after time in ms
|
|
151
|
+
*/
|
|
152
|
+
getRetryAfter(entries) {
|
|
153
|
+
if (entries.length < this.options.maxRequests) {
|
|
154
|
+
return 0;
|
|
155
|
+
}
|
|
156
|
+
const oldest = entries[0];
|
|
157
|
+
const resetAt = oldest.timestamp + this.options.windowMs;
|
|
158
|
+
return Math.max(0, resetAt - Date.now());
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Token Bucket Rate Limiter
|
|
163
|
+
*
|
|
164
|
+
* Uses token bucket algorithm for burst-friendly rate limiting.
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* const limiter = new TokenBucketRateLimiter({
|
|
168
|
+
* maxRequests: 10, // bucket size
|
|
169
|
+
* windowMs: 1000, // refill interval
|
|
170
|
+
* });
|
|
171
|
+
*/
|
|
172
|
+
export class TokenBucketRateLimiter {
|
|
173
|
+
options;
|
|
174
|
+
buckets = new Map();
|
|
175
|
+
cleanupInterval;
|
|
176
|
+
constructor(options) {
|
|
177
|
+
this.options = options;
|
|
178
|
+
// Periodic cleanup
|
|
179
|
+
this.cleanupInterval = setInterval(() => {
|
|
180
|
+
this.cleanup();
|
|
181
|
+
}, this.options.windowMs * 10);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Check if a request would be allowed
|
|
185
|
+
*/
|
|
186
|
+
check(key = 'default') {
|
|
187
|
+
this.refill(key);
|
|
188
|
+
const bucket = this.getBucket(key);
|
|
189
|
+
return {
|
|
190
|
+
allowed: bucket.tokens >= 1,
|
|
191
|
+
remaining: Math.floor(bucket.tokens),
|
|
192
|
+
resetAt: new Date(bucket.lastRefill + this.options.windowMs),
|
|
193
|
+
retryAfter: bucket.tokens >= 1 ? 0 : this.options.windowMs,
|
|
194
|
+
total: this.options.maxRequests,
|
|
195
|
+
used: this.options.maxRequests - Math.floor(bucket.tokens),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Consume a token
|
|
200
|
+
*/
|
|
201
|
+
consume(key = 'default') {
|
|
202
|
+
this.refill(key);
|
|
203
|
+
const bucket = this.getBucket(key);
|
|
204
|
+
if (bucket.tokens < 1) {
|
|
205
|
+
const result = {
|
|
206
|
+
allowed: false,
|
|
207
|
+
remaining: 0,
|
|
208
|
+
resetAt: new Date(bucket.lastRefill + this.options.windowMs),
|
|
209
|
+
retryAfter: this.options.windowMs,
|
|
210
|
+
total: this.options.maxRequests,
|
|
211
|
+
used: this.options.maxRequests,
|
|
212
|
+
};
|
|
213
|
+
this.options.onRateLimited?.(key, 0, result.resetAt);
|
|
214
|
+
return result;
|
|
215
|
+
}
|
|
216
|
+
bucket.tokens -= 1;
|
|
217
|
+
return {
|
|
218
|
+
allowed: true,
|
|
219
|
+
remaining: Math.floor(bucket.tokens),
|
|
220
|
+
resetAt: new Date(bucket.lastRefill + this.options.windowMs),
|
|
221
|
+
retryAfter: 0,
|
|
222
|
+
total: this.options.maxRequests,
|
|
223
|
+
used: this.options.maxRequests - Math.floor(bucket.tokens),
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Reset bucket for a key
|
|
228
|
+
*/
|
|
229
|
+
reset(key) {
|
|
230
|
+
if (key) {
|
|
231
|
+
this.buckets.delete(key);
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
this.buckets.clear();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Get current status
|
|
239
|
+
*/
|
|
240
|
+
status(key = 'default') {
|
|
241
|
+
return this.check(key);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Cleanup resources
|
|
245
|
+
*/
|
|
246
|
+
destroy() {
|
|
247
|
+
if (this.cleanupInterval) {
|
|
248
|
+
clearInterval(this.cleanupInterval);
|
|
249
|
+
this.cleanupInterval = undefined;
|
|
250
|
+
}
|
|
251
|
+
this.buckets.clear();
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Get or create bucket for key
|
|
255
|
+
*/
|
|
256
|
+
getBucket(key) {
|
|
257
|
+
let bucket = this.buckets.get(key);
|
|
258
|
+
if (!bucket) {
|
|
259
|
+
bucket = { tokens: this.options.maxRequests, lastRefill: Date.now() };
|
|
260
|
+
this.buckets.set(key, bucket);
|
|
261
|
+
}
|
|
262
|
+
return bucket;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Refill tokens based on elapsed time
|
|
266
|
+
*/
|
|
267
|
+
refill(key) {
|
|
268
|
+
const bucket = this.getBucket(key);
|
|
269
|
+
const now = Date.now();
|
|
270
|
+
const elapsed = now - bucket.lastRefill;
|
|
271
|
+
if (elapsed >= this.options.windowMs) {
|
|
272
|
+
// Full refill after window
|
|
273
|
+
const intervals = Math.floor(elapsed / this.options.windowMs);
|
|
274
|
+
bucket.tokens = Math.min(this.options.maxRequests, bucket.tokens + intervals * this.options.maxRequests);
|
|
275
|
+
bucket.lastRefill = now;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Clean inactive buckets
|
|
280
|
+
*/
|
|
281
|
+
cleanup() {
|
|
282
|
+
const cutoff = Date.now() - this.options.windowMs * 10;
|
|
283
|
+
for (const [key, bucket] of this.buckets) {
|
|
284
|
+
if (bucket.lastRefill < cutoff && bucket.tokens >= this.options.maxRequests) {
|
|
285
|
+
this.buckets.delete(key);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Rate limiter middleware for Express-like frameworks
|
|
292
|
+
*/
|
|
293
|
+
export function createRateLimiterMiddleware(limiter) {
|
|
294
|
+
return (req, res, next) => {
|
|
295
|
+
// Get key from IP or header
|
|
296
|
+
const key = req.ip || req.headers?.['x-forwarded-for'] || 'anonymous';
|
|
297
|
+
const result = limiter.consume(key);
|
|
298
|
+
// Set rate limit headers
|
|
299
|
+
res.setHeader('X-RateLimit-Limit', String(result.total));
|
|
300
|
+
res.setHeader('X-RateLimit-Remaining', String(result.remaining));
|
|
301
|
+
res.setHeader('X-RateLimit-Reset', String(Math.ceil(result.resetAt.getTime() / 1000)));
|
|
302
|
+
if (!result.allowed) {
|
|
303
|
+
res.setHeader('Retry-After', String(Math.ceil(result.retryAfter / 1000)));
|
|
304
|
+
res.status(429).json({
|
|
305
|
+
error: 'Too Many Requests',
|
|
306
|
+
retryAfter: result.retryAfter,
|
|
307
|
+
resetAt: result.resetAt.toISOString(),
|
|
308
|
+
});
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
next();
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry with Exponential Backoff
|
|
3
|
+
*
|
|
4
|
+
* Production-ready retry logic with jitter, max retries, and error filtering.
|
|
5
|
+
*
|
|
6
|
+
* @module v3/shared/resilience/retry
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Retry options
|
|
10
|
+
*/
|
|
11
|
+
export interface RetryOptions {
|
|
12
|
+
/** Maximum number of retry attempts (default: 3) */
|
|
13
|
+
maxAttempts: number;
|
|
14
|
+
/** Initial delay in milliseconds (default: 100) */
|
|
15
|
+
initialDelay: number;
|
|
16
|
+
/** Maximum delay in milliseconds (default: 10000) */
|
|
17
|
+
maxDelay: number;
|
|
18
|
+
/** Backoff multiplier (default: 2) */
|
|
19
|
+
backoffMultiplier: number;
|
|
20
|
+
/** Jitter factor 0-1 to randomize delays (default: 0.1) */
|
|
21
|
+
jitter: number;
|
|
22
|
+
/** Timeout for each attempt in milliseconds (default: 30000) */
|
|
23
|
+
timeout: number;
|
|
24
|
+
/** Errors that should trigger a retry (default: all errors) */
|
|
25
|
+
retryableErrors?: (error: Error) => boolean;
|
|
26
|
+
/** Callback for each retry attempt */
|
|
27
|
+
onRetry?: (error: Error, attempt: number, delay: number) => void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Retry result
|
|
31
|
+
*/
|
|
32
|
+
export interface RetryResult<T> {
|
|
33
|
+
success: boolean;
|
|
34
|
+
result?: T;
|
|
35
|
+
attempts: number;
|
|
36
|
+
totalTime: number;
|
|
37
|
+
errors: Error[];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Retry error with attempt history
|
|
41
|
+
*/
|
|
42
|
+
export declare class RetryError extends Error {
|
|
43
|
+
readonly attempts: number;
|
|
44
|
+
readonly errors: Error[];
|
|
45
|
+
readonly totalTime: number;
|
|
46
|
+
constructor(message: string, attempts: number, errors: Error[], totalTime: number);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Retry a function with exponential backoff
|
|
50
|
+
*
|
|
51
|
+
* @param fn Function to retry
|
|
52
|
+
* @param options Retry configuration
|
|
53
|
+
* @returns Result with success/failure and metadata
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* const result = await retry(
|
|
57
|
+
* () => fetchData(),
|
|
58
|
+
* { maxAttempts: 5, initialDelay: 200 }
|
|
59
|
+
* );
|
|
60
|
+
*
|
|
61
|
+
* if (result.success) {
|
|
62
|
+
* console.log('Data:', result.result);
|
|
63
|
+
* } else {
|
|
64
|
+
* console.log('Failed after', result.attempts, 'attempts');
|
|
65
|
+
* }
|
|
66
|
+
*/
|
|
67
|
+
export declare function retry<T>(fn: () => Promise<T>, options?: Partial<RetryOptions>): Promise<RetryResult<T>>;
|
|
68
|
+
/**
|
|
69
|
+
* Wrap a function with retry behavior
|
|
70
|
+
*
|
|
71
|
+
* @param fn Function to wrap
|
|
72
|
+
* @param options Retry configuration
|
|
73
|
+
* @returns Wrapped function that retries on failure
|
|
74
|
+
*/
|
|
75
|
+
export declare function withRetry<T extends (...args: unknown[]) => Promise<unknown>>(fn: T, options?: Partial<RetryOptions>): (...args: Parameters<T>) => Promise<RetryResult<Awaited<ReturnType<T>>>>;
|
|
76
|
+
/**
|
|
77
|
+
* Common retryable error predicates
|
|
78
|
+
*/
|
|
79
|
+
export declare const RetryableErrors: {
|
|
80
|
+
/** Network errors (ECONNRESET, ETIMEDOUT, etc.) */
|
|
81
|
+
network: (error: Error) => boolean;
|
|
82
|
+
/** Rate limit errors (429) */
|
|
83
|
+
rateLimit: (error: Error) => boolean;
|
|
84
|
+
/** Server errors (5xx) */
|
|
85
|
+
serverError: (error: Error) => boolean;
|
|
86
|
+
/** Transient errors (network + rate limit + 5xx) */
|
|
87
|
+
transient: (error: Error) => boolean;
|
|
88
|
+
/** All errors are retryable */
|
|
89
|
+
all: () => boolean;
|
|
90
|
+
};
|
|
91
|
+
//# sourceMappingURL=retry.d.ts.map
|