@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.
- package/LICENSE +6 -0
- package/dist/index.es.js +875 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.umd.js +878 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/server-mongodb/src/connection.d.ts +35 -0
- package/dist/server-mongodb/src/connection.d.ts.map +1 -0
- package/dist/server-mongodb/src/db/MongoConditionBuilder.d.ts +64 -0
- package/dist/server-mongodb/src/db/MongoConditionBuilder.d.ts.map +1 -0
- package/dist/server-mongodb/src/db/MongoEntityService.d.ts +98 -0
- package/dist/server-mongodb/src/db/MongoEntityService.d.ts.map +1 -0
- package/dist/server-mongodb/src/factory.d.ts +139 -0
- package/dist/server-mongodb/src/factory.d.ts.map +1 -0
- package/dist/server-mongodb/src/index.d.ts +17 -0
- package/dist/server-mongodb/src/index.d.ts.map +1 -0
- package/dist/server-mongodb/src/services/MongoDriver.d.ts +81 -0
- package/dist/server-mongodb/src/services/MongoDriver.d.ts.map +1 -0
- package/dist/server-mongodb/src/services/MongoRealtimeService.d.ts +65 -0
- package/dist/server-mongodb/src/services/MongoRealtimeService.d.ts.map +1 -0
- package/dist/server-mongodb/src/useMongoDriver.d.ts +18 -0
- package/dist/server-mongodb/src/useMongoDriver.d.ts.map +1 -0
- package/dist/server-mongodb/src/utils.d.ts +10 -0
- package/dist/server-mongodb/src/utils.d.ts.map +1 -0
- package/dist/types/src/controllers/analytics_controller.d.ts +8 -0
- package/dist/types/src/controllers/analytics_controller.d.ts.map +1 -0
- package/dist/types/src/controllers/auth.d.ts +118 -0
- package/dist/types/src/controllers/auth.d.ts.map +1 -0
- package/dist/types/src/controllers/client.d.ts +59 -0
- package/dist/types/src/controllers/client.d.ts.map +1 -0
- package/dist/types/src/controllers/collection_registry.d.ts +45 -0
- package/dist/types/src/controllers/collection_registry.d.ts.map +1 -0
- package/dist/types/src/controllers/customization_controller.d.ts +55 -0
- package/dist/types/src/controllers/customization_controller.d.ts.map +1 -0
- package/dist/types/src/controllers/data.d.ts +142 -0
- package/dist/types/src/controllers/data.d.ts.map +1 -0
- package/dist/types/src/controllers/data_driver.d.ts +169 -0
- package/dist/types/src/controllers/data_driver.d.ts.map +1 -0
- package/dist/types/src/controllers/database_admin.d.ts +12 -0
- package/dist/types/src/controllers/database_admin.d.ts.map +1 -0
- package/dist/types/src/controllers/dialogs_controller.d.ts +37 -0
- package/dist/types/src/controllers/dialogs_controller.d.ts.map +1 -0
- package/dist/types/src/controllers/effective_role.d.ts +5 -0
- package/dist/types/src/controllers/effective_role.d.ts.map +1 -0
- package/dist/types/src/controllers/index.d.ts +18 -0
- package/dist/types/src/controllers/index.d.ts.map +1 -0
- package/dist/types/src/controllers/local_config_persistence.d.ts +21 -0
- package/dist/types/src/controllers/local_config_persistence.d.ts.map +1 -0
- package/dist/types/src/controllers/navigation.d.ts +214 -0
- package/dist/types/src/controllers/navigation.d.ts.map +1 -0
- package/dist/types/src/controllers/registry.d.ts +52 -0
- package/dist/types/src/controllers/registry.d.ts.map +1 -0
- package/dist/types/src/controllers/side_dialogs_controller.d.ts +68 -0
- package/dist/types/src/controllers/side_dialogs_controller.d.ts.map +1 -0
- package/dist/types/src/controllers/side_entity_controller.d.ts +90 -0
- package/dist/types/src/controllers/side_entity_controller.d.ts.map +1 -0
- package/dist/types/src/controllers/snackbar.d.ts +25 -0
- package/dist/types/src/controllers/snackbar.d.ts.map +1 -0
- package/dist/types/src/controllers/storage.d.ts +174 -0
- package/dist/types/src/controllers/storage.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +5 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/rebase_context.d.ts +102 -0
- package/dist/types/src/rebase_context.d.ts.map +1 -0
- package/dist/types/src/types/backend.d.ts +534 -0
- package/dist/types/src/types/backend.d.ts.map +1 -0
- package/dist/types/src/types/builders.d.ts +15 -0
- package/dist/types/src/types/builders.d.ts.map +1 -0
- package/dist/types/src/types/chips.d.ts +6 -0
- package/dist/types/src/types/chips.d.ts.map +1 -0
- package/dist/types/src/types/collections.d.ts +813 -0
- package/dist/types/src/types/collections.d.ts.map +1 -0
- package/dist/types/src/types/data_source.d.ts +65 -0
- package/dist/types/src/types/data_source.d.ts.map +1 -0
- package/dist/types/src/types/entities.d.ts +146 -0
- package/dist/types/src/types/entities.d.ts.map +1 -0
- package/dist/types/src/types/entity_actions.d.ts +99 -0
- package/dist/types/src/types/entity_actions.d.ts.map +1 -0
- package/dist/types/src/types/entity_callbacks.d.ts +174 -0
- package/dist/types/src/types/entity_callbacks.d.ts.map +1 -0
- package/dist/types/src/types/entity_link_builder.d.ts +8 -0
- package/dist/types/src/types/entity_link_builder.d.ts.map +1 -0
- package/dist/types/src/types/entity_overrides.d.ts +10 -0
- package/dist/types/src/types/entity_overrides.d.ts.map +1 -0
- package/dist/types/src/types/entity_views.d.ts +62 -0
- package/dist/types/src/types/entity_views.d.ts.map +1 -0
- package/dist/types/src/types/export_import.d.ts +22 -0
- package/dist/types/src/types/export_import.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +23 -0
- package/dist/types/src/types/index.d.ts.map +1 -0
- package/dist/types/src/types/locales.d.ts +5 -0
- package/dist/types/src/types/locales.d.ts.map +1 -0
- package/dist/types/src/types/modify_collections.d.ts +6 -0
- package/dist/types/src/types/modify_collections.d.ts.map +1 -0
- package/dist/types/src/types/plugins.d.ts +226 -0
- package/dist/types/src/types/plugins.d.ts.map +1 -0
- package/dist/types/src/types/properties.d.ts +1092 -0
- package/dist/types/src/types/properties.d.ts.map +1 -0
- package/dist/types/src/types/property_config.d.ts +71 -0
- package/dist/types/src/types/property_config.d.ts.map +1 -0
- package/dist/types/src/types/relations.d.ts +337 -0
- package/dist/types/src/types/relations.d.ts.map +1 -0
- package/dist/types/src/types/slots.d.ts +229 -0
- package/dist/types/src/types/slots.d.ts.map +1 -0
- package/dist/types/src/types/translations.d.ts +827 -0
- package/dist/types/src/types/translations.d.ts.map +1 -0
- package/dist/types/src/types/user_management_delegate.d.ts +121 -0
- package/dist/types/src/types/user_management_delegate.d.ts.map +1 -0
- package/dist/types/src/types/websockets.d.ts +79 -0
- package/dist/types/src/types/websockets.d.ts.map +1 -0
- package/dist/types/src/users/index.d.ts +3 -0
- package/dist/types/src/users/index.d.ts.map +1 -0
- package/dist/types/src/users/roles.d.ts +23 -0
- package/dist/types/src/users/roles.d.ts.map +1 -0
- package/dist/types/src/users/user.d.ts +47 -0
- package/dist/types/src/users/user.d.ts.map +1 -0
- package/package.json +81 -0
- package/src/connection.ts +60 -0
- package/src/db/MongoConditionBuilder.ts +181 -0
- package/src/db/MongoEntityService.ts +350 -0
- package/src/factory.ts +274 -0
- package/src/index.ts +24 -0
- package/src/services/MongoDriver.ts +267 -0
- package/src/services/MongoRealtimeService.ts +295 -0
- package/src/useMongoDriver.ts +516 -0
- 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
|
+
}
|