@swal/edge-mesh 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (464) hide show
  1. package/LICENSE +128 -0
  2. package/NOTICE +37 -0
  3. package/README.md +470 -2
  4. package/dist/adapters/gpu-agent/index.d.ts +61 -0
  5. package/dist/adapters/gpu-agent/index.d.ts.map +1 -0
  6. package/dist/adapters/gpu-agent/index.js +364 -0
  7. package/dist/adapters/gpu-agent/index.js.map +1 -0
  8. package/dist/adapters/maloca-salud/citas-distribuidas.d.ts +19 -0
  9. package/dist/adapters/maloca-salud/citas-distribuidas.d.ts.map +1 -0
  10. package/dist/adapters/maloca-salud/citas-distribuidas.js +51 -0
  11. package/dist/adapters/maloca-salud/citas-distribuidas.js.map +1 -0
  12. package/dist/adapters/maloca-salud/eps-bridge.d.ts +15 -0
  13. package/dist/adapters/maloca-salud/eps-bridge.d.ts.map +1 -0
  14. package/dist/adapters/maloca-salud/eps-bridge.js +27 -0
  15. package/dist/adapters/maloca-salud/eps-bridge.js.map +1 -0
  16. package/dist/adapters/maloca-salud/patient-profile.d.ts +20 -0
  17. package/dist/adapters/maloca-salud/patient-profile.d.ts.map +1 -0
  18. package/dist/adapters/maloca-salud/patient-profile.js +36 -0
  19. package/dist/adapters/maloca-salud/patient-profile.js.map +1 -0
  20. package/dist/adapters/maloca-salud/types.d.ts +40 -0
  21. package/dist/adapters/maloca-salud/types.d.ts.map +1 -0
  22. package/dist/adapters/maloca-salud/types.js +2 -0
  23. package/dist/adapters/maloca-salud/types.js.map +1 -0
  24. package/dist/adapters/maloca-veeduria/contract-bridge.d.ts +31 -0
  25. package/dist/adapters/maloca-veeduria/contract-bridge.d.ts.map +1 -0
  26. package/dist/adapters/maloca-veeduria/contract-bridge.js +103 -0
  27. package/dist/adapters/maloca-veeduria/contract-bridge.js.map +1 -0
  28. package/dist/adapters/maloca-veeduria/evidentia-veeduria.d.ts +14 -0
  29. package/dist/adapters/maloca-veeduria/evidentia-veeduria.d.ts.map +1 -0
  30. package/dist/adapters/maloca-veeduria/evidentia-veeduria.js +40 -0
  31. package/dist/adapters/maloca-veeduria/evidentia-veeduria.js.map +1 -0
  32. package/dist/adapters/maloca-veeduria/licitante-profile.d.ts +21 -0
  33. package/dist/adapters/maloca-veeduria/licitante-profile.d.ts.map +1 -0
  34. package/dist/adapters/maloca-veeduria/licitante-profile.js +58 -0
  35. package/dist/adapters/maloca-veeduria/licitante-profile.js.map +1 -0
  36. package/dist/adapters/maloca-veeduria/types.d.ts +30 -0
  37. package/dist/adapters/maloca-veeduria/types.d.ts.map +1 -0
  38. package/dist/adapters/maloca-veeduria/types.js +2 -0
  39. package/dist/adapters/maloca-veeduria/types.js.map +1 -0
  40. package/dist/adapters/maloca-xavier/agent-profile.d.ts +32 -0
  41. package/dist/adapters/maloca-xavier/agent-profile.d.ts.map +1 -0
  42. package/dist/adapters/maloca-xavier/agent-profile.js +56 -0
  43. package/dist/adapters/maloca-xavier/agent-profile.js.map +1 -0
  44. package/dist/adapters/maloca-xavier/codegraph-bridge.d.ts +27 -0
  45. package/dist/adapters/maloca-xavier/codegraph-bridge.d.ts.map +1 -0
  46. package/dist/adapters/maloca-xavier/codegraph-bridge.js +58 -0
  47. package/dist/adapters/maloca-xavier/codegraph-bridge.js.map +1 -0
  48. package/dist/adapters/maloca-xavier/llm-router.d.ts +25 -0
  49. package/dist/adapters/maloca-xavier/llm-router.d.ts.map +1 -0
  50. package/dist/adapters/maloca-xavier/llm-router.js +58 -0
  51. package/dist/adapters/maloca-xavier/llm-router.js.map +1 -0
  52. package/dist/authz/index.d.ts +81 -0
  53. package/dist/authz/index.d.ts.map +1 -0
  54. package/dist/authz/index.js +271 -0
  55. package/dist/authz/index.js.map +1 -0
  56. package/dist/chat/index.d.ts +138 -0
  57. package/dist/chat/index.d.ts.map +1 -0
  58. package/dist/chat/index.js +394 -0
  59. package/dist/chat/index.js.map +1 -0
  60. package/dist/chat/offline-queue.d.ts +32 -0
  61. package/dist/chat/offline-queue.d.ts.map +1 -0
  62. package/dist/chat/offline-queue.js +93 -0
  63. package/dist/chat/offline-queue.js.map +1 -0
  64. package/dist/core/DefaultEdgeMeshNode.d.ts +17 -0
  65. package/dist/core/DefaultEdgeMeshNode.d.ts.map +1 -0
  66. package/dist/core/DefaultEdgeMeshNode.js +57 -0
  67. package/dist/core/DefaultEdgeMeshNode.js.map +1 -0
  68. package/dist/core/node.d.ts +26 -0
  69. package/dist/core/node.d.ts.map +1 -0
  70. package/dist/core/node.js +16 -0
  71. package/dist/core/node.js.map +1 -0
  72. package/dist/edge-mesh.d.ts +139 -0
  73. package/dist/edge-mesh.d.ts.map +1 -0
  74. package/dist/edge-mesh.js +1146 -0
  75. package/dist/edge-mesh.js.map +1 -0
  76. package/dist/gos/GosBridge.d.ts +49 -0
  77. package/dist/gos/GosBridge.d.ts.map +1 -0
  78. package/dist/gos/GosBridge.js +133 -0
  79. package/dist/gos/GosBridge.js.map +1 -0
  80. package/dist/governance/authority.d.ts +44 -0
  81. package/dist/governance/authority.d.ts.map +1 -0
  82. package/dist/governance/authority.js +123 -0
  83. package/dist/governance/authority.js.map +1 -0
  84. package/dist/governance/index.d.ts +66 -0
  85. package/dist/governance/index.d.ts.map +1 -0
  86. package/dist/governance/index.js +210 -0
  87. package/dist/governance/index.js.map +1 -0
  88. package/dist/governance/merge.d.ts +31 -0
  89. package/dist/governance/merge.d.ts.map +1 -0
  90. package/dist/governance/merge.js +167 -0
  91. package/dist/governance/merge.js.map +1 -0
  92. package/dist/identity/index.d.ts +199 -0
  93. package/dist/identity/index.d.ts.map +1 -0
  94. package/dist/identity/index.js +415 -0
  95. package/dist/identity/index.js.map +1 -0
  96. package/dist/index.d.ts +71 -0
  97. package/dist/index.d.ts.map +1 -0
  98. package/dist/index.js +56 -0
  99. package/dist/index.js.map +1 -0
  100. package/dist/maloca/backoffice.d.ts +31 -0
  101. package/dist/maloca/backoffice.d.ts.map +1 -0
  102. package/dist/maloca/backoffice.js +40 -0
  103. package/dist/maloca/backoffice.js.map +1 -0
  104. package/dist/maloca/event-bus.d.ts +60 -0
  105. package/dist/maloca/event-bus.d.ts.map +1 -0
  106. package/dist/maloca/event-bus.js +175 -0
  107. package/dist/maloca/event-bus.js.map +1 -0
  108. package/dist/maloca/evidentia.d.ts +136 -0
  109. package/dist/maloca/evidentia.d.ts.map +1 -0
  110. package/dist/maloca/evidentia.js +447 -0
  111. package/dist/maloca/evidentia.js.map +1 -0
  112. package/dist/maloca/gateway/api.d.ts +146 -0
  113. package/dist/maloca/gateway/api.d.ts.map +1 -0
  114. package/dist/maloca/gateway/api.js +228 -0
  115. package/dist/maloca/gateway/api.js.map +1 -0
  116. package/dist/maloca/gateway/auth.d.ts +20 -0
  117. package/dist/maloca/gateway/auth.d.ts.map +1 -0
  118. package/dist/maloca/gateway/auth.js +115 -0
  119. package/dist/maloca/gateway/auth.js.map +1 -0
  120. package/dist/maloca/gateway/dashboard.d.ts +42 -0
  121. package/dist/maloca/gateway/dashboard.d.ts.map +1 -0
  122. package/dist/maloca/gateway/dashboard.js +67 -0
  123. package/dist/maloca/gateway/dashboard.js.map +1 -0
  124. package/dist/maloca/gateway/websocket.d.ts +34 -0
  125. package/dist/maloca/gateway/websocket.d.ts.map +1 -0
  126. package/dist/maloca/gateway/websocket.js +71 -0
  127. package/dist/maloca/gateway/websocket.js.map +1 -0
  128. package/dist/maloca/karma.d.ts +165 -0
  129. package/dist/maloca/karma.d.ts.map +1 -0
  130. package/dist/maloca/karma.js +307 -0
  131. package/dist/maloca/karma.js.map +1 -0
  132. package/dist/maloca/kernel.d.ts +20 -0
  133. package/dist/maloca/kernel.d.ts.map +1 -0
  134. package/dist/maloca/kernel.js +76 -0
  135. package/dist/maloca/kernel.js.map +1 -0
  136. package/dist/maloca/metadata.d.ts +30 -0
  137. package/dist/maloca/metadata.d.ts.map +1 -0
  138. package/dist/maloca/metadata.js +80 -0
  139. package/dist/maloca/metadata.js.map +1 -0
  140. package/dist/maloca/perfil.d.ts +57 -0
  141. package/dist/maloca/perfil.d.ts.map +1 -0
  142. package/dist/maloca/perfil.js +113 -0
  143. package/dist/maloca/perfil.js.map +1 -0
  144. package/dist/maloca/plugin-registry.d.ts +27 -0
  145. package/dist/maloca/plugin-registry.d.ts.map +1 -0
  146. package/dist/maloca/plugin-registry.js +76 -0
  147. package/dist/maloca/plugin-registry.js.map +1 -0
  148. package/dist/maloca/polygon-bridge.d.ts +63 -0
  149. package/dist/maloca/polygon-bridge.d.ts.map +1 -0
  150. package/dist/maloca/polygon-bridge.js +198 -0
  151. package/dist/maloca/polygon-bridge.js.map +1 -0
  152. package/dist/maloca/types.d.ts +44 -0
  153. package/dist/maloca/types.d.ts.map +1 -0
  154. package/dist/maloca/types.js +2 -0
  155. package/dist/maloca/types.js.map +1 -0
  156. package/dist/medico/aval.d.ts +149 -0
  157. package/dist/medico/aval.d.ts.map +1 -0
  158. package/dist/medico/aval.js +281 -0
  159. package/dist/medico/aval.js.map +1 -0
  160. package/dist/medico/canonica.d.ts +96 -0
  161. package/dist/medico/canonica.d.ts.map +1 -0
  162. package/dist/medico/canonica.js +148 -0
  163. package/dist/medico/canonica.js.map +1 -0
  164. package/dist/medico/credencial.d.ts +160 -0
  165. package/dist/medico/credencial.d.ts.map +1 -0
  166. package/dist/medico/credencial.js +240 -0
  167. package/dist/medico/credencial.js.map +1 -0
  168. package/dist/medico/index.d.ts +36 -0
  169. package/dist/medico/index.d.ts.map +1 -0
  170. package/dist/medico/index.js +39 -0
  171. package/dist/medico/index.js.map +1 -0
  172. package/dist/medico/malla.d.ts +218 -0
  173. package/dist/medico/malla.d.ts.map +1 -0
  174. package/dist/medico/malla.js +347 -0
  175. package/dist/medico/malla.js.map +1 -0
  176. package/dist/medico/registro-profesional.d.ts +84 -0
  177. package/dist/medico/registro-profesional.d.ts.map +1 -0
  178. package/dist/medico/registro-profesional.js +135 -0
  179. package/dist/medico/registro-profesional.js.map +1 -0
  180. package/dist/medico/registro.d.ts +149 -0
  181. package/dist/medico/registro.d.ts.map +1 -0
  182. package/dist/medico/registro.js +173 -0
  183. package/dist/medico/registro.js.map +1 -0
  184. package/dist/medico/types.d.ts +287 -0
  185. package/dist/medico/types.d.ts.map +1 -0
  186. package/dist/medico/types.js +135 -0
  187. package/dist/medico/types.js.map +1 -0
  188. package/dist/medico/verificacion-registro.d.ts +300 -0
  189. package/dist/medico/verificacion-registro.d.ts.map +1 -0
  190. package/dist/medico/verificacion-registro.js +319 -0
  191. package/dist/medico/verificacion-registro.js.map +1 -0
  192. package/dist/mesh/index.d.ts +457 -0
  193. package/dist/mesh/index.d.ts.map +1 -0
  194. package/dist/mesh/index.js +929 -0
  195. package/dist/mesh/index.js.map +1 -0
  196. package/dist/namespaces/encrypted-plugin.d.ts +56 -0
  197. package/dist/namespaces/encrypted-plugin.d.ts.map +1 -0
  198. package/dist/namespaces/encrypted-plugin.js +173 -0
  199. package/dist/namespaces/encrypted-plugin.js.map +1 -0
  200. package/dist/namespaces/index.d.ts +52 -0
  201. package/dist/namespaces/index.d.ts.map +1 -0
  202. package/dist/namespaces/index.js +134 -0
  203. package/dist/namespaces/index.js.map +1 -0
  204. package/dist/namespaces/ivn-proofs.d.ts +54 -0
  205. package/dist/namespaces/ivn-proofs.d.ts.map +1 -0
  206. package/dist/namespaces/ivn-proofs.js +162 -0
  207. package/dist/namespaces/ivn-proofs.js.map +1 -0
  208. package/dist/namespaces/offers-gossip.d.ts +95 -0
  209. package/dist/namespaces/offers-gossip.d.ts.map +1 -0
  210. package/dist/namespaces/offers-gossip.js +213 -0
  211. package/dist/namespaces/offers-gossip.js.map +1 -0
  212. package/dist/node-memory/idb-store.d.ts +22 -0
  213. package/dist/node-memory/idb-store.d.ts.map +1 -0
  214. package/dist/node-memory/idb-store.js +154 -0
  215. package/dist/node-memory/idb-store.js.map +1 -0
  216. package/dist/node-memory/index.d.ts +14 -0
  217. package/dist/node-memory/index.d.ts.map +1 -0
  218. package/dist/node-memory/index.js +159 -0
  219. package/dist/node-memory/index.js.map +1 -0
  220. package/dist/node-memory/sync-flush.d.ts +11 -0
  221. package/dist/node-memory/sync-flush.d.ts.map +1 -0
  222. package/dist/node-memory/sync-flush.js +37 -0
  223. package/dist/node-memory/sync-flush.js.map +1 -0
  224. package/dist/node-memory/types.d.ts +34 -0
  225. package/dist/node-memory/types.d.ts.map +1 -0
  226. package/dist/node-memory/types.js +2 -0
  227. package/dist/node-memory/types.js.map +1 -0
  228. package/dist/node-memory/xavier-store.d.ts +12 -0
  229. package/dist/node-memory/xavier-store.d.ts.map +1 -0
  230. package/dist/node-memory/xavier-store.js +75 -0
  231. package/dist/node-memory/xavier-store.js.map +1 -0
  232. package/dist/op-log/index.d.ts +49 -0
  233. package/dist/op-log/index.d.ts.map +1 -0
  234. package/dist/op-log/index.js +190 -0
  235. package/dist/op-log/index.js.map +1 -0
  236. package/dist/presence/health.d.ts +54 -0
  237. package/dist/presence/health.d.ts.map +1 -0
  238. package/dist/presence/health.js +139 -0
  239. package/dist/presence/health.js.map +1 -0
  240. package/dist/presence/index.d.ts +85 -0
  241. package/dist/presence/index.d.ts.map +1 -0
  242. package/dist/presence/index.js +255 -0
  243. package/dist/presence/index.js.map +1 -0
  244. package/dist/presence/peer-health.d.ts +40 -0
  245. package/dist/presence/peer-health.d.ts.map +1 -0
  246. package/dist/presence/peer-health.js +118 -0
  247. package/dist/presence/peer-health.js.map +1 -0
  248. package/dist/protocol/canonical.d.ts +10 -0
  249. package/dist/protocol/canonical.d.ts.map +1 -0
  250. package/dist/protocol/canonical.js +32 -0
  251. package/dist/protocol/canonical.js.map +1 -0
  252. package/dist/protocol/crypto.d.ts +299 -0
  253. package/dist/protocol/crypto.d.ts.map +1 -0
  254. package/dist/protocol/crypto.js +739 -0
  255. package/dist/protocol/crypto.js.map +1 -0
  256. package/dist/protocol/index.d.ts +30 -0
  257. package/dist/protocol/index.d.ts.map +1 -0
  258. package/dist/protocol/index.js +116 -0
  259. package/dist/protocol/index.js.map +1 -0
  260. package/dist/protocol/utils.d.ts +5 -0
  261. package/dist/protocol/utils.d.ts.map +1 -0
  262. package/dist/protocol/utils.js +27 -0
  263. package/dist/protocol/utils.js.map +1 -0
  264. package/dist/salones/manager.d.ts +100 -0
  265. package/dist/salones/manager.d.ts.map +1 -0
  266. package/dist/salones/manager.js +240 -0
  267. package/dist/salones/manager.js.map +1 -0
  268. package/dist/security/rate-limiter.d.ts +13 -0
  269. package/dist/security/rate-limiter.d.ts.map +1 -0
  270. package/dist/security/rate-limiter.js +32 -0
  271. package/dist/security/rate-limiter.js.map +1 -0
  272. package/dist/snapshot/index.d.ts +90 -0
  273. package/dist/snapshot/index.d.ts.map +1 -0
  274. package/dist/snapshot/index.js +189 -0
  275. package/dist/snapshot/index.js.map +1 -0
  276. package/dist/storage/index.d.ts +49 -0
  277. package/dist/storage/index.d.ts.map +1 -0
  278. package/dist/storage/index.js +176 -0
  279. package/dist/storage/index.js.map +1 -0
  280. package/dist/storage/optimizer.d.ts +28 -0
  281. package/dist/storage/optimizer.d.ts.map +1 -0
  282. package/dist/storage/optimizer.js +50 -0
  283. package/dist/storage/optimizer.js.map +1 -0
  284. package/dist/storage/ydoc-persistence.d.ts +27 -0
  285. package/dist/storage/ydoc-persistence.d.ts.map +1 -0
  286. package/dist/storage/ydoc-persistence.js +128 -0
  287. package/dist/storage/ydoc-persistence.js.map +1 -0
  288. package/dist/sync/ChunkSyncManager.d.ts +41 -0
  289. package/dist/sync/ChunkSyncManager.d.ts.map +1 -0
  290. package/dist/sync/ChunkSyncManager.js +115 -0
  291. package/dist/sync/ChunkSyncManager.js.map +1 -0
  292. package/dist/sync/conflict-resolver.d.ts +33 -0
  293. package/dist/sync/conflict-resolver.d.ts.map +1 -0
  294. package/dist/sync/conflict-resolver.js +54 -0
  295. package/dist/sync/conflict-resolver.js.map +1 -0
  296. package/dist/sync/engine.d.ts +168 -0
  297. package/dist/sync/engine.d.ts.map +1 -0
  298. package/dist/sync/engine.js +315 -0
  299. package/dist/sync/engine.js.map +1 -0
  300. package/dist/sync/index.d.ts +3 -0
  301. package/dist/sync/index.d.ts.map +1 -0
  302. package/dist/sync/index.js +3 -0
  303. package/dist/sync/index.js.map +1 -0
  304. package/dist/transport/TorTransportAdapter.d.ts +70 -0
  305. package/dist/transport/TorTransportAdapter.d.ts.map +1 -0
  306. package/dist/transport/TorTransportAdapter.js +192 -0
  307. package/dist/transport/TorTransportAdapter.js.map +1 -0
  308. package/dist/transport/memory.d.ts +31 -0
  309. package/dist/transport/memory.d.ts.map +1 -0
  310. package/dist/transport/memory.js +114 -0
  311. package/dist/transport/memory.js.map +1 -0
  312. package/dist/transport/peerjs.d.ts +45 -0
  313. package/dist/transport/peerjs.d.ts.map +1 -0
  314. package/dist/transport/peerjs.js +301 -0
  315. package/dist/transport/peerjs.js.map +1 -0
  316. package/dist/transport/pqc-handshake.d.ts +72 -0
  317. package/dist/transport/pqc-handshake.d.ts.map +1 -0
  318. package/dist/transport/pqc-handshake.js +129 -0
  319. package/dist/transport/pqc-handshake.js.map +1 -0
  320. package/dist/transport/relay-config.d.ts +11 -0
  321. package/dist/transport/relay-config.d.ts.map +1 -0
  322. package/dist/transport/relay-config.js +48 -0
  323. package/dist/transport/relay-config.js.map +1 -0
  324. package/dist/transport/relay-server.d.ts +37 -0
  325. package/dist/transport/relay-server.d.ts.map +1 -0
  326. package/dist/transport/relay-server.js +230 -0
  327. package/dist/transport/relay-server.js.map +1 -0
  328. package/dist/transport/tor.d.ts +47 -0
  329. package/dist/transport/tor.d.ts.map +1 -0
  330. package/dist/transport/tor.js +158 -0
  331. package/dist/transport/tor.js.map +1 -0
  332. package/dist/transport/types.d.ts +34 -0
  333. package/dist/transport/types.d.ts.map +1 -0
  334. package/dist/transport/types.js +2 -0
  335. package/dist/transport/types.js.map +1 -0
  336. package/dist/types/index.d.ts +293 -0
  337. package/dist/types/index.d.ts.map +1 -0
  338. package/dist/types/index.js +73 -0
  339. package/dist/types/index.js.map +1 -0
  340. package/dist/web/admission.d.ts +24 -0
  341. package/dist/web/admission.d.ts.map +1 -0
  342. package/dist/web/admission.js +41 -0
  343. package/dist/web/admission.js.map +1 -0
  344. package/dist/web/crypto.d.ts +18 -0
  345. package/dist/web/crypto.d.ts.map +1 -0
  346. package/dist/web/crypto.js +78 -0
  347. package/dist/web/crypto.js.map +1 -0
  348. package/dist/web/exchange.d.ts +23 -0
  349. package/dist/web/exchange.d.ts.map +1 -0
  350. package/dist/web/exchange.js +54 -0
  351. package/dist/web/exchange.js.map +1 -0
  352. package/dist/web/fragment.d.ts +58 -0
  353. package/dist/web/fragment.d.ts.map +1 -0
  354. package/dist/web/fragment.js +198 -0
  355. package/dist/web/fragment.js.map +1 -0
  356. package/dist/web/index.d.ts +25 -0
  357. package/dist/web/index.d.ts.map +1 -0
  358. package/dist/web/index.js +14 -0
  359. package/dist/web/index.js.map +1 -0
  360. package/dist/web/merge/index.d.ts +104 -0
  361. package/dist/web/merge/index.d.ts.map +1 -0
  362. package/dist/web/merge/index.js +236 -0
  363. package/dist/web/merge/index.js.map +1 -0
  364. package/dist/web/oplog/hlc.d.ts +19 -0
  365. package/dist/web/oplog/hlc.d.ts.map +1 -0
  366. package/dist/web/oplog/hlc.js +39 -0
  367. package/dist/web/oplog/hlc.js.map +1 -0
  368. package/dist/web/oplog/index.d.ts +10 -0
  369. package/dist/web/oplog/index.d.ts.map +1 -0
  370. package/dist/web/oplog/index.js +6 -0
  371. package/dist/web/oplog/index.js.map +1 -0
  372. package/dist/web/oplog/log.d.ts +182 -0
  373. package/dist/web/oplog/log.d.ts.map +1 -0
  374. package/dist/web/oplog/log.js +693 -0
  375. package/dist/web/oplog/log.js.map +1 -0
  376. package/dist/web/oplog/store.d.ts +40 -0
  377. package/dist/web/oplog/store.d.ts.map +1 -0
  378. package/dist/web/oplog/store.js +85 -0
  379. package/dist/web/oplog/store.js.map +1 -0
  380. package/dist/web/oplog/sync.d.ts +54 -0
  381. package/dist/web/oplog/sync.d.ts.map +1 -0
  382. package/dist/web/oplog/sync.js +162 -0
  383. package/dist/web/oplog/sync.js.map +1 -0
  384. package/dist/web/oplog/types.d.ts +119 -0
  385. package/dist/web/oplog/types.d.ts.map +1 -0
  386. package/dist/web/oplog/types.js +2 -0
  387. package/dist/web/oplog/types.js.map +1 -0
  388. package/dist/web/pairing.d.ts +186 -0
  389. package/dist/web/pairing.d.ts.map +1 -0
  390. package/dist/web/pairing.js +395 -0
  391. package/dist/web/pairing.js.map +1 -0
  392. package/dist/web/pq.d.ts +36 -0
  393. package/dist/web/pq.d.ts.map +1 -0
  394. package/dist/web/pq.js +59 -0
  395. package/dist/web/pq.js.map +1 -0
  396. package/dist/web/provider.d.ts +188 -0
  397. package/dist/web/provider.d.ts.map +1 -0
  398. package/dist/web/provider.js +2042 -0
  399. package/dist/web/provider.js.map +1 -0
  400. package/dist/web/rooms.d.ts +20 -0
  401. package/dist/web/rooms.d.ts.map +1 -0
  402. package/dist/web/rooms.js +51 -0
  403. package/dist/web/rooms.js.map +1 -0
  404. package/dist/web/rotation.d.ts +73 -0
  405. package/dist/web/rotation.d.ts.map +1 -0
  406. package/dist/web/rotation.js +130 -0
  407. package/dist/web/rotation.js.map +1 -0
  408. package/dist/web/secstate.d.ts +213 -0
  409. package/dist/web/secstate.d.ts.map +1 -0
  410. package/dist/web/secstate.js +789 -0
  411. package/dist/web/secstate.js.map +1 -0
  412. package/dist/web/store.d.ts +9 -0
  413. package/dist/web/store.d.ts.map +1 -0
  414. package/dist/web/store.js +32 -0
  415. package/dist/web/store.js.map +1 -0
  416. package/dist/web/transports/loopback.d.ts +11 -0
  417. package/dist/web/transports/loopback.d.ts.map +1 -0
  418. package/dist/web/transports/loopback.js +97 -0
  419. package/dist/web/transports/loopback.js.map +1 -0
  420. package/dist/web/transports/qr-sdp.d.ts +27 -0
  421. package/dist/web/transports/qr-sdp.d.ts.map +1 -0
  422. package/dist/web/transports/qr-sdp.js +131 -0
  423. package/dist/web/transports/qr-sdp.js.map +1 -0
  424. package/dist/web/transports/ws.d.ts +12 -0
  425. package/dist/web/transports/ws.d.ts.map +1 -0
  426. package/dist/web/transports/ws.js +139 -0
  427. package/dist/web/transports/ws.js.map +1 -0
  428. package/dist/web/trust/canonical.d.ts +8 -0
  429. package/dist/web/trust/canonical.d.ts.map +1 -0
  430. package/dist/web/trust/canonical.js +27 -0
  431. package/dist/web/trust/canonical.js.map +1 -0
  432. package/dist/web/trust/docs.d.ts +51 -0
  433. package/dist/web/trust/docs.d.ts.map +1 -0
  434. package/dist/web/trust/docs.js +170 -0
  435. package/dist/web/trust/docs.js.map +1 -0
  436. package/dist/web/trust/index.d.ts +10 -0
  437. package/dist/web/trust/index.d.ts.map +1 -0
  438. package/dist/web/trust/index.js +7 -0
  439. package/dist/web/trust/index.js.map +1 -0
  440. package/dist/web/trust/keys.d.ts +37 -0
  441. package/dist/web/trust/keys.d.ts.map +1 -0
  442. package/dist/web/trust/keys.js +75 -0
  443. package/dist/web/trust/keys.js.map +1 -0
  444. package/dist/web/trust/store.d.ts +150 -0
  445. package/dist/web/trust/store.d.ts.map +1 -0
  446. package/dist/web/trust/store.js +587 -0
  447. package/dist/web/trust/store.js.map +1 -0
  448. package/dist/web/trust/types.d.ts +134 -0
  449. package/dist/web/trust/types.d.ts.map +1 -0
  450. package/dist/web/trust/types.js +4 -0
  451. package/dist/web/trust/types.js.map +1 -0
  452. package/dist/web/types.d.ts +115 -0
  453. package/dist/web/types.d.ts.map +1 -0
  454. package/dist/web/types.js +2 -0
  455. package/dist/web/types.js.map +1 -0
  456. package/dist/web/util.d.ts +12 -0
  457. package/dist/web/util.d.ts.map +1 -0
  458. package/dist/web/util.js +54 -0
  459. package/dist/web/util.js.map +1 -0
  460. package/dist/web/webrtc.d.ts +35 -0
  461. package/dist/web/webrtc.d.ts.map +1 -0
  462. package/dist/web/webrtc.js +304 -0
  463. package/dist/web/webrtc.js.map +1 -0
  464. package/package.json +138 -4
@@ -0,0 +1,929 @@
1
+ // ─── MESH MANAGER ESCALABLE ─────────────────────────────────────────────
2
+ // Mesh manager optimizado para escalar a 50+ peers simultáneos.
3
+ // No flood broadcast: usa gossip protocol + fan-out limitado.
4
+ // Namespace-aware routing: solo envía updates a peers en el mismo salón.
5
+ //
6
+ // Estrategia: cada peer mantiene un subconjunto aleatorio de conexiones
7
+ // y propaga mensajes via gossip con fan-out controlado (default 3).
8
+ // Los heartbeats mantienen el mesh vivo. La detección de peers nuevos
9
+ // se hace via el broker PeerJS.
10
+ import { createEnvelope } from "../protocol/index.js";
11
+ import { generarNonce } from "../protocol/utils.js";
12
+ import { TokenBucketRateLimiter } from "../security/rate-limiter.js";
13
+ import { TIPO_MENSAJE } from "../types/index.js";
14
+ // ─── CONST OBJECT PATTERNS ────────────────────────────────────────────────
15
+ export const ESTRATEGIA_FAN_OUT = {
16
+ ALEATORIA: "aleatoria",
17
+ POR_SALUD: "por_salud",
18
+ POR_LATENCIA: "por_latencia",
19
+ };
20
+ // ─── CONSTANTS ─────────────────────────────────────────────────────────────
21
+ const FAN_OUT_POR_DEFECTO = 3;
22
+ const MAX_PEERS_POR_NODO = 12;
23
+ const HEARTBEAT_MESH_MS = 3_000;
24
+ const TIMEOUT_PEER_MS = 12_000;
25
+ const GOSSIP_TTL_POR_DEFECTO = 5;
26
+ const MAX_RECONEXIONES = 3;
27
+ const INTERVALO_LIMPIEZA_MS = 30_000;
28
+ /**
29
+ * Cuántos registros de divergencia retiene {@link MeshManager.obtenerDivergencias}.
30
+ *
31
+ * Acotado por la misma razón que `gossipsVistos`: esto es una cola de
32
+ * diagnóstico, no un libro mayor, y un mapa sin límite alimentado por pares
33
+ * remotos es un bug de crecimiento de memoria. La reconciliación, cuando llegue,
34
+ * será dueña del estado durable — no esta cola.
35
+ */
36
+ export const MAX_DIVERGENCIAS_REGISTRADAS = 256;
37
+ /**
38
+ * Acepta el consumidor de karma directamente o una bolsa de opciones con nombre,
39
+ * así el emparejamiento común sigue siendo una llamada de dos argumentos y
40
+ * añadir una segunda opción más adelante no es un cambio incompatible.
41
+ */
42
+ function resolverConsumidorKarma(arg) {
43
+ if (arg === undefined)
44
+ return undefined;
45
+ if ("aplicarTransaccion" in arg)
46
+ return arg;
47
+ return arg.consumidorKarma;
48
+ }
49
+ /**
50
+ * Payload que MeshManager gossipea para llevar un aval. Versionado en el cable
51
+ * para que un cambio futuro en el formato de transacción sea distinguible.
52
+ */
53
+ export const PAYLOAD_KARMA_TIPO = "karma:tx";
54
+ /**
55
+ * Rehidrat y valida estructuralmente un aval que llegó por el cable.
56
+ *
57
+ * Comprueba SOLO la forma necesaria para sobrevivir el transporte (las firmas
58
+ * Uint8Array vuelven como ArrayBuffer o como arrays de números en la práctica) y
59
+ * para handing algo bien tipado a KarmaManager.
60
+ *
61
+ * NO es una comprobación de seguridad. Nada de esto se confía: no inspecciona la
62
+ * firma ni la semántica de `emisor`, `sujeto` o `delta`. Esos veredictos
63
+ * pertenecen a KarmaManager.aplicarTransaccion, el único camino que puede tomar
64
+ * una transacción remota.
65
+ */
66
+ function normalizarTransaccionKarma(valor) {
67
+ if (typeof valor !== "object" || valor === null)
68
+ return null;
69
+ const p = valor;
70
+ if (typeof p.id !== "string" || p.id.length === 0)
71
+ return null;
72
+ if (typeof p.emisor !== "string" || p.emisor.length === 0)
73
+ return null;
74
+ if (typeof p.sujeto !== "string" || p.sujeto.length === 0)
75
+ return null;
76
+ // Solo "es un numero". El rango y el signo son reglas de KarmaManager.
77
+ if (typeof p.delta !== "number" || !Number.isFinite(p.delta))
78
+ return null;
79
+ const firma = normalizarFirma(p.firma);
80
+ if (firma === null)
81
+ return null;
82
+ return {
83
+ id: p.id,
84
+ tipo: typeof p.tipo === "string" ? p.tipo : "",
85
+ proyecto: typeof p.proyecto === "string" ? p.proyecto : "",
86
+ sujeto: p.sujeto,
87
+ delta: p.delta,
88
+ razon: typeof p.razon === "string" ? p.razon : "",
89
+ emisor: p.emisor,
90
+ timestamp: typeof p.timestamp === "number" && Number.isFinite(p.timestamp)
91
+ ? p.timestamp
92
+ : 0,
93
+ firma,
94
+ };
95
+ }
96
+ /**
97
+ * Rehidrata una firma que llegó como ArrayBuffer o como array de números
98
+ * (round-trip JSON) al Uint8Array que espera KarmaManager.verify. Un transporte
99
+ * que la estropeó más allá de lo reconocible da null y se rechaza.
100
+ */
101
+ function normalizarFirma(firma) {
102
+ if (firma instanceof Uint8Array)
103
+ return firma.length > 0 ? firma : null;
104
+ if (firma instanceof ArrayBuffer)
105
+ return firma.byteLength > 0 ? new Uint8Array(firma) : null;
106
+ if (Array.isArray(firma)) {
107
+ if (firma.length === 0)
108
+ return null;
109
+ if (!firma.every((b) => typeof b === "number" && Number.isInteger(b) && b >= 0 && b <= 255))
110
+ return null;
111
+ return new Uint8Array(firma);
112
+ }
113
+ return null;
114
+ }
115
+ // ─── MESH MANAGER ─────────────────────────────────────────────────────────
116
+ export class MeshManager extends EventTarget {
117
+ config;
118
+ edgeMesh;
119
+ peers;
120
+ gossipsVistos;
121
+ gossipRateLimiter = new TokenBucketRateLimiter({
122
+ tokensPerInterval: 100,
123
+ intervalMs: 1000,
124
+ maxTokens: 200,
125
+ });
126
+ namespacePeers;
127
+ /**
128
+ * Motor de karma al que replicar. Ausente significa que este MeshManager es un
129
+ * portador puro (solo chat/sync) y que los avales remotos se ignoran en vez de
130
+ * validarse con un doble.
131
+ */
132
+ consumidorKarma;
133
+ /** Aplicaciones de karma en vuelo, para poder esperar la propagacion sin dormir. */
134
+ karmaEnCurso = new Set();
135
+ /**
136
+ * Cola de diagnostico de divergencias de reputacion observadas, de la mas
137
+ * antigua a la mas reciente, acotada por {@link MAX_DIVERGENCIAS_REGISTRADAS}.
138
+ *
139
+ * Acotada y SOLO EN MEMORIA, a proposito. Esto no es un libro mayor y no debe
140
+ * convertirse en uno: escribir registros de divergencia en el OpLog seria el
141
+ * primer paso de la reconciliación, que es explicitamente una fase posterior.
142
+ * Perder esta cola al reiniciar es el coste aceptado de "hacerlo visible ahora,
143
+ * reconciliar despues".
144
+ */
145
+ divergencias = [];
146
+ /**
147
+ * Serializa las aplicaciones de karma para que la lectura de score antes/despues
148
+ * de cada una abarque exactamente su propia aplicacion. Ver
149
+ * {@link MeshManager.encolarAplicacionKarma} para por qué importa.
150
+ *
151
+ * Cadena resuelta, nunca rechazada: cada eslabon captura su propio fallo, asi
152
+ * que un aval que lance no puede bloquear a todos los que tiene detrás.
153
+ */
154
+ colaKarma = Promise.resolve();
155
+ activo = false;
156
+ intervalos = {};
157
+ constructor(config, edgeMesh, consumidorOrOpciones) {
158
+ super();
159
+ this.edgeMesh = edgeMesh;
160
+ this.peers = new Map();
161
+ this.gossipsVistos = new Set();
162
+ this.namespacePeers = new Map();
163
+ this.consumidorKarma = resolverConsumidorKarma(consumidorOrOpciones);
164
+ this.config = {
165
+ nodoId: config.nodoId,
166
+ fanOut: config.fanOut ?? FAN_OUT_POR_DEFECTO,
167
+ maxPeers: config.maxPeers ?? MAX_PEERS_POR_NODO,
168
+ heartbeatIntervalMs: config.heartbeatIntervalMs ?? HEARTBEAT_MESH_MS,
169
+ peerTimeoutMs: config.peerTimeoutMs ?? TIMEOUT_PEER_MS,
170
+ gossipTTL: config.gossipTTL ?? GOSSIP_TTL_POR_DEFECTO,
171
+ estrategia: config.estrategia ?? ESTRATEGIA_FAN_OUT.ALEATORIA,
172
+ namespacePorDefecto: config.namespacePorDefecto ?? "global",
173
+ };
174
+ }
175
+ // ─── CICLO DE VIDA ───────────────────────────────────────────────────
176
+ async iniciar() {
177
+ if (this.activo)
178
+ return;
179
+ this.activo = true;
180
+ // Heartbeat periódico
181
+ this.intervalos.heartbeat = setInterval(() => {
182
+ void this.transmitirHeartbeat();
183
+ }, this.config.heartbeatIntervalMs);
184
+ // Limpieza periódica de peers caídos y gossip cache
185
+ this.intervalos.limpieza = setInterval(() => {
186
+ this.limpiarPeersCaidos();
187
+ this.limpiarGossipCache();
188
+ }, INTERVALO_LIMPIEZA_MS);
189
+ // Escuchar eventos del edge mesh para actualizar peers
190
+ this.edgeMesh.on("nodoConectado", (ev) => {
191
+ void this.conectarPeer(ev.detail.nodoId);
192
+ });
193
+ this.edgeMesh.on("nodoDesconectado", (ev) => {
194
+ void this.desconectarPeer(ev.detail.nodoId);
195
+ });
196
+ }
197
+ async detener() {
198
+ this.activo = false;
199
+ if (this.intervalos.heartbeat !== undefined) {
200
+ clearInterval(this.intervalos.heartbeat);
201
+ }
202
+ if (this.intervalos.limpieza !== undefined) {
203
+ clearInterval(this.intervalos.limpieza);
204
+ }
205
+ this.peers.clear();
206
+ this.gossipsVistos.clear();
207
+ this.namespacePeers.clear();
208
+ // Las divergencias tambien se van. `detener` devuelve la malla a una hoja
209
+ // limpia, y la cola describe una sesion de trafico: conservar registros
210
+ // viejos junto a un nodo reiniciado haria que un operador los leyera como
211
+ // estado actual.
212
+ this.divergencias.length = 0;
213
+ }
214
+ // ─── GESTION DE PEERS ────────────────────────────────────────────────
215
+ async conectarPeer(peerId, namespace) {
216
+ if (peerId === this.config.nodoId)
217
+ return;
218
+ if (!this.activo)
219
+ return;
220
+ const existente = this.peers.get(peerId);
221
+ if (existente !== undefined) {
222
+ // Actualizar estado
223
+ this.peers.set(peerId, {
224
+ ...existente,
225
+ estado: "activo",
226
+ ultimoHeartbeat: Date.now(),
227
+ namespace: namespace ?? existente.namespace,
228
+ });
229
+ return;
230
+ }
231
+ // Verificar límite de peers
232
+ if (this.peers.size >= this.config.maxPeers) {
233
+ // Reemplazar el peer más inactivo
234
+ const peorPeer = this.encontrarPeorPeer();
235
+ if (peorPeer !== null) {
236
+ this.peers.delete(peorPeer);
237
+ }
238
+ else {
239
+ return; // No se puede conectar más peers
240
+ }
241
+ }
242
+ const peerInfo = {
243
+ nodoId: peerId,
244
+ timestamp: Date.now(),
245
+ ultimoHeartbeat: Date.now(),
246
+ latenciaMs: 0,
247
+ fanOutIndex: Math.floor(Math.random() * this.config.fanOut),
248
+ estado: "activo",
249
+ intentosReconexion: 0,
250
+ namespace,
251
+ };
252
+ this.peers.set(peerId, peerInfo);
253
+ // Registrar en el namespace correspondiente
254
+ const ns = namespace ?? this.config.namespacePorDefecto;
255
+ this.agregarPeerANamespace(peerId, ns);
256
+ this.dispatchEvent(new CustomEvent("peerConectado", {
257
+ detail: { peerId, namespace: ns },
258
+ }));
259
+ this.emitMeshSalud();
260
+ }
261
+ async desconectarPeer(peerId) {
262
+ this.peers.delete(peerId);
263
+ // Remover de todos los namespaces
264
+ for (const [, peers] of this.namespacePeers) {
265
+ peers.delete(peerId);
266
+ }
267
+ this.dispatchEvent(new CustomEvent("peerDesconectado", { detail: { peerId } }));
268
+ this.emitMeshSalud();
269
+ }
270
+ encontrarPeorPeer() {
271
+ let peorId = null;
272
+ let peorLatencia = -1;
273
+ for (const [id, info] of this.peers) {
274
+ if (info.estado === "caido" || info.latenciaMs > peorLatencia) {
275
+ peorId = id;
276
+ peorLatencia = info.latenciaMs;
277
+ }
278
+ }
279
+ return peorId;
280
+ }
281
+ // ─── NAMESPACE-AWARE ROUTING ─────────────────────────────────────────
282
+ agregarPeerANamespace(peerId, namespace) {
283
+ let peers = this.namespacePeers.get(namespace);
284
+ if (peers === undefined) {
285
+ peers = new Set();
286
+ this.namespacePeers.set(namespace, peers);
287
+ }
288
+ peers.add(peerId);
289
+ }
290
+ async unirANamespace(namespace, peerId) {
291
+ const targetPeer = peerId ?? this.config.nodoId;
292
+ this.agregarPeerANamespace(targetPeer, namespace);
293
+ // Notificar al mesh completo del namespace change via broadcast
294
+ // que será limitado por fan-out
295
+ }
296
+ async abandonarNamespace(namespace, peerId) {
297
+ const targetPeer = peerId ?? this.config.nodoId;
298
+ const peers = this.namespacePeers.get(namespace);
299
+ if (peers !== undefined) {
300
+ peers.delete(targetPeer);
301
+ }
302
+ }
303
+ obtenerPeersEnNamespace(namespace) {
304
+ const peers = this.namespacePeers.get(namespace);
305
+ if (peers === undefined)
306
+ return [];
307
+ return Array.from(peers).filter((p) => this.peers.has(p));
308
+ }
309
+ // ─── GOSSIP PROTOCOL ─────────────────────────────────────────────────
310
+ async transmitirConGossip(namespace, payload, fanOut) {
311
+ if (!this.activo)
312
+ return;
313
+ const mensaje = {
314
+ id: generarNonce(),
315
+ namespace,
316
+ ttl: this.config.gossipTTL,
317
+ payload,
318
+ origen: this.config.nodoId,
319
+ timestamp: Date.now(),
320
+ ruta: [this.config.nodoId],
321
+ };
322
+ // Marcar como visto para no re-procesar
323
+ this.gossipsVistos.add(mensaje.id);
324
+ // Seleccionar peers según estrategia
325
+ const peersEnNamespace = this.obtenerPeersEnNamespace(namespace);
326
+ const peersObjetivo = this.seleccionarPeersParaFanOut(peersEnNamespace, fanOut ?? this.config.fanOut, []);
327
+ // Propagar a peers seleccionados
328
+ const promesas = peersObjetivo.map(async (peerId) => {
329
+ try {
330
+ const env = createEnvelope(TIPO_MENSAJE.GOVERNANCE, this.config.nodoId, peerId, { tipo: "gossip", mensaje });
331
+ await this.edgeMesh.enviar(peerId, env);
332
+ this.actualizarLatencia(peerId);
333
+ }
334
+ catch {
335
+ // Peer puede estar caído, marcar
336
+ this.marcarPeerCaido(peerId);
337
+ }
338
+ });
339
+ await Promise.allSettled(promesas);
340
+ }
341
+ seleccionarPeersParaFanOut(candidatos, fanOut, excluir) {
342
+ const disponibles = candidatos.filter((p) => p !== this.config.nodoId &&
343
+ !excluir.includes(p) &&
344
+ this.peers.get(p)?.estado === "activo");
345
+ if (disponibles.length <= fanOut)
346
+ return disponibles;
347
+ switch (this.config.estrategia) {
348
+ case ESTRATEGIA_FAN_OUT.ALEATORIA: {
349
+ return this.seleccionAleatoria(disponibles, fanOut);
350
+ }
351
+ case ESTRATEGIA_FAN_OUT.POR_SALUD: {
352
+ return this.seleccionPorSalud(disponibles, fanOut);
353
+ }
354
+ case ESTRATEGIA_FAN_OUT.POR_LATENCIA: {
355
+ return this.seleccionPorLatencia(disponibles, fanOut);
356
+ }
357
+ default: {
358
+ return this.seleccionAleatoria(disponibles, fanOut);
359
+ }
360
+ }
361
+ }
362
+ seleccionAleatoria(peers, count) {
363
+ const shuffled = [...peers].sort(() => Math.random() - 0.5);
364
+ return shuffled.slice(0, count);
365
+ }
366
+ seleccionPorSalud(peers, count) {
367
+ const ordenados = [...peers].sort((a, b) => {
368
+ const pa = this.peers.get(a);
369
+ const pb = this.peers.get(b);
370
+ if (pa === undefined && pb === undefined)
371
+ return 0;
372
+ if (pa === undefined)
373
+ return -1;
374
+ if (pb === undefined)
375
+ return 1;
376
+ return (pa.latenciaMs ?? Infinity) - (pb.latenciaMs ?? Infinity);
377
+ });
378
+ return ordenados.slice(0, count);
379
+ }
380
+ seleccionPorLatencia(peers, count) {
381
+ return this.seleccionPorSalud(peers, count);
382
+ }
383
+ // ─── PROCESAR GOSSIP ─────────────────────────────────────────────────
384
+ /**
385
+ * EL CAMINO DE REPLICACIÓN DE LA REPUTACIÓN.
386
+ *
387
+ * ─── POR QUÉ EXISTE ESTE GANCHO ────────────────────────────────────────
388
+ *
389
+ * Hasta ahora `KarmaManager` escribía los avales en el OpLog LOCAL
390
+ * (`karma:emit`) y ahí terminaba, mientras que `procesarGossip` — lo único en
391
+ * el repo que podía haberlos llevado a un par — nunca lo invocaba nadie. Dos
392
+ * observadores honestos del mismo aval sostenían por tanto scores distintos.
393
+ *
394
+ * ─── MODELO DE CONFIANZA: EL GOSSIP ES UN PORTADOR, NO UNA AUTORIDAD ────
395
+ *
396
+ * Un payload de gossip es datos controlados por un atacante. Puede ser
397
+ * reenviado, falsificado, re-firmado por un par coludido, o enviado por un nodo
398
+ * que nunca ha sido avalado por nadie. Por eso este método NUNCA escribe en un
399
+ * OpLog y NUNCA toca un score. Hace exactamente una cosa con el payload:
400
+ * entregarlo a `ConsumidorKarma.aplicarTransaccion`, que es la entrada publica
401
+ * del propio KarmaManager. Ese método es el único lugar donde se verifica la
402
+ * firma ML-DSA-65 contra la clave REGISTRADA del emisor, se rechaza la
403
+ * auto-emisión, se aplica el techo de delta y el reenvío del mismo `tx.id` es
404
+ * idempotente. Reimplementar aquí cualquiera de esas reglas crearía un segundo
405
+ * validador más débil, que es exactamente el bug que este camino cierra.
406
+ *
407
+ * En consecuencia, un par NUNCA puede inyectar karma en un nodo que no ha
408
+ * firmado un aval: sin una firma válida de una clave en la que el nodo confía, la
409
+ * transacción se rechaza y ningún score se mueve.
410
+ *
411
+ * La confianza en claves públicas deliberadamente NO se toma del payload de
412
+ * gossip (eso dejaría que cualquier par respaldara a cualquier otro). Las únicas
413
+ * claves que se registran desde aquí son las que EdgeMesh ya autenticó en el
414
+ * handshake, leídas vía `obtenerClavePublica`.
415
+ *
416
+ * ─── LIMITACIÓN CONOCIDA — MEJOR ESFUERZO, NO CONSENSO (SIN RESOLVER) ───
417
+ *
418
+ * El gossip es mejor-esfuerzo. TTL, el rate limiter de tokens, pares offline y
419
+ * la seleccion de fan-out significan que un aval PUEDE PERDERSE: un médico que
420
+ * está offline cuando se gossipea su aval nunca se entera. Esta capa convierte
421
+ * por tanto la reputación en una SEÑAL que se propaga oportunísticamente, NO en
422
+ * un libro mayor autoritativo. Dos nodos pueden sostener legítimamente scores
423
+ * distintos para el mismo sujeto, y ningún reintento dentro de esta capa lo
424
+ * arregla — es un problema de convergencia, no de transporte.
425
+ *
426
+ * ─── QUÉ HACE ESTE MÉTODO AL RESPECTO: DETECTA, NO CORRIGE ─────────────
427
+ *
428
+ * Antes de este cambio la capa aplicaba el aval y seguía, así que el score se
429
+ * corregía solo y en silencio y ningún operador se enteraba de que dos nodos
430
+ * honestos habían mostrado reputaciones distintas del mismo médico. Ahora lee
431
+ * el score justo antes y justo después de la aplicación y, cuando el aval lo
432
+ * movió, emite `karmaDivergente` nombrando al sujeto, el score desfasado, el
433
+ * nuevo y el aval responsable.
434
+ *
435
+ * LA CORRECCIÓN SIGUE SIENDO LA DEL MOTOR. Esta capa lee scores y los reporta;
436
+ * solo KarmaManager decide qué es un score. Y nada se reconcilia: sin escritura
437
+ * en el OpLog, sin reordenación, sin retractación, sin intentar que los pares
438
+ * coincidan. Hacerlos coincidir es la fase siguiente y necesita diseño de
439
+ * quórum/anti-entropía que no pertenece a un portador.
440
+ *
441
+ * La solución real es un protocolo de reconciliación y está DELIBERADAMENTE NO
442
+ * IMPLEMENTADO AQUÍ. Direcciones candidatas, ninguna elegida aún:
443
+ * (a) Anti-entropía de OpLog: intercambio periódico de entradas `karma:emit`
444
+ * indexadas por `tx.id`, aplicadas vía `aplicarTransaccion` (la
445
+ * idempotencia hace gratis los reenvíos), para que un nodo que regresa se
446
+ * ponga al día.
447
+ * (b) Quórum/acuerdo Bizantino sobre deltas, para que un score se acredite
448
+ * solo cuando una supermayoría de pares lo ha visto de forma
449
+ * independiente.
450
+ * (c) Exposición explícita del conflicto: divergencia (el conjunto de `tx.id`s)
451
+ * para que un operador pueda ver que dos nodos honestos discrepan en vez
452
+ * de confiar calladamente en el gossip que llegó primero. ← esta fase: el
453
+ * evento `karmaDivergente` y {@link MeshManager.obtenerDivergencias} son
454
+ * (c), acotado a lo que la sola llegada de gossip puede probar.
455
+ * Sea cual se elija, los invariantes se mantienen: el karma nunca otorga acceso,
456
+ * y solo un aval firmado puede mover un score. Hasta entonces, quien consuma un
457
+ * número de karma debe tratarlo como una pista no verificada — nunca como una
458
+ * credencial.
459
+ *
460
+ * @returns el motivo del rechazo, o null cuando la transacción se aplicó (o fue
461
+ * un duplicado ya aplicado, que KarmaManager dobla en `null` por
462
+ * diseño).
463
+ */
464
+ async aplicarKarmaRemoto(tx, desde) {
465
+ const consumidor = this.consumidorKarma;
466
+ if (consumidor === undefined) {
467
+ // Sin consumidor conectado: la malla sigue siendo un portador tonto.
468
+ // Nada se aplica, así que un MeshManager usado para chat/sync no puede
469
+ // crecer por accidente un motor de karma.
470
+ return null;
471
+ }
472
+ // Puente de las claves de par que la malla ya autenticó en el handshake al
473
+ // motor de karma. Nunca una clave tomada del payload.
474
+ if (consumidor.registrarClavePublica !== undefined) {
475
+ const clave = this.obtenerClavePublicaDePeer(tx.emisor);
476
+ if (clave !== undefined) {
477
+ // Llamado como método, nunca desligado: el registro de
478
+ // KarmaManager vive en su propia instancia y una referencia suelta
479
+ // perdería `this` y lanzaría.
480
+ consumidor.registrarClavePublica(tx.emisor, clave);
481
+ }
482
+ }
483
+ // Lee el score que vamos a mover, ANTES de la aplicación. Un throw aquí no
484
+ // debe saltarse el aval: una capa de detección que puede vetar reputación es
485
+ // exactamente la autoridad que este archivo se niega a ser. Así que la
486
+ // lectura va aislada y su ausencia solo cuesta visibilidad, nunca el aval.
487
+ const scoreAntes = this.leerScoreSiPuede(consumidor, tx.sujeto);
488
+ const motivo = await consumidor.aplicarTransaccion(tx);
489
+ if (motivo !== null) {
490
+ this.dispatchEvent(new CustomEvent("karmaRechazado", {
491
+ detail: { txId: tx.id, motivo, desde },
492
+ }));
493
+ return motivo;
494
+ }
495
+ this.dispatchEvent(new CustomEvent("karmaRecibido", { detail: { tx, desde } }));
496
+ // ─── DETECCIÓN DE DIVERGENCIA ───────────────────────────────────
497
+ //
498
+ // El aval fue aceptado. Si el score del sujeto se MOVIÓ, este nodo nunca
499
+ // había visto ese `tx.id` — KarmaManager dobla un duplicado en `null` sin
500
+ // tocar el score — así que nuestra vista estaba desfasada exactamente en
501
+ // `delta` y la divergencia ya es un hecho, no una sospecha. Un duplicado
502
+ // deja el score intacto y no emite nada, que es lo que mantiene significativo
503
+ // el evento.
504
+ //
505
+ // Determinista por construcción, no heurístico: sin umbral, sin reloj, sin
506
+ // ventana. El mismo conjunto de avales en el mismo orden siempre produce los
507
+ // mismos eventos, y dos nodos que vieron el mismo conjunto en distinto orden
508
+ // son justo lo que esto reporta.
509
+ if (scoreAntes !== null) {
510
+ const scoreDespues = this.leerScoreSiPuede(consumidor, tx.sujeto);
511
+ if (scoreDespues !== null && scoreDespues !== scoreAntes) {
512
+ this.registrarDivergencia({
513
+ sujeto: tx.sujeto,
514
+ txId: tx.id,
515
+ emisor: tx.emisor,
516
+ desde,
517
+ delta: tx.delta,
518
+ scoreLocalAntes: scoreAntes,
519
+ scoreLocalDespues: scoreDespues,
520
+ motivo: "aval_tardio",
521
+ });
522
+ }
523
+ }
524
+ return null;
525
+ }
526
+ /**
527
+ * Lee un score, convirtiendo cualquier mal comportamiento del consumidor en
528
+ * "desconocido".
529
+ *
530
+ * Devuelve `null` — nunca un `0` fabricado — para que un lector roto sea
531
+ * indistinguible de uno ausente. Inventar un cero aquí haría que el informe de
532
+ * divergencia afirmara un movimiento que nunca ocurrió, que es peor que
533
+ * callarse: un operador no podría distinguir una discrepancia real de una
534
+ * inventada.
535
+ */
536
+ leerScoreSiPuede(consumidor, nodoId) {
537
+ try {
538
+ const score = consumidor.getScore(nodoId);
539
+ return typeof score === "number" && Number.isFinite(score) ? score : null;
540
+ }
541
+ catch {
542
+ return null;
543
+ }
544
+ }
545
+ /**
546
+ * Registra una divergencia y la anuncia.
547
+ *
548
+ * Ambas mitades importan y sirven a lectores distintos: el evento llega a quien
549
+ * esté mirando la malla en vivo, mientras que
550
+ * {@link MeshManager.obtenerDivergencias} permite a un operador descubrir A
551
+ * POSTERIORI EL HECHO que dos nodos discrepaban — incluidas divergencias
552
+ * ocurridas mientras nadie escuchaba. Solo con un evento, la divergencia
553
+ * seguiría desapareciendo en silencio justo en el momento en que importaba.
554
+ *
555
+ * `karmaRecibido` se emite primero, así que un listener que reacciona a un
556
+ * score nuevo ya tiene el registro de divergencia garantizado.
557
+ */
558
+ registrarDivergencia(detalle) {
559
+ this.divergencias.push(detalle);
560
+ // Descarta los más antiguos en vez de crecer sin límite. Perder las
561
+ // divergencias más antiguas es estrictamente mejor que un crecimiento de
562
+ // memoria sin límite alimentado por entrada remota; el OpLog — no esta cola
563
+ // — es donde vive el historial durable.
564
+ if (this.divergencias.length > MAX_DIVERGENCIAS_REGISTRADAS) {
565
+ this.divergencias.splice(0, this.divergencias.length - MAX_DIVERGENCIAS_REGISTRADAS);
566
+ }
567
+ this.dispatchEvent(new CustomEvent("karmaDivergente", { detail: detalle }));
568
+ }
569
+ /** Lee la clave pública de un par del registro de EdgeMesh (verificada en handshake). */
570
+ obtenerClavePublicaDePeer(nodoId) {
571
+ const registros = this.edgeMesh;
572
+ if (typeof registros.obtenerClavePublica !== "function")
573
+ return undefined;
574
+ try {
575
+ return registros.obtenerClavePublica(nodoId);
576
+ }
577
+ catch {
578
+ return undefined;
579
+ }
580
+ }
581
+ /**
582
+ * Conecta (o desconecta) el motor de karma al que este MeshManager replica.
583
+ *
584
+ * Existe porque el orden de construccion lo impone: `EdgeMesh` crea el
585
+ * MeshManager en su constructor, y `MalocaKernel` crea el KarmaManager DESPUES
586
+ * de `super(config)`. Sin este gancho, la malla quedaria permanentemente sin
587
+ * consumidor y los avales seguirian sin viajar.
588
+ *
589
+ * Pasar `undefined` devuelve la malla a portador puro: deja de aplicar avales
590
+ * en vez de inventar un motor propio.
591
+ */
592
+ setConsumidorKarma(consumidor) {
593
+ this.consumidorKarma = consumidor;
594
+ }
595
+ /**
596
+ * Gossipea un aval a la malla. Quien llame le pasa el objeto transacción que
597
+ * devolvió `KarmaManager.emit`, firma incluida — una transacción sin firma es
598
+ * inútil para todo par, y KarmaManager ya garantiza que no puede producirse.
599
+ */
600
+ async transmitirKarma(tx, namespace) {
601
+ await this.transmitirConGossip(namespace ?? this.config.namespacePorDefecto, {
602
+ tipo: PAYLOAD_KARMA_TIPO,
603
+ tx,
604
+ });
605
+ }
606
+ /**
607
+ * Resuelve cuando todas las transacciones de karma en vuelo se han
608
+ * estabilizado. Los tests y gateways lo usan en vez de dormir; la malla nunca
609
+ * espera la aplicación de gossip en el camino caliente.
610
+ */
611
+ async esperarPropagacionKarma() {
612
+ while (this.karmaEnCurso.size > 0) {
613
+ await Promise.allSettled(Array.from(this.karmaEnCurso));
614
+ }
615
+ }
616
+ /**
617
+ * Extrae un aval de un payload de gossip y lo aplica por el motor de karma.
618
+ * Cualquier cosa que no sea reconociblemente un aval se ignora: otros tipos de
619
+ * payload (eventos maloca, descubrimiento de plugins) no son asunto nuestro y
620
+ * se dejan a los listeners de `gossipRecibido`.
621
+ */
622
+ enrutarKarmaDeGossip(mensaje) {
623
+ const consumidor = this.consumidorKarma;
624
+ if (consumidor === undefined)
625
+ return;
626
+ const payload = mensaje.payload;
627
+ if (payload === null || typeof payload !== "object")
628
+ return;
629
+ if (payload.tipo !== PAYLOAD_KARMA_TIPO)
630
+ return;
631
+ const tx = normalizarTransaccionKarma(payload.tx);
632
+ if (tx === null) {
633
+ const tarea = (async () => {
634
+ this.dispatchEvent(new CustomEvent("karmaRechazado", {
635
+ detail: {
636
+ txId: null,
637
+ motivo: "payload_malformado",
638
+ desde: mensaje.origen,
639
+ },
640
+ }));
641
+ })();
642
+ this.karmaEnCurso.add(tarea);
643
+ void tarea.finally(() => this.karmaEnCurso.delete(tarea));
644
+ return;
645
+ }
646
+ const desde = mensaje.ruta.length > 0
647
+ ? mensaje.ruta[mensaje.ruta.length - 1]
648
+ : mensaje.origen;
649
+ this.encolarAplicacionKarma(tx, desde);
650
+ }
651
+ /**
652
+ * Aplica avales de UNO EN UNO, en orden de llegada.
653
+ *
654
+ * Por qué la cola, dado que el gossip deliberadamente no se espera en el
655
+ * camino caliente: `aplicarKarmaRemoto` lee el score antes de la aplicación y
656
+ * otra vez después, con un `await` en medio (la comprobación de firma es una
657
+ * operación PQC real). Sin serialización, varios gossips procesados en el mismo
658
+ * tick leerían el MISMO score "anterior" y cada uno aplicaría encima de él — así
659
+ * que el `scoreLocalAntes` registrado sería el valor de antes de todo el lote y
660
+ * no el valor que el nodo realmente mostraba cuando llegó ese aval. El evento
661
+ * seguiría disparándose, pero describiría un score que nadie vio, que es
662
+ * exactamente el tipo de número silenciosamente incorrecto que esta fase existe
663
+ * para dejar de publicar.
664
+ *
665
+ * La cola conserva el orden de llegada (`.then` sobre la tarea anterior), así
666
+ * que la secuencia es determinista e idéntica en cada nodo que vio los mismos
667
+ * gossips en el mismo orden.Tampoco cambia el resultado independiente del
668
+ * orden: KarmaManager suma deltas, así que el score FINAL es el mismo
669
+ * cualquiera — la cola hace que el reporte intermedio sea fiel, no que el
670
+ * total sea distinto.
671
+ *
672
+ * Los fallos no atascan la cola: cada eslabon absorbe su propio rechazo, así
673
+ * que un aval envenenado no bloquea a todos los que tiene detrás.
674
+ */
675
+ encolarAplicacionKarma(tx, desde) {
676
+ const tarea = this.colaKarma
677
+ .then(() => this.aplicarKarmaRemoto(tx, desde))
678
+ .catch(() => null);
679
+ this.colaKarma = tarea;
680
+ this.karmaEnCurso.add(tarea);
681
+ void tarea.finally(() => this.karmaEnCurso.delete(tarea));
682
+ }
683
+ procesarGossip(mensaje) {
684
+ // Verificar mensaje y TTL
685
+ if (!mensaje ||
686
+ typeof mensaje !== "object" ||
687
+ typeof mensaje.ttl !== "number" ||
688
+ mensaje.ttl <= 0)
689
+ return;
690
+ // Verificar duplicado
691
+ if (this.gossipsVistos.has(mensaje.id))
692
+ return;
693
+ // Rate Limiting
694
+ const peerId = mensaje.ruta.length > 0
695
+ ? mensaje.ruta[mensaje.ruta.length - 1]
696
+ : mensaje.origen;
697
+ if (!this.gossipRateLimiter.consume(peerId)) {
698
+ console.warn(`Rate limit exceeded for peer: ${peerId} in gossip receive`);
699
+ this.dispatchEvent(new CustomEvent("rate_limited", {
700
+ detail: { peerId, resource: "gossip" },
701
+ }));
702
+ return;
703
+ }
704
+ // Marcar como visto
705
+ this.gossipsVistos.add(mensaje.id);
706
+ // Verificar que estamos en el namespace
707
+ const peersEnNs = this.namespacePeers.get(mensaje.namespace);
708
+ if (peersEnNs === undefined || !peersEnNs.has(this.config.nodoId)) {
709
+ // Si no estamos en el namespace, no propagamos
710
+ // Pero procesamos si el payload es relevante
711
+ }
712
+ // Replicacion de reputacion: enruta el aval por KarmaManager. Se dispara sin
713
+ // await para que el reenvio de gossip siga siendo rapido; los errores quedan
714
+ // contenidos y se hace visible como `karmaRechazado` en aplicarKarmaRemoto.
715
+ this.enrutarKarmaDeGossip(mensaje);
716
+ this.dispatchEvent(new CustomEvent("gossipRecibido", { detail: { mensaje } }));
717
+ // Re-propagar con TTL reducido
718
+ if (mensaje.ttl > 1) {
719
+ const mensajeReenviado = {
720
+ ...mensaje,
721
+ ttl: mensaje.ttl - 1,
722
+ ruta: [...mensaje.ruta, this.config.nodoId],
723
+ };
724
+ const peersParaReenvio = this.seleccionarPeersParaFanOut(this.obtenerPeersEnNamespace(mensaje.namespace), this.config.fanOut, mensaje.ruta);
725
+ for (const peerId of peersParaReenvio) {
726
+ if (peerId === this.config.nodoId)
727
+ continue;
728
+ void this.reenviarGossip(peerId, mensajeReenviado);
729
+ }
730
+ }
731
+ }
732
+ async reenviarGossip(peerId, mensaje) {
733
+ try {
734
+ const env = createEnvelope(TIPO_MENSAJE.GOVERNANCE, this.config.nodoId, peerId, { tipo: "gossip", mensaje });
735
+ await this.edgeMesh.enviar(peerId, env);
736
+ }
737
+ catch {
738
+ // Ignorar errores individuales
739
+ }
740
+ }
741
+ // ─── HEARTBEAT ───────────────────────────────────────────────────────
742
+ async transmitirHeartbeat() {
743
+ if (!this.activo || this.peers.size === 0)
744
+ return;
745
+ const peersActivos = this.obtenerPeersActivos();
746
+ const fanOut = Math.min(this.config.fanOut, peersActivos.length);
747
+ const objetivos = this.seleccionAleatoria(peersActivos, fanOut);
748
+ const heartbeatPayload = {
749
+ nodoId: this.config.nodoId,
750
+ timestamp: Date.now(),
751
+ peersConocidos: Array.from(this.peers.keys()),
752
+ namespaces: Array.from(this.namespacePeers.keys()),
753
+ };
754
+ for (const peerId of objetivos) {
755
+ try {
756
+ const env = createEnvelope(TIPO_MENSAJE.HEARTBEAT, this.config.nodoId, peerId, heartbeatPayload);
757
+ await this.edgeMesh.enviar(peerId, env);
758
+ this.actualizarLatencia(peerId);
759
+ }
760
+ catch {
761
+ this.marcarPeerCaido(peerId);
762
+ }
763
+ }
764
+ }
765
+ procesarHeartbeatPeer(peerId, peersConocidos, namespaces) {
766
+ const existente = this.peers.get(peerId);
767
+ if (existente !== undefined) {
768
+ this.peers.set(peerId, {
769
+ ...existente,
770
+ ultimoHeartbeat: Date.now(),
771
+ estado: "activo",
772
+ intentosReconexion: 0,
773
+ });
774
+ }
775
+ else {
776
+ // Auto-descubrimiento: conectar si hay espacio
777
+ if (this.peers.size < this.config.maxPeers) {
778
+ void this.conectarPeer(peerId);
779
+ }
780
+ }
781
+ // Descubrir nuevos peers via heartbeat de otros
782
+ for (const conocido of peersConocidos) {
783
+ if (!this.peers.has(conocido) && conocido !== this.config.nodoId) {
784
+ this.dispatchEvent(new CustomEvent("peerDescubierto", {
785
+ detail: { peerId: conocido, via: peerId },
786
+ }));
787
+ }
788
+ }
789
+ // Registrar namespaces
790
+ for (const ns of namespaces) {
791
+ this.agregarPeerANamespace(peerId, ns);
792
+ }
793
+ this.emitMeshSalud();
794
+ }
795
+ // ─── PEER DISCOVERY ──────────────────────────────────────────────────
796
+ async descubrirSalon(salonId) {
797
+ // Buscar peers que estén en el namespace del salón
798
+ const peersEnSalon = this.namespacePeers.get(`salon:${salonId}`);
799
+ if (peersEnSalon !== undefined) {
800
+ return Array.from(peersEnSalon);
801
+ }
802
+ // Preguntar via gossip a peers conocidos
803
+ const preguntaId = `discover:${salonId}:${Date.now()}`;
804
+ await this.transmitirConGossip(this.config.namespacePorDefecto, { tipo: "discover", salonId, preguntaId }, 3);
805
+ // Retornar lo que tenemos (puede estar vacío si nadie responde aún)
806
+ return [];
807
+ }
808
+ // ─── CONSULTAS ───────────────────────────────────────────────────────
809
+ obtenerPeersConectados() {
810
+ return Array.from(this.peers.keys());
811
+ }
812
+ /**
813
+ * Divergencias que este nodo ha observado, de la más antigua a la más reciente,
814
+ * acotadas por {@link MAX_DIVERGENCIAS_REGISTRADAS}.
815
+ *
816
+ * La respuesta a "¿mi red está de acuerdo conmigo?" — la pregunta que un
817
+ * presupuesto de divergencia v1 no puede responder por sí solo, ya que todo el
818
+ * coste del modelo es que el score PODRÍA estar mal.
819
+ *
820
+ * Devuelve una copia: la cola es contabilidad interna y handing fuera una
821
+ * referencia viva dejaría que un llamador mutara el estado de detección desde
822
+ * fuera.
823
+ *
824
+ * Vacío significa "no se observó divergencia DESDE QUE ARRANCÓ ESTE PROCESO", no
825
+ * "la red está de acuerdo". No es una prueba de consenso, y nada de esto
826
+ * debería presentarse como tal a un clínico.
827
+ */
828
+ obtenerDivergencias() {
829
+ return [...this.divergencias];
830
+ }
831
+ obtenerPeersActivos() {
832
+ const ahora = Date.now();
833
+ return Array.from(this.peers.entries())
834
+ .filter(([_, info]) => ahora - info.ultimoHeartbeat < this.config.peerTimeoutMs)
835
+ .map(([id, _]) => id);
836
+ }
837
+ obtenerPeersLentos() {
838
+ return Array.from(this.peers.entries())
839
+ .filter(([_, info]) => info.estado === "lento")
840
+ .map(([id, _]) => id);
841
+ }
842
+ obtenerPeerInfo(peerId) {
843
+ return this.peers.get(peerId) ?? null;
844
+ }
845
+ obtenerTotalPeers() {
846
+ return this.peers.size;
847
+ }
848
+ obtenerNamespaces() {
849
+ return Array.from(this.namespacePeers.keys());
850
+ }
851
+ estaActivo() {
852
+ return this.activo;
853
+ }
854
+ // ─── UTILIDADES INTERNAS ─────────────────────────────────────────────
855
+ actualizarLatencia(peerId) {
856
+ const peer = this.peers.get(peerId);
857
+ if (peer === undefined)
858
+ return;
859
+ const latencia = Date.now() - peer.ultimoHeartbeat;
860
+ this.peers.set(peerId, {
861
+ ...peer,
862
+ latenciaMs: latencia,
863
+ ultimoHeartbeat: Date.now(),
864
+ estado: latencia > 500 ? "lento" : "activo",
865
+ });
866
+ }
867
+ marcarPeerCaido(peerId) {
868
+ const peer = this.peers.get(peerId);
869
+ if (peer === undefined)
870
+ return;
871
+ const nuevosIntentos = peer.intentosReconexion + 1;
872
+ if (nuevosIntentos >= MAX_RECONEXIONES) {
873
+ // Peer definitivamente caído
874
+ void this.desconectarPeer(peerId);
875
+ }
876
+ else {
877
+ this.peers.set(peerId, {
878
+ ...peer,
879
+ estado: "caido",
880
+ intentosReconexion: nuevosIntentos,
881
+ });
882
+ }
883
+ }
884
+ limpiarPeersCaidos() {
885
+ const ahora = Date.now();
886
+ const aEliminar = [];
887
+ for (const [id, info] of this.peers) {
888
+ if (ahora - info.ultimoHeartbeat > this.config.peerTimeoutMs &&
889
+ info.intentosReconexion >= MAX_RECONEXIONES) {
890
+ aEliminar.push(id);
891
+ }
892
+ }
893
+ for (const id of aEliminar) {
894
+ void this.desconectarPeer(id);
895
+ }
896
+ }
897
+ limpiarGossipCache() {
898
+ const maxCache = 10_000;
899
+ if (this.gossipsVistos.size > maxCache) {
900
+ // Limpiar solo un batch para no bloquear
901
+ const entries = Array.from(this.gossipsVistos);
902
+ const aEliminar = entries.slice(0, entries.length - maxCache);
903
+ for (const id of aEliminar) {
904
+ this.gossipsVistos.delete(id);
905
+ }
906
+ }
907
+ }
908
+ emitMeshSalud() {
909
+ const activos = this.obtenerPeersActivos();
910
+ this.dispatchEvent(new CustomEvent("meshSaludActualizada", {
911
+ detail: {
912
+ peersActivos: activos.length,
913
+ peersTotales: this.peers.size,
914
+ },
915
+ }));
916
+ }
917
+ destruir() {
918
+ void this.detener();
919
+ }
920
+ }
921
+ export class MeshGossip extends MeshManager {
922
+ recibirGossip(_origen, mensaje) {
923
+ this.procesarGossip(mensaje);
924
+ }
925
+ async propagarGossip(mensaje) {
926
+ await this.transmitirConGossip(mensaje.namespace, mensaje.payload, mensaje.ttl);
927
+ }
928
+ }
929
+ //# sourceMappingURL=index.js.map