@mxf-dev/core 1.0.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/dist/adapters/FrameworkAdapter.js +1 -1
- package/dist/adapters/FrameworkAdapter.js.map +1 -1
- package/dist/config/ConfigManager.d.ts +9 -8
- package/dist/config/ConfigManager.d.ts.map +1 -1
- package/dist/config/ConfigManager.js +47 -17
- package/dist/config/ConfigManager.js.map +1 -1
- package/dist/config/memory-strata.config.d.ts +25 -0
- package/dist/config/memory-strata.config.d.ts.map +1 -0
- package/dist/config/memory-strata.config.js +107 -0
- package/dist/config/memory-strata.config.js.map +1 -0
- package/dist/constants/ToolNames.d.ts +1 -29
- package/dist/constants/ToolNames.d.ts.map +1 -1
- package/dist/constants/ToolNames.js +4 -17
- package/dist/constants/ToolNames.js.map +1 -1
- package/dist/database/DatabaseAdapterFactory.d.ts +14 -2
- package/dist/database/DatabaseAdapterFactory.d.ts.map +1 -1
- package/dist/database/DatabaseAdapterFactory.js +21 -6
- package/dist/database/DatabaseAdapterFactory.js.map +1 -1
- package/dist/database/adapters/mongodb/MongoBaseRepository.d.ts +7 -1
- package/dist/database/adapters/mongodb/MongoBaseRepository.d.ts.map +1 -1
- package/dist/database/adapters/mongodb/MongoBaseRepository.js +12 -2
- package/dist/database/adapters/mongodb/MongoBaseRepository.js.map +1 -1
- package/dist/events/ClientEventBus.d.ts +16 -77
- package/dist/events/ClientEventBus.d.ts.map +1 -1
- package/dist/events/ClientEventBus.js +38 -311
- package/dist/events/ClientEventBus.js.map +1 -1
- package/dist/events/EventBus.d.ts +1 -1
- package/dist/events/EventBus.d.ts.map +1 -1
- package/dist/events/EventBus.js +5 -4
- package/dist/events/EventBus.js.map +1 -1
- package/dist/events/EventBusBase.d.ts +139 -34
- package/dist/events/EventBusBase.d.ts.map +1 -1
- package/dist/events/EventBusBase.js +366 -88
- package/dist/events/EventBusBase.js.map +1 -1
- package/dist/events/EventNames.d.ts +10 -1
- package/dist/events/EventNames.d.ts.map +1 -1
- package/dist/events/EventNames.js +3 -0
- package/dist/events/EventNames.js.map +1 -1
- package/dist/events/PublicEvents.d.ts +6 -2
- package/dist/events/PublicEvents.d.ts.map +1 -1
- package/dist/events/PublicEvents.js +7 -4
- package/dist/events/PublicEvents.js.map +1 -1
- package/dist/events/ServerEventBus.d.ts +23 -112
- package/dist/events/ServerEventBus.d.ts.map +1 -1
- package/dist/events/ServerEventBus.js +58 -323
- package/dist/events/ServerEventBus.js.map +1 -1
- package/dist/events/event-definitions/LlmBudgetEvents.d.ts +68 -0
- package/dist/events/event-definitions/LlmBudgetEvents.d.ts.map +1 -0
- package/dist/events/event-definitions/LlmBudgetEvents.js +35 -0
- package/dist/events/event-definitions/LlmBudgetEvents.js.map +1 -0
- package/dist/interfaces/IMemoryPersistence.d.ts +41 -0
- package/dist/interfaces/IMemoryPersistence.d.ts.map +1 -0
- package/dist/interfaces/IMemoryPersistence.js +15 -0
- package/dist/interfaces/IMemoryPersistence.js.map +1 -0
- package/dist/middleware/MxpMiddleware.d.ts +8 -1
- package/dist/middleware/MxpMiddleware.d.ts.map +1 -1
- package/dist/middleware/MxpMiddleware.js +42 -20
- package/dist/middleware/MxpMiddleware.js.map +1 -1
- package/dist/models/channelKey.d.ts +1 -0
- package/dist/models/channelKey.d.ts.map +1 -1
- package/dist/models/channelKey.js +5 -0
- package/dist/models/channelKey.js.map +1 -1
- package/dist/models/memoryUtility.d.ts +39 -0
- package/dist/models/memoryUtility.d.ts.map +1 -0
- package/dist/models/memoryUtility.js +39 -0
- package/dist/models/memoryUtility.js.map +1 -0
- package/dist/models/user.d.ts.map +1 -1
- package/dist/models/user.js +23 -1
- package/dist/models/user.js.map +1 -1
- package/dist/mxp/BinaryProtocolLayer.d.ts +24 -8
- package/dist/mxp/BinaryProtocolLayer.d.ts.map +1 -1
- package/dist/mxp/BinaryProtocolLayer.js +90 -103
- package/dist/mxp/BinaryProtocolLayer.js.map +1 -1
- package/dist/mxp/ContextCompressionEngine.d.ts +57 -10
- package/dist/mxp/ContextCompressionEngine.d.ts.map +1 -1
- package/dist/mxp/ContextCompressionEngine.js +170 -128
- package/dist/mxp/ContextCompressionEngine.js.map +1 -1
- package/dist/mxp/MxpTokenOptimizer.d.ts +7 -3
- package/dist/mxp/MxpTokenOptimizer.d.ts.map +1 -1
- package/dist/mxp/MxpTokenOptimizer.js +8 -5
- package/dist/mxp/MxpTokenOptimizer.js.map +1 -1
- package/dist/prompts/MxfAgentSystemPrompt.d.ts +1 -1
- package/dist/prompts/MxfAgentSystemPrompt.d.ts.map +1 -1
- package/dist/prompts/MxfAgentSystemPrompt.js +47 -57
- package/dist/prompts/MxfAgentSystemPrompt.js.map +1 -1
- package/dist/protocols/mcp/ToolError.d.ts +123 -0
- package/dist/protocols/mcp/ToolError.d.ts.map +1 -0
- package/dist/protocols/mcp/ToolError.js +108 -0
- package/dist/protocols/mcp/ToolError.js.map +1 -0
- package/dist/protocols/mcp/defineTool.d.ts +133 -0
- package/dist/protocols/mcp/defineTool.d.ts.map +1 -0
- package/dist/protocols/mcp/defineTool.js +236 -0
- package/dist/protocols/mcp/defineTool.js.map +1 -0
- package/dist/protocols/mcp/security/HttpTargetGuard.d.ts +38 -0
- package/dist/protocols/mcp/security/HttpTargetGuard.d.ts.map +1 -0
- package/dist/protocols/mcp/security/HttpTargetGuard.js +227 -0
- package/dist/protocols/mcp/security/HttpTargetGuard.js.map +1 -0
- package/dist/protocols/mcp/security/McpSecurityGuard.d.ts +9 -1
- package/dist/protocols/mcp/security/McpSecurityGuard.d.ts.map +1 -1
- package/dist/protocols/mcp/security/McpSecurityGuard.js +10 -2
- package/dist/protocols/mcp/security/McpSecurityGuard.js.map +1 -1
- package/dist/protocols/mcp/security/McpToolPolicy.d.ts +81 -0
- package/dist/protocols/mcp/security/McpToolPolicy.d.ts.map +1 -0
- package/dist/protocols/mcp/security/McpToolPolicy.js +202 -0
- package/dist/protocols/mcp/security/McpToolPolicy.js.map +1 -0
- package/dist/protocols/mcp/services/ExternalMcpServerManager.d.ts +51 -18
- package/dist/protocols/mcp/services/ExternalMcpServerManager.d.ts.map +1 -1
- package/dist/protocols/mcp/services/ExternalMcpServerManager.js +256 -283
- package/dist/protocols/mcp/services/ExternalMcpServerManager.js.map +1 -1
- package/dist/protocols/mcp/services/ExternalServerConfigs.d.ts +39 -6
- package/dist/protocols/mcp/services/ExternalServerConfigs.d.ts.map +1 -1
- package/dist/protocols/mcp/services/ExternalServerConfigs.js +76 -18
- package/dist/protocols/mcp/services/ExternalServerConfigs.js.map +1 -1
- package/dist/protocols/mcp/tools/AnalyticsTools.js +1 -1
- package/dist/protocols/mcp/tools/AnalyticsTools.js.map +1 -1
- package/dist/protocols/mcp/tools/CodeAnalysisTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/CodeAnalysisTools.js +15 -0
- package/dist/protocols/mcp/tools/CodeAnalysisTools.js.map +1 -1
- package/dist/protocols/mcp/tools/EffectivenessTools.js +3 -3
- package/dist/protocols/mcp/tools/EffectivenessTools.js.map +1 -1
- package/dist/protocols/mcp/tools/GitTools.d.ts +9 -369
- package/dist/protocols/mcp/tools/GitTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/GitTools.js +222 -517
- package/dist/protocols/mcp/tools/GitTools.js.map +1 -1
- package/dist/protocols/mcp/tools/InfrastructureTools.d.ts +56 -24
- package/dist/protocols/mcp/tools/InfrastructureTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/InfrastructureTools.js +117 -26
- package/dist/protocols/mcp/tools/InfrastructureTools.js.map +1 -1
- package/dist/protocols/mcp/tools/MemorySearchTools.d.ts +0 -3
- package/dist/protocols/mcp/tools/MemorySearchTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/MemorySearchTools.js +58 -1
- package/dist/protocols/mcp/tools/MemorySearchTools.js.map +1 -1
- package/dist/protocols/mcp/tools/OrparTools.d.ts +8 -0
- package/dist/protocols/mcp/tools/OrparTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/OrparTools.js +66 -0
- package/dist/protocols/mcp/tools/OrparTools.js.map +1 -1
- package/dist/protocols/mcp/tools/SafetyTools.d.ts +7 -525
- package/dist/protocols/mcp/tools/SafetyTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/SafetyTools.js +349 -514
- package/dist/protocols/mcp/tools/SafetyTools.js.map +1 -1
- package/dist/protocols/mcp/tools/TaskPlanningTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/TaskPlanningTools.js +56 -26
- package/dist/protocols/mcp/tools/TaskPlanningTools.js.map +1 -1
- package/dist/protocols/mcp/tools/TestTools.d.ts +15 -376
- package/dist/protocols/mcp/tools/TestTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/TestTools.js +241 -363
- package/dist/protocols/mcp/tools/TestTools.js.map +1 -1
- package/dist/protocols/mcp/tools/TypeScriptTools.d.ts +6 -276
- package/dist/protocols/mcp/tools/TypeScriptTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/TypeScriptTools.js +223 -337
- package/dist/protocols/mcp/tools/TypeScriptTools.js.map +1 -1
- package/dist/protocols/mcp/tools/WebTools.d.ts +15 -728
- package/dist/protocols/mcp/tools/WebTools.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/WebTools.js +151 -257
- package/dist/protocols/mcp/tools/WebTools.js.map +1 -1
- package/dist/protocols/mcp/tools/shell/ShellExecuteHandler.d.ts +5 -3
- package/dist/protocols/mcp/tools/shell/ShellExecuteHandler.d.ts.map +1 -1
- package/dist/protocols/mcp/tools/shell/ShellExecuteHandler.js +31 -6
- package/dist/protocols/mcp/tools/shell/ShellExecuteHandler.js.map +1 -1
- package/dist/schemas/EventPayloadSchema.d.ts +21 -0
- package/dist/schemas/EventPayloadSchema.d.ts.map +1 -1
- package/dist/schemas/EventPayloadSchema.js +14 -0
- package/dist/schemas/EventPayloadSchema.js.map +1 -1
- package/dist/services/BackgroundTaskManager.d.ts.map +1 -1
- package/dist/services/BackgroundTaskManager.js +12 -4
- package/dist/services/BackgroundTaskManager.js.map +1 -1
- package/dist/services/BrowserManager.d.ts +1 -1
- package/dist/services/BrowserManager.d.ts.map +1 -1
- package/dist/services/BrowserManager.js +26 -3
- package/dist/services/BrowserManager.js.map +1 -1
- package/dist/services/MemoryCompressor.d.ts +11 -2
- package/dist/services/MemoryCompressor.d.ts.map +1 -1
- package/dist/services/MemoryCompressor.js +8 -26
- package/dist/services/MemoryCompressor.js.map +1 -1
- package/dist/services/MemoryService.d.ts +16 -3
- package/dist/services/MemoryService.d.ts.map +1 -1
- package/dist/services/MemoryService.js +79 -55
- package/dist/services/MemoryService.js.map +1 -1
- package/dist/services/MxfMLService.d.ts +5 -0
- package/dist/services/MxfMLService.d.ts.map +1 -1
- package/dist/services/MxfMLService.js +32 -6
- package/dist/services/MxfMLService.js.map +1 -1
- package/dist/services/PredictiveAnalyticsService.d.ts +45 -9
- package/dist/services/PredictiveAnalyticsService.d.ts.map +1 -1
- package/dist/services/PredictiveAnalyticsService.js +82 -44
- package/dist/services/PredictiveAnalyticsService.js.map +1 -1
- package/dist/services/QValueManager.d.ts +7 -0
- package/dist/services/QValueManager.d.ts.map +1 -1
- package/dist/services/QValueManager.js +9 -0
- package/dist/services/QValueManager.js.map +1 -1
- package/dist/services/RewardSignalProcessor.d.ts +38 -1
- package/dist/services/RewardSignalProcessor.d.ts.map +1 -1
- package/dist/services/RewardSignalProcessor.js +109 -7
- package/dist/services/RewardSignalProcessor.js.map +1 -1
- package/dist/services/SharedStateService.d.ts +16 -2
- package/dist/services/SharedStateService.d.ts.map +1 -1
- package/dist/services/SharedStateService.js +44 -3
- package/dist/services/SharedStateService.js.map +1 -1
- package/dist/services/TaskEffectivenessService.d.ts +34 -18
- package/dist/services/TaskEffectivenessService.d.ts.map +1 -1
- package/dist/services/TaskEffectivenessService.js +91 -155
- package/dist/services/TaskEffectivenessService.js.map +1 -1
- package/dist/services/WebSearchService.d.ts.map +1 -1
- package/dist/services/WebSearchService.js.map +1 -1
- package/dist/services/dag/TaskDagService.d.ts.map +1 -1
- package/dist/services/dag/TaskDagService.js +2 -0
- package/dist/services/dag/TaskDagService.js.map +1 -1
- package/dist/services/orpar-memory/OrparMemoryCoordinator.d.ts +7 -0
- package/dist/services/orpar-memory/OrparMemoryCoordinator.d.ts.map +1 -1
- package/dist/services/orpar-memory/OrparMemoryCoordinator.js +71 -10
- package/dist/services/orpar-memory/OrparMemoryCoordinator.js.map +1 -1
- package/dist/types/MemoryUtilityTypes.d.ts +2 -0
- package/dist/types/MemoryUtilityTypes.d.ts.map +1 -1
- package/dist/types/MemoryUtilityTypes.js.map +1 -1
- package/dist/utils/Logger.d.ts +43 -3
- package/dist/utils/Logger.d.ts.map +1 -1
- package/dist/utils/Logger.js +78 -36
- package/dist/utils/Logger.js.map +1 -1
- package/dist/utils/MxpEncryption.d.ts +19 -6
- package/dist/utils/MxpEncryption.d.ts.map +1 -1
- package/dist/utils/MxpEncryption.js +48 -16
- package/dist/utils/MxpEncryption.js.map +1 -1
- package/package.json +19 -4
- package/src/adapters/FrameworkAdapter.ts +1 -1
- package/src/config/ConfigManager.ts +52 -19
- package/src/config/memory-strata.config.ts +136 -0
- package/src/constants/ToolNames.ts +4 -18
- package/src/database/DatabaseAdapterFactory.ts +23 -6
- package/src/database/adapters/mongodb/MongoBaseRepository.ts +16 -2
- package/src/events/ClientEventBus.ts +68 -403
- package/src/events/EventBus.ts +7 -8
- package/src/events/EventBusBase.ts +479 -116
- package/src/events/EventNames.ts +6 -0
- package/src/events/PublicEvents.ts +7 -4
- package/src/events/ServerEventBus.ts +78 -473
- package/src/events/event-definitions/LlmBudgetEvents.ts +74 -0
- package/src/interfaces/IMemoryPersistence.ts +51 -0
- package/src/middleware/MxpMiddleware.ts +51 -24
- package/src/models/channelKey.ts +18 -4
- package/src/models/memoryUtility.ts +64 -0
- package/src/models/user.ts +36 -4
- package/src/mxp/BinaryProtocolLayer.ts +108 -124
- package/src/mxp/ContextCompressionEngine.ts +219 -161
- package/src/mxp/MxpTokenOptimizer.ts +10 -10
- package/src/prompts/MxfAgentSystemPrompt.ts +45 -55
- package/src/protocols/mcp/ToolError.ts +201 -0
- package/src/protocols/mcp/defineTool.ts +379 -0
- package/src/protocols/mcp/security/HttpTargetGuard.ts +267 -0
- package/src/protocols/mcp/security/McpSecurityGuard.ts +10 -3
- package/src/protocols/mcp/security/McpToolPolicy.ts +221 -0
- package/src/protocols/mcp/services/ExternalMcpServerManager.ts +340 -322
- package/src/protocols/mcp/services/ExternalServerConfigs.ts +82 -19
- package/src/protocols/mcp/tools/AnalyticsTools.ts +1 -1
- package/src/protocols/mcp/tools/CodeAnalysisTools.ts +15 -0
- package/src/protocols/mcp/tools/EffectivenessTools.ts +3 -3
- package/src/protocols/mcp/tools/GitTools.ts +290 -569
- package/src/protocols/mcp/tools/InfrastructureTools.ts +189 -43
- package/src/protocols/mcp/tools/MemorySearchTools.ts +77 -1
- package/src/protocols/mcp/tools/OrparTools.ts +87 -0
- package/src/protocols/mcp/tools/SafetyTools.ts +501 -579
- package/src/protocols/mcp/tools/TaskPlanningTools.ts +81 -39
- package/src/protocols/mcp/tools/TestTools.ts +339 -419
- package/src/protocols/mcp/tools/TypeScriptTools.ts +316 -376
- package/src/protocols/mcp/tools/WebTools.ts +209 -307
- package/src/protocols/mcp/tools/shell/ShellExecuteHandler.ts +53 -14
- package/src/schemas/EventPayloadSchema.ts +37 -0
- package/src/services/BackgroundTaskManager.ts +12 -4
- package/src/services/BrowserManager.ts +41 -1
- package/src/services/MemoryCompressor.ts +20 -32
- package/src/services/MemoryService.ts +96 -58
- package/src/services/MxfMLService.ts +41 -7
- package/src/services/PredictiveAnalyticsService.ts +101 -51
- package/src/services/QValueManager.ts +10 -0
- package/src/services/RewardSignalProcessor.ts +138 -9
- package/src/services/SharedStateService.ts +56 -8
- package/src/services/TaskEffectivenessService.ts +114 -187
- package/src/services/WebSearchService.ts +2 -1
- package/src/services/dag/TaskDagService.ts +2 -0
- package/src/services/orpar-memory/OrparMemoryCoordinator.ts +86 -10
- package/src/types/MemoryUtilityTypes.ts +2 -0
- package/src/utils/Logger.ts +116 -40
- package/src/utils/MxpEncryption.ts +69 -34
- package/dist/mxp/MxpEventForwardingEnhancer.d.ts +0 -134
- package/dist/mxp/MxpEventForwardingEnhancer.d.ts.map +0 -1
- package/dist/mxp/MxpEventForwardingEnhancer.js +0 -361
- package/dist/mxp/MxpEventForwardingEnhancer.js.map +0 -1
- package/dist/protocols/mcp/services/ToolSchemaEnhancements.d.ts +0 -56
- package/dist/protocols/mcp/services/ToolSchemaEnhancements.d.ts.map +0 -1
- package/dist/protocols/mcp/services/ToolSchemaEnhancements.js +0 -57
- package/dist/protocols/mcp/services/ToolSchemaEnhancements.js.map +0 -1
- package/dist/protocols/mcp/tools/ControlLoopLifecycle.d.ts +0 -270
- package/dist/protocols/mcp/tools/ControlLoopLifecycle.d.ts.map +0 -1
- package/dist/protocols/mcp/tools/ControlLoopLifecycle.js +0 -256
- package/dist/protocols/mcp/tools/ControlLoopLifecycle.js.map +0 -1
- package/dist/protocols/mcp/tools/LspTools.d.ts +0 -495
- package/dist/protocols/mcp/tools/LspTools.d.ts.map +0 -1
- package/dist/protocols/mcp/tools/LspTools.js +0 -469
- package/dist/protocols/mcp/tools/LspTools.js.map +0 -1
- package/src/mxp/MxpEventForwardingEnhancer.ts +0 -471
- package/src/protocols/mcp/services/ToolSchemaEnhancements.ts +0 -92
- package/src/protocols/mcp/tools/ControlLoopLifecycle.ts +0 -302
- package/src/protocols/mcp/tools/LspTools.ts +0 -476
|
@@ -26,13 +26,14 @@
|
|
|
26
26
|
* by both client and server event bus implementations.
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
|
-
import { Subject,
|
|
30
|
-
import { filter, map } from 'rxjs/operators';
|
|
29
|
+
import { Subject, Subscription } from 'rxjs';
|
|
30
|
+
import { filter, map, take } from 'rxjs/operators';
|
|
31
31
|
import { SOCKET_RESERVED_EVENTS, Events } from './EventNames.js';
|
|
32
32
|
import type { EventMap } from './EventNames.js';
|
|
33
33
|
import type { BaseEventPayload } from '../schemas/EventPayloadSchema.js';
|
|
34
|
+
import { createBaseEventPayload } from '../schemas/EventPayloadSchema.js';
|
|
34
35
|
import { MessageEvents } from './event-definitions/MessageEvents.js';
|
|
35
|
-
import { createStrictValidator
|
|
36
|
+
import { createStrictValidator } from '../utils/validation.js';
|
|
36
37
|
import { Logger } from '../utils/Logger.js';
|
|
37
38
|
|
|
38
39
|
/**
|
|
@@ -212,152 +213,525 @@ export function isReservedEvent(event: string): boolean {
|
|
|
212
213
|
}
|
|
213
214
|
|
|
214
215
|
/**
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
*
|
|
216
|
+
* Base event bus implementation with RxJS.
|
|
217
|
+
*
|
|
218
|
+
* Holds everything both buses share: subscription bookkeeping, on/once/off,
|
|
219
|
+
* categories, debug mode, and the emit contract. Subclasses supply only their
|
|
220
|
+
* transport (a client socket, or the Socket.IO server) via forwardToTransport().
|
|
221
|
+
*
|
|
222
|
+
* ## Subscription bookkeeping
|
|
223
|
+
*
|
|
224
|
+
* Three structures are kept in step by registerHandler()/forgetSubscription():
|
|
225
|
+
* - handlerSubscriptions: event → handler → subscriptions, so off(event, handler)
|
|
226
|
+
* can find and dispose exactly the right RxJS subscriptions.
|
|
227
|
+
* - subscriptionCountMap: event → live count, so hasSubscribers()/listenerCount()/
|
|
228
|
+
* hasListeners() report the truth instead of guessing.
|
|
229
|
+
* - subscriptions: a flat list of everything this bus owns, for bulk teardown.
|
|
230
|
+
*
|
|
231
|
+
* once() is keyed by the CALLER's handler, not by an internal wrapper. Keying by
|
|
232
|
+
* the wrapper (as an earlier version did) meant off() could never find the entry,
|
|
233
|
+
* so the RxJS subscription was never disposed and the "one-shot" listener kept
|
|
234
|
+
* firing on every later event. take(1) now disposes the subscription itself and
|
|
235
|
+
* the completion callback clears the registry entry.
|
|
236
|
+
*
|
|
237
|
+
* ## Emit contract
|
|
238
|
+
*
|
|
239
|
+
* Both buses behave identically: a bad event name, a null/undefined payload, or
|
|
240
|
+
* a payload that fails schema validation THROWS. Nothing is coerced, defaulted,
|
|
241
|
+
* or swallowed — a malformed event is a programming error and the caller needs
|
|
242
|
+
* to see it.
|
|
233
243
|
*/
|
|
234
244
|
export abstract class BaseEventBusImplementation implements EventBusBase {
|
|
235
245
|
protected eventSubject: Subject<EventMessage>;
|
|
236
|
-
|
|
237
|
-
|
|
246
|
+
|
|
247
|
+
/** Logger for this bus. Subclasses pass their own so output is tagged correctly. */
|
|
248
|
+
protected readonly busLogger: Logger;
|
|
249
|
+
|
|
250
|
+
/** Every subscription this bus owns, so teardown can dispose all of them. */
|
|
251
|
+
protected subscriptions: Subscription[] = [];
|
|
252
|
+
|
|
253
|
+
/** Category name → subscriptions, backing unsubscribeCategory(). */
|
|
254
|
+
protected categorySubscriptions: Map<string, Subscription[]> = new Map();
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* event → (caller's handler → its subscriptions).
|
|
258
|
+
* A handler may be registered more than once for the same event, so the
|
|
259
|
+
* value is a list; off() disposes all of them.
|
|
260
|
+
*/
|
|
261
|
+
protected handlerSubscriptions: Map<string, Map<EventHandler<any>, Subscription[]>> = new Map();
|
|
262
|
+
|
|
263
|
+
/** event → number of live per-event subscribers. */
|
|
238
264
|
protected subscriptionCountMap: Map<string, number> = new Map();
|
|
239
|
-
|
|
240
|
-
|
|
265
|
+
|
|
266
|
+
/** onAll() subscriptions, which are not tied to any single event name. */
|
|
267
|
+
protected allEventSubscriptions: Set<Subscription> = new Set();
|
|
268
|
+
|
|
269
|
+
private debugMode: boolean = false;
|
|
270
|
+
private debugSubscription: Subscription | null = null;
|
|
271
|
+
|
|
272
|
+
constructor(eventSubject: Subject<EventMessage>, busLogger: Logger) {
|
|
241
273
|
this.eventSubject = eventSubject;
|
|
274
|
+
this.busLogger = busLogger;
|
|
242
275
|
}
|
|
243
|
-
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Hand the event to this bus's transport (client socket / Socket.IO server).
|
|
279
|
+
* Called after the event has been validated and pushed to local subscribers.
|
|
280
|
+
*/
|
|
281
|
+
protected abstract forwardToTransport<K extends AnyEventName>(event: K, payload: PayloadOf<K>): void;
|
|
282
|
+
|
|
283
|
+
// ---------------------------------------------------------------------
|
|
284
|
+
// Emitting
|
|
285
|
+
// ---------------------------------------------------------------------
|
|
286
|
+
|
|
244
287
|
/**
|
|
245
|
-
* Emit an event
|
|
288
|
+
* Emit an event to local subscribers and this bus's transport.
|
|
289
|
+
*
|
|
290
|
+
* Throws if the event name is empty, if the payload is null/undefined, or
|
|
291
|
+
* if the payload does not match the BaseEventPayload schema. Build payloads
|
|
292
|
+
* with the helpers in EventPayloadSchema.
|
|
293
|
+
*
|
|
246
294
|
* @param event Event name
|
|
247
295
|
* @param payload Event payload
|
|
248
296
|
*/
|
|
249
297
|
public emit<K extends AnyEventName>(event: K, payload: PayloadOf<K>): void {
|
|
250
298
|
try {
|
|
251
|
-
// Validate event name
|
|
252
299
|
validateEventName(event);
|
|
253
|
-
|
|
254
|
-
// Validate payload is not null or undefined, which can cause client errors
|
|
300
|
+
|
|
255
301
|
if (payload === undefined || payload === null) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
if (isCriticalEvent(event)) {
|
|
263
|
-
} else {
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// Only emit if we have subscribers to avoid wasted processing
|
|
267
|
-
if (this.hasSubscribers(event)) {
|
|
268
|
-
this.eventSubject.next({ type: event, payload });
|
|
302
|
+
throw new Error(
|
|
303
|
+
`Event '${String(event)}' was emitted with a ${payload === null ? 'null' : 'undefined'} payload. ` +
|
|
304
|
+
`Build the payload with a helper from EventPayloadSchema.`
|
|
305
|
+
);
|
|
269
306
|
}
|
|
307
|
+
|
|
308
|
+
this.validateEventPayload(String(event), payload);
|
|
309
|
+
|
|
310
|
+
this.eventSubject.next({ type: event, payload });
|
|
311
|
+
|
|
312
|
+
this.forwardToTransport(event, payload);
|
|
270
313
|
} catch (error) {
|
|
271
|
-
|
|
314
|
+
this.busLogger.error(
|
|
315
|
+
`Error emitting event '${String(event)}': ${error instanceof Error ? error.message : String(error)}`
|
|
316
|
+
);
|
|
317
|
+
this.reportEmitFailure(event, error);
|
|
318
|
+
throw error;
|
|
272
319
|
}
|
|
273
320
|
}
|
|
274
|
-
|
|
321
|
+
|
|
275
322
|
/**
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
323
|
+
* Publish a properly-formed error event describing a failed emit.
|
|
324
|
+
*
|
|
325
|
+
* Pushed straight to the Subject rather than through emit() so a broken
|
|
326
|
+
* error payload cannot recurse. The payload uses createBaseEventPayload —
|
|
327
|
+
* the bus does not get to break the payload rules it enforces on callers.
|
|
279
328
|
*/
|
|
280
|
-
|
|
281
|
-
//
|
|
282
|
-
|
|
283
|
-
|
|
329
|
+
private reportEmitFailure(event: AnyEventName, error: unknown): void {
|
|
330
|
+
// Never report a failure to emit the error event with another error event.
|
|
331
|
+
if (event === Events.Agent.ERROR) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
const payload = createBaseEventPayload(
|
|
337
|
+
Events.Agent.ERROR,
|
|
338
|
+
'system',
|
|
339
|
+
'system',
|
|
340
|
+
{
|
|
341
|
+
error: error instanceof Error ? error.message : String(error),
|
|
342
|
+
event: String(event),
|
|
343
|
+
timestamp: Date.now()
|
|
344
|
+
},
|
|
345
|
+
{ source: this.constructor.name }
|
|
346
|
+
);
|
|
347
|
+
this.eventSubject.next({ type: Events.Agent.ERROR, payload });
|
|
348
|
+
} catch (reportingError) {
|
|
349
|
+
this.busLogger.error(
|
|
350
|
+
`Failed to publish the error event for '${String(event)}': ` +
|
|
351
|
+
`${reportingError instanceof Error ? reportingError.message : String(reportingError)}`
|
|
352
|
+
);
|
|
353
|
+
}
|
|
284
354
|
}
|
|
285
|
-
|
|
355
|
+
|
|
356
|
+
// ---------------------------------------------------------------------
|
|
357
|
+
// Subscribing
|
|
358
|
+
// ---------------------------------------------------------------------
|
|
359
|
+
|
|
286
360
|
/**
|
|
287
|
-
* Subscribe to an event
|
|
361
|
+
* Subscribe to an event.
|
|
362
|
+
*
|
|
288
363
|
* @param event Event name
|
|
289
364
|
* @param handler Event handler function
|
|
290
|
-
* @returns Subscription
|
|
365
|
+
* @returns Subscription; unsubscribe() disposes it, as does off(event, handler)
|
|
291
366
|
*/
|
|
292
367
|
public on<K extends AnyEventName>(event: K, handler: EventHandler<PayloadOf<K>>): Subscription {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
368
|
+
validateEventName(event);
|
|
369
|
+
|
|
370
|
+
const subscription = this.eventSubject
|
|
371
|
+
.pipe(
|
|
372
|
+
filter((e: EventMessage) => e.type === event),
|
|
373
|
+
map((e: EventMessage) => e.payload)
|
|
374
|
+
)
|
|
375
|
+
.subscribe({
|
|
376
|
+
// Isolate throws/rejections so one bad handler cannot kill the
|
|
377
|
+
// subscription or fail silently.
|
|
378
|
+
next: (payload) => invokeHandlerSafely(this.busLogger, event, handler, payload),
|
|
379
|
+
complete: () => this.forgetSubscription(event, handler, subscription)
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
this.registerHandler(event, handler, subscription);
|
|
383
|
+
|
|
384
|
+
return subscription;
|
|
296
385
|
}
|
|
297
|
-
|
|
386
|
+
|
|
298
387
|
/**
|
|
299
|
-
* Subscribe to an event
|
|
388
|
+
* Subscribe to an event for exactly one delivery.
|
|
389
|
+
*
|
|
390
|
+
* take(1) disposes the RxJS subscription after the first event, and the
|
|
391
|
+
* completion callback removes it from the registry, so nothing is left
|
|
392
|
+
* behind. off(event, handler) still works before the event arrives.
|
|
393
|
+
*
|
|
300
394
|
* @param event Event name
|
|
301
395
|
* @param handler Event handler function
|
|
302
|
-
* @returns Subscription
|
|
396
|
+
* @returns Subscription
|
|
303
397
|
*/
|
|
304
398
|
public once<K extends AnyEventName>(event: K, handler: EventHandler<PayloadOf<K>>): Subscription {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
399
|
+
validateEventName(event);
|
|
400
|
+
|
|
401
|
+
// The completion callback needs the Subscription, which only exists once
|
|
402
|
+
// subscribe() returns. With a plain Subject the source cannot emit during
|
|
403
|
+
// subscribe(), but this flag keeps the bookkeeping correct even if the
|
|
404
|
+
// Subject were ever swapped for a replaying one.
|
|
405
|
+
let registered = false;
|
|
406
|
+
let completedBeforeRegistration = false;
|
|
407
|
+
|
|
408
|
+
const subscription: Subscription = this.eventSubject
|
|
409
|
+
.pipe(
|
|
410
|
+
filter((e: EventMessage) => e.type === event),
|
|
411
|
+
take(1),
|
|
412
|
+
map((e: EventMessage) => e.payload)
|
|
413
|
+
)
|
|
414
|
+
.subscribe({
|
|
415
|
+
next: (payload) => invokeHandlerSafely(this.busLogger, event, handler, payload),
|
|
416
|
+
complete: () => {
|
|
417
|
+
if (registered) {
|
|
418
|
+
this.forgetSubscription(event, handler, subscription);
|
|
419
|
+
} else {
|
|
420
|
+
completedBeforeRegistration = true;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
this.registerHandler(event, handler, subscription);
|
|
426
|
+
registered = true;
|
|
427
|
+
|
|
428
|
+
if (completedBeforeRegistration) {
|
|
429
|
+
this.forgetSubscription(event, handler, subscription);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
return subscription;
|
|
308
433
|
}
|
|
309
|
-
|
|
434
|
+
|
|
310
435
|
/**
|
|
311
|
-
*
|
|
436
|
+
* Subscribe to every event on this bus.
|
|
437
|
+
*
|
|
438
|
+
* @param listener Receives the event type and the payload
|
|
439
|
+
* @returns Subscription
|
|
440
|
+
*/
|
|
441
|
+
public onAll(listener: (eventType: string, payload: any) => void): Subscription {
|
|
442
|
+
const subscription = this.eventSubject.subscribe({
|
|
443
|
+
next: (e: EventMessage) =>
|
|
444
|
+
invokeHandlerSafely(this.busLogger, e.type, (payload) => listener(e.type, payload), e.payload)
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
this.subscriptions.push(subscription);
|
|
448
|
+
this.allEventSubscriptions.add(subscription);
|
|
449
|
+
|
|
450
|
+
return subscription;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Subscribe to an event and tag the subscription with a category so a group
|
|
455
|
+
* of related subscriptions can be dropped together.
|
|
456
|
+
*
|
|
457
|
+
* @param category Category name for grouping subscriptions
|
|
312
458
|
* @param event Event name
|
|
313
|
-
* @param
|
|
314
|
-
*
|
|
315
|
-
* Note: For best results in RxJS-based EventBus, it's preferable to keep
|
|
316
|
-
* the Subscription object returned by on() and call subscription.unsubscribe() directly.
|
|
317
|
-
* This method is provided for compatibility with traditional event emitter patterns.
|
|
459
|
+
* @param listener Event handler function
|
|
460
|
+
* @returns Subscription
|
|
318
461
|
*/
|
|
319
|
-
public
|
|
320
|
-
|
|
321
|
-
|
|
462
|
+
public onWithCategory<T extends AnyEventName>(
|
|
463
|
+
category: string,
|
|
464
|
+
event: T,
|
|
465
|
+
listener: EventHandler<PayloadOf<T>>
|
|
466
|
+
): Subscription {
|
|
467
|
+
const subscription = this.on(event, listener);
|
|
468
|
+
|
|
469
|
+
const categorySubs = this.categorySubscriptions.get(category);
|
|
470
|
+
if (categorySubs) {
|
|
471
|
+
categorySubs.push(subscription);
|
|
472
|
+
} else {
|
|
473
|
+
this.categorySubscriptions.set(category, [subscription]);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
return subscription;
|
|
322
477
|
}
|
|
323
|
-
|
|
478
|
+
|
|
479
|
+
// ---------------------------------------------------------------------
|
|
480
|
+
// Unsubscribing
|
|
481
|
+
// ---------------------------------------------------------------------
|
|
482
|
+
|
|
324
483
|
/**
|
|
325
|
-
*
|
|
326
|
-
*
|
|
484
|
+
* Unsubscribe from an event.
|
|
485
|
+
*
|
|
327
486
|
* @param event Event name
|
|
328
|
-
* @
|
|
487
|
+
* @param handler The handler passed to on()/once(). Omit to drop every
|
|
488
|
+
* handler registered for this event.
|
|
489
|
+
*/
|
|
490
|
+
public off<K extends AnyEventName>(event: K, handler?: EventHandler<any>): void {
|
|
491
|
+
validateEventName(event);
|
|
492
|
+
|
|
493
|
+
const eventHandlers = this.handlerSubscriptions.get(event);
|
|
494
|
+
if (!eventHandlers) {
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
if (handler) {
|
|
499
|
+
const subs = eventHandlers.get(handler);
|
|
500
|
+
if (!subs) {
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
// Copy: forgetSubscription() mutates the list we are walking.
|
|
504
|
+
for (const subscription of [...subs]) {
|
|
505
|
+
subscription.unsubscribe();
|
|
506
|
+
this.forgetSubscription(event, handler, subscription);
|
|
507
|
+
}
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
for (const [registeredHandler, subs] of Array.from(eventHandlers.entries())) {
|
|
512
|
+
for (const subscription of [...subs]) {
|
|
513
|
+
subscription.unsubscribe();
|
|
514
|
+
this.forgetSubscription(event, registeredHandler, subscription);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Unsubscribe every subscription tagged with a category.
|
|
521
|
+
*
|
|
522
|
+
* @param category Category name
|
|
523
|
+
* @returns this (for chaining)
|
|
524
|
+
*/
|
|
525
|
+
public unsubscribeCategory(category: string): this {
|
|
526
|
+
const categorySubs = this.categorySubscriptions.get(category);
|
|
527
|
+
if (!categorySubs) {
|
|
528
|
+
return this;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
for (const subscription of categorySubs) {
|
|
532
|
+
subscription.unsubscribe();
|
|
533
|
+
this.forgetSubscriptionEverywhere(subscription);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
this.categorySubscriptions.delete(category);
|
|
537
|
+
|
|
538
|
+
return this;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Remove listeners for one event, or every listener this bus owns.
|
|
543
|
+
*
|
|
544
|
+
* Only subscriptions this bus created are disposed. An earlier version
|
|
545
|
+
* cleared the Subject's observer list directly, which reached across into
|
|
546
|
+
* the other bus's subscribers (both buses share one Subject) and left the
|
|
547
|
+
* Subscription objects stranded.
|
|
548
|
+
*
|
|
549
|
+
* @param event Optional event name; omit to remove everything
|
|
550
|
+
* @returns this (for chaining)
|
|
551
|
+
*/
|
|
552
|
+
public removeAllListeners(event?: AnyEventName): this {
|
|
553
|
+
if (event !== undefined) {
|
|
554
|
+
this.off(event);
|
|
555
|
+
return this;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
for (const registeredEvent of Array.from(this.handlerSubscriptions.keys())) {
|
|
559
|
+
this.off(registeredEvent);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
for (const subscription of Array.from(this.allEventSubscriptions)) {
|
|
563
|
+
subscription.unsubscribe();
|
|
564
|
+
this.forgetSubscriptionEverywhere(subscription);
|
|
565
|
+
}
|
|
566
|
+
this.allEventSubscriptions.clear();
|
|
567
|
+
|
|
568
|
+
// Anything still tracked (e.g. a subscription pushed by a subclass) is
|
|
569
|
+
// disposed here so nothing outlives the call.
|
|
570
|
+
for (const subscription of [...this.subscriptions]) {
|
|
571
|
+
subscription.unsubscribe();
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
this.subscriptions = [];
|
|
575
|
+
this.categorySubscriptions.clear();
|
|
576
|
+
this.handlerSubscriptions.clear();
|
|
577
|
+
this.subscriptionCountMap.clear();
|
|
578
|
+
this.debugSubscription = null;
|
|
579
|
+
this.debugMode = false;
|
|
580
|
+
|
|
581
|
+
return this;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// ---------------------------------------------------------------------
|
|
585
|
+
// Introspection
|
|
586
|
+
// ---------------------------------------------------------------------
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Whether any handler is subscribed to this event.
|
|
590
|
+
* onAll() subscribers are not counted — they are not tied to an event name.
|
|
591
|
+
*/
|
|
592
|
+
public hasSubscribers(event: AnyEventName): boolean {
|
|
593
|
+
return this.listenerCount(event) > 0;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Number of handlers subscribed to this event.
|
|
329
598
|
*/
|
|
330
599
|
public listenerCount(event: AnyEventName): number {
|
|
331
|
-
|
|
332
|
-
// This is a limitation of our current implementation
|
|
333
|
-
logger.warn('[EventBus] listenerCount is not fully supported with RxJS');
|
|
334
|
-
return this.hasListeners(event) ? 1 : 0;
|
|
600
|
+
return this.subscriptionCountMap.get(event) ?? 0;
|
|
335
601
|
}
|
|
336
|
-
|
|
602
|
+
|
|
337
603
|
/**
|
|
338
|
-
*
|
|
339
|
-
* Note: This is an approximation as RxJS doesn't expose subscriber counts directly
|
|
340
|
-
* @param event Event name
|
|
341
|
-
* @returns true if the event has listeners
|
|
604
|
+
* Whether this event has listeners. Alias of hasSubscribers().
|
|
342
605
|
*/
|
|
343
606
|
public hasListeners(event: AnyEventName): boolean {
|
|
344
|
-
|
|
345
|
-
// This is a limitation of our current implementation
|
|
346
|
-
logger.warn('[EventBus] hasListeners is not fully supported with RxJS');
|
|
347
|
-
return true; // We don't know for sure, so assume there might be listeners
|
|
607
|
+
return this.hasSubscribers(event);
|
|
348
608
|
}
|
|
349
|
-
|
|
609
|
+
|
|
350
610
|
/**
|
|
351
|
-
*
|
|
352
|
-
|
|
353
|
-
|
|
611
|
+
* Number of onAll() subscribers on this bus.
|
|
612
|
+
*/
|
|
613
|
+
public allListenerCount(): number {
|
|
614
|
+
return this.allEventSubscriptions.size;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Log every event passing through this bus at debug level.
|
|
619
|
+
*
|
|
620
|
+
* @param enabled Whether debug mode should be on
|
|
354
621
|
* @returns this (for chaining)
|
|
355
622
|
*/
|
|
356
|
-
public
|
|
357
|
-
|
|
623
|
+
public enableDebugMode(enabled: boolean = true): this {
|
|
624
|
+
if (this.debugMode === enabled) {
|
|
625
|
+
return this;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
this.debugMode = enabled;
|
|
629
|
+
|
|
630
|
+
if (enabled) {
|
|
631
|
+
this.debugSubscription = this.onAll((eventType, payload) => {
|
|
632
|
+
this.busLogger.debug(`event '${eventType}': ${JSON.stringify(payload)}`);
|
|
633
|
+
});
|
|
634
|
+
return this;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
if (this.debugSubscription) {
|
|
638
|
+
this.debugSubscription.unsubscribe();
|
|
639
|
+
this.allEventSubscriptions.delete(this.debugSubscription);
|
|
640
|
+
this.forgetSubscriptionEverywhere(this.debugSubscription);
|
|
641
|
+
this.debugSubscription = null;
|
|
642
|
+
}
|
|
643
|
+
|
|
358
644
|
return this;
|
|
359
645
|
}
|
|
360
|
-
|
|
646
|
+
|
|
647
|
+
// ---------------------------------------------------------------------
|
|
648
|
+
// Internal bookkeeping
|
|
649
|
+
// ---------------------------------------------------------------------
|
|
650
|
+
|
|
651
|
+
/** Record a new per-event subscription across all three structures. */
|
|
652
|
+
private registerHandler(
|
|
653
|
+
event: AnyEventName,
|
|
654
|
+
handler: EventHandler<any>,
|
|
655
|
+
subscription: Subscription
|
|
656
|
+
): void {
|
|
657
|
+
this.subscriptions.push(subscription);
|
|
658
|
+
|
|
659
|
+
let eventHandlers = this.handlerSubscriptions.get(event);
|
|
660
|
+
if (!eventHandlers) {
|
|
661
|
+
eventHandlers = new Map();
|
|
662
|
+
this.handlerSubscriptions.set(event, eventHandlers);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
const existing = eventHandlers.get(handler);
|
|
666
|
+
if (existing) {
|
|
667
|
+
existing.push(subscription);
|
|
668
|
+
} else {
|
|
669
|
+
eventHandlers.set(handler, [subscription]);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
this.subscriptionCountMap.set(event, this.listenerCount(event) + 1);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Drop a per-event subscription from the registry. Idempotent: the count is
|
|
677
|
+
* only decremented when the entry was actually present, so a subscription
|
|
678
|
+
* that both completes and is off()'d is not counted twice.
|
|
679
|
+
*/
|
|
680
|
+
private forgetSubscription(
|
|
681
|
+
event: AnyEventName,
|
|
682
|
+
handler: EventHandler<any>,
|
|
683
|
+
subscription: Subscription
|
|
684
|
+
): void {
|
|
685
|
+
const eventHandlers = this.handlerSubscriptions.get(event);
|
|
686
|
+
if (eventHandlers) {
|
|
687
|
+
const subs = eventHandlers.get(handler);
|
|
688
|
+
if (subs) {
|
|
689
|
+
const subIndex = subs.indexOf(subscription);
|
|
690
|
+
if (subIndex !== -1) {
|
|
691
|
+
subs.splice(subIndex, 1);
|
|
692
|
+
this.subscriptionCountMap.set(event, Math.max(0, this.listenerCount(event) - 1));
|
|
693
|
+
}
|
|
694
|
+
if (subs.length === 0) {
|
|
695
|
+
eventHandlers.delete(handler);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
if (eventHandlers.size === 0) {
|
|
699
|
+
this.handlerSubscriptions.delete(event);
|
|
700
|
+
this.subscriptionCountMap.delete(event);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const index = this.subscriptions.indexOf(subscription);
|
|
705
|
+
if (index !== -1) {
|
|
706
|
+
this.subscriptions.splice(index, 1);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* Drop a subscription whose event/handler keys are not known to the caller
|
|
712
|
+
* (category and onAll subscriptions).
|
|
713
|
+
*/
|
|
714
|
+
private forgetSubscriptionEverywhere(subscription: Subscription): void {
|
|
715
|
+
for (const [event, eventHandlers] of Array.from(this.handlerSubscriptions.entries())) {
|
|
716
|
+
for (const [handler, subs] of Array.from(eventHandlers.entries())) {
|
|
717
|
+
if (subs.includes(subscription)) {
|
|
718
|
+
this.forgetSubscription(event, handler, subscription);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
this.allEventSubscriptions.delete(subscription);
|
|
724
|
+
|
|
725
|
+
const index = this.subscriptions.indexOf(subscription);
|
|
726
|
+
if (index !== -1) {
|
|
727
|
+
this.subscriptions.splice(index, 1);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// ---------------------------------------------------------------------
|
|
732
|
+
// Validation
|
|
733
|
+
// ---------------------------------------------------------------------
|
|
734
|
+
|
|
361
735
|
/**
|
|
362
736
|
* Validate event payload to ensure it follows the expected schema
|
|
363
737
|
* @param event Event name
|
|
@@ -365,26 +739,17 @@ export abstract class BaseEventBusImplementation implements EventBusBase {
|
|
|
365
739
|
* @returns true if valid, throws error if invalid
|
|
366
740
|
*/
|
|
367
741
|
protected validateEventPayload(event: string, payload: any): boolean {
|
|
368
|
-
// Log event details to help track events without proper schemas
|
|
369
|
-
// eventName: event,
|
|
370
|
-
// payloadType: typeof payload,
|
|
371
|
-
// hasAgentId: typeof payload === 'object' && payload !== null && 'agentId' in payload,
|
|
372
|
-
// hasChannelId: typeof payload === 'object' && payload !== null && 'channelId' in payload,
|
|
373
|
-
// hasEventId: typeof payload === 'object' && payload !== null && 'eventId' in payload,
|
|
374
|
-
// hasData: typeof payload === 'object' && payload !== null && 'data' in payload
|
|
375
|
-
// });
|
|
376
|
-
|
|
377
742
|
// Use validation utility for fail-fast behavior - all events must follow BaseEventPayload structure
|
|
378
743
|
const validator = createStrictValidator('EventBus');
|
|
379
|
-
|
|
744
|
+
|
|
380
745
|
try {
|
|
381
746
|
// All events must have the standard BaseEventPayload structure
|
|
382
747
|
validator.assertIsEventPayload(payload);
|
|
383
|
-
|
|
748
|
+
|
|
384
749
|
// Additional validation for eventId and data fields
|
|
385
750
|
validator.assertHasProperty(payload, 'eventId', 'EventPayload must have eventId field');
|
|
386
751
|
validator.assertHasProperty(payload, 'data', 'EventPayload must have data field');
|
|
387
|
-
|
|
752
|
+
|
|
388
753
|
// Data-specific validation for message events
|
|
389
754
|
// Channel message events (should validate as ChannelMessage)
|
|
390
755
|
const channelMessageEvents = [
|
|
@@ -393,24 +758,22 @@ export abstract class BaseEventBusImplementation implements EventBusBase {
|
|
|
393
758
|
MessageEvents.SYSTEM_MESSAGE,
|
|
394
759
|
MessageEvents.SYSTEM_MESSAGE_DELIVERED,
|
|
395
760
|
];
|
|
396
|
-
|
|
397
|
-
// Agent message events (should validate as AgentMessage)
|
|
761
|
+
|
|
762
|
+
// Agent message events (should validate as AgentMessage)
|
|
398
763
|
const agentMessageEvents = [
|
|
399
764
|
MessageEvents.AGENT_MESSAGE_DELIVERED,
|
|
400
765
|
MessageEvents.AGENT_MESSAGE,
|
|
401
766
|
MessageEvents.LLM_MESSAGE
|
|
402
767
|
];
|
|
403
|
-
|
|
768
|
+
|
|
404
769
|
if (channelMessageEvents.includes(event)) {
|
|
405
770
|
validator.assertIsChannelMessage(payload.data);
|
|
406
771
|
} else if (agentMessageEvents.includes(event)) {
|
|
407
772
|
validator.assertIsAgentMessage(payload.data);
|
|
408
773
|
}
|
|
409
|
-
|
|
774
|
+
|
|
410
775
|
return true;
|
|
411
776
|
} catch (error) {
|
|
412
|
-
// For rejected events, log detailed payload information
|
|
413
|
-
|
|
414
777
|
// Log the rejection and re-throw for fail-fast behavior
|
|
415
778
|
logger.error(`[EventBus] Rejecting non-standard payload for event ${event}: ${error instanceof Error ? error.message : 'Non-standard payload for event ' + event + ' - all events MUST use proper schema'}`);
|
|
416
779
|
throw new Error(`Invalid payload for event ${event}: ${error instanceof Error ? error.message : String(error)}`);
|