@rebasepro/server-mongodb 0.0.1-canary.09e5ec5

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 (286) hide show
  1. package/LICENSE +6 -0
  2. package/dist/ensure-collections-CNrcwVgY.js +74 -0
  3. package/dist/ensure-collections-CNrcwVgY.js.map +1 -0
  4. package/dist/ensure-history-collection-DBIiwmCm.js +15 -0
  5. package/dist/ensure-history-collection-DBIiwmCm.js.map +1 -0
  6. package/dist/index.es.js +1734 -0
  7. package/dist/index.es.js.map +1 -0
  8. package/dist/index.umd.js +2043 -0
  9. package/dist/index.umd.js.map +1 -0
  10. package/dist/server-core/src/api/ast-schema-editor.d.ts +22 -0
  11. package/dist/server-core/src/api/ast-schema-editor.d.ts.map +1 -0
  12. package/dist/server-core/src/api/errors.d.ts +36 -0
  13. package/dist/server-core/src/api/errors.d.ts.map +1 -0
  14. package/dist/server-core/src/api/graphql/graphql-schema-generator.d.ts +36 -0
  15. package/dist/server-core/src/api/graphql/graphql-schema-generator.d.ts.map +1 -0
  16. package/dist/server-core/src/api/graphql/index.d.ts +2 -0
  17. package/dist/server-core/src/api/graphql/index.d.ts.map +1 -0
  18. package/dist/server-core/src/api/index.d.ts +10 -0
  19. package/dist/server-core/src/api/index.d.ts.map +1 -0
  20. package/dist/server-core/src/api/openapi-generator.d.ts +17 -0
  21. package/dist/server-core/src/api/openapi-generator.d.ts.map +1 -0
  22. package/dist/server-core/src/api/rest/api-generator.d.ts +65 -0
  23. package/dist/server-core/src/api/rest/api-generator.d.ts.map +1 -0
  24. package/dist/server-core/src/api/rest/index.d.ts +2 -0
  25. package/dist/server-core/src/api/rest/index.d.ts.map +1 -0
  26. package/dist/server-core/src/api/rest/query-parser.d.ts +10 -0
  27. package/dist/server-core/src/api/rest/query-parser.d.ts.map +1 -0
  28. package/dist/server-core/src/api/schema-editor-routes.d.ts +4 -0
  29. package/dist/server-core/src/api/schema-editor-routes.d.ts.map +1 -0
  30. package/dist/server-core/src/api/server.d.ts +41 -0
  31. package/dist/server-core/src/api/server.d.ts.map +1 -0
  32. package/dist/server-core/src/api/types.d.ts +91 -0
  33. package/dist/server-core/src/api/types.d.ts.map +1 -0
  34. package/dist/server-core/src/auth/admin-routes.d.ts +17 -0
  35. package/dist/server-core/src/auth/admin-routes.d.ts.map +1 -0
  36. package/dist/server-core/src/auth/apple-oauth.d.ts +31 -0
  37. package/dist/server-core/src/auth/apple-oauth.d.ts.map +1 -0
  38. package/dist/server-core/src/auth/bitbucket-oauth.d.ts +12 -0
  39. package/dist/server-core/src/auth/bitbucket-oauth.d.ts.map +1 -0
  40. package/dist/server-core/src/auth/discord-oauth.d.ts +15 -0
  41. package/dist/server-core/src/auth/discord-oauth.d.ts.map +1 -0
  42. package/dist/server-core/src/auth/facebook-oauth.d.ts +15 -0
  43. package/dist/server-core/src/auth/facebook-oauth.d.ts.map +1 -0
  44. package/dist/server-core/src/auth/github-oauth.d.ts +16 -0
  45. package/dist/server-core/src/auth/github-oauth.d.ts.map +1 -0
  46. package/dist/server-core/src/auth/gitlab-oauth.d.ts +14 -0
  47. package/dist/server-core/src/auth/gitlab-oauth.d.ts.map +1 -0
  48. package/dist/server-core/src/auth/google-oauth.d.ts +15 -0
  49. package/dist/server-core/src/auth/google-oauth.d.ts.map +1 -0
  50. package/dist/server-core/src/auth/index.d.ts +24 -0
  51. package/dist/server-core/src/auth/index.d.ts.map +1 -0
  52. package/dist/server-core/src/auth/interfaces.d.ts +310 -0
  53. package/dist/server-core/src/auth/interfaces.d.ts.map +1 -0
  54. package/dist/server-core/src/auth/jwt.d.ts +44 -0
  55. package/dist/server-core/src/auth/jwt.d.ts.map +1 -0
  56. package/dist/server-core/src/auth/linkedin-oauth.d.ts +19 -0
  57. package/dist/server-core/src/auth/linkedin-oauth.d.ts.map +1 -0
  58. package/dist/server-core/src/auth/microsoft-oauth.d.ts +17 -0
  59. package/dist/server-core/src/auth/microsoft-oauth.d.ts.map +1 -0
  60. package/dist/server-core/src/auth/middleware.d.ts +82 -0
  61. package/dist/server-core/src/auth/middleware.d.ts.map +1 -0
  62. package/dist/server-core/src/auth/password.d.ts +23 -0
  63. package/dist/server-core/src/auth/password.d.ts.map +1 -0
  64. package/dist/server-core/src/auth/rate-limiter.d.ts +32 -0
  65. package/dist/server-core/src/auth/rate-limiter.d.ts.map +1 -0
  66. package/dist/server-core/src/auth/routes.d.ts +28 -0
  67. package/dist/server-core/src/auth/routes.d.ts.map +1 -0
  68. package/dist/server-core/src/auth/slack-oauth.d.ts +13 -0
  69. package/dist/server-core/src/auth/slack-oauth.d.ts.map +1 -0
  70. package/dist/server-core/src/auth/spotify-oauth.d.ts +13 -0
  71. package/dist/server-core/src/auth/spotify-oauth.d.ts.map +1 -0
  72. package/dist/server-core/src/auth/twitter-oauth.d.ts +19 -0
  73. package/dist/server-core/src/auth/twitter-oauth.d.ts.map +1 -0
  74. package/dist/server-core/src/collections/BackendCollectionRegistry.d.ts +14 -0
  75. package/dist/server-core/src/collections/BackendCollectionRegistry.d.ts.map +1 -0
  76. package/dist/server-core/src/collections/loader.d.ts +6 -0
  77. package/dist/server-core/src/collections/loader.d.ts.map +1 -0
  78. package/dist/server-core/src/cron/cron-loader.d.ts +18 -0
  79. package/dist/server-core/src/cron/cron-loader.d.ts.map +1 -0
  80. package/dist/server-core/src/cron/cron-routes.d.ts +15 -0
  81. package/dist/server-core/src/cron/cron-routes.d.ts.map +1 -0
  82. package/dist/server-core/src/cron/cron-scheduler.d.ts +62 -0
  83. package/dist/server-core/src/cron/cron-scheduler.d.ts.map +1 -0
  84. package/dist/server-core/src/cron/cron-store.d.ts +33 -0
  85. package/dist/server-core/src/cron/cron-store.d.ts.map +1 -0
  86. package/dist/server-core/src/cron/index.d.ts +7 -0
  87. package/dist/server-core/src/cron/index.d.ts.map +1 -0
  88. package/dist/server-core/src/db/interfaces.d.ts +19 -0
  89. package/dist/server-core/src/db/interfaces.d.ts.map +1 -0
  90. package/dist/server-core/src/email/index.d.ts +7 -0
  91. package/dist/server-core/src/email/index.d.ts.map +1 -0
  92. package/dist/server-core/src/email/smtp-email-service.d.ts +26 -0
  93. package/dist/server-core/src/email/smtp-email-service.d.ts.map +1 -0
  94. package/dist/server-core/src/email/templates.d.ts +43 -0
  95. package/dist/server-core/src/email/templates.d.ts.map +1 -0
  96. package/dist/server-core/src/email/types.d.ts +108 -0
  97. package/dist/server-core/src/email/types.d.ts.map +1 -0
  98. package/dist/server-core/src/functions/function-loader.d.ts +18 -0
  99. package/dist/server-core/src/functions/function-loader.d.ts.map +1 -0
  100. package/dist/server-core/src/functions/function-routes.d.ts +11 -0
  101. package/dist/server-core/src/functions/function-routes.d.ts.map +1 -0
  102. package/dist/server-core/src/functions/index.d.ts +4 -0
  103. package/dist/server-core/src/functions/index.d.ts.map +1 -0
  104. package/dist/server-core/src/history/history-routes.d.ts +24 -0
  105. package/dist/server-core/src/history/history-routes.d.ts.map +1 -0
  106. package/dist/server-core/src/history/index.d.ts +2 -0
  107. package/dist/server-core/src/history/index.d.ts.map +1 -0
  108. package/dist/server-core/src/index.d.ts +30 -0
  109. package/dist/server-core/src/index.d.ts.map +1 -0
  110. package/dist/server-core/src/init.d.ts +160 -0
  111. package/dist/server-core/src/init.d.ts.map +1 -0
  112. package/dist/server-core/src/serve-spa.d.ts +31 -0
  113. package/dist/server-core/src/serve-spa.d.ts.map +1 -0
  114. package/dist/server-core/src/services/driver-registry.d.ts +79 -0
  115. package/dist/server-core/src/services/driver-registry.d.ts.map +1 -0
  116. package/dist/server-core/src/singleton.d.ts +36 -0
  117. package/dist/server-core/src/singleton.d.ts.map +1 -0
  118. package/dist/server-core/src/storage/LocalStorageController.d.ts +47 -0
  119. package/dist/server-core/src/storage/LocalStorageController.d.ts.map +1 -0
  120. package/dist/server-core/src/storage/S3StorageController.d.ts +37 -0
  121. package/dist/server-core/src/storage/S3StorageController.d.ts.map +1 -0
  122. package/dist/server-core/src/storage/index.d.ts +26 -0
  123. package/dist/server-core/src/storage/index.d.ts.map +1 -0
  124. package/dist/server-core/src/storage/routes.d.ts +39 -0
  125. package/dist/server-core/src/storage/routes.d.ts.map +1 -0
  126. package/dist/server-core/src/storage/storage-registry.d.ts +79 -0
  127. package/dist/server-core/src/storage/storage-registry.d.ts.map +1 -0
  128. package/dist/server-core/src/storage/types.d.ts +104 -0
  129. package/dist/server-core/src/storage/types.d.ts.map +1 -0
  130. package/dist/server-core/src/types/index.d.ts +12 -0
  131. package/dist/server-core/src/types/index.d.ts.map +1 -0
  132. package/dist/server-core/src/utils/dev-port.d.ts +36 -0
  133. package/dist/server-core/src/utils/dev-port.d.ts.map +1 -0
  134. package/dist/server-core/src/utils/logger.d.ts +32 -0
  135. package/dist/server-core/src/utils/logger.d.ts.map +1 -0
  136. package/dist/server-core/src/utils/logging.d.ts +10 -0
  137. package/dist/server-core/src/utils/logging.d.ts.map +1 -0
  138. package/dist/server-core/src/utils/request-logger.d.ts +20 -0
  139. package/dist/server-core/src/utils/request-logger.d.ts.map +1 -0
  140. package/dist/server-core/src/utils/sql.d.ts +28 -0
  141. package/dist/server-core/src/utils/sql.d.ts.map +1 -0
  142. package/dist/server-mongodb/src/MongoBootstrapper.d.ts +18 -0
  143. package/dist/server-mongodb/src/MongoBootstrapper.d.ts.map +1 -0
  144. package/dist/server-mongodb/src/auth/ensure-collections.d.ts +3 -0
  145. package/dist/server-mongodb/src/auth/ensure-collections.d.ts.map +1 -0
  146. package/dist/server-mongodb/src/auth/services.d.ts +135 -0
  147. package/dist/server-mongodb/src/auth/services.d.ts.map +1 -0
  148. package/dist/server-mongodb/src/connection.d.ts +35 -0
  149. package/dist/server-mongodb/src/connection.d.ts.map +1 -0
  150. package/dist/server-mongodb/src/db/MongoConditionBuilder.d.ts +64 -0
  151. package/dist/server-mongodb/src/db/MongoConditionBuilder.d.ts.map +1 -0
  152. package/dist/server-mongodb/src/db/MongoEntityService.d.ts +98 -0
  153. package/dist/server-mongodb/src/db/MongoEntityService.d.ts.map +1 -0
  154. package/dist/server-mongodb/src/factory.d.ts +142 -0
  155. package/dist/server-mongodb/src/factory.d.ts.map +1 -0
  156. package/dist/server-mongodb/src/history/ensure-history-collection.d.ts +3 -0
  157. package/dist/server-mongodb/src/history/ensure-history-collection.d.ts.map +1 -0
  158. package/dist/server-mongodb/src/index.d.ts +18 -0
  159. package/dist/server-mongodb/src/index.d.ts.map +1 -0
  160. package/dist/server-mongodb/src/services/MongoDriver.d.ts +83 -0
  161. package/dist/server-mongodb/src/services/MongoDriver.d.ts.map +1 -0
  162. package/dist/server-mongodb/src/services/MongoHistoryService.d.ts +37 -0
  163. package/dist/server-mongodb/src/services/MongoHistoryService.d.ts.map +1 -0
  164. package/dist/server-mongodb/src/services/MongoRealtimeService.d.ts +86 -0
  165. package/dist/server-mongodb/src/services/MongoRealtimeService.d.ts.map +1 -0
  166. package/dist/server-mongodb/src/useMongoDriver.d.ts +18 -0
  167. package/dist/server-mongodb/src/useMongoDriver.d.ts.map +1 -0
  168. package/dist/server-mongodb/src/utils.d.ts +10 -0
  169. package/dist/server-mongodb/src/utils.d.ts.map +1 -0
  170. package/dist/server-mongodb/src/websocket.d.ts +7 -0
  171. package/dist/server-mongodb/src/websocket.d.ts.map +1 -0
  172. package/dist/types/src/controllers/analytics_controller.d.ts +8 -0
  173. package/dist/types/src/controllers/analytics_controller.d.ts.map +1 -0
  174. package/dist/types/src/controllers/auth.d.ts +120 -0
  175. package/dist/types/src/controllers/auth.d.ts.map +1 -0
  176. package/dist/types/src/controllers/client.d.ts +171 -0
  177. package/dist/types/src/controllers/client.d.ts.map +1 -0
  178. package/dist/types/src/controllers/collection_registry.d.ts +46 -0
  179. package/dist/types/src/controllers/collection_registry.d.ts.map +1 -0
  180. package/dist/types/src/controllers/customization_controller.d.ts +61 -0
  181. package/dist/types/src/controllers/customization_controller.d.ts.map +1 -0
  182. package/dist/types/src/controllers/data.d.ts +169 -0
  183. package/dist/types/src/controllers/data.d.ts.map +1 -0
  184. package/dist/types/src/controllers/data_driver.d.ts +161 -0
  185. package/dist/types/src/controllers/data_driver.d.ts.map +1 -0
  186. package/dist/types/src/controllers/database_admin.d.ts +12 -0
  187. package/dist/types/src/controllers/database_admin.d.ts.map +1 -0
  188. package/dist/types/src/controllers/dialogs_controller.d.ts +37 -0
  189. package/dist/types/src/controllers/dialogs_controller.d.ts.map +1 -0
  190. package/dist/types/src/controllers/effective_role.d.ts +5 -0
  191. package/dist/types/src/controllers/effective_role.d.ts.map +1 -0
  192. package/dist/types/src/controllers/email.d.ts +35 -0
  193. package/dist/types/src/controllers/email.d.ts.map +1 -0
  194. package/dist/types/src/controllers/index.d.ts +19 -0
  195. package/dist/types/src/controllers/index.d.ts.map +1 -0
  196. package/dist/types/src/controllers/local_config_persistence.d.ts +21 -0
  197. package/dist/types/src/controllers/local_config_persistence.d.ts.map +1 -0
  198. package/dist/types/src/controllers/navigation.d.ts +214 -0
  199. package/dist/types/src/controllers/navigation.d.ts.map +1 -0
  200. package/dist/types/src/controllers/registry.d.ts +55 -0
  201. package/dist/types/src/controllers/registry.d.ts.map +1 -0
  202. package/dist/types/src/controllers/side_dialogs_controller.d.ts +68 -0
  203. package/dist/types/src/controllers/side_dialogs_controller.d.ts.map +1 -0
  204. package/dist/types/src/controllers/side_entity_controller.d.ts +91 -0
  205. package/dist/types/src/controllers/side_entity_controller.d.ts.map +1 -0
  206. package/dist/types/src/controllers/snackbar.d.ts +25 -0
  207. package/dist/types/src/controllers/snackbar.d.ts.map +1 -0
  208. package/dist/types/src/controllers/storage.d.ts +172 -0
  209. package/dist/types/src/controllers/storage.d.ts.map +1 -0
  210. package/dist/types/src/index.d.ts +5 -0
  211. package/dist/types/src/index.d.ts.map +1 -0
  212. package/dist/types/src/rebase_context.d.ts +106 -0
  213. package/dist/types/src/rebase_context.d.ts.map +1 -0
  214. package/dist/types/src/types/backend.d.ts +537 -0
  215. package/dist/types/src/types/backend.d.ts.map +1 -0
  216. package/dist/types/src/types/builders.d.ts +16 -0
  217. package/dist/types/src/types/builders.d.ts.map +1 -0
  218. package/dist/types/src/types/chips.d.ts +6 -0
  219. package/dist/types/src/types/chips.d.ts.map +1 -0
  220. package/dist/types/src/types/collections.d.ts +857 -0
  221. package/dist/types/src/types/collections.d.ts.map +1 -0
  222. package/dist/types/src/types/cron.d.ts +103 -0
  223. package/dist/types/src/types/cron.d.ts.map +1 -0
  224. package/dist/types/src/types/data_source.d.ts +65 -0
  225. package/dist/types/src/types/data_source.d.ts.map +1 -0
  226. package/dist/types/src/types/entities.d.ts +146 -0
  227. package/dist/types/src/types/entities.d.ts.map +1 -0
  228. package/dist/types/src/types/entity_actions.d.ts +99 -0
  229. package/dist/types/src/types/entity_actions.d.ts.map +1 -0
  230. package/dist/types/src/types/entity_callbacks.d.ts +174 -0
  231. package/dist/types/src/types/entity_callbacks.d.ts.map +1 -0
  232. package/dist/types/src/types/entity_link_builder.d.ts +8 -0
  233. package/dist/types/src/types/entity_link_builder.d.ts.map +1 -0
  234. package/dist/types/src/types/entity_overrides.d.ts +11 -0
  235. package/dist/types/src/types/entity_overrides.d.ts.map +1 -0
  236. package/dist/types/src/types/entity_views.d.ts +62 -0
  237. package/dist/types/src/types/entity_views.d.ts.map +1 -0
  238. package/dist/types/src/types/export_import.d.ts +22 -0
  239. package/dist/types/src/types/export_import.d.ts.map +1 -0
  240. package/dist/types/src/types/index.d.ts +24 -0
  241. package/dist/types/src/types/index.d.ts.map +1 -0
  242. package/dist/types/src/types/locales.d.ts +5 -0
  243. package/dist/types/src/types/locales.d.ts.map +1 -0
  244. package/dist/types/src/types/modify_collections.d.ts +6 -0
  245. package/dist/types/src/types/modify_collections.d.ts.map +1 -0
  246. package/dist/types/src/types/plugins.d.ts +280 -0
  247. package/dist/types/src/types/plugins.d.ts.map +1 -0
  248. package/dist/types/src/types/properties.d.ts +1177 -0
  249. package/dist/types/src/types/properties.d.ts.map +1 -0
  250. package/dist/types/src/types/property_config.d.ts +71 -0
  251. package/dist/types/src/types/property_config.d.ts.map +1 -0
  252. package/dist/types/src/types/relations.d.ts +337 -0
  253. package/dist/types/src/types/relations.d.ts.map +1 -0
  254. package/dist/types/src/types/slots.d.ts +253 -0
  255. package/dist/types/src/types/slots.d.ts.map +1 -0
  256. package/dist/types/src/types/translations.d.ts +871 -0
  257. package/dist/types/src/types/translations.d.ts.map +1 -0
  258. package/dist/types/src/types/user_management_delegate.d.ts +122 -0
  259. package/dist/types/src/types/user_management_delegate.d.ts.map +1 -0
  260. package/dist/types/src/types/websockets.d.ts +79 -0
  261. package/dist/types/src/types/websockets.d.ts.map +1 -0
  262. package/dist/types/src/users/index.d.ts +3 -0
  263. package/dist/types/src/users/index.d.ts.map +1 -0
  264. package/dist/types/src/users/roles.d.ts +23 -0
  265. package/dist/types/src/users/roles.d.ts.map +1 -0
  266. package/dist/types/src/users/user.d.ts +47 -0
  267. package/dist/types/src/users/user.d.ts.map +1 -0
  268. package/dist/websocket-BZlPuJrt.js +220 -0
  269. package/dist/websocket-BZlPuJrt.js.map +1 -0
  270. package/package.json +79 -0
  271. package/src/MongoBootstrapper.ts +177 -0
  272. package/src/auth/ensure-collections.ts +94 -0
  273. package/src/auth/services.ts +638 -0
  274. package/src/connection.ts +60 -0
  275. package/src/db/MongoConditionBuilder.ts +181 -0
  276. package/src/db/MongoEntityService.ts +350 -0
  277. package/src/factory.ts +289 -0
  278. package/src/history/ensure-history-collection.ts +19 -0
  279. package/src/index.ts +25 -0
  280. package/src/services/MongoDriver.ts +297 -0
  281. package/src/services/MongoDriver.ts.backup +266 -0
  282. package/src/services/MongoHistoryService.ts +154 -0
  283. package/src/services/MongoRealtimeService.ts +394 -0
  284. package/src/useMongoDriver.ts +519 -0
  285. package/src/utils.ts +28 -0
  286. package/src/websocket.ts +257 -0
@@ -0,0 +1,394 @@
1
+ /**
2
+ * MongoDB Realtime Service
3
+ *
4
+ * Implements RealtimeProvider interface using MongoDB Change Streams.
5
+ * Provides real-time subscriptions to collection and entity changes.
6
+ */
7
+
8
+ import { Db, ChangeStream, ChangeStreamDocument, Document, ObjectId } from "mongodb";
9
+ import {
10
+ Entity,
11
+ FilterValues,
12
+ RealtimeProvider,
13
+ CollectionSubscriptionConfig,
14
+ EntitySubscriptionConfig,
15
+ WebSocketMessage
16
+ } from "@rebasepro/types";
17
+ import { WebSocket } from "ws";
18
+ import { MongoEntityService } from "../db/MongoEntityService";
19
+
20
+ interface Subscription {
21
+ type: "collection" | "entity";
22
+ config: CollectionSubscriptionConfig | EntitySubscriptionConfig;
23
+ changeStream?: ChangeStream;
24
+ callback?: (data: any) => void;
25
+ }
26
+
27
+ /**
28
+ * MongoDB Realtime Service
29
+ *
30
+ * Implements real-time subscriptions using MongoDB Change Streams.
31
+ * Requires MongoDB replica set for change streams to work.
32
+ */
33
+ export class MongoRealtimeService implements RealtimeProvider {
34
+ private subscriptions = new Map<string, Subscription>();
35
+ private clients = new Map<string, WebSocket>();
36
+ private entityService: MongoEntityService;
37
+
38
+ constructor(private db: Db) {
39
+ this.entityService = new MongoEntityService(db);
40
+ }
41
+
42
+ /**
43
+ * Get the collection name from a path
44
+ */
45
+ private getCollectionName(path: string): string {
46
+ return path.replace(/\//g, "_");
47
+ }
48
+
49
+ /**
50
+ * Subscribe to collection changes
51
+ */
52
+ subscribeToCollection(
53
+ subscriptionId: string,
54
+ config: CollectionSubscriptionConfig,
55
+ callback?: (entities: Entity[]) => void
56
+ ): void {
57
+ // Clean up existing subscription if any
58
+ this.unsubscribe(subscriptionId);
59
+
60
+ const collectionName = this.getCollectionName(config.path);
61
+ const collection = this.db.collection(collectionName);
62
+
63
+ // Build pipeline for change stream filtering
64
+ const pipeline: Document[] = [];
65
+
66
+ // Filter by operation types we care about
67
+ pipeline.push({
68
+ $match: {
69
+ operationType: { $in: ["insert", "update", "replace", "delete"] }
70
+ }
71
+ });
72
+
73
+ try {
74
+ // Create change stream
75
+ const changeStream = collection.watch(pipeline, {
76
+ fullDocument: "updateLookup"
77
+ });
78
+
79
+ const subscription: Subscription = {
80
+ type: "collection",
81
+ config,
82
+ changeStream,
83
+ callback
84
+ };
85
+
86
+ this.subscriptions.set(subscriptionId, subscription);
87
+
88
+ // Fetch initial data
89
+ this.fetchAndNotifyCollection(subscriptionId, config, callback);
90
+
91
+ // Listen for changes
92
+ changeStream.on("change", async (change: ChangeStreamDocument) => {
93
+ // Re-fetch the entire collection when any change happens
94
+ // This is simpler and ensures consistent sorting/filtering
95
+ await this.fetchAndNotifyCollection(subscriptionId, config, callback);
96
+ });
97
+
98
+ changeStream.on("error", (error: Error) => {
99
+ console.error(`Change stream error for subscription ${subscriptionId}:`, error);
100
+ });
101
+
102
+ } catch (error) {
103
+ // Change streams might not be available (e.g., standalone MongoDB)
104
+ console.warn("Change streams not available, falling back to polling:", error);
105
+
106
+ // Store subscription without change stream for manual notifications
107
+ const subscription: Subscription = {
108
+ type: "collection",
109
+ config,
110
+ callback
111
+ };
112
+
113
+ this.subscriptions.set(subscriptionId, subscription);
114
+
115
+ // Fetch initial data
116
+ this.fetchAndNotifyCollection(subscriptionId, config, callback);
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Fetch collection and notify callback
122
+ */
123
+ private async fetchAndNotifyCollection(
124
+ subscriptionId: string,
125
+ config: CollectionSubscriptionConfig,
126
+ callback?: (entities: Entity[]) => void
127
+ ): Promise<void> {
128
+ try {
129
+ const entities = await this.entityService.fetchCollection(config.path, {
130
+ filter: config.filter as FilterValues<string> | undefined,
131
+ orderBy: config.orderBy,
132
+ order: config.order,
133
+ limit: config.limit,
134
+ startAfter: config.startAfter,
135
+ searchString: config.searchString
136
+ });
137
+
138
+ if (callback) {
139
+ callback(entities);
140
+ }
141
+ } catch (error) {
142
+ console.error(`Error fetching collection for subscription ${subscriptionId}:`, error);
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Subscribe to single entity changes
148
+ */
149
+ subscribeToEntity(
150
+ subscriptionId: string,
151
+ config: EntitySubscriptionConfig,
152
+ callback?: (entity: Entity | null) => void
153
+ ): void {
154
+ // Clean up existing subscription if any
155
+ this.unsubscribe(subscriptionId);
156
+
157
+ const collectionName = this.getCollectionName(config.path);
158
+ const collection = this.db.collection(collectionName);
159
+
160
+ // Build pipeline to watch specific document
161
+ const entityId = typeof config.entityId === "string" && ObjectId.isValid(config.entityId)
162
+ ? new ObjectId(config.entityId)
163
+ : config.entityId;
164
+
165
+ const pipeline: Document[] = [
166
+ {
167
+ $match: {
168
+ "documentKey._id": entityId,
169
+ operationType: { $in: ["insert", "update", "replace", "delete"] }
170
+ }
171
+ }
172
+ ];
173
+
174
+ try {
175
+ const changeStream = collection.watch(pipeline, {
176
+ fullDocument: "updateLookup"
177
+ });
178
+
179
+ const subscription: Subscription = {
180
+ type: "entity",
181
+ config,
182
+ changeStream,
183
+ callback
184
+ };
185
+
186
+ this.subscriptions.set(subscriptionId, subscription);
187
+
188
+ // Fetch initial data
189
+ this.fetchAndNotifyEntity(subscriptionId, config, callback);
190
+
191
+ // Listen for changes
192
+ changeStream.on("change", async (change: ChangeStreamDocument) => {
193
+ if (change.operationType === "delete") {
194
+ if (callback) {
195
+ callback(null);
196
+ }
197
+ } else {
198
+ await this.fetchAndNotifyEntity(subscriptionId, config, callback);
199
+ }
200
+ });
201
+
202
+ changeStream.on("error", (error: Error) => {
203
+ console.error(`Change stream error for subscription ${subscriptionId}:`, error);
204
+ });
205
+
206
+ } catch (error) {
207
+ console.warn("Change streams not available, falling back to polling:", error);
208
+
209
+ const subscription: Subscription = {
210
+ type: "entity",
211
+ config,
212
+ callback
213
+ };
214
+
215
+ this.subscriptions.set(subscriptionId, subscription);
216
+
217
+ // Fetch initial data
218
+ this.fetchAndNotifyEntity(subscriptionId, config, callback);
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Fetch entity and notify callback
224
+ */
225
+ private async fetchAndNotifyEntity(
226
+ subscriptionId: string,
227
+ config: EntitySubscriptionConfig,
228
+ callback?: (entity: Entity | null) => void
229
+ ): Promise<void> {
230
+ try {
231
+ const entity = await this.entityService.fetchEntity(config.path, config.entityId);
232
+
233
+ if (callback) {
234
+ callback(entity || null);
235
+ }
236
+ } catch (error) {
237
+ console.error(`Error fetching entity for subscription ${subscriptionId}:`, error);
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Unsubscribe from a subscription
243
+ */
244
+ unsubscribe(subscriptionId: string): void {
245
+ const subscription = this.subscriptions.get(subscriptionId);
246
+ if (subscription) {
247
+ if (subscription.changeStream) {
248
+ subscription.changeStream.close().catch(console.error);
249
+ }
250
+ this.subscriptions.delete(subscriptionId);
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Notify all relevant subscribers of an entity update
256
+ * This is called after save/delete operations to push updates
257
+ */
258
+ async notifyEntityUpdate(
259
+ path: string,
260
+ entityId: string,
261
+ entity: Entity | null,
262
+ _databaseId?: string
263
+ ): Promise<void> {
264
+ // Find all subscriptions that might be affected by this update
265
+ for (const [subscriptionId, subscription] of this.subscriptions) {
266
+ if (subscription.type === "entity") {
267
+ const config = subscription.config as EntitySubscriptionConfig;
268
+ if (config.path === path && config.entityId.toString() === entityId) {
269
+ if (subscription.callback) {
270
+ subscription.callback(entity);
271
+ }
272
+ }
273
+ } else if (subscription.type === "collection") {
274
+ const config = subscription.config as CollectionSubscriptionConfig;
275
+ if (config.path === path) {
276
+ // Re-fetch the collection to get updated data
277
+ await this.fetchAndNotifyCollection(subscriptionId, config, subscription.callback);
278
+ }
279
+ }
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Get all active subscriptions (for debugging)
285
+ */
286
+ getSubscriptions(): Map<string, Subscription> {
287
+ return this.subscriptions;
288
+ }
289
+
290
+ /**
291
+ * Close all subscriptions
292
+ */
293
+ async closeAll(): Promise<void> {
294
+ for (const [subscriptionId] of this.subscriptions) {
295
+ this.unsubscribe(subscriptionId);
296
+ }
297
+ }
298
+
299
+ // =============================================================================
300
+ // WebSocket Client Management (parity with PostgreSQL RealtimeService)
301
+ // =============================================================================
302
+
303
+ /**
304
+ * Register a WebSocket client for real-time communication
305
+ */
306
+ addClient(clientId: string, ws: WebSocket) {
307
+ this.clients.set(clientId, ws);
308
+
309
+ ws.on("close", () => {
310
+ this.removeClient(clientId);
311
+ });
312
+
313
+ ws.on("error", (error) => {
314
+ console.error("WebSocket error for client", clientId, error);
315
+ this.removeClient(clientId);
316
+ });
317
+ }
318
+
319
+ /**
320
+ * Remove a WebSocket client and clean up its subscriptions
321
+ */
322
+ private removeClient(clientId: string) {
323
+ this.clients.delete(clientId);
324
+ }
325
+
326
+ /**
327
+ * Handle an incoming WebSocket message for subscription management
328
+ */
329
+ async handleClientMessage(
330
+ clientId: string,
331
+ message: { type: string; payload?: any; subscriptionId?: string },
332
+ _authContext?: { userId: string; roles: unknown[] }
333
+ ): Promise<void> {
334
+ const ws = this.clients.get(clientId);
335
+ if (!ws) return;
336
+
337
+ switch (message.type) {
338
+ case "subscribe_collection": {
339
+ const subscriptionId = message.payload?.subscriptionId ?? message.subscriptionId;
340
+ if (!subscriptionId) return;
341
+
342
+ this.subscribeToCollection(
343
+ subscriptionId,
344
+ {
345
+ clientId,
346
+ path: message.payload?.path,
347
+ filter: message.payload?.filter,
348
+ orderBy: message.payload?.orderBy,
349
+ order: message.payload?.order,
350
+ limit: message.payload?.limit,
351
+ startAfter: message.payload?.startAfter,
352
+ searchString: message.payload?.searchString
353
+ },
354
+ (entities) => {
355
+ ws.send(JSON.stringify({
356
+ type: "collection_update",
357
+ subscriptionId,
358
+ entities
359
+ }));
360
+ }
361
+ );
362
+ break;
363
+ }
364
+ case "subscribe_entity": {
365
+ const subscriptionId = message.payload?.subscriptionId ?? message.subscriptionId;
366
+ if (!subscriptionId) return;
367
+
368
+ this.subscribeToEntity(
369
+ subscriptionId,
370
+ {
371
+ clientId,
372
+ path: message.payload?.path,
373
+ entityId: message.payload?.entityId
374
+ },
375
+ (entity) => {
376
+ ws.send(JSON.stringify({
377
+ type: "entity_update",
378
+ subscriptionId,
379
+ entity
380
+ }));
381
+ }
382
+ );
383
+ break;
384
+ }
385
+ case "unsubscribe": {
386
+ const subscriptionId = message.payload?.subscriptionId ?? message.subscriptionId;
387
+ if (subscriptionId) {
388
+ this.unsubscribe(subscriptionId);
389
+ }
390
+ break;
391
+ }
392
+ }
393
+ }
394
+ }