@rebasepro/server-mongo 0.0.1-canary.4829d6e

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 (52) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +86 -0
  3. package/dist/MongoBootstrapper.d.ts +18 -0
  4. package/dist/MongoBootstrapper.d.ts.map +1 -0
  5. package/dist/auth/ensure-collections.d.ts +3 -0
  6. package/dist/auth/ensure-collections.d.ts.map +1 -0
  7. package/dist/auth/services.d.ts +156 -0
  8. package/dist/auth/services.d.ts.map +1 -0
  9. package/dist/connection.d.ts +35 -0
  10. package/dist/connection.d.ts.map +1 -0
  11. package/dist/db/MongoConditionBuilder.d.ts +64 -0
  12. package/dist/db/MongoConditionBuilder.d.ts.map +1 -0
  13. package/dist/db/MongoDataService.d.ts +101 -0
  14. package/dist/db/MongoDataService.d.ts.map +1 -0
  15. package/dist/ensure-collections-Bkx_O5CQ.js +94 -0
  16. package/dist/ensure-collections-Bkx_O5CQ.js.map +1 -0
  17. package/dist/ensure-history-collection-yajOt2dv.js +21 -0
  18. package/dist/ensure-history-collection-yajOt2dv.js.map +1 -0
  19. package/dist/factory.d.ts +151 -0
  20. package/dist/factory.d.ts.map +1 -0
  21. package/dist/history/ensure-history-collection.d.ts +3 -0
  22. package/dist/history/ensure-history-collection.d.ts.map +1 -0
  23. package/dist/index.d.ts +18 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.es.js +2508 -0
  26. package/dist/index.es.js.map +1 -0
  27. package/dist/index.umd.js +2925 -0
  28. package/dist/index.umd.js.map +1 -0
  29. package/dist/services/MongoDriver.d.ts +125 -0
  30. package/dist/services/MongoDriver.d.ts.map +1 -0
  31. package/dist/services/MongoHistoryService.d.ts +37 -0
  32. package/dist/services/MongoHistoryService.d.ts.map +1 -0
  33. package/dist/services/MongoRealtimeService.d.ts +103 -0
  34. package/dist/services/MongoRealtimeService.d.ts.map +1 -0
  35. package/dist/websocket-DQlwCHFq.js +281 -0
  36. package/dist/websocket-DQlwCHFq.js.map +1 -0
  37. package/dist/websocket.d.ts +7 -0
  38. package/dist/websocket.d.ts.map +1 -0
  39. package/package.json +81 -0
  40. package/src/MongoBootstrapper.ts +196 -0
  41. package/src/auth/ensure-collections.ts +105 -0
  42. package/src/auth/services.ts +732 -0
  43. package/src/connection.ts +60 -0
  44. package/src/db/MongoConditionBuilder.ts +224 -0
  45. package/src/db/MongoDataService.ts +368 -0
  46. package/src/factory.ts +305 -0
  47. package/src/history/ensure-history-collection.ts +22 -0
  48. package/src/index.ts +25 -0
  49. package/src/services/MongoDriver.ts +1120 -0
  50. package/src/services/MongoHistoryService.ts +181 -0
  51. package/src/services/MongoRealtimeService.ts +446 -0
  52. package/src/websocket.ts +297 -0
package/src/factory.ts ADDED
@@ -0,0 +1,305 @@
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 row services.
6
+ */
7
+
8
+ import { Db, MongoClient } from "mongodb";
9
+ import { DataDriver, CollectionConfig } from "@rebasepro/types";
10
+
11
+ import { MongoDataService } from "./db/MongoDataService";
12
+ import { MongoRealtimeService } from "./services/MongoRealtimeService";
13
+ import { MongoDriver } from "./services/MongoDriver";
14
+ import { MongoHistoryService, HistoryRetentionConfig } from "./services/MongoHistoryService";
15
+ import { MongoDBConnection } from "./connection";
16
+ import { BackendConfig, BackendInstance, CollectionRegistryInterface, DataRepository, RealtimeProvider, DatabaseConnection, DatabaseAdmin, DocumentAdmin, SchemaAdmin, HealthCheckResult } from "@rebasepro/types";
17
+
18
+ /**
19
+ * Configuration for creating a MongoDB backend.
20
+ */
21
+ export interface MongoBackendConfig extends BackendConfig {
22
+ type: "mongodb";
23
+ /** MongoDB database instance */
24
+ connection: Db;
25
+ /** MongoDB client (for connection management) */
26
+ client: MongoClient;
27
+ /** Collections to register (optional, can be registered later) */
28
+ collections?: CollectionConfig[];
29
+ /** History retention configuration */
30
+ historyRetention?: Partial<HistoryRetentionConfig>;
31
+ }
32
+
33
+ /**
34
+ * MongoDB-specific backend instance with additional MongoDB types.
35
+ */
36
+ export interface MongoBackendInstance extends BackendInstance {
37
+ /** The MongoDB database instance */
38
+ db: Db;
39
+ /** The MongoDB client */
40
+ client: MongoClient;
41
+ /** MongoDB DataDriver for use with Rebase */
42
+ driver: DataDriver;
43
+ /** Entity service for direct database operations */
44
+ dataService: MongoDataService;
45
+ /** Realtime service for subscriptions */
46
+ realtimeService: MongoRealtimeService;
47
+ /** Admin capabilities (DocumentAdmin + SchemaAdmin) */
48
+ admin: DatabaseAdmin;
49
+ }
50
+
51
+ // =============================================================================
52
+ // Simple Collection Registry
53
+ // =============================================================================
54
+
55
+ /**
56
+ * Simple in-memory collection registry for MongoDB.
57
+ */
58
+ export class MongoCollectionRegistry implements CollectionRegistryInterface {
59
+ private collections = new Map<string, CollectionConfig>();
60
+ private _globalCallbacks?: any;
61
+
62
+ /**
63
+ * Register a collection
64
+ */
65
+ register(collection: CollectionConfig): void {
66
+ this.collections.set(collection.name, collection);
67
+ }
68
+
69
+ /**
70
+ * Get a collection by its path
71
+ */
72
+ getCollectionByPath(path: string): CollectionConfig | undefined {
73
+ return this.collections.get(path);
74
+ }
75
+
76
+ /**
77
+ * Get all registered collections
78
+ */
79
+ getCollections(): CollectionConfig[] {
80
+ return Array.from(this.collections.values());
81
+ }
82
+
83
+ /**
84
+ * Get the currently registered global callbacks, if any.
85
+ */
86
+ getGlobalCallbacks(): any | undefined {
87
+ return this._globalCallbacks;
88
+ }
89
+
90
+ /**
91
+ * Set global lifecycle callbacks that apply to every collection.
92
+ */
93
+ setGlobalCallbacks(callbacks: any): void {
94
+ this._globalCallbacks = callbacks;
95
+ }
96
+ }
97
+
98
+ // =============================================================================
99
+ // Factory Functions
100
+ // =============================================================================
101
+
102
+ /**
103
+ * Create a complete MongoDB backend instance.
104
+ *
105
+ * This factory function creates all the necessary services for a MongoDB backend:
106
+ * - MongoDBConnection (database connection wrapper)
107
+ * - MongoDataService (implements DataRepository)
108
+ * - MongoRealtimeService (implements RealtimeProvider)
109
+ * - MongoCollectionRegistry (implements CollectionRegistryInterface)
110
+ * - MongoDriver (for Rebase integration)
111
+ *
112
+ * @example
113
+ * ```typescript
114
+ * import { createMongoBackend } from "@rebasepro/server-mongo";
115
+ *
116
+ * const client = new MongoClient("mongodb://localhost:27017");
117
+ * await client.connect();
118
+ * const db = client.db("my_database");
119
+ *
120
+ * const backend = createMongoBackend({
121
+ * type: "mongodb",
122
+ * connection: db,
123
+ * client: client,
124
+ * collections: myCollections
125
+ * });
126
+ *
127
+ * // Use the backend
128
+ * const rows = await backend.entityRepository.fetchCollection("users", {});
129
+ * ```
130
+ */
131
+ export function createMongoBackend(config: MongoBackendConfig): MongoBackendInstance {
132
+ const { connection: db, client, collections } = config;
133
+
134
+ // Create collection registry
135
+ const collectionRegistry = new MongoCollectionRegistry();
136
+
137
+ // Register collections if provided
138
+ if (collections) {
139
+ collections.forEach(collection => collectionRegistry.register(collection));
140
+ }
141
+
142
+ // Create services
143
+ const dataService = new MongoDataService(db);
144
+ const realtimeService = new MongoRealtimeService(db);
145
+ const historyService = new MongoHistoryService(db, config.historyRetention);
146
+ const driver = new MongoDriver(db, realtimeService, historyService, collectionRegistry);
147
+ const mongoConnection = new MongoDBConnection(db, client);
148
+
149
+ // Build admin capabilities for MongoDB
150
+ const admin: DatabaseAdmin = {
151
+ async executeAggregate(pipeline: Record<string, unknown>[]) {
152
+ // Run aggregation on a collection — requires a target collection
153
+ // from the pipeline's $match or $lookup stage:
154
+ const firstStage = pipeline[0];
155
+ const collName = typeof firstStage.$from === "string" ? firstStage.$from : "__admin__";
156
+ const cursor = db.collection(collName).aggregate(pipeline);
157
+ return await cursor.toArray() as Record<string, unknown>[];
158
+ },
159
+ async fetchCollectionStats(collectionName: string) {
160
+ const stats = await db.command({ collStats: collectionName }) as { count: number; size: number };
161
+ return { count: stats.count,
162
+ sizeBytes: stats.size };
163
+ },
164
+ async fetchUnmappedTables(mappedPaths?: string[]) {
165
+ const allCollections = await db.listCollections().toArray();
166
+ const names = allCollections.map(c => c.name).filter(n => !n.startsWith("system."));
167
+ if (!mappedPaths || mappedPaths.length === 0) return names;
168
+ const mappedSet = new Set(mappedPaths.map(p => p.toLowerCase()));
169
+ return names.filter(n => !mappedSet.has(n.toLowerCase()));
170
+ },
171
+ async fetchTableMetadata(collectionName: string) {
172
+ // Sample a document to infer fields
173
+ const sample = await db.collection(collectionName).findOne();
174
+ if (!sample) return { columns: [],
175
+ foreignKeys: [],
176
+ junctions: [],
177
+ policies: [] };
178
+ const columns = Object.entries(sample).map(([key, value]) => ({
179
+ column_name: key,
180
+ data_type: typeof value,
181
+ udt_name: typeof value,
182
+ is_nullable: "YES",
183
+ column_default: null,
184
+ character_maximum_length: null
185
+ }));
186
+ return { columns,
187
+ foreignKeys: [],
188
+ junctions: [],
189
+ policies: [] };
190
+ }
191
+ } satisfies DocumentAdmin & SchemaAdmin;
192
+
193
+ return {
194
+ // Abstract interface implementations
195
+ connection: mongoConnection,
196
+ entityRepository: dataService,
197
+ realtimeProvider: realtimeService,
198
+ collectionRegistry: collectionRegistry,
199
+ admin,
200
+
201
+ // Lifecycle
202
+ async initialize() {
203
+ // Connection is already established via the MongoClient constructor
204
+ },
205
+ async healthCheck(): Promise<HealthCheckResult> {
206
+ const start = Date.now();
207
+ try {
208
+ await db.command({ ping: 1 });
209
+ return { healthy: true,
210
+ latencyMs: Date.now() - start };
211
+ } catch {
212
+ return { healthy: false,
213
+ latencyMs: Date.now() - start };
214
+ }
215
+ },
216
+ async destroy() {
217
+ await client.close();
218
+ },
219
+
220
+ // MongoDB-specific accessors
221
+ db,
222
+ client,
223
+ driver,
224
+ dataService,
225
+ realtimeService
226
+ };
227
+ }
228
+
229
+ /**
230
+ * Create a MongoDB DataDriver.
231
+ *
232
+ * This is a convenience function when you only need the DataDriver
233
+ * without the full backend instance.
234
+ *
235
+ * @example
236
+ * ```typescript
237
+ * import { createMongoDelegate } from "@rebasepro/server-mongo";
238
+ *
239
+ * const delegate = createMongoDelegate(db);
240
+ * ```
241
+ */
242
+ export function createMongoDelegate(
243
+ db: Db,
244
+ realtimeService?: MongoRealtimeService,
245
+ historyService?: MongoHistoryService,
246
+ registry?: CollectionRegistryInterface
247
+ ): MongoDriver {
248
+ const realtime = realtimeService ?? new MongoRealtimeService(db);
249
+ const history = historyService ?? new MongoHistoryService(db);
250
+ return new MongoDriver(db, realtime, history, registry);
251
+ }
252
+
253
+ /**
254
+ * Create a RealtimeService for MongoDB.
255
+ *
256
+ * @example
257
+ * ```typescript
258
+ * import { createMongoRealtimeService } from "@rebasepro/server-mongo";
259
+ *
260
+ * const realtimeService = createMongoRealtimeService(db);
261
+ * ```
262
+ */
263
+ export function createMongoRealtimeService(db: Db): MongoRealtimeService {
264
+ return new MongoRealtimeService(db);
265
+ }
266
+
267
+ /**
268
+ * Create a MongoDB row repository.
269
+ *
270
+ * @example
271
+ * ```typescript
272
+ * import { createMongoEntityRepository } from "@rebasepro/server-mongo";
273
+ *
274
+ * const repository = createMongoEntityRepository(db);
275
+ * const users = await repository.fetchCollection("users", {});
276
+ * ```
277
+ */
278
+ export function createMongoEntityRepository(db: Db): DataRepository {
279
+ return new MongoDataService(db);
280
+ }
281
+
282
+ // =============================================================================
283
+ // Type Guards
284
+ // =============================================================================
285
+
286
+ /**
287
+ * Check if a backend config is for MongoDB.
288
+ */
289
+ export function isMongoBackendConfig(config: BackendConfig): config is MongoBackendConfig {
290
+ return config.type === "mongodb" &&
291
+ typeof (config as MongoBackendConfig).connection !== "undefined" &&
292
+ typeof (config as MongoBackendConfig).client !== "undefined";
293
+ }
294
+
295
+ /**
296
+ * Check if a driver config is for MongoDB.
297
+ */
298
+ export function isMongoDriverConfig(obj: unknown): obj is { type: "mongodb"; connection: Db; client: MongoClient } {
299
+ return typeof obj === "object" &&
300
+ obj !== null &&
301
+ "type" in obj &&
302
+ (obj as Record<string, unknown>).type === "mongodb" &&
303
+ "connection" in obj &&
304
+ "client" in obj;
305
+ }
@@ -0,0 +1,22 @@
1
+ import { Db } from "mongodb";
2
+ import { logger } from "@rebasepro/server";
3
+
4
+ export async function ensureHistoryCollectionExists(db: Db): Promise<void> {
5
+ logger.info("🔍 Checking MongoDB history collection and indexes...");
6
+
7
+ try {
8
+ const history = db.collection("__rebase_history");
9
+
10
+ // Index for finding history entries for a specific row
11
+ await history.createIndex({ entity_id: 1,
12
+ table_name: 1,
13
+ updated_at: -1 });
14
+
15
+ // Index for pruning by date
16
+ await history.createIndex({ updated_at: 1 });
17
+
18
+ logger.info("✅ MongoDB History collection ready");
19
+ } catch (error) {
20
+ logger.error("❌ Failed to set up MongoDB history collection", { error: error });
21
+ }
22
+ }
package/src/index.ts ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @rebasepro/server-mongo
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
9
+ * (DataRepository, 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/MongoDataService";
20
+ export * from "./db/MongoConditionBuilder";
21
+
22
+ // Services
23
+ export * from "./services/MongoRealtimeService";
24
+ export * from "./services/MongoDriver";
25
+ export * from "./MongoBootstrapper";