@brightchain/brightchain-api-lib 0.23.25 → 0.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (438) hide show
  1. package/package.json +5 -5
  2. package/src/lib/__tests__/fixtures/mock-backend-brightchain-member.d.ts.map +1 -1
  3. package/src/lib/__tests__/fixtures/mock-backend-brightchain-member.js +11 -0
  4. package/src/lib/__tests__/fixtures/mock-backend-brightchain-member.js.map +1 -1
  5. package/src/lib/__tests__/fixtures/mocked-model.js +63 -60
  6. package/src/lib/__tests__/fixtures/mocked-model.js.map +1 -1
  7. package/src/lib/application.d.ts +1 -1
  8. package/src/lib/application.d.ts.map +1 -1
  9. package/src/lib/application.js +103 -34
  10. package/src/lib/application.js.map +1 -1
  11. package/src/lib/auth/aclEnforcedAvailability.js +4 -0
  12. package/src/lib/auth/aclEnforcedAvailability.js.map +1 -1
  13. package/src/lib/auth/aclEnforcedBlockStore.js +7 -0
  14. package/src/lib/auth/aclEnforcedBlockStore.js.map +1 -1
  15. package/src/lib/auth/ecdsaNodeAuthenticator.js +1 -0
  16. package/src/lib/auth/ecdsaNodeAuthenticator.js.map +1 -1
  17. package/src/lib/auth/poolAclBootstrap.js +2 -0
  18. package/src/lib/auth/poolAclBootstrap.js.map +1 -1
  19. package/src/lib/auth/poolAclStore.js +2 -1
  20. package/src/lib/auth/poolAclStore.js.map +1 -1
  21. package/src/lib/auth/poolAclUpdater.js +4 -0
  22. package/src/lib/auth/poolAclUpdater.js.map +1 -1
  23. package/src/lib/availability/availabilityMetrics.js +43 -45
  24. package/src/lib/availability/availabilityMetrics.js.map +1 -1
  25. package/src/lib/availability/availabilityService.js +26 -20
  26. package/src/lib/availability/availabilityService.js.map +1 -1
  27. package/src/lib/availability/blockRegistry.js +46 -25
  28. package/src/lib/availability/blockRegistry.js.map +1 -1
  29. package/src/lib/availability/discoveryProtocol.js +10 -8
  30. package/src/lib/availability/discoveryProtocol.js.map +1 -1
  31. package/src/lib/availability/gossipService.js +56 -45
  32. package/src/lib/availability/gossipService.js.map +1 -1
  33. package/src/lib/availability/heartbeatMonitor.js +22 -20
  34. package/src/lib/availability/heartbeatMonitor.js.map +1 -1
  35. package/src/lib/availability/poolDiscoveryService.js +7 -1
  36. package/src/lib/availability/poolDiscoveryService.js.map +1 -1
  37. package/src/lib/availability/quorumGossipHandler.js +13 -6
  38. package/src/lib/availability/quorumGossipHandler.js.map +1 -1
  39. package/src/lib/availability/reconciliationService.js +18 -12
  40. package/src/lib/availability/reconciliationService.js.map +1 -1
  41. package/src/lib/blockFetch/blockFetcher.js +14 -8
  42. package/src/lib/blockFetch/blockFetcher.js.map +1 -1
  43. package/src/lib/blockFetch/fetchQueue.js +9 -7
  44. package/src/lib/blockFetch/fetchQueue.js.map +1 -1
  45. package/src/lib/blockFetch/httpBlockFetchTransport.js +2 -0
  46. package/src/lib/blockFetch/httpBlockFetchTransport.js.map +1 -1
  47. package/src/lib/browserKeyring.js +5 -2
  48. package/src/lib/browserKeyring.js.map +1 -1
  49. package/src/lib/controllers/api/blocks.d.ts.map +1 -1
  50. package/src/lib/controllers/api/blocks.js +9 -3
  51. package/src/lib/controllers/api/blocks.js.map +1 -1
  52. package/src/lib/controllers/api/brighthub/connectionController.d.ts +80 -0
  53. package/src/lib/controllers/api/brighthub/connectionController.d.ts.map +1 -0
  54. package/src/lib/controllers/api/brighthub/connectionController.js +890 -0
  55. package/src/lib/controllers/api/brighthub/connectionController.js.map +1 -0
  56. package/src/lib/controllers/api/brighthub/index.d.ts +9 -0
  57. package/src/lib/controllers/api/brighthub/index.d.ts.map +1 -0
  58. package/src/lib/controllers/api/brighthub/index.js +12 -0
  59. package/src/lib/controllers/api/brighthub/index.js.map +1 -0
  60. package/src/lib/controllers/api/brighthub/messagingController.d.ts +84 -0
  61. package/src/lib/controllers/api/brighthub/messagingController.d.ts.map +1 -0
  62. package/src/lib/controllers/api/brighthub/messagingController.js +1077 -0
  63. package/src/lib/controllers/api/brighthub/messagingController.js.map +1 -0
  64. package/src/lib/controllers/api/brighthub/notificationController.d.ts +89 -0
  65. package/src/lib/controllers/api/brighthub/notificationController.d.ts.map +1 -0
  66. package/src/lib/controllers/api/brighthub/notificationController.js +444 -0
  67. package/src/lib/controllers/api/brighthub/notificationController.js.map +1 -0
  68. package/src/lib/controllers/api/brighthub/postController.d.ts +75 -0
  69. package/src/lib/controllers/api/brighthub/postController.d.ts.map +1 -0
  70. package/src/lib/controllers/api/brighthub/postController.js +388 -0
  71. package/src/lib/controllers/api/brighthub/postController.js.map +1 -0
  72. package/src/lib/controllers/api/brighthub/timelineController.d.ts +74 -0
  73. package/src/lib/controllers/api/brighthub/timelineController.d.ts.map +1 -0
  74. package/src/lib/controllers/api/brighthub/timelineController.js +418 -0
  75. package/src/lib/controllers/api/brighthub/timelineController.js.map +1 -0
  76. package/src/lib/controllers/api/brightpass.d.ts.map +1 -1
  77. package/src/lib/controllers/api/brightpass.js +46 -4
  78. package/src/lib/controllers/api/brightpass.js.map +1 -1
  79. package/src/lib/controllers/api/cbl.js +1 -0
  80. package/src/lib/controllers/api/cbl.js.map +1 -1
  81. package/src/lib/controllers/api/channels.js +2 -2
  82. package/src/lib/controllers/api/channels.js.map +1 -1
  83. package/src/lib/controllers/api/conversations.js +1 -1
  84. package/src/lib/controllers/api/conversations.js.map +1 -1
  85. package/src/lib/controllers/api/docs.js +2 -1
  86. package/src/lib/controllers/api/docs.js.map +1 -1
  87. package/src/lib/controllers/api/emails.d.ts.map +1 -1
  88. package/src/lib/controllers/api/emails.js +16 -2
  89. package/src/lib/controllers/api/emails.js.map +1 -1
  90. package/src/lib/controllers/api/explodingMessages.js +2 -4
  91. package/src/lib/controllers/api/explodingMessages.js.map +1 -1
  92. package/src/lib/controllers/api/groups.js +2 -2
  93. package/src/lib/controllers/api/groups.js.map +1 -1
  94. package/src/lib/controllers/api/health.d.ts +9 -2
  95. package/src/lib/controllers/api/health.d.ts.map +1 -1
  96. package/src/lib/controllers/api/health.js +48 -6
  97. package/src/lib/controllers/api/health.js.map +1 -1
  98. package/src/lib/controllers/api/index.d.ts +1 -0
  99. package/src/lib/controllers/api/index.d.ts.map +1 -1
  100. package/src/lib/controllers/api/index.js +1 -0
  101. package/src/lib/controllers/api/index.js.map +1 -1
  102. package/src/lib/controllers/api/introspection.js +2 -0
  103. package/src/lib/controllers/api/introspection.js.map +1 -1
  104. package/src/lib/controllers/api/messages.js +1 -1
  105. package/src/lib/controllers/api/messages.js.map +1 -1
  106. package/src/lib/controllers/api/nodes.js +6 -6
  107. package/src/lib/controllers/api/nodes.js.map +1 -1
  108. package/src/lib/controllers/api/quorum.d.ts +1 -1
  109. package/src/lib/controllers/api/quorum.d.ts.map +1 -1
  110. package/src/lib/controllers/api/quorum.js +3 -2
  111. package/src/lib/controllers/api/quorum.js.map +1 -1
  112. package/src/lib/controllers/api/scbl.js +3 -0
  113. package/src/lib/controllers/api/scbl.js.map +1 -1
  114. package/src/lib/controllers/api/sessions.d.ts +18 -2
  115. package/src/lib/controllers/api/sessions.d.ts.map +1 -1
  116. package/src/lib/controllers/api/sessions.js +60 -3
  117. package/src/lib/controllers/api/sessions.js.map +1 -1
  118. package/src/lib/controllers/api/sync.js +4 -4
  119. package/src/lib/controllers/api/sync.js.map +1 -1
  120. package/src/lib/controllers/api/user.d.ts +2 -0
  121. package/src/lib/controllers/api/user.d.ts.map +1 -1
  122. package/src/lib/controllers/api/user.js +95 -3
  123. package/src/lib/controllers/api/user.js.map +1 -1
  124. package/src/lib/controllers/identity/deviceController.js +2 -2
  125. package/src/lib/controllers/identity/deviceController.js.map +1 -1
  126. package/src/lib/controllers/identity/directoryController.js +1 -1
  127. package/src/lib/controllers/identity/directoryController.js.map +1 -1
  128. package/src/lib/controllers/identity/identityProofController.js +4 -6
  129. package/src/lib/controllers/identity/identityProofController.js.map +1 -1
  130. package/src/lib/databaseInit.d.ts +3 -3
  131. package/src/lib/databaseInit.js +5 -5
  132. package/src/lib/databaseInit.js.map +1 -1
  133. package/src/lib/datastore/block-document-store.d.ts.map +1 -1
  134. package/src/lib/datastore/block-document-store.js +18 -6
  135. package/src/lib/datastore/block-document-store.js.map +1 -1
  136. package/src/lib/datastore/document-store.d.ts.map +1 -1
  137. package/src/lib/datastore/memory-document-store.js +5 -2
  138. package/src/lib/datastore/memory-document-store.js.map +1 -1
  139. package/src/lib/encryption/encryptedMetadataService.js +2 -0
  140. package/src/lib/encryption/encryptedMetadataService.js.map +1 -1
  141. package/src/lib/encryption/encryptionAwareReplication.js +3 -0
  142. package/src/lib/encryption/encryptionAwareReplication.js.map +1 -1
  143. package/src/lib/encryption/errors.d.ts.map +1 -1
  144. package/src/lib/encryption/errors.js +8 -0
  145. package/src/lib/encryption/errors.js.map +1 -1
  146. package/src/lib/encryption/poolKeyManager.js +2 -0
  147. package/src/lib/encryption/poolKeyManager.js.map +1 -1
  148. package/src/lib/environment.js +10 -0
  149. package/src/lib/environment.js.map +1 -1
  150. package/src/lib/errors/express-validation.js +1 -0
  151. package/src/lib/errors/express-validation.js.map +1 -1
  152. package/src/lib/errors/invalid-backup-code-version.js +1 -0
  153. package/src/lib/errors/invalid-backup-code-version.js.map +1 -1
  154. package/src/lib/errors/memberIndexSchemaValidationError.js +1 -0
  155. package/src/lib/errors/memberIndexSchemaValidationError.js.map +1 -1
  156. package/src/lib/errors/missing-validated-data.js +2 -0
  157. package/src/lib/errors/missing-validated-data.js.map +1 -1
  158. package/src/lib/errors/token-not-found.js +1 -0
  159. package/src/lib/errors/token-not-found.js.map +1 -1
  160. package/src/lib/errors/typed-error-local.js +3 -0
  161. package/src/lib/errors/typed-error-local.js.map +1 -1
  162. package/src/lib/interfaces/brightpass/index.d.ts +1 -1
  163. package/src/lib/interfaces/brightpass/index.d.ts.map +1 -1
  164. package/src/lib/interfaces/environment.d.ts +1 -1
  165. package/src/lib/interfaces/environment.d.ts.map +1 -1
  166. package/src/lib/interfaces/member/memberProfileResponse.d.ts.map +1 -1
  167. package/src/lib/interfaces/member/operational.d.ts.map +1 -1
  168. package/src/lib/interfaces/member-init-config.d.ts +1 -1
  169. package/src/lib/interfaces/member-init-config.d.ts.map +1 -1
  170. package/src/lib/interfaces/responses/api-backup-codes-response.d.ts.map +1 -1
  171. package/src/lib/interfaces/responses/api-challenge-response.d.ts.map +1 -1
  172. package/src/lib/interfaces/responses/api-code-count-response.d.ts.map +1 -1
  173. package/src/lib/interfaces/responses/api-detailed-health-response.d.ts.map +1 -1
  174. package/src/lib/interfaces/responses/api-discover-block-response.d.ts.map +1 -1
  175. package/src/lib/interfaces/responses/api-express-validation-error.d.ts.map +1 -1
  176. package/src/lib/interfaces/responses/api-get-block-response.d.ts.map +1 -1
  177. package/src/lib/interfaces/responses/api-get-node-response.d.ts.map +1 -1
  178. package/src/lib/interfaces/responses/api-health-response.d.ts.map +1 -1
  179. package/src/lib/interfaces/responses/api-list-nodes-response.d.ts.map +1 -1
  180. package/src/lib/interfaces/responses/api-login-response.d.ts.map +1 -1
  181. package/src/lib/interfaces/responses/api-members-response.d.ts.map +1 -1
  182. package/src/lib/interfaces/responses/api-mnemonic-response.d.ts.map +1 -1
  183. package/src/lib/interfaces/responses/api-reconcile-response.d.ts.map +1 -1
  184. package/src/lib/interfaces/responses/api-register-node-response.d.ts.map +1 -1
  185. package/src/lib/interfaces/responses/api-registration-response.d.ts.map +1 -1
  186. package/src/lib/interfaces/responses/api-replicate-block-response.d.ts.map +1 -1
  187. package/src/lib/interfaces/responses/api-request-user-response.d.ts.map +1 -1
  188. package/src/lib/interfaces/responses/api-store-block-response.d.ts.map +1 -1
  189. package/src/lib/interfaces/responses/api-store-cbl-response.d.ts.map +1 -1
  190. package/src/lib/interfaces/responses/api-sync-request-response.d.ts.map +1 -1
  191. package/src/lib/interfaces/responses/brighthub/api-connection-response.d.ts +21 -0
  192. package/src/lib/interfaces/responses/brighthub/api-connection-response.d.ts.map +1 -0
  193. package/src/lib/interfaces/responses/brighthub/api-connection-response.js +3 -0
  194. package/src/lib/interfaces/responses/brighthub/api-connection-response.js.map +1 -0
  195. package/src/lib/interfaces/responses/brighthub/api-messaging-response.d.ts +39 -0
  196. package/src/lib/interfaces/responses/brighthub/api-messaging-response.d.ts.map +1 -0
  197. package/src/lib/interfaces/responses/brighthub/api-messaging-response.js +3 -0
  198. package/src/lib/interfaces/responses/brighthub/api-messaging-response.js.map +1 -0
  199. package/src/lib/interfaces/responses/brighthub/api-notification-response.d.ts +30 -0
  200. package/src/lib/interfaces/responses/brighthub/api-notification-response.d.ts.map +1 -0
  201. package/src/lib/interfaces/responses/brighthub/api-notification-response.js +3 -0
  202. package/src/lib/interfaces/responses/brighthub/api-notification-response.js.map +1 -0
  203. package/src/lib/interfaces/responses/brighthub/api-post-response.d.ts +21 -0
  204. package/src/lib/interfaces/responses/brighthub/api-post-response.d.ts.map +1 -0
  205. package/src/lib/interfaces/responses/brighthub/api-post-response.js +3 -0
  206. package/src/lib/interfaces/responses/brighthub/api-post-response.js.map +1 -0
  207. package/src/lib/interfaces/responses/brighthub/api-timeline-response.d.ts +14 -0
  208. package/src/lib/interfaces/responses/brighthub/api-timeline-response.d.ts.map +1 -0
  209. package/src/lib/interfaces/responses/brighthub/api-timeline-response.js +3 -0
  210. package/src/lib/interfaces/responses/brighthub/api-timeline-response.js.map +1 -0
  211. package/src/lib/interfaces/responses/brighthub/api-user-profile-response.d.ts +10 -0
  212. package/src/lib/interfaces/responses/brighthub/api-user-profile-response.d.ts.map +1 -0
  213. package/src/lib/interfaces/responses/brighthub/api-user-profile-response.js +3 -0
  214. package/src/lib/interfaces/responses/brighthub/api-user-profile-response.js.map +1 -0
  215. package/src/lib/interfaces/responses/brighthub/index.d.ts +13 -0
  216. package/src/lib/interfaces/responses/brighthub/index.d.ts.map +1 -0
  217. package/src/lib/interfaces/responses/brighthub/index.js +9 -0
  218. package/src/lib/interfaces/responses/brighthub/index.js.map +1 -0
  219. package/src/lib/interfaces/responses/index.d.ts +1 -0
  220. package/src/lib/interfaces/responses/index.d.ts.map +1 -1
  221. package/src/lib/interfaces/storage/storedDocumentTypes.d.ts +1 -1
  222. package/src/lib/interfaces/storage/storedDocumentTypes.js +1 -1
  223. package/src/lib/interfaces/storage/userRoleSchema.d.ts.map +1 -1
  224. package/src/lib/interfaces/storage/userRoleSchema.js +1 -3
  225. package/src/lib/interfaces/storage/userRoleSchema.js.map +1 -1
  226. package/src/lib/middlewares.js +31 -31
  227. package/src/lib/middlewares.js.map +1 -1
  228. package/src/lib/nodeKeyring.js +2 -0
  229. package/src/lib/nodeKeyring.js.map +1 -1
  230. package/src/lib/plugins/brightchain-database-plugin.d.ts +14 -14
  231. package/src/lib/plugins/brightchain-database-plugin.d.ts.map +1 -1
  232. package/src/lib/plugins/brightchain-database-plugin.js +36 -34
  233. package/src/lib/plugins/brightchain-database-plugin.js.map +1 -1
  234. package/src/lib/routers/api.d.ts +62 -1
  235. package/src/lib/routers/api.d.ts.map +1 -1
  236. package/src/lib/routers/api.js +123 -2
  237. package/src/lib/routers/api.js.map +1 -1
  238. package/src/lib/routers/app.d.ts.map +1 -1
  239. package/src/lib/routers/app.js +10 -1
  240. package/src/lib/routers/app.js.map +1 -1
  241. package/src/lib/routers/base.js +2 -0
  242. package/src/lib/routers/base.js.map +1 -1
  243. package/src/lib/secureEnclaveKeyring.js +4 -2
  244. package/src/lib/secureEnclaveKeyring.js.map +1 -1
  245. package/src/lib/services/auth.d.ts.map +1 -1
  246. package/src/lib/services/auth.js +11 -2
  247. package/src/lib/services/auth.js.map +1 -1
  248. package/src/lib/services/base.js +1 -0
  249. package/src/lib/services/base.js.map +1 -1
  250. package/src/lib/services/blockServiceFactory.js +2 -0
  251. package/src/lib/services/blockServiceFactory.js.map +1 -1
  252. package/src/lib/services/blockStore.js +3 -2
  253. package/src/lib/services/blockStore.js.map +1 -1
  254. package/src/lib/services/blocks.js +1 -0
  255. package/src/lib/services/blocks.js.map +1 -1
  256. package/src/lib/services/brightChainBackupCodeService.d.ts +114 -0
  257. package/src/lib/services/brightChainBackupCodeService.d.ts.map +1 -0
  258. package/src/lib/services/brightChainBackupCodeService.js +303 -0
  259. package/src/lib/services/brightChainBackupCodeService.js.map +1 -0
  260. package/src/lib/services/brightchain-authentication-provider.d.ts.map +1 -1
  261. package/src/lib/services/brightchain-authentication-provider.js +40 -9
  262. package/src/lib/services/brightchain-authentication-provider.js.map +1 -1
  263. package/src/lib/services/brightchain-member-init.service.d.ts +17 -17
  264. package/src/lib/services/brightchain-member-init.service.d.ts.map +1 -1
  265. package/src/lib/services/brightchain-member-init.service.js +12 -9
  266. package/src/lib/services/brightchain-member-init.service.js.map +1 -1
  267. package/src/lib/services/brighthub/connectionService.d.ts +286 -0
  268. package/src/lib/services/brighthub/connectionService.d.ts.map +1 -0
  269. package/src/lib/services/brighthub/connectionService.js +1887 -0
  270. package/src/lib/services/brighthub/connectionService.js.map +1 -0
  271. package/src/lib/services/brighthub/discoveryService.d.ts +110 -0
  272. package/src/lib/services/brighthub/discoveryService.d.ts.map +1 -0
  273. package/src/lib/services/brighthub/discoveryService.js +528 -0
  274. package/src/lib/services/brighthub/discoveryService.js.map +1 -0
  275. package/src/lib/services/brighthub/feedService.d.ts +141 -0
  276. package/src/lib/services/brighthub/feedService.d.ts.map +1 -0
  277. package/src/lib/services/brighthub/feedService.js +418 -0
  278. package/src/lib/services/brighthub/feedService.js.map +1 -0
  279. package/src/lib/services/brighthub/index.d.ts +11 -0
  280. package/src/lib/services/brighthub/index.d.ts.map +1 -0
  281. package/src/lib/services/brighthub/index.js +14 -0
  282. package/src/lib/services/brighthub/index.js.map +1 -0
  283. package/src/lib/services/brighthub/messagingService.d.ts +109 -0
  284. package/src/lib/services/brighthub/messagingService.d.ts.map +1 -0
  285. package/src/lib/services/brighthub/messagingService.js +947 -0
  286. package/src/lib/services/brighthub/messagingService.js.map +1 -0
  287. package/src/lib/services/brighthub/messagingService.test-helpers.d.ts +75 -0
  288. package/src/lib/services/brighthub/messagingService.test-helpers.d.ts.map +1 -0
  289. package/src/lib/services/brighthub/messagingService.test-helpers.js +237 -0
  290. package/src/lib/services/brighthub/messagingService.test-helpers.js.map +1 -0
  291. package/src/lib/services/brighthub/notificationService.d.ts +172 -0
  292. package/src/lib/services/brighthub/notificationService.d.ts.map +1 -0
  293. package/src/lib/services/brighthub/notificationService.js +768 -0
  294. package/src/lib/services/brighthub/notificationService.js.map +1 -0
  295. package/src/lib/services/brighthub/notificationService.test-helpers.d.ts +75 -0
  296. package/src/lib/services/brighthub/notificationService.test-helpers.d.ts.map +1 -0
  297. package/src/lib/services/brighthub/notificationService.test-helpers.js +230 -0
  298. package/src/lib/services/brighthub/notificationService.test-helpers.js.map +1 -0
  299. package/src/lib/services/brighthub/postService.d.ts +129 -0
  300. package/src/lib/services/brighthub/postService.d.ts.map +1 -0
  301. package/src/lib/services/brighthub/postService.js +470 -0
  302. package/src/lib/services/brighthub/postService.js.map +1 -0
  303. package/src/lib/services/brighthub/postService.test-helpers.d.ts +40 -0
  304. package/src/lib/services/brighthub/postService.test-helpers.d.ts.map +1 -0
  305. package/src/lib/services/brighthub/postService.test-helpers.js +84 -0
  306. package/src/lib/services/brighthub/postService.test-helpers.js.map +1 -0
  307. package/src/lib/services/brighthub/textFormatter.d.ts +64 -0
  308. package/src/lib/services/brighthub/textFormatter.d.ts.map +1 -0
  309. package/src/lib/services/brighthub/textFormatter.js +256 -0
  310. package/src/lib/services/brighthub/textFormatter.js.map +1 -0
  311. package/src/lib/services/brighthub/threadService.d.ts +79 -0
  312. package/src/lib/services/brighthub/threadService.d.ts.map +1 -0
  313. package/src/lib/services/brighthub/threadService.js +246 -0
  314. package/src/lib/services/brighthub/threadService.js.map +1 -0
  315. package/src/lib/services/brighthub/userProfileService.d.ts +203 -0
  316. package/src/lib/services/brighthub/userProfileService.d.ts.map +1 -0
  317. package/src/lib/services/brighthub/userProfileService.js +868 -0
  318. package/src/lib/services/brighthub/userProfileService.js.map +1 -0
  319. package/src/lib/services/brighthub/userProfileService.test-helpers.d.ts +86 -0
  320. package/src/lib/services/brighthub/userProfileService.test-helpers.d.ts.map +1 -0
  321. package/src/lib/services/brighthub/userProfileService.test-helpers.js +169 -0
  322. package/src/lib/services/brighthub/userProfileService.test-helpers.js.map +1 -0
  323. package/src/lib/services/brighthub/webSocketServer.d.ts +68 -0
  324. package/src/lib/services/brighthub/webSocketServer.d.ts.map +1 -0
  325. package/src/lib/services/brighthub/webSocketServer.js +194 -0
  326. package/src/lib/services/brighthub/webSocketServer.js.map +1 -0
  327. package/src/lib/services/brightpass/auditLogger.js +10 -5
  328. package/src/lib/services/brightpass/auditLogger.js.map +1 -1
  329. package/src/lib/services/brightpass/vaultEncryption.js +8 -8
  330. package/src/lib/services/brightpass/vaultEncryption.js.map +1 -1
  331. package/src/lib/services/brightpass.js +16 -8
  332. package/src/lib/services/brightpass.js.map +1 -1
  333. package/src/lib/services/cliOperatorPrompt.js +5 -2
  334. package/src/lib/services/cliOperatorPrompt.js.map +1 -1
  335. package/src/lib/services/clientWebSocketServer.d.ts +69 -4
  336. package/src/lib/services/clientWebSocketServer.d.ts.map +1 -1
  337. package/src/lib/services/clientWebSocketServer.js +188 -10
  338. package/src/lib/services/clientWebSocketServer.js.map +1 -1
  339. package/src/lib/services/contentAwareBlocksService.js +2 -0
  340. package/src/lib/services/contentAwareBlocksService.js.map +1 -1
  341. package/src/lib/services/contentIngestionService.d.ts.map +1 -1
  342. package/src/lib/services/contentIngestionService.js +2 -0
  343. package/src/lib/services/contentIngestionService.js.map +1 -1
  344. package/src/lib/services/diskQuorumService.js +3 -0
  345. package/src/lib/services/diskQuorumService.js.map +1 -1
  346. package/src/lib/services/email.js +5 -0
  347. package/src/lib/services/email.js.map +1 -1
  348. package/src/lib/services/eventNotificationSystem.d.ts +51 -10
  349. package/src/lib/services/eventNotificationSystem.d.ts.map +1 -1
  350. package/src/lib/services/eventNotificationSystem.js +76 -23
  351. package/src/lib/services/eventNotificationSystem.js.map +1 -1
  352. package/src/lib/services/expirationScheduler.js +6 -3
  353. package/src/lib/services/expirationScheduler.js.map +1 -1
  354. package/src/lib/services/fakeEmailService.js +3 -4
  355. package/src/lib/services/fakeEmailService.js.map +1 -1
  356. package/src/lib/services/fec.js +1 -3
  357. package/src/lib/services/fec.js.map +1 -1
  358. package/src/lib/services/fecServiceFactory.js +2 -2
  359. package/src/lib/services/fecServiceFactory.js.map +1 -1
  360. package/src/lib/services/fecUsageExample.js +1 -3
  361. package/src/lib/services/fecUsageExample.js.map +1 -1
  362. package/src/lib/services/identityExpirationScheduler.d.ts.map +1 -1
  363. package/src/lib/services/identityExpirationScheduler.js +7 -2
  364. package/src/lib/services/identityExpirationScheduler.js.map +1 -1
  365. package/src/lib/services/index.d.ts +3 -2
  366. package/src/lib/services/index.d.ts.map +1 -1
  367. package/src/lib/services/index.js +3 -2
  368. package/src/lib/services/index.js.map +1 -1
  369. package/src/lib/services/messageEventsWebSocketHandler.js +1 -0
  370. package/src/lib/services/messageEventsWebSocketHandler.js.map +1 -1
  371. package/src/lib/services/messagePassingService.js +5 -0
  372. package/src/lib/services/messagePassingService.js.map +1 -1
  373. package/src/lib/services/nativeRsFecService.js +3 -5
  374. package/src/lib/services/nativeRsFecService.js.map +1 -1
  375. package/src/lib/services/presenceService.js +5 -4
  376. package/src/lib/services/presenceService.js.map +1 -1
  377. package/src/lib/services/quorum.js +3 -2
  378. package/src/lib/services/quorum.js.map +1 -1
  379. package/src/lib/services/quorumDatabaseAdapter.d.ts +5 -5
  380. package/src/lib/services/quorumDatabaseAdapter.d.ts.map +1 -1
  381. package/src/lib/services/quorumDatabaseAdapter.js +5 -3
  382. package/src/lib/services/quorumDatabaseAdapter.js.map +1 -1
  383. package/src/lib/services/rbac-input-builder.js +5 -0
  384. package/src/lib/services/rbac-input-builder.js.map +1 -1
  385. package/src/lib/services/secureKeyStorage.js +3 -0
  386. package/src/lib/services/secureKeyStorage.js.map +1 -1
  387. package/src/lib/services/sessionAdapter.d.ts +4 -4
  388. package/src/lib/services/sessionAdapter.d.ts.map +1 -1
  389. package/src/lib/services/sessionAdapter.js +3 -2
  390. package/src/lib/services/sessionAdapter.js.map +1 -1
  391. package/src/lib/services/webSocketMessageServer.js +7 -4
  392. package/src/lib/services/webSocketMessageServer.js.map +1 -1
  393. package/src/lib/services/webSocketPeerProvider.js +2 -1
  394. package/src/lib/services/webSocketPeerProvider.js.map +1 -1
  395. package/src/lib/services/websocketHandler.js +9 -1
  396. package/src/lib/services/websocketHandler.js.map +1 -1
  397. package/src/lib/shared-types.d.ts +1 -2
  398. package/src/lib/shared-types.d.ts.map +1 -1
  399. package/src/lib/stores/__tests__/helpers/mockCloudBlockStore.d.ts +63 -0
  400. package/src/lib/stores/__tests__/helpers/mockCloudBlockStore.d.ts.map +1 -0
  401. package/src/lib/stores/__tests__/helpers/mockCloudBlockStore.js +160 -0
  402. package/src/lib/stores/__tests__/helpers/mockCloudBlockStore.js.map +1 -0
  403. package/src/lib/stores/availabilityAwareBlockStore.js +34 -5
  404. package/src/lib/stores/availabilityAwareBlockStore.js.map +1 -1
  405. package/src/lib/stores/cloudBlockStoreBase.d.ts +121 -0
  406. package/src/lib/stores/cloudBlockStoreBase.d.ts.map +1 -0
  407. package/src/lib/stores/cloudBlockStoreBase.js +1165 -0
  408. package/src/lib/stores/cloudBlockStoreBase.js.map +1 -0
  409. package/src/lib/stores/diskBlockAsyncStore.js +9 -5
  410. package/src/lib/stores/diskBlockAsyncStore.js.map +1 -1
  411. package/src/lib/stores/diskBlockMetadataStore.js +2 -0
  412. package/src/lib/stores/diskBlockMetadataStore.js.map +1 -1
  413. package/src/lib/stores/diskBlockStore.js +10 -8
  414. package/src/lib/stores/diskBlockStore.js.map +1 -1
  415. package/src/lib/stores/diskCBLStore.d.ts.map +1 -1
  416. package/src/lib/stores/diskCBLStore.js +8 -0
  417. package/src/lib/stores/diskCBLStore.js.map +1 -1
  418. package/src/lib/stores/index.d.ts +1 -0
  419. package/src/lib/stores/index.d.ts.map +1 -1
  420. package/src/lib/stores/index.js +1 -0
  421. package/src/lib/stores/index.js.map +1 -1
  422. package/src/lib/systemKeyring.d.ts.map +1 -1
  423. package/src/lib/systemKeyring.js +5 -4
  424. package/src/lib/systemKeyring.js.map +1 -1
  425. package/src/lib/transforms/checksumTransform.js +1 -0
  426. package/src/lib/transforms/checksumTransform.js.map +1 -1
  427. package/src/lib/transforms/memoryWritableStream.js +1 -0
  428. package/src/lib/transforms/memoryWritableStream.js.map +1 -1
  429. package/src/lib/transforms/xorMultipleTransform.js +3 -0
  430. package/src/lib/transforms/xorMultipleTransform.js.map +1 -1
  431. package/src/lib/utils/rehydration.d.ts +1 -1
  432. package/src/lib/utils/rehydration.js +1 -1
  433. package/src/lib/utils/serialization.d.ts +1 -1
  434. package/src/lib/utils/serialization.js +1 -1
  435. package/src/lib/services/backupCodeService.d.ts +0 -35
  436. package/src/lib/services/backupCodeService.d.ts.map +0 -1
  437. package/src/lib/services/backupCodeService.js +0 -109
  438. package/src/lib/services/backupCodeService.js.map +0 -1
@@ -0,0 +1,947 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MessagingService = void 0;
4
+ exports.createMessagingService = createMessagingService;
5
+ const brighthub_lib_1 = require("@brightchain/brighthub-lib");
6
+ const crypto_1 = require("crypto");
7
+ const textFormatter_1 = require("./textFormatter");
8
+ /** Default pagination limit */
9
+ const DEFAULT_PAGE_LIMIT = 20;
10
+ /** Maximum pagination limit */
11
+ const MAX_PAGE_LIMIT = 100;
12
+ // ═══════════════════════════════════════════════════════
13
+ // MessagingService Implementation
14
+ // ═══════════════════════════════════════════════════════
15
+ /**
16
+ * Messaging_Service implementation
17
+ * Handles conversations, messages, reactions, read receipts, and message requests
18
+ * @see Requirements: 39-43
19
+ */
20
+ class MessagingService {
21
+ conversationsCollection;
22
+ messagesCollection;
23
+ messageRequestsCollection;
24
+ messageReactionsCollection;
25
+ readReceiptsCollection;
26
+ pinnedConversationsCollection;
27
+ archivedConversationsCollection;
28
+ mutedConversationsCollection;
29
+ participantsCollection;
30
+ followsCollection;
31
+ blocksCollection;
32
+ deletedConversationsCollection;
33
+ textFormatter = (0, textFormatter_1.getTextFormatter)();
34
+ constructor(application) {
35
+ this.conversationsCollection = application.getModel('brighthub_conversations');
36
+ this.messagesCollection =
37
+ application.getModel('brighthub_messages');
38
+ this.messageRequestsCollection = application.getModel('brighthub_message_requests');
39
+ this.messageReactionsCollection =
40
+ application.getModel('brighthub_message_reactions');
41
+ this.readReceiptsCollection = application.getModel('brighthub_read_receipts');
42
+ this.pinnedConversationsCollection =
43
+ application.getModel('brighthub_pinned_conversations');
44
+ this.archivedConversationsCollection =
45
+ application.getModel('brighthub_archived_conversations');
46
+ this.mutedConversationsCollection =
47
+ application.getModel('brighthub_muted_conversations');
48
+ this.participantsCollection =
49
+ application.getModel('brighthub_conversation_participants');
50
+ this.followsCollection =
51
+ application.getModel('brighthub_follows');
52
+ this.blocksCollection =
53
+ application.getModel('brighthub_blocks');
54
+ this.deletedConversationsCollection =
55
+ application.getModel('brighthub_deleted_conversations');
56
+ }
57
+ // ═══════════════════════════════════════════════════════
58
+ // Private helpers
59
+ // ═══════════════════════════════════════════════════════
60
+ clampLimit(limit) {
61
+ const l = limit ?? DEFAULT_PAGE_LIMIT;
62
+ return Math.min(Math.max(1, l), MAX_PAGE_LIMIT);
63
+ }
64
+ validateMessageContent(content) {
65
+ if (!content || content.trim().length === 0) {
66
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.EmptyContent, 'Message content cannot be empty');
67
+ }
68
+ if (this.textFormatter.getCharacterCount(content) > brighthub_lib_1.MAX_MESSAGE_LENGTH) {
69
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.ContentTooLong, `Message content exceeds maximum of ${brighthub_lib_1.MAX_MESSAGE_LENGTH} characters`);
70
+ }
71
+ }
72
+ validateAttachments(attachments) {
73
+ if (!attachments || attachments.length === 0)
74
+ return;
75
+ if (attachments.length > brighthub_lib_1.MAX_MESSAGE_ATTACHMENTS) {
76
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.TooManyAttachments, `Maximum ${brighthub_lib_1.MAX_MESSAGE_ATTACHMENTS} attachments allowed per message`);
77
+ }
78
+ const totalSize = attachments.reduce((sum, a) => sum + (a.size ?? 0), 0);
79
+ if (totalSize > brighthub_lib_1.MAX_MESSAGE_ATTACHMENT_SIZE) {
80
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.AttachmentSizeTooLarge, `Total attachment size exceeds ${brighthub_lib_1.MAX_MESSAGE_ATTACHMENT_SIZE / (1024 * 1024)}MB limit`);
81
+ }
82
+ for (const att of attachments) {
83
+ if (!brighthub_lib_1.ALLOWED_MESSAGE_MEDIA_TYPES.includes(att.mimeType)) {
84
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.InvalidMediaFormat, `Invalid media format: ${att.mimeType}. Allowed: ${brighthub_lib_1.ALLOWED_MESSAGE_MEDIA_TYPES.join(', ')}`);
85
+ }
86
+ }
87
+ }
88
+ recordToConversation(record) {
89
+ return {
90
+ _id: record._id,
91
+ type: record.type,
92
+ participantIds: record.participantIds,
93
+ name: record.name,
94
+ avatarUrl: record.avatarUrl,
95
+ lastMessageAt: record.lastMessageAt,
96
+ lastMessagePreview: record.lastMessagePreview,
97
+ createdAt: record.createdAt,
98
+ updatedAt: record.updatedAt,
99
+ };
100
+ }
101
+ recordToGroupConversation(record) {
102
+ return {
103
+ ...this.recordToConversation(record),
104
+ type: brighthub_lib_1.ConversationType.Group,
105
+ adminIds: record.adminIds ?? [],
106
+ creatorId: record.creatorId ?? '',
107
+ };
108
+ }
109
+ recordToMessage(record) {
110
+ return {
111
+ _id: record._id,
112
+ conversationId: record.conversationId,
113
+ senderId: record.senderId,
114
+ content: record.content,
115
+ formattedContent: record.formattedContent,
116
+ attachments: record.attachments,
117
+ replyToMessageId: record.replyToMessageId,
118
+ forwardedFromId: record.forwardedFromId,
119
+ isEdited: record.isEdited,
120
+ editedAt: record.editedAt,
121
+ isDeleted: record.isDeleted,
122
+ createdAt: record.createdAt,
123
+ };
124
+ }
125
+ async assertParticipant(conversationId, userId) {
126
+ const conv = await this.conversationsCollection
127
+ .findOne({ _id: conversationId })
128
+ .exec();
129
+ if (!conv) {
130
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.ConversationNotFound, 'Conversation not found');
131
+ }
132
+ // Check if user deleted this conversation from their view
133
+ const deleted = await this.deletedConversationsCollection
134
+ .findOne({
135
+ userId,
136
+ conversationId,
137
+ })
138
+ .exec();
139
+ if (deleted) {
140
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.ConversationNotFound, 'Conversation not found');
141
+ }
142
+ if (!conv.participantIds.includes(userId)) {
143
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.NotParticipant, 'User is not a participant in this conversation');
144
+ }
145
+ return conv;
146
+ }
147
+ async assertGroupAdmin(conv, userId) {
148
+ if (conv.type !== brighthub_lib_1.ConversationType.Group) {
149
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.NotAdmin, 'Not a group conversation');
150
+ }
151
+ if (!conv.adminIds?.includes(userId)) {
152
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.NotAdmin, 'User is not an admin of this group');
153
+ }
154
+ }
155
+ async isBlocked(userId, otherUserId) {
156
+ const block1 = await this.blocksCollection
157
+ .findOne({
158
+ blockerId: userId,
159
+ blockedId: otherUserId,
160
+ })
161
+ .exec();
162
+ if (block1)
163
+ return true;
164
+ const block2 = await this.blocksCollection
165
+ .findOne({
166
+ blockerId: otherUserId,
167
+ blockedId: userId,
168
+ })
169
+ .exec();
170
+ return !!block2;
171
+ }
172
+ // ═══════════════════════════════════════════════════════
173
+ // Conversation Management (Req 39.1, 39.2, 43.1-43.11)
174
+ // ═══════════════════════════════════════════════════════
175
+ async createDirectConversation(userId, otherUserId) {
176
+ if (await this.isBlocked(userId, otherUserId)) {
177
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.UserBlocked, 'Cannot create conversation with blocked user');
178
+ }
179
+ // Check for existing direct conversation between these two users
180
+ const allConvs = await this.conversationsCollection
181
+ .find({ type: brighthub_lib_1.ConversationType.Direct })
182
+ .exec();
183
+ const existing = allConvs.find((c) => c.participantIds.length === 2 &&
184
+ c.participantIds.includes(userId) &&
185
+ c.participantIds.includes(otherUserId));
186
+ if (existing) {
187
+ // If user previously deleted it, remove the deletion record
188
+ if (this.deletedConversationsCollection.deleteOne) {
189
+ await this.deletedConversationsCollection
190
+ .deleteOne({
191
+ userId,
192
+ conversationId: existing._id,
193
+ })
194
+ .exec();
195
+ }
196
+ return this.recordToConversation(existing);
197
+ }
198
+ const now = new Date().toISOString();
199
+ const record = {
200
+ _id: (0, crypto_1.randomUUID)(),
201
+ type: brighthub_lib_1.ConversationType.Direct,
202
+ participantIds: [userId, otherUserId],
203
+ createdAt: now,
204
+ updatedAt: now,
205
+ };
206
+ await this.conversationsCollection.create(record);
207
+ return this.recordToConversation(record);
208
+ }
209
+ async createGroupConversation(creatorId, participantIds, options) {
210
+ if (!options.name || options.name.trim().length === 0) {
211
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.InvalidGroupName, 'Group name cannot be empty');
212
+ }
213
+ const allParticipants = [...new Set([creatorId, ...participantIds])];
214
+ if (allParticipants.length > brighthub_lib_1.MAX_GROUP_PARTICIPANTS) {
215
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.GroupParticipantLimitExceeded, `Group cannot exceed ${brighthub_lib_1.MAX_GROUP_PARTICIPANTS} participants`);
216
+ }
217
+ const now = new Date().toISOString();
218
+ const record = {
219
+ _id: (0, crypto_1.randomUUID)(),
220
+ type: brighthub_lib_1.ConversationType.Group,
221
+ participantIds: allParticipants,
222
+ name: options.name.trim(),
223
+ avatarUrl: options.avatarUrl,
224
+ adminIds: [creatorId],
225
+ creatorId,
226
+ createdAt: now,
227
+ updatedAt: now,
228
+ };
229
+ await this.conversationsCollection.create(record);
230
+ // Create participant records
231
+ for (const pid of allParticipants) {
232
+ await this.participantsCollection.create({
233
+ _id: (0, crypto_1.randomUUID)(),
234
+ conversationId: record._id,
235
+ userId: pid,
236
+ role: pid === creatorId
237
+ ? brighthub_lib_1.GroupParticipantRole.Admin
238
+ : brighthub_lib_1.GroupParticipantRole.Participant,
239
+ notificationsEnabled: true,
240
+ joinedAt: now,
241
+ });
242
+ }
243
+ return this.recordToGroupConversation(record);
244
+ }
245
+ async getConversation(conversationId, userId) {
246
+ const conv = await this.assertParticipant(conversationId, userId);
247
+ if (conv.type === brighthub_lib_1.ConversationType.Group) {
248
+ return this.recordToGroupConversation(conv);
249
+ }
250
+ return this.recordToConversation(conv);
251
+ }
252
+ async getConversations(userId, options) {
253
+ const limit = this.clampLimit(options?.limit);
254
+ // Get all conversations where user is a participant
255
+ const allConvs = await this.conversationsCollection
256
+ .find({})
257
+ .exec();
258
+ // Get deleted conversation IDs for this user
259
+ const deletedRecords = await this.deletedConversationsCollection
260
+ .find({ userId })
261
+ .exec();
262
+ const deletedIds = new Set(deletedRecords.map((d) => d.conversationId));
263
+ let filtered = allConvs.filter((c) => c.participantIds.includes(userId) && !deletedIds.has(c._id));
264
+ // Apply cursor (ISO timestamp)
265
+ if (options?.cursor) {
266
+ filtered = filtered.filter((c) => (c.lastMessageAt ?? c.createdAt) < options.cursor);
267
+ }
268
+ // Sort by most recent activity
269
+ filtered.sort((a, b) => {
270
+ const aTime = a.lastMessageAt ?? a.createdAt;
271
+ const bTime = b.lastMessageAt ?? b.createdAt;
272
+ return bTime.localeCompare(aTime);
273
+ });
274
+ const items = filtered
275
+ .slice(0, limit)
276
+ .map((c) => c.type === brighthub_lib_1.ConversationType.Group
277
+ ? this.recordToGroupConversation(c)
278
+ : this.recordToConversation(c));
279
+ const hasMore = filtered.length > limit;
280
+ const lastItem = items[items.length - 1];
281
+ const cursor = hasMore && lastItem
282
+ ? (lastItem.lastMessageAt ?? lastItem.createdAt)
283
+ : undefined;
284
+ return { items, cursor, hasMore };
285
+ }
286
+ async deleteConversation(conversationId, userId) {
287
+ await this.assertParticipant(conversationId, userId);
288
+ // Soft-delete: record that this user deleted the conversation
289
+ const existing = await this.deletedConversationsCollection
290
+ .findOne({ userId, conversationId })
291
+ .exec();
292
+ if (!existing) {
293
+ await this.deletedConversationsCollection.create({
294
+ _id: (0, crypto_1.randomUUID)(),
295
+ userId,
296
+ conversationId,
297
+ deletedAt: new Date().toISOString(),
298
+ });
299
+ }
300
+ }
301
+ // ═══════════════════════════════════════════════════════
302
+ // Message CRUD (Req 39.1-39.4, 41.1-41.9)
303
+ // ═══════════════════════════════════════════════════════
304
+ async sendMessage(conversationId, senderId, content, options) {
305
+ this.validateMessageContent(content);
306
+ this.validateAttachments(options?.attachments);
307
+ await this.assertParticipant(conversationId, senderId);
308
+ const formatted = this.textFormatter.format(content);
309
+ const now = new Date().toISOString();
310
+ // Validate replyToMessageId if provided
311
+ if (options?.replyToMessageId) {
312
+ const replyTarget = await this.messagesCollection
313
+ .findOne({ _id: options.replyToMessageId })
314
+ .exec();
315
+ if (!replyTarget || replyTarget.conversationId !== conversationId) {
316
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.MessageNotFound, 'Reply target message not found in this conversation');
317
+ }
318
+ }
319
+ // Validate forwardedFromId if provided
320
+ if (options?.forwardedFromId) {
321
+ const forwardSource = await this.messagesCollection
322
+ .findOne({ _id: options.forwardedFromId })
323
+ .exec();
324
+ if (!forwardSource) {
325
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.MessageNotFound, 'Forwarded message not found');
326
+ }
327
+ }
328
+ const record = {
329
+ _id: (0, crypto_1.randomUUID)(),
330
+ conversationId,
331
+ senderId,
332
+ content,
333
+ formattedContent: formatted.formatted,
334
+ attachments: (options?.attachments ?? []),
335
+ replyToMessageId: options?.replyToMessageId,
336
+ forwardedFromId: options?.forwardedFromId,
337
+ isEdited: false,
338
+ isDeleted: false,
339
+ createdAt: now,
340
+ };
341
+ await this.messagesCollection.create(record);
342
+ // Update conversation's last message info
343
+ const preview = content.length > 100 ? content.substring(0, 97) + '...' : content;
344
+ await this.conversationsCollection
345
+ .updateOne({ _id: conversationId }, {
346
+ lastMessageAt: now,
347
+ lastMessagePreview: preview,
348
+ updatedAt: now,
349
+ })
350
+ .exec();
351
+ return this.recordToMessage(record);
352
+ }
353
+ async editMessage(messageId, userId, newContent) {
354
+ this.validateMessageContent(newContent);
355
+ const message = await this.messagesCollection
356
+ .findOne({ _id: messageId })
357
+ .exec();
358
+ if (!message) {
359
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.MessageNotFound, 'Message not found');
360
+ }
361
+ if (message.senderId !== userId) {
362
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.Unauthorized, 'Only the sender can edit a message');
363
+ }
364
+ if (message.isDeleted) {
365
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.MessageAlreadyDeleted, 'Cannot edit a deleted message');
366
+ }
367
+ const createdAt = new Date(message.createdAt).getTime();
368
+ if (Date.now() - createdAt > brighthub_lib_1.MESSAGE_EDIT_WINDOW_MS) {
369
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.EditWindowExpired, 'Edit window has expired (15 minutes)');
370
+ }
371
+ const formatted = this.textFormatter.format(newContent);
372
+ const now = new Date().toISOString();
373
+ await this.messagesCollection
374
+ .updateOne({ _id: messageId }, {
375
+ content: newContent,
376
+ formattedContent: formatted.formatted,
377
+ isEdited: true,
378
+ editedAt: now,
379
+ })
380
+ .exec();
381
+ return this.recordToMessage({
382
+ ...message,
383
+ content: newContent,
384
+ formattedContent: formatted.formatted,
385
+ isEdited: true,
386
+ editedAt: now,
387
+ });
388
+ }
389
+ async deleteMessage(messageId, userId) {
390
+ const message = await this.messagesCollection
391
+ .findOne({ _id: messageId })
392
+ .exec();
393
+ if (!message) {
394
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.MessageNotFound, 'Message not found');
395
+ }
396
+ if (message.senderId !== userId) {
397
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.Unauthorized, 'Only the sender can delete a message');
398
+ }
399
+ if (message.isDeleted) {
400
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.MessageAlreadyDeleted, 'Message is already deleted');
401
+ }
402
+ // Soft-delete: replace content with placeholder
403
+ await this.messagesCollection
404
+ .updateOne({ _id: messageId }, {
405
+ content: '',
406
+ formattedContent: '<p><em>This message was deleted</em></p>',
407
+ isDeleted: true,
408
+ attachments: [],
409
+ })
410
+ .exec();
411
+ }
412
+ async getMessages(conversationId, userId, options) {
413
+ await this.assertParticipant(conversationId, userId);
414
+ const limit = this.clampLimit(options?.limit);
415
+ let messages = await this.messagesCollection
416
+ .find({ conversationId })
417
+ .exec();
418
+ // Apply cursor
419
+ if (options?.cursor) {
420
+ messages = messages.filter((m) => m.createdAt < options.cursor);
421
+ }
422
+ // Sort by newest first
423
+ messages.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
424
+ const page = messages.slice(0, limit);
425
+ const hasMore = messages.length > limit;
426
+ const lastItem = page[page.length - 1];
427
+ return {
428
+ items: page.map((m) => this.recordToMessage(m)),
429
+ cursor: hasMore && lastItem ? lastItem.createdAt : undefined,
430
+ hasMore,
431
+ };
432
+ }
433
+ // ═══════════════════════════════════════════════════════
434
+ // Reactions (Req 39.8-39.10)
435
+ // ═══════════════════════════════════════════════════════
436
+ async addReaction(messageId, userId, emoji) {
437
+ const message = await this.messagesCollection
438
+ .findOne({ _id: messageId })
439
+ .exec();
440
+ if (!message || message.isDeleted) {
441
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.MessageNotFound, 'Message not found');
442
+ }
443
+ // Check user is participant
444
+ await this.assertParticipant(message.conversationId, userId);
445
+ // Check for duplicate reaction (same user, same emoji)
446
+ const existing = await this.messageReactionsCollection
447
+ .find({ messageId })
448
+ .exec();
449
+ const duplicate = existing.find((r) => r.userId === userId && r.emoji === emoji);
450
+ if (duplicate) {
451
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.ReactionAlreadyExists, 'You already reacted with this emoji');
452
+ }
453
+ // Check unique emoji limit per message
454
+ const uniqueEmojis = new Set(existing.map((r) => r.emoji));
455
+ if (!uniqueEmojis.has(emoji) &&
456
+ uniqueEmojis.size >= brighthub_lib_1.MAX_REACTIONS_PER_MESSAGE) {
457
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.ReactionLimitExceeded, `Maximum ${brighthub_lib_1.MAX_REACTIONS_PER_MESSAGE} unique emoji reactions per message`);
458
+ }
459
+ const record = {
460
+ _id: (0, crypto_1.randomUUID)(),
461
+ messageId,
462
+ userId,
463
+ emoji,
464
+ createdAt: new Date().toISOString(),
465
+ };
466
+ await this.messageReactionsCollection.create(record);
467
+ return {
468
+ _id: record._id,
469
+ messageId: record.messageId,
470
+ userId: record.userId,
471
+ emoji: record.emoji,
472
+ createdAt: record.createdAt,
473
+ };
474
+ }
475
+ async removeReaction(messageId, userId, emoji) {
476
+ const reactions = await this.messageReactionsCollection
477
+ .find({ messageId })
478
+ .exec();
479
+ const reaction = reactions.find((r) => r.userId === userId && r.emoji === emoji);
480
+ if (!reaction) {
481
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.ReactionNotFound, 'Reaction not found');
482
+ }
483
+ await this.messageReactionsCollection
484
+ .deleteOne({ _id: reaction._id })
485
+ .exec();
486
+ }
487
+ // ═══════════════════════════════════════════════════════
488
+ // Read Receipts & Typing (Req 39.5-39.7)
489
+ // ═══════════════════════════════════════════════════════
490
+ async markAsRead(conversationId, userId, messageId) {
491
+ await this.assertParticipant(conversationId, userId);
492
+ const now = new Date().toISOString();
493
+ // Upsert read receipt
494
+ const existing = await this.readReceiptsCollection
495
+ .find({ conversationId })
496
+ .exec();
497
+ const userReceipt = existing.find((r) => r.userId === userId);
498
+ if (userReceipt) {
499
+ await this.readReceiptsCollection
500
+ .updateOne({ _id: userReceipt._id }, {
501
+ lastReadAt: now,
502
+ lastReadMessageId: messageId,
503
+ })
504
+ .exec();
505
+ return {
506
+ conversationId,
507
+ userId,
508
+ lastReadAt: now,
509
+ lastReadMessageId: messageId,
510
+ };
511
+ }
512
+ const record = {
513
+ _id: (0, crypto_1.randomUUID)(),
514
+ conversationId,
515
+ userId,
516
+ lastReadAt: now,
517
+ lastReadMessageId: messageId,
518
+ };
519
+ await this.readReceiptsCollection.create(record);
520
+ return {
521
+ conversationId: record.conversationId,
522
+ userId: record.userId,
523
+ lastReadAt: record.lastReadAt,
524
+ lastReadMessageId: record.lastReadMessageId,
525
+ };
526
+ }
527
+ async sendTypingIndicator(conversationId, userId) {
528
+ await this.assertParticipant(conversationId, userId);
529
+ const expiresAt = new Date(Date.now() + brighthub_lib_1.TYPING_INDICATOR_TIMEOUT_MS).toISOString();
530
+ return { expiresAt };
531
+ }
532
+ // ═══════════════════════════════════════════════════════
533
+ // Message Requests & Privacy (Req 42.1-42.12)
534
+ // ═══════════════════════════════════════════════════════
535
+ async createMessageRequest(senderId, recipientId, messagePreview) {
536
+ if (await this.isBlocked(senderId, recipientId)) {
537
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.UserBlocked, 'Cannot send message request to this user');
538
+ }
539
+ // Check for existing pending request
540
+ const allRequests = await this.messageRequestsCollection
541
+ .find({ senderId })
542
+ .exec();
543
+ const existing = allRequests.find((r) => r.recipientId === recipientId &&
544
+ r.status === brighthub_lib_1.MessageRequestStatus.Pending);
545
+ if (existing) {
546
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.MessageRequestAlreadyExists, 'A pending message request already exists for this user');
547
+ }
548
+ const preview = messagePreview.length > 100
549
+ ? messagePreview.substring(0, 97) + '...'
550
+ : messagePreview;
551
+ const record = {
552
+ _id: (0, crypto_1.randomUUID)(),
553
+ senderId,
554
+ recipientId,
555
+ messagePreview: preview,
556
+ status: brighthub_lib_1.MessageRequestStatus.Pending,
557
+ createdAt: new Date().toISOString(),
558
+ };
559
+ await this.messageRequestsCollection.create(record);
560
+ return {
561
+ _id: record._id,
562
+ senderId: record.senderId,
563
+ recipientId: record.recipientId,
564
+ messagePreview: record.messagePreview,
565
+ status: record.status,
566
+ createdAt: record.createdAt,
567
+ };
568
+ }
569
+ async acceptMessageRequest(requestId, recipientId) {
570
+ const request = await this.messageRequestsCollection
571
+ .findOne({ _id: requestId })
572
+ .exec();
573
+ if (!request || request.recipientId !== recipientId) {
574
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.MessageRequestNotFound, 'Message request not found');
575
+ }
576
+ // Update request status
577
+ await this.messageRequestsCollection
578
+ .updateOne({ _id: requestId }, {
579
+ status: brighthub_lib_1.MessageRequestStatus.Accepted,
580
+ })
581
+ .exec();
582
+ // Create a direct conversation
583
+ return this.createDirectConversation(recipientId, request.senderId);
584
+ }
585
+ async declineMessageRequest(requestId, recipientId) {
586
+ const request = await this.messageRequestsCollection
587
+ .findOne({ _id: requestId })
588
+ .exec();
589
+ if (!request || request.recipientId !== recipientId) {
590
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.MessageRequestNotFound, 'Message request not found');
591
+ }
592
+ await this.messageRequestsCollection
593
+ .updateOne({ _id: requestId }, {
594
+ status: brighthub_lib_1.MessageRequestStatus.Declined,
595
+ })
596
+ .exec();
597
+ }
598
+ async canMessageDirectly(senderId, recipientId) {
599
+ if (await this.isBlocked(senderId, recipientId)) {
600
+ return false;
601
+ }
602
+ // Check mutual follow
603
+ const senderFollows = await this.followsCollection
604
+ .findOne({
605
+ followerId: senderId,
606
+ followedId: recipientId,
607
+ })
608
+ .exec();
609
+ const recipientFollows = await this.followsCollection
610
+ .findOne({
611
+ followerId: recipientId,
612
+ followedId: senderId,
613
+ })
614
+ .exec();
615
+ return !!(senderFollows && recipientFollows);
616
+ }
617
+ // ═══════════════════════════════════════════════════════
618
+ // Conversation Management Features (Req 43.3-43.11)
619
+ // ═══════════════════════════════════════════════════════
620
+ async pinConversation(conversationId, userId) {
621
+ await this.assertParticipant(conversationId, userId);
622
+ const existing = await this.pinnedConversationsCollection
623
+ .findOne({ userId, conversationId })
624
+ .exec();
625
+ if (existing) {
626
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.AlreadyPinned, 'Conversation is already pinned');
627
+ }
628
+ // Check pin limit
629
+ const pinned = await this.pinnedConversationsCollection
630
+ .find({ userId })
631
+ .exec();
632
+ if (pinned.length >= brighthub_lib_1.MAX_PINNED_CONVERSATIONS) {
633
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.PinLimitExceeded, `Maximum ${brighthub_lib_1.MAX_PINNED_CONVERSATIONS} pinned conversations allowed`);
634
+ }
635
+ await this.pinnedConversationsCollection.create({
636
+ _id: (0, crypto_1.randomUUID)(),
637
+ userId,
638
+ conversationId,
639
+ pinnedAt: new Date().toISOString(),
640
+ });
641
+ }
642
+ async unpinConversation(conversationId, userId) {
643
+ const existing = await this.pinnedConversationsCollection
644
+ .findOne({ userId, conversationId })
645
+ .exec();
646
+ if (!existing) {
647
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.NotPinned, 'Conversation is not pinned');
648
+ }
649
+ await this.pinnedConversationsCollection
650
+ .deleteOne({ _id: existing._id })
651
+ .exec();
652
+ }
653
+ async archiveConversation(conversationId, userId) {
654
+ await this.assertParticipant(conversationId, userId);
655
+ const existing = await this.archivedConversationsCollection
656
+ .findOne({
657
+ userId,
658
+ conversationId,
659
+ })
660
+ .exec();
661
+ if (existing) {
662
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.AlreadyArchived, 'Conversation is already archived');
663
+ }
664
+ await this.archivedConversationsCollection.create({
665
+ _id: (0, crypto_1.randomUUID)(),
666
+ userId,
667
+ conversationId,
668
+ archivedAt: new Date().toISOString(),
669
+ });
670
+ }
671
+ async unarchiveConversation(conversationId, userId) {
672
+ const existing = await this.archivedConversationsCollection
673
+ .findOne({
674
+ userId,
675
+ conversationId,
676
+ })
677
+ .exec();
678
+ if (!existing) {
679
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.NotArchived, 'Conversation is not archived');
680
+ }
681
+ await this.archivedConversationsCollection
682
+ .deleteOne({ _id: existing._id })
683
+ .exec();
684
+ }
685
+ async muteConversation(conversationId, userId, expiresAt) {
686
+ await this.assertParticipant(conversationId, userId);
687
+ const existing = await this.mutedConversationsCollection
688
+ .findOne({ userId, conversationId })
689
+ .exec();
690
+ if (existing) {
691
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.AlreadyMuted, 'Conversation is already muted');
692
+ }
693
+ await this.mutedConversationsCollection.create({
694
+ _id: (0, crypto_1.randomUUID)(),
695
+ userId,
696
+ conversationId,
697
+ expiresAt,
698
+ mutedAt: new Date().toISOString(),
699
+ });
700
+ }
701
+ async unmuteConversation(conversationId, userId) {
702
+ const existing = await this.mutedConversationsCollection
703
+ .findOne({ userId, conversationId })
704
+ .exec();
705
+ if (!existing) {
706
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.NotMuted, 'Conversation is not muted');
707
+ }
708
+ await this.mutedConversationsCollection
709
+ .deleteOne({ _id: existing._id })
710
+ .exec();
711
+ }
712
+ async searchMessages(userId, query, options) {
713
+ const limit = this.clampLimit(options?.limit);
714
+ const lowerQuery = query.toLowerCase();
715
+ // Get user's conversations
716
+ const allConvs = await this.conversationsCollection
717
+ .find({})
718
+ .exec();
719
+ const userConvIds = allConvs
720
+ .filter((c) => c.participantIds.includes(userId))
721
+ .map((c) => c._id);
722
+ // Search messages in those conversations
723
+ const allMessages = await this.messagesCollection
724
+ .find({})
725
+ .exec();
726
+ let matched = allMessages.filter((m) => userConvIds.includes(m.conversationId) &&
727
+ !m.isDeleted &&
728
+ m.content.toLowerCase().includes(lowerQuery));
729
+ if (options?.cursor) {
730
+ matched = matched.filter((m) => m.createdAt < options.cursor);
731
+ }
732
+ matched.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
733
+ const page = matched.slice(0, limit);
734
+ const hasMore = matched.length > limit;
735
+ const lastItem = page[page.length - 1];
736
+ return {
737
+ items: page.map((m) => this.recordToMessage(m)),
738
+ cursor: hasMore && lastItem ? lastItem.createdAt : undefined,
739
+ hasMore,
740
+ };
741
+ }
742
+ async searchInConversation(conversationId, userId, query, options) {
743
+ await this.assertParticipant(conversationId, userId);
744
+ const limit = this.clampLimit(options?.limit);
745
+ const lowerQuery = query.toLowerCase();
746
+ let messages = await this.messagesCollection
747
+ .find({ conversationId })
748
+ .exec();
749
+ messages = messages.filter((m) => !m.isDeleted && m.content.toLowerCase().includes(lowerQuery));
750
+ if (options?.cursor) {
751
+ messages = messages.filter((m) => m.createdAt < options.cursor);
752
+ }
753
+ messages.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
754
+ const page = messages.slice(0, limit);
755
+ const hasMore = messages.length > limit;
756
+ const lastItem = page[page.length - 1];
757
+ return {
758
+ items: page.map((m) => this.recordToMessage(m)),
759
+ cursor: hasMore && lastItem ? lastItem.createdAt : undefined,
760
+ hasMore,
761
+ };
762
+ }
763
+ // ═══════════════════════════════════════════════════════
764
+ // Group Conversation Management (Req 40.1-40.12)
765
+ // ═══════════════════════════════════════════════════════
766
+ async addParticipants(conversationId, adminId, userIds) {
767
+ const conv = await this.assertParticipant(conversationId, adminId);
768
+ await this.assertGroupAdmin(conv, adminId);
769
+ const newParticipants = userIds.filter((id) => !conv.participantIds.includes(id));
770
+ if (newParticipants.length === 0)
771
+ return;
772
+ const totalAfter = conv.participantIds.length + newParticipants.length;
773
+ if (totalAfter > brighthub_lib_1.MAX_GROUP_PARTICIPANTS) {
774
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.GroupParticipantLimitExceeded, `Group cannot exceed ${brighthub_lib_1.MAX_GROUP_PARTICIPANTS} participants`);
775
+ }
776
+ const now = new Date().toISOString();
777
+ const updatedParticipants = [...conv.participantIds, ...newParticipants];
778
+ await this.conversationsCollection
779
+ .updateOne({ _id: conversationId }, {
780
+ participantIds: updatedParticipants,
781
+ updatedAt: now,
782
+ })
783
+ .exec();
784
+ for (const uid of newParticipants) {
785
+ await this.participantsCollection.create({
786
+ _id: (0, crypto_1.randomUUID)(),
787
+ conversationId,
788
+ userId: uid,
789
+ role: brighthub_lib_1.GroupParticipantRole.Participant,
790
+ notificationsEnabled: true,
791
+ joinedAt: now,
792
+ });
793
+ }
794
+ }
795
+ async removeParticipant(conversationId, adminId, userId) {
796
+ const conv = await this.assertParticipant(conversationId, adminId);
797
+ await this.assertGroupAdmin(conv, adminId);
798
+ if (!conv.participantIds.includes(userId)) {
799
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.NotParticipant, 'User is not a participant in this group');
800
+ }
801
+ const now = new Date().toISOString();
802
+ const updatedParticipants = conv.participantIds.filter((id) => id !== userId);
803
+ const updatedAdmins = (conv.adminIds ?? []).filter((id) => id !== userId);
804
+ await this.conversationsCollection
805
+ .updateOne({ _id: conversationId }, {
806
+ participantIds: updatedParticipants,
807
+ adminIds: updatedAdmins,
808
+ updatedAt: now,
809
+ })
810
+ .exec();
811
+ // Remove participant record
812
+ const participants = await this.participantsCollection
813
+ .find({ conversationId })
814
+ .exec();
815
+ const participantRecord = participants.find((p) => p.userId === userId);
816
+ if (participantRecord) {
817
+ await this.participantsCollection
818
+ .deleteOne({ _id: participantRecord._id })
819
+ .exec();
820
+ }
821
+ }
822
+ async promoteToAdmin(conversationId, adminId, userId) {
823
+ const conv = await this.assertParticipant(conversationId, adminId);
824
+ await this.assertGroupAdmin(conv, adminId);
825
+ if (!conv.participantIds.includes(userId)) {
826
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.NotParticipant, 'User is not a participant in this group');
827
+ }
828
+ if (conv.adminIds?.includes(userId)) {
829
+ return; // Already admin, idempotent
830
+ }
831
+ const updatedAdmins = [...(conv.adminIds ?? []), userId];
832
+ await this.conversationsCollection
833
+ .updateOne({ _id: conversationId }, {
834
+ adminIds: updatedAdmins,
835
+ updatedAt: new Date().toISOString(),
836
+ })
837
+ .exec();
838
+ // Update participant role
839
+ const participants = await this.participantsCollection
840
+ .find({ conversationId })
841
+ .exec();
842
+ const participantRecord = participants.find((p) => p.userId === userId);
843
+ if (participantRecord) {
844
+ await this.participantsCollection
845
+ .updateOne({ _id: participantRecord._id }, {
846
+ role: brighthub_lib_1.GroupParticipantRole.Admin,
847
+ })
848
+ .exec();
849
+ }
850
+ }
851
+ async demoteFromAdmin(conversationId, adminId, userId) {
852
+ const conv = await this.assertParticipant(conversationId, adminId);
853
+ await this.assertGroupAdmin(conv, adminId);
854
+ if (!conv.adminIds?.includes(userId)) {
855
+ return; // Not admin, idempotent
856
+ }
857
+ // Prevent demoting the last admin
858
+ if (conv.adminIds.length <= 1) {
859
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.LastAdminCannotLeave, 'Cannot demote the last admin');
860
+ }
861
+ const updatedAdmins = conv.adminIds.filter((id) => id !== userId);
862
+ await this.conversationsCollection
863
+ .updateOne({ _id: conversationId }, {
864
+ adminIds: updatedAdmins,
865
+ updatedAt: new Date().toISOString(),
866
+ })
867
+ .exec();
868
+ // Update participant role
869
+ const participants = await this.participantsCollection
870
+ .find({ conversationId })
871
+ .exec();
872
+ const participantRecord = participants.find((p) => p.userId === userId);
873
+ if (participantRecord) {
874
+ await this.participantsCollection
875
+ .updateOne({ _id: participantRecord._id }, {
876
+ role: brighthub_lib_1.GroupParticipantRole.Participant,
877
+ })
878
+ .exec();
879
+ }
880
+ }
881
+ async leaveGroup(conversationId, userId) {
882
+ const conv = await this.assertParticipant(conversationId, userId);
883
+ if (conv.type !== brighthub_lib_1.ConversationType.Group) {
884
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.NotAdmin, 'Cannot leave a direct conversation');
885
+ }
886
+ // If user is the last admin, they can't leave
887
+ if (conv.adminIds?.includes(userId) &&
888
+ conv.adminIds.length === 1 &&
889
+ conv.participantIds.length > 1) {
890
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.LastAdminCannotLeave, 'Last admin must promote another admin before leaving');
891
+ }
892
+ const now = new Date().toISOString();
893
+ const updatedParticipants = conv.participantIds.filter((id) => id !== userId);
894
+ const updatedAdmins = (conv.adminIds ?? []).filter((id) => id !== userId);
895
+ await this.conversationsCollection
896
+ .updateOne({ _id: conversationId }, {
897
+ participantIds: updatedParticipants,
898
+ adminIds: updatedAdmins,
899
+ updatedAt: now,
900
+ })
901
+ .exec();
902
+ // Remove participant record
903
+ const participants = await this.participantsCollection
904
+ .find({ conversationId })
905
+ .exec();
906
+ const participantRecord = participants.find((p) => p.userId === userId);
907
+ if (participantRecord) {
908
+ await this.participantsCollection
909
+ .deleteOne({ _id: participantRecord._id })
910
+ .exec();
911
+ }
912
+ }
913
+ async updateGroupSettings(conversationId, adminId, updates) {
914
+ const conv = await this.assertParticipant(conversationId, adminId);
915
+ await this.assertGroupAdmin(conv, adminId);
916
+ if (updates.name !== undefined && updates.name.trim().length === 0) {
917
+ throw new brighthub_lib_1.MessagingServiceError(brighthub_lib_1.MessagingErrorCode.InvalidGroupName, 'Group name cannot be empty');
918
+ }
919
+ const now = new Date().toISOString();
920
+ const updateFields = { updatedAt: now };
921
+ if (updates.name !== undefined)
922
+ updateFields.name = updates.name.trim();
923
+ if (updates.avatarUrl !== undefined)
924
+ updateFields.avatarUrl = updates.avatarUrl;
925
+ await this.conversationsCollection
926
+ .updateOne({ _id: conversationId }, updateFields)
927
+ .exec();
928
+ const updated = {
929
+ ...conv,
930
+ ...updateFields,
931
+ };
932
+ return this.recordToGroupConversation(updated);
933
+ }
934
+ }
935
+ exports.MessagingService = MessagingService;
936
+ // ═══════════════════════════════════════════════════════
937
+ // Factory function
938
+ // ═══════════════════════════════════════════════════════
939
+ /**
940
+ * Create a new MessagingService instance
941
+ * @param application Application with database collections
942
+ * @returns MessagingService instance
943
+ */
944
+ function createMessagingService(application) {
945
+ return new MessagingService(application);
946
+ }
947
+ //# sourceMappingURL=messagingService.js.map