@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
package/src/factory.ts ADDED
@@ -0,0 +1,274 @@
1
+ /**
2
+ * MongoDB Backend Factory
3
+ *
4
+ * This module provides factory functions for creating MongoDB backend instances.
5
+ * It abstracts the creation of drivers, realtime services, and entity services.
6
+ */
7
+
8
+ import { Db, MongoClient } from "mongodb";
9
+ import { DataDriver, EntityCollection } from "@rebasepro/types";
10
+
11
+ import { MongoEntityService } from "./db/MongoEntityService";
12
+ import { MongoRealtimeService } from "./services/MongoRealtimeService";
13
+ import { MongoDriver } from "./services/MongoDriver";
14
+ import { MongoDBConnection } from "./connection";
15
+ import { BackendConfig, BackendInstance, CollectionRegistryInterface, EntityRepository, RealtimeProvider, DatabaseConnection, DatabaseAdmin, DocumentAdmin, SchemaAdmin, HealthCheckResult } from "@rebasepro/types";
16
+
17
+ /**
18
+ * Configuration for creating a MongoDB backend.
19
+ */
20
+ export interface MongoBackendConfig extends BackendConfig {
21
+ type: "mongodb";
22
+ /** MongoDB database instance */
23
+ connection: Db;
24
+ /** MongoDB client (for connection management) */
25
+ client: MongoClient;
26
+ /** Collections to register (optional, can be registered later) */
27
+ collections?: EntityCollection[];
28
+ }
29
+
30
+ /**
31
+ * MongoDB-specific backend instance with additional MongoDB types.
32
+ */
33
+ export interface MongoBackendInstance extends BackendInstance {
34
+ /** The MongoDB database instance */
35
+ db: Db;
36
+ /** The MongoDB client */
37
+ client: MongoClient;
38
+ /** MongoDB DataDriver for use with Rebase */
39
+ driver: DataDriver;
40
+ /** Entity service for direct database operations */
41
+ entityService: MongoEntityService;
42
+ /** Realtime service for subscriptions */
43
+ realtimeService: MongoRealtimeService;
44
+ /** Admin capabilities (DocumentAdmin + SchemaAdmin) */
45
+ admin: DatabaseAdmin;
46
+ }
47
+
48
+ // =============================================================================
49
+ // Simple Collection Registry
50
+ // =============================================================================
51
+
52
+ /**
53
+ * Simple in-memory collection registry for MongoDB.
54
+ */
55
+ export class MongoCollectionRegistry implements CollectionRegistryInterface {
56
+ private collections = new Map<string, EntityCollection>();
57
+
58
+ /**
59
+ * Register a collection
60
+ */
61
+ register(collection: EntityCollection): void {
62
+ this.collections.set(collection.name, collection);
63
+ }
64
+
65
+ /**
66
+ * Get a collection by its path
67
+ */
68
+ getCollectionByPath(path: string): EntityCollection | undefined {
69
+ return this.collections.get(path);
70
+ }
71
+
72
+ /**
73
+ * Get all registered collections
74
+ */
75
+ getCollections(): EntityCollection[] {
76
+ return Array.from(this.collections.values());
77
+ }
78
+ }
79
+
80
+ // =============================================================================
81
+ // Factory Functions
82
+ // =============================================================================
83
+
84
+ /**
85
+ * Create a complete MongoDB backend instance.
86
+ *
87
+ * This factory function creates all the necessary services for a MongoDB backend:
88
+ * - MongoDBConnection (database connection wrapper)
89
+ * - MongoEntityService (implements EntityRepository)
90
+ * - MongoRealtimeService (implements RealtimeProvider)
91
+ * - MongoCollectionRegistry (implements CollectionRegistryInterface)
92
+ * - MongoDriver (for Rebase integration)
93
+ *
94
+ * @example
95
+ * ```typescript
96
+ * import { createMongoBackend } from "@rebasepro/server-mongodb";
97
+ *
98
+ * const client = new MongoClient("mongodb://localhost:27017");
99
+ * await client.connect();
100
+ * const db = client.db("my_database");
101
+ *
102
+ * const backend = createMongoBackend({
103
+ * type: "mongodb",
104
+ * connection: db,
105
+ * client: client,
106
+ * collections: myCollections
107
+ * });
108
+ *
109
+ * // Use the backend
110
+ * const entities = await backend.entityRepository.fetchCollection("users", {});
111
+ * ```
112
+ */
113
+ export function createMongoBackend(config: MongoBackendConfig): MongoBackendInstance {
114
+ const { connection: db, client, collections } = config;
115
+
116
+ // Create collection registry
117
+ const collectionRegistry = new MongoCollectionRegistry();
118
+
119
+ // Register collections if provided
120
+ if (collections) {
121
+ collections.forEach(collection => collectionRegistry.register(collection));
122
+ }
123
+
124
+ // Create services
125
+ const entityService = new MongoEntityService(db);
126
+ const realtimeService = new MongoRealtimeService(db);
127
+ const driver = new MongoDriver(db, realtimeService);
128
+ const mongoConnection = new MongoDBConnection(db, client);
129
+
130
+ // Build admin capabilities for MongoDB
131
+ const admin: DatabaseAdmin = {
132
+ async executeAggregate(pipeline: Record<string, unknown>[]) {
133
+ // Run aggregation on a collection — requires a target collection
134
+ // from the pipeline's $match or $lookup stage:
135
+ const firstStage = pipeline[0];
136
+ const collName = (firstStage as any)?.$from ?? "__admin__";
137
+ const cursor = db.collection(collName).aggregate(pipeline);
138
+ return await cursor.toArray() as Record<string, unknown>[];
139
+ },
140
+ async fetchCollectionStats(collectionName: string) {
141
+ const stats = await db.command({ collStats: collectionName }) as { count: number; size: number };
142
+ return { count: stats.count, sizeBytes: stats.size };
143
+ },
144
+ async fetchUnmappedTables(mappedPaths?: string[]) {
145
+ const allCollections = await db.listCollections().toArray();
146
+ const names = allCollections.map(c => c.name).filter(n => !n.startsWith("system."));
147
+ if (!mappedPaths || mappedPaths.length === 0) return names;
148
+ const mappedSet = new Set(mappedPaths.map(p => p.toLowerCase()));
149
+ return names.filter(n => !mappedSet.has(n.toLowerCase()));
150
+ },
151
+ async fetchTableMetadata(collectionName: string) {
152
+ // Sample a document to infer fields
153
+ const sample = await db.collection(collectionName).findOne();
154
+ if (!sample) return { columns: [], foreignKeys: [], junctions: [], policies: [] };
155
+ const columns = Object.entries(sample).map(([key, value]) => ({
156
+ column_name: key,
157
+ data_type: typeof value,
158
+ udt_name: typeof value,
159
+ is_nullable: "YES",
160
+ column_default: null,
161
+ character_maximum_length: null,
162
+ }));
163
+ return { columns, foreignKeys: [], junctions: [], policies: [] };
164
+ },
165
+ } satisfies DocumentAdmin & SchemaAdmin;
166
+
167
+ return {
168
+ // Abstract interface implementations
169
+ connection: mongoConnection,
170
+ entityRepository: entityService,
171
+ realtimeProvider: realtimeService,
172
+ collectionRegistry: collectionRegistry,
173
+ admin,
174
+
175
+ // Lifecycle
176
+ async initialize() {
177
+ // Connection is already established via the MongoClient constructor
178
+ },
179
+ async healthCheck(): Promise<HealthCheckResult> {
180
+ const start = Date.now();
181
+ try {
182
+ await db.command({ ping: 1 });
183
+ return { healthy: true, latencyMs: Date.now() - start };
184
+ } catch {
185
+ return { healthy: false, latencyMs: Date.now() - start };
186
+ }
187
+ },
188
+ async destroy() {
189
+ await client.close();
190
+ },
191
+
192
+ // MongoDB-specific accessors
193
+ db,
194
+ client,
195
+ driver,
196
+ entityService,
197
+ realtimeService
198
+ };
199
+ }
200
+
201
+ /**
202
+ * Create a MongoDB DataDriver.
203
+ *
204
+ * This is a convenience function when you only need the DataDriver
205
+ * without the full backend instance.
206
+ *
207
+ * @example
208
+ * ```typescript
209
+ * import { createMongoDelegate } from "@rebasepro/server-mongodb";
210
+ *
211
+ * const delegate = createMongoDelegate(db);
212
+ * ```
213
+ */
214
+ export function createMongoDelegate(
215
+ db: Db,
216
+ realtimeService?: MongoRealtimeService
217
+ ): MongoDriver {
218
+ const realtime = realtimeService ?? new MongoRealtimeService(db);
219
+ return new MongoDriver(db, realtime);
220
+ }
221
+
222
+ /**
223
+ * Create a RealtimeService for MongoDB.
224
+ *
225
+ * @example
226
+ * ```typescript
227
+ * import { createMongoRealtimeService } from "@rebasepro/server-mongodb";
228
+ *
229
+ * const realtimeService = createMongoRealtimeService(db);
230
+ * ```
231
+ */
232
+ export function createMongoRealtimeService(db: Db): MongoRealtimeService {
233
+ return new MongoRealtimeService(db);
234
+ }
235
+
236
+ /**
237
+ * Create a MongoDB entity repository.
238
+ *
239
+ * @example
240
+ * ```typescript
241
+ * import { createMongoEntityRepository } from "@rebasepro/server-mongodb";
242
+ *
243
+ * const repository = createMongoEntityRepository(db);
244
+ * const users = await repository.fetchCollection("users", {});
245
+ * ```
246
+ */
247
+ export function createMongoEntityRepository(db: Db): EntityRepository {
248
+ return new MongoEntityService(db);
249
+ }
250
+
251
+ // =============================================================================
252
+ // Type Guards
253
+ // =============================================================================
254
+
255
+ /**
256
+ * Check if a backend config is for MongoDB.
257
+ */
258
+ export function isMongoBackendConfig(config: BackendConfig): config is MongoBackendConfig {
259
+ return config.type === "mongodb" &&
260
+ typeof (config as MongoBackendConfig).connection !== "undefined" &&
261
+ typeof (config as MongoBackendConfig).client !== "undefined";
262
+ }
263
+
264
+ /**
265
+ * Check if a driver config is for MongoDB.
266
+ */
267
+ export function isMongoDriverConfig(obj: unknown): obj is { type: "mongodb"; connection: Db; client: MongoClient } {
268
+ return typeof obj === "object" &&
269
+ obj !== null &&
270
+ "type" in obj &&
271
+ (obj as Record<string, unknown>).type === "mongodb" &&
272
+ "connection" in obj &&
273
+ "client" in obj;
274
+ }
package/src/index.ts ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @rebasepro/server-mongodb
3
+ *
4
+ * MongoDB backend implementation for Rebase
5
+ * This package provides a complete backend solution for Rebase applications
6
+ * using MongoDB as the database.
7
+ *
8
+ * The package implements the abstract interfaces from @rebasepro/server-core
9
+ * (EntityRepository, RealtimeProvider, CollectionRegistryInterface, etc.)
10
+ */
11
+
12
+ // Connection
13
+ export * from "./connection";
14
+
15
+ // Factory functions
16
+ export * from "./factory";
17
+
18
+ // Database services
19
+ export * from "./db/MongoEntityService";
20
+ export * from "./db/MongoConditionBuilder";
21
+
22
+ // Services
23
+ export * from "./services/MongoRealtimeService";
24
+ export * from "./services/MongoDriver";
@@ -0,0 +1,267 @@
1
+ /**
2
+ * MongoDB DataDriver Delegate
3
+ *
4
+ * Implements the DataDriver interface for Rebase frontend integration.
5
+ * This is the main entry point for Rebase to interact with MongoDB.
6
+ */
7
+
8
+ import { Db, ObjectId } from "mongodb";
9
+ import {
10
+ DataDriver,
11
+ DeleteEntityProps,
12
+ Entity,
13
+ EntityCollection,
14
+ FetchCollectionProps,
15
+ FetchEntityProps,
16
+ ListenCollectionProps,
17
+ ListenEntityProps,
18
+ SaveEntityProps
19
+ } from "@rebasepro/types";
20
+ import { MongoEntityService } from "../db/MongoEntityService";
21
+ import { MongoRealtimeService } from "./MongoRealtimeService";
22
+
23
+ /**
24
+ * MongoDB DataDriver Delegate
25
+ *
26
+ * Implements the DataDriver interface for Rebase.
27
+ * Provides all data operations needed by the Rebase frontend.
28
+ */
29
+ export class MongoDriver implements DataDriver {
30
+ key = "mongodb";
31
+ initialised = true;
32
+
33
+ private entityService: MongoEntityService;
34
+ private realtimeService: MongoRealtimeService;
35
+
36
+
37
+ constructor(
38
+ private db: Db,
39
+ realtimeService?: MongoRealtimeService
40
+ ) {
41
+ this.entityService = new MongoEntityService(db);
42
+ this.realtimeService = realtimeService ?? new MongoRealtimeService(db);
43
+ }
44
+
45
+
46
+
47
+ /**
48
+ * Get the current timestamp
49
+ */
50
+ currentTime(): Date {
51
+ return new Date();
52
+ }
53
+
54
+ /**
55
+ * Fetch a collection of entities
56
+ */
57
+ async fetchCollection<M extends Record<string, any>>({
58
+ path,
59
+ collection,
60
+ filter,
61
+ limit,
62
+ startAfter,
63
+ orderBy,
64
+ searchString,
65
+ order
66
+ }: FetchCollectionProps<M>): Promise<Entity<M>[]> {
67
+ return this.entityService.fetchCollection<M>(path, {
68
+ filter,
69
+ limit,
70
+ startAfter,
71
+ orderBy,
72
+ order,
73
+ searchString,
74
+ collection: collection as unknown as EntityCollection
75
+ });
76
+ }
77
+
78
+ /**
79
+ * Listen to collection changes
80
+ */
81
+ listenCollection<M extends Record<string, any>>({
82
+ path,
83
+ collection,
84
+ filter,
85
+ limit,
86
+ startAfter,
87
+ orderBy,
88
+ searchString,
89
+ order,
90
+ onUpdate,
91
+ onError
92
+ }: ListenCollectionProps<M>): () => void {
93
+ const subscriptionId = this.generateSubscriptionId();
94
+
95
+ const callback = (entities: Entity<any>[]) => {
96
+ try {
97
+ onUpdate(entities as Entity<M>[]);
98
+ } catch (error) {
99
+ console.error("Error in collection update callback:", error);
100
+ if (onError) {
101
+ onError(error instanceof Error ? error : new Error(String(error)));
102
+ }
103
+ }
104
+ };
105
+
106
+ this.realtimeService.subscribeToCollection(
107
+ subscriptionId,
108
+ {
109
+ clientId: "driver",
110
+ path,
111
+ filter,
112
+ orderBy,
113
+ order,
114
+ limit,
115
+ startAfter,
116
+ searchString
117
+ },
118
+ callback
119
+ );
120
+
121
+ // Return unsubscribe function
122
+ return () => {
123
+ this.realtimeService.unsubscribe(subscriptionId);
124
+ };
125
+ }
126
+
127
+ /**
128
+ * Fetch a single entity
129
+ */
130
+ async fetchEntity<M extends Record<string, any>>({
131
+ path,
132
+ entityId,
133
+ databaseId,
134
+ collection
135
+ }: FetchEntityProps<M>): Promise<Entity<M> | undefined> {
136
+ return this.entityService.fetchEntity<M>(path, entityId, databaseId);
137
+ }
138
+
139
+ /**
140
+ * Listen to entity changes
141
+ */
142
+ listenEntity<M extends Record<string, any>>({
143
+ path,
144
+ entityId,
145
+ collection,
146
+ onUpdate,
147
+ onError
148
+ }: ListenEntityProps<M>): () => void {
149
+ const subscriptionId = this.generateSubscriptionId();
150
+
151
+ const callback = (entity: Entity<any> | null) => {
152
+ try {
153
+ onUpdate(entity as Entity<M>);
154
+ } catch (error) {
155
+ console.error("Error in entity update callback:", error);
156
+ if (onError) {
157
+ onError(error instanceof Error ? error : new Error(String(error)));
158
+ }
159
+ }
160
+ };
161
+
162
+ this.realtimeService.subscribeToEntity(
163
+ subscriptionId,
164
+ {
165
+ clientId: "driver",
166
+ path,
167
+ entityId
168
+ },
169
+ callback
170
+ );
171
+
172
+ // Return unsubscribe function
173
+ return () => {
174
+ this.realtimeService.unsubscribe(subscriptionId);
175
+ };
176
+ }
177
+
178
+ /**
179
+ * Save an entity (create or update)
180
+ */
181
+ async saveEntity<M extends Record<string, any>>({
182
+ path,
183
+ entityId,
184
+ values,
185
+ collection,
186
+ status
187
+ }: SaveEntityProps<M>): Promise<Entity<M>> {
188
+ const entity = await this.entityService.saveEntity<M>(path, values, entityId);
189
+
190
+ // Notify subscribers of the update
191
+ await this.realtimeService.notifyEntityUpdate(path, String(entity.id), entity);
192
+
193
+ return entity;
194
+ }
195
+
196
+ /**
197
+ * Delete an entity
198
+ */
199
+ async deleteEntity<M extends Record<string, any>>({
200
+ entity,
201
+ collection
202
+ }: DeleteEntityProps<M>): Promise<void> {
203
+ await this.entityService.deleteEntity(entity.path, entity.id);
204
+
205
+ // Notify subscribers of the deletion
206
+ await this.realtimeService.notifyEntityUpdate(entity.path, String(entity.id), null);
207
+ }
208
+
209
+ /**
210
+ * Check if a field value is unique
211
+ */
212
+ async checkUniqueField(
213
+ path: string,
214
+ name: string,
215
+ value: any,
216
+ entityId?: string,
217
+ collection?: EntityCollection
218
+ ): Promise<boolean> {
219
+ return this.entityService.checkUniqueField(path, name, value, entityId);
220
+ }
221
+
222
+ /**
223
+ * Generate a new entity ID
224
+ */
225
+ generateEntityId(path: string, collection?: EntityCollection): string {
226
+ return this.entityService.generateEntityId();
227
+ }
228
+
229
+ /**
230
+ * Count entities in a collection
231
+ */
232
+ async countEntities<M extends Record<string, any>>({
233
+ path,
234
+ collection,
235
+ filter
236
+ }: FetchCollectionProps<M>): Promise<number> {
237
+ return this.entityService.countEntities<M>(path, { filter });
238
+ }
239
+
240
+ /**
241
+ * Generate a unique subscription ID
242
+ */
243
+ private generateSubscriptionId(): string {
244
+ return `mongo_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
245
+ }
246
+
247
+ /**
248
+ * Check if the delegate is ready
249
+ */
250
+ isReady(): boolean {
251
+ return this.initialised;
252
+ }
253
+
254
+ /**
255
+ * Get the underlying entity service for direct access
256
+ */
257
+ getEntityService(): MongoEntityService {
258
+ return this.entityService;
259
+ }
260
+
261
+ /**
262
+ * Get the underlying realtime service for direct access
263
+ */
264
+ getRealtimeService(): MongoRealtimeService {
265
+ return this.realtimeService;
266
+ }
267
+ }