@myko/core 4.4.1 → 4.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (360) hide show
  1. package/dist/client.d.ts +252 -0
  2. package/dist/client.d.ts.map +1 -0
  3. package/dist/client.js +1316 -0
  4. package/dist/generated/CancelSubscription.d.ts +7 -0
  5. package/dist/generated/CancelSubscription.d.ts.map +1 -0
  6. package/{src/generated/ServerId.ts → dist/generated/CancelSubscription.js} +1 -2
  7. package/dist/generated/ChildEntities.d.ts +8 -0
  8. package/dist/generated/ChildEntities.d.ts.map +1 -0
  9. package/{src/generated/ClientId.ts → dist/generated/ChildEntities.js} +1 -2
  10. package/dist/generated/ChildEntitiesAllTime.d.ts +8 -0
  11. package/dist/generated/ChildEntitiesAllTime.d.ts.map +1 -0
  12. package/{src/generated/MEventType.ts → dist/generated/ChildEntitiesAllTime.js} +1 -2
  13. package/{src/generated/ClearClientWindbackTime.ts → dist/generated/ClearClientWindbackTime.d.ts} +1 -2
  14. package/dist/generated/ClearClientWindbackTime.d.ts.map +1 -0
  15. package/{src/generated/ClientCount.ts → dist/generated/ClearClientWindbackTime.js} +1 -2
  16. package/dist/generated/ClearClientWindbackTimeArgs.d.ts +2 -0
  17. package/dist/generated/ClearClientWindbackTimeArgs.d.ts.map +1 -0
  18. package/dist/generated/ClearClientWindbackTimeArgs.js +2 -0
  19. package/dist/generated/Client.d.ts +12 -0
  20. package/dist/generated/Client.d.ts.map +1 -0
  21. package/dist/generated/Client.js +1 -0
  22. package/dist/generated/ClientCount.d.ts +4 -0
  23. package/dist/generated/ClientCount.d.ts.map +1 -0
  24. package/dist/generated/ClientCount.js +2 -0
  25. package/dist/generated/ClientId.d.ts +2 -0
  26. package/dist/generated/ClientId.d.ts.map +1 -0
  27. package/dist/generated/ClientId.js +2 -0
  28. package/dist/generated/ClientStatus.d.ts +8 -0
  29. package/dist/generated/ClientStatus.d.ts.map +1 -0
  30. package/dist/generated/ClientStatus.js +1 -0
  31. package/dist/generated/ClientStatusOutput.d.ts +4 -0
  32. package/dist/generated/ClientStatusOutput.d.ts.map +1 -0
  33. package/dist/generated/ClientStatusOutput.js +2 -0
  34. package/dist/generated/CommandError.d.ts +6 -0
  35. package/dist/generated/CommandError.d.ts.map +1 -0
  36. package/dist/generated/CommandError.js +2 -0
  37. package/dist/generated/CommandResponse.d.ts +6 -0
  38. package/dist/generated/CommandResponse.d.ts.map +1 -0
  39. package/dist/generated/CommandResponse.js +1 -0
  40. package/dist/generated/CountAllClients.d.ts +2 -0
  41. package/dist/generated/CountAllClients.d.ts.map +1 -0
  42. package/dist/generated/CountAllClients.js +2 -0
  43. package/dist/generated/CountAllServers.d.ts +2 -0
  44. package/dist/generated/CountAllServers.d.ts.map +1 -0
  45. package/dist/generated/CountAllServers.js +2 -0
  46. package/dist/generated/CountClients.d.ts +3 -0
  47. package/dist/generated/CountClients.d.ts.map +1 -0
  48. package/dist/generated/CountClients.js +1 -0
  49. package/dist/generated/CountServers.d.ts +3 -0
  50. package/dist/generated/CountServers.d.ts.map +1 -0
  51. package/dist/generated/CountServers.js +1 -0
  52. package/dist/generated/DeleteClient.d.ts +8 -0
  53. package/dist/generated/DeleteClient.d.ts.map +1 -0
  54. package/dist/generated/DeleteClient.js +1 -0
  55. package/dist/generated/DeleteClientArgs.d.ts +5 -0
  56. package/dist/generated/DeleteClientArgs.d.ts.map +1 -0
  57. package/dist/generated/DeleteClientArgs.js +1 -0
  58. package/dist/generated/DeleteClientResult.d.ts +7 -0
  59. package/dist/generated/DeleteClientResult.d.ts.map +1 -0
  60. package/dist/generated/DeleteClientResult.js +2 -0
  61. package/dist/generated/DeleteClients.d.ts +8 -0
  62. package/dist/generated/DeleteClients.d.ts.map +1 -0
  63. package/dist/generated/DeleteClients.js +1 -0
  64. package/dist/generated/DeleteClientsArgs.d.ts +5 -0
  65. package/dist/generated/DeleteClientsArgs.d.ts.map +1 -0
  66. package/dist/generated/DeleteClientsArgs.js +1 -0
  67. package/dist/generated/DeleteClientsResult.d.ts +7 -0
  68. package/dist/generated/DeleteClientsResult.d.ts.map +1 -0
  69. package/dist/generated/DeleteClientsResult.js +2 -0
  70. package/dist/generated/DeleteServer.d.ts +8 -0
  71. package/dist/generated/DeleteServer.d.ts.map +1 -0
  72. package/dist/generated/DeleteServer.js +1 -0
  73. package/dist/generated/DeleteServerArgs.d.ts +5 -0
  74. package/dist/generated/DeleteServerArgs.d.ts.map +1 -0
  75. package/dist/generated/DeleteServerArgs.js +1 -0
  76. package/dist/generated/DeleteServerResult.d.ts +7 -0
  77. package/dist/generated/DeleteServerResult.d.ts.map +1 -0
  78. package/dist/generated/DeleteServerResult.js +2 -0
  79. package/dist/generated/DeleteServers.d.ts +8 -0
  80. package/dist/generated/DeleteServers.d.ts.map +1 -0
  81. package/dist/generated/DeleteServers.js +1 -0
  82. package/dist/generated/DeleteServersArgs.d.ts +5 -0
  83. package/dist/generated/DeleteServersArgs.d.ts.map +1 -0
  84. package/dist/generated/DeleteServersArgs.js +1 -0
  85. package/dist/generated/DeleteServersResult.d.ts +7 -0
  86. package/dist/generated/DeleteServersResult.d.ts.map +1 -0
  87. package/dist/generated/DeleteServersResult.js +2 -0
  88. package/dist/generated/EntitySearch.d.ts +21 -0
  89. package/dist/generated/EntitySearch.d.ts.map +1 -0
  90. package/dist/generated/EntitySearch.js +2 -0
  91. package/dist/generated/EntitySearchResult.d.ts +10 -0
  92. package/dist/generated/EntitySearchResult.d.ts.map +1 -0
  93. package/dist/generated/EntitySearchResult.js +2 -0
  94. package/dist/generated/EntitySnapshotDifference.d.ts +8 -0
  95. package/dist/generated/EntitySnapshotDifference.d.ts.map +1 -0
  96. package/dist/generated/EntitySnapshotDifference.js +2 -0
  97. package/dist/generated/EntitySnapshotDifferenceData.d.ts +10 -0
  98. package/dist/generated/EntitySnapshotDifferenceData.d.ts.map +1 -0
  99. package/dist/generated/EntitySnapshotDifferenceData.js +1 -0
  100. package/dist/generated/EntityTreeExport.d.ts +27 -0
  101. package/dist/generated/EntityTreeExport.d.ts.map +1 -0
  102. package/dist/generated/EntityTreeExport.js +1 -0
  103. package/dist/generated/EventContainer.d.ts +9 -0
  104. package/dist/generated/EventContainer.d.ts.map +1 -0
  105. package/dist/generated/EventContainer.js +1 -0
  106. package/dist/generated/EventOptions.d.ts +21 -0
  107. package/dist/generated/EventOptions.d.ts.map +1 -0
  108. package/dist/generated/EventOptions.js +2 -0
  109. package/dist/generated/EventsForTransaction.d.ts +7 -0
  110. package/dist/generated/EventsForTransaction.d.ts.map +1 -0
  111. package/dist/generated/EventsForTransaction.js +2 -0
  112. package/dist/generated/ExportEntityTree.d.ts +24 -0
  113. package/dist/generated/ExportEntityTree.d.ts.map +1 -0
  114. package/dist/generated/ExportEntityTree.js +2 -0
  115. package/dist/generated/ExportedEntity.d.ts +15 -0
  116. package/dist/generated/ExportedEntity.d.ts.map +1 -0
  117. package/dist/generated/ExportedEntity.js +1 -0
  118. package/dist/generated/FullChildEntities.d.ts +8 -0
  119. package/dist/generated/FullChildEntities.d.ts.map +1 -0
  120. package/dist/generated/FullChildEntities.js +2 -0
  121. package/dist/generated/GetAllClients.d.ts +2 -0
  122. package/dist/generated/GetAllClients.d.ts.map +1 -0
  123. package/dist/generated/GetAllClients.js +2 -0
  124. package/dist/generated/GetAllServers.d.ts +2 -0
  125. package/dist/generated/GetAllServers.d.ts.map +1 -0
  126. package/dist/generated/GetAllServers.js +2 -0
  127. package/dist/generated/GetClientById.d.ts +5 -0
  128. package/dist/generated/GetClientById.d.ts.map +1 -0
  129. package/dist/generated/GetClientById.js +1 -0
  130. package/dist/generated/GetClientsByIds.d.ts +5 -0
  131. package/dist/generated/GetClientsByIds.d.ts.map +1 -0
  132. package/dist/generated/GetClientsByIds.js +1 -0
  133. package/dist/generated/GetClientsByQuery.d.ts +3 -0
  134. package/dist/generated/GetClientsByQuery.d.ts.map +1 -0
  135. package/dist/generated/GetClientsByQuery.js +1 -0
  136. package/dist/generated/GetConnectedServer.d.ts +2 -0
  137. package/dist/generated/GetConnectedServer.d.ts.map +1 -0
  138. package/dist/generated/GetConnectedServer.js +2 -0
  139. package/dist/generated/GetItemsByTypeAndIds.d.ts +14 -0
  140. package/dist/generated/GetItemsByTypeAndIds.d.ts.map +1 -0
  141. package/dist/generated/GetItemsByTypeAndIds.js +2 -0
  142. package/dist/generated/GetPeerServers.d.ts +2 -0
  143. package/dist/generated/GetPeerServers.d.ts.map +1 -0
  144. package/dist/generated/GetPeerServers.js +2 -0
  145. package/{src/generated/GetPersistHealth.ts → dist/generated/GetPersistHealth.d.ts} +1 -2
  146. package/dist/generated/GetPersistHealth.d.ts.map +1 -0
  147. package/dist/generated/GetPersistHealth.js +2 -0
  148. package/dist/generated/GetServerById.d.ts +5 -0
  149. package/dist/generated/GetServerById.d.ts.map +1 -0
  150. package/dist/generated/GetServerById.js +1 -0
  151. package/dist/generated/GetServersByIds.d.ts +5 -0
  152. package/dist/generated/GetServersByIds.d.ts.map +1 -0
  153. package/dist/generated/GetServersByIds.js +1 -0
  154. package/dist/generated/GetServersByQuery.d.ts +3 -0
  155. package/dist/generated/GetServersByQuery.d.ts.map +1 -0
  156. package/dist/generated/GetServersByQuery.js +1 -0
  157. package/dist/generated/ImportItems.d.ts +19 -0
  158. package/dist/generated/ImportItems.d.ts.map +1 -0
  159. package/dist/generated/ImportItems.js +1 -0
  160. package/dist/generated/ImportItemsArgs.d.ts +13 -0
  161. package/dist/generated/ImportItemsArgs.d.ts.map +1 -0
  162. package/dist/generated/ImportItemsArgs.js +1 -0
  163. package/dist/generated/ItemStub.d.ts +10 -0
  164. package/dist/generated/ItemStub.d.ts.map +1 -0
  165. package/dist/generated/ItemStub.js +2 -0
  166. package/{src/generated/LogLevel.ts → dist/generated/LogLevel.d.ts} +1 -2
  167. package/dist/generated/LogLevel.d.ts.map +1 -0
  168. package/dist/generated/LogLevel.js +2 -0
  169. package/{src/generated/Loggers.ts → dist/generated/Loggers.d.ts} +1 -2
  170. package/dist/generated/Loggers.d.ts.map +1 -0
  171. package/dist/generated/Loggers.js +2 -0
  172. package/dist/generated/MEvent.d.ts +16 -0
  173. package/dist/generated/MEvent.d.ts.map +1 -0
  174. package/dist/generated/MEvent.js +1 -0
  175. package/dist/generated/MEventType.d.ts +2 -0
  176. package/dist/generated/MEventType.d.ts.map +1 -0
  177. package/dist/generated/MEventType.js +2 -0
  178. package/dist/generated/MykoMessage.d.ts +86 -0
  179. package/dist/generated/MykoMessage.d.ts.map +1 -0
  180. package/dist/generated/MykoMessage.js +1 -0
  181. package/dist/generated/PartialClient.d.ts +12 -0
  182. package/dist/generated/PartialClient.d.ts.map +1 -0
  183. package/dist/generated/PartialClient.js +1 -0
  184. package/dist/generated/PartialServer.d.ts +9 -0
  185. package/dist/generated/PartialServer.d.ts.map +1 -0
  186. package/dist/generated/PartialServer.js +1 -0
  187. package/dist/generated/PeerAlive.d.ts +8 -0
  188. package/dist/generated/PeerAlive.d.ts.map +1 -0
  189. package/dist/generated/PeerAlive.js +2 -0
  190. package/dist/generated/PersistHealthStatus.d.ts +34 -0
  191. package/dist/generated/PersistHealthStatus.d.ts.map +1 -0
  192. package/dist/generated/PersistHealthStatus.js +2 -0
  193. package/dist/generated/PingData.d.ts +14 -0
  194. package/dist/generated/PingData.d.ts.map +1 -0
  195. package/dist/generated/PingData.js +2 -0
  196. package/dist/generated/QueryError.d.ts +6 -0
  197. package/dist/generated/QueryError.d.ts.map +1 -0
  198. package/dist/generated/QueryError.js +2 -0
  199. package/dist/generated/QueryWindow.d.ts +5 -0
  200. package/dist/generated/QueryWindow.d.ts.map +1 -0
  201. package/dist/generated/QueryWindow.js +2 -0
  202. package/dist/generated/QueryWindowUpdate.d.ts +6 -0
  203. package/dist/generated/QueryWindowUpdate.d.ts.map +1 -0
  204. package/dist/generated/QueryWindowUpdate.js +1 -0
  205. package/dist/generated/ReportError.d.ts +6 -0
  206. package/dist/generated/ReportError.d.ts.map +1 -0
  207. package/dist/generated/ReportError.js +2 -0
  208. package/dist/generated/ReportResponse.d.ts +6 -0
  209. package/dist/generated/ReportResponse.d.ts.map +1 -0
  210. package/dist/generated/ReportResponse.js +1 -0
  211. package/dist/generated/Server.d.ts +9 -0
  212. package/dist/generated/Server.d.ts.map +1 -0
  213. package/dist/generated/Server.js +1 -0
  214. package/dist/generated/ServerCount.d.ts +4 -0
  215. package/dist/generated/ServerCount.d.ts.map +1 -0
  216. package/dist/generated/ServerCount.js +2 -0
  217. package/dist/generated/ServerId.d.ts +2 -0
  218. package/dist/generated/ServerId.d.ts.map +1 -0
  219. package/dist/generated/ServerId.js +2 -0
  220. package/dist/generated/ServerLogLevel.d.ts +7 -0
  221. package/dist/generated/ServerLogLevel.d.ts.map +1 -0
  222. package/dist/generated/ServerLogLevel.js +2 -0
  223. package/{src/generated/ServerStats.ts → dist/generated/ServerStats.d.ts} +1 -2
  224. package/dist/generated/ServerStats.d.ts.map +1 -0
  225. package/dist/generated/ServerStats.js +2 -0
  226. package/dist/generated/ServerStatsOutput.d.ts +20 -0
  227. package/dist/generated/ServerStatsOutput.d.ts.map +1 -0
  228. package/dist/generated/ServerStatsOutput.js +1 -0
  229. package/dist/generated/SetClientWindbackTime.d.ts +11 -0
  230. package/dist/generated/SetClientWindbackTime.d.ts.map +1 -0
  231. package/dist/generated/SetClientWindbackTime.js +2 -0
  232. package/dist/generated/SetClientWindbackTimeArgs.d.ts +7 -0
  233. package/dist/generated/SetClientWindbackTimeArgs.d.ts.map +1 -0
  234. package/dist/generated/SetClientWindbackTimeArgs.js +2 -0
  235. package/dist/generated/SetLogLevel.d.ts +9 -0
  236. package/dist/generated/SetLogLevel.d.ts.map +1 -0
  237. package/dist/generated/SetLogLevel.js +1 -0
  238. package/dist/generated/SetLogLevelArgs.d.ts +6 -0
  239. package/dist/generated/SetLogLevelArgs.d.ts.map +1 -0
  240. package/dist/generated/SetLogLevelArgs.js +1 -0
  241. package/dist/generated/ViewError.d.ts +6 -0
  242. package/dist/generated/ViewError.d.ts.map +1 -0
  243. package/dist/generated/ViewError.js +2 -0
  244. package/dist/generated/ViewWindowUpdate.d.ts +6 -0
  245. package/dist/generated/ViewWindowUpdate.d.ts.map +1 -0
  246. package/dist/generated/ViewWindowUpdate.js +1 -0
  247. package/dist/generated/WindbackStatus.d.ts +2 -0
  248. package/dist/generated/WindbackStatus.d.ts.map +1 -0
  249. package/dist/generated/WindbackStatus.js +2 -0
  250. package/dist/generated/WindbackStatusOutput.d.ts +11 -0
  251. package/dist/generated/WindbackStatusOutput.d.ts.map +1 -0
  252. package/dist/generated/WindbackStatusOutput.js +2 -0
  253. package/dist/generated/WrappedCommand.d.ts +6 -0
  254. package/dist/generated/WrappedCommand.d.ts.map +1 -0
  255. package/dist/generated/WrappedCommand.js +1 -0
  256. package/dist/generated/WrappedItem.d.ts +9 -0
  257. package/dist/generated/WrappedItem.d.ts.map +1 -0
  258. package/dist/generated/WrappedItem.js +1 -0
  259. package/dist/generated/WrappedQuery.d.ts +9 -0
  260. package/dist/generated/WrappedQuery.d.ts.map +1 -0
  261. package/dist/generated/WrappedQuery.js +1 -0
  262. package/dist/generated/WrappedReport.d.ts +6 -0
  263. package/dist/generated/WrappedReport.d.ts.map +1 -0
  264. package/dist/generated/WrappedReport.js +1 -0
  265. package/dist/generated/WrappedView.d.ts +9 -0
  266. package/dist/generated/WrappedView.d.ts.map +1 -0
  267. package/dist/generated/WrappedView.js +1 -0
  268. package/dist/generated/index.d.ts +421 -0
  269. package/dist/generated/index.d.ts.map +1 -0
  270. package/dist/generated/index.js +349 -0
  271. package/dist/generated/serde_json/JsonValue.d.ts +4 -0
  272. package/dist/generated/serde_json/JsonValue.d.ts.map +1 -0
  273. package/dist/generated/serde_json/JsonValue.js +2 -0
  274. package/dist/index.d.ts +76 -0
  275. package/dist/index.d.ts.map +1 -0
  276. package/dist/index.js +68 -0
  277. package/package.json +8 -4
  278. package/src/client.ts +0 -1851
  279. package/src/generated/CancelSubscription.ts +0 -6
  280. package/src/generated/ChildEntities.ts +0 -6
  281. package/src/generated/ChildEntitiesAllTime.ts +0 -6
  282. package/src/generated/ClearClientWindbackTimeArgs.ts +0 -3
  283. package/src/generated/Client.ts +0 -10
  284. package/src/generated/ClientStatus.ts +0 -7
  285. package/src/generated/ClientStatusOutput.ts +0 -3
  286. package/src/generated/CommandError.ts +0 -3
  287. package/src/generated/CommandResponse.ts +0 -4
  288. package/src/generated/CountAllClients.ts +0 -3
  289. package/src/generated/CountAllServers.ts +0 -3
  290. package/src/generated/CountClients.ts +0 -4
  291. package/src/generated/CountServers.ts +0 -4
  292. package/src/generated/DeleteClient.ts +0 -7
  293. package/src/generated/DeleteClientArgs.ts +0 -4
  294. package/src/generated/DeleteClientResult.ts +0 -6
  295. package/src/generated/DeleteClients.ts +0 -7
  296. package/src/generated/DeleteClientsArgs.ts +0 -4
  297. package/src/generated/DeleteClientsResult.ts +0 -6
  298. package/src/generated/DeleteServer.ts +0 -7
  299. package/src/generated/DeleteServerArgs.ts +0 -4
  300. package/src/generated/DeleteServerResult.ts +0 -6
  301. package/src/generated/DeleteServers.ts +0 -7
  302. package/src/generated/DeleteServersArgs.ts +0 -4
  303. package/src/generated/DeleteServersResult.ts +0 -6
  304. package/src/generated/EntitySearch.ts +0 -21
  305. package/src/generated/EntitySearchResult.ts +0 -10
  306. package/src/generated/EntitySnapshotDifference.ts +0 -6
  307. package/src/generated/EntitySnapshotDifferenceData.ts +0 -7
  308. package/src/generated/EntityTreeExport.ts +0 -27
  309. package/src/generated/EventContainer.ts +0 -7
  310. package/src/generated/EventOptions.ts +0 -21
  311. package/src/generated/EventsForTransaction.ts +0 -6
  312. package/src/generated/ExportEntityTree.ts +0 -24
  313. package/src/generated/ExportedEntity.ts +0 -15
  314. package/src/generated/FullChildEntities.ts +0 -6
  315. package/src/generated/GetAllClients.ts +0 -3
  316. package/src/generated/GetAllServers.ts +0 -3
  317. package/src/generated/GetClientById.ts +0 -4
  318. package/src/generated/GetClientsByIds.ts +0 -4
  319. package/src/generated/GetClientsByQuery.ts +0 -4
  320. package/src/generated/GetConnectedServer.ts +0 -3
  321. package/src/generated/GetItemsByTypeAndIds.ts +0 -14
  322. package/src/generated/GetPeerServers.ts +0 -3
  323. package/src/generated/GetServerById.ts +0 -4
  324. package/src/generated/GetServersByIds.ts +0 -4
  325. package/src/generated/GetServersByQuery.ts +0 -4
  326. package/src/generated/ImportItems.ts +0 -19
  327. package/src/generated/ImportItemsArgs.ts +0 -13
  328. package/src/generated/ItemStub.ts +0 -7
  329. package/src/generated/MEvent.ts +0 -10
  330. package/src/generated/MykoMessage.ts +0 -19
  331. package/src/generated/PartialClient.ts +0 -10
  332. package/src/generated/PartialServer.ts +0 -4
  333. package/src/generated/PeerAlive.ts +0 -7
  334. package/src/generated/PersistHealthStatus.ts +0 -34
  335. package/src/generated/PingData.ts +0 -14
  336. package/src/generated/QueryError.ts +0 -3
  337. package/src/generated/QueryWindow.ts +0 -3
  338. package/src/generated/QueryWindowUpdate.ts +0 -4
  339. package/src/generated/ReportError.ts +0 -3
  340. package/src/generated/ReportResponse.ts +0 -4
  341. package/src/generated/Server.ts +0 -4
  342. package/src/generated/ServerCount.ts +0 -3
  343. package/src/generated/ServerLogLevel.ts +0 -6
  344. package/src/generated/ServerStatsOutput.ts +0 -20
  345. package/src/generated/SetClientWindbackTime.ts +0 -11
  346. package/src/generated/SetClientWindbackTimeArgs.ts +0 -7
  347. package/src/generated/SetLogLevel.ts +0 -7
  348. package/src/generated/SetLogLevelArgs.ts +0 -4
  349. package/src/generated/ViewError.ts +0 -3
  350. package/src/generated/ViewWindowUpdate.ts +0 -4
  351. package/src/generated/WindbackStatus.ts +0 -3
  352. package/src/generated/WindbackStatusOutput.ts +0 -11
  353. package/src/generated/WrappedCommand.ts +0 -4
  354. package/src/generated/WrappedItem.ts +0 -7
  355. package/src/generated/WrappedQuery.ts +0 -5
  356. package/src/generated/WrappedReport.ts +0 -4
  357. package/src/generated/WrappedView.ts +0 -5
  358. package/src/generated/index.ts +0 -580
  359. package/src/generated/serde_json/JsonValue.ts +0 -3
  360. package/src/index.ts +0 -128
package/dist/client.js ADDED
@@ -0,0 +1,1316 @@
1
+ /**
2
+ * Pure TypeScript WebSocket client for Myko servers
3
+ *
4
+ * Maintains connections to all known servers for instant failover.
5
+ * When the current server disconnects, instantly switches to another open connection.
6
+ */
7
+ import { GetPeerServers, MykoEvent, } from './generated';
8
+ import { Packr, Unpackr } from 'msgpackr';
9
+ import { bufferCount, bufferTime, catchError, combineLatest, filter, finalize, firstValueFrom, interval, map, merge, Observable, of, ReplaySubject, scan, shareReplay, Subject, switchMap, } from 'rxjs';
10
+ import { v4 as uuid } from 'uuid';
11
+ // msgpackr defaults can emit extension types for values that don't exist in JSON (notably
12
+ // `undefined`). Our Rust server deserializes msgpack into `serde_json::Value`, so ensure we
13
+ // encode `undefined` as nil/null instead of an extension.
14
+ const packr = new Packr({ encodeUndefinedAsNil: true });
15
+ const unpackr = new Unpackr({});
16
+ const EVENT_BATCH = 'ws:m:event-batch';
17
+ function stableStringify(value) {
18
+ const seen = new WeakSet();
19
+ try {
20
+ return JSON.stringify(value, (_key, raw) => {
21
+ if (typeof raw === 'bigint')
22
+ return `__bigint:${raw.toString()}`;
23
+ if (!raw || typeof raw !== 'object')
24
+ return raw;
25
+ if (seen.has(raw))
26
+ return '__circular__';
27
+ seen.add(raw);
28
+ if (Array.isArray(raw))
29
+ return raw;
30
+ const sorted = {};
31
+ for (const key of Object.keys(raw).sort()) {
32
+ sorted[key] = raw[key];
33
+ }
34
+ return sorted;
35
+ });
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ }
41
+ /** Connection status */
42
+ export var ConnectionStatus;
43
+ (function (ConnectionStatus) {
44
+ ConnectionStatus["Connected"] = "Connected";
45
+ ConnectionStatus["Disconnected"] = "Disconnected";
46
+ ConnectionStatus["Connecting"] = "Connecting";
47
+ })(ConnectionStatus || (ConnectionStatus = {}));
48
+ /** Wire protocol for encoding messages */
49
+ export var MykoProtocol;
50
+ (function (MykoProtocol) {
51
+ MykoProtocol["JSON"] = "JSON";
52
+ MykoProtocol["MSGPACK"] = "MSGPACK";
53
+ })(MykoProtocol || (MykoProtocol = {}));
54
+ function queryCacheKey(query, options) {
55
+ const queryPayload = stableStringify(query.query) ?? '__unstable_query__';
56
+ const windowPayload = stableStringify(options?.window ?? null) ?? '__unstable_window__';
57
+ return `query:${query.queryId}:${queryPayload}:${windowPayload}`;
58
+ }
59
+ function viewCacheKey(view, options) {
60
+ const viewPayload = stableStringify(view.view) ?? '__unstable_view__';
61
+ const windowPayload = stableStringify(options?.window ?? null) ?? '__unstable_window__';
62
+ return `view:${view.viewId}:${viewPayload}:${windowPayload}`;
63
+ }
64
+ function reportCacheKey(report) {
65
+ const payload = stableStringify(report.report) ?? '__unstable_report__';
66
+ return `report:${report.reportId}:${payload}`;
67
+ }
68
+ /**
69
+ * Reactive WebSocket client for Myko servers with automatic failover.
70
+ */
71
+ export class MykoClient {
72
+ // Socket management
73
+ sockets = new Map();
74
+ reconnectTimers = new Map();
75
+ endpointSockets = new Map();
76
+ // Main server: the socket used for outbound sends.
77
+ // Other open sockets are warm standbys for failover.
78
+ currentServer = null;
79
+ shouldReconnect = true;
80
+ // Message routing
81
+ queryResponses = new Subject();
82
+ reportResponses = new Subject();
83
+ commandResponses = new Subject();
84
+ commandErrors = new Subject();
85
+ queryErrors = new Subject();
86
+ reportErrors = new Subject();
87
+ pingResponses = new Subject();
88
+ commandIncoming = new Subject();
89
+ // State observables
90
+ connectionStatusSubject = new ReplaySubject(1);
91
+ currentServerSubject = new ReplaySubject(1);
92
+ // Subscription tracking
93
+ activeQueries = new Map();
94
+ activeViews = new Map();
95
+ activeReports = new Map();
96
+ activeQueryNames = new Map();
97
+ activeViewNames = new Map();
98
+ activeReportNames = new Map();
99
+ sharedQueries = new Map();
100
+ sharedViews = new Map();
101
+ sharedQueryDiffs = new Map();
102
+ sharedViewDiffs = new Map();
103
+ sharedReports = new Map();
104
+ subscriptionStartMs = new Map();
105
+ firstResponseLogged = new Set();
106
+ messageQueue = [];
107
+ pendingEventBatch = [];
108
+ eventBatchFlushScheduled = false;
109
+ eventBatchMaxSize = 256;
110
+ connectionLogLevelThreshold = MykoClient.resolveDefaultConnectionLogLevel();
111
+ // Stats
112
+ downMsgCounter = new Subject();
113
+ upMsgCounter = new Subject();
114
+ // Auth & peer discovery
115
+ userToken = null;
116
+ peerDiscoveryEnabled = true;
117
+ peerDiscoverySubscription = null;
118
+ useSecureWebSocket = false;
119
+ // Protocol defaults to JSON for maximum compatibility (no msgpack extensions, bigint issues, etc).
120
+ protocol = MykoProtocol.JSON;
121
+ constructor() {
122
+ this.setConnectionStatus(ConnectionStatus.Disconnected, 'init');
123
+ this.setCurrentServer(null, 'init');
124
+ }
125
+ /** Set connection log verbosity at runtime. */
126
+ setConnectionLogLevel(level) {
127
+ this.connectionLogLevelThreshold = level;
128
+ }
129
+ /** Set the wire protocol (JSON or MSGPACK). Default is MSGPACK. */
130
+ setProtocol(protocol) {
131
+ this.protocol = protocol;
132
+ }
133
+ // ─────────────────────────────────────────────────────────────────────────────
134
+ // Connection Management
135
+ // ─────────────────────────────────────────────────────────────────────────────
136
+ /** Set a single server address, clearing any existing connections */
137
+ setAddress(address) {
138
+ this.shouldReconnect = true; // Re-enable autoreconnect when setting new address
139
+ this.closeAllSockets();
140
+ if (address) {
141
+ this.useSecureWebSocket = address.startsWith('wss://');
142
+ this.createSocket(address, true);
143
+ }
144
+ }
145
+ /** Set multiple server addresses, clearing any existing connections */
146
+ setAddresses(addresses) {
147
+ this.shouldReconnect = true; // Re-enable autoreconnect when setting new addresses
148
+ this.closeAllSockets();
149
+ for (const addr of addresses) {
150
+ this.createSocket(addr, true);
151
+ }
152
+ }
153
+ /** Add additional servers (connects immediately) */
154
+ addServers(addresses, reconnectOnClose = true) {
155
+ this.shouldReconnect = true; // Re-enable autoreconnect when adding servers
156
+ for (const addr of addresses) {
157
+ if (!this.hasConnectionTo(addr)) {
158
+ this.createSocket(addr, reconnectOnClose);
159
+ }
160
+ }
161
+ }
162
+ /** Disconnect from all servers */
163
+ disconnect() {
164
+ this.shouldReconnect = false;
165
+ this.stopPeerDiscovery();
166
+ this.closeAllSockets();
167
+ }
168
+ /** Get the currently active server address */
169
+ getCurrentServer() {
170
+ return this.currentServer;
171
+ }
172
+ /** Get the current main server address used for outbound sends */
173
+ getMainServer() {
174
+ return this.currentServer;
175
+ }
176
+ /** Get all server addresses */
177
+ getServers() {
178
+ return Array.from(this.sockets.values()).map((m) => m.address);
179
+ }
180
+ /** Get addresses of all open connections */
181
+ getOpenServers() {
182
+ return Array.from(this.sockets.values())
183
+ .filter((m) => m.ws.readyState === WebSocket.OPEN)
184
+ .map((m) => m.address);
185
+ }
186
+ /** Observable of current server changes */
187
+ get currentServer$() {
188
+ return this.currentServerSubject.asObservable();
189
+ }
190
+ /** Observable of main server changes */
191
+ get mainServer$() {
192
+ return this.currentServer$;
193
+ }
194
+ /** Get current connection status */
195
+ getConnectionStatus() {
196
+ if (this.currentServer)
197
+ return ConnectionStatus.Connected;
198
+ for (const m of this.sockets.values()) {
199
+ if (m.ws.readyState === WebSocket.CONNECTING)
200
+ return ConnectionStatus.Connecting;
201
+ }
202
+ return ConnectionStatus.Disconnected;
203
+ }
204
+ /** Observable of connection status changes */
205
+ get connectionStatus$() {
206
+ return this.connectionStatusSubject.asObservable();
207
+ }
208
+ /** Observable of incoming command messages (ws:m:command) from the server */
209
+ get commandIncoming$() {
210
+ return this.commandIncoming.asObservable();
211
+ }
212
+ // ─────────────────────────────────────────────────────────────────────────────
213
+ // Peer Discovery
214
+ // ─────────────────────────────────────────────────────────────────────────────
215
+ /** Enable automatic peer discovery via GetPeerServers query */
216
+ enablePeerDiscovery(enabled, secure = false) {
217
+ this.peerDiscoveryEnabled = enabled;
218
+ this.useSecureWebSocket = secure;
219
+ if (!enabled && this.peerDiscoverySubscription) {
220
+ this.peerDiscoverySubscription.unsubscribe();
221
+ this.peerDiscoverySubscription = null;
222
+ }
223
+ else if (enabled && this.hasOpenConnection()) {
224
+ this.startPeerDiscovery();
225
+ }
226
+ }
227
+ startPeerDiscovery() {
228
+ this.peerDiscoverySubscription?.unsubscribe();
229
+ this.logConnection('peer_discovery_started', {
230
+ secure: this.useSecureWebSocket,
231
+ via: 'query:GetPeerServers',
232
+ });
233
+ this.peerDiscoverySubscription = this.watchQuery(new GetPeerServers({})).subscribe((servers) => {
234
+ const addresses = servers.map((s) => this.useSecureWebSocket
235
+ ? `wss://${s.address}/myko`
236
+ : `ws://${s.address}:${s.port}/myko`);
237
+ this.logConnection('peer_discovery_update', {
238
+ peers: servers.length,
239
+ addresses,
240
+ });
241
+ // Discovered peers are ephemeral: if they disconnect, wait for discovery
242
+ // to advertise them again rather than actively redialing.
243
+ if (addresses.length > 0)
244
+ this.addServers(addresses, false);
245
+ });
246
+ }
247
+ stopPeerDiscovery() {
248
+ this.peerDiscoverySubscription?.unsubscribe();
249
+ this.peerDiscoverySubscription = null;
250
+ }
251
+ // ─────────────────────────────────────────────────────────────────────────────
252
+ // Auth & Stats
253
+ // ─────────────────────────────────────────────────────────────────────────────
254
+ /** Set authentication token for commands */
255
+ setToken(token) {
256
+ this.userToken = token;
257
+ }
258
+ /** Observable of all errors */
259
+ get errors$() {
260
+ const toError = (e) => ({ event: e.event, tx: e.data.tx, message: e.data.message });
261
+ return merge(this.queryErrors.pipe(map(toError)), this.commandErrors.pipe(map(toError)), this.reportErrors.pipe(map(toError)));
262
+ }
263
+ /** Observable of successful command completions (tx id) */
264
+ get successes$() {
265
+ return this.commandResponses.pipe(map((r) => r.data.tx));
266
+ }
267
+ /** Measure round-trip latency */
268
+ async ping() {
269
+ const id = uuid();
270
+ const nowMs = Date.now();
271
+ // IMPORTANT: the Rust server expects `timestamp: i64`.
272
+ // - JSON cannot encode bigint, so use number in JSON mode.
273
+ // - msgpack can encode bigint as int64, so use bigint in MSGPACK mode.
274
+ const timestamp = this.protocol === MykoProtocol.MSGPACK ? BigInt(nowMs) : nowMs;
275
+ this.send({
276
+ event: MykoEvent.Ping,
277
+ data: { id, timestamp },
278
+ });
279
+ return firstValueFrom(this.pingResponses.pipe(filter((p) => p.data.id === id), map((p) => Date.now() - Number(p.data.timestamp))));
280
+ }
281
+ /** Get real-time client statistics (emits every second) */
282
+ stats() {
283
+ const pingLatency = interval(1000).pipe(switchMap(() => this.ping()), catchError(() => of(0)));
284
+ const mpsDown = this.downMsgCounter.pipe(bufferTime(100), bufferCount(10), map((b) => b.flat().length));
285
+ const mpsUp = this.upMsgCounter.pipe(bufferTime(100), bufferCount(10), map((b) => b.flat().length));
286
+ return combineLatest([pingLatency, mpsDown, mpsUp]).pipe(map(([ping, down, up]) => ({ ping, mpsDown: down, mpsUp: up })));
287
+ }
288
+ // ─────────────────────────────────────────────────────────────────────────────
289
+ // Queries & Reports
290
+ // ─────────────────────────────────────────────────────────────────────────────
291
+ /** Start a query subscription, returns [tx, responses$] */
292
+ startQuery(query, options) {
293
+ if (!query.queryId || !query.queryItemType || !query.query) {
294
+ const details = {
295
+ ctor: query.constructor?.name ?? 'unknown',
296
+ keys: Object.keys(query ?? {}),
297
+ queryId: query.queryId,
298
+ queryItemType: query.queryItemType,
299
+ };
300
+ this.logConnection('query_shape_invalid', details);
301
+ throw new Error(`Invalid query shape for ${details.ctor}: expected { queryId, queryItemType, query }`);
302
+ }
303
+ const tx = uuid();
304
+ const window = options?.window ?? undefined;
305
+ const queryName = query.constructor?.name ?? query.queryId;
306
+ const wrappedQuery = {
307
+ query: { ...query.query, tx, createdAt: new Date().toISOString() },
308
+ queryId: query.queryId,
309
+ queryItemType: query.queryItemType,
310
+ ...(window ? { window } : {}),
311
+ };
312
+ this.activeQueries.set(tx, wrappedQuery);
313
+ this.activeQueryNames.set(tx, queryName);
314
+ this.subscriptionStartMs.set(tx, this.nowMs());
315
+ this.firstResponseLogged.delete(tx);
316
+ this.logConnection('query_subscribe', {
317
+ tx,
318
+ queryId: wrappedQuery.queryId,
319
+ queryItemType: wrappedQuery.queryItemType,
320
+ queryName,
321
+ window: window ?? null,
322
+ activeQueries: this.activeQueries.size,
323
+ });
324
+ this.send({ event: MykoEvent.Query, data: wrappedQuery });
325
+ const responses$ = new Observable((subscriber) => {
326
+ const responseSub = this.queryResponses
327
+ .pipe(filter((r) => r.data.tx === tx))
328
+ .subscribe({
329
+ next: (response) => subscriber.next(response),
330
+ error: (error) => subscriber.error(error),
331
+ });
332
+ const errorSub = this.queryErrors
333
+ .pipe(filter((error) => error.data.tx === tx))
334
+ .subscribe((error) => {
335
+ subscriber.error(new Error(error.data.message));
336
+ });
337
+ return () => {
338
+ responseSub.unsubscribe();
339
+ errorSub.unsubscribe();
340
+ };
341
+ }).pipe(finalize(() => {
342
+ this.logConnection('query_cancel', {
343
+ tx,
344
+ queryId: wrappedQuery.queryId,
345
+ queryItemType: wrappedQuery.queryItemType,
346
+ queryName,
347
+ activeQueriesBefore: this.activeQueries.size,
348
+ });
349
+ this.activeQueries.delete(tx);
350
+ this.activeQueryNames.delete(tx);
351
+ this.subscriptionStartMs.delete(tx);
352
+ this.firstResponseLogged.delete(tx);
353
+ this.send({ event: MykoEvent.QueryCancel, data: { tx } });
354
+ }));
355
+ return [tx, responses$];
356
+ }
357
+ /** Update server-side window for an active query subscription */
358
+ setQueryWindow(tx, window) {
359
+ const active = this.activeQueries.get(tx);
360
+ if (!active)
361
+ return;
362
+ const updated = {
363
+ ...active,
364
+ ...(window ? { window } : {}),
365
+ };
366
+ if (!window) {
367
+ delete updated.window;
368
+ }
369
+ this.activeQueries.set(tx, updated);
370
+ this.logConnection('query_window_set', {
371
+ tx,
372
+ queryId: active.queryId,
373
+ queryItemType: active.queryItemType,
374
+ queryName: this.activeQueryNames.get(tx) ?? active.queryId,
375
+ window,
376
+ });
377
+ this.send({ event: MykoEvent.QueryWindow, data: { tx, window } });
378
+ }
379
+ /** Start a view subscription, returns [tx, responses$] */
380
+ startView(view, options) {
381
+ if (!view.viewId || !view.viewItemType || !view.view) {
382
+ const details = {
383
+ ctor: view.constructor?.name ?? 'unknown',
384
+ keys: Object.keys(view ?? {}),
385
+ viewId: view.viewId,
386
+ viewItemType: view.viewItemType,
387
+ };
388
+ this.logConnection('view_shape_invalid', details);
389
+ throw new Error(`Invalid view shape for ${details.ctor}: expected { viewId, viewItemType, view }`);
390
+ }
391
+ const tx = uuid();
392
+ const window = options?.window ?? undefined;
393
+ const viewName = view.constructor?.name ?? view.viewId;
394
+ const wrappedView = {
395
+ view: { ...view.view, tx, createdAt: new Date().toISOString() },
396
+ viewId: view.viewId,
397
+ viewItemType: view.viewItemType,
398
+ ...(window ? { window } : {}),
399
+ };
400
+ this.activeViews.set(tx, wrappedView);
401
+ this.activeViewNames.set(tx, viewName);
402
+ this.subscriptionStartMs.set(tx, this.nowMs());
403
+ this.firstResponseLogged.delete(tx);
404
+ this.logConnection('view_subscribe', {
405
+ tx,
406
+ viewId: wrappedView.viewId,
407
+ viewItemType: wrappedView.viewItemType,
408
+ viewName,
409
+ window: wrappedView.window ?? null,
410
+ activeViews: this.activeViews.size,
411
+ });
412
+ this.send({ event: MykoEvent.View, data: wrappedView });
413
+ const responses$ = new Observable((subscriber) => {
414
+ const responseSub = this.queryResponses
415
+ .pipe(filter((r) => r.data.tx === tx))
416
+ .subscribe({
417
+ next: (response) => subscriber.next(response),
418
+ error: (error) => subscriber.error(error),
419
+ });
420
+ const errorSub = this.queryErrors
421
+ .pipe(filter((error) => error.data.tx === tx))
422
+ .subscribe((error) => {
423
+ subscriber.error(new Error(error.data.message));
424
+ });
425
+ return () => {
426
+ responseSub.unsubscribe();
427
+ errorSub.unsubscribe();
428
+ };
429
+ }).pipe(finalize(() => {
430
+ this.logConnection('view_cancel', {
431
+ tx,
432
+ viewId: wrappedView.viewId,
433
+ viewName,
434
+ activeViewsBefore: this.activeViews.size,
435
+ });
436
+ this.activeViews.delete(tx);
437
+ this.activeViewNames.delete(tx);
438
+ this.subscriptionStartMs.delete(tx);
439
+ this.firstResponseLogged.delete(tx);
440
+ this.send({ event: MykoEvent.ViewCancel, data: { tx } });
441
+ }));
442
+ return [tx, responses$];
443
+ }
444
+ /** Update server-side window for an active view subscription */
445
+ setViewWindow(tx, window) {
446
+ const active = this.activeViews.get(tx);
447
+ if (!active)
448
+ return;
449
+ const updated = {
450
+ ...active,
451
+ ...(window ? { window } : {}),
452
+ };
453
+ if (!window) {
454
+ delete updated.window;
455
+ }
456
+ this.activeViews.set(tx, updated);
457
+ this.logConnection('view_window_set', {
458
+ tx,
459
+ viewId: active.viewId,
460
+ viewName: this.activeViewNames.get(tx) ?? active.viewId,
461
+ window,
462
+ });
463
+ this.send({ event: MykoEvent.ViewWindow, data: { tx, window } });
464
+ }
465
+ /** Watch a query and receive live updates with automatic deduplication */
466
+ watchQuery(query, options) {
467
+ const cacheKey = queryCacheKey(query, options);
468
+ const existing = this.sharedQueries.get(cacheKey);
469
+ if (existing)
470
+ return existing;
471
+ const [, responses$] = this.startQuery(query, options);
472
+ const shared$ = responses$.pipe(scan((acc, update) => {
473
+ if (BigInt(update.data.sequence) === 0n)
474
+ acc.clear();
475
+ for (const id of update.data.deletes)
476
+ acc.delete(id);
477
+ for (const wrapped of update.data.upserts) {
478
+ const item = wrapped.item;
479
+ if (item?.id)
480
+ acc.set(item.id, wrapped);
481
+ }
482
+ return acc;
483
+ }, new Map()), map((items) => [...items.values()].map((w) => w.item)), finalize(() => {
484
+ this.sharedQueries.delete(cacheKey);
485
+ }), shareReplay({ bufferSize: 1, refCount: true }),
486
+ // Defensive copy: shareReplay replays the same array reference to all
487
+ // subscribers, so a mutation (e.g. .shift()) by one subscriber would
488
+ // corrupt the shared value for others. Cloning per-subscriber prevents this.
489
+ map((x) => (Array.isArray(x) ? x.slice() : x)));
490
+ this.sharedQueries.set(cacheKey, shared$);
491
+ return shared$;
492
+ }
493
+ /** Watch a view and receive live updates with automatic deduplication */
494
+ watchView(view, options) {
495
+ const cacheKey = viewCacheKey(view, options);
496
+ const existing = this.sharedViews.get(cacheKey);
497
+ if (existing)
498
+ return existing;
499
+ const [, responses$] = this.startView(view, options);
500
+ const shared$ = responses$.pipe(scan((acc, update) => {
501
+ if (BigInt(update.data.sequence) === 0n)
502
+ acc.clear();
503
+ for (const id of update.data.deletes)
504
+ acc.delete(id);
505
+ for (const wrapped of update.data.upserts) {
506
+ const item = wrapped.item;
507
+ if (item?.id)
508
+ acc.set(item.id, wrapped);
509
+ }
510
+ return acc;
511
+ }, new Map()), map((items) => [...items.values()].map((w) => w.item)), finalize(() => {
512
+ this.sharedViews.delete(cacheKey);
513
+ }), shareReplay({ bufferSize: 1, refCount: true }), map((x) => (Array.isArray(x) ? x.slice() : x)));
514
+ this.sharedViews.set(cacheKey, shared$);
515
+ return shared$;
516
+ }
517
+ /** Watch a query and receive raw diff events */
518
+ watchQueryDiff(query, options) {
519
+ const cacheKey = queryCacheKey(query, options);
520
+ const existing = this.sharedQueryDiffs.get(cacheKey);
521
+ if (existing)
522
+ return existing;
523
+ const [, responses$] = this.startQuery(query, options);
524
+ const shared$ = responses$.pipe(map((r) => ({
525
+ sequence: BigInt(r.data.sequence),
526
+ deletes: r.data.deletes.slice(),
527
+ upserts: r.data.upserts.map((w) => w.item),
528
+ })), finalize(() => {
529
+ this.sharedQueryDiffs.delete(cacheKey);
530
+ }), shareReplay({ bufferSize: 1, refCount: true }), map((diff) => ({
531
+ sequence: diff.sequence,
532
+ deletes: diff.deletes.slice(),
533
+ upserts: diff.upserts.slice(),
534
+ })));
535
+ this.sharedQueryDiffs.set(cacheKey, shared$);
536
+ return shared$;
537
+ }
538
+ /** Watch a view and receive raw diff events */
539
+ watchViewDiff(view, options) {
540
+ const cacheKey = viewCacheKey(view, options);
541
+ const existing = this.sharedViewDiffs.get(cacheKey);
542
+ if (existing)
543
+ return existing;
544
+ const [, responses$] = this.startView(view, options);
545
+ const shared$ = responses$.pipe(map((r) => ({
546
+ sequence: BigInt(r.data.sequence),
547
+ deletes: r.data.deletes.slice(),
548
+ upserts: r.data.upserts.map((w) => w.item),
549
+ })), finalize(() => {
550
+ this.sharedViewDiffs.delete(cacheKey);
551
+ }), shareReplay({ bufferSize: 1, refCount: true }), map((diff) => ({
552
+ sequence: diff.sequence,
553
+ deletes: diff.deletes.slice(),
554
+ upserts: diff.upserts.slice(),
555
+ })));
556
+ this.sharedViewDiffs.set(cacheKey, shared$);
557
+ return shared$;
558
+ }
559
+ /**
560
+ * Start a live query with a mutable server-side window.
561
+ * Use `setWindow` to scroll without re-subscribing.
562
+ */
563
+ watchQueryWindowed(query, options) {
564
+ const [tx, responses$] = this.startQuery(query, options);
565
+ const sharedResponses$ = responses$.pipe(shareReplay({ bufferSize: 1, refCount: true }));
566
+ const results$ = sharedResponses$.pipe(scan((state, update) => {
567
+ const data = update.data;
568
+ if (BigInt(update.data.sequence) === 0n) {
569
+ state.cache.clear();
570
+ state.visibleIds = [];
571
+ }
572
+ for (const id of update.data.deletes)
573
+ state.cache.delete(id);
574
+ for (const wrapped of update.data.upserts) {
575
+ const item = wrapped.item;
576
+ if (item?.id)
577
+ state.cache.set(item.id, wrapped);
578
+ }
579
+ const order = data.changes?.find((change) => change.kind === 'windowOrder');
580
+ if (order) {
581
+ state.visibleIds = order.ids.slice();
582
+ }
583
+ else {
584
+ // Fallback when window-order diffs are unavailable: derive visible ids
585
+ // from current cache contents (in insertion order).
586
+ state.visibleIds = [...state.cache.keys()];
587
+ }
588
+ return state;
589
+ }, {
590
+ cache: new Map(),
591
+ visibleIds: [],
592
+ }), map((state) => state.visibleIds
593
+ .map((id) => state.cache.get(id)?.item)
594
+ .filter((item) => item !== undefined)), map((x) => x.slice()));
595
+ const windowInfo$ = sharedResponses$.pipe(map((update) => {
596
+ const data = update.data;
597
+ const order = data.changes?.find((change) => change.kind === 'windowOrder');
598
+ const orderTotalCount = order?.totalCount ?? order?.total_count;
599
+ return {
600
+ totalCount: typeof data.totalCount === 'number'
601
+ ? data.totalCount
602
+ : typeof data.total_count === 'number'
603
+ ? data.total_count
604
+ : typeof orderTotalCount === 'number'
605
+ ? orderTotalCount
606
+ : null,
607
+ window: data.window ?? order?.window ?? null,
608
+ };
609
+ }), shareReplay({ bufferSize: 1, refCount: true }));
610
+ return {
611
+ tx,
612
+ results$,
613
+ windowInfo$,
614
+ setWindow: (window) => this.setQueryWindow(tx, window),
615
+ };
616
+ }
617
+ /** Start a live view with a mutable server-side window. */
618
+ watchViewWindowed(view, options) {
619
+ const [tx, responses$] = this.startView(view, options);
620
+ const sharedResponses$ = responses$.pipe(shareReplay({ bufferSize: 1, refCount: true }));
621
+ const results$ = sharedResponses$.pipe(scan((state, update) => {
622
+ const data = update.data;
623
+ if (BigInt(update.data.sequence) === 0n) {
624
+ state.cache.clear();
625
+ state.visibleIds = [];
626
+ }
627
+ for (const id of update.data.deletes)
628
+ state.cache.delete(id);
629
+ for (const wrapped of update.data.upserts) {
630
+ const item = wrapped.item;
631
+ if (item?.id)
632
+ state.cache.set(item.id, wrapped);
633
+ }
634
+ const order = data.changes?.find((change) => change.kind === 'windowOrder');
635
+ if (order) {
636
+ state.visibleIds = order.ids.slice();
637
+ }
638
+ else {
639
+ state.visibleIds = [...state.cache.keys()];
640
+ }
641
+ return state;
642
+ }, {
643
+ cache: new Map(),
644
+ visibleIds: [],
645
+ }), map((state) => state.visibleIds
646
+ .map((id) => state.cache.get(id)?.item)
647
+ .filter((item) => item !== undefined)), map((x) => x.slice()));
648
+ const windowInfo$ = sharedResponses$.pipe(map((update) => {
649
+ const data = update.data;
650
+ const order = data.changes?.find((change) => change.kind === 'windowOrder');
651
+ const orderTotalCount = order?.totalCount ?? order?.total_count;
652
+ return {
653
+ totalCount: typeof data.totalCount === 'number'
654
+ ? data.totalCount
655
+ : typeof data.total_count === 'number'
656
+ ? data.total_count
657
+ : typeof orderTotalCount === 'number'
658
+ ? orderTotalCount
659
+ : null,
660
+ window: data.window ?? order?.window ?? null,
661
+ };
662
+ }), shareReplay({ bufferSize: 1, refCount: true }));
663
+ return {
664
+ tx,
665
+ results$,
666
+ windowInfo$,
667
+ setWindow: (window) => this.setViewWindow(tx, window),
668
+ };
669
+ }
670
+ /** Watch a report with automatic deduplication */
671
+ watchReport(report) {
672
+ const cacheKey = reportCacheKey(report);
673
+ const existing = this.sharedReports.get(cacheKey);
674
+ if (existing)
675
+ return existing;
676
+ const tx = uuid();
677
+ const reportName = report.constructor?.name ??
678
+ report.reportId;
679
+ const wrappedReport = {
680
+ report: { ...report.report, tx },
681
+ reportId: report.reportId,
682
+ };
683
+ this.activeReports.set(tx, wrappedReport);
684
+ this.activeReportNames.set(tx, reportName);
685
+ this.logConnection('report_subscribe', {
686
+ tx,
687
+ reportId: wrappedReport.reportId,
688
+ reportName,
689
+ report: report.report,
690
+ activeReports: this.activeReports.size,
691
+ });
692
+ this.send({ event: MykoEvent.Report, data: wrappedReport });
693
+ const shared$ = this.reportResponses.pipe(filter((r) => r.data.tx === tx), map((r) => {
694
+ this.logConnection('report_response', {
695
+ tx,
696
+ reportId: wrappedReport.reportId,
697
+ reportName,
698
+ });
699
+ return r;
700
+ }), map((r) => r.data.response), finalize(() => {
701
+ this.logConnection('report_cancel', {
702
+ tx,
703
+ reportId: wrappedReport.reportId,
704
+ reportName,
705
+ activeReportsBefore: this.activeReports.size,
706
+ });
707
+ this.sharedReports.delete(cacheKey);
708
+ this.activeReports.delete(tx);
709
+ this.activeReportNames.delete(tx);
710
+ this.send({ event: MykoEvent.ReportCancel, data: { tx } });
711
+ }), shareReplay({ bufferSize: 1, refCount: true }),
712
+ // Defensive copy: prevent one subscriber's mutations from affecting others
713
+ map((x) => {
714
+ if (Array.isArray(x))
715
+ return x.slice();
716
+ if (x && typeof x === 'object') {
717
+ return { ...x };
718
+ }
719
+ return x;
720
+ }));
721
+ this.sharedReports.set(cacheKey, shared$);
722
+ return shared$;
723
+ }
724
+ // ─────────────────────────────────────────────────────────────────────────────
725
+ // Commands & Events
726
+ // ─────────────────────────────────────────────────────────────────────────────
727
+ /** Send an event to the server */
728
+ sendEvent(event) {
729
+ // Pulses are latency-sensitive; bypass batching and send immediately.
730
+ if (event.itemType === 'Pulse') {
731
+ this.flushPendingEventBatch();
732
+ this.sendNow({ event: MykoEvent.Event, data: event });
733
+ return;
734
+ }
735
+ this.sendEventBatch([event]);
736
+ }
737
+ /** Send a batch of events to the server */
738
+ sendEventBatch(events) {
739
+ if (events.length === 0)
740
+ return;
741
+ const buffered = [];
742
+ const immediatePulses = [];
743
+ for (const event of events) {
744
+ if (event.itemType === 'Pulse') {
745
+ immediatePulses.push(event);
746
+ }
747
+ else {
748
+ buffered.push(event);
749
+ }
750
+ }
751
+ if (buffered.length > 0) {
752
+ this.pendingEventBatch.push(...buffered);
753
+ }
754
+ if (immediatePulses.length > 0) {
755
+ this.flushPendingEventBatch();
756
+ for (const pulse of immediatePulses) {
757
+ this.sendNow({ event: MykoEvent.Event, data: pulse });
758
+ }
759
+ return;
760
+ }
761
+ if (this.pendingEventBatch.length >= this.eventBatchMaxSize) {
762
+ this.flushPendingEventBatch();
763
+ return;
764
+ }
765
+ this.scheduleEventBatchFlush();
766
+ }
767
+ /** Send a command and wait for response */
768
+ sendCommand(command) {
769
+ const tx = uuid();
770
+ const wrappedCommand = {
771
+ command: {
772
+ ...command.command,
773
+ tx,
774
+ createdAt: new Date().toISOString(),
775
+ ...(this.userToken && { userToken: this.userToken }),
776
+ },
777
+ commandId: command.commandId,
778
+ };
779
+ return new Promise((resolve, reject) => {
780
+ const responseSub = this.commandResponses
781
+ .pipe(filter((r) => r.data.tx === tx))
782
+ .subscribe((r) => {
783
+ cleanup();
784
+ resolve(r.data.response);
785
+ });
786
+ const errorSub = this.commandErrors
787
+ .pipe(filter((r) => r.data.tx === tx))
788
+ .subscribe((r) => {
789
+ cleanup();
790
+ reject(new Error(r.data.message));
791
+ });
792
+ const cleanup = () => {
793
+ responseSub.unsubscribe();
794
+ errorSub.unsubscribe();
795
+ };
796
+ this.send({ event: MykoEvent.Command, data: wrappedCommand });
797
+ });
798
+ }
799
+ // ─────────────────────────────────────────────────────────────────────────────
800
+ // Private: Socket Management
801
+ // ─────────────────────────────────────────────────────────────────────────────
802
+ getFirstOpenSocket() {
803
+ for (const m of this.sockets.values()) {
804
+ if (m.ws.readyState === WebSocket.OPEN)
805
+ return m;
806
+ }
807
+ return null;
808
+ }
809
+ hasOpenConnection() {
810
+ return this.getFirstOpenSocket() !== null;
811
+ }
812
+ hasConnectionTo(address) {
813
+ return this.endpointSockets.has(this.endpointKey(address));
814
+ }
815
+ parseAddress(address) {
816
+ try {
817
+ const url = new URL(address);
818
+ const port = url.port
819
+ ? parseInt(url.port, 10)
820
+ : url.protocol === 'wss:'
821
+ ? 443
822
+ : 80;
823
+ return { host: url.hostname.toLowerCase(), port };
824
+ }
825
+ catch {
826
+ return null;
827
+ }
828
+ }
829
+ endpointKey(address) {
830
+ const parsed = this.parseAddress(address);
831
+ if (!parsed)
832
+ return address;
833
+ const host = this.hostsEquivalent(parsed.host, 'localhost')
834
+ ? 'localhost'
835
+ : parsed.host;
836
+ return `${host}:${parsed.port}`;
837
+ }
838
+ hostsEquivalent(a, b) {
839
+ if (a === b)
840
+ return true;
841
+ const loopback = new Set(['localhost', '127.0.0.1', '::1']);
842
+ return loopback.has(a) && loopback.has(b);
843
+ }
844
+ closeAllSockets() {
845
+ for (const timer of this.reconnectTimers.values())
846
+ clearTimeout(timer);
847
+ this.reconnectTimers.clear();
848
+ this.endpointSockets.clear();
849
+ for (const m of this.sockets.values()) {
850
+ m.ws.onclose = null;
851
+ m.ws.onerror = null;
852
+ m.ws.onopen = null;
853
+ m.ws.onmessage = null;
854
+ m.ws.close();
855
+ }
856
+ this.sockets.clear();
857
+ this.setCurrentServer(null, 'all sockets closed');
858
+ this.setConnectionStatus(ConnectionStatus.Disconnected, 'all sockets closed');
859
+ }
860
+ createSocket(address, reconnectOnClose = true) {
861
+ const endpointKey = this.endpointKey(address);
862
+ if (this.endpointSockets.has(endpointKey))
863
+ return;
864
+ this.endpointSockets.set(endpointKey, address);
865
+ const reconnectTimer = this.reconnectTimers.get(endpointKey);
866
+ if (reconnectTimer) {
867
+ clearTimeout(reconnectTimer);
868
+ this.reconnectTimers.delete(endpointKey);
869
+ }
870
+ this.logConnection('socket_connecting', {
871
+ address,
872
+ openServers: this.getOpenServers(),
873
+ knownServers: this.getServers(),
874
+ });
875
+ const ws = new WebSocket(address);
876
+ ws.binaryType = 'arraybuffer'; // Receive binary messages as ArrayBuffer for msgpack
877
+ const managed = {
878
+ ws,
879
+ address,
880
+ endpointKey,
881
+ reconnectOnClose,
882
+ };
883
+ this.sockets.set(address, managed);
884
+ if (this.sockets.size === 1) {
885
+ this.setConnectionStatus(ConnectionStatus.Connecting, `connecting to ${address}`);
886
+ }
887
+ ws.onopen = () => {
888
+ if (this.sockets.get(address) !== managed) {
889
+ ws.close();
890
+ return;
891
+ }
892
+ if (!this.currentServer) {
893
+ this.setCurrentServer(address, 'socket open');
894
+ this.setConnectionStatus(ConnectionStatus.Connected, `connected to ${address}`);
895
+ this.flushQueue();
896
+ this.resendSubscriptions();
897
+ if (this.peerDiscoveryEnabled)
898
+ this.startPeerDiscovery();
899
+ }
900
+ };
901
+ ws.onclose = () => {
902
+ if (this.sockets.get(address) !== managed)
903
+ return;
904
+ this.sockets.delete(address);
905
+ this.endpointSockets.delete(endpointKey);
906
+ if (this.currentServer === address) {
907
+ const next = this.getFirstOpenSocket();
908
+ if (next) {
909
+ this.setCurrentServer(next.address, `failover from ${address} to ${next.address}`);
910
+ this.setConnectionStatus(ConnectionStatus.Connected, `main failover to ${next.address}`);
911
+ this.resendSubscriptions();
912
+ return; // Peer discovery will re-add this server when it's back
913
+ }
914
+ this.setCurrentServer(null, `disconnected from ${address}`);
915
+ this.setConnectionStatus(ConnectionStatus.Disconnected, `no open servers after ${address} closed`);
916
+ }
917
+ // Only retry explicitly configured sockets when completely disconnected.
918
+ // Discovered peers are expected to reappear via peer discovery.
919
+ if (managed.reconnectOnClose &&
920
+ this.shouldReconnect &&
921
+ !this.hasOpenConnection()) {
922
+ this.scheduleReconnect(address, endpointKey);
923
+ }
924
+ };
925
+ ws.onerror = () => { };
926
+ ws.onmessage = (event) => {
927
+ if (this.sockets.get(address) !== managed)
928
+ return;
929
+ this.onMessage(event.data);
930
+ };
931
+ }
932
+ scheduleReconnect(address, endpointKey) {
933
+ if (this.reconnectTimers.has(endpointKey))
934
+ return;
935
+ this.logConnection('reconnect_scheduled', { address, delayMs: 1000 });
936
+ const timer = setTimeout(() => {
937
+ this.reconnectTimers.delete(endpointKey);
938
+ if (this.shouldReconnect &&
939
+ !this.hasOpenConnection() &&
940
+ !this.endpointSockets.has(endpointKey)) {
941
+ this.logConnection('reconnect_attempt', { address });
942
+ this.createSocket(address);
943
+ }
944
+ }, 1000);
945
+ this.reconnectTimers.set(endpointKey, timer);
946
+ }
947
+ // ─────────────────────────────────────────────────────────────────────────────
948
+ // Private: Message Handling
949
+ // ─────────────────────────────────────────────────────────────────────────────
950
+ onMessage(data) {
951
+ this.downMsgCounter.next();
952
+ try {
953
+ let message;
954
+ if (typeof data === 'string') {
955
+ // JSON text message
956
+ message = JSON.parse(data);
957
+ }
958
+ else if (data instanceof ArrayBuffer) {
959
+ // Binary msgpack message
960
+ message = unpackr.unpack(new Uint8Array(data));
961
+ }
962
+ else if (data instanceof Blob) {
963
+ // Handle Blob asynchronously - convert to ArrayBuffer first
964
+ data.arrayBuffer().then((buffer) => {
965
+ const decoded = unpackr.unpack(new Uint8Array(buffer));
966
+ this.routeMessage(decoded);
967
+ });
968
+ return;
969
+ }
970
+ else {
971
+ return;
972
+ }
973
+ this.routeMessage(message);
974
+ }
975
+ catch {
976
+ // Ignore parse errors
977
+ }
978
+ }
979
+ routeMessage(message) {
980
+ switch (message.event) {
981
+ case MykoEvent.QueryResponse:
982
+ this.maybeLogFirstResponseTiming('query', message.data.tx, {
983
+ queryId: this.activeQueries.get(message.data.tx)?.queryId,
984
+ queryName: this.activeQueryNames.get(message.data.tx) ??
985
+ this.activeQueries.get(message.data.tx)?.queryId ??
986
+ 'unknown',
987
+ sequence: message.data.sequence,
988
+ upserts: message.data.upserts.length,
989
+ deletes: message.data.deletes.length,
990
+ });
991
+ this.logConnection('query_response', {
992
+ tx: message.data.tx,
993
+ queryId: this.activeQueries.get(message.data.tx)?.queryId,
994
+ queryItemType: this.activeQueries.get(message.data.tx)?.queryItemType,
995
+ queryName: this.activeQueryNames.get(message.data.tx) ??
996
+ this.activeQueries.get(message.data.tx)?.queryId ??
997
+ 'unknown',
998
+ sequence: message.data.sequence,
999
+ upserts: message.data.upserts.length,
1000
+ deletes: message.data.deletes.length,
1001
+ changes: message.data.changes?.length ?? 0,
1002
+ totalCount: message.data.totalCount ??
1003
+ message.data.total_count ??
1004
+ null,
1005
+ window: message.data.window ?? null,
1006
+ });
1007
+ const queryPublishStarted = this.nowMs();
1008
+ this.queryResponses.next(message);
1009
+ this.logConnection('query_publish_ms', {
1010
+ tx: message.data.tx,
1011
+ sequence: message.data.sequence,
1012
+ publishMs: Number((this.nowMs() - queryPublishStarted).toFixed(2)),
1013
+ });
1014
+ break;
1015
+ case MykoEvent.ViewResponse:
1016
+ // ViewResponse and QueryResponse share the same payload shape.
1017
+ const viewMessage = message;
1018
+ this.maybeLogFirstResponseTiming('view', viewMessage.data.tx, {
1019
+ viewId: this.activeViews.get(viewMessage.data.tx)?.viewId,
1020
+ viewName: this.activeViewNames.get(viewMessage.data.tx) ??
1021
+ this.activeViews.get(viewMessage.data.tx)?.viewId ??
1022
+ 'unknown',
1023
+ sequence: viewMessage.data.sequence,
1024
+ upserts: viewMessage.data.upserts.length,
1025
+ deletes: viewMessage.data.deletes.length,
1026
+ });
1027
+ this.logConnection('view_response', {
1028
+ tx: viewMessage.data.tx,
1029
+ sequence: viewMessage.data.sequence,
1030
+ upserts: viewMessage.data.upserts.length,
1031
+ deletes: viewMessage.data.deletes.length,
1032
+ changes: viewMessage.data.changes?.length ?? 0,
1033
+ totalCount: viewMessage.data
1034
+ .totalCount ??
1035
+ viewMessage.data.total_count ??
1036
+ null,
1037
+ window: viewMessage.data.window ?? null,
1038
+ });
1039
+ const viewPublishStarted = this.nowMs();
1040
+ this.queryResponses.next(viewMessage);
1041
+ this.logConnection('view_publish_ms', {
1042
+ tx: viewMessage.data.tx,
1043
+ sequence: viewMessage.data.sequence,
1044
+ publishMs: Number((this.nowMs() - viewPublishStarted).toFixed(2)),
1045
+ });
1046
+ break;
1047
+ case MykoEvent.ReportResponse:
1048
+ this.reportResponses.next(message);
1049
+ break;
1050
+ case MykoEvent.CommandResponse:
1051
+ this.commandResponses.next(message);
1052
+ break;
1053
+ case MykoEvent.CommandError:
1054
+ this.commandErrors.next(message);
1055
+ break;
1056
+ case MykoEvent.QueryError:
1057
+ this.queryErrors.next(message);
1058
+ this.logConnection('query_error', {
1059
+ tx: message.data.tx,
1060
+ message: message.data.message,
1061
+ queryId: this.activeQueries.get(message.data.tx)?.queryId,
1062
+ queryItemType: this.activeQueries.get(message.data.tx)?.queryItemType,
1063
+ queryName: this.activeQueryNames.get(message.data.tx) ??
1064
+ this.activeQueries.get(message.data.tx)?.queryId ??
1065
+ 'unknown',
1066
+ });
1067
+ break;
1068
+ case MykoEvent.ViewError:
1069
+ this.queryErrors.next(message);
1070
+ this.logConnection('view_error', {
1071
+ tx: message.data.tx,
1072
+ message: message.data.message,
1073
+ viewId: this.activeViews.get(message.data.tx)?.viewId,
1074
+ viewItemType: this.activeViews.get(message.data.tx)?.viewItemType,
1075
+ viewName: this.activeViewNames.get(message.data.tx) ??
1076
+ this.activeViews.get(message.data.tx)?.viewId ??
1077
+ 'unknown',
1078
+ });
1079
+ break;
1080
+ case MykoEvent.ReportError:
1081
+ this.reportErrors.next(message);
1082
+ this.logConnection('report_error', {
1083
+ tx: message.data.tx,
1084
+ message: message.data.message,
1085
+ reportId: this.activeReports.get(message.data.tx)?.reportId,
1086
+ reportName: this.activeReportNames.get(message.data.tx) ??
1087
+ this.activeReports.get(message.data.tx)?.reportId ??
1088
+ 'unknown',
1089
+ });
1090
+ break;
1091
+ case MykoEvent.Ping:
1092
+ this.pingResponses.next(message);
1093
+ break;
1094
+ case MykoEvent.Command:
1095
+ this.commandIncoming.next(message);
1096
+ break;
1097
+ }
1098
+ }
1099
+ scheduleEventBatchFlush() {
1100
+ if (this.eventBatchFlushScheduled)
1101
+ return;
1102
+ this.eventBatchFlushScheduled = true;
1103
+ queueMicrotask(() => {
1104
+ this.eventBatchFlushScheduled = false;
1105
+ this.flushPendingEventBatch();
1106
+ });
1107
+ }
1108
+ flushPendingEventBatch() {
1109
+ if (this.pendingEventBatch.length === 0)
1110
+ return;
1111
+ while (this.pendingEventBatch.length > 0) {
1112
+ const batch = this.pendingEventBatch.splice(0, this.eventBatchMaxSize);
1113
+ this.sendNow({ event: EVENT_BATCH, data: batch });
1114
+ }
1115
+ }
1116
+ send(message) {
1117
+ if (message.event !== EVENT_BATCH) {
1118
+ this.flushPendingEventBatch();
1119
+ }
1120
+ this.sendNow(message);
1121
+ }
1122
+ messageTx(message) {
1123
+ const data = message.data;
1124
+ return typeof data?.tx === 'string' ? data.tx : null;
1125
+ }
1126
+ sendNow(message) {
1127
+ const event = message.event;
1128
+ const tx = this.messageTx(message);
1129
+ if (this.currentServer) {
1130
+ const managed = this.sockets.get(this.currentServer);
1131
+ if (managed?.ws.readyState === WebSocket.OPEN) {
1132
+ const encoded = this.protocol === MykoProtocol.MSGPACK
1133
+ ? new Uint8Array(packr.pack(message))
1134
+ : JSON.stringify(message);
1135
+ managed.ws.send(encoded);
1136
+ this.upMsgCounter.next();
1137
+ return;
1138
+ }
1139
+ }
1140
+ this.messageQueue.push(message);
1141
+ this.logConnection('ws_enqueue', {
1142
+ event,
1143
+ tx,
1144
+ queueDepth: this.messageQueue.length,
1145
+ currentServer: this.currentServer,
1146
+ });
1147
+ }
1148
+ flushQueue() {
1149
+ const queue = this.messageQueue;
1150
+ this.messageQueue = [];
1151
+ this.logConnection('ws_flush_queue', {
1152
+ queuedMessages: queue.length,
1153
+ currentServer: this.currentServer,
1154
+ });
1155
+ for (const msg of queue)
1156
+ this.send(msg);
1157
+ }
1158
+ withReconnectSequenceReset(query) {
1159
+ const queryPayload = query.query;
1160
+ if (queryPayload &&
1161
+ typeof queryPayload === 'object' &&
1162
+ !Array.isArray(queryPayload)) {
1163
+ return {
1164
+ ...query,
1165
+ query: {
1166
+ ...queryPayload,
1167
+ seq: 0,
1168
+ },
1169
+ };
1170
+ }
1171
+ return query;
1172
+ }
1173
+ resendSubscriptions() {
1174
+ for (const q of this.activeQueries.values()) {
1175
+ this.send({
1176
+ event: MykoEvent.Query,
1177
+ data: this.withReconnectSequenceReset(q),
1178
+ });
1179
+ }
1180
+ for (const v of this.activeViews.values()) {
1181
+ this.send({ event: MykoEvent.View, data: v });
1182
+ }
1183
+ for (const r of this.activeReports.values()) {
1184
+ this.send({ event: MykoEvent.Report, data: r });
1185
+ }
1186
+ }
1187
+ setConnectionStatus(status, reason) {
1188
+ this.connectionStatusSubject.next(status);
1189
+ this.logConnection('status', {
1190
+ status,
1191
+ reason,
1192
+ currentServer: this.currentServer,
1193
+ openServers: this.getOpenServers(),
1194
+ });
1195
+ }
1196
+ setCurrentServer(server, reason) {
1197
+ this.currentServer = server;
1198
+ this.currentServerSubject.next(server);
1199
+ this.logConnection('current_server', { server, reason });
1200
+ }
1201
+ nowMs() {
1202
+ if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
1203
+ return performance.now();
1204
+ }
1205
+ return Date.now();
1206
+ }
1207
+ maybeLogFirstResponseTiming(kind, tx, details) {
1208
+ if (this.firstResponseLogged.has(tx))
1209
+ return;
1210
+ const startedAt = this.subscriptionStartMs.get(tx);
1211
+ if (startedAt === undefined)
1212
+ return;
1213
+ const elapsedMs = this.nowMs() - startedAt;
1214
+ this.firstResponseLogged.add(tx);
1215
+ this.logConnection(`${kind}_first_response_timing`, {
1216
+ tx,
1217
+ subscribeToFirstResponseMs: Number(elapsedMs.toFixed(2)),
1218
+ ...details,
1219
+ });
1220
+ }
1221
+ connectionLogLevel(event) {
1222
+ // Main lifecycle state transitions remain visible at info.
1223
+ const infoEvents = new Set(['status', 'current_server']);
1224
+ if (infoEvents.has(event))
1225
+ return 'info';
1226
+ // Subscription setup and first-response timing are debug-level diagnostics.
1227
+ const debugEvents = new Set([
1228
+ 'socket_connecting',
1229
+ 'peer_discovery_started',
1230
+ 'peer_discovery_update',
1231
+ 'query_subscribe',
1232
+ 'view_subscribe',
1233
+ 'report_subscribe',
1234
+ 'query_cancel',
1235
+ 'view_cancel',
1236
+ 'report_cancel',
1237
+ 'query_first_response_timing',
1238
+ 'view_first_response_timing',
1239
+ 'reconnect_scheduled',
1240
+ 'reconnect_attempt',
1241
+ 'query_shape_invalid',
1242
+ ]);
1243
+ if (debugEvents.has(event))
1244
+ return 'debug';
1245
+ // Follow-up response churn and transport internals are verbose-level.
1246
+ const verboseEvents = new Set([
1247
+ 'ws_enqueue',
1248
+ 'ws_flush_queue',
1249
+ 'query_response',
1250
+ 'view_response',
1251
+ 'report_response',
1252
+ 'query_publish_ms',
1253
+ 'view_publish_ms',
1254
+ ]);
1255
+ if (verboseEvents.has(event))
1256
+ return 'verbose';
1257
+ if (event.endsWith('_error'))
1258
+ return 'error';
1259
+ return 'debug';
1260
+ }
1261
+ static resolveDefaultConnectionLogLevel() {
1262
+ const readGlobal = () => {
1263
+ const globalObj = globalThis;
1264
+ const value = globalObj.MYKO_CLIENT_LOG_LEVEL;
1265
+ return typeof value === 'string' ? value : undefined;
1266
+ };
1267
+ const readProcessEnv = () => {
1268
+ const processLike = globalThis.process;
1269
+ return processLike?.env?.MYKO_CLIENT_LOG_LEVEL;
1270
+ };
1271
+ const value = (readGlobal() ?? readProcessEnv() ?? '').toLowerCase();
1272
+ switch (value) {
1273
+ case 'silent':
1274
+ case 'error':
1275
+ case 'warn':
1276
+ case 'info':
1277
+ case 'debug':
1278
+ case 'verbose':
1279
+ return value;
1280
+ default:
1281
+ return 'warn';
1282
+ }
1283
+ }
1284
+ shouldLogConnection(level) {
1285
+ const priority = {
1286
+ silent: 0,
1287
+ error: 1,
1288
+ warn: 2,
1289
+ info: 3,
1290
+ debug: 4,
1291
+ verbose: 5,
1292
+ };
1293
+ return priority[level] <= priority[this.connectionLogLevelThreshold];
1294
+ }
1295
+ logConnection(event, details) {
1296
+ const level = this.connectionLogLevel(event);
1297
+ if (!this.shouldLogConnection(level))
1298
+ return;
1299
+ const maybeVerbose = console.verbose;
1300
+ const logger = level === 'info'
1301
+ ? console.info
1302
+ : level === 'warn'
1303
+ ? console.warn
1304
+ : level === 'error'
1305
+ ? console.error
1306
+ : level === 'verbose' || level === 'debug'
1307
+ ? maybeVerbose ?? console.debug ?? console.log
1308
+ : console.log;
1309
+ const levelPrefix = level === 'verbose' ? '[verbose] ' : '';
1310
+ logger(`${levelPrefix}[MykoClient] ${event}`, {
1311
+ tsIso: new Date().toISOString(),
1312
+ tsMs: Date.now(),
1313
+ ...details,
1314
+ });
1315
+ }
1316
+ }