@holoscript/framework 6.0.3

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 (329) hide show
  1. package/ALL-test-results.json +1 -0
  2. package/CHANGELOG.md +8 -0
  3. package/LICENSE +21 -0
  4. package/ROADMAP.md +175 -0
  5. package/dist/AgentManifest-CB4xM-Ma.d.cts +704 -0
  6. package/dist/AgentManifest-CB4xM-Ma.d.ts +704 -0
  7. package/dist/BehaviorTree-BrBFECv5.d.cts +103 -0
  8. package/dist/BehaviorTree-BrBFECv5.d.ts +103 -0
  9. package/dist/InvisibleWallet-BB6tFvRA.d.cts +1732 -0
  10. package/dist/InvisibleWallet-rtRrBOA8.d.ts +1732 -0
  11. package/dist/OrchestratorAgent-BvWgf9uw.d.cts +798 -0
  12. package/dist/OrchestratorAgent-Q_CbVTmO.d.ts +798 -0
  13. package/dist/agents/index.cjs +4790 -0
  14. package/dist/agents/index.d.cts +1788 -0
  15. package/dist/agents/index.d.ts +1788 -0
  16. package/dist/agents/index.js +4695 -0
  17. package/dist/ai/index.cjs +5347 -0
  18. package/dist/ai/index.d.cts +1753 -0
  19. package/dist/ai/index.d.ts +1753 -0
  20. package/dist/ai/index.js +5244 -0
  21. package/dist/behavior.cjs +449 -0
  22. package/dist/behavior.d.cts +130 -0
  23. package/dist/behavior.d.ts +130 -0
  24. package/dist/behavior.js +407 -0
  25. package/dist/economy/index.cjs +3659 -0
  26. package/dist/economy/index.d.cts +747 -0
  27. package/dist/economy/index.d.ts +747 -0
  28. package/dist/economy/index.js +3617 -0
  29. package/dist/implementations-D9T3un9D.d.cts +236 -0
  30. package/dist/implementations-D9T3un9D.d.ts +236 -0
  31. package/dist/index.cjs +24550 -0
  32. package/dist/index.d.cts +1729 -0
  33. package/dist/index.d.ts +1729 -0
  34. package/dist/index.js +24277 -0
  35. package/dist/learning/index.cjs +219 -0
  36. package/dist/learning/index.d.cts +104 -0
  37. package/dist/learning/index.d.ts +104 -0
  38. package/dist/learning/index.js +189 -0
  39. package/dist/negotiation/index.cjs +970 -0
  40. package/dist/negotiation/index.d.cts +610 -0
  41. package/dist/negotiation/index.d.ts +610 -0
  42. package/dist/negotiation/index.js +931 -0
  43. package/dist/skills/index.cjs +1118 -0
  44. package/dist/skills/index.d.cts +289 -0
  45. package/dist/skills/index.d.ts +289 -0
  46. package/dist/skills/index.js +1079 -0
  47. package/dist/swarm/index.cjs +5268 -0
  48. package/dist/swarm/index.d.cts +2433 -0
  49. package/dist/swarm/index.d.ts +2433 -0
  50. package/dist/swarm/index.js +5221 -0
  51. package/dist/training/index.cjs +2745 -0
  52. package/dist/training/index.d.cts +1734 -0
  53. package/dist/training/index.d.ts +1734 -0
  54. package/dist/training/index.js +2687 -0
  55. package/extract-failures.js +10 -0
  56. package/package.json +82 -0
  57. package/src/__tests__/bounty-marketplace.test.ts +374 -0
  58. package/src/__tests__/delegation.test.ts +144 -0
  59. package/src/__tests__/distributed-claimer.test.ts +147 -0
  60. package/src/__tests__/done-log-audit.test.ts +342 -0
  61. package/src/__tests__/framework.test.ts +865 -0
  62. package/src/__tests__/goal-synthesizer.test.ts +236 -0
  63. package/src/__tests__/presence.test.ts +223 -0
  64. package/src/__tests__/protocol-agent.test.ts +254 -0
  65. package/src/__tests__/revenue-splitter.test.ts +114 -0
  66. package/src/__tests__/scenario-driven-todo.test.ts +197 -0
  67. package/src/__tests__/self-improve.test.ts +349 -0
  68. package/src/__tests__/service-lifecycle.test.ts +237 -0
  69. package/src/__tests__/skill-router.test.ts +121 -0
  70. package/src/agents/AgentManifest.ts +493 -0
  71. package/src/agents/AgentRegistry.ts +475 -0
  72. package/src/agents/AgentTypes.ts +585 -0
  73. package/src/agents/AgentWalletRegistry.ts +83 -0
  74. package/src/agents/AuthenticatedCRDT.ts +388 -0
  75. package/src/agents/CapabilityMatcher.ts +453 -0
  76. package/src/agents/CrossRealityHandoff.ts +305 -0
  77. package/src/agents/CulturalMemory.ts +454 -0
  78. package/src/agents/FederatedRegistryAdapter.ts +429 -0
  79. package/src/agents/NormEngine.ts +450 -0
  80. package/src/agents/OrchestratorAgent.ts +414 -0
  81. package/src/agents/SkillWorkflowEngine.ts +472 -0
  82. package/src/agents/TaskDelegationService.ts +551 -0
  83. package/src/agents/__tests__/AgentManifest.prod.test.ts +134 -0
  84. package/src/agents/__tests__/AgentManifest.test.ts +182 -0
  85. package/src/agents/__tests__/AgentModule.test.ts +864 -0
  86. package/src/agents/__tests__/AgentRegistry.prod.test.ts +125 -0
  87. package/src/agents/__tests__/AgentRegistry.test.ts +148 -0
  88. package/src/agents/__tests__/AgentTypes.test.ts +534 -0
  89. package/src/agents/__tests__/AgentWalletRegistry.test.ts +152 -0
  90. package/src/agents/__tests__/AuthenticatedCRDT.test.ts +558 -0
  91. package/src/agents/__tests__/CapabilityMatcher.prod.test.ts +117 -0
  92. package/src/agents/__tests__/CapabilityMatcher.test.ts +178 -0
  93. package/src/agents/__tests__/CrossRealityHandoff.test.ts +402 -0
  94. package/src/agents/__tests__/CulturalMemory.test.ts +200 -0
  95. package/src/agents/__tests__/FederatedRegistryAdapter.test.ts +409 -0
  96. package/src/agents/__tests__/NormEngine.test.ts +276 -0
  97. package/src/agents/__tests__/OrchestratorAgent.test.ts +182 -0
  98. package/src/agents/__tests__/SkillWorkflowEngine.test.ts +357 -0
  99. package/src/agents/__tests__/TaskDelegationService.test.ts +446 -0
  100. package/src/agents/index.ts +107 -0
  101. package/src/agents/spatial-comms/Layer1RealTime.ts +621 -0
  102. package/src/agents/spatial-comms/Layer2A2A.ts +661 -0
  103. package/src/agents/spatial-comms/Layer3MCP.ts +651 -0
  104. package/src/agents/spatial-comms/ProtocolTypes.ts +543 -0
  105. package/src/agents/spatial-comms/SpatialCommClient.ts +483 -0
  106. package/src/agents/spatial-comms/__tests__/performance-benchmark.test.ts +465 -0
  107. package/src/agents/spatial-comms/examples/multi-agent-world-creation.ts +409 -0
  108. package/src/agents/spatial-comms/index.ts +66 -0
  109. package/src/ai/AIAdapter.ts +313 -0
  110. package/src/ai/AICopilot.ts +331 -0
  111. package/src/ai/AIOutputValidator.ts +203 -0
  112. package/src/ai/BTNodes.ts +239 -0
  113. package/src/ai/BehaviorSelector.ts +135 -0
  114. package/src/ai/BehaviorTree.ts +153 -0
  115. package/src/ai/Blackboard.ts +165 -0
  116. package/src/ai/GenerationAnalytics.ts +461 -0
  117. package/src/ai/GenerationCache.ts +265 -0
  118. package/src/ai/GoalPlanner.ts +165 -0
  119. package/src/ai/HoloScriptGenerator.ts +580 -0
  120. package/src/ai/InfluenceMap.ts +180 -0
  121. package/src/ai/NavMesh.ts +168 -0
  122. package/src/ai/PerceptionSystem.ts +178 -0
  123. package/src/ai/PromptTemplates.ts +453 -0
  124. package/src/ai/SemanticSearchService.ts +80 -0
  125. package/src/ai/StateMachine.ts +196 -0
  126. package/src/ai/SteeringBehavior.ts +150 -0
  127. package/src/ai/SteeringBehaviors.ts +244 -0
  128. package/src/ai/TrainingDataGenerator.ts +1082 -0
  129. package/src/ai/UtilityAI.ts +145 -0
  130. package/src/ai/__tests__/AIAdapter.prod.test.ts +259 -0
  131. package/src/ai/__tests__/AIAdapter.test.ts +109 -0
  132. package/src/ai/__tests__/AICopilot.prod.test.ts +341 -0
  133. package/src/ai/__tests__/AICopilot.test.ts +178 -0
  134. package/src/ai/__tests__/AIOutputValidator.prod.test.ts +226 -0
  135. package/src/ai/__tests__/AIOutputValidator.test.ts +138 -0
  136. package/src/ai/__tests__/BTNodes.prod.test.ts +391 -0
  137. package/src/ai/__tests__/BTNodes.test.ts +263 -0
  138. package/src/ai/__tests__/BehaviorSelector.prod.test.ts +129 -0
  139. package/src/ai/__tests__/BehaviorSelector.test.ts +132 -0
  140. package/src/ai/__tests__/BehaviorTree.prod.test.ts +266 -0
  141. package/src/ai/__tests__/BehaviorTree.test.ts +216 -0
  142. package/src/ai/__tests__/Blackboard.prod.test.ts +339 -0
  143. package/src/ai/__tests__/Blackboard.test.ts +183 -0
  144. package/src/ai/__tests__/GenerationAnalytics.prod.test.ts +141 -0
  145. package/src/ai/__tests__/GenerationAnalytics.test.ts +165 -0
  146. package/src/ai/__tests__/GenerationCache.prod.test.ts +144 -0
  147. package/src/ai/__tests__/GenerationCache.test.ts +171 -0
  148. package/src/ai/__tests__/GoalPlanner.prod.test.ts +189 -0
  149. package/src/ai/__tests__/GoalPlanner.test.ts +137 -0
  150. package/src/ai/__tests__/GoalPlannerDepth.prod.test.ts +217 -0
  151. package/src/ai/__tests__/HoloScriptGenerator.test.ts +125 -0
  152. package/src/ai/__tests__/InfluenceMap.prod.test.ts +146 -0
  153. package/src/ai/__tests__/InfluenceMap.test.ts +149 -0
  154. package/src/ai/__tests__/NavMesh.prod.test.ts +141 -0
  155. package/src/ai/__tests__/NavMesh.test.ts +159 -0
  156. package/src/ai/__tests__/PerceptionSystem.prod.test.ts +135 -0
  157. package/src/ai/__tests__/PerceptionSystem.test.ts +250 -0
  158. package/src/ai/__tests__/PromptTemplates.prod.test.ts +313 -0
  159. package/src/ai/__tests__/PromptTemplates.test.ts +146 -0
  160. package/src/ai/__tests__/SemanticSearch.test.ts +37 -0
  161. package/src/ai/__tests__/StateMachine.prod.test.ts +162 -0
  162. package/src/ai/__tests__/StateMachine.test.ts +163 -0
  163. package/src/ai/__tests__/SteeringBehavior.prod.test.ts +251 -0
  164. package/src/ai/__tests__/SteeringBehavior.test.ts +135 -0
  165. package/src/ai/__tests__/SteeringBehaviors.prod.test.ts +133 -0
  166. package/src/ai/__tests__/SteeringBehaviors.test.ts +151 -0
  167. package/src/ai/__tests__/TrainingDataGenerator.prod.test.ts +286 -0
  168. package/src/ai/__tests__/TrainingDataGenerator.test.ts +286 -0
  169. package/src/ai/__tests__/UtilityAI.prod.test.ts +207 -0
  170. package/src/ai/__tests__/UtilityAI.test.ts +155 -0
  171. package/src/ai/__tests__/adapters.prod.test.ts +263 -0
  172. package/src/ai/__tests__/adapters.test.ts +320 -0
  173. package/src/ai/adapters.ts +1585 -0
  174. package/src/ai/index.ts +130 -0
  175. package/src/behavior/BehaviorPresets.ts +140 -0
  176. package/src/behavior/BehaviorTree.ts +236 -0
  177. package/src/behavior/StateMachine.ts +176 -0
  178. package/src/behavior/StateTrait.ts +67 -0
  179. package/src/behavior/index.ts +8 -0
  180. package/src/behavior.ts +8 -0
  181. package/src/board/audit.ts +284 -0
  182. package/src/board/board-ops.ts +336 -0
  183. package/src/board/board-types.ts +302 -0
  184. package/src/board/index.ts +69 -0
  185. package/src/define-agent.ts +46 -0
  186. package/src/define-team.ts +33 -0
  187. package/src/delegation.ts +265 -0
  188. package/src/distributed-claimer.ts +228 -0
  189. package/src/economy/AgentBudgetEnforcer.ts +464 -0
  190. package/src/economy/BountyManager.ts +185 -0
  191. package/src/economy/CreatorRevenueAggregator.ts +460 -0
  192. package/src/economy/InvisibleWallet.ts +82 -0
  193. package/src/economy/KnowledgeMarketplace.ts +193 -0
  194. package/src/economy/PaymentWebhookService.ts +512 -0
  195. package/src/economy/RevenueSplitter.ts +156 -0
  196. package/src/economy/SubscriptionManager.ts +546 -0
  197. package/src/economy/UnifiedBudgetOptimizer.ts +635 -0
  198. package/src/economy/UsageMeter.ts +440 -0
  199. package/src/economy/_core-stubs.ts +219 -0
  200. package/src/economy/index.ts +100 -0
  201. package/src/economy/x402-facilitator.ts +1978 -0
  202. package/src/index.ts +348 -0
  203. package/src/knowledge/__tests__/knowledge-consolidator.test.ts +444 -0
  204. package/src/knowledge/__tests__/knowledge-store-vector.test.ts +291 -0
  205. package/src/knowledge/brain.ts +167 -0
  206. package/src/knowledge/consolidation.ts +581 -0
  207. package/src/knowledge/knowledge-consolidator.ts +510 -0
  208. package/src/knowledge/knowledge-store.ts +616 -0
  209. package/src/learning/MemoryConsolidator.ts +102 -0
  210. package/src/learning/MemoryScorer.ts +69 -0
  211. package/src/learning/ProceduralCompiler.ts +45 -0
  212. package/src/learning/SemanticClusterer.ts +66 -0
  213. package/src/learning/index.ts +8 -0
  214. package/src/llm/llm-adapter.ts +159 -0
  215. package/src/mesh/index.ts +309 -0
  216. package/src/negotiation/NegotiationProtocol.ts +694 -0
  217. package/src/negotiation/NegotiationTypes.ts +473 -0
  218. package/src/negotiation/VotingMechanisms.ts +691 -0
  219. package/src/negotiation/index.ts +49 -0
  220. package/src/protocol/goal-synthesizer.ts +317 -0
  221. package/src/protocol/implementations.ts +474 -0
  222. package/src/protocol/micro-phase-decomposer.ts +299 -0
  223. package/src/protocol/micro-step-decomposer.test.ts +306 -0
  224. package/src/protocol-agent.test.ts +353 -0
  225. package/src/protocol-agent.ts +670 -0
  226. package/src/self-improve/absorb-scanner.ts +252 -0
  227. package/src/self-improve/evolution-engine.ts +149 -0
  228. package/src/self-improve/framework-absorber.ts +214 -0
  229. package/src/self-improve/index.ts +50 -0
  230. package/src/self-improve/prompt-optimizer.ts +212 -0
  231. package/src/self-improve/test-generator.ts +175 -0
  232. package/src/skill-router.ts +186 -0
  233. package/src/skills/index.ts +5 -0
  234. package/src/skills/skill-md-bridge.ts +1699 -0
  235. package/src/swarm/ACOEngine.ts +261 -0
  236. package/src/swarm/CollectiveIntelligence.ts +383 -0
  237. package/src/swarm/ContributionSynthesizer.ts +481 -0
  238. package/src/swarm/LeaderElection.ts +393 -0
  239. package/src/swarm/PSOEngine.ts +206 -0
  240. package/src/swarm/QuorumPolicy.ts +173 -0
  241. package/src/swarm/SwarmCoordinator.ts +335 -0
  242. package/src/swarm/SwarmManager.ts +442 -0
  243. package/src/swarm/SwarmMembership.ts +456 -0
  244. package/src/swarm/VotingRound.ts +255 -0
  245. package/src/swarm/__tests__/ACOEngine.prod.test.ts +164 -0
  246. package/src/swarm/__tests__/ACOEngine.test.ts +117 -0
  247. package/src/swarm/__tests__/CollectiveIntelligence.prod.test.ts +296 -0
  248. package/src/swarm/__tests__/CollectiveIntelligence.test.ts +457 -0
  249. package/src/swarm/__tests__/ContributionSynthesizer.prod.test.ts +269 -0
  250. package/src/swarm/__tests__/ContributionSynthesizer.test.ts +254 -0
  251. package/src/swarm/__tests__/LeaderElection.prod.test.ts +196 -0
  252. package/src/swarm/__tests__/LeaderElection.test.ts +151 -0
  253. package/src/swarm/__tests__/PSOEngine.prod.test.ts +162 -0
  254. package/src/swarm/__tests__/PSOEngine.test.ts +106 -0
  255. package/src/swarm/__tests__/QuorumPolicy.prod.test.ts +216 -0
  256. package/src/swarm/__tests__/QuorumPolicy.test.ts +177 -0
  257. package/src/swarm/__tests__/SwarmCoordinator.prod.test.ts +186 -0
  258. package/src/swarm/__tests__/SwarmCoordinator.test.ts +167 -0
  259. package/src/swarm/__tests__/SwarmManager.prod.test.ts +308 -0
  260. package/src/swarm/__tests__/SwarmManager.test.ts +373 -0
  261. package/src/swarm/__tests__/SwarmMembership.prod.test.ts +273 -0
  262. package/src/swarm/__tests__/SwarmMembership.test.ts +264 -0
  263. package/src/swarm/__tests__/VotingRound.prod.test.ts +233 -0
  264. package/src/swarm/__tests__/VotingRound.test.ts +174 -0
  265. package/src/swarm/analytics/SwarmInspector.ts +476 -0
  266. package/src/swarm/analytics/SwarmMetrics.ts +449 -0
  267. package/src/swarm/analytics/__tests__/SwarmInspector.prod.test.ts +366 -0
  268. package/src/swarm/analytics/__tests__/SwarmInspector.test.ts +454 -0
  269. package/src/swarm/analytics/__tests__/SwarmMetrics.prod.test.ts +254 -0
  270. package/src/swarm/analytics/__tests__/SwarmMetrics.test.ts +370 -0
  271. package/src/swarm/analytics/index.ts +7 -0
  272. package/src/swarm/index.ts +69 -0
  273. package/src/swarm/messaging/BroadcastChannel.ts +509 -0
  274. package/src/swarm/messaging/GossipProtocol.ts +565 -0
  275. package/src/swarm/messaging/SwarmEventBus.ts +443 -0
  276. package/src/swarm/messaging/__tests__/BroadcastChannel.prod.test.ts +331 -0
  277. package/src/swarm/messaging/__tests__/BroadcastChannel.test.ts +333 -0
  278. package/src/swarm/messaging/__tests__/GossipProtocol.prod.test.ts +356 -0
  279. package/src/swarm/messaging/__tests__/GossipProtocol.test.ts +437 -0
  280. package/src/swarm/messaging/__tests__/SwarmEventBus.prod.test.ts +191 -0
  281. package/src/swarm/messaging/__tests__/SwarmEventBus.test.ts +247 -0
  282. package/src/swarm/messaging/index.ts +8 -0
  283. package/src/swarm/spatial/FlockingBehavior.ts +462 -0
  284. package/src/swarm/spatial/FormationController.ts +500 -0
  285. package/src/swarm/spatial/Vector3.ts +170 -0
  286. package/src/swarm/spatial/ZoneClaiming.ts +509 -0
  287. package/src/swarm/spatial/__tests__/FlockingBehavior.prod.test.ts +239 -0
  288. package/src/swarm/spatial/__tests__/FlockingBehavior.test.ts +298 -0
  289. package/src/swarm/spatial/__tests__/FormationController.prod.test.ts +240 -0
  290. package/src/swarm/spatial/__tests__/FormationController.test.ts +297 -0
  291. package/src/swarm/spatial/__tests__/Vector3.prod.test.ts +283 -0
  292. package/src/swarm/spatial/__tests__/Vector3.test.ts +224 -0
  293. package/src/swarm/spatial/__tests__/ZoneClaiming.prod.test.ts +246 -0
  294. package/src/swarm/spatial/__tests__/ZoneClaiming.test.ts +374 -0
  295. package/src/swarm/spatial/index.ts +28 -0
  296. package/src/team.ts +1245 -0
  297. package/src/training/LRScheduler.ts +377 -0
  298. package/src/training/QualityScoringPipeline.ts +139 -0
  299. package/src/training/SoftDedup.ts +461 -0
  300. package/src/training/SparsityMonitor.ts +685 -0
  301. package/src/training/SparsityMonitorTypes.ts +209 -0
  302. package/src/training/SpatialTrainingDataGenerator.ts +1526 -0
  303. package/src/training/SpatialTrainingDataTypes.ts +216 -0
  304. package/src/training/TrainingPipelineConfig.ts +215 -0
  305. package/src/training/constants.ts +94 -0
  306. package/src/training/index.ts +138 -0
  307. package/src/training/schema.ts +147 -0
  308. package/src/training/scripts/generate-novel-use-cases-dataset.ts +272 -0
  309. package/src/training/scripts/generate-spatial-dataset.ts +521 -0
  310. package/src/training/training/data/novel-use-cases.jsonl +153 -0
  311. package/src/training/training/data/spatial-reasoning-10k.jsonl +9354 -0
  312. package/src/training/trainingmonkey/TrainingMonkeyIntegration.ts +477 -0
  313. package/src/training/trainingmonkey/TrainingMonkeyTypes.ts +230 -0
  314. package/src/training/trainingmonkey/index.ts +26 -0
  315. package/src/training/trait-mappings.ts +157 -0
  316. package/src/types/core-stubs.d.ts +113 -0
  317. package/src/types.ts +304 -0
  318. package/test-output.txt +0 -0
  319. package/test-result.json +1 -0
  320. package/tsc-errors.txt +4 -0
  321. package/tsc_output.txt +0 -0
  322. package/tsconfig.json +14 -0
  323. package/tsup-learning-esm.config.ts +12 -0
  324. package/tsup.config.ts +21 -0
  325. package/typescript-errors-2.txt +0 -0
  326. package/typescript-errors.txt +22 -0
  327. package/vitest-log-utf8.txt +268 -0
  328. package/vitest-log.txt +0 -0
  329. package/vitest.config.ts +8 -0
@@ -0,0 +1,331 @@
1
+ /**
2
+ * BroadcastChannel + ChannelManager — Production Tests
3
+ */
4
+ import { describe, it, expect, vi } from 'vitest';
5
+ import { BroadcastChannel, ChannelManager } from '../BroadcastChannel';
6
+
7
+ function makeCh(cfg = {}) {
8
+ return new BroadcastChannel('ch1', 'TestChannel', cfg);
9
+ }
10
+ function makeMgr() {
11
+ return new ChannelManager();
12
+ }
13
+
14
+ // ─── BroadcastChannel ───────────────────────────────────
15
+
16
+ describe('BroadcastChannel — construction', () => {
17
+ it('id and name stored', () => {
18
+ const c = makeCh();
19
+ expect(c.id).toBe('ch1');
20
+ expect(c.name).toBe('TestChannel');
21
+ });
22
+ it('createdAt is recent', () => {
23
+ expect(Date.now() - makeCh().createdAt).toBeLessThan(100);
24
+ });
25
+ it('default maxSubscribers=1000', () => {
26
+ expect(makeCh().getConfig().maxSubscribers).toBe(1000);
27
+ });
28
+ it('default historySize=100', () => {
29
+ expect(makeCh().getConfig().historySize).toBe(100);
30
+ });
31
+ it('default allowReplay=true', () => {
32
+ expect(makeCh().getConfig().allowReplay).toBe(true);
33
+ });
34
+ });
35
+
36
+ describe('BroadcastChannel — subscribe / unsubscribe', () => {
37
+ it('subscribe returns subId', () => {
38
+ const c = makeCh();
39
+ const id = c.subscribe('a1', () => {});
40
+ expect(id.startsWith('sub-a1')).toBe(true);
41
+ });
42
+ it('isSubscribed=true after subscribe', () => {
43
+ const c = makeCh();
44
+ c.subscribe('a1', () => {});
45
+ expect(c.isSubscribed('a1')).toBe(true);
46
+ });
47
+ it('isSubscribed=false for unknown', () => {
48
+ expect(makeCh().isSubscribed('ghost')).toBe(false);
49
+ });
50
+ it('getSubscriberCount increments', () => {
51
+ const c = makeCh();
52
+ c.subscribe('a1', () => {});
53
+ c.subscribe('a2', () => {});
54
+ expect(c.getSubscriberCount()).toBe(2);
55
+ });
56
+ it('unsubscribe returns true', () => {
57
+ const c = makeCh();
58
+ const id = c.subscribe('a1', () => {});
59
+ expect(c.unsubscribe(id)).toBe(true);
60
+ });
61
+ it('unsubscribe decrements count', () => {
62
+ const c = makeCh();
63
+ const id = c.subscribe('a1', () => {});
64
+ c.unsubscribe(id);
65
+ expect(c.getSubscriberCount()).toBe(0);
66
+ });
67
+ it('unsubscribe unknown returns false', () => {
68
+ expect(makeCh().unsubscribe('ghost')).toBe(false);
69
+ });
70
+ it('throws when maxSubscribers reached', () => {
71
+ const c = makeCh({ maxSubscribers: 1 });
72
+ c.subscribe('a1', () => {});
73
+ expect(() => c.subscribe('a2', () => {})).toThrow();
74
+ });
75
+ });
76
+
77
+ describe('BroadcastChannel — broadcast', () => {
78
+ it('delivers to subscriber', async () => {
79
+ const received: unknown[] = [];
80
+ const c = makeCh();
81
+ c.subscribe('a1', (m) => { received.push((m as any).content); });
82
+ await c.broadcast('sender', 'hello');
83
+ expect(received).toHaveLength(1);
84
+ expect(received[0]).toBe('hello');
85
+ });
86
+ it('delivered to all subscribers', async () => {
87
+ const received: unknown[] = [];
88
+ const c = makeCh();
89
+ c.subscribe('a1', (m) => { received.push(m); });
90
+ c.subscribe('a2', (m) => { received.push(m); });
91
+ await c.broadcast('sender', 'hi');
92
+ expect(received).toHaveLength(2);
93
+ });
94
+ it('publisher-role subscriber does not receive', async () => {
95
+ const received: unknown[] = [];
96
+ const c = makeCh();
97
+ c.subscribe('a1', (m) => { received.push('a1', m); }, { role: 'publisher' });
98
+ await c.broadcast('sender', 'hi');
99
+ expect(received).toHaveLength(0);
100
+ });
101
+ it('returns message id string', async () => {
102
+ const c = makeCh();
103
+ const id = await c.broadcast('sender', 'x');
104
+ expect(id.startsWith('msg-')).toBe(true);
105
+ });
106
+ it('adds to history', async () => {
107
+ const c = makeCh();
108
+ await c.broadcast('s', 'a');
109
+ await c.broadcast('s', 'b');
110
+ expect(c.getStats().historySize).toBe(2);
111
+ });
112
+ it('historySize cap enforced', async () => {
113
+ const c = makeCh({ historySize: 2 });
114
+ await c.broadcast('s', 'a');
115
+ await c.broadcast('s', 'b');
116
+ await c.broadcast('s', 'c');
117
+ expect(c.getStats().historySize).toBe(2);
118
+ });
119
+ });
120
+
121
+ describe('BroadcastChannel — sendDirect', () => {
122
+ it('delivers only to target', async () => {
123
+ const received: string[] = [];
124
+ const c = makeCh();
125
+ c.subscribe('a1', () => { received.push('a1'); });
126
+ c.subscribe('a2', () => { received.push('a2'); });
127
+ await c.sendDirect('sender', 'a1', 'dm');
128
+ expect(received).toHaveLength(1);
129
+ expect(received[0]).toBe('a1');
130
+ });
131
+ it('returns messageId on delivery', async () => {
132
+ const c = makeCh();
133
+ c.subscribe('a1', () => {});
134
+ const id = await c.sendDirect('s', 'a1', 'hi');
135
+ expect(id).not.toBeNull();
136
+ });
137
+ it('returns null for unknown target', async () => {
138
+ const c = makeCh();
139
+ expect(await c.sendDirect('s', 'nobody', 'hi')).toBeNull();
140
+ });
141
+ });
142
+
143
+ describe('BroadcastChannel — ACK', () => {
144
+ it('requireAck tracks pending', async () => {
145
+ const c = makeCh({ requireAck: true });
146
+ c.subscribe('a1', () => {});
147
+ c.subscribe('a2', () => {});
148
+ const id = await c.broadcast('sender', 'hi');
149
+ expect(c.getPendingAcks(id)).toHaveLength(2);
150
+ });
151
+ it('acknowledge reduces pending', async () => {
152
+ const c = makeCh({ requireAck: true });
153
+ c.subscribe('a1', () => {});
154
+ c.subscribe('a2', () => {});
155
+ const id = await c.broadcast('sender', 'hi');
156
+ c.acknowledge(id, 'a1');
157
+ expect(c.getPendingAcks(id)).toHaveLength(1);
158
+ });
159
+ it('isFullyAcknowledged after all ack', async () => {
160
+ const c = makeCh({ requireAck: true });
161
+ c.subscribe('a1', () => {});
162
+ const id = await c.broadcast('sender', 'hi');
163
+ c.acknowledge(id, 'a1');
164
+ expect(c.isFullyAcknowledged(id)).toBe(true);
165
+ });
166
+ it('acknowledge unknown returns false', () => {
167
+ expect(makeCh().acknowledge('ghost', 'a1')).toBe(false);
168
+ });
169
+ it('isFullyAcknowledged true for no-requireAck', async () => {
170
+ const c = makeCh({ requireAck: false });
171
+ c.subscribe('a1', () => {});
172
+ const id = await c.broadcast('sender', 'hi');
173
+ expect(c.isFullyAcknowledged(id)).toBe(true);
174
+ });
175
+ });
176
+
177
+ describe('BroadcastChannel — getHistory', () => {
178
+ it('returns empty when allowReplay=false', async () => {
179
+ const c = makeCh({ allowReplay: false });
180
+ await c.broadcast('s', 'hi');
181
+ expect(c.getHistory()).toHaveLength(0);
182
+ });
183
+ it('returns messages when allowReplay=true', async () => {
184
+ const c = makeCh();
185
+ await c.broadcast('s', 'a');
186
+ await c.broadcast('s', 'b');
187
+ expect(c.getHistory()).toHaveLength(2);
188
+ });
189
+ it('limit option returns last N', async () => {
190
+ const c = makeCh();
191
+ await c.broadcast('s', '1');
192
+ await c.broadcast('s', '2');
193
+ await c.broadcast('s', '3');
194
+ expect(c.getHistory({ limit: 2 })).toHaveLength(2);
195
+ });
196
+ it('senderId filter', async () => {
197
+ const c = makeCh();
198
+ await c.broadcast('alice', 'hi');
199
+ await c.broadcast('bob', 'there');
200
+ expect(c.getHistory({ senderId: 'alice' })).toHaveLength(1);
201
+ });
202
+ it('clearHistory empties history', async () => {
203
+ const c = makeCh();
204
+ await c.broadcast('s', 'x');
205
+ c.clearHistory();
206
+ expect(c.getStats().historySize).toBe(0);
207
+ });
208
+ });
209
+
210
+ describe('BroadcastChannel — replayHistory', () => {
211
+ it('replays all history to subscriber', async () => {
212
+ const received: unknown[] = [];
213
+ const c = makeCh();
214
+ await c.broadcast('s', '1');
215
+ await c.broadcast('s', '2');
216
+ const subId = c.subscribe('a1', (m) => { received.push(m); });
217
+ const count = await c.replayHistory(subId);
218
+ expect(count).toBe(2);
219
+ expect(received).toHaveLength(2);
220
+ });
221
+ it('replayHistory=0 for unknown subscriber', async () => {
222
+ const c = makeCh();
223
+ await c.broadcast('s', 'x');
224
+ expect(await c.replayHistory('ghost')).toBe(0);
225
+ });
226
+ it('replay disabled when allowReplay=false', async () => {
227
+ const c = makeCh({ allowReplay: false });
228
+ await c.broadcast('s', 'x');
229
+ const id = c.subscribe('a1', () => {});
230
+ expect(await c.replayHistory(id)).toBe(0);
231
+ });
232
+ });
233
+
234
+ describe('BroadcastChannel — getStats', () => {
235
+ it('all fields present', async () => {
236
+ const c = makeCh();
237
+ await c.broadcast('s', 'hi');
238
+ const stats = c.getStats();
239
+ expect(stats).toHaveProperty('subscriberCount');
240
+ expect(stats).toHaveProperty('historySize');
241
+ expect(stats).toHaveProperty('pendingAckCount');
242
+ expect(stats).toHaveProperty('oldestMessage');
243
+ expect(stats).toHaveProperty('newestMessage');
244
+ });
245
+ it('oldestMessage=null when no history', () => {
246
+ expect(makeCh().getStats().oldestMessage).toBeNull();
247
+ });
248
+ });
249
+
250
+ // ─── ChannelManager ───────────────────────────────────
251
+
252
+ describe('ChannelManager — createChannel / getChannel', () => {
253
+ it('createChannel returns BroadcastChannel', () => {
254
+ const m = makeMgr();
255
+ const c = m.createChannel('general');
256
+ expect(c).toBeInstanceOf(BroadcastChannel);
257
+ expect(c.name).toBe('general');
258
+ });
259
+ it('getChannel by id', () => {
260
+ const m = makeMgr();
261
+ const c = m.createChannel('news');
262
+ expect(m.getChannel(c.id)).toBe(c);
263
+ });
264
+ it('getChannelByName', () => {
265
+ const m = makeMgr();
266
+ const c = m.createChannel('sport');
267
+ expect(m.getChannelByName('sport')).toBe(c);
268
+ });
269
+ it('getChannelByName unknown=undefined', () => {
270
+ expect(makeMgr().getChannelByName('ghost')).toBeUndefined();
271
+ });
272
+ it('getAllChannels returns all', () => {
273
+ const m = makeMgr();
274
+ m.createChannel('a');
275
+ m.createChannel('b');
276
+ expect(m.getAllChannels()).toHaveLength(2);
277
+ });
278
+ it('getChannelCount', () => {
279
+ const m = makeMgr();
280
+ m.createChannel('x');
281
+ m.createChannel('y');
282
+ expect(m.getChannelCount()).toBe(2);
283
+ });
284
+ it('deleteChannel removes channel', () => {
285
+ const m = makeMgr();
286
+ const c = m.createChannel('temp');
287
+ m.deleteChannel(c.id);
288
+ expect(m.getChannel(c.id)).toBeUndefined();
289
+ });
290
+ it('deleteChannel unknown returns false', () => {
291
+ expect(makeMgr().deleteChannel('ghost')).toBe(false);
292
+ });
293
+ });
294
+
295
+ describe('ChannelManager — subscribeAgent', () => {
296
+ it('subscribeAgent returns subscriberId', () => {
297
+ const m = makeMgr();
298
+ const c = m.createChannel('ch');
299
+ expect(m.subscribeAgent('a1', c.id, () => {})).toBeTruthy();
300
+ });
301
+ it('getAgentChannels returns subscribed', () => {
302
+ const m = makeMgr();
303
+ const c = m.createChannel('ch');
304
+ m.subscribeAgent('a1', c.id, () => {});
305
+ expect(m.getAgentChannels('a1')).toHaveLength(1);
306
+ });
307
+ it('subscribeAgent unknown channel throws', () => {
308
+ const m = makeMgr();
309
+ expect(() => m.subscribeAgent('a1', 'ghost', () => {})).toThrow();
310
+ });
311
+ });
312
+
313
+ describe('ChannelManager — multicast', () => {
314
+ it('delivers to all target channels', async () => {
315
+ const m = makeMgr();
316
+ const c1 = m.createChannel('c1');
317
+ const c2 = m.createChannel('c2');
318
+ const received: string[] = [];
319
+ m.subscribeAgent('a1', c1.id, () => { received.push('c1'); });
320
+ m.subscribeAgent('a2', c2.id, () => { received.push('c2'); });
321
+ const results = await m.multicast([c1.id, c2.id], 'sender', 'msg');
322
+ expect(results.size).toBe(2);
323
+ expect(received).toHaveLength(2);
324
+ });
325
+ it('skips unknown channel ids silently', async () => {
326
+ const m = makeMgr();
327
+ const c = m.createChannel('c1');
328
+ const results = await m.multicast([c.id, 'ghost'], 'sender', 'hi');
329
+ expect(results.size).toBe(1);
330
+ });
331
+ });
@@ -0,0 +1,333 @@
1
+ /**
2
+ * BroadcastChannel Tests
3
+ * HoloScript v3.2 - Autonomous Agent Swarms
4
+ */
5
+
6
+ import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
7
+ import { BroadcastChannel, ChannelManager } from '../BroadcastChannel';
8
+
9
+ describe('BroadcastChannel', () => {
10
+ let channel: BroadcastChannel;
11
+
12
+ beforeEach(() => {
13
+ channel = new BroadcastChannel('ch-1', 'test-channel');
14
+ });
15
+
16
+ afterEach(() => {
17
+ vi.restoreAllMocks();
18
+ });
19
+
20
+ describe('subscription', () => {
21
+ it('should subscribe an agent', () => {
22
+ const handler = vi.fn();
23
+ const subId = channel.subscribe('agent-1', handler);
24
+
25
+ expect(subId).toBeDefined();
26
+ expect(channel.isSubscribed('agent-1')).toBe(true);
27
+ });
28
+
29
+ it('should unsubscribe an agent', () => {
30
+ const handler = vi.fn();
31
+ const subId = channel.subscribe('agent-1', handler);
32
+
33
+ const removed = channel.unsubscribe(subId);
34
+
35
+ expect(removed).toBe(true);
36
+ expect(channel.getSubscriberCount()).toBe(0);
37
+ });
38
+
39
+ it('should support role-based subscription', () => {
40
+ const handler = vi.fn();
41
+ channel.subscribe('agent-1', handler, { role: 'publisher' });
42
+ channel.subscribe('agent-2', handler, { role: 'subscriber' });
43
+
44
+ const subs = channel.getSubscribers();
45
+ expect(subs.find((s) => s.agentId === 'agent-1')?.role).toBe('publisher');
46
+ expect(subs.find((s) => s.agentId === 'agent-2')?.role).toBe('subscriber');
47
+ });
48
+
49
+ it('should enforce maxSubscribers', () => {
50
+ const smallChannel = new BroadcastChannel('ch-1', 'small', { maxSubscribers: 2 });
51
+
52
+ smallChannel.subscribe('a1', vi.fn());
53
+ smallChannel.subscribe('a2', vi.fn());
54
+
55
+ expect(() => smallChannel.subscribe('a3', vi.fn())).toThrow(/maximum subscribers/);
56
+ });
57
+ });
58
+
59
+ describe('broadcast', () => {
60
+ it('should deliver to all subscribers', async () => {
61
+ const handler1 = vi.fn();
62
+ const handler2 = vi.fn();
63
+
64
+ channel.subscribe('agent-1', handler1);
65
+ channel.subscribe('agent-2', handler2);
66
+
67
+ await channel.broadcast('sender', { data: 'test' });
68
+
69
+ expect(handler1).toHaveBeenCalledOnce();
70
+ expect(handler2).toHaveBeenCalledOnce();
71
+ });
72
+
73
+ it('should not deliver to publisher-only subscribers', async () => {
74
+ const pubHandler = vi.fn();
75
+ const subHandler = vi.fn();
76
+
77
+ channel.subscribe('publisher', pubHandler, { role: 'publisher' });
78
+ channel.subscribe('subscriber', subHandler, { role: 'subscriber' });
79
+
80
+ await channel.broadcast('sender', { data: 'test' });
81
+
82
+ expect(pubHandler).not.toHaveBeenCalled();
83
+ expect(subHandler).toHaveBeenCalledOnce();
84
+ });
85
+
86
+ it('should return message ID', async () => {
87
+ channel.subscribe('a1', vi.fn());
88
+ const msgId = await channel.broadcast('sender', { data: 'test' });
89
+
90
+ expect(msgId).toContain('msg-');
91
+ });
92
+
93
+ it('should include metadata', async () => {
94
+ const handler = vi.fn();
95
+ channel.subscribe('agent-1', handler);
96
+
97
+ await channel.broadcast('sender', { data: 'x' }, { metadata: { priority: 'high' } });
98
+
99
+ expect(handler.mock.calls[0][0].metadata).toEqual({ priority: 'high' });
100
+ });
101
+ });
102
+
103
+ describe('direct messages', () => {
104
+ it('should send to specific agent', async () => {
105
+ const handler1 = vi.fn();
106
+ const handler2 = vi.fn();
107
+
108
+ channel.subscribe('agent-1', handler1);
109
+ channel.subscribe('agent-2', handler2);
110
+
111
+ await channel.sendDirect('sender', 'agent-2', { data: 'private' });
112
+
113
+ expect(handler1).not.toHaveBeenCalled();
114
+ expect(handler2).toHaveBeenCalledOnce();
115
+ });
116
+
117
+ it('should return null if target not found', async () => {
118
+ channel.subscribe('agent-1', vi.fn());
119
+
120
+ const result = await channel.sendDirect('sender', 'unknown', { data: 'x' });
121
+
122
+ expect(result).toBeNull();
123
+ });
124
+ });
125
+
126
+ describe('acknowledgments', () => {
127
+ it('should track pending acks', async () => {
128
+ const ackChannel = new BroadcastChannel('ack-ch', 'ack-test', { requireAck: true });
129
+
130
+ ackChannel.subscribe('agent-1', vi.fn());
131
+ ackChannel.subscribe('agent-2', vi.fn());
132
+
133
+ const msgId = await ackChannel.broadcast('sender', { data: 'x' });
134
+
135
+ const pending = ackChannel.getPendingAcks(msgId);
136
+ expect(pending).toContain('agent-1');
137
+ expect(pending).toContain('agent-2');
138
+ });
139
+
140
+ it('should remove ack when acknowledged', async () => {
141
+ const ackChannel = new BroadcastChannel('ack-ch', 'ack-test', { requireAck: true });
142
+
143
+ ackChannel.subscribe('agent-1', vi.fn());
144
+
145
+ const msgId = await ackChannel.broadcast('sender', { data: 'x' });
146
+ ackChannel.acknowledge(msgId, 'agent-1');
147
+
148
+ expect(ackChannel.isFullyAcknowledged(msgId)).toBe(true);
149
+ });
150
+ });
151
+
152
+ describe('history', () => {
153
+ it('should maintain message history', async () => {
154
+ channel.subscribe('a1', vi.fn());
155
+
156
+ await channel.broadcast('s', { msg: 1 });
157
+ await channel.broadcast('s', { msg: 2 });
158
+
159
+ const history = channel.getHistory();
160
+ expect(history.length).toBe(2);
161
+ });
162
+
163
+ it('should limit history size', async () => {
164
+ const smallHistory = new BroadcastChannel('ch', 'small', { historySize: 2 });
165
+ smallHistory.subscribe('a1', vi.fn());
166
+
167
+ await smallHistory.broadcast('s', { msg: 1 });
168
+ await smallHistory.broadcast('s', { msg: 2 });
169
+ await smallHistory.broadcast('s', { msg: 3 });
170
+
171
+ const history = smallHistory.getHistory();
172
+ expect(history.length).toBe(2);
173
+ });
174
+
175
+ it('should filter history by senderId', async () => {
176
+ channel.subscribe('a1', vi.fn());
177
+
178
+ await channel.broadcast('s1', { data: 1 });
179
+ await channel.broadcast('s2', { data: 2 });
180
+
181
+ const history = channel.getHistory({ senderId: 's1' });
182
+ expect(history.length).toBe(1);
183
+ expect(history[0].senderId).toBe('s1');
184
+ });
185
+
186
+ it('should replay history to subscriber', async () => {
187
+ const handler1 = vi.fn();
188
+ channel.subscribe('a1', handler1);
189
+
190
+ await channel.broadcast('s', { msg: 1 });
191
+ await channel.broadcast('s', { msg: 2 });
192
+
193
+ const newHandler = vi.fn();
194
+ const newSubId = channel.subscribe('a2', newHandler);
195
+
196
+ const replayed = await channel.replayHistory(newSubId);
197
+
198
+ expect(replayed).toBe(2);
199
+ expect(newHandler).toHaveBeenCalledTimes(2);
200
+ });
201
+
202
+ it('should not replay when replay disabled', async () => {
203
+ const noReplay = new BroadcastChannel('ch', 'no-replay', { allowReplay: false });
204
+ noReplay.subscribe('a1', vi.fn());
205
+ await noReplay.broadcast('s', { msg: 1 });
206
+
207
+ const handler = vi.fn();
208
+ const subId = noReplay.subscribe('a2', handler);
209
+
210
+ const replayed = await noReplay.replayHistory(subId);
211
+
212
+ expect(replayed).toBe(0);
213
+ });
214
+
215
+ it('should clear history', async () => {
216
+ channel.subscribe('a1', vi.fn());
217
+ await channel.broadcast('s', { msg: 1 });
218
+
219
+ channel.clearHistory();
220
+
221
+ expect(channel.getHistory().length).toBe(0);
222
+ });
223
+ });
224
+
225
+ describe('statistics', () => {
226
+ it('should return stats', async () => {
227
+ channel.subscribe('a1', vi.fn());
228
+ await channel.broadcast('s', { msg: 1 });
229
+
230
+ const stats = channel.getStats();
231
+
232
+ expect(stats.subscriberCount).toBe(1);
233
+ expect(stats.historySize).toBe(1);
234
+ });
235
+ });
236
+ });
237
+
238
+ describe('ChannelManager', () => {
239
+ let manager: ChannelManager;
240
+
241
+ beforeEach(() => {
242
+ manager = new ChannelManager();
243
+ });
244
+
245
+ describe('channel management', () => {
246
+ it('should create a channel', () => {
247
+ const channel = manager.createChannel('test-channel');
248
+
249
+ expect(channel.name).toBe('test-channel');
250
+ expect(manager.getChannelCount()).toBe(1);
251
+ });
252
+
253
+ it('should get channel by ID', () => {
254
+ const channel = manager.createChannel('test');
255
+
256
+ const found = manager.getChannel(channel.id);
257
+
258
+ expect(found).toBe(channel);
259
+ });
260
+
261
+ it('should get channel by name', () => {
262
+ manager.createChannel('my-channel');
263
+
264
+ const found = manager.getChannelByName('my-channel');
265
+
266
+ expect(found?.name).toBe('my-channel');
267
+ });
268
+
269
+ it('should delete a channel', () => {
270
+ const channel = manager.createChannel('test');
271
+
272
+ const deleted = manager.deleteChannel(channel.id);
273
+
274
+ expect(deleted).toBe(true);
275
+ expect(manager.getChannelCount()).toBe(0);
276
+ });
277
+
278
+ it('should list all channels', () => {
279
+ manager.createChannel('ch1');
280
+ manager.createChannel('ch2');
281
+
282
+ const channels = manager.getAllChannels();
283
+
284
+ expect(channels.length).toBe(2);
285
+ });
286
+ });
287
+
288
+ describe('agent subscription', () => {
289
+ it('should subscribe agent to channel', () => {
290
+ const channel = manager.createChannel('test');
291
+
292
+ const subId = manager.subscribeAgent('agent-1', channel.id, vi.fn());
293
+
294
+ expect(subId).toBeDefined();
295
+ expect(channel.isSubscribed('agent-1')).toBe(true);
296
+ });
297
+
298
+ it('should track agent channels', () => {
299
+ const ch1 = manager.createChannel('ch1');
300
+ const ch2 = manager.createChannel('ch2');
301
+
302
+ manager.subscribeAgent('agent-1', ch1.id, vi.fn());
303
+ manager.subscribeAgent('agent-1', ch2.id, vi.fn());
304
+
305
+ const agentChannels = manager.getAgentChannels('agent-1');
306
+
307
+ expect(agentChannels.length).toBe(2);
308
+ });
309
+
310
+ it('should throw if channel not found', () => {
311
+ expect(() => manager.subscribeAgent('a', 'unknown', vi.fn())).toThrow(/not found/);
312
+ });
313
+ });
314
+
315
+ describe('multicast', () => {
316
+ it('should broadcast to multiple channels', async () => {
317
+ const ch1 = manager.createChannel('ch1');
318
+ const ch2 = manager.createChannel('ch2');
319
+
320
+ const handler1 = vi.fn();
321
+ const handler2 = vi.fn();
322
+
323
+ ch1.subscribe('a1', handler1);
324
+ ch2.subscribe('a2', handler2);
325
+
326
+ const results = await manager.multicast([ch1.id, ch2.id], 'sender', { data: 'multi' });
327
+
328
+ expect(results.size).toBe(2);
329
+ expect(handler1).toHaveBeenCalledOnce();
330
+ expect(handler2).toHaveBeenCalledOnce();
331
+ });
332
+ });
333
+ });