@rebasepro/server-mongodb 0.0.1-canary.4d4fb3e

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 (125) hide show
  1. package/LICENSE +6 -0
  2. package/dist/index.es.js +875 -0
  3. package/dist/index.es.js.map +1 -0
  4. package/dist/index.umd.js +878 -0
  5. package/dist/index.umd.js.map +1 -0
  6. package/dist/server-mongodb/src/connection.d.ts +35 -0
  7. package/dist/server-mongodb/src/connection.d.ts.map +1 -0
  8. package/dist/server-mongodb/src/db/MongoConditionBuilder.d.ts +64 -0
  9. package/dist/server-mongodb/src/db/MongoConditionBuilder.d.ts.map +1 -0
  10. package/dist/server-mongodb/src/db/MongoEntityService.d.ts +98 -0
  11. package/dist/server-mongodb/src/db/MongoEntityService.d.ts.map +1 -0
  12. package/dist/server-mongodb/src/factory.d.ts +139 -0
  13. package/dist/server-mongodb/src/factory.d.ts.map +1 -0
  14. package/dist/server-mongodb/src/index.d.ts +17 -0
  15. package/dist/server-mongodb/src/index.d.ts.map +1 -0
  16. package/dist/server-mongodb/src/services/MongoDriver.d.ts +81 -0
  17. package/dist/server-mongodb/src/services/MongoDriver.d.ts.map +1 -0
  18. package/dist/server-mongodb/src/services/MongoRealtimeService.d.ts +65 -0
  19. package/dist/server-mongodb/src/services/MongoRealtimeService.d.ts.map +1 -0
  20. package/dist/server-mongodb/src/useMongoDriver.d.ts +18 -0
  21. package/dist/server-mongodb/src/useMongoDriver.d.ts.map +1 -0
  22. package/dist/server-mongodb/src/utils.d.ts +10 -0
  23. package/dist/server-mongodb/src/utils.d.ts.map +1 -0
  24. package/dist/types/src/controllers/analytics_controller.d.ts +8 -0
  25. package/dist/types/src/controllers/analytics_controller.d.ts.map +1 -0
  26. package/dist/types/src/controllers/auth.d.ts +118 -0
  27. package/dist/types/src/controllers/auth.d.ts.map +1 -0
  28. package/dist/types/src/controllers/client.d.ts +59 -0
  29. package/dist/types/src/controllers/client.d.ts.map +1 -0
  30. package/dist/types/src/controllers/collection_registry.d.ts +45 -0
  31. package/dist/types/src/controllers/collection_registry.d.ts.map +1 -0
  32. package/dist/types/src/controllers/customization_controller.d.ts +55 -0
  33. package/dist/types/src/controllers/customization_controller.d.ts.map +1 -0
  34. package/dist/types/src/controllers/data.d.ts +142 -0
  35. package/dist/types/src/controllers/data.d.ts.map +1 -0
  36. package/dist/types/src/controllers/data_driver.d.ts +169 -0
  37. package/dist/types/src/controllers/data_driver.d.ts.map +1 -0
  38. package/dist/types/src/controllers/database_admin.d.ts +12 -0
  39. package/dist/types/src/controllers/database_admin.d.ts.map +1 -0
  40. package/dist/types/src/controllers/dialogs_controller.d.ts +37 -0
  41. package/dist/types/src/controllers/dialogs_controller.d.ts.map +1 -0
  42. package/dist/types/src/controllers/effective_role.d.ts +5 -0
  43. package/dist/types/src/controllers/effective_role.d.ts.map +1 -0
  44. package/dist/types/src/controllers/index.d.ts +18 -0
  45. package/dist/types/src/controllers/index.d.ts.map +1 -0
  46. package/dist/types/src/controllers/local_config_persistence.d.ts +21 -0
  47. package/dist/types/src/controllers/local_config_persistence.d.ts.map +1 -0
  48. package/dist/types/src/controllers/navigation.d.ts +214 -0
  49. package/dist/types/src/controllers/navigation.d.ts.map +1 -0
  50. package/dist/types/src/controllers/registry.d.ts +52 -0
  51. package/dist/types/src/controllers/registry.d.ts.map +1 -0
  52. package/dist/types/src/controllers/side_dialogs_controller.d.ts +68 -0
  53. package/dist/types/src/controllers/side_dialogs_controller.d.ts.map +1 -0
  54. package/dist/types/src/controllers/side_entity_controller.d.ts +90 -0
  55. package/dist/types/src/controllers/side_entity_controller.d.ts.map +1 -0
  56. package/dist/types/src/controllers/snackbar.d.ts +25 -0
  57. package/dist/types/src/controllers/snackbar.d.ts.map +1 -0
  58. package/dist/types/src/controllers/storage.d.ts +174 -0
  59. package/dist/types/src/controllers/storage.d.ts.map +1 -0
  60. package/dist/types/src/index.d.ts +5 -0
  61. package/dist/types/src/index.d.ts.map +1 -0
  62. package/dist/types/src/rebase_context.d.ts +102 -0
  63. package/dist/types/src/rebase_context.d.ts.map +1 -0
  64. package/dist/types/src/types/backend.d.ts +534 -0
  65. package/dist/types/src/types/backend.d.ts.map +1 -0
  66. package/dist/types/src/types/builders.d.ts +15 -0
  67. package/dist/types/src/types/builders.d.ts.map +1 -0
  68. package/dist/types/src/types/chips.d.ts +6 -0
  69. package/dist/types/src/types/chips.d.ts.map +1 -0
  70. package/dist/types/src/types/collections.d.ts +813 -0
  71. package/dist/types/src/types/collections.d.ts.map +1 -0
  72. package/dist/types/src/types/data_source.d.ts +65 -0
  73. package/dist/types/src/types/data_source.d.ts.map +1 -0
  74. package/dist/types/src/types/entities.d.ts +146 -0
  75. package/dist/types/src/types/entities.d.ts.map +1 -0
  76. package/dist/types/src/types/entity_actions.d.ts +99 -0
  77. package/dist/types/src/types/entity_actions.d.ts.map +1 -0
  78. package/dist/types/src/types/entity_callbacks.d.ts +174 -0
  79. package/dist/types/src/types/entity_callbacks.d.ts.map +1 -0
  80. package/dist/types/src/types/entity_link_builder.d.ts +8 -0
  81. package/dist/types/src/types/entity_link_builder.d.ts.map +1 -0
  82. package/dist/types/src/types/entity_overrides.d.ts +10 -0
  83. package/dist/types/src/types/entity_overrides.d.ts.map +1 -0
  84. package/dist/types/src/types/entity_views.d.ts +62 -0
  85. package/dist/types/src/types/entity_views.d.ts.map +1 -0
  86. package/dist/types/src/types/export_import.d.ts +22 -0
  87. package/dist/types/src/types/export_import.d.ts.map +1 -0
  88. package/dist/types/src/types/index.d.ts +23 -0
  89. package/dist/types/src/types/index.d.ts.map +1 -0
  90. package/dist/types/src/types/locales.d.ts +5 -0
  91. package/dist/types/src/types/locales.d.ts.map +1 -0
  92. package/dist/types/src/types/modify_collections.d.ts +6 -0
  93. package/dist/types/src/types/modify_collections.d.ts.map +1 -0
  94. package/dist/types/src/types/plugins.d.ts +226 -0
  95. package/dist/types/src/types/plugins.d.ts.map +1 -0
  96. package/dist/types/src/types/properties.d.ts +1092 -0
  97. package/dist/types/src/types/properties.d.ts.map +1 -0
  98. package/dist/types/src/types/property_config.d.ts +71 -0
  99. package/dist/types/src/types/property_config.d.ts.map +1 -0
  100. package/dist/types/src/types/relations.d.ts +337 -0
  101. package/dist/types/src/types/relations.d.ts.map +1 -0
  102. package/dist/types/src/types/slots.d.ts +229 -0
  103. package/dist/types/src/types/slots.d.ts.map +1 -0
  104. package/dist/types/src/types/translations.d.ts +827 -0
  105. package/dist/types/src/types/translations.d.ts.map +1 -0
  106. package/dist/types/src/types/user_management_delegate.d.ts +121 -0
  107. package/dist/types/src/types/user_management_delegate.d.ts.map +1 -0
  108. package/dist/types/src/types/websockets.d.ts +79 -0
  109. package/dist/types/src/types/websockets.d.ts.map +1 -0
  110. package/dist/types/src/users/index.d.ts +3 -0
  111. package/dist/types/src/users/index.d.ts.map +1 -0
  112. package/dist/types/src/users/roles.d.ts +23 -0
  113. package/dist/types/src/users/roles.d.ts.map +1 -0
  114. package/dist/types/src/users/user.d.ts +47 -0
  115. package/dist/types/src/users/user.d.ts.map +1 -0
  116. package/package.json +81 -0
  117. package/src/connection.ts +60 -0
  118. package/src/db/MongoConditionBuilder.ts +181 -0
  119. package/src/db/MongoEntityService.ts +350 -0
  120. package/src/factory.ts +274 -0
  121. package/src/index.ts +24 -0
  122. package/src/services/MongoDriver.ts +267 -0
  123. package/src/services/MongoRealtimeService.ts +295 -0
  124. package/src/useMongoDriver.ts +516 -0
  125. package/src/utils.ts +28 -0
@@ -0,0 +1,295 @@
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
+ } from "@rebasepro/types";
16
+ import { MongoEntityService } from "../db/MongoEntityService";
17
+
18
+ interface Subscription {
19
+ type: "collection" | "entity";
20
+ config: CollectionSubscriptionConfig | EntitySubscriptionConfig;
21
+ changeStream?: ChangeStream;
22
+ callback?: (data: any) => void;
23
+ }
24
+
25
+ /**
26
+ * MongoDB Realtime Service
27
+ *
28
+ * Implements real-time subscriptions using MongoDB Change Streams.
29
+ * Requires MongoDB replica set for change streams to work.
30
+ */
31
+ export class MongoRealtimeService implements RealtimeProvider {
32
+ private subscriptions = new Map<string, Subscription>();
33
+ private entityService: MongoEntityService;
34
+
35
+ constructor(private db: Db) {
36
+ this.entityService = new MongoEntityService(db);
37
+ }
38
+
39
+ /**
40
+ * Get the collection name from a path
41
+ */
42
+ private getCollectionName(path: string): string {
43
+ return path.replace(/\//g, "_");
44
+ }
45
+
46
+ /**
47
+ * Subscribe to collection changes
48
+ */
49
+ subscribeToCollection(
50
+ subscriptionId: string,
51
+ config: CollectionSubscriptionConfig,
52
+ callback?: (entities: Entity[]) => void
53
+ ): void {
54
+ // Clean up existing subscription if any
55
+ this.unsubscribe(subscriptionId);
56
+
57
+ const collectionName = this.getCollectionName(config.path);
58
+ const collection = this.db.collection(collectionName);
59
+
60
+ // Build pipeline for change stream filtering
61
+ const pipeline: Document[] = [];
62
+
63
+ // Filter by operation types we care about
64
+ pipeline.push({
65
+ $match: {
66
+ operationType: { $in: ["insert", "update", "replace", "delete"] }
67
+ }
68
+ });
69
+
70
+ try {
71
+ // Create change stream
72
+ const changeStream = collection.watch(pipeline, {
73
+ fullDocument: "updateLookup"
74
+ });
75
+
76
+ const subscription: Subscription = {
77
+ type: "collection",
78
+ config,
79
+ changeStream,
80
+ callback
81
+ };
82
+
83
+ this.subscriptions.set(subscriptionId, subscription);
84
+
85
+ // Fetch initial data
86
+ this.fetchAndNotifyCollection(subscriptionId, config, callback);
87
+
88
+ // Listen for changes
89
+ changeStream.on("change", async (change: ChangeStreamDocument) => {
90
+ // Re-fetch the entire collection when any change happens
91
+ // This is simpler and ensures consistent sorting/filtering
92
+ await this.fetchAndNotifyCollection(subscriptionId, config, callback);
93
+ });
94
+
95
+ changeStream.on("error", (error: Error) => {
96
+ console.error(`Change stream error for subscription ${subscriptionId}:`, error);
97
+ });
98
+
99
+ } catch (error) {
100
+ // Change streams might not be available (e.g., standalone MongoDB)
101
+ console.warn("Change streams not available, falling back to polling:", error);
102
+
103
+ // Store subscription without change stream for manual notifications
104
+ const subscription: Subscription = {
105
+ type: "collection",
106
+ config,
107
+ callback
108
+ };
109
+
110
+ this.subscriptions.set(subscriptionId, subscription);
111
+
112
+ // Fetch initial data
113
+ this.fetchAndNotifyCollection(subscriptionId, config, callback);
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Fetch collection and notify callback
119
+ */
120
+ private async fetchAndNotifyCollection(
121
+ subscriptionId: string,
122
+ config: CollectionSubscriptionConfig,
123
+ callback?: (entities: Entity[]) => void
124
+ ): Promise<void> {
125
+ try {
126
+ const entities = await this.entityService.fetchCollection(config.path, {
127
+ filter: config.filter as FilterValues<string> | undefined,
128
+ orderBy: config.orderBy,
129
+ order: config.order,
130
+ limit: config.limit,
131
+ startAfter: config.startAfter,
132
+ searchString: config.searchString
133
+ });
134
+
135
+ if (callback) {
136
+ callback(entities);
137
+ }
138
+ } catch (error) {
139
+ console.error(`Error fetching collection for subscription ${subscriptionId}:`, error);
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Subscribe to single entity changes
145
+ */
146
+ subscribeToEntity(
147
+ subscriptionId: string,
148
+ config: EntitySubscriptionConfig,
149
+ callback?: (entity: Entity | null) => void
150
+ ): void {
151
+ // Clean up existing subscription if any
152
+ this.unsubscribe(subscriptionId);
153
+
154
+ const collectionName = this.getCollectionName(config.path);
155
+ const collection = this.db.collection(collectionName);
156
+
157
+ // Build pipeline to watch specific document
158
+ const entityId = typeof config.entityId === "string" && ObjectId.isValid(config.entityId)
159
+ ? new ObjectId(config.entityId)
160
+ : config.entityId;
161
+
162
+ const pipeline: Document[] = [
163
+ {
164
+ $match: {
165
+ "documentKey._id": entityId,
166
+ operationType: { $in: ["insert", "update", "replace", "delete"] }
167
+ }
168
+ }
169
+ ];
170
+
171
+ try {
172
+ const changeStream = collection.watch(pipeline, {
173
+ fullDocument: "updateLookup"
174
+ });
175
+
176
+ const subscription: Subscription = {
177
+ type: "entity",
178
+ config,
179
+ changeStream,
180
+ callback
181
+ };
182
+
183
+ this.subscriptions.set(subscriptionId, subscription);
184
+
185
+ // Fetch initial data
186
+ this.fetchAndNotifyEntity(subscriptionId, config, callback);
187
+
188
+ // Listen for changes
189
+ changeStream.on("change", async (change: ChangeStreamDocument) => {
190
+ if (change.operationType === "delete") {
191
+ if (callback) {
192
+ callback(null);
193
+ }
194
+ } else {
195
+ await this.fetchAndNotifyEntity(subscriptionId, config, callback);
196
+ }
197
+ });
198
+
199
+ changeStream.on("error", (error: Error) => {
200
+ console.error(`Change stream error for subscription ${subscriptionId}:`, error);
201
+ });
202
+
203
+ } catch (error) {
204
+ console.warn("Change streams not available, falling back to polling:", error);
205
+
206
+ const subscription: Subscription = {
207
+ type: "entity",
208
+ config,
209
+ callback
210
+ };
211
+
212
+ this.subscriptions.set(subscriptionId, subscription);
213
+
214
+ // Fetch initial data
215
+ this.fetchAndNotifyEntity(subscriptionId, config, callback);
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Fetch entity and notify callback
221
+ */
222
+ private async fetchAndNotifyEntity(
223
+ subscriptionId: string,
224
+ config: EntitySubscriptionConfig,
225
+ callback?: (entity: Entity | null) => void
226
+ ): Promise<void> {
227
+ try {
228
+ const entity = await this.entityService.fetchEntity(config.path, config.entityId);
229
+
230
+ if (callback) {
231
+ callback(entity || null);
232
+ }
233
+ } catch (error) {
234
+ console.error(`Error fetching entity for subscription ${subscriptionId}:`, error);
235
+ }
236
+ }
237
+
238
+ /**
239
+ * Unsubscribe from a subscription
240
+ */
241
+ unsubscribe(subscriptionId: string): void {
242
+ const subscription = this.subscriptions.get(subscriptionId);
243
+ if (subscription) {
244
+ if (subscription.changeStream) {
245
+ subscription.changeStream.close().catch(console.error);
246
+ }
247
+ this.subscriptions.delete(subscriptionId);
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Notify all relevant subscribers of an entity update
253
+ * This is called after save/delete operations to push updates
254
+ */
255
+ async notifyEntityUpdate(
256
+ path: string,
257
+ entityId: string,
258
+ entity: Entity | null,
259
+ _databaseId?: string
260
+ ): Promise<void> {
261
+ // Find all subscriptions that might be affected by this update
262
+ for (const [subscriptionId, subscription] of this.subscriptions) {
263
+ if (subscription.type === "entity") {
264
+ const config = subscription.config as EntitySubscriptionConfig;
265
+ if (config.path === path && config.entityId.toString() === entityId) {
266
+ if (subscription.callback) {
267
+ subscription.callback(entity);
268
+ }
269
+ }
270
+ } else if (subscription.type === "collection") {
271
+ const config = subscription.config as CollectionSubscriptionConfig;
272
+ if (config.path === path) {
273
+ // Re-fetch the collection to get updated data
274
+ await this.fetchAndNotifyCollection(subscriptionId, config, subscription.callback);
275
+ }
276
+ }
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Get all active subscriptions (for debugging)
282
+ */
283
+ getSubscriptions(): Map<string, Subscription> {
284
+ return this.subscriptions;
285
+ }
286
+
287
+ /**
288
+ * Close all subscriptions
289
+ */
290
+ async closeAll(): Promise<void> {
291
+ for (const [subscriptionId] of this.subscriptions) {
292
+ this.unsubscribe(subscriptionId);
293
+ }
294
+ }
295
+ }