@rebasepro/server-mongo 0.14.0 → 0.14.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.es.js","names":[],"sources":["../src/connection.ts","../src/db/MongoConditionBuilder.ts","../src/db/MongoDataService.ts","../src/services/MongoRealtimeService.ts","../src/services/MongoHistoryService.ts","../src/db/securityRuleFilter.ts","../src/services/MongoDriver.ts","../src/factory.ts","../src/auth/services.ts","../src/MongoBootstrapper.ts"],"sourcesContent":["/**\n * MongoDB Connection\n *\n * Wraps MongoDB connection to implement the DatabaseConnection interface.\n */\n\nimport { Db, MongoClient } from \"mongodb\";\nimport { DatabaseConnection } from \"@rebasepro/types\";\n\n/**\n * MongoDB database connection wrapper that implements DatabaseConnection interface.\n */\nexport class MongoDBConnection implements DatabaseConnection {\n readonly type = \"mongodb\";\n\n constructor(\n public readonly db: Db,\n public readonly client: MongoClient\n ) { }\n\n get isConnected(): boolean {\n // MongoClient doesn't have a direct isConnected property in v6+\n // We check if the client topology is connected\n try {\n const clientInternal = this.client as unknown as Record<string, { isConnected?: () => boolean } | undefined>;\n return clientInternal.topology?.isConnected?.() ?? false;\n } catch {\n return false;\n }\n }\n\n async close(): Promise<void> {\n await this.client.close();\n }\n}\n\n/**\n * Create a MongoDB database connection from a connection string.\n *\n * @param connectionString - MongoDB connection string (e.g., mongodb://localhost:27017)\n * @param databaseName - Name of the database to use\n * @returns Promise resolving to MongoDBConnection\n *\n * @example\n * ```typescript\n * const connection = await createMongoDBConnection(\n * \"mongodb://localhost:27017\",\n * \"my_database\"\n * );\n * ```\n */\nexport async function createMongoDBConnection(\n connectionString: string,\n databaseName: string\n): Promise<MongoDBConnection> {\n const client = new MongoClient(connectionString);\n await client.connect();\n const db = client.db(databaseName);\n return new MongoDBConnection(db, client);\n}\n","/**\n * MongoDB Condition Builder\n *\n * Translates Rebase filter conditions to MongoDB query operators.\n */\n\nimport { CollectionConfig, FilterCondition, FilterValues, LogicalCondition, WhereFilterOp } from \"@rebasepro/types\";\nimport { toFilterTuples } from \"@rebasepro/common\";\nimport { Filter, Document } from \"mongodb\";\nimport { ApiError, logger } from \"@rebasepro/server\";\n\n/**\n * Mapping from Rebase filter operators to MongoDB query operators\n */\nconst REBASE_TO_MONGO_OP: Partial<Record<WhereFilterOp, string>> = {\n \"<\": \"$lt\",\n \"<=\": \"$lte\",\n \"==\": \"$eq\",\n \"!=\": \"$ne\",\n \">=\": \"$gte\",\n \">\": \"$gt\",\n \"array-contains\": \"$elemMatch\",\n \"array-contains-any\": \"$in\",\n \"in\": \"$in\",\n \"not-in\": \"$nin\"\n};\n\nfunction escapeRegExp(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Translate a SQL LIKE/ILIKE pattern into an anchored regular expression.\n * `%` matches any sequence of characters, `_` matches a single character;\n * every other character is matched literally.\n */\nfunction likePatternToRegExp(pattern: string, caseInsensitive: boolean): RegExp {\n let body = \"\";\n // Runs of `%` collapse to one. `%%%%X` means exactly what `%X` means, but as\n // a regular expression it is four adjacent unbounded quantifiers, and on a\n // subject that does not match, the engine tries every way of splitting the\n // subject between them — exponential time. The pattern comes from a public\n // filter operator over HTTP (`?title=like.%25%25%25…`), and this expression\n // is handed to MongoDB as `$regex`, so the time is spent on a database\n // thread rather than the caller's.\n let lastWasWildcard = false;\n for (const ch of String(pattern)) {\n if (ch === \"%\") {\n if (!lastWasWildcard) body += \".*\";\n lastWasWildcard = true;\n continue;\n }\n body += ch === \"_\" ? \".\" : escapeRegExp(ch);\n lastWasWildcard = false;\n }\n return new RegExp(`^${body}$`, caseInsensitive ? \"i\" : \"\");\n}\n\n/**\n * MongoDB Condition Builder\n *\n * Provides static methods to translate Rebase filter conditions\n * to MongoDB query filters.\n */\nexport class MongoConditionBuilder {\n /**\n * Build MongoDB filter conditions from Rebase FilterValues\n *\n * @param filter - Rebase filter values\n * @returns Array of MongoDB filter objects\n */\n static buildFilterConditions<M extends Record<string, any>>(\n filter: FilterValues<Extract<keyof M, string>>\n ): Filter<Document>[] {\n if (!filter) return [];\n\n const conditions: Filter<Document>[] = [];\n\n for (const [field, filterParam] of Object.entries(filter)) {\n if (!filterParam) continue;\n\n // One tuple or an array of them. This destructured the param\n // directly, so `{ age: [[\">=\", 18], [\"<\", 65]] }` bound `op` to the\n // tuple `[\">=\", 18]`, matched no operator, and dropped **both**\n // conditions — a filtered read answering 200 with the whole\n // collection. The grammar is shared with the Postgres compiler now.\n for (const [op, value] of toFilterTuples(filterParam)) {\n conditions.push(this.buildCondition(field, op, value));\n }\n }\n\n return conditions;\n }\n\n /**\n * One field, one operator, one value.\n *\n * Extracted so that `logical` groups translate through exactly this code.\n * A group written as a second dialect is a group where `array-contains` or\n * `ilike` quietly means something else than it does in `filter`, which is\n * the kind of difference nobody finds until a query returns the wrong rows.\n *\n * Always returns a condition or throws: there is no operator this can be\n * given that legitimately means \"no condition\".\n */\n private static buildCondition(\n field: string,\n op: WhereFilterOp,\n value: any\n ): Filter<Document> {\n // Null-testing operators ignore their value.\n if (op === \"is-null\") return { [field]: { $eq: null } };\n if (op === \"is-not-null\") return { [field]: { $ne: null } };\n\n // Pattern matching → regular expressions.\n if (op === \"like\" || op === \"ilike\" || op === \"not-like\" || op === \"not-ilike\") {\n const caseInsensitive = op === \"ilike\" || op === \"not-ilike\";\n const regex = likePatternToRegExp(value, caseInsensitive);\n const negated = op === \"not-like\" || op === \"not-ilike\";\n return { [field]: negated ? { $not: regex } : { $regex: regex } };\n }\n\n const mongoOp = REBASE_TO_MONGO_OP[op];\n\n if (!mongoOp) {\n // A filter that cannot be compiled must not compile to \"no filter\".\n // Returning `undefined` here dropped the condition and widened the\n // read — inside an `or(...)` group it drops a branch, which widens\n // it further — and the only trace was a line in the server log\n // behind a 200.\n logger.warn(`Unsupported filter operator '${op}' on field '${field}'`);\n throw ApiError.badRequest(\n `Operator '${op}' is not supported on field '${field}' by the MongoDB driver.`,\n \"UNSUPPORTED_FILTER_OPERATOR\",\n { field, operator: op }\n );\n }\n\n // Handle array-contains specially\n if (op === \"array-contains\") {\n return { [field]: { $elemMatch: { $eq: value } } };\n }\n return { [field]: { [mongoOp]: value } };\n }\n\n /**\n * Translate an `or(...)` / `and(...)` group, nesting included.\n *\n * Returns `undefined` for a group with nothing in it. `$or: []` is an error\n * in Mongo and `$and: []` matches every document, so neither is a\n * defensible reading of \"no conditions\".\n */\n static buildLogicalConditions(logical: LogicalCondition | undefined): Filter<Document> | undefined {\n if (!logical || !Array.isArray(logical.conditions)) return undefined;\n\n const parts: Filter<Document>[] = [];\n for (const entry of logical.conditions) {\n if (!entry) continue;\n if (\"type\" in entry && \"conditions\" in entry) {\n const nested = this.buildLogicalConditions(entry as LogicalCondition);\n if (nested) parts.push(nested);\n continue;\n }\n const { column, operator, value } = entry as FilterCondition;\n parts.push(this.buildCondition(column, operator, value));\n }\n\n // Through the same combiners the rest of this class uses, so a\n // one-condition group reads as the bare condition — identical to how\n // `filter` would have expressed it — rather than as `{ $and: [x] }`.\n return logical.type === \"or\"\n ? this.combineConditionsWithOr(parts)\n : this.combineConditionsWithAnd(parts);\n }\n\n /**\n * Build search conditions for text search\n *\n * @param searchString - Text to search for\n * @param properties - The collection's properties, searched for string fields\n * @returns Array of MongoDB filter objects for text search\n */\n static buildSearchConditions(\n searchString: string,\n // Typed as the real property map, not `Record<string, any>`. The loose\n // type is what let the `dataType` bug below survive: a caller — and,\n // more to the point, a test fixture — could invent any key it liked and\n // nothing checked it against a property a user can actually declare.\n properties: CollectionConfig[\"properties\"]\n ): Filter<Document>[] {\n if (!searchString) return [];\n\n // Build regex conditions for each searchable string property\n const orConditions: Filter<Document>[] = [];\n const escapedSearch = escapeRegExp(searchString);\n const searchRegex = new RegExp(escapedSearch, \"i\");\n\n for (const [key, prop] of Object.entries(properties)) {\n // `type`, not `dataType`. No property in `@rebasepro/types` has ever\n // had a `dataType` field — a real collection carries `type:\n // \"string\"` — so this matched nothing for every collection a user\n // could actually declare. With no field matching, the fallback\n // below took over and every search became a `$text` query, which\n // needs a text index and throws `IndexNotFound` without one.\n //\n // The suite passed because its fixtures were written with the same\n // wrong key, so the test data agreed with the bug and the two\n // never met a real collection between them.\n if (prop?.type === \"string\" || typeof prop === \"string\") {\n orConditions.push({\n [key]: { $regex: searchRegex }\n });\n }\n }\n\n // If no properties to search, use MongoDB text search\n if (orConditions.length === 0) {\n return [{ $text: { $search: searchString } }];\n }\n\n return orConditions;\n }\n\n /**\n * Combine multiple conditions with AND operator\n *\n * @param conditions - Array of filter conditions\n * @returns Combined filter or undefined if empty\n */\n static combineConditionsWithAnd(conditions: Filter<Document>[]): Filter<Document> | undefined {\n if (conditions.length === 0) return undefined;\n if (conditions.length === 1) return conditions[0];\n return { $and: conditions };\n }\n\n /**\n * Combine multiple conditions with OR operator\n *\n * @param conditions - Array of filter conditions\n * @returns Combined filter or undefined if empty\n */\n static combineConditionsWithOr(conditions: Filter<Document>[]): Filter<Document> | undefined {\n if (conditions.length === 0) return undefined;\n if (conditions.length === 1) return conditions[0];\n return { $or: conditions };\n }\n\n /**\n * Build a complete MongoDB query from Rebase options\n *\n * @param options - Rebase fetch options\n * @returns MongoDB filter object\n */\n static buildQuery<M extends Record<string, any>>(options: {\n filter?: FilterValues<Extract<keyof M, string>>;\n /**\n * An `or(...)`/`and(...)` group, AND-ed with `filter` and\n * `searchString` — the three are independent, as `FindParams`\n * documents. Absent here until now, so a group that reached this\n * driver was dropped and the read ran unfiltered.\n */\n logical?: LogicalCondition;\n searchString?: string;\n properties?: CollectionConfig[\"properties\"];\n }): Filter<Document> {\n const conditions: Filter<Document>[] = [];\n\n // Add filter conditions\n if (options.filter) {\n const filterConditions = this.buildFilterConditions<M>(options.filter);\n conditions.push(...filterConditions);\n }\n\n const logicalCondition = this.buildLogicalConditions(options.logical);\n if (logicalCondition) conditions.push(logicalCondition);\n\n // Add search conditions\n if (options.searchString && options.properties) {\n const searchConditions = this.buildSearchConditions(\n options.searchString,\n options.properties\n );\n if (searchConditions.length > 0) {\n // Search conditions are OR'd together\n const searchFilter = this.combineConditionsWithOr(searchConditions);\n if (searchFilter) {\n conditions.push(searchFilter);\n }\n }\n }\n\n return this.combineConditionsWithAnd(conditions) ?? {};\n }\n\n /**\n * Build MongoDB sort options from Rebase options\n *\n * @param orderBy - Field to order by\n * @param order - Sort direction\n * @returns MongoDB sort object\n */\n static buildSort(\n orderBy?: string,\n order?: \"asc\" | \"desc\"\n ): Record<string, 1 | -1> | undefined {\n if (!orderBy) return undefined;\n return { [orderBy]: order === \"desc\" ? -1 : 1 };\n }\n}\n","/**\n * MongoDB Row Service\n *\n * Implements DataRepository interface for MongoDB.\n * Provides all CRUD operations for rows.\n */\n\nimport { Db, ObjectId, Collection, Document, FindOptions, Filter } from \"mongodb\";\nimport { FilterValues, DataRepository, CollectionConfig, EntityReference, LogicalCondition } from \"@rebasepro/types\";\nimport { MongoConditionBuilder } from \"./MongoConditionBuilder\";\nimport { ApiError } from \"@rebasepro/server\";\n\n/**\n * MongoDB Row Service\n *\n * Implements the DataRepository interface for MongoDB.\n * Provides all CRUD operations for rows stored in MongoDB collections.\n */\nexport class MongoDataService implements DataRepository {\n constructor(private db: Db) { }\n\n /**\n * Get a MongoDB collection by its path\n */\n private getCollection(collectionPath: string): Collection<Document> {\n // Handle nested paths (e.g., \"posts/123/comments\" -> \"posts_123_comments\")\n const collectionName = collectionPath.replace(/\\//g, \"_\");\n return this.db.collection(collectionName);\n }\n\n /**\n * Convert a string ID to ObjectId if it's a valid ObjectId string\n */\n private toObjectId(id: string | number): ObjectId | string | number {\n if (typeof id === \"string\" && ObjectId.isValid(id) && id.length === 24) {\n return new ObjectId(id);\n }\n return id;\n }\n\n /**\n * Convert a MongoDB document to a flat row (`{ id, ...fields }`)\n */\n private documentToRow(doc: Document): Record<string, unknown> {\n const { _id, ...values } = doc;\n return {\n ...this.convertFromMongoValues(values),\n // Spread the canonical id last so it wins over a literal `id` field\n id: _id.toString()\n };\n }\n\n /**\n * Convert values from MongoDB format to Rebase format\n */\n private convertFromMongoValues(values: Record<string, any>): Record<string, any> {\n const result: Record<string, any> = {};\n\n for (const [key, value] of Object.entries(values)) {\n result[key] = this.convertFromMongoValue(value);\n }\n\n return result;\n }\n\n /**\n * Convert a single value from MongoDB format\n */\n private convertFromMongoValue(value: any): any {\n if (value === null || value === undefined) return value;\n\n // Handle ObjectId\n if (value instanceof ObjectId) {\n return value.toString();\n }\n\n // Handle Date\n if (value instanceof Date) {\n return value;\n }\n\n // Handle arrays\n if (Array.isArray(value)) {\n return value.map(v => this.convertFromMongoValue(v));\n }\n\n // Handle stored EntityReference. New writes are tagged with the\n // `__type: \"reference\"` sentinel (see convertToMongoValue), which is\n // unambiguous. We also accept the legacy shape — an object whose ONLY\n // keys are `id` and `path` — so references written before the sentinel\n // existed still decode. We deliberately do NOT treat any object that\n // merely *contains* `id` and `path` as a reference, because that\n // silently rewrites ordinary embedded sub-documents.\n if (typeof value === \"object\") {\n const keys = Object.keys(value);\n const isTagged = value.__type === \"reference\" && \"id\" in value && \"path\" in value;\n const isLegacy = keys.length === 2 && keys.includes(\"id\") && keys.includes(\"path\");\n if (isTagged || isLegacy) {\n return new EntityReference({\n id: value.id instanceof ObjectId ? value.id.toString() : String(value.id),\n path: value.path,\n driver: value.driver,\n databaseId: value.databaseId\n });\n }\n }\n\n // Handle nested objects\n if (typeof value === \"object\") {\n return this.convertFromMongoValues(value);\n }\n\n return value;\n }\n\n /**\n * Convert values to MongoDB format for storage\n */\n private convertToMongoValues(values: Record<string, any>): Record<string, any> {\n const result: Record<string, any> = {};\n\n for (const [key, value] of Object.entries(values)) {\n result[key] = this.convertToMongoValue(value);\n }\n\n return result;\n }\n\n /**\n * Convert a single value to MongoDB format\n */\n private convertToMongoValue(value: any): any {\n if (value === null || value === undefined) return value;\n\n // Handle EntityReference. Persist with a `__type: \"reference\"` sentinel\n // so it round-trips unambiguously, and preserve `driver`/`databaseId`\n // so cross-datasource pointers don't lose their target on write.\n if (typeof value === \"object\" && value.isEntityReference?.()) {\n const ref: Record<string, unknown> = {\n __type: \"reference\",\n id: ObjectId.isValid(value.id) ? new ObjectId(value.id) : value.id,\n path: value.path\n };\n if (value.driver !== undefined) ref.driver = value.driver;\n if (value.databaseId !== undefined) ref.databaseId = value.databaseId;\n return ref;\n }\n\n // Handle Date\n if (value instanceof Date) {\n return value;\n }\n\n // Handle arrays\n if (Array.isArray(value)) {\n return value.map(v => this.convertToMongoValue(v));\n }\n\n // Handle nested objects\n if (typeof value === \"object\") {\n return this.convertToMongoValues(value);\n }\n\n return value;\n }\n\n // =============================================================\n // DataRepository Implementation\n // =============================================================\n\n /**\n * Fetch a single row by ID\n */\n async fetchOne<M extends Record<string, any>>(\n collectionPath: string,\n id: string | number,\n _databaseId?: string\n ): Promise<Record<string, unknown> | undefined> {\n const collection = this.getCollection(collectionPath);\n const objectId = this.toObjectId(id);\n\n const doc = await collection.findOne({ _id: objectId } as Filter<Document>);\n\n if (!doc) return undefined;\n\n return this.documentToRow(doc);\n }\n\n /**\n * Fetch a collection of rows with optional filtering, ordering, and pagination\n */\n async fetchCollection<M extends Record<string, any>>(\n collectionPath: string,\n options: {\n filter?: FilterValues<Extract<keyof M, string>>;\n /** An `or(...)`/`and(...)` group, AND-ed with `filter`. */\n logical?: LogicalCondition;\n orderBy?: string;\n order?: \"desc\" | \"asc\";\n limit?: number;\n offset?: number;\n startAfter?: any;\n searchString?: string;\n databaseId?: string;\n collection?: CollectionConfig;\n rawQuery?: Filter<Document>;\n } = {}\n ): Promise<Record<string, unknown>[]> {\n const collection = this.getCollection(collectionPath);\n\n // Build query\n const query = options.rawQuery ?? MongoConditionBuilder.buildQuery<M>({\n filter: options.filter,\n logical: options.logical,\n searchString: options.searchString,\n properties: options.collection?.properties ?? {}\n });\n\n // Build find options\n const findOptions: FindOptions = {};\n\n // Apply sorting\n const sort = MongoConditionBuilder.buildSort(options.orderBy, options.order);\n if (sort) {\n findOptions.sort = sort;\n }\n\n // Apply limit\n if (options.limit) {\n findOptions.limit = options.limit;\n }\n\n // Apply pagination. `offset` is the parameter the REST layer and the\n // SDK both speak; it was absent here, so every `?offset=` reaching this\n // driver was discarded and page three served page one. `startAfter`\n // stays as the older spelling and wins when both are given.\n if (options.startAfter !== undefined) {\n findOptions.skip = Number(options.startAfter);\n } else if (options.offset) {\n findOptions.skip = options.offset;\n }\n\n const docs = await collection.find(query, findOptions).toArray();\n\n return docs.map((doc: Document) => this.documentToRow(doc));\n }\n\n /**\n * Search rows by text\n */\n async searchRows<M extends Record<string, any>>(\n collectionPath: string,\n searchString: string,\n options: {\n filter?: FilterValues<Extract<keyof M, string>>;\n orderBy?: string;\n order?: \"desc\" | \"asc\";\n limit?: number;\n databaseId?: string;\n collection?: CollectionConfig;\n rawQuery?: Filter<Document>;\n } = {}\n ): Promise<Record<string, unknown>[]> {\n return this.fetchCollection<M>(collectionPath, {\n ...options,\n searchString\n });\n }\n\n /**\n * Count rows in a collection\n */\n async count<M extends Record<string, any>>(\n collectionPath: string,\n options: {\n filter?: FilterValues<Extract<keyof M, string>>;\n /** An `or(...)`/`and(...)` group, AND-ed with `filter`. */\n logical?: LogicalCondition;\n searchString?: string;\n collection?: CollectionConfig;\n databaseId?: string;\n rawQuery?: Filter<Document>;\n } = {}\n ): Promise<number> {\n const collection = this.getCollection(collectionPath);\n\n // Every narrowing the listing applies has to apply here too, or the\n // count describes a different query than the one it is reported\n // against. `logical` and `searchString` were both missing.\n const query = options.rawQuery ?? MongoConditionBuilder.buildQuery<M>({\n filter: options.filter,\n logical: options.logical,\n searchString: options.searchString,\n properties: options.collection?.properties ?? {}\n });\n\n return collection.countDocuments(query);\n }\n\n /**\n * Save an row (create or update)\n *\n * Returns the **stored** document, not the values that were sent. A partial\n * update sends only the fields that changed, and returning those was a\n * partial row everywhere it went: the REST response, `afterSave`, the\n * history entry a revert restores from, and the row pushed to realtime\n * subscribers. Postgres returns the whole row here (`RETURNING *`).\n */\n async save<M extends Record<string, any>>(\n collectionPath: string,\n values: Partial<M>,\n id?: string | number,\n _databaseId?: string\n ): Promise<Record<string, unknown>> {\n const collection = this.getCollection(collectionPath);\n const mongoValues = this.convertToMongoValues(values as Record<string, any>);\n\n if (id) {\n // Still an upsert: this is also the call that creates a row with a\n // client-chosen id. Addressing an id that does not exist is caught\n // above — the REST `PUT` 404s and the authenticated driver refuses\n // — so the upsert only ever lands as the create it is meant to be.\n const objectId = this.toObjectId(id);\n await collection.updateOne(\n { _id: objectId } as Filter<Document>,\n { $set: mongoValues },\n { upsert: true }\n );\n\n return await this.readBack(collectionPath, objectId, { ...values,\nid: id.toString() });\n } else {\n // Create new row\n const newId = new ObjectId();\n await collection.insertOne({\n _id: newId,\n ...mongoValues\n });\n\n return await this.readBack(collectionPath, newId, { ...values,\nid: newId.toString() });\n }\n }\n\n /**\n * Read the document back after a write, falling back to the caller's own\n * values if it has already been removed by a concurrent delete.\n */\n private async readBack(\n collectionPath: string,\n objectId: ObjectId | string | number,\n fallback: Record<string, unknown>\n ): Promise<Record<string, unknown>> {\n const doc = await this.getCollection(collectionPath).findOne({ _id: objectId } as Filter<Document>);\n return doc ? this.documentToRow(doc) : fallback;\n }\n\n /**\n * Delete a row by ID.\n *\n * Rejects when nothing matched, which is the `DataDriver.delete` contract:\n * resolving means the row is gone because this call removed it. This used\n * to log a warning and resolve, so a caller could not tell a delete from a\n * no-op — and the same call on the Postgres driver threw. The wording is\n * the Postgres driver's, verbatim, because two spellings of one answer is\n * how the two came apart in the first place.\n */\n async delete(\n collectionPath: string,\n id: string | number,\n _databaseId?: string\n ): Promise<void> {\n const collection = this.getCollection(collectionPath);\n const objectId = this.toObjectId(id);\n\n const result = await collection.deleteOne({ _id: objectId } as Filter<Document>);\n\n if (result.deletedCount === 0) {\n throw ApiError.notFound(`No row \"${id}\" in \"${collectionPath}\" to delete.`);\n }\n }\n\n /**\n * Check if a field value is unique in a collection\n */\n async checkUniqueField(\n collectionPath: string,\n fieldName: string,\n value: any,\n excludeEntityId?: string,\n _databaseId?: string\n ): Promise<boolean> {\n const collection = this.getCollection(collectionPath);\n\n const query: Filter<Document> = { [fieldName]: value };\n\n if (excludeEntityId) {\n const objectId = this.toObjectId(excludeEntityId);\n (query as Record<string, unknown>)._id = { $ne: objectId };\n }\n\n const count = await collection.countDocuments(query);\n return count === 0;\n }\n\n /**\n * Generate a new row ID\n */\n generateId(): string {\n return new ObjectId().toString();\n }\n}\n","/**\n * MongoDB Realtime Service\n *\n * Implements RealtimeProvider interface using MongoDB Change Streams.\n * Provides real-time subscriptions to collection and row changes.\n */\n\nimport { Db, ChangeStream, ChangeStreamDocument, Document, ObjectId } from \"mongodb\";\nimport {\n ANONYMOUS_USER_ID,\n DataDriver,\n FilterValues,\n RealtimeProvider,\n CollectionSubscriptionConfig,\n SingleSubscriptionConfig,\n WebSocketMessage,\n User,\n ListLimitError,\n resolveClientListLimit\n} from \"@rebasepro/types\";\nimport { WebSocket } from \"ws\";\n\nimport type { MongoDriver } from \"./MongoDriver\";\nimport { logger } from \"@rebasepro/server\";\n\n/** The acting user for a subscription, as the driver and the socket carry it. */\nexport interface SubscriptionAuthContext {\n uid: string;\n roles: string[];\n}\n\ninterface Subscription {\n type: \"collection\" | \"single\";\n /**\n * Carries `authContext`. There is deliberately no second copy on this\n * object: every fetch reads `config.authContext`, and the one that used to\n * live here was written from three places and read from none — so a\n * subscription that looked authorized was re-fetched as nobody.\n */\n config: (CollectionSubscriptionConfig | SingleSubscriptionConfig) & { authContext?: SubscriptionAuthContext };\n changeStream?: ChangeStream;\n callback?: (data: any) => void;\n}\n\n/**\n * MongoDB Realtime Service\n *\n * Implements real-time subscriptions using MongoDB Change Streams.\n * Requires MongoDB replica set for change streams to work.\n */\nexport class MongoRealtimeService implements RealtimeProvider {\n private subscriptions = new Map<string, Subscription>();\n private clients = new Map<string, WebSocket>();\n private driver?: MongoDriver;\n\n constructor(private db: Db) {}\n\n setDataDriver(driver: MongoDriver) {\n this.driver = driver;\n }\n\n /**\n * Get the collection name from a path\n */\n private getCollectionName(path: string): string {\n return path.replace(/\\//g, \"_\");\n }\n\n /**\n * Subscribe to collection changes\n */\n subscribeToCollection(\n subscriptionId: string,\n config: CollectionSubscriptionConfig & { authContext?: SubscriptionAuthContext },\n callback?: (rows: Record<string, unknown>[]) => void\n ): void {\n // Clean up existing subscription if any\n this.unsubscribe(subscriptionId);\n\n const collectionName = this.getCollectionName(config.path);\n const collection = this.db.collection(collectionName);\n\n // Build pipeline for change stream filtering\n const pipeline: Document[] = [];\n\n // Filter by operation types we care about\n pipeline.push({\n $match: {\n operationType: { $in: [\"insert\", \"update\", \"replace\", \"delete\"] }\n }\n });\n\n try {\n // Create change stream\n const changeStream = collection.watch(pipeline, {\n fullDocument: \"updateLookup\"\n });\n\n const subscription: Subscription = {\n type: \"collection\",\n config,\n changeStream,\n callback\n };\n\n this.subscriptions.set(subscriptionId, subscription);\n\n // Fetch initial data\n this.fetchAndNotifyCollection(subscriptionId, config, callback);\n\n // Listen for changes\n changeStream.on(\"change\", async (change: ChangeStreamDocument) => {\n // Re-fetch the entire collection when any change happens\n // This is simpler and ensures consistent sorting/filtering\n await this.fetchAndNotifyCollection(subscriptionId, config, callback);\n });\n\n changeStream.on(\"error\", (error: Error) => {\n logger.error(`Change stream error for subscription ${subscriptionId}`, { error: error });\n });\n\n } catch (error) {\n // Change streams might not be available (e.g., standalone MongoDB)\n logger.warn(\"Change streams not available, falling back to polling\", { error: error });\n\n // Store subscription without change stream for manual notifications\n const subscription: Subscription = {\n type: \"collection\",\n config,\n callback\n };\n\n this.subscriptions.set(subscriptionId, subscription);\n\n // Fetch initial data\n this.fetchAndNotifyCollection(subscriptionId, config, callback);\n }\n }\n\n /**\n * Fetch collection and notify callback\n */\n private async fetchAndNotifyCollection(\n subscriptionId: string,\n config: CollectionSubscriptionConfig & { authContext?: SubscriptionAuthContext },\n callback?: (rows: Record<string, unknown>[]) => void\n ): Promise<void> {\n try {\n const registryCollection = this.driver?.registry?.getCollectionByPath(config.path);\n // One path, authenticated or not. The `else` branch used to reach\n // past the driver into the repository, which applies no security\n // rules at all — the fallback stubbing out the contract the primary\n // branch honours, and granting more while doing it. An anonymous\n // subscriber is now a user like any other: rules are evaluated\n // against the anonymous uid, and a rule that needs a real one\n // matches nothing.\n const driver = await this.scopedDriver(config.authContext);\n const rows = await driver.fetchCollection({\n path: config.path,\n collection: registryCollection,\n filter: config.filter as FilterValues<string> | undefined,\n orderBy: config.orderBy,\n order: config.order,\n limit: config.limit,\n startAfter: config.startAfter,\n searchString: config.searchString\n });\n\n if (callback) {\n callback(rows);\n }\n } catch (error) {\n logger.error(`Error fetching collection for subscription ${subscriptionId}`, { error: error });\n }\n }\n\n /**\n * The driver scoped to a subscriber.\n *\n * Never the bare repository: everything a subscription delivers has to pass\n * the same row authorization an HTTP read does.\n */\n private async scopedDriver(authContext?: SubscriptionAuthContext): Promise<DataDriver> {\n if (!this.driver) {\n throw new Error(\"MongoRealtimeService has no data driver — subscriptions cannot be authorized\");\n }\n const user = { uid: authContext?.uid ?? ANONYMOUS_USER_ID,\nroles: authContext?.roles ?? [] } as User;\n return this.driver.withAuth(user);\n }\n\n /**\n * Subscribe to single row changes\n */\n subscribeToOne(\n subscriptionId: string,\n config: SingleSubscriptionConfig & { authContext?: SubscriptionAuthContext },\n callback?: (row: Record<string, unknown> | null) => void\n ): void {\n // Clean up existing subscription if any\n this.unsubscribe(subscriptionId);\n\n const collectionName = this.getCollectionName(config.path);\n const collection = this.db.collection(collectionName);\n\n // Build pipeline to watch specific document\n const id = typeof config.id === \"string\" && ObjectId.isValid(config.id)\n ? new ObjectId(config.id)\n : config.id;\n\n const pipeline: Document[] = [\n {\n $match: {\n \"documentKey._id\": id,\n operationType: { $in: [\"insert\", \"update\", \"replace\", \"delete\"] }\n }\n }\n ];\n\n try {\n const changeStream = collection.watch(pipeline, {\n fullDocument: \"updateLookup\"\n });\n\n const subscription: Subscription = {\n type: \"single\",\n config,\n changeStream,\n callback\n };\n\n this.subscriptions.set(subscriptionId, subscription);\n\n // Fetch initial data\n this.fetchAndNotifyOne(subscriptionId, config, callback);\n\n // Listen for changes\n changeStream.on(\"change\", async (change: ChangeStreamDocument) => {\n if (change.operationType === \"delete\") {\n if (callback) {\n callback(null);\n }\n } else {\n await this.fetchAndNotifyOne(subscriptionId, config, callback);\n }\n });\n\n changeStream.on(\"error\", (error: Error) => {\n logger.error(`Change stream error for subscription ${subscriptionId}`, { error: error });\n });\n\n } catch (error) {\n logger.warn(\"Change streams not available, falling back to polling\", { error: error });\n\n const subscription: Subscription = {\n type: \"single\",\n config,\n callback\n };\n\n this.subscriptions.set(subscriptionId, subscription);\n\n // Fetch initial data\n this.fetchAndNotifyOne(subscriptionId, config, callback);\n }\n }\n\n /**\n * Fetch row and notify callback\n */\n private async fetchAndNotifyOne(\n subscriptionId: string,\n config: SingleSubscriptionConfig & { authContext?: SubscriptionAuthContext },\n callback?: (row: Record<string, unknown> | null) => void\n ): Promise<void> {\n try {\n const registryCollection = this.driver?.registry?.getCollectionByPath(config.path);\n const driver = await this.scopedDriver(config.authContext);\n const row = await driver.fetchOne({\n path: config.path,\n id: config.id,\n collection: registryCollection\n });\n\n if (callback) {\n callback(row || null);\n }\n } catch (error) {\n logger.error(`Error fetching row for subscription ${subscriptionId}`, { error: error });\n }\n }\n\n /**\n * Unsubscribe from a subscription\n */\n unsubscribe(subscriptionId: string): void {\n const subscription = this.subscriptions.get(subscriptionId);\n if (subscription) {\n if (subscription.changeStream) {\n subscription.changeStream.close().catch((err) => logger.error(\"Operation failed\", { error: err }));\n }\n this.subscriptions.delete(subscriptionId);\n }\n }\n\n /**\n * Notify all relevant subscribers of an row update\n * This is called after save/delete operations to push updates\n */\n async notifyUpdate(\n path: string,\n id: string,\n row: Record<string, unknown> | null,\n _databaseId?: string\n ): Promise<void> {\n // Find all subscriptions that might be affected by this update\n for (const [subscriptionId, subscription] of this.subscriptions) {\n if (subscription.type === \"single\") {\n const config = subscription.config as SingleSubscriptionConfig & { authContext?: SubscriptionAuthContext };\n if (config.path === path && config.id.toString() === id) {\n if (row === null) {\n // A deletion carries no row to authorize.\n subscription.callback?.(null);\n } else {\n // Re-fetched through the subscriber's own driver rather\n // than pushed verbatim: `notifyUpdate` runs after every\n // save, and handing it the row as written broadcast any\n // document to whoever happened to be watching its id.\n await this.fetchAndNotifyOne(subscriptionId, config, subscription.callback);\n }\n }\n } else if (subscription.type === \"collection\") {\n const config = subscription.config as CollectionSubscriptionConfig & { authContext?: SubscriptionAuthContext };\n if (config.path === path) {\n // Re-fetch the collection to get updated data\n await this.fetchAndNotifyCollection(subscriptionId, config, subscription.callback);\n }\n }\n }\n }\n\n /**\n * Get all active subscriptions (for debugging)\n */\n getSubscriptions(): Map<string, Subscription> {\n return this.subscriptions;\n }\n\n /**\n * Close all subscriptions\n */\n async closeAll(): Promise<void> {\n for (const [subscriptionId] of this.subscriptions) {\n this.unsubscribe(subscriptionId);\n }\n }\n\n // =============================================================================\n // WebSocket Client Management (parity with PostgreSQL RealtimeService)\n // =============================================================================\n\n /**\n * Register a WebSocket client for real-time communication\n */\n addClient(clientId: string, ws: WebSocket) {\n this.clients.set(clientId, ws);\n\n ws.on(\"close\", () => {\n this.removeClient(clientId);\n });\n\n ws.on(\"error\", (error) => {\n logger.error(\"WebSocket error for client\", { detail: clientId, error });\n this.removeClient(clientId);\n });\n }\n\n /**\n * Remove a WebSocket client and clean up its subscriptions\n */\n private removeClient(clientId: string) {\n this.clients.delete(clientId);\n }\n\n /**\n * Handle an incoming WebSocket message for subscription management\n */\n async handleClientMessage(\n clientId: string,\n message: { type: string; payload?: any; subscriptionId?: string },\n _authContext?: { uid: string; roles: unknown[] }\n ): Promise<void> {\n const ws = this.clients.get(clientId);\n if (!ws) return;\n\n const authContext = _authContext ? { uid: _authContext.uid,\nroles: (_authContext.roles ?? []).map(String) } : undefined;\n\n switch (message.type) {\n case \"subscribe_collection\": {\n const subscriptionId = message.payload?.subscriptionId ?? message.subscriptionId;\n if (!subscriptionId) return;\n\n // The same list bound the Postgres socket and every REST route\n // apply. This ingress applied none: an absent limit reached the\n // driver as `undefined` and emitted no `.limit()` at all, so one\n // subscribe frame streamed the whole collection — and re-streamed\n // it on every matching write. An over-large limit is refused\n // rather than shrunk, because a `collection_update` frame carries\n // no `total` or `hasMore` for the client to notice with.\n let boundedLimit: number;\n try {\n boundedLimit = resolveClientListLimit(message.payload?.limit);\n } catch (e) {\n if (!(e instanceof ListLimitError)) throw e;\n logger.warn(`⚠️ [MongoRealtime] Refused subscription to '${message.payload?.path}': ${e.message}`);\n ws.send(JSON.stringify({\n type: \"ERROR\",\n subscriptionId,\n payload: { error: { message: e.message, code: \"INVALID_LIMIT\" } },\n error: e.message\n }));\n return;\n }\n\n this.subscribeToCollection(\n subscriptionId,\n {\n clientId,\n path: message.payload?.path,\n filter: message.payload?.filter,\n orderBy: message.payload?.orderBy,\n order: message.payload?.order,\n limit: boundedLimit,\n startAfter: message.payload?.startAfter,\n searchString: message.payload?.searchString,\n authContext\n },\n (rows) => {\n ws.send(JSON.stringify({\n type: \"collection_update\",\n subscriptionId,\n rows\n }));\n }\n );\n break;\n }\n case \"subscribe_one\": {\n const subscriptionId = message.payload?.subscriptionId ?? message.subscriptionId;\n if (!subscriptionId) return;\n\n this.subscribeToOne(\n subscriptionId,\n {\n clientId,\n path: message.payload?.path,\n id: message.payload?.id,\n authContext\n },\n (row) => {\n ws.send(JSON.stringify({\n type: \"single_update\",\n subscriptionId,\n row\n }));\n }\n );\n break;\n }\n case \"unsubscribe\": {\n const subscriptionId = message.payload?.subscriptionId ?? message.subscriptionId;\n if (subscriptionId) {\n this.unsubscribe(subscriptionId);\n }\n break;\n }\n default: {\n // A silent `switch` over a wire protocol is how channel,\n // presence and broadcast frames came to be accepted here and\n // dropped: the client's `broadcast()` resolved, `onPresence`\n // never fired, and a `channel_history` request buffered live\n // messages until the catch-up timeout on every join. Say so,\n // and tell the sender rather than leaving it waiting.\n logger.warn(\n `⚠️ [MongoRealtime] Unhandled realtime message type \"${message.type}\" — ` +\n \"channels, presence and broadcast are not implemented by the Mongo driver.\"\n );\n ws.send(JSON.stringify({\n type: \"ERROR\",\n subscriptionId: message.subscriptionId,\n payload: {\n error: {\n message: `Realtime message type \"${message.type}\" is not supported by the Mongo driver`,\n code: \"REALTIME_UNSUPPORTED\"\n }\n },\n error: `Realtime message type \"${message.type}\" is not supported by the Mongo driver`\n }));\n break;\n }\n }\n }\n}\n","import { Db, ObjectId } from \"mongodb\";\nimport { logger } from \"@rebasepro/server\";\n\n/**\n * Deep equality without JSON.stringify.\n * Handles primitives, arrays, Dates, and plain objects recursively.\n */\nfunction deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a == null || b == null) return false;\n if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n return a.every((v, i) => deepEqual(v, b[i]));\n }\n if (typeof a === \"object\" && typeof b === \"object\") {\n const aObj = a as Record<string, unknown>;\n const bObj = b as Record<string, unknown>;\n const aKeys = Object.keys(aObj);\n const bKeys = Object.keys(bObj);\n if (aKeys.length !== bKeys.length) return false;\n return aKeys.every(k => deepEqual(aObj[k], bObj[k]));\n }\n return false;\n}\n\n/**\n * Shallow comparison to find top-level keys that changed between two objects.\n */\nexport function findChangedFields(\n oldValues: Record<string, unknown>,\n newValues: Record<string, unknown>\n): string[] | null {\n const changed: string[] = [];\n const allKeys = new Set([\n ...Object.keys(oldValues),\n ...Object.keys(newValues)\n ]);\n\n for (const key of allKeys) {\n const oldVal = oldValues[key];\n const newVal = newValues[key];\n\n // Skip internal metadata\n if (key.startsWith(\"__\")) continue;\n\n if (oldVal !== newVal) {\n // For objects/arrays, use structural comparison\n if (\n typeof oldVal === \"object\" && oldVal !== null &&\n typeof newVal === \"object\" && newVal !== null\n ) {\n if (!deepEqual(oldVal, newVal)) {\n changed.push(key);\n }\n } else {\n changed.push(key);\n }\n }\n }\n\n return changed.length > 0 ? changed : null;\n}\n\nexport type { RecordHistoryParams, HistoryRetentionConfig } from \"@rebasepro/types\";\nimport type { EntityHistoryEntry, RecordHistoryParams, HistoryRetentionConfig } from \"@rebasepro/types\";\n\n/**\n * A history entry as MongoDB stores it — not as it travels.\n *\n * Two fields differ from {@link EntityHistoryEntry}: the driver's own `_id`,\n * and `updated_at` as a native `Date` so the retention query can compare it\n * with `$lt`. Both of these used to be on an interface *named* `HistoryEntry`,\n * which is also what `@rebasepro/server-postgres` called its wire shape — so\n * the same name meant `string` in one driver and `Date` in the other.\n */\nexport interface MongoHistoryDocument extends Omit<EntityHistoryEntry, \"updated_at\"> {\n _id?: ObjectId;\n updated_at: Date;\n}\n\nconst DEFAULT_RETENTION: HistoryRetentionConfig = {\n maxEntries: 200,\n ttlDays: 90\n};\n\nexport class MongoHistoryService {\n public retention: HistoryRetentionConfig;\n\n constructor(\n private db: Db,\n retention?: Partial<HistoryRetentionConfig>\n ) {\n this.retention = { ...DEFAULT_RETENTION,\n...retention };\n }\n\n async recordHistory(params: RecordHistoryParams): Promise<void> {\n const {\n tableName,\n id,\n action,\n values,\n previousValues,\n updatedBy\n } = params;\n\n const changedFields = previousValues && values\n ? findChangedFields(previousValues, values)\n : null;\n\n if (action === \"update\" && (!changedFields || changedFields.length === 0)) {\n return;\n }\n\n try {\n const entry: MongoHistoryDocument = {\n id: new ObjectId().toString(),\n table_name: tableName,\n entity_id: String(id),\n action,\n changed_fields: changedFields,\n values: values || null,\n previous_values: previousValues || null,\n updated_by: updatedBy || null,\n updated_at: new Date()\n };\n\n await this.db.collection(\"__rebase_history\").insertOne(entry);\n\n // Non-blocking prune for this specific row\n this.pruneHistory(String(id), tableName).catch(e => {\n logger.error(`[HistoryService] Failed to prune history for ${tableName}/${id}`, { error: e });\n });\n } catch (error) {\n logger.error(`[HistoryService] Failed to record history for ${tableName}/${id}`, { error: error });\n }\n }\n\n private async pruneHistory(id: string, tableName: string): Promise<void> {\n const collection = this.db.collection(\"__rebase_history\");\n\n // 1. Enforce maxEntries\n const count = await collection.countDocuments({ entity_id: id,\ntable_name: tableName });\n if (count > this.retention.maxEntries) {\n const toDelete = count - this.retention.maxEntries;\n const oldestEntries = await collection\n .find({ entity_id: id,\ntable_name: tableName })\n .sort({ updated_at: 1 })\n .limit(toDelete)\n .toArray();\n\n if (oldestEntries.length > 0) {\n const idsToDelete = oldestEntries.map(entry => entry._id);\n await collection.deleteMany({ _id: { $in: idsToDelete } });\n }\n }\n\n // 2. Enforce ttlDays\n const cutoffDate = new Date();\n cutoffDate.setDate(cutoffDate.getDate() - this.retention.ttlDays);\n\n await collection.deleteMany({\n entity_id: id,\n table_name: tableName,\n updated_at: { $lt: cutoffDate }\n });\n }\n}\n","/**\n * Row security for MongoDB.\n *\n * MongoDB has no RLS, so this driver enforces `securityRules` in-process. That\n * makes the translation from a rule to a query the enforcement boundary, and it\n * has exactly one safe failure mode: refuse.\n *\n * Two properties this file exists to hold:\n *\n * 1. **One predicate, one implementation.** The rules are compiled through the\n * same {@link securityRuleToConditions} the Postgres DDL generator and the\n * admin UI's `checkOperation` use, so \"what does this rule mean\" is answered\n * in one place. The previous translator re-parsed the raw SQL itself and\n * recognised four shapes — a second, smaller parser that disagreed with the\n * first about the same rule.\n * 2. **Fail closed, out loud.** An expression with no MongoDB equivalent (raw\n * SQL, a membership subquery, a negated row predicate) used to become `{}` —\n * \"match every document\". It now raises {@link SECURITY_RULE_UNSUPPORTED},\n * the same shape the REST layer uses to refuse bulk writes this driver\n * cannot perform: a request that cannot be authorized is not served.\n */\n\nimport { Document, Filter } from \"mongodb\";\nimport {\n ANONYMOUS_USER_ID,\n CollectionConfig,\n PolicyExpression,\n PolicyOperand,\n SecurityOperation,\n SecurityRule,\n User,\n isAnonymousUid\n} from \"@rebasepro/types\";\nimport { securityRuleToConditions } from \"@rebasepro/common\";\nimport { ApiError } from \"@rebasepro/server\";\n\n/** Matches every document. */\nconst MATCH_ALL: Filter<Document> = {};\n\n/**\n * Matches no document. A distinct object rather than a `false` sentinel so it\n * can be nested inside `$and`/`$or` like any other filter; identity is what\n * {@link isMatchNone} tests, so never mutate or copy it.\n */\nconst MATCH_NONE: Filter<Document> = { _id: { $exists: false } };\n\n/** The expression has no MongoDB equivalent — the caller must refuse. */\nconst UNTRANSLATABLE = \"untranslatable\" as const;\n\ntype TranslationResult = Filter<Document> | typeof UNTRANSLATABLE;\n\nfunction isMatchAll(f: TranslationResult): boolean {\n return f !== UNTRANSLATABLE && f !== MATCH_NONE && Object.keys(f).length === 0;\n}\n\nfunction isMatchNone(f: TranslationResult): boolean {\n return f === MATCH_NONE;\n}\n\n/** The error code a caller sees when a rule cannot be honoured. */\nexport const SECURITY_RULE_UNSUPPORTED = \"SECURITY_RULE_UNSUPPORTED\";\n\n/**\n * The refusal. Names the collection, the clause and the expression, because the\n * only useful thing an operator can do with this is rewrite that rule — or move\n * the collection to an engine that can enforce it.\n */\nexport function securityRuleUnsupported(\n collectionSlug: string,\n clause: \"using\" | \"withCheck\",\n detail: string\n): ApiError {\n return ApiError.internal(\n `This collection's data source (MongoDB) cannot enforce a security rule on \"${collectionSlug}\": ` +\n `the \\`${clause}\\` expression ${detail} has no MongoDB equivalent. The request was refused rather ` +\n \"than served without row authorization. Express the rule with `access`, `ownerField`, `roles`, or a \" +\n \"structured `condition`/`check`, or move this collection to a Postgres data source.\",\n SECURITY_RULE_UNSUPPORTED\n );\n}\n\n/** Describe an expression well enough for the refusal message to be actionable. */\nfunction describe(expr: PolicyExpression): string {\n switch (expr.kind) {\n case \"raw\":\n return `\\`${expr.sql}\\``;\n case \"existsIn\":\n return `a membership subquery over \\`${expr.collection}\\``;\n case \"not\":\n return \"a negated row predicate\";\n default:\n return `a \\`${expr.kind}\\` node`;\n }\n}\n\n/** The first node of an expression tree this driver cannot translate, if any. */\nfunction findUntranslatable(expr: PolicyExpression, hasRow: boolean): PolicyExpression | undefined {\n switch (expr.kind) {\n case \"and\":\n case \"or\": {\n for (const operand of expr.operands) {\n const found = findUntranslatable(operand, hasRow);\n if (found) return found;\n }\n return undefined;\n }\n case \"not\":\n // Only decidable without the row when the operand is: negating a\n // column predicate in MongoDB (`$nor`) also matches documents that\n // lack the column, which SQL's three-valued logic would exclude.\n return hasRow ? findUntranslatable(expr.operand, hasRow) : (referencesField(expr.operand) ? expr : undefined);\n case \"compare\":\n return operandUntranslatable(expr.left) || operandUntranslatable(expr.right) ? expr : undefined;\n case \"existsIn\":\n case \"raw\":\n return expr;\n default:\n return undefined;\n }\n}\n\nfunction operandUntranslatable(operand: PolicyOperand): boolean {\n return operand.kind === \"outerField\";\n}\n\nfunction referencesField(expr: PolicyExpression): boolean {\n switch (expr.kind) {\n case \"and\":\n case \"or\":\n return expr.operands.some(referencesField);\n case \"not\":\n return referencesField(expr.operand);\n case \"compare\":\n return expr.left.kind === \"field\" || expr.right.kind === \"field\" ||\n expr.left.kind === \"outerField\" || expr.right.kind === \"outerField\";\n default:\n return false;\n }\n}\n\n/** The acting user, as the policy model sees them. */\ninterface PolicyUserContext {\n uid: string;\n roles: string[];\n}\n\nfunction userContext(user: User | undefined): PolicyUserContext {\n // The sentinel, not an empty string: `rebase.uid()` is never NULL for a\n // request that came from a client, and an `ownerField` rule compared\n // against `undefined` would become `{ owner: undefined }` — which MongoDB\n // reads as `{ owner: null }` and matches every document that has no owner.\n return {\n uid: user?.uid || ANONYMOUS_USER_ID,\n roles: user?.roles ?? []\n };\n}\n\nconst COMPARE_TO_MONGO = {\n eq: \"$eq\",\n neq: \"$ne\",\n lt: \"$lt\",\n lte: \"$lte\",\n gt: \"$gt\",\n gte: \"$gte\"\n} as const;\n\nconst INVERTED_COMPARE = {\n eq: \"eq\",\n neq: \"neq\",\n lt: \"gt\",\n lte: \"gte\",\n gt: \"lt\",\n gte: \"lte\"\n} as const;\n\ntype ResolvedOperand =\n | { kind: \"field\"; name: string }\n | { kind: \"value\"; value: unknown }\n | { kind: \"unknown\" };\n\nfunction resolveOperand(operand: PolicyOperand, ctx: PolicyUserContext): ResolvedOperand {\n switch (operand.kind) {\n case \"literal\":\n return { kind: \"value\", value: operand.value };\n case \"authUid\":\n return { kind: \"value\", value: ctx.uid };\n case \"authRoles\":\n return { kind: \"value\", value: ctx.roles };\n case \"field\":\n return { kind: \"field\", name: operand.name };\n case \"outerField\":\n return { kind: \"unknown\" };\n }\n}\n\n/**\n * Translate one {@link PolicyExpression} into a MongoDB filter, or\n * {@link UNTRANSLATABLE}.\n *\n * The JavaScript twin of `evaluatePolicy`, one level up: where that decides a\n * single row, this narrows a query. `\"unknown\"` there and `UNTRANSLATABLE` here\n * are the same condition, and both are resolved fail-closed by their callers.\n */\nexport function policyToMongoFilter(expr: PolicyExpression, user: User | undefined): TranslationResult {\n const ctx = userContext(user);\n\n switch (expr.kind) {\n case \"true\":\n return MATCH_ALL;\n case \"false\":\n return MATCH_NONE;\n case \"and\": {\n const parts = expr.operands.map(o => policyToMongoFilter(o, user));\n // Kleene AND: a `false` operand settles the conjunction even when a\n // sibling is untranslatable, which is what keeps a role-scoped raw\n // rule from refusing requests it does not even apply to.\n if (parts.some(isMatchNone)) return MATCH_NONE;\n if (parts.some(p => p === UNTRANSLATABLE)) return UNTRANSLATABLE;\n const clauses = (parts as Filter<Document>[]).filter(p => !isMatchAll(p));\n if (clauses.length === 0) return MATCH_ALL;\n if (clauses.length === 1) return clauses[0];\n return { $and: clauses } as Filter<Document>;\n }\n case \"or\": {\n const parts = expr.operands.map(o => policyToMongoFilter(o, user));\n if (parts.some(isMatchAll)) return MATCH_ALL;\n if (parts.some(p => p === UNTRANSLATABLE)) return UNTRANSLATABLE;\n const clauses = (parts as Filter<Document>[]).filter(p => !isMatchNone(p));\n if (clauses.length === 0) return MATCH_NONE;\n if (clauses.length === 1) return clauses[0];\n return { $or: clauses } as Filter<Document>;\n }\n case \"not\": {\n const inner = policyToMongoFilter(expr.operand, user);\n // Constant-folded only. See `findUntranslatable` for why a negated\n // column predicate is refused instead of becoming `$nor`.\n if (isMatchAll(inner)) return MATCH_NONE;\n if (isMatchNone(inner)) return MATCH_ALL;\n return UNTRANSLATABLE;\n }\n case \"compare\": {\n const left = resolveOperand(expr.left, ctx);\n const right = resolveOperand(expr.right, ctx);\n if (left.kind === \"unknown\" || right.kind === \"unknown\") return UNTRANSLATABLE;\n\n if (left.kind === \"field\" && right.kind === \"value\") {\n return { [left.name]: { [COMPARE_TO_MONGO[expr.op]]: right.value } } as Filter<Document>;\n }\n if (left.kind === \"value\" && right.kind === \"field\") {\n return { [right.name]: { [COMPARE_TO_MONGO[INVERTED_COMPARE[expr.op]]]: left.value } } as Filter<Document>;\n }\n if (left.kind === \"field\" && right.kind === \"field\") {\n return { $expr: { [COMPARE_TO_MONGO[expr.op]]: [`$${left.name}`, `$${right.name}`] } } as Filter<Document>;\n }\n // Both sides are known values — the comparison is a constant.\n if (left.kind === \"value\" && right.kind === \"value\") {\n return compareValues(expr.op, left.value, right.value);\n }\n return UNTRANSLATABLE;\n }\n case \"rolesOverlap\":\n return expr.roles.some(r => r === \"public\" || ctx.roles.includes(r)) ? MATCH_ALL : MATCH_NONE;\n case \"rolesContain\":\n return expr.roles.every(r => r === \"public\" || ctx.roles.includes(r)) ? MATCH_ALL : MATCH_NONE;\n case \"authenticated\":\n return !isAnonymousUid(ctx.uid) ? MATCH_ALL : MATCH_NONE;\n case \"serverContext\":\n // A scoped driver is always acting for a user, never the server\n // context — the same answer `evaluatePolicy` gives.\n return MATCH_NONE;\n case \"existsIn\":\n case \"raw\":\n return UNTRANSLATABLE;\n }\n}\n\nfunction compareValues(op: keyof typeof COMPARE_TO_MONGO, a: unknown, b: unknown): TranslationResult {\n if (op === \"eq\") return a === b ? MATCH_ALL : MATCH_NONE;\n if (op === \"neq\") return a !== b ? MATCH_ALL : MATCH_NONE;\n if ((typeof a === \"string\" && typeof b === \"string\") || (typeof a === \"number\" && typeof b === \"number\")) {\n const decided = op === \"lt\" ? a < b : op === \"lte\" ? a <= b : op === \"gt\" ? a > b : a >= b;\n return decided ? MATCH_ALL : MATCH_NONE;\n }\n return UNTRANSLATABLE;\n}\n\n/** The rules that apply to `targetOperation`, mirroring `checkOperation`. */\nfunction applicableRules(collection: CollectionConfig | undefined, targetOperation: SecurityOperation): SecurityRule[] {\n const rules = collection?.securityRules;\n if (!rules || rules.length === 0) return [];\n return rules.filter((rule: SecurityRule) => {\n const ops = rule.operations && rule.operations.length > 0 ? rule.operations : [rule.operation ?? \"all\"];\n return ops.includes(targetOperation) || ops.includes(\"all\");\n });\n}\n\n/** Which clause of a rule constrains `targetOperation` — Postgres's own split. */\nfunction clauseFor(targetOperation: SecurityOperation): \"using\" | \"withCheck\" {\n return targetOperation === \"insert\" ? \"withCheck\" : \"using\";\n}\n\n/**\n * Refuse up front when this collection's rules cannot be enforced for\n * `targetOperation`.\n *\n * The row-in-hand paths (`fetchOne`, `save`, `delete`) resolve an undecidable\n * rule through `checkOperation`'s `onUnknown: \"deny\"`, which is safe but\n * indistinguishable from a plain \"you may not do that\". Calling this first\n * turns the same condition into the refusal an operator can act on.\n */\nexport function assertSecurityRulesEnforceable(\n collection: CollectionConfig | undefined,\n targetOperation: SecurityOperation\n): void {\n for (const rule of applicableRules(collection, targetOperation)) {\n const conditions = securityRuleToConditions(rule);\n const clauses: (\"using\" | \"withCheck\")[] = targetOperation === \"insert\"\n ? [\"withCheck\"]\n : targetOperation === \"update\" ? [\"using\", \"withCheck\"] : [\"using\"];\n for (const clause of clauses) {\n const expr = clause === \"using\" ? conditions.usingExpr : conditions.withCheckExpr;\n if (!expr) continue;\n // `hasRow: true` — these callers evaluate against a fetched row, so\n // only the nodes no JavaScript evaluator can decide are refused.\n const offending = findUntranslatable(expr, true);\n if (offending) {\n throw securityRuleUnsupported(collection?.slug ?? \"unknown\", clause, describe(offending));\n }\n }\n }\n}\n\n/**\n * Build the MongoDB filter that narrows a query to the rows `user` may see\n * under `collection`'s security rules.\n *\n * Returns `null` when no row can qualify (the caller answers with an empty\n * result), `{}` when the rules impose no narrowing, and throws\n * {@link SECURITY_RULE_UNSUPPORTED} when a rule cannot be translated.\n */\nexport function buildMongoFilterFromSecurityRules<M extends Record<string, any>>(\n collection: CollectionConfig<M> | undefined,\n user: User | undefined,\n targetOperation: SecurityOperation\n): Filter<Document> | null {\n const rules = applicableRules(collection as CollectionConfig | undefined, targetOperation);\n if (!collection?.securityRules || collection.securityRules.length === 0) {\n return MATCH_ALL;\n }\n // Rules exist but none covers this operation — Postgres denies, so do we.\n if (rules.length === 0) return null;\n\n const clause = clauseFor(targetOperation);\n const permissive: Filter<Document>[] = [];\n const restrictive: Filter<Document>[] = [];\n\n for (const rule of rules) {\n const conditions = securityRuleToConditions(rule);\n const expr = clause === \"using\" ? conditions.usingExpr : conditions.withCheckExpr;\n // A null clause denies, matching Postgres's `USING (false)`.\n const filter = expr === null ? MATCH_NONE : policyToMongoFilter(expr, user);\n if (filter === UNTRANSLATABLE) {\n const offending = expr === null ? undefined : findUntranslatable(expr, false);\n throw securityRuleUnsupported(\n collection.slug,\n clause,\n offending ? describe(offending) : \"this rule\"\n );\n }\n if ((rule.mode || \"permissive\") === \"restrictive\") {\n restrictive.push(filter);\n } else {\n permissive.push(filter);\n }\n }\n\n // No permissive rule can grant → nothing is visible, exactly as\n // `checkOperation` returns false when `hasPermissive` is false.\n if (permissive.length === 0) return null;\n\n const parts: Filter<Document>[] = [];\n if (!permissive.some(isMatchAll)) {\n // A permissive rule that matches nothing contributes nothing to the\n // union; if that is all of them, nothing is visible.\n const granting = permissive.filter(p => !isMatchNone(p));\n if (granting.length === 0) return null;\n parts.push(granting.length === 1 ? granting[0] : ({ $or: granting } as Filter<Document>));\n }\n\n for (const rf of restrictive) {\n if (isMatchNone(rf)) return null;\n if (!isMatchAll(rf)) parts.push(rf);\n }\n\n if (parts.length === 0) return MATCH_ALL;\n if (parts.length === 1) return parts[0];\n return { $and: parts } as Filter<Document>;\n}\n","/**\n * MongoDB DataDriver Delegate\n *\n * Implements the DataDriver interface for Rebase frontend integration.\n * This is the main entry point for Rebase to interact with MongoDB.\n */\n\nimport { Db } from \"mongodb\";\nimport {\n DataDriver,\n DeleteProps,\n Entity,\n CollectionConfig,\n FetchCollectionProps,\n FetchOneProps,\n ListenCollectionProps,\n ListenOneProps,\n SaveProps,\n RebaseCallContext,\n CollectionRegistryInterface,\n User,\n RebaseClient,\n RebaseData,\n RebaseSdkData,\n SecurityOperation\n} from \"@rebasepro/types\";\nimport { MongoDataService } from \"../db/MongoDataService\";\nimport { MongoRealtimeService } from \"./MongoRealtimeService\";\nimport { MongoHistoryService } from \"./MongoHistoryService\";\nimport { buildPropertyCallbacks, updateDateAutoValues, buildSdkData, checkOperation, PolicyClauses } from \"@rebasepro/common\";\nimport { mergeDeep } from \"@rebasepro/utils\";\nimport { Filter, Document } from \"mongodb\";\nimport { ApiError } from \"@rebasepro/server\";\nimport { MongoConditionBuilder } from \"../db/MongoConditionBuilder\";\nimport { assertSecurityRulesEnforceable, buildMongoFilterFromSecurityRules } from \"../db/securityRuleFilter\";\nimport { logger } from \"@rebasepro/server\";\n\n/**\n * MongoDB DataDriver Delegate\n *\n * Implements the DataDriver interface for Rebase.\n * Provides all data operations needed by the Rebase frontend.\n */\nexport class MongoDriver implements DataDriver {\n key = \"mongodb\";\n initialised = true;\n\n private dataService: MongoDataService;\n private realtimeService: MongoRealtimeService;\n public historyService: MongoHistoryService;\n public user?: User;\n public data: RebaseSdkData;\n public client?: RebaseClient;\n\n constructor(\n private db: Db,\n realtimeService?: MongoRealtimeService,\n historyService?: MongoHistoryService,\n public readonly registry?: CollectionRegistryInterface,\n user?: User\n ) {\n this.dataService = new MongoDataService(db);\n this.realtimeService = realtimeService ?? new MongoRealtimeService(db);\n this.historyService = historyService ?? new MongoHistoryService(db);\n this.user = user;\n this.data = buildSdkData(this);\n this.realtimeService.setDataDriver(this);\n }\n\n /**\n * Get the current timestamp\n */\n currentTime(): Date {\n return new Date();\n }\n\n /**\n * Resolve a collection's callbacks and property callbacks from the registry.\n * Used by AuthenticatedMongoDriver to apply callbacks after RLS filtering.\n */\n resolveCollectionCallbacks<M extends Record<string, unknown>>(\n collection: CollectionConfig<M> | undefined,\n path: string\n ) {\n if (!collection && !path) return { collection: undefined,\ncallbacks: undefined,\nglobalCallbacks: undefined,\npropertyCallbacks: undefined };\n const registryCollection = this.registry?.getCollectionByPath(path);\n const resolvedCollection = registryCollection\n ? ({ ...collection,\n...registryCollection } as CollectionConfig<M>)\n : (collection as CollectionConfig<M>);\n\n const callbacks = resolvedCollection?.callbacks;\n const globalCallbacks = this.registry?.getGlobalCallbacks();\n const properties = resolvedCollection?.properties;\n let propertyCallbacks;\n if (properties) {\n propertyCallbacks = buildPropertyCallbacks(properties);\n }\n return {\n collection: resolvedCollection,\n callbacks,\n globalCallbacks,\n propertyCallbacks\n };\n }\n\n /**\n * Fetch a collection of rows\n */\n async fetchCollection<M extends Record<string, any>>(\n props: FetchCollectionProps<M>\n ): Promise<Record<string, unknown>[]> {\n // Forwarded whole rather than re-listed. The hand-written list here\n // named eight of the eleven fields `FetchCollectionProps` declares, so\n // `logical` and `offset` were accepted by every type-checked boundary\n // above and then dropped — an `or(...)` query ran unfiltered and\n // `?offset=` served page one.\n const { path, collection, ...query } = props;\n const rows = await this.dataService.fetchCollection<M>(path, {\n ...query,\n collection: collection as CollectionConfig\n });\n\n const { collection: resolvedCollection, callbacks, globalCallbacks, propertyCallbacks } = this.resolveCollectionCallbacks(collection, path);\n\n if (globalCallbacks?.afterRead || callbacks?.afterRead || propertyCallbacks?.afterRead) {\n const contextForCallback = {\n user: this.user,\n driver: this,\n data: this.data,\n client: this.client,\n storageSource: this.client?.storage\n } as unknown as RebaseCallContext; // Backend context\n return Promise.all(rows.map(async (row) => {\n let fetched = row;\n if (globalCallbacks?.afterRead) {\n fetched = await globalCallbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n row: fetched,\n context: contextForCallback\n }) ?? fetched;\n }\n if (callbacks?.afterRead) {\n fetched = await callbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n row: fetched,\n context: contextForCallback\n }) ?? fetched;\n }\n if (propertyCallbacks?.afterRead) {\n fetched = await propertyCallbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n row: fetched,\n context: contextForCallback\n }) ?? fetched;\n }\n return fetched;\n }));\n }\n\n return rows;\n }\n\n /**\n * Listen to collection changes.\n *\n * `authContext` is not part of `ListenCollectionProps`; it is supplied by\n * {@link AuthenticatedMongoDriver}, which is the only caller that has one.\n * It has to travel *into* the subscription config, because that config is\n * what every re-fetch reads — the wrapper used to stamp the field on the\n * `Subscription` object instead, and nothing has ever read that one.\n */\n listenCollection<M extends Record<string, any>>({\n path,\n collection,\n filter,\n limit,\n startAfter,\n orderBy,\n searchString,\n order,\n onUpdate,\n onError\n }: ListenCollectionProps<M>, authContext?: { uid: string; roles: string[] }): () => void {\n const subscriptionId = this.generateSubscriptionId();\n\n const callback = (rows: Record<string, unknown>[]) => {\n try {\n onUpdate(rows);\n } catch (error) {\n logger.error(\"Error in collection update callback\", { error: error });\n if (onError) {\n onError(error instanceof Error ? error : new Error(String(error)));\n }\n }\n };\n\n this.realtimeService.subscribeToCollection(\n subscriptionId,\n {\n clientId: \"driver\",\n path,\n filter,\n orderBy,\n order,\n limit,\n startAfter,\n searchString,\n authContext\n },\n callback\n );\n\n // Return unsubscribe function\n return () => {\n this.realtimeService.unsubscribe(subscriptionId);\n };\n }\n\n /**\n * Fetch a single row\n */\n async fetchOne<M extends Record<string, any>>({\n path,\n id,\n databaseId,\n collection\n }: FetchOneProps<M>): Promise<Record<string, unknown> | undefined> {\n let row = await this.dataService.fetchOne<M>(path, id, databaseId);\n\n const { collection: resolvedCollection, callbacks, globalCallbacks, propertyCallbacks } = this.resolveCollectionCallbacks(collection, path);\n\n if (row && (globalCallbacks?.afterRead || callbacks?.afterRead || propertyCallbacks?.afterRead)) {\n const contextForCallback = {\n user: this.user,\n driver: this,\n data: this.data,\n client: this.client,\n storageSource: this.client?.storage\n } as unknown as RebaseCallContext; // Backend context\n let processedRow: Record<string, unknown> = row;\n if (globalCallbacks?.afterRead) {\n processedRow = await globalCallbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n row: processedRow,\n context: contextForCallback\n }) ?? processedRow;\n }\n if (callbacks?.afterRead) {\n processedRow = await callbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n row: processedRow,\n context: contextForCallback\n }) ?? processedRow;\n }\n if (propertyCallbacks?.afterRead) {\n processedRow = await propertyCallbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n row: processedRow,\n context: contextForCallback\n }) ?? processedRow;\n }\n row = processedRow;\n }\n\n return row;\n }\n\n /**\n * Listen to row changes\n */\n listenOne<M extends Record<string, any>>({\n path,\n id,\n collection,\n onUpdate,\n onError\n }: ListenOneProps<M>, authContext?: { uid: string; roles: string[] }): () => void {\n const subscriptionId = this.generateSubscriptionId();\n\n const callback = (row: Record<string, unknown> | null) => {\n try {\n onUpdate(row);\n } catch (error) {\n logger.error(\"Error in row update callback\", { error: error });\n if (onError) {\n onError(error instanceof Error ? error : new Error(String(error)));\n }\n }\n };\n\n this.realtimeService.subscribeToOne(\n subscriptionId,\n {\n clientId: \"driver\",\n path,\n id,\n authContext\n },\n callback\n );\n\n // Return unsubscribe function\n return () => {\n this.realtimeService.unsubscribe(subscriptionId);\n };\n }\n\n /**\n * Save an row (create or update)\n */\n async save<M extends Record<string, any>>({\n path,\n id,\n values,\n collection,\n status\n }: SaveProps<M>): Promise<Record<string, unknown>> {\n const { collection: resolvedCollection, callbacks, globalCallbacks, propertyCallbacks } = this.resolveCollectionCallbacks(collection, path);\n\n let updatedValues = values;\n const contextForCallback = {\n user: this.user,\n driver: this,\n data: this.data,\n client: this.client,\n storageSource: this.client?.storage\n } as unknown as RebaseCallContext;\n\n // Fetch previous values for callbacks AND history recording\n let previousValuesForHistory: Partial<M> | undefined;\n if (status === \"existing\" && id) {\n const existing = await this.dataService.fetchOne<M>(path, id, resolvedCollection?.databaseId);\n if (existing) {\n const { id: _existingId, ...existingValues } = existing;\n previousValuesForHistory = existingValues as Partial<M>;\n }\n }\n\n if (globalCallbacks?.beforeSave || callbacks?.beforeSave || propertyCallbacks?.beforeSave) {\n if (globalCallbacks?.beforeSave) {\n const result = await globalCallbacks.beforeSave({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n id,\n values: updatedValues,\n previousValues: previousValuesForHistory,\n status,\n context: contextForCallback\n });\n if (result) updatedValues = mergeDeep(updatedValues, result);\n }\n\n if (callbacks?.beforeSave) {\n const result = await callbacks.beforeSave({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n id,\n values: updatedValues,\n previousValues: previousValuesForHistory,\n status,\n context: contextForCallback\n });\n if (result) updatedValues = mergeDeep(updatedValues, result);\n }\n\n if (propertyCallbacks?.beforeSave) {\n const result = await propertyCallbacks.beforeSave({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n id,\n values: updatedValues,\n previousValues: previousValuesForHistory,\n status,\n context: contextForCallback\n });\n if (result) updatedValues = mergeDeep(updatedValues, result);\n }\n }\n\n // Apply autoValue timestamps (on_create / on_update) at the application layer.\n if (resolvedCollection?.properties) {\n updatedValues = updateDateAutoValues({\n inputValues: updatedValues,\n properties: resolvedCollection.properties,\n status: status ?? \"new\",\n timestampNowValue: new Date()\n });\n }\n\n try {\n let savedRow = await this.dataService.save<M>(\n path,\n updatedValues,\n id,\n resolvedCollection?.databaseId\n );\n\n if (savedRow && (globalCallbacks?.afterRead || callbacks?.afterRead || propertyCallbacks?.afterRead)) {\n if (globalCallbacks?.afterRead) {\n savedRow = await globalCallbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n row: savedRow,\n context: contextForCallback\n }) ?? savedRow;\n }\n if (callbacks?.afterRead) {\n savedRow = await callbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n row: savedRow,\n context: contextForCallback\n }) ?? savedRow;\n }\n if (propertyCallbacks?.afterRead) {\n savedRow = await propertyCallbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n row: savedRow,\n context: contextForCallback\n }) ?? savedRow;\n }\n }\n\n const savedId = savedRow.id as string | number;\n const { id: _savedId, ...savedValues } = savedRow;\n\n if (globalCallbacks?.afterSave || callbacks?.afterSave || propertyCallbacks?.afterSave) {\n if (globalCallbacks?.afterSave) {\n await globalCallbacks.afterSave({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n id: savedId,\n values: savedValues,\n previousValues: previousValuesForHistory,\n status,\n context: contextForCallback\n });\n }\n if (callbacks?.afterSave) {\n await callbacks.afterSave({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n id: savedId,\n values: savedValues as Partial<M>,\n previousValues: previousValuesForHistory,\n status,\n context: contextForCallback\n });\n }\n if (propertyCallbacks?.afterSave) {\n await propertyCallbacks.afterSave({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n id: savedId,\n values: savedValues,\n previousValues: previousValuesForHistory,\n status,\n context: contextForCallback\n });\n }\n }\n\n // Record row history (fire-and-forget, never blocks the save)\n if (this.historyService && resolvedCollection?.history) {\n this.historyService.recordHistory({\n tableName: path,\n id: savedId.toString(),\n action: status === \"new\" ? \"create\" : \"update\",\n values: savedValues as Record<string, unknown>,\n previousValues: previousValuesForHistory as Record<string, unknown> | undefined,\n updatedBy: this.user?.uid\n }).catch(err => {\n logger.error(`Failed to record history for ${path}/${savedId}`, { error: err });\n });\n }\n\n // Notify real-time subscribers\n await this.realtimeService.notifyUpdate(\n path,\n savedId.toString(),\n savedRow\n );\n\n return savedRow;\n } catch (error) {\n if (callbacks?.afterSaveError || propertyCallbacks?.afterSaveError) {\n if (callbacks?.afterSaveError) {\n await callbacks.afterSaveError({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n id: id || \"unknown\",\n values: updatedValues,\n previousValues: undefined,\n status,\n context: contextForCallback\n });\n }\n if (propertyCallbacks?.afterSaveError) {\n await propertyCallbacks.afterSaveError({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n id: id || \"unknown\",\n values: updatedValues,\n previousValues: undefined,\n status,\n context: contextForCallback\n });\n }\n }\n throw error;\n }\n }\n\n /**\n * Delete an row\n */\n async delete<M extends Record<string, any>>({\n row,\n collection\n }: DeleteProps<M>): Promise<void> {\n const { collection: resolvedCollection, callbacks, globalCallbacks, propertyCallbacks } = this.resolveCollectionCallbacks(collection, row.path);\n\n const callbackRow: Record<string, unknown> = { id: row.id, ...(row.values ?? {}) };\n\n const contextForCallback = {\n user: this.user,\n driver: this,\n data: this.data,\n client: this.client,\n storageSource: this.client?.storage\n } as unknown as RebaseCallContext;\n\n if (globalCallbacks?.beforeDelete || callbacks?.beforeDelete || propertyCallbacks?.beforeDelete) {\n let preventDefault = false;\n if (globalCallbacks?.beforeDelete) {\n const result = await globalCallbacks.beforeDelete({\n collection: resolvedCollection as CollectionConfig<M>,\n path: row.path,\n id: row.id,\n row: callbackRow,\n context: contextForCallback\n });\n if (result === false) {\n preventDefault = true;\n }\n }\n if (callbacks?.beforeDelete) {\n const result = await callbacks.beforeDelete({\n collection: resolvedCollection as CollectionConfig<M>,\n path: row.path,\n id: row.id,\n row: callbackRow,\n context: contextForCallback\n });\n if (result === false) {\n preventDefault = true;\n }\n }\n if (propertyCallbacks?.beforeDelete) {\n const result = await propertyCallbacks.beforeDelete({\n collection: resolvedCollection as CollectionConfig<M>,\n path: row.path,\n id: row.id,\n row: callbackRow,\n context: contextForCallback\n });\n if (result === false) {\n preventDefault = true;\n }\n }\n if (preventDefault) {\n return;\n }\n }\n\n await this.dataService.delete(row.path, row.id);\n\n if (globalCallbacks?.afterDelete || callbacks?.afterDelete || propertyCallbacks?.afterDelete) {\n if (globalCallbacks?.afterDelete) {\n await globalCallbacks.afterDelete({\n collection: resolvedCollection as CollectionConfig<M>,\n path: row.path,\n id: row.id,\n row: callbackRow,\n context: contextForCallback\n });\n }\n if (callbacks?.afterDelete) {\n await callbacks.afterDelete({\n collection: resolvedCollection as CollectionConfig<M>,\n path: row.path,\n id: row.id,\n row: callbackRow,\n context: contextForCallback\n });\n }\n if (propertyCallbacks?.afterDelete) {\n await propertyCallbacks.afterDelete({\n collection: resolvedCollection as CollectionConfig<M>,\n path: row.path,\n id: row.id,\n row: callbackRow,\n context: contextForCallback\n });\n }\n }\n\n // Record history\n if (this.historyService && resolvedCollection?.history) {\n this.historyService.recordHistory({\n action: \"delete\",\n id: String(row.id),\n tableName: row.path,\n previousValues: row.values,\n updatedBy: this.user?.uid\n }).catch(err => {\n logger.error(`Failed to record history for ${row.path}/${row.id}`, { error: err });\n });\n }\n\n // Notify subscribers of the deletion\n await this.realtimeService.notifyUpdate(row.path, String(row.id), null);\n }\n\n /**\n * Check if a field value is unique\n */\n async checkUniqueField(\n path: string,\n name: string,\n value: any,\n id?: string,\n collection?: CollectionConfig\n ): Promise<boolean> {\n return this.dataService.checkUniqueField(path, name, value, id);\n }\n\n /**\n * Generate a new row ID\n */\n generateId(path: string, collection?: CollectionConfig): string {\n return this.dataService.generateId();\n }\n\n /**\n * Count rows in a collection\n */\n async count<M extends Record<string, any>>({\n path,\n collection,\n filter,\n logical,\n searchString\n }: FetchCollectionProps<M>): Promise<number> {\n // The same narrowing the listing gets, or the total describes a\n // different query than the rows it is reported beside.\n return this.dataService.count<M>(path, {\n filter,\n logical,\n searchString,\n collection: collection as CollectionConfig\n });\n }\n\n /**\n * Generate a unique subscription ID\n */\n private generateSubscriptionId(): string {\n return `mongo_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n }\n\n /**\n * Check if the delegate is ready\n */\n isReady(): boolean {\n return this.initialised;\n }\n\n /**\n * Get the underlying row service for direct access\n */\n getDataService(): MongoDataService {\n return this.dataService;\n }\n\n /**\n * Get the underlying realtime service for direct access\n */\n getRealtimeService(): MongoRealtimeService {\n return this.realtimeService;\n }\n\n /**\n * Scope the MongoDriver with an authenticated user context\n */\n async withAuth(user: User): Promise<DataDriver> {\n return new AuthenticatedMongoDriver(this, user);\n }\n}\n\nexport class AuthenticatedMongoDriver implements DataDriver {\n key = \"mongodb\";\n initialised = true;\n public user: User;\n public data: RebaseSdkData;\n\n constructor(public delegate: MongoDriver, user: User) {\n this.user = user;\n this.data = buildSdkData(this);\n }\n\n currentTime(): Date {\n return this.delegate.currentTime();\n }\n\n async fetchCollection<M extends Record<string, any>>(props: FetchCollectionProps<M>): Promise<Record<string, unknown>[]> {\n const { collection: resolvedCollection } = this.delegate.resolveCollectionCallbacks(props.collection, props.path);\n const rlsFilter = buildMongoFilterFromSecurityRules(resolvedCollection, this.user, \"select\");\n if (rlsFilter === null) {\n return [];\n }\n\n // `logical` belongs in this query, not in the props spread below: the\n // repository reads `rawQuery ?? buildQuery(...)`, so a `logical` that\n // travelled only in the spread was never consulted — and a dropped\n // `or(...)` group does not fail, it widens.\n const userQuery = MongoConditionBuilder.buildQuery({\n filter: props.filter,\n logical: props.logical,\n searchString: props.searchString,\n properties: resolvedCollection?.properties\n });\n\n const combinedQuery = Object.keys(rlsFilter).length > 0\n ? ({ $and: [userQuery, rlsFilter] } as Filter<Document>)\n : userQuery;\n\n const originalService = this.delegate.getDataService();\n const rows = await originalService.fetchCollection<M>(props.path, {\n ...props,\n rawQuery: combinedQuery,\n collection: resolvedCollection\n });\n\n const { callbacks, globalCallbacks, propertyCallbacks } = this.delegate.resolveCollectionCallbacks(props.collection, props.path);\n\n if (globalCallbacks?.afterRead || callbacks?.afterRead || propertyCallbacks?.afterRead) {\n const contextForCallback = {\n user: this.user,\n driver: this,\n data: this.data,\n client: this.delegate.client,\n storageSource: this.delegate.client?.storage\n } as unknown as RebaseCallContext;\n return Promise.all(rows.map(async (row) => {\n let fetched = row;\n if (globalCallbacks?.afterRead) {\n fetched = await globalCallbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path: props.path,\n row: fetched,\n context: contextForCallback\n }) ?? fetched;\n }\n if (callbacks?.afterRead) {\n fetched = await callbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path: props.path,\n row: fetched,\n context: contextForCallback\n }) ?? fetched;\n }\n if (propertyCallbacks?.afterRead) {\n fetched = await propertyCallbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path: props.path,\n row: fetched,\n context: contextForCallback\n }) ?? fetched;\n }\n return fetched;\n }));\n }\n\n return rows;\n }\n\n listenCollection<M extends Record<string, any>>(props: ListenCollectionProps<M>): () => void {\n // Handed to the subscription rather than stamped on it afterwards: the\n // config is what every re-fetch reads, and the stamp also landed after\n // the initial fetch had already been dispatched unfiltered.\n return this.delegate.listenCollection(props, this.authContext());\n }\n\n /** The acting user, in the shape the realtime subscriptions carry. */\n private authContext(): { uid: string; roles: string[] } {\n return { uid: this.user.uid,\nroles: this.user.roles ?? [] };\n }\n\n /**\n * Evaluate the collection's rules for one row, fail-closed.\n *\n * A path with no resolvable collection has no declared rules — the same\n * answer `buildMongoFilterFromSecurityRules` gives a listing on such a path,\n * so the two never disagree about whether this engine has row security.\n */\n private authorize(\n collection: CollectionConfig | undefined,\n entity: Entity,\n operation: SecurityOperation,\n clauses?: PolicyClauses\n ): boolean {\n if (!collection) return true;\n return checkOperation(collection, { user: this.user }, entity, operation, { onUnknown: \"deny\",\nclauses });\n }\n\n async fetchOne<M extends Record<string, any>>(props: FetchOneProps<M>): Promise<Record<string, unknown> | undefined> {\n const { collection: resolvedCollection } = this.delegate.resolveCollectionCallbacks(props.collection, props.path);\n assertSecurityRulesEnforceable(resolvedCollection, \"select\");\n const row = await this.delegate.fetchOne(props);\n if (row && !this.authorize(resolvedCollection, rowToEntityForCheck(row, props.path), \"select\")) {\n return undefined;\n }\n return row;\n }\n\n listenOne<M extends Record<string, any>>(props: ListenOneProps<M>): () => void {\n return this.delegate.listenOne(props, this.authContext());\n }\n\n /**\n * Save, with both halves of the rule checked *before* the write.\n *\n * There is no transaction here, so a check that runs after\n * `delegate.save` cannot undo anything: the document is written, history is\n * recorded and subscribers have been notified by then, and a 403 at that\n * point only misleads the caller about what happened. Postgres evaluates\n * `WITH CHECK` inside the transaction; the closest this driver can get is to\n * evaluate it against the row as it *will* be, and refuse before writing.\n */\n async save<M extends Record<string, any>>(props: SaveProps<M>): Promise<Record<string, unknown>> {\n const { collection: resolvedCollection } = this.delegate.resolveCollectionCallbacks(props.collection, props.path);\n\n if (props.status === \"existing\" && props.id) {\n assertSecurityRulesEnforceable(resolvedCollection, \"update\");\n const existing = await this.delegate.fetchOne({ path: props.path,\nid: props.id,\ncollection: resolvedCollection });\n // USING against the stored row, WITH CHECK against the row that\n // will replace it — the split Postgres makes, and the reason\n // `clauses` exists on `checkOperation`.\n const projected = rowToEntityForCheck({ ...existing,\n...props.values,\nid: props.id }, props.path);\n if (!existing ||\n !this.authorize(resolvedCollection, rowToEntityForCheck(existing, props.path), \"update\", \"using\") ||\n !this.authorize(resolvedCollection, projected, \"update\", \"withCheck\")) {\n throw ApiError.forbidden(\"Forbidden\");\n }\n } else {\n assertSecurityRulesEnforceable(resolvedCollection, \"insert\");\n const tempEntity = { id: props.id || \"new\",\npath: props.path,\nvalues: props.values } as Entity;\n if (!this.authorize(resolvedCollection, tempEntity, \"insert\")) {\n throw ApiError.forbidden(\"Forbidden\");\n }\n }\n\n return this.delegate.save({\n ...props,\n collection: resolvedCollection\n });\n }\n\n async delete<M extends Record<string, any>>(props: DeleteProps<M>): Promise<void> {\n const { collection: resolvedCollection } = this.delegate.resolveCollectionCallbacks(props.collection, props.row.path);\n assertSecurityRulesEnforceable(resolvedCollection, \"delete\");\n\n const existing = await this.delegate.fetchOne({ path: props.row.path,\nid: props.row.id,\ncollection: resolvedCollection });\n if (!existing || !this.authorize(resolvedCollection, rowToEntityForCheck(existing, props.row.path), \"delete\")) {\n throw ApiError.forbidden(\"Forbidden\");\n }\n\n return this.delegate.delete(props);\n }\n\n async checkUniqueField(\n path: string,\n name: string,\n value: any,\n id?: string,\n collection?: CollectionConfig\n ): Promise<boolean> {\n return this.delegate.checkUniqueField(path, name, value, id, collection);\n }\n\n generateId(path: string, collection?: CollectionConfig): string {\n return this.delegate.generateId(path, collection);\n }\n\n async count<M extends Record<string, any>>(props: FetchCollectionProps<M>): Promise<number> {\n const { collection: resolvedCollection } = this.delegate.resolveCollectionCallbacks(props.collection, props.path);\n const rlsFilter = buildMongoFilterFromSecurityRules(resolvedCollection, this.user, \"select\");\n if (rlsFilter === null) {\n return 0;\n }\n\n // Narrowed by exactly what the listing is narrowed by — `logical`\n // included — or the total describes a different query than the rows.\n const userQuery = MongoConditionBuilder.buildQuery({\n filter: props.filter,\n logical: props.logical,\n searchString: props.searchString,\n properties: resolvedCollection?.properties\n });\n\n const combinedQuery = Object.keys(rlsFilter).length > 0\n ? ({ $and: [userQuery, rlsFilter] } as Filter<Document>)\n : userQuery;\n\n const originalService = this.delegate.getDataService();\n return originalService.count(props.path, {\n ...props,\n rawQuery: combinedQuery\n });\n }\n\n isReady(): boolean {\n return this.delegate.isReady();\n }\n}\n\n/**\n * Wrap a flat row into the Entity shape expected by `checkOperation`,\n * which evaluates security rules against `row.values`.\n */\nfunction rowToEntityForCheck(row: Record<string, unknown>, path: string): Entity {\n return {\n id: row.id as string | number,\n path,\n values: row\n };\n}\n","/**\n * MongoDB Backend Factory\n *\n * This module provides factory functions for creating MongoDB backend instances.\n * It abstracts the creation of drivers, realtime services, and row services.\n */\n\nimport { Db, MongoClient } from \"mongodb\";\nimport { DataDriver, CollectionConfig, getCollectionDataPath } from \"@rebasepro/types\";\n\nimport { MongoDataService } from \"./db/MongoDataService\";\nimport { MongoRealtimeService } from \"./services/MongoRealtimeService\";\nimport { MongoDriver } from \"./services/MongoDriver\";\nimport { MongoHistoryService, HistoryRetentionConfig } from \"./services/MongoHistoryService\";\nimport { MongoDBConnection } from \"./connection\";\nimport { BackendConfig, BackendInstance, CollectionRegistryInterface, DataRepository, RealtimeProvider, DatabaseConnection, DatabaseAdmin, DocumentAdmin, SchemaAdmin, HealthCheckResult } from \"@rebasepro/types\";\n\n/**\n * Configuration for creating a MongoDB backend.\n */\nexport interface MongoBackendConfig extends BackendConfig {\n type: \"mongodb\";\n /** MongoDB database instance */\n connection: Db;\n /** MongoDB client (for connection management) */\n client: MongoClient;\n /** Collections to register (optional, can be registered later) */\n collections?: CollectionConfig[];\n /** History retention configuration */\n historyRetention?: Partial<HistoryRetentionConfig>;\n}\n\n/**\n * MongoDB-specific backend instance with additional MongoDB types.\n */\nexport interface MongoBackendInstance extends BackendInstance {\n /** The MongoDB database instance */\n db: Db;\n /** The MongoDB client */\n client: MongoClient;\n /** MongoDB DataDriver for use with Rebase */\n driver: DataDriver;\n /** Entity service for direct database operations */\n dataService: MongoDataService;\n /** Realtime service for subscriptions */\n realtimeService: MongoRealtimeService;\n /** Admin capabilities (DocumentAdmin + SchemaAdmin) */\n admin: DatabaseAdmin;\n}\n\n// =============================================================================\n// Simple Collection Registry\n// =============================================================================\n\n/**\n * Simple in-memory collection registry for MongoDB.\n */\nexport class MongoCollectionRegistry implements CollectionRegistryInterface {\n /** Every addressable key → collection. See {@link register}. */\n private collections = new Map<string, CollectionConfig>();\n /** Registration order, so `getCollections()` returns each collection once. */\n private registered: CollectionConfig[] = [];\n private _globalCallbacks?: any;\n\n /**\n * Register a collection under every name it can be addressed by.\n *\n * A Mongo collection has up to three: `slug` (the routing key), `path` (the\n * MongoDB collection-name override, which is what `getCollectionDataPath`\n * hands the driver) and `name` (the human label). Registering only `name`\n * meant every `getCollectionByPath` lookup missed — and the realtime path,\n * whose only source of the collection is this registry, ran with no\n * `securityRules`, no `properties` and no callbacks.\n */\n register(collection: CollectionConfig): void {\n this.registered.push(collection);\n for (const key of [getCollectionDataPath(collection), collection.slug, collection.name]) {\n if (key) this.collections.set(key, collection);\n }\n }\n\n /**\n * Get a collection by its path\n */\n getCollectionByPath(path: string): CollectionConfig | undefined {\n return this.collections.get(path);\n }\n\n /**\n * Get all registered collections\n */\n getCollections(): CollectionConfig[] {\n return [...this.registered];\n }\n\n /**\n * Get the currently registered global callbacks, if any.\n */\n getGlobalCallbacks(): any | undefined {\n return this._globalCallbacks;\n }\n\n /**\n * Set global lifecycle callbacks that apply to every collection.\n */\n setGlobalCallbacks(callbacks: any): void {\n this._globalCallbacks = callbacks;\n }\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\n/**\n * Create a complete MongoDB backend instance.\n *\n * This factory function creates all the necessary services for a MongoDB backend:\n * - MongoDBConnection (database connection wrapper)\n * - MongoDataService (implements DataRepository)\n * - MongoRealtimeService (implements RealtimeProvider)\n * - MongoCollectionRegistry (implements CollectionRegistryInterface)\n * - MongoDriver (for Rebase integration)\n *\n * @example\n * ```typescript\n * import { createMongoBackend } from \"@rebasepro/server-mongo\";\n *\n * const client = new MongoClient(\"mongodb://localhost:27017\");\n * await client.connect();\n * const db = client.db(\"my_database\");\n *\n * const backend = createMongoBackend({\n * type: \"mongodb\",\n * connection: db,\n * client: client,\n * collections: myCollections\n * });\n *\n * // Use the backend\n * const rows = await backend.entityRepository.fetchCollection(\"users\", {});\n * ```\n */\nexport function createMongoBackend(config: MongoBackendConfig): MongoBackendInstance {\n const { connection: db, client, collections } = config;\n\n // Create collection registry\n const collectionRegistry = new MongoCollectionRegistry();\n\n // Register collections if provided\n if (collections) {\n collections.forEach(collection => collectionRegistry.register(collection));\n }\n\n // Create services\n const dataService = new MongoDataService(db);\n const realtimeService = new MongoRealtimeService(db);\n const historyService = new MongoHistoryService(db, config.historyRetention);\n const driver = new MongoDriver(db, realtimeService, historyService, collectionRegistry);\n const mongoConnection = new MongoDBConnection(db, client);\n\n // Build admin capabilities for MongoDB\n const admin: DatabaseAdmin = {\n async executeAggregate(pipeline: Record<string, unknown>[]) {\n // Run aggregation on a collection — requires a target collection\n // from the pipeline's $match or $lookup stage:\n const firstStage = pipeline[0];\n const collName = typeof firstStage.$from === \"string\" ? firstStage.$from : \"__admin__\";\n const cursor = db.collection(collName).aggregate(pipeline);\n return await cursor.toArray() as Record<string, unknown>[];\n },\n async fetchCollectionStats(collectionName: string) {\n const stats = await db.command({ collStats: collectionName }) as { count: number; size: number };\n return { count: stats.count,\nsizeBytes: stats.size };\n },\n async fetchUnmappedTables(mappedPaths?: string[]) {\n const allCollections = await db.listCollections().toArray();\n const names = allCollections.map(c => c.name).filter(n => !n.startsWith(\"system.\"));\n if (!mappedPaths || mappedPaths.length === 0) return names;\n const mappedSet = new Set(mappedPaths.map(p => p.toLowerCase()));\n return names.filter(n => !mappedSet.has(n.toLowerCase()));\n },\n async fetchTableMetadata(collectionName: string) {\n // Sample a document to infer fields\n const sample = await db.collection(collectionName).findOne();\n if (!sample) return { columns: [],\nforeignKeys: [],\njunctions: [],\npolicies: [] };\n const columns = Object.entries(sample).map(([key, value]) => ({\n column_name: key,\n data_type: typeof value,\n udt_name: typeof value,\n is_nullable: \"YES\",\n column_default: null,\n character_maximum_length: null\n }));\n return { columns,\nforeignKeys: [],\njunctions: [],\npolicies: [] };\n }\n } satisfies DocumentAdmin & SchemaAdmin;\n\n return {\n // Abstract interface implementations\n connection: mongoConnection,\n entityRepository: dataService,\n realtimeProvider: realtimeService,\n collectionRegistry: collectionRegistry,\n admin,\n\n // Lifecycle\n async initialize() {\n // Connection is already established via the MongoClient constructor\n },\n async healthCheck(): Promise<HealthCheckResult> {\n const start = Date.now();\n try {\n await db.command({ ping: 1 });\n return { healthy: true,\nlatencyMs: Date.now() - start };\n } catch {\n return { healthy: false,\nlatencyMs: Date.now() - start };\n }\n },\n async destroy() {\n await client.close();\n },\n\n // MongoDB-specific accessors\n db,\n client,\n driver,\n dataService,\n realtimeService\n };\n}\n\n/**\n * Create a MongoDB DataDriver.\n *\n * This is a convenience function when you only need the DataDriver\n * without the full backend instance.\n *\n * @example\n * ```typescript\n * import { createMongoDelegate } from \"@rebasepro/server-mongo\";\n *\n * const delegate = createMongoDelegate(db);\n * ```\n */\nexport function createMongoDelegate(\n db: Db,\n realtimeService?: MongoRealtimeService,\n historyService?: MongoHistoryService,\n registry?: CollectionRegistryInterface\n): MongoDriver {\n const realtime = realtimeService ?? new MongoRealtimeService(db);\n const history = historyService ?? new MongoHistoryService(db);\n return new MongoDriver(db, realtime, history, registry);\n}\n\n/**\n * Create a RealtimeService for MongoDB.\n *\n * @example\n * ```typescript\n * import { createMongoRealtimeService } from \"@rebasepro/server-mongo\";\n *\n * const realtimeService = createMongoRealtimeService(db);\n * ```\n */\nexport function createMongoRealtimeService(db: Db): MongoRealtimeService {\n return new MongoRealtimeService(db);\n}\n\n/**\n * Create a MongoDB row repository.\n *\n * @example\n * ```typescript\n * import { createMongoEntityRepository } from \"@rebasepro/server-mongo\";\n *\n * const repository = createMongoEntityRepository(db);\n * const users = await repository.fetchCollection(\"users\", {});\n * ```\n */\nexport function createMongoEntityRepository(db: Db): DataRepository {\n return new MongoDataService(db);\n}\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\n/**\n * Check if a backend config is for MongoDB.\n */\nexport function isMongoBackendConfig(config: BackendConfig): config is MongoBackendConfig {\n return config.type === \"mongodb\" &&\n typeof (config as MongoBackendConfig).connection !== \"undefined\" &&\n typeof (config as MongoBackendConfig).client !== \"undefined\";\n}\n\n/**\n * Check if a driver config is for MongoDB.\n */\nexport function isMongoDriverConfig(obj: unknown): obj is { type: \"mongodb\"; connection: Db; client: MongoClient } {\n return typeof obj === \"object\" &&\n obj !== null &&\n \"type\" in obj &&\n (obj as Record<string, unknown>).type === \"mongodb\" &&\n \"connection\" in obj &&\n \"client\" in obj;\n}\n","import { Db, ObjectId } from \"mongodb\";\nimport { normalizeEmail } from \"@rebasepro/common\";\n\n/** Loose document type that allows string _id values (Rebase convention). */\nexport interface MongoDoc { _id?: string; [key: string]: any; }\nimport {\n UserRepository,\n RoleRepository,\n TokenRepository,\n AuthRepository,\n UserData,\n CreateUserData,\n RoleData,\n CreateRoleData,\n RefreshTokenInfo,\n RefreshTokenSession,\n PasswordResetTokenInfo,\n MagicLinkTokenInfo,\n UserIdentityData,\n ListUsersOptions,\n PaginatedUsersResult,\n MfaFactor,\n MfaChallengeInfo\n} from \"@rebasepro/server\";\n\nexport type Role = RoleData;\n\nfunction escapeRegExp(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction toUser(doc: any): UserData {\n return {\n id: doc._id || doc.id,\n email: doc.email,\n passwordHash: doc.passwordHash ?? null,\n displayName: doc.displayName ?? null,\n photoUrl: doc.photoUrl ?? null,\n emailVerified: doc.emailVerified ?? false,\n emailVerificationToken: doc.emailVerificationToken ?? null,\n emailVerificationSentAt: doc.emailVerificationSentAt ? new Date(doc.emailVerificationSentAt) : null,\n createdAt: new Date(doc.createdAt),\n updatedAt: new Date(doc.updatedAt)\n };\n}\n\nexport class MongoUserService implements UserRepository {\n constructor(private db: Db) {}\n\n private get collection() {\n return this.db.collection<MongoDoc>(\"rebase_users\");\n }\n\n private get identitiesCollection() {\n return this.db.collection<MongoDoc>(\"rebase_user_identities\");\n }\n\n private get userRolesCollection() {\n return this.db.collection<MongoDoc>(\"rebase_user_roles\");\n }\n\n private get rolesCollection() {\n return this.db.collection<MongoDoc>(\"rebase_roles\");\n }\n\n async createUser(data: CreateUserData): Promise<UserData> {\n const id = new ObjectId().toString();\n const now = new Date();\n const doc = {\n _id: id,\n id,\n email: normalizeEmail(data.email),\n passwordHash: data.passwordHash ?? null,\n displayName: data.displayName ?? null,\n photoUrl: data.photoUrl ?? null,\n emailVerified: data.emailVerified ?? false,\n createdAt: now,\n updatedAt: now\n };\n await this.collection.insertOne(doc);\n return toUser(doc);\n }\n\n async getUserById(id: string): Promise<UserData | null> {\n const doc = await this.collection.findOne({ id });\n return doc ? toUser(doc) : null;\n }\n\n async getUserByEmail(email: string): Promise<UserData | null> {\n const doc = await this.collection.findOne({ email: normalizeEmail(email) });\n return doc ? toUser(doc) : null;\n }\n\n async getUserByIdentity(provider: string, providerId: string): Promise<UserData | null> {\n const identity = await this.identitiesCollection.findOne({ provider,\nproviderId });\n if (!identity) return null;\n return this.getUserById(identity.uid);\n }\n\n async getUserIdentities(uid: string): Promise<UserIdentityData[]> {\n const docs = await this.identitiesCollection.find({ uid }).toArray();\n return docs.map(doc => ({\n id: doc.id,\n uid: doc.uid,\n provider: doc.provider,\n providerId: doc.providerId,\n profileData: doc.profileData ?? null,\n createdAt: new Date(doc.createdAt),\n updatedAt: new Date(doc.updatedAt)\n }));\n }\n\n async linkUserIdentity(uid: string, provider: string, providerId: string, profileData?: Record<string, unknown>): Promise<void> {\n const now = new Date();\n await this.identitiesCollection.updateOne(\n { provider,\nproviderId },\n {\n $setOnInsert: {\n _id: new ObjectId().toString(),\n id: new ObjectId().toString(),\n uid,\n provider,\n providerId,\n createdAt: now\n },\n $set: {\n profileData: profileData ?? null,\n updatedAt: now\n }\n },\n { upsert: true }\n );\n }\n\n async updateUser(id: string, data: Partial<Omit<CreateUserData, \"id\">>): Promise<UserData | null> {\n const updateData: Record<string, unknown> = { ...data,\nupdatedAt: new Date() };\n if (typeof updateData.email === \"string\") updateData.email = normalizeEmail(updateData.email);\n\n await this.collection.updateOne({ id }, { $set: updateData });\n return this.getUserById(id);\n }\n\n async deleteUser(id: string): Promise<void> {\n await this.collection.deleteOne({ id });\n await this.identitiesCollection.deleteMany({ uid: id });\n await this.userRolesCollection.deleteMany({ uid: id });\n }\n\n async listUsers(): Promise<UserData[]> {\n const docs = await this.collection.find().toArray();\n return docs.map(toUser);\n }\n\n async listUsersPaginated(options?: ListUsersOptions): Promise<PaginatedUsersResult> {\n const limit = options?.limit ?? 25;\n const offset = options?.offset ?? 0;\n const search = options?.search?.trim() || \"\";\n const orderBy = options?.orderBy || \"createdAt\";\n const orderDir = options?.orderDir || \"desc\";\n const roleId = options?.roleId;\n\n const query: Record<string, unknown> = {};\n\n if (search) {\n const escapedSearch = escapeRegExp(search);\n query.$or = [\n { email: { $regex: escapedSearch,\n$options: \"i\" } },\n { displayName: { $regex: escapedSearch,\n$options: \"i\" } }\n ];\n }\n\n if (roleId) {\n const userRoles = await this.userRolesCollection.find({ roleId }).toArray();\n const userIds = userRoles.map(ur => ur.uid);\n query.id = { $in: userIds };\n }\n\n const sort: Record<string, 1 | -1> = {};\n sort[orderBy] = orderDir === \"asc\" ? 1 : -1;\n\n const total = await this.collection.countDocuments(query);\n const docs = await this.collection.find(query).sort(sort).skip(offset).limit(limit).toArray();\n\n return {\n users: docs.map(toUser),\n total,\n limit,\n offset\n };\n }\n\n async updatePassword(id: string, passwordHash: string): Promise<void> {\n await this.collection.updateOne(\n { id },\n { $set: { passwordHash,\nupdatedAt: new Date() } }\n );\n }\n\n async setEmailVerified(id: string, verified: boolean): Promise<void> {\n await this.collection.updateOne(\n { id },\n { $set: { emailVerified: verified,\nemailVerificationToken: null,\nupdatedAt: new Date() } }\n );\n }\n\n async setVerificationToken(id: string, token: string | null): Promise<void> {\n await this.collection.updateOne(\n { id },\n { $set: { emailVerificationToken: token,\nemailVerificationSentAt: token ? new Date() : null,\nupdatedAt: new Date() } }\n );\n }\n\n async getUserByVerificationToken(token: string): Promise<UserData | null> {\n const doc = await this.collection.findOne({ emailVerificationToken: token });\n return doc ? toUser(doc) : null;\n }\n\n async getUserRoles(uid: string): Promise<RoleData[]> {\n const userRoles = await this.userRolesCollection.find({ uid }).toArray();\n const roleIds = userRoles.map(ur => ur.roleId);\n if (roleIds.length === 0) return [];\n\n const roles = await this.rolesCollection.find({ id: { $in: roleIds } }).toArray();\n return roles.map(r => ({\n id: r.id,\n name: r.name,\n isAdmin: r.isAdmin ?? false,\n defaultPermissions: r.defaultPermissions ?? null,\n collectionPermissions: r.collectionPermissions ?? null\n }));\n }\n\n async getUserRoleIds(uid: string): Promise<string[]> {\n const userRoles = await this.userRolesCollection.find({ uid }).toArray();\n return userRoles.map(ur => ur.roleId);\n }\n\n async setUserRoles(uid: string, roleIds: string[]): Promise<void> {\n await this.userRolesCollection.deleteMany({ uid });\n if (roleIds.length > 0) {\n const docs = roleIds.map(roleId => ({\n _id: new ObjectId().toString(),\n uid,\n roleId\n }));\n await this.userRolesCollection.insertMany(docs);\n }\n }\n\n async assignDefaultRole(uid: string, roleId: string): Promise<void> {\n await this.userRolesCollection.updateOne(\n { uid,\nroleId },\n { $setOnInsert: { _id: new ObjectId().toString(),\nuid,\nroleId } },\n { upsert: true }\n );\n }\n\n async getUserWithRoles(uid: string): Promise<{ user: UserData; roles: RoleData[] } | null> {\n const user = await this.getUserById(uid);\n if (!user) return null;\n const roles = await this.getUserRoles(uid);\n return { user,\nroles };\n }\n}\n\nexport class MongoRoleService implements RoleRepository {\n constructor(private db: Db) {}\n\n private get collection() {\n return this.db.collection<MongoDoc>(\"rebase_roles\");\n }\n\n async getRoleById(id: string): Promise<RoleData | null> {\n const doc = await this.collection.findOne({ id });\n if (!doc) return null;\n return {\n id: doc.id,\n name: doc.name,\n isAdmin: doc.isAdmin ?? false,\n defaultPermissions: doc.defaultPermissions ?? null,\n collectionPermissions: doc.collectionPermissions ?? null\n };\n }\n\n async listRoles(): Promise<RoleData[]> {\n const docs = await this.collection.find().sort({ name: 1 }).toArray();\n return docs.map(doc => ({\n id: doc.id,\n name: doc.name,\n isAdmin: doc.isAdmin ?? false,\n defaultPermissions: doc.defaultPermissions ?? null,\n collectionPermissions: doc.collectionPermissions ?? null\n }));\n }\n\n async createRole(data: CreateRoleData): Promise<RoleData> {\n const doc = {\n _id: data.id,\n id: data.id,\n name: data.name,\n isAdmin: data.isAdmin ?? false,\n defaultPermissions: data.defaultPermissions ?? null,\n collectionPermissions: data.collectionPermissions ?? null\n };\n await this.collection.insertOne(doc);\n return { ...doc } as RoleData;\n }\n\n async updateRole(id: string, data: Partial<Omit<RoleData, \"id\">>): Promise<RoleData | null> {\n await this.collection.updateOne({ id }, { $set: data });\n return this.getRoleById(id);\n }\n\n async deleteRole(id: string): Promise<void> {\n await this.collection.deleteOne({ id });\n await this.db.collection(\"rebase_user_roles\").deleteMany({ roleId: id });\n }\n}\n\nexport class MongoRefreshTokenService {\n constructor(private db: Db) {}\n\n private get collection() {\n return this.db.collection<MongoDoc>(\"rebase_refresh_tokens\");\n }\n\n private toInfo(doc: MongoDoc): RefreshTokenInfo {\n return {\n id: doc.id,\n uid: doc.uid,\n tokenHash: doc.tokenHash,\n expiresAt: new Date(doc.expiresAt),\n createdAt: new Date(doc.createdAt),\n userAgent: doc.userAgent,\n ipAddress: doc.ipAddress,\n sessionId: doc.sessionId,\n rotatedAt: doc.rotatedAt ? new Date(doc.rotatedAt) : null,\n revoked: Boolean(doc.revoked),\n sessionStartedAt: new Date(doc.sessionStartedAt || doc.createdAt)\n };\n }\n\n async createToken(\n uid: string,\n tokenHash: string,\n expiresAt: Date,\n userAgent?: string,\n ipAddress?: string,\n session?: RefreshTokenSession\n ): Promise<void> {\n const safeUserAgent = userAgent || \"\";\n const safeIpAddress = ipAddress || \"\";\n\n // No deleteMany first. Tokens of one sign-in accumulate under a shared\n // sessionId and are pruned once nobody can still be holding them —\n // evicting by (uid, userAgent, ipAddress) is what used to sign out a\n // second browser profile behind the same address.\n const now = new Date();\n await this.collection.insertOne({\n _id: new ObjectId().toString(),\n id: new ObjectId().toString(),\n uid,\n tokenHash,\n expiresAt,\n createdAt: now,\n userAgent: safeUserAgent,\n ipAddress: safeIpAddress,\n sessionId: session?.id ?? new ObjectId().toString(),\n sessionStartedAt: session?.startedAt ?? now,\n rotatedAt: null,\n revoked: false\n });\n }\n\n async findByHash(tokenHash: string): Promise<RefreshTokenInfo | null> {\n const doc = await this.collection.findOne({ tokenHash });\n return doc ? this.toInfo(doc) : null;\n }\n\n /** Superseded, not gone — see the Postgres service for why that matters. */\n async markRotated(tokenHash: string): Promise<void> {\n await this.collection.updateOne({ tokenHash }, { $set: { rotatedAt: new Date() } });\n }\n\n async revokeSession(sessionId: string): Promise<void> {\n await this.collection.updateMany(\n { sessionId },\n { $set: { revoked: true, rotatedAt: new Date() } }\n );\n }\n\n async prune(uid: string, sessionId: string, supersededBefore: Date): Promise<void> {\n await this.collection.deleteMany({\n uid,\n $or: [\n { expiresAt: { $lt: new Date() } },\n { sessionId, rotatedAt: { $ne: null, $lt: supersededBefore } }\n ]\n });\n }\n\n async getTokensValidAfter(uid: string): Promise<Date | null> {\n const user = await this.db.collection<MongoDoc>(\"rebase_users\").findOne({ id: uid });\n return user?.tokensValidAfter ? new Date(user.tokensValidAfter) : null;\n }\n\n async setTokensValidAfter(uid: string, at: Date): Promise<void> {\n await this.db.collection<MongoDoc>(\"rebase_users\")\n .updateOne({ id: uid }, { $set: { tokensValidAfter: at } });\n }\n\n async deleteByHash(tokenHash: string): Promise<void> {\n await this.collection.deleteOne({ tokenHash });\n }\n\n async deleteAllForUser(uid: string): Promise<void> {\n await this.collection.deleteMany({ uid });\n }\n\n async listForUser(uid: string): Promise<RefreshTokenInfo[]> {\n const docs = await this.collection.find({ uid }).sort({ createdAt: 1 }).toArray();\n return docs.map(doc => this.toInfo(doc));\n }\n\n async deleteById(id: string, uid: string): Promise<void> {\n await this.collection.deleteOne({ id,\nuid });\n }\n}\n\nexport class MongoPasswordResetTokenService {\n constructor(private db: Db) {}\n\n private get collection() {\n return this.db.collection<MongoDoc>(\"rebase_password_reset_tokens\");\n }\n\n async createToken(uid: string, tokenHash: string, expiresAt: Date): Promise<void> {\n await this.collection.deleteMany({ uid,\nusedAt: null });\n\n await this.collection.insertOne({\n _id: new ObjectId().toString(),\n uid,\n tokenHash,\n expiresAt,\n usedAt: null\n });\n }\n\n async findValidByHash(tokenHash: string): Promise<{ uid: string; expiresAt: Date } | null> {\n const doc = await this.collection.findOne({\n tokenHash,\n usedAt: null,\n expiresAt: { $gt: new Date() }\n });\n\n if (!doc) return null;\n\n return {\n uid: doc.uid,\n expiresAt: new Date(doc.expiresAt)\n };\n }\n\n async markAsUsed(tokenHash: string): Promise<void> {\n await this.collection.updateOne(\n { tokenHash },\n { $set: { usedAt: new Date() } }\n );\n }\n\n async deleteAllForUser(uid: string): Promise<void> {\n await this.collection.deleteMany({ uid });\n }\n\n async deleteExpired(): Promise<void> {\n await this.collection.deleteMany({ expiresAt: { $lt: new Date() } });\n }\n}\n\nexport class MongoTokenRepository implements TokenRepository {\n private refreshTokenService: MongoRefreshTokenService;\n private passwordResetTokenService: MongoPasswordResetTokenService;\n\n constructor(private db: Db) {\n this.refreshTokenService = new MongoRefreshTokenService(db);\n this.passwordResetTokenService = new MongoPasswordResetTokenService(db);\n }\n\n async createRefreshToken(uid: string, tokenHash: string, expiresAt: Date, userAgent?: string, ipAddress?: string, session?: RefreshTokenSession): Promise<void> {\n await this.refreshTokenService.createToken(uid, tokenHash, expiresAt, userAgent, ipAddress, session);\n }\n\n async markRefreshTokenRotated(tokenHash: string): Promise<void> {\n await this.refreshTokenService.markRotated(tokenHash);\n }\n\n async revokeRefreshTokenSession(sessionId: string): Promise<void> {\n await this.refreshTokenService.revokeSession(sessionId);\n }\n\n async pruneRefreshTokens(uid: string, sessionId: string, supersededBefore: Date): Promise<void> {\n await this.refreshTokenService.prune(uid, sessionId, supersededBefore);\n }\n\n async getTokensValidAfter(uid: string): Promise<Date | null> {\n return this.refreshTokenService.getTokensValidAfter(uid);\n }\n\n async setTokensValidAfter(uid: string, at: Date): Promise<void> {\n await this.refreshTokenService.setTokensValidAfter(uid, at);\n }\n\n async findRefreshTokenByHash(tokenHash: string): Promise<RefreshTokenInfo | null> {\n return this.refreshTokenService.findByHash(tokenHash);\n }\n\n async deleteRefreshToken(tokenHash: string): Promise<void> {\n await this.refreshTokenService.deleteByHash(tokenHash);\n }\n\n async deleteAllRefreshTokensForUser(uid: string): Promise<void> {\n await this.refreshTokenService.deleteAllForUser(uid);\n }\n\n async listRefreshTokensForUser(uid: string): Promise<RefreshTokenInfo[]> {\n return this.refreshTokenService.listForUser(uid);\n }\n\n async deleteRefreshTokenById(id: string, uid: string): Promise<void> {\n await this.refreshTokenService.deleteById(id, uid);\n }\n\n async createPasswordResetToken(uid: string, tokenHash: string, expiresAt: Date): Promise<void> {\n await this.passwordResetTokenService.createToken(uid, tokenHash, expiresAt);\n }\n\n async findValidPasswordResetToken(tokenHash: string): Promise<PasswordResetTokenInfo | null> {\n return this.passwordResetTokenService.findValidByHash(tokenHash);\n }\n\n async markPasswordResetTokenUsed(tokenHash: string): Promise<void> {\n await this.passwordResetTokenService.markAsUsed(tokenHash);\n }\n\n async deleteAllPasswordResetTokensForUser(uid: string): Promise<void> {\n await this.passwordResetTokenService.deleteAllForUser(uid);\n }\n\n async deleteExpiredTokens(): Promise<void> {\n await this.passwordResetTokenService.deleteExpired();\n }\n\n // Magic link token operations\n\n async createMagicLinkToken(uid: string, tokenHash: string, expiresAt: Date): Promise<void> {\n const col = this.db.collection(\"magic_link_tokens\");\n await col.deleteMany({ uid, usedAt: null });\n await col.insertOne({ uid, tokenHash, expiresAt, usedAt: null, createdAt: new Date() });\n }\n\n async findValidMagicLinkToken(tokenHash: string): Promise<MagicLinkTokenInfo | null> {\n const col = this.db.collection(\"magic_link_tokens\");\n const doc = await col.findOne({ tokenHash, usedAt: null, expiresAt: { $gt: new Date() } });\n if (!doc) return null;\n return { uid: doc.uid as string, expiresAt: doc.expiresAt as Date };\n }\n\n async markMagicLinkTokenUsed(tokenHash: string): Promise<void> {\n const col = this.db.collection(\"magic_link_tokens\");\n await col.updateOne({ tokenHash }, { $set: { usedAt: new Date() } });\n }\n}\n\nexport class MongoAuthRepository implements AuthRepository {\n private userService: MongoUserService;\n private roleService: MongoRoleService;\n private tokenRepository: MongoTokenRepository;\n\n constructor(private db: Db) {\n this.userService = new MongoUserService(db);\n this.roleService = new MongoRoleService(db);\n this.tokenRepository = new MongoTokenRepository(db);\n }\n\n async createUser(data: CreateUserData): Promise<UserData> {\n return this.userService.createUser(data);\n }\n\n async getUserById(id: string): Promise<UserData | null> {\n return this.userService.getUserById(id);\n }\n\n async getUserByEmail(email: string): Promise<UserData | null> {\n return this.userService.getUserByEmail(email);\n }\n\n async getUserByIdentity(provider: string, providerId: string): Promise<UserData | null> {\n return this.userService.getUserByIdentity(provider, providerId);\n }\n\n async getUserIdentities(uid: string): Promise<UserIdentityData[]> {\n return this.userService.getUserIdentities(uid);\n }\n\n async linkUserIdentity(uid: string, provider: string, providerId: string, profileData?: Record<string, unknown>): Promise<void> {\n return this.userService.linkUserIdentity(uid, provider, providerId, profileData);\n }\n\n async updateUser(id: string, data: Partial<Omit<CreateUserData, \"id\">>): Promise<UserData | null> {\n return this.userService.updateUser(id, data);\n }\n\n async deleteUser(id: string): Promise<void> {\n await this.userService.deleteUser(id);\n }\n\n async listUsers(): Promise<UserData[]> {\n return this.userService.listUsers();\n }\n\n async listUsersPaginated(options?: ListUsersOptions): Promise<PaginatedUsersResult> {\n return this.userService.listUsersPaginated(options);\n }\n\n async updatePassword(id: string, passwordHash: string): Promise<void> {\n await this.userService.updatePassword(id, passwordHash);\n }\n\n async setEmailVerified(id: string, verified: boolean): Promise<void> {\n await this.userService.setEmailVerified(id, verified);\n }\n\n async setVerificationToken(id: string, token: string | null): Promise<void> {\n await this.userService.setVerificationToken(id, token);\n }\n\n async getUserByVerificationToken(token: string): Promise<UserData | null> {\n return this.userService.getUserByVerificationToken(token);\n }\n\n async getUserRoles(uid: string): Promise<RoleData[]> {\n return this.userService.getUserRoles(uid);\n }\n\n async getUserRoleIds(uid: string): Promise<string[]> {\n return this.userService.getUserRoleIds(uid);\n }\n\n async setUserRoles(uid: string, roleIds: string[]): Promise<void> {\n await this.userService.setUserRoles(uid, roleIds);\n }\n\n async assignDefaultRole(uid: string, roleId: string): Promise<void> {\n await this.userService.assignDefaultRole(uid, roleId);\n }\n\n async getUserWithRoles(uid: string): Promise<{ user: UserData; roles: RoleData[] } | null> {\n return this.userService.getUserWithRoles(uid);\n }\n\n async getRoleById(id: string): Promise<RoleData | null> {\n return this.roleService.getRoleById(id);\n }\n\n async listRoles(): Promise<RoleData[]> {\n return this.roleService.listRoles();\n }\n\n async createRole(data: CreateRoleData): Promise<RoleData> {\n return this.roleService.createRole(data);\n }\n\n async updateRole(id: string, data: Partial<Omit<RoleData, \"id\">>): Promise<RoleData | null> {\n return this.roleService.updateRole(id, data);\n }\n\n async deleteRole(id: string): Promise<void> {\n await this.roleService.deleteRole(id);\n }\n\n async createRefreshToken(uid: string, tokenHash: string, expiresAt: Date, userAgent?: string, ipAddress?: string, session?: RefreshTokenSession): Promise<void> {\n await this.tokenRepository.createRefreshToken(uid, tokenHash, expiresAt, userAgent, ipAddress, session);\n }\n\n async markRefreshTokenRotated(tokenHash: string): Promise<void> {\n await this.tokenRepository.markRefreshTokenRotated(tokenHash);\n }\n\n async revokeRefreshTokenSession(sessionId: string): Promise<void> {\n await this.tokenRepository.revokeRefreshTokenSession(sessionId);\n }\n\n async pruneRefreshTokens(uid: string, sessionId: string, supersededBefore: Date): Promise<void> {\n await this.tokenRepository.pruneRefreshTokens(uid, sessionId, supersededBefore);\n }\n\n async getTokensValidAfter(uid: string): Promise<Date | null> {\n return this.tokenRepository.getTokensValidAfter(uid);\n }\n\n async setTokensValidAfter(uid: string, at: Date): Promise<void> {\n await this.tokenRepository.setTokensValidAfter(uid, at);\n }\n\n async findRefreshTokenByHash(tokenHash: string): Promise<RefreshTokenInfo | null> {\n return this.tokenRepository.findRefreshTokenByHash(tokenHash);\n }\n\n async deleteRefreshToken(tokenHash: string): Promise<void> {\n await this.tokenRepository.deleteRefreshToken(tokenHash);\n }\n\n async deleteAllRefreshTokensForUser(uid: string): Promise<void> {\n await this.tokenRepository.deleteAllRefreshTokensForUser(uid);\n }\n\n async listRefreshTokensForUser(uid: string): Promise<RefreshTokenInfo[]> {\n return this.tokenRepository.listRefreshTokensForUser(uid);\n }\n\n async deleteRefreshTokenById(id: string, uid: string): Promise<void> {\n await this.tokenRepository.deleteRefreshTokenById(id, uid);\n }\n\n async createPasswordResetToken(uid: string, tokenHash: string, expiresAt: Date): Promise<void> {\n await this.tokenRepository.createPasswordResetToken(uid, tokenHash, expiresAt);\n }\n\n async findValidPasswordResetToken(tokenHash: string): Promise<PasswordResetTokenInfo | null> {\n return this.tokenRepository.findValidPasswordResetToken(tokenHash);\n }\n\n async markPasswordResetTokenUsed(tokenHash: string): Promise<void> {\n await this.tokenRepository.markPasswordResetTokenUsed(tokenHash);\n }\n\n async deleteAllPasswordResetTokensForUser(uid: string): Promise<void> {\n await this.tokenRepository.deleteAllPasswordResetTokensForUser(uid);\n }\n\n async deleteExpiredTokens(): Promise<void> {\n await this.tokenRepository.deleteExpiredTokens();\n }\n\n // Magic link token operations\n\n async createMagicLinkToken(uid: string, tokenHash: string, expiresAt: Date): Promise<void> {\n await this.tokenRepository.createMagicLinkToken(uid, tokenHash, expiresAt);\n }\n\n async findValidMagicLinkToken(tokenHash: string): Promise<MagicLinkTokenInfo | null> {\n return this.tokenRepository.findValidMagicLinkToken(tokenHash);\n }\n\n async markMagicLinkTokenUsed(tokenHash: string): Promise<void> {\n await this.tokenRepository.markMagicLinkTokenUsed(tokenHash);\n }\n\n // MFA Repository Stub\n async createMfaFactor(uid: string, factorType: \"totp\", secretEncrypted: string, friendlyName?: string): Promise<MfaFactor> {\n throw new Error(\"MFA is not implemented for MongoDB\");\n }\n async getMfaFactors(uid: string): Promise<MfaFactor[]> {\n return [];\n }\n async getMfaFactorById(factorId: string): Promise<(MfaFactor & { secretEncrypted: string }) | null> {\n return null;\n }\n async verifyMfaFactor(factorId: string): Promise<void> {\n throw new Error(\"MFA is not implemented for MongoDB\");\n }\n async deleteMfaFactor(factorId: string, uid: string): Promise<void> {\n throw new Error(\"MFA is not implemented for MongoDB\");\n }\n async createMfaChallenge(factorId: string, ipAddress?: string): Promise<MfaChallengeInfo> {\n throw new Error(\"MFA is not implemented for MongoDB\");\n }\n async getMfaChallengeById(challengeId: string): Promise<MfaChallengeInfo | null> {\n return null;\n }\n async verifyMfaChallenge(challengeId: string): Promise<void> {\n throw new Error(\"MFA is not implemented for MongoDB\");\n }\n async createRecoveryCodes(uid: string, codeHashes: string[]): Promise<void> {\n throw new Error(\"MFA is not implemented for MongoDB\");\n }\n async useRecoveryCode(uid: string, codeHash: string): Promise<boolean> {\n return false;\n }\n async getUnusedRecoveryCodeCount(uid: string): Promise<number> {\n return 0;\n }\n async deleteAllRecoveryCodes(uid: string): Promise<void> {\n // No-op\n }\n async hasVerifiedMfaFactors(uid: string): Promise<boolean> {\n return false;\n }\n}\n","import { Db, MongoClient } from \"mongodb\";\nimport type {\n AuthAdapter,\n BackendBootstrapper,\n InitializedDriver,\n BootstrappedAuth,\n DatabaseAdmin,\n HistoryConfig,\n RealtimeProvider,\n DataDriver,\n CollectionConfig\n} from \"@rebasepro/types\";\nimport { MongoDriver } from \"./services/MongoDriver\";\nimport { MongoRealtimeService } from \"./services/MongoRealtimeService\";\nimport { MongoCollectionRegistry } from \"./factory\";\nimport { MongoAuthRepository, MongoUserService, MongoRoleService } from \"./auth/services\";\nimport { logger } from \"@rebasepro/server\";\n\nexport interface MongoDriverConfig {\n connection: Db;\n client: MongoClient;\n}\n\nexport interface MongoDriverInternals {\n db: Db;\n client: MongoClient;\n registry: MongoCollectionRegistry;\n realtimeService: MongoRealtimeService;\n driver: MongoDriver;\n}\n\n/** Shape of the config object passed to `initializeDriver` by the coordinator. */\ninterface DriverInitConfig {\n collections?: CollectionConfig[];\n}\n\n/** Shape of the auth config passed to `initializeAuth`. */\ninterface AuthInitConfig {\n email?: import(\"@rebasepro/server\").EmailConfig;\n}\n\n\n\nexport function createMongoBootstrapper(mongoConfig: MongoDriverConfig): BackendBootstrapper {\n // Cached admin object, set during getAdmin() and used by initializeWebsockets\n let cachedAdmin: DatabaseAdmin | undefined;\n\n return {\n type: \"mongodb\",\n\n async initializeDriver(config: unknown): Promise<InitializedDriver> {\n const { collections } = config as DriverInitConfig;\n\n const registry = new MongoCollectionRegistry();\n if (collections) {\n collections.forEach(collection => registry.register(collection));\n }\n\n const db = mongoConfig.connection;\n const client = mongoConfig.client;\n\n // Verify connection\n try {\n await db.command({ ping: 1 });\n } catch (err) {\n logger.error(\"❌ Failed to connect to MongoDB\", { error: err });\n }\n\n const realtimeService = new MongoRealtimeService(db);\n const driver = new MongoDriver(db, realtimeService, undefined, registry);\n\n const internals: MongoDriverInternals = {\n db,\n client,\n registry,\n realtimeService,\n driver\n };\n\n return {\n driver,\n realtimeProvider: realtimeService,\n collectionRegistry: registry,\n internals\n };\n },\n\n async initializeAuth(config: unknown, driverResult: InitializedDriver): Promise<BootstrappedAuth | undefined> {\n const internals = driverResult.internals as MongoDriverInternals;\n const db = internals.db;\n\n const { ensureAuthCollectionsExist } = await import(\"./auth/ensure-collections\");\n await ensureAuthCollectionsExist(db);\n\n const { createEmailService } = await import(\"@rebasepro/server\");\n const authConfig = config as AuthInitConfig | undefined;\n let emailService: unknown;\n if (authConfig?.email) {\n emailService = createEmailService(authConfig.email);\n }\n\n const userService = new MongoUserService(db);\n const roleService = new MongoRoleService(db);\n const authRepository = new MongoAuthRepository(db);\n\n return {\n userService,\n roleService,\n authRepository,\n emailService\n };\n },\n\n async initializeHistory(config: HistoryConfig, driverResult: InitializedDriver): Promise<{ historyService: unknown } | undefined> {\n if (!config) return undefined;\n\n const internals = driverResult.internals as MongoDriverInternals;\n const db = internals.db;\n\n const { ensureHistoryCollectionExists } = await import(\"./history/ensure-history-collection\");\n await ensureHistoryCollectionExists(db);\n\n const { MongoHistoryService } = await import(\"./services/MongoHistoryService\");\n\n const retention = typeof config === \"object\" ? config.retention : undefined;\n const historyService = new MongoHistoryService(db, retention ? { ttlDays: retention } : undefined);\n\n return { historyService };\n },\n\n async initializeRealtime(_config: unknown, driverResult: InitializedDriver): Promise<RealtimeProvider | undefined> {\n const internals = driverResult.internals as MongoDriverInternals;\n return internals.realtimeService;\n },\n\n getAdmin(driverResult: InitializedDriver): DatabaseAdmin | undefined {\n const internals = driverResult.internals as MongoDriverInternals;\n const db = internals.db;\n\n const admin: DatabaseAdmin = {\n async executeAggregate(pipeline: Record<string, unknown>[]) {\n const firstStage = pipeline[0];\n const collName = (firstStage as { $from?: string })?.$from ?? \"__admin__\";\n const cursor = db.collection(collName).aggregate(pipeline);\n return await cursor.toArray() as Record<string, unknown>[];\n },\n async fetchCollectionStats(collectionName: string) {\n const stats = await db.command({ collStats: collectionName }) as { count: number; size: number };\n return { count: stats.count,\nsizeBytes: stats.size };\n },\n async fetchUnmappedTables(mappedPaths?: string[]) {\n const allCollections = await db.listCollections().toArray();\n const names = allCollections.map(c => c.name).filter(n => !n.startsWith(\"system.\"));\n if (!mappedPaths || mappedPaths.length === 0) return names;\n const mappedSet = new Set(mappedPaths.map(p => p.toLowerCase()));\n return names.filter(n => !mappedSet.has(n.toLowerCase()));\n },\n async fetchTableMetadata(collectionName: string) {\n const sample = await db.collection(collectionName).findOne();\n if (!sample) return { columns: [],\nforeignKeys: [],\njunctions: [],\npolicies: [] };\n const columns = Object.entries(sample).map(([key, value]) => ({\n column_name: key,\n data_type: typeof value,\n udt_name: typeof value,\n is_nullable: \"YES\",\n column_default: null,\n character_maximum_length: null\n }));\n return { columns,\nforeignKeys: [],\njunctions: [],\npolicies: [] };\n }\n };\n\n cachedAdmin = admin;\n return admin;\n },\n\n mountRoutes() {},\n\n // Five parameters, not four. `BackendBootstrapper` declares an\n // `authAdapter` here and `init.ts` passes one; dropping it left the\n // socket with only its built-in JWT verifier, so on a backend using an\n // AuthAdapter every realtime AUTHENTICATE failed with \"Invalid or\n // expired token\" — and nothing on either side could have type-checked\n // the mismatch.\n async initializeWebsockets(server: unknown, realtimeService: RealtimeProvider, driver: DataDriver, config?: unknown, authAdapter?: AuthAdapter): Promise<void> {\n const { createMongoWebSocket } = await import(\"./websocket\");\n createMongoWebSocket(\n server as import(\"http\").Server,\n realtimeService as MongoRealtimeService,\n driver as MongoDriver,\n config as Record<string, unknown> | undefined,\n cachedAdmin,\n authAdapter\n );\n }\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.es.js","names":[],"sources":["../src/connection.ts","../src/db/MongoConditionBuilder.ts","../src/db/MongoDataService.ts","../src/services/MongoRealtimeService.ts","../src/services/MongoHistoryService.ts","../src/db/securityRuleFilter.ts","../src/services/MongoDriver.ts","../src/factory.ts","../src/auth/services.ts","../src/MongoBootstrapper.ts"],"sourcesContent":["/**\n * MongoDB Connection\n *\n * Wraps MongoDB connection to implement the DatabaseConnection interface.\n */\n\nimport { Db, MongoClient } from \"mongodb\";\nimport { DatabaseConnection } from \"@rebasepro/types\";\n\n/**\n * MongoDB database connection wrapper that implements DatabaseConnection interface.\n */\nexport class MongoDBConnection implements DatabaseConnection {\n readonly type = \"mongodb\";\n\n constructor(\n public readonly db: Db,\n public readonly client: MongoClient\n ) { }\n\n get isConnected(): boolean {\n // MongoClient doesn't have a direct isConnected property in v6+\n // We check if the client topology is connected\n try {\n const clientInternal = this.client as unknown as Record<string, { isConnected?: () => boolean } | undefined>;\n return clientInternal.topology?.isConnected?.() ?? false;\n } catch {\n return false;\n }\n }\n\n async close(): Promise<void> {\n await this.client.close();\n }\n}\n\n/**\n * Create a MongoDB database connection from a connection string.\n *\n * @param connectionString - MongoDB connection string (e.g., mongodb://localhost:27017)\n * @param databaseName - Name of the database to use\n * @returns Promise resolving to MongoDBConnection\n *\n * @example\n * ```typescript\n * const connection = await createMongoDBConnection(\n * \"mongodb://localhost:27017\",\n * \"my_database\"\n * );\n * ```\n */\nexport async function createMongoDBConnection(\n connectionString: string,\n databaseName: string\n): Promise<MongoDBConnection> {\n const client = new MongoClient(connectionString);\n await client.connect();\n const db = client.db(databaseName);\n return new MongoDBConnection(db, client);\n}\n","/**\n * MongoDB Condition Builder\n *\n * Translates Rebase filter conditions to MongoDB query operators.\n */\n\nimport { CollectionConfig, FilterCondition, FilterValues, LogicalCondition, OrderByTuple, WhereFilterOp } from \"@rebasepro/types\";\nimport { normalizeDriverOrderBy, toFilterTuples } from \"@rebasepro/common\";\nimport { Filter, Document } from \"mongodb\";\nimport { ApiError, logger } from \"@rebasepro/server\";\n\n/**\n * Mapping from Rebase filter operators to MongoDB query operators\n */\nconst REBASE_TO_MONGO_OP: Partial<Record<WhereFilterOp, string>> = {\n \"<\": \"$lt\",\n \"<=\": \"$lte\",\n \"==\": \"$eq\",\n \"!=\": \"$ne\",\n \">=\": \"$gte\",\n \">\": \"$gt\",\n \"array-contains\": \"$elemMatch\",\n \"array-contains-any\": \"$in\",\n \"in\": \"$in\",\n \"not-in\": \"$nin\"\n};\n\nfunction escapeRegExp(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Translate a SQL LIKE/ILIKE pattern into an anchored regular expression.\n * `%` matches any sequence of characters, `_` matches a single character;\n * every other character is matched literally.\n */\nfunction likePatternToRegExp(pattern: string, caseInsensitive: boolean): RegExp {\n let body = \"\";\n // Runs of `%` collapse to one. `%%%%X` means exactly what `%X` means, but as\n // a regular expression it is four adjacent unbounded quantifiers, and on a\n // subject that does not match, the engine tries every way of splitting the\n // subject between them — exponential time. The pattern comes from a public\n // filter operator over HTTP (`?title=like.%25%25%25…`), and this expression\n // is handed to MongoDB as `$regex`, so the time is spent on a database\n // thread rather than the caller's.\n let lastWasWildcard = false;\n for (const ch of String(pattern)) {\n if (ch === \"%\") {\n if (!lastWasWildcard) body += \".*\";\n lastWasWildcard = true;\n continue;\n }\n body += ch === \"_\" ? \".\" : escapeRegExp(ch);\n lastWasWildcard = false;\n }\n return new RegExp(`^${body}$`, caseInsensitive ? \"i\" : \"\");\n}\n\n/**\n * MongoDB Condition Builder\n *\n * Provides static methods to translate Rebase filter conditions\n * to MongoDB query filters.\n */\nexport class MongoConditionBuilder {\n /**\n * Build MongoDB filter conditions from Rebase FilterValues\n *\n * @param filter - Rebase filter values\n * @returns Array of MongoDB filter objects\n */\n static buildFilterConditions<M extends Record<string, any>>(\n filter: FilterValues<Extract<keyof M, string>>\n ): Filter<Document>[] {\n if (!filter) return [];\n\n const conditions: Filter<Document>[] = [];\n\n for (const [field, filterParam] of Object.entries(filter)) {\n if (!filterParam) continue;\n\n // One tuple or an array of them. This destructured the param\n // directly, so `{ age: [[\">=\", 18], [\"<\", 65]] }` bound `op` to the\n // tuple `[\">=\", 18]`, matched no operator, and dropped **both**\n // conditions — a filtered read answering 200 with the whole\n // collection. The grammar is shared with the Postgres compiler now.\n for (const [op, value] of toFilterTuples(filterParam)) {\n conditions.push(this.buildCondition(field, op, value));\n }\n }\n\n return conditions;\n }\n\n /**\n * One field, one operator, one value.\n *\n * Extracted so that `logical` groups translate through exactly this code.\n * A group written as a second dialect is a group where `array-contains` or\n * `ilike` quietly means something else than it does in `filter`, which is\n * the kind of difference nobody finds until a query returns the wrong rows.\n *\n * Always returns a condition or throws: there is no operator this can be\n * given that legitimately means \"no condition\".\n */\n private static buildCondition(\n field: string,\n op: WhereFilterOp,\n value: any\n ): Filter<Document> {\n // Null-testing operators ignore their value.\n if (op === \"is-null\") return { [field]: { $eq: null } };\n if (op === \"is-not-null\") return { [field]: { $ne: null } };\n\n // Pattern matching → regular expressions.\n if (op === \"like\" || op === \"ilike\" || op === \"not-like\" || op === \"not-ilike\") {\n const caseInsensitive = op === \"ilike\" || op === \"not-ilike\";\n const regex = likePatternToRegExp(value, caseInsensitive);\n const negated = op === \"not-like\" || op === \"not-ilike\";\n return { [field]: negated ? { $not: regex } : { $regex: regex } };\n }\n\n const mongoOp = REBASE_TO_MONGO_OP[op];\n\n if (!mongoOp) {\n // A filter that cannot be compiled must not compile to \"no filter\".\n // Returning `undefined` here dropped the condition and widened the\n // read — inside an `or(...)` group it drops a branch, which widens\n // it further — and the only trace was a line in the server log\n // behind a 200.\n logger.warn(`Unsupported filter operator '${op}' on field '${field}'`);\n throw ApiError.badRequest(\n `Operator '${op}' is not supported on field '${field}' by the MongoDB driver.`,\n \"UNSUPPORTED_FILTER_OPERATOR\",\n { field, operator: op }\n );\n }\n\n // Handle array-contains specially\n if (op === \"array-contains\") {\n return { [field]: { $elemMatch: { $eq: value } } };\n }\n return { [field]: { [mongoOp]: value } };\n }\n\n /**\n * Translate an `or(...)` / `and(...)` group, nesting included.\n *\n * Returns `undefined` for a group with nothing in it. `$or: []` is an error\n * in Mongo and `$and: []` matches every document, so neither is a\n * defensible reading of \"no conditions\".\n */\n static buildLogicalConditions(logical: LogicalCondition | undefined): Filter<Document> | undefined {\n if (!logical || !Array.isArray(logical.conditions)) return undefined;\n\n const parts: Filter<Document>[] = [];\n for (const entry of logical.conditions) {\n if (!entry) continue;\n if (\"type\" in entry && \"conditions\" in entry) {\n const nested = this.buildLogicalConditions(entry as LogicalCondition);\n if (nested) parts.push(nested);\n continue;\n }\n const { column, operator, value } = entry as FilterCondition;\n parts.push(this.buildCondition(column, operator, value));\n }\n\n // Through the same combiners the rest of this class uses, so a\n // one-condition group reads as the bare condition — identical to how\n // `filter` would have expressed it — rather than as `{ $and: [x] }`.\n return logical.type === \"or\"\n ? this.combineConditionsWithOr(parts)\n : this.combineConditionsWithAnd(parts);\n }\n\n /**\n * Build search conditions for text search\n *\n * @param searchString - Text to search for\n * @param properties - The collection's properties, searched for string fields\n * @returns Array of MongoDB filter objects for text search\n */\n static buildSearchConditions(\n searchString: string,\n // Typed as the real property map, not `Record<string, any>`. The loose\n // type is what let the `dataType` bug below survive: a caller — and,\n // more to the point, a test fixture — could invent any key it liked and\n // nothing checked it against a property a user can actually declare.\n properties: CollectionConfig[\"properties\"]\n ): Filter<Document>[] {\n if (!searchString) return [];\n\n // Build regex conditions for each searchable string property\n const orConditions: Filter<Document>[] = [];\n const escapedSearch = escapeRegExp(searchString);\n const searchRegex = new RegExp(escapedSearch, \"i\");\n\n for (const [key, prop] of Object.entries(properties)) {\n // `type`, not `dataType`. No property in `@rebasepro/types` has ever\n // had a `dataType` field — a real collection carries `type:\n // \"string\"` — so this matched nothing for every collection a user\n // could actually declare. With no field matching, the fallback\n // below took over and every search became a `$text` query, which\n // needs a text index and throws `IndexNotFound` without one.\n //\n // The suite passed because its fixtures were written with the same\n // wrong key, so the test data agreed with the bug and the two\n // never met a real collection between them.\n if (prop?.type === \"string\" || typeof prop === \"string\") {\n orConditions.push({\n [key]: { $regex: searchRegex }\n });\n }\n }\n\n // If no properties to search, use MongoDB text search\n if (orConditions.length === 0) {\n return [{ $text: { $search: searchString } }];\n }\n\n return orConditions;\n }\n\n /**\n * Combine multiple conditions with AND operator\n *\n * @param conditions - Array of filter conditions\n * @returns Combined filter or undefined if empty\n */\n static combineConditionsWithAnd(conditions: Filter<Document>[]): Filter<Document> | undefined {\n if (conditions.length === 0) return undefined;\n if (conditions.length === 1) return conditions[0];\n return { $and: conditions };\n }\n\n /**\n * Combine multiple conditions with OR operator\n *\n * @param conditions - Array of filter conditions\n * @returns Combined filter or undefined if empty\n */\n static combineConditionsWithOr(conditions: Filter<Document>[]): Filter<Document> | undefined {\n if (conditions.length === 0) return undefined;\n if (conditions.length === 1) return conditions[0];\n return { $or: conditions };\n }\n\n /**\n * Build a complete MongoDB query from Rebase options\n *\n * @param options - Rebase fetch options\n * @returns MongoDB filter object\n */\n static buildQuery<M extends Record<string, any>>(options: {\n filter?: FilterValues<Extract<keyof M, string>>;\n /**\n * An `or(...)`/`and(...)` group, AND-ed with `filter` and\n * `searchString` — the three are independent, as `FindParams`\n * documents. Absent here until now, so a group that reached this\n * driver was dropped and the read ran unfiltered.\n */\n logical?: LogicalCondition;\n searchString?: string;\n properties?: CollectionConfig[\"properties\"];\n }): Filter<Document> {\n const conditions: Filter<Document>[] = [];\n\n // Add filter conditions\n if (options.filter) {\n const filterConditions = this.buildFilterConditions<M>(options.filter);\n conditions.push(...filterConditions);\n }\n\n const logicalCondition = this.buildLogicalConditions(options.logical);\n if (logicalCondition) conditions.push(logicalCondition);\n\n // Add search conditions\n if (options.searchString && options.properties) {\n const searchConditions = this.buildSearchConditions(\n options.searchString,\n options.properties\n );\n if (searchConditions.length > 0) {\n // Search conditions are OR'd together\n const searchFilter = this.combineConditionsWithOr(searchConditions);\n if (searchFilter) {\n conditions.push(searchFilter);\n }\n }\n }\n\n return this.combineConditionsWithAnd(conditions) ?? {};\n }\n\n /**\n * The primary key's name in a stored document.\n *\n * Rows leave this driver with `_id` renamed to `id` — see\n * `MongoDataService.documentToRow` — so `id` is the only name a caller ever\n * sees, and the one the SDK's own examples use for a tie-breaker. A sort\n * document naming `id` names a field no document has: Mongo does not\n * complain, it just returns them in natural order, so `.orderBy(\"id\")` read\n * as \"no sort at all\" with a 200 to go with it.\n */\n private static readonly ID_FIELD = \"_id\";\n\n /**\n * Build MongoDB sort options from Rebase options\n *\n * A Mongo sort document is already an ordered map of field to direction, so\n * a multi-key sort is expressed directly: the keys are applied in insertion\n * order, each breaking ties on the one before it.\n *\n * The id closes every sort, descending, for the same reason the Postgres\n * driver appends `id DESC`: it is what makes the ordering *total*. Without\n * it, two rows sharing a sort value are returned in whatever order the\n * engine pleases, and that order is free to differ between two executions of\n * the same query — so paging by `offset` over a non-unique sort column\n * repeats some rows and skips others. A single-column sort has always had\n * this exposure here; a multi-key sort merely made it easier to reach, since\n * the whole point of the later keys is that the earlier ones tie.\n *\n * @param orderBy - Field to order by, or the `[field, direction]` list\n * @param order - Sort direction, for the single-field spelling\n * @returns MongoDB sort object\n */\n static buildSort(\n orderBy?: string | OrderByTuple[],\n order?: \"asc\" | \"desc\"\n ): Record<string, 1 | -1> | undefined {\n const keys = normalizeDriverOrderBy(orderBy, order);\n if (!keys) return undefined;\n const sort: Record<string, 1 | -1> = {};\n // A repeated field is the first occurrence's: that is the key Mongo\n // would sort by, and letting a later duplicate overwrite it would order\n // by a direction the caller listed as less significant.\n for (const [field, direction] of keys) {\n const key = field === \"id\" ? MongoConditionBuilder.ID_FIELD : field;\n if (!(key in sort)) sort[key] = direction === \"desc\" ? -1 : 1;\n }\n // Already named by the caller — at whatever direction and rank they\n // chose — the sort is total and there is nothing left to break.\n if (!(MongoConditionBuilder.ID_FIELD in sort)) {\n sort[MongoConditionBuilder.ID_FIELD] = -1;\n }\n return sort;\n }\n}\n","/**\n * MongoDB Row Service\n *\n * Implements DataRepository interface for MongoDB.\n * Provides all CRUD operations for rows.\n */\n\nimport { Db, ObjectId, Collection, Document, FindOptions, Filter } from \"mongodb\";\nimport { FilterValues, DataRepository, CollectionConfig, EntityReference, LogicalCondition, OrderByTuple } from \"@rebasepro/types\";\nimport { MongoConditionBuilder } from \"./MongoConditionBuilder\";\nimport { ApiError } from \"@rebasepro/server\";\n\n/**\n * MongoDB Row Service\n *\n * Implements the DataRepository interface for MongoDB.\n * Provides all CRUD operations for rows stored in MongoDB collections.\n */\nexport class MongoDataService implements DataRepository {\n constructor(private db: Db) { }\n\n /**\n * Get a MongoDB collection by its path\n */\n private getCollection(collectionPath: string): Collection<Document> {\n // Handle nested paths (e.g., \"posts/123/comments\" -> \"posts_123_comments\")\n const collectionName = collectionPath.replace(/\\//g, \"_\");\n return this.db.collection(collectionName);\n }\n\n /**\n * Convert a string ID to ObjectId if it's a valid ObjectId string\n */\n private toObjectId(id: string | number): ObjectId | string | number {\n if (typeof id === \"string\" && ObjectId.isValid(id) && id.length === 24) {\n return new ObjectId(id);\n }\n return id;\n }\n\n /**\n * Convert a MongoDB document to a flat row (`{ id, ...fields }`)\n */\n private documentToRow(doc: Document): Record<string, unknown> {\n const { _id, ...values } = doc;\n return {\n ...this.convertFromMongoValues(values),\n // Spread the canonical id last so it wins over a literal `id` field\n id: _id.toString()\n };\n }\n\n /**\n * Convert values from MongoDB format to Rebase format\n */\n private convertFromMongoValues(values: Record<string, any>): Record<string, any> {\n const result: Record<string, any> = {};\n\n for (const [key, value] of Object.entries(values)) {\n result[key] = this.convertFromMongoValue(value);\n }\n\n return result;\n }\n\n /**\n * Convert a single value from MongoDB format\n */\n private convertFromMongoValue(value: any): any {\n if (value === null || value === undefined) return value;\n\n // Handle ObjectId\n if (value instanceof ObjectId) {\n return value.toString();\n }\n\n // Handle Date\n if (value instanceof Date) {\n return value;\n }\n\n // Handle arrays\n if (Array.isArray(value)) {\n return value.map(v => this.convertFromMongoValue(v));\n }\n\n // Handle stored EntityReference. New writes are tagged with the\n // `__type: \"reference\"` sentinel (see convertToMongoValue), which is\n // unambiguous. We also accept the legacy shape — an object whose ONLY\n // keys are `id` and `path` — so references written before the sentinel\n // existed still decode. We deliberately do NOT treat any object that\n // merely *contains* `id` and `path` as a reference, because that\n // silently rewrites ordinary embedded sub-documents.\n if (typeof value === \"object\") {\n const keys = Object.keys(value);\n const isTagged = value.__type === \"reference\" && \"id\" in value && \"path\" in value;\n const isLegacy = keys.length === 2 && keys.includes(\"id\") && keys.includes(\"path\");\n if (isTagged || isLegacy) {\n return new EntityReference({\n id: value.id instanceof ObjectId ? value.id.toString() : String(value.id),\n path: value.path,\n driver: value.driver,\n databaseId: value.databaseId\n });\n }\n }\n\n // Handle nested objects\n if (typeof value === \"object\") {\n return this.convertFromMongoValues(value);\n }\n\n return value;\n }\n\n /**\n * Convert values to MongoDB format for storage\n */\n private convertToMongoValues(values: Record<string, any>): Record<string, any> {\n const result: Record<string, any> = {};\n\n for (const [key, value] of Object.entries(values)) {\n result[key] = this.convertToMongoValue(value);\n }\n\n return result;\n }\n\n /**\n * Convert a single value to MongoDB format\n */\n private convertToMongoValue(value: any): any {\n if (value === null || value === undefined) return value;\n\n // Handle EntityReference. Persist with a `__type: \"reference\"` sentinel\n // so it round-trips unambiguously, and preserve `driver`/`databaseId`\n // so cross-datasource pointers don't lose their target on write.\n if (typeof value === \"object\" && value.isEntityReference?.()) {\n const ref: Record<string, unknown> = {\n __type: \"reference\",\n id: ObjectId.isValid(value.id) ? new ObjectId(value.id) : value.id,\n path: value.path\n };\n if (value.driver !== undefined) ref.driver = value.driver;\n if (value.databaseId !== undefined) ref.databaseId = value.databaseId;\n return ref;\n }\n\n // Handle Date\n if (value instanceof Date) {\n return value;\n }\n\n // Handle arrays\n if (Array.isArray(value)) {\n return value.map(v => this.convertToMongoValue(v));\n }\n\n // Handle nested objects\n if (typeof value === \"object\") {\n return this.convertToMongoValues(value);\n }\n\n return value;\n }\n\n // =============================================================\n // DataRepository Implementation\n // =============================================================\n\n /**\n * Fetch a single row by ID\n */\n async fetchOne<M extends Record<string, any>>(\n collectionPath: string,\n id: string | number,\n _databaseId?: string\n ): Promise<Record<string, unknown> | undefined> {\n const collection = this.getCollection(collectionPath);\n const objectId = this.toObjectId(id);\n\n const doc = await collection.findOne({ _id: objectId } as Filter<Document>);\n\n if (!doc) return undefined;\n\n return this.documentToRow(doc);\n }\n\n /**\n * Fetch a collection of rows with optional filtering, ordering, and pagination\n */\n async fetchCollection<M extends Record<string, any>>(\n collectionPath: string,\n options: {\n filter?: FilterValues<Extract<keyof M, string>>;\n /** An `or(...)`/`and(...)` group, AND-ed with `filter`. */\n logical?: LogicalCondition;\n orderBy?: string | OrderByTuple[];\n order?: \"desc\" | \"asc\";\n limit?: number;\n offset?: number;\n startAfter?: any;\n searchString?: string;\n databaseId?: string;\n collection?: CollectionConfig;\n rawQuery?: Filter<Document>;\n } = {}\n ): Promise<Record<string, unknown>[]> {\n const collection = this.getCollection(collectionPath);\n\n // Build query\n const query = options.rawQuery ?? MongoConditionBuilder.buildQuery<M>({\n filter: options.filter,\n logical: options.logical,\n searchString: options.searchString,\n properties: options.collection?.properties ?? {}\n });\n\n // Build find options\n const findOptions: FindOptions = {};\n\n // Apply sorting\n const sort = MongoConditionBuilder.buildSort(options.orderBy, options.order);\n if (sort) {\n findOptions.sort = sort;\n }\n\n // Apply limit\n if (options.limit) {\n findOptions.limit = options.limit;\n }\n\n // Apply pagination. `offset` is the parameter the REST layer and the\n // SDK both speak; it was absent here, so every `?offset=` reaching this\n // driver was discarded and page three served page one. `startAfter`\n // stays as the older spelling and wins when both are given.\n if (options.startAfter !== undefined) {\n findOptions.skip = Number(options.startAfter);\n } else if (options.offset) {\n findOptions.skip = options.offset;\n }\n\n const docs = await collection.find(query, findOptions).toArray();\n\n return docs.map((doc: Document) => this.documentToRow(doc));\n }\n\n /**\n * Search rows by text\n */\n async searchRows<M extends Record<string, any>>(\n collectionPath: string,\n searchString: string,\n options: {\n filter?: FilterValues<Extract<keyof M, string>>;\n orderBy?: string | OrderByTuple[];\n order?: \"desc\" | \"asc\";\n limit?: number;\n databaseId?: string;\n collection?: CollectionConfig;\n rawQuery?: Filter<Document>;\n } = {}\n ): Promise<Record<string, unknown>[]> {\n return this.fetchCollection<M>(collectionPath, {\n ...options,\n searchString\n });\n }\n\n /**\n * Count rows in a collection\n */\n async count<M extends Record<string, any>>(\n collectionPath: string,\n options: {\n filter?: FilterValues<Extract<keyof M, string>>;\n /** An `or(...)`/`and(...)` group, AND-ed with `filter`. */\n logical?: LogicalCondition;\n searchString?: string;\n collection?: CollectionConfig;\n databaseId?: string;\n rawQuery?: Filter<Document>;\n } = {}\n ): Promise<number> {\n const collection = this.getCollection(collectionPath);\n\n // Every narrowing the listing applies has to apply here too, or the\n // count describes a different query than the one it is reported\n // against. `logical` and `searchString` were both missing.\n const query = options.rawQuery ?? MongoConditionBuilder.buildQuery<M>({\n filter: options.filter,\n logical: options.logical,\n searchString: options.searchString,\n properties: options.collection?.properties ?? {}\n });\n\n return collection.countDocuments(query);\n }\n\n /**\n * Save an row (create or update)\n *\n * Returns the **stored** document, not the values that were sent. A partial\n * update sends only the fields that changed, and returning those was a\n * partial row everywhere it went: the REST response, `afterSave`, the\n * history entry a revert restores from, and the row pushed to realtime\n * subscribers. Postgres returns the whole row here (`RETURNING *`).\n */\n async save<M extends Record<string, any>>(\n collectionPath: string,\n values: Partial<M>,\n id?: string | number,\n _databaseId?: string\n ): Promise<Record<string, unknown>> {\n const collection = this.getCollection(collectionPath);\n const mongoValues = this.convertToMongoValues(values as Record<string, any>);\n\n if (id) {\n // Still an upsert: this is also the call that creates a row with a\n // client-chosen id. Addressing an id that does not exist is caught\n // above — the REST `PUT` 404s and the authenticated driver refuses\n // — so the upsert only ever lands as the create it is meant to be.\n const objectId = this.toObjectId(id);\n await collection.updateOne(\n { _id: objectId } as Filter<Document>,\n { $set: mongoValues },\n { upsert: true }\n );\n\n return await this.readBack(collectionPath, objectId, { ...values,\nid: id.toString() });\n } else {\n // Create new row\n const newId = new ObjectId();\n await collection.insertOne({\n _id: newId,\n ...mongoValues\n });\n\n return await this.readBack(collectionPath, newId, { ...values,\nid: newId.toString() });\n }\n }\n\n /**\n * Read the document back after a write, falling back to the caller's own\n * values if it has already been removed by a concurrent delete.\n */\n private async readBack(\n collectionPath: string,\n objectId: ObjectId | string | number,\n fallback: Record<string, unknown>\n ): Promise<Record<string, unknown>> {\n const doc = await this.getCollection(collectionPath).findOne({ _id: objectId } as Filter<Document>);\n return doc ? this.documentToRow(doc) : fallback;\n }\n\n /**\n * Delete a row by ID.\n *\n * Rejects when nothing matched, which is the `DataDriver.delete` contract:\n * resolving means the row is gone because this call removed it. This used\n * to log a warning and resolve, so a caller could not tell a delete from a\n * no-op — and the same call on the Postgres driver threw. The wording is\n * the Postgres driver's, verbatim, because two spellings of one answer is\n * how the two came apart in the first place.\n */\n async delete(\n collectionPath: string,\n id: string | number,\n _databaseId?: string\n ): Promise<void> {\n const collection = this.getCollection(collectionPath);\n const objectId = this.toObjectId(id);\n\n const result = await collection.deleteOne({ _id: objectId } as Filter<Document>);\n\n if (result.deletedCount === 0) {\n throw ApiError.notFound(`No row \"${id}\" in \"${collectionPath}\" to delete.`);\n }\n }\n\n /**\n * Check if a field value is unique in a collection\n */\n async checkUniqueField(\n collectionPath: string,\n fieldName: string,\n value: any,\n excludeEntityId?: string,\n _databaseId?: string\n ): Promise<boolean> {\n const collection = this.getCollection(collectionPath);\n\n const query: Filter<Document> = { [fieldName]: value };\n\n if (excludeEntityId) {\n const objectId = this.toObjectId(excludeEntityId);\n (query as Record<string, unknown>)._id = { $ne: objectId };\n }\n\n const count = await collection.countDocuments(query);\n return count === 0;\n }\n\n /**\n * Generate a new row ID\n */\n generateId(): string {\n return new ObjectId().toString();\n }\n}\n","/**\n * MongoDB Realtime Service\n *\n * Implements RealtimeProvider interface using MongoDB Change Streams.\n * Provides real-time subscriptions to collection and row changes.\n */\n\nimport { Db, ChangeStream, ChangeStreamDocument, Document, ObjectId } from \"mongodb\";\nimport {\n ANONYMOUS_USER_ID,\n DataDriver,\n FilterValues,\n RealtimeProvider,\n CollectionSubscriptionConfig,\n SingleSubscriptionConfig,\n WebSocketMessage,\n User,\n ListLimitError,\n resolveClientListLimit\n} from \"@rebasepro/types\";\nimport { WebSocket } from \"ws\";\n\nimport type { MongoDriver } from \"./MongoDriver\";\nimport { logger } from \"@rebasepro/server\";\n\n/** The acting user for a subscription, as the driver and the socket carry it. */\nexport interface SubscriptionAuthContext {\n uid: string;\n roles: string[];\n}\n\n/**\n * The query half of a subscription config — everything except who is watching.\n *\n * Spread into the re-fetch rather than re-listed field by field. Re-listing is\n * how `logical` and `offset` went missing twice on this path: the type declares\n * them, every boundary accepted them, and each hand-written list quietly named\n * a subset. A function that removes the two non-query fields cannot fall behind\n * the type the way a list of the other nine can.\n */\nconst queryOf = <T extends { clientId: string; authContext?: SubscriptionAuthContext }>(\n config: T\n): Omit<T, \"clientId\" | \"authContext\"> => {\n const { clientId: _clientId, authContext: _authContext, ...query } = config;\n return query;\n};\n\ninterface Subscription {\n type: \"collection\" | \"single\";\n /**\n * Carries `authContext`. There is deliberately no second copy on this\n * object: every fetch reads `config.authContext`, and the one that used to\n * live here was written from three places and read from none — so a\n * subscription that looked authorized was re-fetched as nobody.\n */\n config: (CollectionSubscriptionConfig | SingleSubscriptionConfig) & { authContext?: SubscriptionAuthContext };\n changeStream?: ChangeStream;\n callback?: (data: any) => void;\n /**\n * How many deliveries have been started for this subscription, and the\n * highest that has already reached the callback.\n *\n * Every delivery here is a re-fetch, and three independent things start one\n * for the same subscription: the initial fetch, the change stream, and\n * `notifyUpdate` after a save. They overlap, and a fetch that started\n * earlier can finish later — at which point the callback replaces the\n * client's whole list with the state before the change. Nothing corrects it\n * until something else happens to that collection.\n *\n * A counter taken before the await and checked after it is what makes the\n * last *started* delivery the last *delivered* one.\n */\n started: number;\n delivered: number;\n}\n\n/**\n * MongoDB Realtime Service\n *\n * Implements real-time subscriptions using MongoDB Change Streams.\n * Requires MongoDB replica set for change streams to work.\n */\nexport class MongoRealtimeService implements RealtimeProvider {\n private subscriptions = new Map<string, Subscription>();\n private clients = new Map<string, WebSocket>();\n private driver?: MongoDriver;\n\n constructor(private db: Db) {}\n\n setDataDriver(driver: MongoDriver) {\n this.driver = driver;\n }\n\n /**\n * Get the collection name from a path\n */\n private getCollectionName(path: string): string {\n return path.replace(/\\//g, \"_\");\n }\n\n /**\n * Claim a delivery slot for a subscription, before doing the work.\n *\n * Returns the check to run immediately before calling the callback. It\n * refuses in three cases, all of which used to deliver:\n *\n * - **Out of order.** A newer fetch has already delivered, so this one is\n * stale — the client would go back to the state before the change.\n * - **Unsubscribed.** The subscription was cancelled while the fetch was in\n * flight, and its callback belongs to a client that stopped listening.\n * - **Re-subscribed.** `subscribeToCollection` unsubscribes first, so the\n * same id can name a *different* subscription by the time a fetch lands —\n * with a different filter, and a different caller's rows.\n *\n * Synchronous deliveries claim a slot too. A `delete` notification with no\n * fetch behind it is the newest thing known about the row, so it must also\n * be the thing that closes the door on an older fetch still in flight —\n * otherwise the deleted row reappears a moment after it vanished.\n */\n private beginDelivery(subscriptionId: string, subscription: Subscription): () => boolean {\n const seq = ++subscription.started;\n return () => {\n if (this.subscriptions.get(subscriptionId) !== subscription) return false;\n if (seq <= subscription.delivered) return false;\n subscription.delivered = seq;\n return true;\n };\n }\n\n /**\n * Subscribe to collection changes\n */\n subscribeToCollection(\n subscriptionId: string,\n config: CollectionSubscriptionConfig & { authContext?: SubscriptionAuthContext },\n callback?: (rows: Record<string, unknown>[]) => void\n ): void {\n // Clean up existing subscription if any\n this.unsubscribe(subscriptionId);\n\n const collectionName = this.getCollectionName(config.path);\n const collection = this.db.collection(collectionName);\n\n // Build pipeline for change stream filtering\n const pipeline: Document[] = [];\n\n // Filter by operation types we care about\n pipeline.push({\n $match: {\n operationType: { $in: [\"insert\", \"update\", \"replace\", \"delete\"] }\n }\n });\n\n try {\n // Create change stream\n const changeStream = collection.watch(pipeline, {\n fullDocument: \"updateLookup\"\n });\n\n const subscription: Subscription = {\n type: \"collection\",\n config,\n changeStream,\n callback,\n started: 0,\n delivered: 0\n };\n\n this.subscriptions.set(subscriptionId, subscription);\n\n // Fetch initial data\n this.fetchAndNotifyCollection(subscriptionId, subscription);\n\n // Listen for changes\n changeStream.on(\"change\", async (change: ChangeStreamDocument) => {\n // Re-fetch the entire collection when any change happens\n // This is simpler and ensures consistent sorting/filtering\n await this.fetchAndNotifyCollection(subscriptionId, subscription);\n });\n\n changeStream.on(\"error\", (error: Error) => {\n logger.error(`Change stream error for subscription ${subscriptionId}`, { error: error });\n });\n\n } catch (error) {\n // Change streams might not be available (e.g., standalone MongoDB)\n logger.warn(\"Change streams not available, falling back to polling\", { error: error });\n\n // Store subscription without change stream for manual notifications\n const subscription: Subscription = {\n type: \"collection\",\n config,\n callback,\n started: 0,\n delivered: 0\n };\n\n this.subscriptions.set(subscriptionId, subscription);\n\n // Fetch initial data\n this.fetchAndNotifyCollection(subscriptionId, subscription);\n }\n }\n\n /**\n * Fetch collection and notify callback\n */\n private async fetchAndNotifyCollection(\n subscriptionId: string,\n subscription: Subscription\n ): Promise<void> {\n const config = subscription.config as CollectionSubscriptionConfig & { authContext?: SubscriptionAuthContext };\n const callback = subscription.callback;\n const canDeliver = this.beginDelivery(subscriptionId, subscription);\n try {\n const registryCollection = this.driver?.registry?.getCollectionByPath(config.path);\n // One path, authenticated or not. The `else` branch used to reach\n // past the driver into the repository, which applies no security\n // rules at all — the fallback stubbing out the contract the primary\n // branch honours, and granting more while doing it. An anonymous\n // subscriber is now a user like any other: rules are evaluated\n // against the anonymous uid, and a rule that needs a real one\n // matches nothing.\n const driver = await this.scopedDriver(config.authContext);\n // The stored config forwarded whole. Re-listing its fields here is\n // how `logical` and `offset` went missing a second time, one layer\n // below where they went missing the first time: the subscription\n // carried them and the re-fetch did not ask for them.\n const rows = await driver.fetchCollection({\n ...queryOf(config),\n filter: config.filter as FilterValues<string> | undefined,\n collection: registryCollection\n });\n\n if (callback && canDeliver()) {\n callback(rows);\n }\n } catch (error) {\n logger.error(`Error fetching collection for subscription ${subscriptionId}`, { error: error });\n }\n }\n\n /**\n * The driver scoped to a subscriber.\n *\n * Never the bare repository: everything a subscription delivers has to pass\n * the same row authorization an HTTP read does.\n */\n private async scopedDriver(authContext?: SubscriptionAuthContext): Promise<DataDriver> {\n if (!this.driver) {\n throw new Error(\"MongoRealtimeService has no data driver — subscriptions cannot be authorized\");\n }\n const user = { uid: authContext?.uid ?? ANONYMOUS_USER_ID,\nroles: authContext?.roles ?? [] } as User;\n return this.driver.withAuth(user);\n }\n\n /**\n * Subscribe to single row changes\n */\n subscribeToOne(\n subscriptionId: string,\n config: SingleSubscriptionConfig & { authContext?: SubscriptionAuthContext },\n callback?: (row: Record<string, unknown> | null) => void\n ): void {\n // Clean up existing subscription if any\n this.unsubscribe(subscriptionId);\n\n const collectionName = this.getCollectionName(config.path);\n const collection = this.db.collection(collectionName);\n\n // Build pipeline to watch specific document\n const id = typeof config.id === \"string\" && ObjectId.isValid(config.id)\n ? new ObjectId(config.id)\n : config.id;\n\n const pipeline: Document[] = [\n {\n $match: {\n \"documentKey._id\": id,\n operationType: { $in: [\"insert\", \"update\", \"replace\", \"delete\"] }\n }\n }\n ];\n\n try {\n const changeStream = collection.watch(pipeline, {\n fullDocument: \"updateLookup\"\n });\n\n const subscription: Subscription = {\n type: \"single\",\n config,\n changeStream,\n callback,\n started: 0,\n delivered: 0\n };\n\n this.subscriptions.set(subscriptionId, subscription);\n\n // Fetch initial data\n this.fetchAndNotifyOne(subscriptionId, subscription);\n\n // Listen for changes\n changeStream.on(\"change\", async (change: ChangeStreamDocument) => {\n if (change.operationType === \"delete\") {\n // Claims a slot like any other delivery: the deletion is the\n // newest fact about this row, so an older fetch still in\n // flight must not put it back.\n const canDeliver = this.beginDelivery(subscriptionId, subscription);\n if (callback && canDeliver()) {\n callback(null);\n }\n } else {\n await this.fetchAndNotifyOne(subscriptionId, subscription);\n }\n });\n\n changeStream.on(\"error\", (error: Error) => {\n logger.error(`Change stream error for subscription ${subscriptionId}`, { error: error });\n });\n\n } catch (error) {\n logger.warn(\"Change streams not available, falling back to polling\", { error: error });\n\n const subscription: Subscription = {\n type: \"single\",\n config,\n callback,\n started: 0,\n delivered: 0\n };\n\n this.subscriptions.set(subscriptionId, subscription);\n\n // Fetch initial data\n this.fetchAndNotifyOne(subscriptionId, subscription);\n }\n }\n\n /**\n * Fetch row and notify callback\n */\n private async fetchAndNotifyOne(\n subscriptionId: string,\n subscription: Subscription\n ): Promise<void> {\n const config = subscription.config as SingleSubscriptionConfig & { authContext?: SubscriptionAuthContext };\n const callback = subscription.callback;\n const canDeliver = this.beginDelivery(subscriptionId, subscription);\n try {\n const registryCollection = this.driver?.registry?.getCollectionByPath(config.path);\n const driver = await this.scopedDriver(config.authContext);\n const row = await driver.fetchOne({\n path: config.path,\n id: config.id,\n collection: registryCollection\n });\n\n if (callback && canDeliver()) {\n callback(row || null);\n }\n } catch (error) {\n logger.error(`Error fetching row for subscription ${subscriptionId}`, { error: error });\n }\n }\n\n /**\n * Unsubscribe from a subscription\n */\n unsubscribe(subscriptionId: string): void {\n const subscription = this.subscriptions.get(subscriptionId);\n if (subscription) {\n if (subscription.changeStream) {\n subscription.changeStream.close().catch((err) => logger.error(\"Operation failed\", { error: err }));\n }\n this.subscriptions.delete(subscriptionId);\n }\n }\n\n /**\n * Notify all relevant subscribers of an row update\n * This is called after save/delete operations to push updates\n */\n async notifyUpdate(\n path: string,\n id: string,\n row: Record<string, unknown> | null,\n _databaseId?: string\n ): Promise<void> {\n // Find all subscriptions that might be affected by this update\n for (const [subscriptionId, subscription] of this.subscriptions) {\n if (subscription.type === \"single\") {\n const config = subscription.config as SingleSubscriptionConfig & { authContext?: SubscriptionAuthContext };\n if (config.path === path && config.id.toString() === id) {\n if (row === null) {\n // A deletion carries no row to authorize — but it still\n // claims a delivery slot, so a re-fetch already in\n // flight cannot land after it and resurrect the row.\n const canDeliver = this.beginDelivery(subscriptionId, subscription);\n if (canDeliver()) subscription.callback?.(null);\n } else {\n // Re-fetched through the subscriber's own driver rather\n // than pushed verbatim: `notifyUpdate` runs after every\n // save, and handing it the row as written broadcast any\n // document to whoever happened to be watching its id.\n await this.fetchAndNotifyOne(subscriptionId, subscription);\n }\n }\n } else if (subscription.type === \"collection\") {\n const config = subscription.config as CollectionSubscriptionConfig & { authContext?: SubscriptionAuthContext };\n if (config.path === path) {\n // Re-fetch the collection to get updated data\n await this.fetchAndNotifyCollection(subscriptionId, subscription);\n }\n }\n }\n }\n\n /**\n * Get all active subscriptions (for debugging)\n */\n getSubscriptions(): Map<string, Subscription> {\n return this.subscriptions;\n }\n\n /**\n * Close all subscriptions\n */\n async closeAll(): Promise<void> {\n for (const [subscriptionId] of this.subscriptions) {\n this.unsubscribe(subscriptionId);\n }\n }\n\n // =============================================================================\n // WebSocket Client Management (parity with PostgreSQL RealtimeService)\n // =============================================================================\n\n /**\n * Register a WebSocket client for real-time communication\n */\n addClient(clientId: string, ws: WebSocket) {\n this.clients.set(clientId, ws);\n\n ws.on(\"close\", () => {\n this.removeClient(clientId);\n });\n\n ws.on(\"error\", (error) => {\n logger.error(\"WebSocket error for client\", { detail: clientId, error });\n this.removeClient(clientId);\n });\n }\n\n /**\n * Remove a WebSocket client and clean up its subscriptions\n */\n private removeClient(clientId: string) {\n this.clients.delete(clientId);\n }\n\n /**\n * Handle an incoming WebSocket message for subscription management\n */\n async handleClientMessage(\n clientId: string,\n message: { type: string; payload?: any; subscriptionId?: string },\n _authContext?: { uid: string; roles: unknown[] }\n ): Promise<void> {\n const ws = this.clients.get(clientId);\n if (!ws) return;\n\n const authContext = _authContext ? { uid: _authContext.uid,\nroles: (_authContext.roles ?? []).map(String) } : undefined;\n\n switch (message.type) {\n case \"subscribe_collection\": {\n const subscriptionId = message.payload?.subscriptionId ?? message.subscriptionId;\n if (!subscriptionId) return;\n\n // The same list bound the Postgres socket and every REST route\n // apply. This ingress applied none: an absent limit reached the\n // driver as `undefined` and emitted no `.limit()` at all, so one\n // subscribe frame streamed the whole collection — and re-streamed\n // it on every matching write. An over-large limit is refused\n // rather than shrunk, because a `collection_update` frame carries\n // no `total` or `hasMore` for the client to notice with.\n let boundedLimit: number;\n try {\n boundedLimit = resolveClientListLimit(message.payload?.limit);\n } catch (e) {\n if (!(e instanceof ListLimitError)) throw e;\n logger.warn(`⚠️ [MongoRealtime] Refused subscription to '${message.payload?.path}': ${e.message}`);\n ws.send(JSON.stringify({\n type: \"ERROR\",\n subscriptionId,\n payload: { error: { message: e.message, code: \"INVALID_LIMIT\" } },\n error: e.message\n }));\n return;\n }\n\n this.subscribeToCollection(\n subscriptionId,\n {\n clientId,\n path: message.payload?.path,\n filter: message.payload?.filter,\n // `logical` and `offset` were absent from this list, so\n // an `or(...)` subscription was pushed every row the\n // caller's policies allowed and a subscription to page\n // two was pushed page one. The client has been sending\n // both since it stopped stringifying `offset` into\n // `startAfter`; nothing here read them.\n logical: message.payload?.logical,\n offset: message.payload?.offset,\n orderBy: message.payload?.orderBy,\n order: message.payload?.order,\n limit: boundedLimit,\n startAfter: message.payload?.startAfter,\n searchString: message.payload?.searchString,\n searchExplain: message.payload?.searchExplain,\n authContext\n },\n (rows) => {\n ws.send(JSON.stringify({\n type: \"collection_update\",\n subscriptionId,\n rows\n }));\n }\n );\n break;\n }\n case \"subscribe_one\": {\n const subscriptionId = message.payload?.subscriptionId ?? message.subscriptionId;\n if (!subscriptionId) return;\n\n this.subscribeToOne(\n subscriptionId,\n {\n clientId,\n path: message.payload?.path,\n id: message.payload?.id,\n authContext\n },\n (row) => {\n ws.send(JSON.stringify({\n type: \"single_update\",\n subscriptionId,\n row\n }));\n }\n );\n break;\n }\n case \"unsubscribe\": {\n const subscriptionId = message.payload?.subscriptionId ?? message.subscriptionId;\n if (subscriptionId) {\n this.unsubscribe(subscriptionId);\n }\n break;\n }\n default: {\n // A silent `switch` over a wire protocol is how channel,\n // presence and broadcast frames came to be accepted here and\n // dropped: the client's `broadcast()` resolved, `onPresence`\n // never fired, and a `channel_history` request buffered live\n // messages until the catch-up timeout on every join. Say so,\n // and tell the sender rather than leaving it waiting.\n logger.warn(\n `⚠️ [MongoRealtime] Unhandled realtime message type \"${message.type}\" — ` +\n \"channels, presence and broadcast are not implemented by the Mongo driver.\"\n );\n ws.send(JSON.stringify({\n type: \"ERROR\",\n subscriptionId: message.subscriptionId,\n payload: {\n error: {\n message: `Realtime message type \"${message.type}\" is not supported by the Mongo driver`,\n code: \"REALTIME_UNSUPPORTED\"\n }\n },\n error: `Realtime message type \"${message.type}\" is not supported by the Mongo driver`\n }));\n break;\n }\n }\n }\n}\n","import { Db, ObjectId } from \"mongodb\";\nimport { logger } from \"@rebasepro/server\";\n\n/**\n * Deep equality without JSON.stringify.\n * Handles primitives, arrays, Dates, and plain objects recursively.\n */\nfunction deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (a == null || b == null) return false;\n if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();\n if (Array.isArray(a) && Array.isArray(b)) {\n if (a.length !== b.length) return false;\n return a.every((v, i) => deepEqual(v, b[i]));\n }\n if (typeof a === \"object\" && typeof b === \"object\") {\n const aObj = a as Record<string, unknown>;\n const bObj = b as Record<string, unknown>;\n const aKeys = Object.keys(aObj);\n const bKeys = Object.keys(bObj);\n if (aKeys.length !== bKeys.length) return false;\n return aKeys.every(k => deepEqual(aObj[k], bObj[k]));\n }\n return false;\n}\n\n/**\n * Shallow comparison to find top-level keys that changed between two objects.\n */\nexport function findChangedFields(\n oldValues: Record<string, unknown>,\n newValues: Record<string, unknown>\n): string[] | null {\n const changed: string[] = [];\n const allKeys = new Set([\n ...Object.keys(oldValues),\n ...Object.keys(newValues)\n ]);\n\n for (const key of allKeys) {\n const oldVal = oldValues[key];\n const newVal = newValues[key];\n\n // Skip internal metadata\n if (key.startsWith(\"__\")) continue;\n\n if (oldVal !== newVal) {\n // For objects/arrays, use structural comparison\n if (\n typeof oldVal === \"object\" && oldVal !== null &&\n typeof newVal === \"object\" && newVal !== null\n ) {\n if (!deepEqual(oldVal, newVal)) {\n changed.push(key);\n }\n } else {\n changed.push(key);\n }\n }\n }\n\n return changed.length > 0 ? changed : null;\n}\n\nexport type { RecordHistoryParams, HistoryRetentionConfig } from \"@rebasepro/types\";\nimport type { EntityHistoryEntry, RecordHistoryParams, HistoryRetentionConfig } from \"@rebasepro/types\";\n\n/**\n * A history entry as MongoDB stores it — not as it travels.\n *\n * Two fields differ from {@link EntityHistoryEntry}: the driver's own `_id`,\n * and `updated_at` as a native `Date` so the retention query can compare it\n * with `$lt`. Both of these used to be on an interface *named* `HistoryEntry`,\n * which is also what `@rebasepro/server-postgres` called its wire shape — so\n * the same name meant `string` in one driver and `Date` in the other.\n */\nexport interface MongoHistoryDocument extends Omit<EntityHistoryEntry, \"updated_at\"> {\n _id?: ObjectId;\n updated_at: Date;\n}\n\nconst DEFAULT_RETENTION: HistoryRetentionConfig = {\n maxEntries: 200,\n ttlDays: 90\n};\n\nexport class MongoHistoryService {\n public retention: HistoryRetentionConfig;\n\n constructor(\n private db: Db,\n retention?: Partial<HistoryRetentionConfig>\n ) {\n this.retention = { ...DEFAULT_RETENTION,\n...retention };\n }\n\n async recordHistory(params: RecordHistoryParams): Promise<void> {\n const {\n tableName,\n id,\n action,\n values,\n previousValues,\n updatedBy\n } = params;\n\n const changedFields = previousValues && values\n ? findChangedFields(previousValues, values)\n : null;\n\n if (action === \"update\" && (!changedFields || changedFields.length === 0)) {\n return;\n }\n\n try {\n const entry: MongoHistoryDocument = {\n id: new ObjectId().toString(),\n table_name: tableName,\n entity_id: String(id),\n action,\n changed_fields: changedFields,\n values: values || null,\n previous_values: previousValues || null,\n updated_by: updatedBy || null,\n updated_at: new Date()\n };\n\n await this.db.collection(\"__rebase_history\").insertOne(entry);\n\n // Non-blocking prune for this specific row\n this.pruneHistory(String(id), tableName).catch(e => {\n logger.error(`[HistoryService] Failed to prune history for ${tableName}/${id}`, { error: e });\n });\n } catch (error) {\n logger.error(`[HistoryService] Failed to record history for ${tableName}/${id}`, { error: error });\n }\n }\n\n private async pruneHistory(id: string, tableName: string): Promise<void> {\n const collection = this.db.collection(\"__rebase_history\");\n\n // 1. Enforce maxEntries\n const count = await collection.countDocuments({ entity_id: id,\ntable_name: tableName });\n if (count > this.retention.maxEntries) {\n const toDelete = count - this.retention.maxEntries;\n const oldestEntries = await collection\n .find({ entity_id: id,\ntable_name: tableName })\n .sort({ updated_at: 1 })\n .limit(toDelete)\n .toArray();\n\n if (oldestEntries.length > 0) {\n const idsToDelete = oldestEntries.map(entry => entry._id);\n await collection.deleteMany({ _id: { $in: idsToDelete } });\n }\n }\n\n // 2. Enforce ttlDays\n const cutoffDate = new Date();\n cutoffDate.setDate(cutoffDate.getDate() - this.retention.ttlDays);\n\n await collection.deleteMany({\n entity_id: id,\n table_name: tableName,\n updated_at: { $lt: cutoffDate }\n });\n }\n}\n","/**\n * Row security for MongoDB.\n *\n * MongoDB has no RLS, so this driver enforces `securityRules` in-process. That\n * makes the translation from a rule to a query the enforcement boundary, and it\n * has exactly one safe failure mode: refuse.\n *\n * Two properties this file exists to hold:\n *\n * 1. **One predicate, one implementation.** The rules are compiled through the\n * same {@link securityRuleToConditions} the Postgres DDL generator and the\n * admin UI's `checkOperation` use, so \"what does this rule mean\" is answered\n * in one place. The previous translator re-parsed the raw SQL itself and\n * recognised four shapes — a second, smaller parser that disagreed with the\n * first about the same rule.\n * 2. **Fail closed, out loud.** An expression with no MongoDB equivalent (raw\n * SQL, a membership subquery, a negated row predicate) used to become `{}` —\n * \"match every document\". It now raises {@link SECURITY_RULE_UNSUPPORTED},\n * the same shape the REST layer uses to refuse bulk writes this driver\n * cannot perform: a request that cannot be authorized is not served.\n */\n\nimport { Document, Filter } from \"mongodb\";\nimport {\n ANONYMOUS_USER_ID,\n CollectionConfig,\n PolicyExpression,\n PolicyOperand,\n SecurityOperation,\n SecurityRule,\n User,\n isAnonymousUid\n} from \"@rebasepro/types\";\nimport { securityRuleToConditions } from \"@rebasepro/common\";\nimport { ApiError } from \"@rebasepro/server\";\n\n/** Matches every document. */\nconst MATCH_ALL: Filter<Document> = {};\n\n/**\n * Matches no document. A distinct object rather than a `false` sentinel so it\n * can be nested inside `$and`/`$or` like any other filter; identity is what\n * {@link isMatchNone} tests, so never mutate or copy it.\n */\nconst MATCH_NONE: Filter<Document> = { _id: { $exists: false } };\n\n/** The expression has no MongoDB equivalent — the caller must refuse. */\nconst UNTRANSLATABLE = \"untranslatable\" as const;\n\ntype TranslationResult = Filter<Document> | typeof UNTRANSLATABLE;\n\nfunction isMatchAll(f: TranslationResult): boolean {\n return f !== UNTRANSLATABLE && f !== MATCH_NONE && Object.keys(f).length === 0;\n}\n\nfunction isMatchNone(f: TranslationResult): boolean {\n return f === MATCH_NONE;\n}\n\n/** The error code a caller sees when a rule cannot be honoured. */\nexport const SECURITY_RULE_UNSUPPORTED = \"SECURITY_RULE_UNSUPPORTED\";\n\n/**\n * The refusal. Names the collection, the clause and the expression, because the\n * only useful thing an operator can do with this is rewrite that rule — or move\n * the collection to an engine that can enforce it.\n */\nexport function securityRuleUnsupported(\n collectionSlug: string,\n clause: \"using\" | \"withCheck\",\n detail: string\n): ApiError {\n return ApiError.internal(\n `This collection's data source (MongoDB) cannot enforce a security rule on \"${collectionSlug}\": ` +\n `the \\`${clause}\\` expression ${detail} has no MongoDB equivalent. The request was refused rather ` +\n \"than served without row authorization. Express the rule with `access`, `ownerField`, `roles`, or a \" +\n \"structured `condition`/`check`, or move this collection to a Postgres data source.\",\n SECURITY_RULE_UNSUPPORTED\n );\n}\n\n/** Describe an expression well enough for the refusal message to be actionable. */\nfunction describe(expr: PolicyExpression): string {\n switch (expr.kind) {\n case \"raw\":\n return `\\`${expr.sql}\\``;\n case \"existsIn\":\n return `a membership subquery over \\`${expr.collection}\\``;\n case \"not\":\n return \"a negated row predicate\";\n default:\n return `a \\`${expr.kind}\\` node`;\n }\n}\n\n/** The first node of an expression tree this driver cannot translate, if any. */\nfunction findUntranslatable(expr: PolicyExpression, hasRow: boolean): PolicyExpression | undefined {\n switch (expr.kind) {\n case \"and\":\n case \"or\": {\n for (const operand of expr.operands) {\n const found = findUntranslatable(operand, hasRow);\n if (found) return found;\n }\n return undefined;\n }\n case \"not\":\n // Only decidable without the row when the operand is: negating a\n // column predicate in MongoDB (`$nor`) also matches documents that\n // lack the column, which SQL's three-valued logic would exclude.\n return hasRow ? findUntranslatable(expr.operand, hasRow) : (referencesField(expr.operand) ? expr : undefined);\n case \"compare\":\n return operandUntranslatable(expr.left) || operandUntranslatable(expr.right) ? expr : undefined;\n case \"existsIn\":\n case \"raw\":\n return expr;\n default:\n return undefined;\n }\n}\n\nfunction operandUntranslatable(operand: PolicyOperand): boolean {\n return operand.kind === \"outerField\";\n}\n\nfunction referencesField(expr: PolicyExpression): boolean {\n switch (expr.kind) {\n case \"and\":\n case \"or\":\n return expr.operands.some(referencesField);\n case \"not\":\n return referencesField(expr.operand);\n case \"compare\":\n return expr.left.kind === \"field\" || expr.right.kind === \"field\" ||\n expr.left.kind === \"outerField\" || expr.right.kind === \"outerField\";\n default:\n return false;\n }\n}\n\n/** The acting user, as the policy model sees them. */\ninterface PolicyUserContext {\n uid: string;\n roles: string[];\n}\n\nfunction userContext(user: User | undefined): PolicyUserContext {\n // The sentinel, not an empty string: `rebase.uid()` is never NULL for a\n // request that came from a client, and an `ownerField` rule compared\n // against `undefined` would become `{ owner: undefined }` — which MongoDB\n // reads as `{ owner: null }` and matches every document that has no owner.\n return {\n uid: user?.uid || ANONYMOUS_USER_ID,\n roles: user?.roles ?? []\n };\n}\n\nconst COMPARE_TO_MONGO = {\n eq: \"$eq\",\n neq: \"$ne\",\n lt: \"$lt\",\n lte: \"$lte\",\n gt: \"$gt\",\n gte: \"$gte\"\n} as const;\n\nconst INVERTED_COMPARE = {\n eq: \"eq\",\n neq: \"neq\",\n lt: \"gt\",\n lte: \"gte\",\n gt: \"lt\",\n gte: \"lte\"\n} as const;\n\ntype ResolvedOperand =\n | { kind: \"field\"; name: string }\n | { kind: \"value\"; value: unknown }\n | { kind: \"unknown\" };\n\nfunction resolveOperand(operand: PolicyOperand, ctx: PolicyUserContext): ResolvedOperand {\n switch (operand.kind) {\n case \"literal\":\n return { kind: \"value\", value: operand.value };\n case \"authUid\":\n return { kind: \"value\", value: ctx.uid };\n case \"authRoles\":\n return { kind: \"value\", value: ctx.roles };\n case \"field\":\n return { kind: \"field\", name: operand.name };\n case \"outerField\":\n return { kind: \"unknown\" };\n }\n}\n\n/**\n * Translate one {@link PolicyExpression} into a MongoDB filter, or\n * {@link UNTRANSLATABLE}.\n *\n * The JavaScript twin of `evaluatePolicy`, one level up: where that decides a\n * single row, this narrows a query. `\"unknown\"` there and `UNTRANSLATABLE` here\n * are the same condition, and both are resolved fail-closed by their callers.\n */\nexport function policyToMongoFilter(expr: PolicyExpression, user: User | undefined): TranslationResult {\n const ctx = userContext(user);\n\n switch (expr.kind) {\n case \"true\":\n return MATCH_ALL;\n case \"false\":\n return MATCH_NONE;\n case \"and\": {\n const parts = expr.operands.map(o => policyToMongoFilter(o, user));\n // Kleene AND: a `false` operand settles the conjunction even when a\n // sibling is untranslatable, which is what keeps a role-scoped raw\n // rule from refusing requests it does not even apply to.\n if (parts.some(isMatchNone)) return MATCH_NONE;\n if (parts.some(p => p === UNTRANSLATABLE)) return UNTRANSLATABLE;\n const clauses = (parts as Filter<Document>[]).filter(p => !isMatchAll(p));\n if (clauses.length === 0) return MATCH_ALL;\n if (clauses.length === 1) return clauses[0];\n return { $and: clauses } as Filter<Document>;\n }\n case \"or\": {\n const parts = expr.operands.map(o => policyToMongoFilter(o, user));\n if (parts.some(isMatchAll)) return MATCH_ALL;\n if (parts.some(p => p === UNTRANSLATABLE)) return UNTRANSLATABLE;\n const clauses = (parts as Filter<Document>[]).filter(p => !isMatchNone(p));\n if (clauses.length === 0) return MATCH_NONE;\n if (clauses.length === 1) return clauses[0];\n return { $or: clauses } as Filter<Document>;\n }\n case \"not\": {\n const inner = policyToMongoFilter(expr.operand, user);\n // Constant-folded only. See `findUntranslatable` for why a negated\n // column predicate is refused instead of becoming `$nor`.\n if (isMatchAll(inner)) return MATCH_NONE;\n if (isMatchNone(inner)) return MATCH_ALL;\n return UNTRANSLATABLE;\n }\n case \"compare\": {\n const left = resolveOperand(expr.left, ctx);\n const right = resolveOperand(expr.right, ctx);\n if (left.kind === \"unknown\" || right.kind === \"unknown\") return UNTRANSLATABLE;\n\n if (left.kind === \"field\" && right.kind === \"value\") {\n return { [left.name]: { [COMPARE_TO_MONGO[expr.op]]: right.value } } as Filter<Document>;\n }\n if (left.kind === \"value\" && right.kind === \"field\") {\n return { [right.name]: { [COMPARE_TO_MONGO[INVERTED_COMPARE[expr.op]]]: left.value } } as Filter<Document>;\n }\n if (left.kind === \"field\" && right.kind === \"field\") {\n return { $expr: { [COMPARE_TO_MONGO[expr.op]]: [`$${left.name}`, `$${right.name}`] } } as Filter<Document>;\n }\n // Both sides are known values — the comparison is a constant.\n if (left.kind === \"value\" && right.kind === \"value\") {\n return compareValues(expr.op, left.value, right.value);\n }\n return UNTRANSLATABLE;\n }\n case \"rolesOverlap\":\n return expr.roles.some(r => r === \"public\" || ctx.roles.includes(r)) ? MATCH_ALL : MATCH_NONE;\n case \"rolesContain\":\n return expr.roles.every(r => r === \"public\" || ctx.roles.includes(r)) ? MATCH_ALL : MATCH_NONE;\n case \"authenticated\":\n return !isAnonymousUid(ctx.uid) ? MATCH_ALL : MATCH_NONE;\n case \"serverContext\":\n // A scoped driver is always acting for a user, never the server\n // context — the same answer `evaluatePolicy` gives.\n return MATCH_NONE;\n case \"existsIn\":\n case \"raw\":\n return UNTRANSLATABLE;\n }\n}\n\nfunction compareValues(op: keyof typeof COMPARE_TO_MONGO, a: unknown, b: unknown): TranslationResult {\n if (op === \"eq\") return a === b ? MATCH_ALL : MATCH_NONE;\n if (op === \"neq\") return a !== b ? MATCH_ALL : MATCH_NONE;\n if ((typeof a === \"string\" && typeof b === \"string\") || (typeof a === \"number\" && typeof b === \"number\")) {\n const decided = op === \"lt\" ? a < b : op === \"lte\" ? a <= b : op === \"gt\" ? a > b : a >= b;\n return decided ? MATCH_ALL : MATCH_NONE;\n }\n return UNTRANSLATABLE;\n}\n\n/** The rules that apply to `targetOperation`, mirroring `checkOperation`. */\nfunction applicableRules(collection: CollectionConfig | undefined, targetOperation: SecurityOperation): SecurityRule[] {\n const rules = collection?.securityRules;\n if (!rules || rules.length === 0) return [];\n return rules.filter((rule: SecurityRule) => {\n const ops = rule.operations && rule.operations.length > 0 ? rule.operations : [rule.operation ?? \"all\"];\n return ops.includes(targetOperation) || ops.includes(\"all\");\n });\n}\n\n/** Which clause of a rule constrains `targetOperation` — Postgres's own split. */\nfunction clauseFor(targetOperation: SecurityOperation): \"using\" | \"withCheck\" {\n return targetOperation === \"insert\" ? \"withCheck\" : \"using\";\n}\n\n/**\n * Refuse up front when this collection's rules cannot be enforced for\n * `targetOperation`.\n *\n * The row-in-hand paths (`fetchOne`, `save`, `delete`) resolve an undecidable\n * rule through `checkOperation`'s `onUnknown: \"deny\"`, which is safe but\n * indistinguishable from a plain \"you may not do that\". Calling this first\n * turns the same condition into the refusal an operator can act on.\n */\nexport function assertSecurityRulesEnforceable(\n collection: CollectionConfig | undefined,\n targetOperation: SecurityOperation\n): void {\n for (const rule of applicableRules(collection, targetOperation)) {\n const conditions = securityRuleToConditions(rule);\n const clauses: (\"using\" | \"withCheck\")[] = targetOperation === \"insert\"\n ? [\"withCheck\"]\n : targetOperation === \"update\" ? [\"using\", \"withCheck\"] : [\"using\"];\n for (const clause of clauses) {\n const expr = clause === \"using\" ? conditions.usingExpr : conditions.withCheckExpr;\n if (!expr) continue;\n // `hasRow: true` — these callers evaluate against a fetched row, so\n // only the nodes no JavaScript evaluator can decide are refused.\n const offending = findUntranslatable(expr, true);\n if (offending) {\n throw securityRuleUnsupported(collection?.slug ?? \"unknown\", clause, describe(offending));\n }\n }\n }\n}\n\n/**\n * Build the MongoDB filter that narrows a query to the rows `user` may see\n * under `collection`'s security rules.\n *\n * Returns `null` when no row can qualify (the caller answers with an empty\n * result), `{}` when the rules impose no narrowing, and throws\n * {@link SECURITY_RULE_UNSUPPORTED} when a rule cannot be translated.\n */\nexport function buildMongoFilterFromSecurityRules<M extends Record<string, any>>(\n collection: CollectionConfig<M> | undefined,\n user: User | undefined,\n targetOperation: SecurityOperation\n): Filter<Document> | null {\n const rules = applicableRules(collection as CollectionConfig | undefined, targetOperation);\n if (!collection?.securityRules || collection.securityRules.length === 0) {\n return MATCH_ALL;\n }\n // Rules exist but none covers this operation — Postgres denies, so do we.\n if (rules.length === 0) return null;\n\n const clause = clauseFor(targetOperation);\n const permissive: Filter<Document>[] = [];\n const restrictive: Filter<Document>[] = [];\n\n for (const rule of rules) {\n const conditions = securityRuleToConditions(rule);\n const expr = clause === \"using\" ? conditions.usingExpr : conditions.withCheckExpr;\n // A null clause denies, matching Postgres's `USING (false)`.\n const filter = expr === null ? MATCH_NONE : policyToMongoFilter(expr, user);\n if (filter === UNTRANSLATABLE) {\n const offending = expr === null ? undefined : findUntranslatable(expr, false);\n throw securityRuleUnsupported(\n collection.slug,\n clause,\n offending ? describe(offending) : \"this rule\"\n );\n }\n if ((rule.mode || \"permissive\") === \"restrictive\") {\n restrictive.push(filter);\n } else {\n permissive.push(filter);\n }\n }\n\n // No permissive rule can grant → nothing is visible, exactly as\n // `checkOperation` returns false when `hasPermissive` is false.\n if (permissive.length === 0) return null;\n\n const parts: Filter<Document>[] = [];\n if (!permissive.some(isMatchAll)) {\n // A permissive rule that matches nothing contributes nothing to the\n // union; if that is all of them, nothing is visible.\n const granting = permissive.filter(p => !isMatchNone(p));\n if (granting.length === 0) return null;\n parts.push(granting.length === 1 ? granting[0] : ({ $or: granting } as Filter<Document>));\n }\n\n for (const rf of restrictive) {\n if (isMatchNone(rf)) return null;\n if (!isMatchAll(rf)) parts.push(rf);\n }\n\n if (parts.length === 0) return MATCH_ALL;\n if (parts.length === 1) return parts[0];\n return { $and: parts } as Filter<Document>;\n}\n","/**\n * MongoDB DataDriver Delegate\n *\n * Implements the DataDriver interface for Rebase frontend integration.\n * This is the main entry point for Rebase to interact with MongoDB.\n */\n\nimport { Db } from \"mongodb\";\nimport {\n DataDriver,\n DeleteProps,\n Entity,\n CollectionConfig,\n FetchCollectionProps,\n FetchOneProps,\n ListenCollectionProps,\n ListenOneProps,\n SaveProps,\n RebaseCallContext,\n CollectionRegistryInterface,\n User,\n RebaseClient,\n RebaseData,\n RebaseSdkData,\n SecurityOperation\n} from \"@rebasepro/types\";\nimport { MongoDataService } from \"../db/MongoDataService\";\nimport { MongoRealtimeService } from \"./MongoRealtimeService\";\nimport { MongoHistoryService } from \"./MongoHistoryService\";\nimport { buildPropertyCallbacks, updateDateAutoValues, buildSdkData, checkOperation, PolicyClauses } from \"@rebasepro/common\";\nimport { mergeDeep } from \"@rebasepro/utils\";\nimport { Filter, Document } from \"mongodb\";\nimport { ApiError } from \"@rebasepro/server\";\nimport { MongoConditionBuilder } from \"../db/MongoConditionBuilder\";\nimport { assertSecurityRulesEnforceable, buildMongoFilterFromSecurityRules } from \"../db/securityRuleFilter\";\nimport { logger } from \"@rebasepro/server\";\n\n/**\n * MongoDB DataDriver Delegate\n *\n * Implements the DataDriver interface for Rebase.\n * Provides all data operations needed by the Rebase frontend.\n */\nexport class MongoDriver implements DataDriver {\n key = \"mongodb\";\n initialised = true;\n\n private dataService: MongoDataService;\n private realtimeService: MongoRealtimeService;\n public historyService: MongoHistoryService;\n public user?: User;\n public data: RebaseSdkData;\n public client?: RebaseClient;\n\n constructor(\n private db: Db,\n realtimeService?: MongoRealtimeService,\n historyService?: MongoHistoryService,\n public readonly registry?: CollectionRegistryInterface,\n user?: User\n ) {\n this.dataService = new MongoDataService(db);\n this.realtimeService = realtimeService ?? new MongoRealtimeService(db);\n this.historyService = historyService ?? new MongoHistoryService(db);\n this.user = user;\n this.data = buildSdkData(this);\n this.realtimeService.setDataDriver(this);\n }\n\n /**\n * Get the current timestamp\n */\n currentTime(): Date {\n return new Date();\n }\n\n /**\n * Resolve a collection's callbacks and property callbacks from the registry.\n * Used by AuthenticatedMongoDriver to apply callbacks after RLS filtering.\n */\n resolveCollectionCallbacks<M extends Record<string, unknown>>(\n collection: CollectionConfig<M> | undefined,\n path: string\n ) {\n if (!collection && !path) return { collection: undefined,\ncallbacks: undefined,\nglobalCallbacks: undefined,\npropertyCallbacks: undefined };\n const registryCollection = this.registry?.getCollectionByPath(path);\n const resolvedCollection = registryCollection\n ? ({ ...collection,\n...registryCollection } as CollectionConfig<M>)\n : (collection as CollectionConfig<M>);\n\n const callbacks = resolvedCollection?.callbacks;\n const globalCallbacks = this.registry?.getGlobalCallbacks();\n const properties = resolvedCollection?.properties;\n let propertyCallbacks;\n if (properties) {\n propertyCallbacks = buildPropertyCallbacks(properties);\n }\n return {\n collection: resolvedCollection,\n callbacks,\n globalCallbacks,\n propertyCallbacks\n };\n }\n\n /**\n * Fetch a collection of rows\n */\n async fetchCollection<M extends Record<string, any>>(\n props: FetchCollectionProps<M>\n ): Promise<Record<string, unknown>[]> {\n // Forwarded whole rather than re-listed. The hand-written list here\n // named eight of the eleven fields `FetchCollectionProps` declares, so\n // `logical` and `offset` were accepted by every type-checked boundary\n // above and then dropped — an `or(...)` query ran unfiltered and\n // `?offset=` served page one.\n const { path, collection, ...query } = props;\n const rows = await this.dataService.fetchCollection<M>(path, {\n ...query,\n collection: collection as CollectionConfig\n });\n\n const { collection: resolvedCollection, callbacks, globalCallbacks, propertyCallbacks } = this.resolveCollectionCallbacks(collection, path);\n\n if (globalCallbacks?.afterRead || callbacks?.afterRead || propertyCallbacks?.afterRead) {\n const contextForCallback = {\n user: this.user,\n driver: this,\n data: this.data,\n client: this.client,\n storageSource: this.client?.storage\n } as unknown as RebaseCallContext; // Backend context\n return Promise.all(rows.map(async (row) => {\n let fetched = row;\n if (globalCallbacks?.afterRead) {\n fetched = await globalCallbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n row: fetched,\n context: contextForCallback\n }) ?? fetched;\n }\n if (callbacks?.afterRead) {\n fetched = await callbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n row: fetched,\n context: contextForCallback\n }) ?? fetched;\n }\n if (propertyCallbacks?.afterRead) {\n fetched = await propertyCallbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n row: fetched,\n context: contextForCallback\n }) ?? fetched;\n }\n return fetched;\n }));\n }\n\n return rows;\n }\n\n /**\n * Listen to collection changes.\n *\n * `authContext` is not part of `ListenCollectionProps`; it is supplied by\n * {@link AuthenticatedMongoDriver}, which is the only caller that has one.\n * It has to travel *into* the subscription config, because that config is\n * what every re-fetch reads — the wrapper used to stamp the field on the\n * `Subscription` object instead, and nothing has ever read that one.\n */\n listenCollection<M extends Record<string, any>>(\n // `collection` is re-resolved from the registry on every re-fetch, and\n // `vectorSearch` is not a thing a subscription can do — the Postgres\n // service refuses it outright rather than run it once and never again.\n { onUpdate, onError, collection, vectorSearch, ...query }: ListenCollectionProps<M>,\n authContext?: { uid: string; roles: string[] }\n ): () => void {\n const subscriptionId = this.generateSubscriptionId();\n\n const callback = (rows: Record<string, unknown>[]) => {\n try {\n onUpdate(rows);\n } catch (error) {\n logger.error(\"Error in collection update callback\", { error: error });\n if (onError) {\n onError(error instanceof Error ? error : new Error(String(error)));\n }\n }\n };\n\n // Forwarded whole rather than re-listed, for the reason `fetchCollection`\n // gives above: the hand-written list named seven of the eleven fields\n // `ListenCollectionProps` declares, so `logical` and `offset` were\n // accepted at every type-checked boundary and then dropped — an\n // `or(...)` subscription was pushed every row, and a subscription to\n // page two was pushed page one.\n this.realtimeService.subscribeToCollection(\n subscriptionId,\n { clientId: \"driver\", ...query, authContext },\n callback\n );\n\n // Return unsubscribe function\n return () => {\n this.realtimeService.unsubscribe(subscriptionId);\n };\n }\n\n /**\n * Fetch a single row\n */\n async fetchOne<M extends Record<string, any>>({\n path,\n id,\n databaseId,\n collection\n }: FetchOneProps<M>): Promise<Record<string, unknown> | undefined> {\n let row = await this.dataService.fetchOne<M>(path, id, databaseId);\n\n const { collection: resolvedCollection, callbacks, globalCallbacks, propertyCallbacks } = this.resolveCollectionCallbacks(collection, path);\n\n if (row && (globalCallbacks?.afterRead || callbacks?.afterRead || propertyCallbacks?.afterRead)) {\n const contextForCallback = {\n user: this.user,\n driver: this,\n data: this.data,\n client: this.client,\n storageSource: this.client?.storage\n } as unknown as RebaseCallContext; // Backend context\n let processedRow: Record<string, unknown> = row;\n if (globalCallbacks?.afterRead) {\n processedRow = await globalCallbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n row: processedRow,\n context: contextForCallback\n }) ?? processedRow;\n }\n if (callbacks?.afterRead) {\n processedRow = await callbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n row: processedRow,\n context: contextForCallback\n }) ?? processedRow;\n }\n if (propertyCallbacks?.afterRead) {\n processedRow = await propertyCallbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n row: processedRow,\n context: contextForCallback\n }) ?? processedRow;\n }\n row = processedRow;\n }\n\n return row;\n }\n\n /**\n * Listen to row changes\n */\n listenOne<M extends Record<string, any>>({\n path,\n id,\n collection,\n onUpdate,\n onError\n }: ListenOneProps<M>, authContext?: { uid: string; roles: string[] }): () => void {\n const subscriptionId = this.generateSubscriptionId();\n\n const callback = (row: Record<string, unknown> | null) => {\n try {\n onUpdate(row);\n } catch (error) {\n logger.error(\"Error in row update callback\", { error: error });\n if (onError) {\n onError(error instanceof Error ? error : new Error(String(error)));\n }\n }\n };\n\n this.realtimeService.subscribeToOne(\n subscriptionId,\n {\n clientId: \"driver\",\n path,\n id,\n authContext\n },\n callback\n );\n\n // Return unsubscribe function\n return () => {\n this.realtimeService.unsubscribe(subscriptionId);\n };\n }\n\n /**\n * Save an row (create or update)\n */\n async save<M extends Record<string, any>>({\n path,\n id,\n values,\n collection,\n status\n }: SaveProps<M>): Promise<Record<string, unknown>> {\n const { collection: resolvedCollection, callbacks, globalCallbacks, propertyCallbacks } = this.resolveCollectionCallbacks(collection, path);\n\n let updatedValues = values;\n const contextForCallback = {\n user: this.user,\n driver: this,\n data: this.data,\n client: this.client,\n storageSource: this.client?.storage\n } as unknown as RebaseCallContext;\n\n // Fetch previous values for callbacks AND history recording\n let previousValuesForHistory: Partial<M> | undefined;\n if (status === \"existing\" && id) {\n const existing = await this.dataService.fetchOne<M>(path, id, resolvedCollection?.databaseId);\n if (existing) {\n const { id: _existingId, ...existingValues } = existing;\n previousValuesForHistory = existingValues as Partial<M>;\n }\n }\n\n if (globalCallbacks?.beforeSave || callbacks?.beforeSave || propertyCallbacks?.beforeSave) {\n if (globalCallbacks?.beforeSave) {\n const result = await globalCallbacks.beforeSave({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n id,\n values: updatedValues,\n previousValues: previousValuesForHistory,\n status,\n context: contextForCallback\n });\n if (result) updatedValues = mergeDeep(updatedValues, result);\n }\n\n if (callbacks?.beforeSave) {\n const result = await callbacks.beforeSave({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n id,\n values: updatedValues,\n previousValues: previousValuesForHistory,\n status,\n context: contextForCallback\n });\n if (result) updatedValues = mergeDeep(updatedValues, result);\n }\n\n if (propertyCallbacks?.beforeSave) {\n const result = await propertyCallbacks.beforeSave({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n id,\n values: updatedValues,\n previousValues: previousValuesForHistory,\n status,\n context: contextForCallback\n });\n if (result) updatedValues = mergeDeep(updatedValues, result);\n }\n }\n\n // Apply autoValue timestamps (on_create / on_update) at the application layer.\n if (resolvedCollection?.properties) {\n updatedValues = updateDateAutoValues({\n inputValues: updatedValues,\n properties: resolvedCollection.properties,\n status: status ?? \"new\",\n timestampNowValue: new Date()\n });\n }\n\n try {\n let savedRow = await this.dataService.save<M>(\n path,\n updatedValues,\n id,\n resolvedCollection?.databaseId\n );\n\n if (savedRow && (globalCallbacks?.afterRead || callbacks?.afterRead || propertyCallbacks?.afterRead)) {\n if (globalCallbacks?.afterRead) {\n savedRow = await globalCallbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n row: savedRow,\n context: contextForCallback\n }) ?? savedRow;\n }\n if (callbacks?.afterRead) {\n savedRow = await callbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n row: savedRow,\n context: contextForCallback\n }) ?? savedRow;\n }\n if (propertyCallbacks?.afterRead) {\n savedRow = await propertyCallbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n row: savedRow,\n context: contextForCallback\n }) ?? savedRow;\n }\n }\n\n const savedId = savedRow.id as string | number;\n const { id: _savedId, ...savedValues } = savedRow;\n\n if (globalCallbacks?.afterSave || callbacks?.afterSave || propertyCallbacks?.afterSave) {\n if (globalCallbacks?.afterSave) {\n await globalCallbacks.afterSave({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n id: savedId,\n values: savedValues,\n previousValues: previousValuesForHistory,\n status,\n context: contextForCallback\n });\n }\n if (callbacks?.afterSave) {\n await callbacks.afterSave({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n id: savedId,\n values: savedValues as Partial<M>,\n previousValues: previousValuesForHistory,\n status,\n context: contextForCallback\n });\n }\n if (propertyCallbacks?.afterSave) {\n await propertyCallbacks.afterSave({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n id: savedId,\n values: savedValues,\n previousValues: previousValuesForHistory,\n status,\n context: contextForCallback\n });\n }\n }\n\n // Record row history (fire-and-forget, never blocks the save)\n if (this.historyService && resolvedCollection?.history) {\n this.historyService.recordHistory({\n tableName: path,\n id: savedId.toString(),\n action: status === \"new\" ? \"create\" : \"update\",\n values: savedValues as Record<string, unknown>,\n previousValues: previousValuesForHistory as Record<string, unknown> | undefined,\n updatedBy: this.user?.uid\n }).catch(err => {\n logger.error(`Failed to record history for ${path}/${savedId}`, { error: err });\n });\n }\n\n // Notify real-time subscribers\n await this.realtimeService.notifyUpdate(\n path,\n savedId.toString(),\n savedRow\n );\n\n return savedRow;\n } catch (error) {\n if (callbacks?.afterSaveError || propertyCallbacks?.afterSaveError) {\n if (callbacks?.afterSaveError) {\n await callbacks.afterSaveError({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n id: id || \"unknown\",\n values: updatedValues,\n previousValues: undefined,\n status,\n context: contextForCallback\n });\n }\n if (propertyCallbacks?.afterSaveError) {\n await propertyCallbacks.afterSaveError({\n collection: resolvedCollection as CollectionConfig<M>,\n path,\n id: id || \"unknown\",\n values: updatedValues,\n previousValues: undefined,\n status,\n context: contextForCallback\n });\n }\n }\n throw error;\n }\n }\n\n /**\n * Delete an row\n */\n async delete<M extends Record<string, any>>({\n row,\n collection\n }: DeleteProps<M>): Promise<void> {\n const { collection: resolvedCollection, callbacks, globalCallbacks, propertyCallbacks } = this.resolveCollectionCallbacks(collection, row.path);\n\n const callbackRow: Record<string, unknown> = { id: row.id, ...(row.values ?? {}) };\n\n const contextForCallback = {\n user: this.user,\n driver: this,\n data: this.data,\n client: this.client,\n storageSource: this.client?.storage\n } as unknown as RebaseCallContext;\n\n if (globalCallbacks?.beforeDelete || callbacks?.beforeDelete || propertyCallbacks?.beforeDelete) {\n let preventDefault = false;\n if (globalCallbacks?.beforeDelete) {\n const result = await globalCallbacks.beforeDelete({\n collection: resolvedCollection as CollectionConfig<M>,\n path: row.path,\n id: row.id,\n row: callbackRow,\n context: contextForCallback\n });\n if (result === false) {\n preventDefault = true;\n }\n }\n if (callbacks?.beforeDelete) {\n const result = await callbacks.beforeDelete({\n collection: resolvedCollection as CollectionConfig<M>,\n path: row.path,\n id: row.id,\n row: callbackRow,\n context: contextForCallback\n });\n if (result === false) {\n preventDefault = true;\n }\n }\n if (propertyCallbacks?.beforeDelete) {\n const result = await propertyCallbacks.beforeDelete({\n collection: resolvedCollection as CollectionConfig<M>,\n path: row.path,\n id: row.id,\n row: callbackRow,\n context: contextForCallback\n });\n if (result === false) {\n preventDefault = true;\n }\n }\n if (preventDefault) {\n return;\n }\n }\n\n await this.dataService.delete(row.path, row.id);\n\n if (globalCallbacks?.afterDelete || callbacks?.afterDelete || propertyCallbacks?.afterDelete) {\n if (globalCallbacks?.afterDelete) {\n await globalCallbacks.afterDelete({\n collection: resolvedCollection as CollectionConfig<M>,\n path: row.path,\n id: row.id,\n row: callbackRow,\n context: contextForCallback\n });\n }\n if (callbacks?.afterDelete) {\n await callbacks.afterDelete({\n collection: resolvedCollection as CollectionConfig<M>,\n path: row.path,\n id: row.id,\n row: callbackRow,\n context: contextForCallback\n });\n }\n if (propertyCallbacks?.afterDelete) {\n await propertyCallbacks.afterDelete({\n collection: resolvedCollection as CollectionConfig<M>,\n path: row.path,\n id: row.id,\n row: callbackRow,\n context: contextForCallback\n });\n }\n }\n\n // Record history\n if (this.historyService && resolvedCollection?.history) {\n this.historyService.recordHistory({\n action: \"delete\",\n id: String(row.id),\n tableName: row.path,\n previousValues: row.values,\n updatedBy: this.user?.uid\n }).catch(err => {\n logger.error(`Failed to record history for ${row.path}/${row.id}`, { error: err });\n });\n }\n\n // Notify subscribers of the deletion\n await this.realtimeService.notifyUpdate(row.path, String(row.id), null);\n }\n\n /**\n * Check if a field value is unique\n */\n async checkUniqueField(\n path: string,\n name: string,\n value: any,\n id?: string,\n collection?: CollectionConfig\n ): Promise<boolean> {\n return this.dataService.checkUniqueField(path, name, value, id);\n }\n\n /**\n * Generate a new row ID\n */\n generateId(path: string, collection?: CollectionConfig): string {\n return this.dataService.generateId();\n }\n\n /**\n * Count rows in a collection\n */\n async count<M extends Record<string, any>>({\n path,\n collection,\n filter,\n logical,\n searchString\n }: FetchCollectionProps<M>): Promise<number> {\n // The same narrowing the listing gets, or the total describes a\n // different query than the rows it is reported beside.\n return this.dataService.count<M>(path, {\n filter,\n logical,\n searchString,\n collection: collection as CollectionConfig\n });\n }\n\n /**\n * Generate a unique subscription ID\n */\n private generateSubscriptionId(): string {\n return `mongo_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n }\n\n /**\n * Check if the delegate is ready\n */\n isReady(): boolean {\n return this.initialised;\n }\n\n /**\n * Get the underlying row service for direct access\n */\n getDataService(): MongoDataService {\n return this.dataService;\n }\n\n /**\n * Get the underlying realtime service for direct access\n */\n getRealtimeService(): MongoRealtimeService {\n return this.realtimeService;\n }\n\n /**\n * Scope the MongoDriver with an authenticated user context\n */\n async withAuth(user: User): Promise<DataDriver> {\n return new AuthenticatedMongoDriver(this, user);\n }\n}\n\nexport class AuthenticatedMongoDriver implements DataDriver {\n key = \"mongodb\";\n initialised = true;\n public user: User;\n public data: RebaseSdkData;\n\n constructor(public delegate: MongoDriver, user: User) {\n this.user = user;\n this.data = buildSdkData(this);\n }\n\n currentTime(): Date {\n return this.delegate.currentTime();\n }\n\n async fetchCollection<M extends Record<string, any>>(props: FetchCollectionProps<M>): Promise<Record<string, unknown>[]> {\n const { collection: resolvedCollection } = this.delegate.resolveCollectionCallbacks(props.collection, props.path);\n const rlsFilter = buildMongoFilterFromSecurityRules(resolvedCollection, this.user, \"select\");\n if (rlsFilter === null) {\n return [];\n }\n\n // `logical` belongs in this query, not in the props spread below: the\n // repository reads `rawQuery ?? buildQuery(...)`, so a `logical` that\n // travelled only in the spread was never consulted — and a dropped\n // `or(...)` group does not fail, it widens.\n const userQuery = MongoConditionBuilder.buildQuery({\n filter: props.filter,\n logical: props.logical,\n searchString: props.searchString,\n properties: resolvedCollection?.properties\n });\n\n const combinedQuery = Object.keys(rlsFilter).length > 0\n ? ({ $and: [userQuery, rlsFilter] } as Filter<Document>)\n : userQuery;\n\n const originalService = this.delegate.getDataService();\n const rows = await originalService.fetchCollection<M>(props.path, {\n ...props,\n rawQuery: combinedQuery,\n collection: resolvedCollection\n });\n\n const { callbacks, globalCallbacks, propertyCallbacks } = this.delegate.resolveCollectionCallbacks(props.collection, props.path);\n\n if (globalCallbacks?.afterRead || callbacks?.afterRead || propertyCallbacks?.afterRead) {\n const contextForCallback = {\n user: this.user,\n driver: this,\n data: this.data,\n client: this.delegate.client,\n storageSource: this.delegate.client?.storage\n } as unknown as RebaseCallContext;\n return Promise.all(rows.map(async (row) => {\n let fetched = row;\n if (globalCallbacks?.afterRead) {\n fetched = await globalCallbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path: props.path,\n row: fetched,\n context: contextForCallback\n }) ?? fetched;\n }\n if (callbacks?.afterRead) {\n fetched = await callbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path: props.path,\n row: fetched,\n context: contextForCallback\n }) ?? fetched;\n }\n if (propertyCallbacks?.afterRead) {\n fetched = await propertyCallbacks.afterRead({\n collection: resolvedCollection as CollectionConfig<M>,\n path: props.path,\n row: fetched,\n context: contextForCallback\n }) ?? fetched;\n }\n return fetched;\n }));\n }\n\n return rows;\n }\n\n listenCollection<M extends Record<string, any>>(props: ListenCollectionProps<M>): () => void {\n // Handed to the subscription rather than stamped on it afterwards: the\n // config is what every re-fetch reads, and the stamp also landed after\n // the initial fetch had already been dispatched unfiltered.\n return this.delegate.listenCollection(props, this.authContext());\n }\n\n /** The acting user, in the shape the realtime subscriptions carry. */\n private authContext(): { uid: string; roles: string[] } {\n return { uid: this.user.uid,\nroles: this.user.roles ?? [] };\n }\n\n /**\n * Evaluate the collection's rules for one row, fail-closed.\n *\n * A path with no resolvable collection has no declared rules — the same\n * answer `buildMongoFilterFromSecurityRules` gives a listing on such a path,\n * so the two never disagree about whether this engine has row security.\n */\n private authorize(\n collection: CollectionConfig | undefined,\n entity: Entity,\n operation: SecurityOperation,\n clauses?: PolicyClauses\n ): boolean {\n if (!collection) return true;\n return checkOperation(collection, { user: this.user }, entity, operation, { onUnknown: \"deny\",\nclauses });\n }\n\n async fetchOne<M extends Record<string, any>>(props: FetchOneProps<M>): Promise<Record<string, unknown> | undefined> {\n const { collection: resolvedCollection } = this.delegate.resolveCollectionCallbacks(props.collection, props.path);\n assertSecurityRulesEnforceable(resolvedCollection, \"select\");\n const row = await this.delegate.fetchOne(props);\n if (row && !this.authorize(resolvedCollection, rowToEntityForCheck(row, props.path), \"select\")) {\n return undefined;\n }\n return row;\n }\n\n listenOne<M extends Record<string, any>>(props: ListenOneProps<M>): () => void {\n return this.delegate.listenOne(props, this.authContext());\n }\n\n /**\n * Save, with both halves of the rule checked *before* the write.\n *\n * There is no transaction here, so a check that runs after\n * `delegate.save` cannot undo anything: the document is written, history is\n * recorded and subscribers have been notified by then, and a 403 at that\n * point only misleads the caller about what happened. Postgres evaluates\n * `WITH CHECK` inside the transaction; the closest this driver can get is to\n * evaluate it against the row as it *will* be, and refuse before writing.\n */\n async save<M extends Record<string, any>>(props: SaveProps<M>): Promise<Record<string, unknown>> {\n const { collection: resolvedCollection } = this.delegate.resolveCollectionCallbacks(props.collection, props.path);\n\n if (props.status === \"existing\" && props.id) {\n assertSecurityRulesEnforceable(resolvedCollection, \"update\");\n const existing = await this.delegate.fetchOne({ path: props.path,\nid: props.id,\ncollection: resolvedCollection });\n // USING against the stored row, WITH CHECK against the row that\n // will replace it — the split Postgres makes, and the reason\n // `clauses` exists on `checkOperation`.\n const projected = rowToEntityForCheck({ ...existing,\n...props.values,\nid: props.id }, props.path);\n if (!existing ||\n !this.authorize(resolvedCollection, rowToEntityForCheck(existing, props.path), \"update\", \"using\") ||\n !this.authorize(resolvedCollection, projected, \"update\", \"withCheck\")) {\n throw ApiError.forbidden(\"Forbidden\");\n }\n } else {\n assertSecurityRulesEnforceable(resolvedCollection, \"insert\");\n const tempEntity = { id: props.id || \"new\",\npath: props.path,\nvalues: props.values } as Entity;\n if (!this.authorize(resolvedCollection, tempEntity, \"insert\")) {\n throw ApiError.forbidden(\"Forbidden\");\n }\n }\n\n return this.delegate.save({\n ...props,\n collection: resolvedCollection\n });\n }\n\n async delete<M extends Record<string, any>>(props: DeleteProps<M>): Promise<void> {\n const { collection: resolvedCollection } = this.delegate.resolveCollectionCallbacks(props.collection, props.row.path);\n assertSecurityRulesEnforceable(resolvedCollection, \"delete\");\n\n const existing = await this.delegate.fetchOne({ path: props.row.path,\nid: props.row.id,\ncollection: resolvedCollection });\n if (!existing || !this.authorize(resolvedCollection, rowToEntityForCheck(existing, props.row.path), \"delete\")) {\n throw ApiError.forbidden(\"Forbidden\");\n }\n\n return this.delegate.delete(props);\n }\n\n async checkUniqueField(\n path: string,\n name: string,\n value: any,\n id?: string,\n collection?: CollectionConfig\n ): Promise<boolean> {\n return this.delegate.checkUniqueField(path, name, value, id, collection);\n }\n\n generateId(path: string, collection?: CollectionConfig): string {\n return this.delegate.generateId(path, collection);\n }\n\n async count<M extends Record<string, any>>(props: FetchCollectionProps<M>): Promise<number> {\n const { collection: resolvedCollection } = this.delegate.resolveCollectionCallbacks(props.collection, props.path);\n const rlsFilter = buildMongoFilterFromSecurityRules(resolvedCollection, this.user, \"select\");\n if (rlsFilter === null) {\n return 0;\n }\n\n // Narrowed by exactly what the listing is narrowed by — `logical`\n // included — or the total describes a different query than the rows.\n const userQuery = MongoConditionBuilder.buildQuery({\n filter: props.filter,\n logical: props.logical,\n searchString: props.searchString,\n properties: resolvedCollection?.properties\n });\n\n const combinedQuery = Object.keys(rlsFilter).length > 0\n ? ({ $and: [userQuery, rlsFilter] } as Filter<Document>)\n : userQuery;\n\n const originalService = this.delegate.getDataService();\n return originalService.count(props.path, {\n ...props,\n rawQuery: combinedQuery\n });\n }\n\n isReady(): boolean {\n return this.delegate.isReady();\n }\n}\n\n/**\n * Wrap a flat row into the Entity shape expected by `checkOperation`,\n * which evaluates security rules against `row.values`.\n */\nfunction rowToEntityForCheck(row: Record<string, unknown>, path: string): Entity {\n return {\n id: row.id as string | number,\n path,\n values: row\n };\n}\n","/**\n * MongoDB Backend Factory\n *\n * This module provides factory functions for creating MongoDB backend instances.\n * It abstracts the creation of drivers, realtime services, and row services.\n */\n\nimport { Db, MongoClient } from \"mongodb\";\nimport { DataDriver, CollectionConfig, getCollectionDataPath } from \"@rebasepro/types\";\n\nimport { MongoDataService } from \"./db/MongoDataService\";\nimport { MongoRealtimeService } from \"./services/MongoRealtimeService\";\nimport { MongoDriver } from \"./services/MongoDriver\";\nimport { MongoHistoryService, HistoryRetentionConfig } from \"./services/MongoHistoryService\";\nimport { MongoDBConnection } from \"./connection\";\nimport { BackendConfig, BackendInstance, CollectionRegistryInterface, DataRepository, RealtimeProvider, DatabaseConnection, DatabaseAdmin, DocumentAdmin, SchemaAdmin, HealthCheckResult } from \"@rebasepro/types\";\n\n/**\n * Configuration for creating a MongoDB backend.\n */\nexport interface MongoBackendConfig extends BackendConfig {\n type: \"mongodb\";\n /** MongoDB database instance */\n connection: Db;\n /** MongoDB client (for connection management) */\n client: MongoClient;\n /** Collections to register (optional, can be registered later) */\n collections?: CollectionConfig[];\n /** History retention configuration */\n historyRetention?: Partial<HistoryRetentionConfig>;\n}\n\n/**\n * MongoDB-specific backend instance with additional MongoDB types.\n */\nexport interface MongoBackendInstance extends BackendInstance {\n /** The MongoDB database instance */\n db: Db;\n /** The MongoDB client */\n client: MongoClient;\n /** MongoDB DataDriver for use with Rebase */\n driver: DataDriver;\n /** Entity service for direct database operations */\n dataService: MongoDataService;\n /** Realtime service for subscriptions */\n realtimeService: MongoRealtimeService;\n /** Admin capabilities (DocumentAdmin + SchemaAdmin) */\n admin: DatabaseAdmin;\n}\n\n// =============================================================================\n// Simple Collection Registry\n// =============================================================================\n\n/**\n * Simple in-memory collection registry for MongoDB.\n */\nexport class MongoCollectionRegistry implements CollectionRegistryInterface {\n /** Every addressable key → collection. See {@link register}. */\n private collections = new Map<string, CollectionConfig>();\n /** Registration order, so `getCollections()` returns each collection once. */\n private registered: CollectionConfig[] = [];\n private _globalCallbacks?: any;\n\n /**\n * Register a collection under every name it can be addressed by.\n *\n * A Mongo collection has up to three: `slug` (the routing key), `path` (the\n * MongoDB collection-name override, which is what `getCollectionDataPath`\n * hands the driver) and `name` (the human label). Registering only `name`\n * meant every `getCollectionByPath` lookup missed — and the realtime path,\n * whose only source of the collection is this registry, ran with no\n * `securityRules`, no `properties` and no callbacks.\n */\n register(collection: CollectionConfig): void {\n this.registered.push(collection);\n for (const key of [getCollectionDataPath(collection), collection.slug, collection.name]) {\n if (key) this.collections.set(key, collection);\n }\n }\n\n /**\n * Get a collection by its path\n */\n getCollectionByPath(path: string): CollectionConfig | undefined {\n return this.collections.get(path);\n }\n\n /**\n * Get all registered collections\n */\n getCollections(): CollectionConfig[] {\n return [...this.registered];\n }\n\n /**\n * Get the currently registered global callbacks, if any.\n */\n getGlobalCallbacks(): any | undefined {\n return this._globalCallbacks;\n }\n\n /**\n * Set global lifecycle callbacks that apply to every collection.\n */\n setGlobalCallbacks(callbacks: any): void {\n this._globalCallbacks = callbacks;\n }\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\n/**\n * Create a complete MongoDB backend instance.\n *\n * This factory function creates all the necessary services for a MongoDB backend:\n * - MongoDBConnection (database connection wrapper)\n * - MongoDataService (implements DataRepository)\n * - MongoRealtimeService (implements RealtimeProvider)\n * - MongoCollectionRegistry (implements CollectionRegistryInterface)\n * - MongoDriver (for Rebase integration)\n *\n * @example\n * ```typescript\n * import { createMongoBackend } from \"@rebasepro/server-mongo\";\n *\n * const client = new MongoClient(\"mongodb://localhost:27017\");\n * await client.connect();\n * const db = client.db(\"my_database\");\n *\n * const backend = createMongoBackend({\n * type: \"mongodb\",\n * connection: db,\n * client: client,\n * collections: myCollections\n * });\n *\n * // Use the backend\n * const rows = await backend.entityRepository.fetchCollection(\"users\", {});\n * ```\n */\nexport function createMongoBackend(config: MongoBackendConfig): MongoBackendInstance {\n const { connection: db, client, collections } = config;\n\n // Create collection registry\n const collectionRegistry = new MongoCollectionRegistry();\n\n // Register collections if provided\n if (collections) {\n collections.forEach(collection => collectionRegistry.register(collection));\n }\n\n // Create services\n const dataService = new MongoDataService(db);\n const realtimeService = new MongoRealtimeService(db);\n const historyService = new MongoHistoryService(db, config.historyRetention);\n const driver = new MongoDriver(db, realtimeService, historyService, collectionRegistry);\n const mongoConnection = new MongoDBConnection(db, client);\n\n // Build admin capabilities for MongoDB\n const admin: DatabaseAdmin = {\n async executeAggregate(pipeline: Record<string, unknown>[]) {\n // Run aggregation on a collection — requires a target collection\n // from the pipeline's $match or $lookup stage:\n const firstStage = pipeline[0];\n const collName = typeof firstStage.$from === \"string\" ? firstStage.$from : \"__admin__\";\n const cursor = db.collection(collName).aggregate(pipeline);\n return await cursor.toArray() as Record<string, unknown>[];\n },\n async fetchCollectionStats(collectionName: string) {\n const stats = await db.command({ collStats: collectionName }) as { count: number; size: number };\n return { count: stats.count,\nsizeBytes: stats.size };\n },\n async fetchUnmappedTables(mappedPaths?: string[]) {\n const allCollections = await db.listCollections().toArray();\n const names = allCollections.map(c => c.name).filter(n => !n.startsWith(\"system.\"));\n if (!mappedPaths || mappedPaths.length === 0) return names;\n const mappedSet = new Set(mappedPaths.map(p => p.toLowerCase()));\n return names.filter(n => !mappedSet.has(n.toLowerCase()));\n },\n async fetchTableMetadata(collectionName: string) {\n // Sample a document to infer fields\n const sample = await db.collection(collectionName).findOne();\n if (!sample) return { columns: [],\nforeignKeys: [],\njunctions: [],\npolicies: [] };\n const columns = Object.entries(sample).map(([key, value]) => ({\n column_name: key,\n data_type: typeof value,\n udt_name: typeof value,\n is_nullable: \"YES\",\n column_default: null,\n character_maximum_length: null\n }));\n return { columns,\nforeignKeys: [],\njunctions: [],\npolicies: [] };\n }\n } satisfies DocumentAdmin & SchemaAdmin;\n\n return {\n // Abstract interface implementations\n connection: mongoConnection,\n entityRepository: dataService,\n realtimeProvider: realtimeService,\n collectionRegistry: collectionRegistry,\n admin,\n\n // Lifecycle\n async initialize() {\n // Connection is already established via the MongoClient constructor\n },\n async healthCheck(): Promise<HealthCheckResult> {\n const start = Date.now();\n try {\n await db.command({ ping: 1 });\n return { healthy: true,\nlatencyMs: Date.now() - start };\n } catch {\n return { healthy: false,\nlatencyMs: Date.now() - start };\n }\n },\n async destroy() {\n await client.close();\n },\n\n // MongoDB-specific accessors\n db,\n client,\n driver,\n dataService,\n realtimeService\n };\n}\n\n/**\n * Create a MongoDB DataDriver.\n *\n * This is a convenience function when you only need the DataDriver\n * without the full backend instance.\n *\n * @example\n * ```typescript\n * import { createMongoDelegate } from \"@rebasepro/server-mongo\";\n *\n * const delegate = createMongoDelegate(db);\n * ```\n */\nexport function createMongoDelegate(\n db: Db,\n realtimeService?: MongoRealtimeService,\n historyService?: MongoHistoryService,\n registry?: CollectionRegistryInterface\n): MongoDriver {\n const realtime = realtimeService ?? new MongoRealtimeService(db);\n const history = historyService ?? new MongoHistoryService(db);\n return new MongoDriver(db, realtime, history, registry);\n}\n\n/**\n * Create a RealtimeService for MongoDB.\n *\n * @example\n * ```typescript\n * import { createMongoRealtimeService } from \"@rebasepro/server-mongo\";\n *\n * const realtimeService = createMongoRealtimeService(db);\n * ```\n */\nexport function createMongoRealtimeService(db: Db): MongoRealtimeService {\n return new MongoRealtimeService(db);\n}\n\n/**\n * Create a MongoDB row repository.\n *\n * @example\n * ```typescript\n * import { createMongoEntityRepository } from \"@rebasepro/server-mongo\";\n *\n * const repository = createMongoEntityRepository(db);\n * const users = await repository.fetchCollection(\"users\", {});\n * ```\n */\nexport function createMongoEntityRepository(db: Db): DataRepository {\n return new MongoDataService(db);\n}\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\n/**\n * Check if a backend config is for MongoDB.\n */\nexport function isMongoBackendConfig(config: BackendConfig): config is MongoBackendConfig {\n return config.type === \"mongodb\" &&\n typeof (config as MongoBackendConfig).connection !== \"undefined\" &&\n typeof (config as MongoBackendConfig).client !== \"undefined\";\n}\n\n/**\n * Check if a driver config is for MongoDB.\n */\nexport function isMongoDriverConfig(obj: unknown): obj is { type: \"mongodb\"; connection: Db; client: MongoClient } {\n return typeof obj === \"object\" &&\n obj !== null &&\n \"type\" in obj &&\n (obj as Record<string, unknown>).type === \"mongodb\" &&\n \"connection\" in obj &&\n \"client\" in obj;\n}\n","import { Db, ObjectId } from \"mongodb\";\nimport { normalizeEmail } from \"@rebasepro/common\";\n\n/** Loose document type that allows string _id values (Rebase convention). */\nexport interface MongoDoc { _id?: string; [key: string]: any; }\nimport {\n UserRepository,\n RoleRepository,\n TokenRepository,\n AuthRepository,\n UserData,\n CreateUserData,\n RoleData,\n CreateRoleData,\n RefreshTokenInfo,\n RefreshTokenSession,\n PasswordResetTokenInfo,\n MagicLinkTokenInfo,\n UserIdentityData,\n ListUsersOptions,\n PaginatedUsersResult,\n MfaFactor,\n MfaChallengeInfo,\n ApiError\n} from \"@rebasepro/server\";\n\nexport type Role = RoleData;\n\nfunction escapeRegExp(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction toUser(doc: any): UserData {\n return {\n id: doc._id || doc.id,\n email: doc.email,\n passwordHash: doc.passwordHash ?? null,\n displayName: doc.displayName ?? null,\n photoUrl: doc.photoUrl ?? null,\n emailVerified: doc.emailVerified ?? false,\n emailVerificationToken: doc.emailVerificationToken ?? null,\n emailVerificationSentAt: doc.emailVerificationSentAt ? new Date(doc.emailVerificationSentAt) : null,\n createdAt: new Date(doc.createdAt),\n updatedAt: new Date(doc.updatedAt)\n };\n}\n\nexport class MongoUserService implements UserRepository {\n constructor(private db: Db) {}\n\n private get collection() {\n return this.db.collection<MongoDoc>(\"rebase_users\");\n }\n\n private get identitiesCollection() {\n return this.db.collection<MongoDoc>(\"rebase_user_identities\");\n }\n\n private get userRolesCollection() {\n return this.db.collection<MongoDoc>(\"rebase_user_roles\");\n }\n\n private get rolesCollection() {\n return this.db.collection<MongoDoc>(\"rebase_roles\");\n }\n\n async createUser(data: CreateUserData): Promise<UserData> {\n const id = new ObjectId().toString();\n const now = new Date();\n const doc = {\n _id: id,\n id,\n email: normalizeEmail(data.email),\n passwordHash: data.passwordHash ?? null,\n displayName: data.displayName ?? null,\n photoUrl: data.photoUrl ?? null,\n emailVerified: data.emailVerified ?? false,\n createdAt: now,\n updatedAt: now\n };\n try {\n await this.collection.insertOne(doc);\n } catch (error) {\n // 11000 is Mongo's duplicate key, and the unique index on `email`\n // in `ensure-collections.ts` is what raises it. Same answer as\n // Postgres gives for its 23505, and the same answer the route\n // gives when its pre-check sees the row — see\n // `UserRepository.createUser`.\n if ((error as { code?: number })?.code === 11000) {\n throw ApiError.conflict(\"Email already registered\", \"EMAIL_EXISTS\");\n }\n throw error;\n }\n return toUser(doc);\n }\n\n async getUserById(id: string): Promise<UserData | null> {\n const doc = await this.collection.findOne({ id });\n return doc ? toUser(doc) : null;\n }\n\n async getUserByEmail(email: string): Promise<UserData | null> {\n const doc = await this.collection.findOne({ email: normalizeEmail(email) });\n return doc ? toUser(doc) : null;\n }\n\n async getUserByIdentity(provider: string, providerId: string): Promise<UserData | null> {\n const identity = await this.identitiesCollection.findOne({ provider,\nproviderId });\n if (!identity) return null;\n return this.getUserById(identity.uid);\n }\n\n async getUserIdentities(uid: string): Promise<UserIdentityData[]> {\n const docs = await this.identitiesCollection.find({ uid }).toArray();\n return docs.map(doc => ({\n id: doc.id,\n uid: doc.uid,\n provider: doc.provider,\n providerId: doc.providerId,\n profileData: doc.profileData ?? null,\n createdAt: new Date(doc.createdAt),\n updatedAt: new Date(doc.updatedAt)\n }));\n }\n\n async linkUserIdentity(uid: string, provider: string, providerId: string, profileData?: Record<string, unknown>): Promise<void> {\n const now = new Date();\n await this.identitiesCollection.updateOne(\n { provider,\nproviderId },\n {\n $setOnInsert: {\n _id: new ObjectId().toString(),\n id: new ObjectId().toString(),\n uid,\n provider,\n providerId,\n createdAt: now\n },\n $set: {\n profileData: profileData ?? null,\n updatedAt: now\n }\n },\n { upsert: true }\n );\n }\n\n async updateUser(id: string, data: Partial<Omit<CreateUserData, \"id\">>): Promise<UserData | null> {\n const updateData: Record<string, unknown> = { ...data,\nupdatedAt: new Date() };\n if (typeof updateData.email === \"string\") updateData.email = normalizeEmail(updateData.email);\n\n await this.collection.updateOne({ id }, { $set: updateData });\n return this.getUserById(id);\n }\n\n async deleteUser(id: string): Promise<void> {\n await this.collection.deleteOne({ id });\n await this.identitiesCollection.deleteMany({ uid: id });\n await this.userRolesCollection.deleteMany({ uid: id });\n }\n\n async listUsers(): Promise<UserData[]> {\n const docs = await this.collection.find().toArray();\n return docs.map(toUser);\n }\n\n async listUsersPaginated(options?: ListUsersOptions): Promise<PaginatedUsersResult> {\n const limit = options?.limit ?? 25;\n const offset = options?.offset ?? 0;\n const search = options?.search?.trim() || \"\";\n const orderBy = options?.orderBy || \"createdAt\";\n const orderDir = options?.orderDir || \"desc\";\n const roleId = options?.roleId;\n\n const query: Record<string, unknown> = {};\n\n if (search) {\n const escapedSearch = escapeRegExp(search);\n query.$or = [\n { email: { $regex: escapedSearch,\n$options: \"i\" } },\n { displayName: { $regex: escapedSearch,\n$options: \"i\" } }\n ];\n }\n\n if (roleId) {\n const userRoles = await this.userRolesCollection.find({ roleId }).toArray();\n const userIds = userRoles.map(ur => ur.uid);\n query.id = { $in: userIds };\n }\n\n const sort: Record<string, 1 | -1> = {};\n sort[orderBy] = orderDir === \"asc\" ? 1 : -1;\n\n const total = await this.collection.countDocuments(query);\n const docs = await this.collection.find(query).sort(sort).skip(offset).limit(limit).toArray();\n\n return {\n users: docs.map(toUser),\n total,\n limit,\n offset\n };\n }\n\n async updatePassword(id: string, passwordHash: string): Promise<void> {\n await this.collection.updateOne(\n { id },\n { $set: { passwordHash,\nupdatedAt: new Date() } }\n );\n }\n\n async setEmailVerified(id: string, verified: boolean): Promise<void> {\n await this.collection.updateOne(\n { id },\n { $set: { emailVerified: verified,\nemailVerificationToken: null,\nupdatedAt: new Date() } }\n );\n }\n\n async setVerificationToken(id: string, token: string | null): Promise<void> {\n await this.collection.updateOne(\n { id },\n { $set: { emailVerificationToken: token,\nemailVerificationSentAt: token ? new Date() : null,\nupdatedAt: new Date() } }\n );\n }\n\n async getUserByVerificationToken(token: string): Promise<UserData | null> {\n const doc = await this.collection.findOne({ emailVerificationToken: token });\n return doc ? toUser(doc) : null;\n }\n\n async getUserRoles(uid: string): Promise<RoleData[]> {\n const userRoles = await this.userRolesCollection.find({ uid }).toArray();\n const roleIds = userRoles.map(ur => ur.roleId);\n if (roleIds.length === 0) return [];\n\n const roles = await this.rolesCollection.find({ id: { $in: roleIds } }).toArray();\n return roles.map(r => ({\n id: r.id,\n name: r.name,\n isAdmin: r.isAdmin ?? false,\n defaultPermissions: r.defaultPermissions ?? null,\n collectionPermissions: r.collectionPermissions ?? null\n }));\n }\n\n async getUserRoleIds(uid: string): Promise<string[]> {\n const userRoles = await this.userRolesCollection.find({ uid }).toArray();\n return userRoles.map(ur => ur.roleId);\n }\n\n async setUserRoles(uid: string, roleIds: string[]): Promise<void> {\n await this.userRolesCollection.deleteMany({ uid });\n if (roleIds.length > 0) {\n const docs = roleIds.map(roleId => ({\n _id: new ObjectId().toString(),\n uid,\n roleId\n }));\n await this.userRolesCollection.insertMany(docs);\n }\n }\n\n async assignDefaultRole(uid: string, roleId: string): Promise<void> {\n await this.userRolesCollection.updateOne(\n { uid,\nroleId },\n { $setOnInsert: { _id: new ObjectId().toString(),\nuid,\nroleId } },\n { upsert: true }\n );\n }\n\n async getUserWithRoles(uid: string): Promise<{ user: UserData; roles: RoleData[] } | null> {\n const user = await this.getUserById(uid);\n if (!user) return null;\n const roles = await this.getUserRoles(uid);\n return { user,\nroles };\n }\n}\n\nexport class MongoRoleService implements RoleRepository {\n constructor(private db: Db) {}\n\n private get collection() {\n return this.db.collection<MongoDoc>(\"rebase_roles\");\n }\n\n async getRoleById(id: string): Promise<RoleData | null> {\n const doc = await this.collection.findOne({ id });\n if (!doc) return null;\n return {\n id: doc.id,\n name: doc.name,\n isAdmin: doc.isAdmin ?? false,\n defaultPermissions: doc.defaultPermissions ?? null,\n collectionPermissions: doc.collectionPermissions ?? null\n };\n }\n\n async listRoles(): Promise<RoleData[]> {\n const docs = await this.collection.find().sort({ name: 1 }).toArray();\n return docs.map(doc => ({\n id: doc.id,\n name: doc.name,\n isAdmin: doc.isAdmin ?? false,\n defaultPermissions: doc.defaultPermissions ?? null,\n collectionPermissions: doc.collectionPermissions ?? null\n }));\n }\n\n async createRole(data: CreateRoleData): Promise<RoleData> {\n const doc = {\n _id: data.id,\n id: data.id,\n name: data.name,\n isAdmin: data.isAdmin ?? false,\n defaultPermissions: data.defaultPermissions ?? null,\n collectionPermissions: data.collectionPermissions ?? null\n };\n await this.collection.insertOne(doc);\n return { ...doc } as RoleData;\n }\n\n async updateRole(id: string, data: Partial<Omit<RoleData, \"id\">>): Promise<RoleData | null> {\n await this.collection.updateOne({ id }, { $set: data });\n return this.getRoleById(id);\n }\n\n async deleteRole(id: string): Promise<void> {\n await this.collection.deleteOne({ id });\n await this.db.collection(\"rebase_user_roles\").deleteMany({ roleId: id });\n }\n}\n\nexport class MongoRefreshTokenService {\n constructor(private db: Db) {}\n\n private get collection() {\n return this.db.collection<MongoDoc>(\"rebase_refresh_tokens\");\n }\n\n private toInfo(doc: MongoDoc): RefreshTokenInfo {\n return {\n id: doc.id,\n uid: doc.uid,\n tokenHash: doc.tokenHash,\n expiresAt: new Date(doc.expiresAt),\n createdAt: new Date(doc.createdAt),\n userAgent: doc.userAgent,\n ipAddress: doc.ipAddress,\n sessionId: doc.sessionId,\n rotatedAt: doc.rotatedAt ? new Date(doc.rotatedAt) : null,\n revoked: Boolean(doc.revoked),\n sessionStartedAt: new Date(doc.sessionStartedAt || doc.createdAt)\n };\n }\n\n async createToken(\n uid: string,\n tokenHash: string,\n expiresAt: Date,\n userAgent?: string,\n ipAddress?: string,\n session?: RefreshTokenSession\n ): Promise<void> {\n const safeUserAgent = userAgent || \"\";\n const safeIpAddress = ipAddress || \"\";\n\n // No deleteMany first. Tokens of one sign-in accumulate under a shared\n // sessionId and are pruned once nobody can still be holding them —\n // evicting by (uid, userAgent, ipAddress) is what used to sign out a\n // second browser profile behind the same address.\n const now = new Date();\n await this.collection.insertOne({\n _id: new ObjectId().toString(),\n id: new ObjectId().toString(),\n uid,\n tokenHash,\n expiresAt,\n createdAt: now,\n userAgent: safeUserAgent,\n ipAddress: safeIpAddress,\n sessionId: session?.id ?? new ObjectId().toString(),\n sessionStartedAt: session?.startedAt ?? now,\n rotatedAt: null,\n revoked: false\n });\n }\n\n async findByHash(tokenHash: string): Promise<RefreshTokenInfo | null> {\n const doc = await this.collection.findOne({ tokenHash });\n return doc ? this.toInfo(doc) : null;\n }\n\n /** Superseded, not gone — see the Postgres service for why that matters. */\n async markRotated(tokenHash: string): Promise<void> {\n await this.collection.updateOne({ tokenHash }, { $set: { rotatedAt: new Date() } });\n }\n\n async revokeSession(sessionId: string): Promise<void> {\n await this.collection.updateMany(\n { sessionId },\n { $set: { revoked: true, rotatedAt: new Date() } }\n );\n }\n\n async prune(uid: string, sessionId: string, supersededBefore: Date): Promise<void> {\n await this.collection.deleteMany({\n uid,\n $or: [\n { expiresAt: { $lt: new Date() } },\n { sessionId, rotatedAt: { $ne: null, $lt: supersededBefore } }\n ]\n });\n }\n\n async getTokensValidAfter(uid: string): Promise<Date | null> {\n const user = await this.db.collection<MongoDoc>(\"rebase_users\").findOne({ id: uid });\n return user?.tokensValidAfter ? new Date(user.tokensValidAfter) : null;\n }\n\n async setTokensValidAfter(uid: string, at: Date): Promise<void> {\n await this.db.collection<MongoDoc>(\"rebase_users\")\n .updateOne({ id: uid }, { $set: { tokensValidAfter: at } });\n }\n\n async deleteByHash(tokenHash: string): Promise<void> {\n await this.collection.deleteOne({ tokenHash });\n }\n\n async deleteAllForUser(uid: string): Promise<void> {\n await this.collection.deleteMany({ uid });\n }\n\n async listForUser(uid: string): Promise<RefreshTokenInfo[]> {\n const docs = await this.collection.find({ uid }).sort({ createdAt: 1 }).toArray();\n return docs.map(doc => this.toInfo(doc));\n }\n\n async deleteById(id: string, uid: string): Promise<void> {\n await this.collection.deleteOne({ id,\nuid });\n }\n}\n\nexport class MongoPasswordResetTokenService {\n constructor(private db: Db) {}\n\n private get collection() {\n return this.db.collection<MongoDoc>(\"rebase_password_reset_tokens\");\n }\n\n async createToken(uid: string, tokenHash: string, expiresAt: Date): Promise<void> {\n await this.collection.deleteMany({ uid,\nusedAt: null });\n\n await this.collection.insertOne({\n _id: new ObjectId().toString(),\n uid,\n tokenHash,\n expiresAt,\n usedAt: null\n });\n }\n\n async findValidByHash(tokenHash: string): Promise<{ uid: string; expiresAt: Date } | null> {\n const doc = await this.collection.findOne({\n tokenHash,\n usedAt: null,\n expiresAt: { $gt: new Date() }\n });\n\n if (!doc) return null;\n\n return {\n uid: doc.uid,\n expiresAt: new Date(doc.expiresAt)\n };\n }\n\n async markAsUsed(tokenHash: string): Promise<void> {\n await this.collection.updateOne(\n { tokenHash },\n { $set: { usedAt: new Date() } }\n );\n }\n\n async deleteAllForUser(uid: string): Promise<void> {\n await this.collection.deleteMany({ uid });\n }\n\n async deleteExpired(): Promise<void> {\n await this.collection.deleteMany({ expiresAt: { $lt: new Date() } });\n }\n}\n\nexport class MongoTokenRepository implements TokenRepository {\n private refreshTokenService: MongoRefreshTokenService;\n private passwordResetTokenService: MongoPasswordResetTokenService;\n\n constructor(private db: Db) {\n this.refreshTokenService = new MongoRefreshTokenService(db);\n this.passwordResetTokenService = new MongoPasswordResetTokenService(db);\n }\n\n async createRefreshToken(uid: string, tokenHash: string, expiresAt: Date, userAgent?: string, ipAddress?: string, session?: RefreshTokenSession): Promise<void> {\n await this.refreshTokenService.createToken(uid, tokenHash, expiresAt, userAgent, ipAddress, session);\n }\n\n async markRefreshTokenRotated(tokenHash: string): Promise<void> {\n await this.refreshTokenService.markRotated(tokenHash);\n }\n\n async revokeRefreshTokenSession(sessionId: string): Promise<void> {\n await this.refreshTokenService.revokeSession(sessionId);\n }\n\n async pruneRefreshTokens(uid: string, sessionId: string, supersededBefore: Date): Promise<void> {\n await this.refreshTokenService.prune(uid, sessionId, supersededBefore);\n }\n\n async getTokensValidAfter(uid: string): Promise<Date | null> {\n return this.refreshTokenService.getTokensValidAfter(uid);\n }\n\n async setTokensValidAfter(uid: string, at: Date): Promise<void> {\n await this.refreshTokenService.setTokensValidAfter(uid, at);\n }\n\n async findRefreshTokenByHash(tokenHash: string): Promise<RefreshTokenInfo | null> {\n return this.refreshTokenService.findByHash(tokenHash);\n }\n\n async deleteRefreshToken(tokenHash: string): Promise<void> {\n await this.refreshTokenService.deleteByHash(tokenHash);\n }\n\n async deleteAllRefreshTokensForUser(uid: string): Promise<void> {\n await this.refreshTokenService.deleteAllForUser(uid);\n }\n\n async listRefreshTokensForUser(uid: string): Promise<RefreshTokenInfo[]> {\n return this.refreshTokenService.listForUser(uid);\n }\n\n async deleteRefreshTokenById(id: string, uid: string): Promise<void> {\n await this.refreshTokenService.deleteById(id, uid);\n }\n\n async createPasswordResetToken(uid: string, tokenHash: string, expiresAt: Date): Promise<void> {\n await this.passwordResetTokenService.createToken(uid, tokenHash, expiresAt);\n }\n\n async findValidPasswordResetToken(tokenHash: string): Promise<PasswordResetTokenInfo | null> {\n return this.passwordResetTokenService.findValidByHash(tokenHash);\n }\n\n async markPasswordResetTokenUsed(tokenHash: string): Promise<void> {\n await this.passwordResetTokenService.markAsUsed(tokenHash);\n }\n\n async deleteAllPasswordResetTokensForUser(uid: string): Promise<void> {\n await this.passwordResetTokenService.deleteAllForUser(uid);\n }\n\n async deleteExpiredTokens(): Promise<void> {\n await this.passwordResetTokenService.deleteExpired();\n }\n\n // Magic link token operations\n\n async createMagicLinkToken(uid: string, tokenHash: string, expiresAt: Date): Promise<void> {\n const col = this.db.collection(\"magic_link_tokens\");\n await col.deleteMany({ uid, usedAt: null });\n await col.insertOne({ uid, tokenHash, expiresAt, usedAt: null, createdAt: new Date() });\n }\n\n async findValidMagicLinkToken(tokenHash: string): Promise<MagicLinkTokenInfo | null> {\n const col = this.db.collection(\"magic_link_tokens\");\n const doc = await col.findOne({ tokenHash, usedAt: null, expiresAt: { $gt: new Date() } });\n if (!doc) return null;\n return { uid: doc.uid as string, expiresAt: doc.expiresAt as Date };\n }\n\n async markMagicLinkTokenUsed(tokenHash: string): Promise<void> {\n const col = this.db.collection(\"magic_link_tokens\");\n await col.updateOne({ tokenHash }, { $set: { usedAt: new Date() } });\n }\n}\n\nexport class MongoAuthRepository implements AuthRepository {\n private userService: MongoUserService;\n private roleService: MongoRoleService;\n private tokenRepository: MongoTokenRepository;\n\n constructor(private db: Db) {\n this.userService = new MongoUserService(db);\n this.roleService = new MongoRoleService(db);\n this.tokenRepository = new MongoTokenRepository(db);\n }\n\n async createUser(data: CreateUserData): Promise<UserData> {\n return this.userService.createUser(data);\n }\n\n async getUserById(id: string): Promise<UserData | null> {\n return this.userService.getUserById(id);\n }\n\n async getUserByEmail(email: string): Promise<UserData | null> {\n return this.userService.getUserByEmail(email);\n }\n\n async getUserByIdentity(provider: string, providerId: string): Promise<UserData | null> {\n return this.userService.getUserByIdentity(provider, providerId);\n }\n\n async getUserIdentities(uid: string): Promise<UserIdentityData[]> {\n return this.userService.getUserIdentities(uid);\n }\n\n async linkUserIdentity(uid: string, provider: string, providerId: string, profileData?: Record<string, unknown>): Promise<void> {\n return this.userService.linkUserIdentity(uid, provider, providerId, profileData);\n }\n\n async updateUser(id: string, data: Partial<Omit<CreateUserData, \"id\">>): Promise<UserData | null> {\n return this.userService.updateUser(id, data);\n }\n\n async deleteUser(id: string): Promise<void> {\n await this.userService.deleteUser(id);\n }\n\n async listUsers(): Promise<UserData[]> {\n return this.userService.listUsers();\n }\n\n async listUsersPaginated(options?: ListUsersOptions): Promise<PaginatedUsersResult> {\n return this.userService.listUsersPaginated(options);\n }\n\n async updatePassword(id: string, passwordHash: string): Promise<void> {\n await this.userService.updatePassword(id, passwordHash);\n }\n\n async setEmailVerified(id: string, verified: boolean): Promise<void> {\n await this.userService.setEmailVerified(id, verified);\n }\n\n async setVerificationToken(id: string, token: string | null): Promise<void> {\n await this.userService.setVerificationToken(id, token);\n }\n\n async getUserByVerificationToken(token: string): Promise<UserData | null> {\n return this.userService.getUserByVerificationToken(token);\n }\n\n async getUserRoles(uid: string): Promise<RoleData[]> {\n return this.userService.getUserRoles(uid);\n }\n\n async getUserRoleIds(uid: string): Promise<string[]> {\n return this.userService.getUserRoleIds(uid);\n }\n\n async setUserRoles(uid: string, roleIds: string[]): Promise<void> {\n await this.userService.setUserRoles(uid, roleIds);\n }\n\n async assignDefaultRole(uid: string, roleId: string): Promise<void> {\n await this.userService.assignDefaultRole(uid, roleId);\n }\n\n async getUserWithRoles(uid: string): Promise<{ user: UserData; roles: RoleData[] } | null> {\n return this.userService.getUserWithRoles(uid);\n }\n\n async getRoleById(id: string): Promise<RoleData | null> {\n return this.roleService.getRoleById(id);\n }\n\n async listRoles(): Promise<RoleData[]> {\n return this.roleService.listRoles();\n }\n\n async createRole(data: CreateRoleData): Promise<RoleData> {\n return this.roleService.createRole(data);\n }\n\n async updateRole(id: string, data: Partial<Omit<RoleData, \"id\">>): Promise<RoleData | null> {\n return this.roleService.updateRole(id, data);\n }\n\n async deleteRole(id: string): Promise<void> {\n await this.roleService.deleteRole(id);\n }\n\n async createRefreshToken(uid: string, tokenHash: string, expiresAt: Date, userAgent?: string, ipAddress?: string, session?: RefreshTokenSession): Promise<void> {\n await this.tokenRepository.createRefreshToken(uid, tokenHash, expiresAt, userAgent, ipAddress, session);\n }\n\n async markRefreshTokenRotated(tokenHash: string): Promise<void> {\n await this.tokenRepository.markRefreshTokenRotated(tokenHash);\n }\n\n async revokeRefreshTokenSession(sessionId: string): Promise<void> {\n await this.tokenRepository.revokeRefreshTokenSession(sessionId);\n }\n\n async pruneRefreshTokens(uid: string, sessionId: string, supersededBefore: Date): Promise<void> {\n await this.tokenRepository.pruneRefreshTokens(uid, sessionId, supersededBefore);\n }\n\n async getTokensValidAfter(uid: string): Promise<Date | null> {\n return this.tokenRepository.getTokensValidAfter(uid);\n }\n\n async setTokensValidAfter(uid: string, at: Date): Promise<void> {\n await this.tokenRepository.setTokensValidAfter(uid, at);\n }\n\n async findRefreshTokenByHash(tokenHash: string): Promise<RefreshTokenInfo | null> {\n return this.tokenRepository.findRefreshTokenByHash(tokenHash);\n }\n\n async deleteRefreshToken(tokenHash: string): Promise<void> {\n await this.tokenRepository.deleteRefreshToken(tokenHash);\n }\n\n async deleteAllRefreshTokensForUser(uid: string): Promise<void> {\n await this.tokenRepository.deleteAllRefreshTokensForUser(uid);\n }\n\n async listRefreshTokensForUser(uid: string): Promise<RefreshTokenInfo[]> {\n return this.tokenRepository.listRefreshTokensForUser(uid);\n }\n\n async deleteRefreshTokenById(id: string, uid: string): Promise<void> {\n await this.tokenRepository.deleteRefreshTokenById(id, uid);\n }\n\n async createPasswordResetToken(uid: string, tokenHash: string, expiresAt: Date): Promise<void> {\n await this.tokenRepository.createPasswordResetToken(uid, tokenHash, expiresAt);\n }\n\n async findValidPasswordResetToken(tokenHash: string): Promise<PasswordResetTokenInfo | null> {\n return this.tokenRepository.findValidPasswordResetToken(tokenHash);\n }\n\n async markPasswordResetTokenUsed(tokenHash: string): Promise<void> {\n await this.tokenRepository.markPasswordResetTokenUsed(tokenHash);\n }\n\n async deleteAllPasswordResetTokensForUser(uid: string): Promise<void> {\n await this.tokenRepository.deleteAllPasswordResetTokensForUser(uid);\n }\n\n async deleteExpiredTokens(): Promise<void> {\n await this.tokenRepository.deleteExpiredTokens();\n }\n\n // Magic link token operations\n\n async createMagicLinkToken(uid: string, tokenHash: string, expiresAt: Date): Promise<void> {\n await this.tokenRepository.createMagicLinkToken(uid, tokenHash, expiresAt);\n }\n\n async findValidMagicLinkToken(tokenHash: string): Promise<MagicLinkTokenInfo | null> {\n return this.tokenRepository.findValidMagicLinkToken(tokenHash);\n }\n\n async markMagicLinkTokenUsed(tokenHash: string): Promise<void> {\n await this.tokenRepository.markMagicLinkTokenUsed(tokenHash);\n }\n\n // ── MFA: not implemented on this engine ──────────────────────────────\n //\n // The routes are mounted for every backend, so `POST /auth/mfa/enroll`\n // is live here and reaches these. Throwing a bare `Error` made every one\n // of them a 500 — and a 500's message is sanitized on the way out, so the\n // caller was told \"Internal Server Error\" while the reason stayed in the\n // server log. Someone turning on two-factor auth got a server fault\n // instead of an answer, every time, on this engine.\n //\n // 501 with the reason, which is what `init.ts` already does for an admin\n // surface it mounts and cannot serve: \"they answer 501 instead, and stay\n // mounted to say why\". The reads below stay as they are — no factor can\n // exist here, so `[]`, `null` and `false` are true rather than merely\n // convenient, and `assertMfaSatisfied` reading `false` correctly leaves\n // the login gate inert.\n async createMfaFactor(uid: string, factorType: \"totp\", secretEncrypted: string, friendlyName?: string): Promise<MfaFactor> {\n throw new ApiError(\n 501,\n \"MFA_NOT_SUPPORTED\",\n \"Multi-factor authentication is not implemented for the MongoDB backend, so a factor cannot be stored. Use a Postgres data source for accounts that need a second factor.\"\n );\n }\n async getMfaFactors(uid: string): Promise<MfaFactor[]> {\n return [];\n }\n async getMfaFactorById(factorId: string): Promise<(MfaFactor & { secretEncrypted: string }) | null> {\n return null;\n }\n async verifyMfaFactor(factorId: string): Promise<void> {\n throw new ApiError(\n 501,\n \"MFA_NOT_SUPPORTED\",\n \"Multi-factor authentication is not implemented for the MongoDB backend, so a factor's verification cannot be stored. Use a Postgres data source for accounts that need a second factor.\"\n );\n }\n async deleteMfaFactor(factorId: string, uid: string): Promise<void> {\n throw new ApiError(\n 501,\n \"MFA_NOT_SUPPORTED\",\n \"Multi-factor authentication is not implemented for the MongoDB backend, so a factor cannot be stored. Use a Postgres data source for accounts that need a second factor.\"\n );\n }\n async createMfaChallenge(factorId: string, ipAddress?: string): Promise<MfaChallengeInfo> {\n throw new ApiError(\n 501,\n \"MFA_NOT_SUPPORTED\",\n \"Multi-factor authentication is not implemented for the MongoDB backend, so a challenge cannot be stored. Use a Postgres data source for accounts that need a second factor.\"\n );\n }\n async getMfaChallengeById(challengeId: string): Promise<MfaChallengeInfo | null> {\n return null;\n }\n async verifyMfaChallenge(challengeId: string): Promise<void> {\n throw new ApiError(\n 501,\n \"MFA_NOT_SUPPORTED\",\n \"Multi-factor authentication is not implemented for the MongoDB backend, so a challenge's verification cannot be stored. Use a Postgres data source for accounts that need a second factor.\"\n );\n }\n async createRecoveryCodes(uid: string, codeHashes: string[]): Promise<void> {\n throw new ApiError(\n 501,\n \"MFA_NOT_SUPPORTED\",\n \"Multi-factor authentication is not implemented for the MongoDB backend, so recovery codes cannot be stored. Use a Postgres data source for accounts that need a second factor.\"\n );\n }\n async useRecoveryCode(uid: string, codeHash: string): Promise<boolean> {\n return false;\n }\n async getUnusedRecoveryCodeCount(uid: string): Promise<number> {\n return 0;\n }\n async deleteAllRecoveryCodes(uid: string): Promise<void> {\n // No-op\n }\n async hasVerifiedMfaFactors(uid: string): Promise<boolean> {\n return false;\n }\n}\n","import { Db, MongoClient } from \"mongodb\";\nimport type {\n AuthAdapter,\n BackendBootstrapper,\n InitializedDriver,\n BootstrappedAuth,\n DatabaseAdmin,\n HistoryConfig,\n RealtimeProvider,\n DataDriver,\n CollectionConfig\n} from \"@rebasepro/types\";\nimport { MongoDriver } from \"./services/MongoDriver\";\nimport { MongoRealtimeService } from \"./services/MongoRealtimeService\";\nimport { MongoCollectionRegistry } from \"./factory\";\nimport { MongoAuthRepository, MongoUserService, MongoRoleService } from \"./auth/services\";\nimport { logger } from \"@rebasepro/server\";\n\nexport interface MongoDriverConfig {\n connection: Db;\n client: MongoClient;\n}\n\nexport interface MongoDriverInternals {\n db: Db;\n client: MongoClient;\n registry: MongoCollectionRegistry;\n realtimeService: MongoRealtimeService;\n driver: MongoDriver;\n}\n\n/** Shape of the config object passed to `initializeDriver` by the coordinator. */\ninterface DriverInitConfig {\n collections?: CollectionConfig[];\n}\n\n/** Shape of the auth config passed to `initializeAuth`. */\ninterface AuthInitConfig {\n email?: import(\"@rebasepro/server\").EmailConfig;\n}\n\n\n\nexport function createMongoBootstrapper(mongoConfig: MongoDriverConfig): BackendBootstrapper {\n // Cached admin object, set during getAdmin() and used by initializeWebsockets\n let cachedAdmin: DatabaseAdmin | undefined;\n\n return {\n type: \"mongodb\",\n\n async initializeDriver(config: unknown): Promise<InitializedDriver> {\n const { collections } = config as DriverInitConfig;\n\n const registry = new MongoCollectionRegistry();\n if (collections) {\n collections.forEach(collection => registry.register(collection));\n }\n\n const db = mongoConfig.connection;\n const client = mongoConfig.client;\n\n // Verify connection\n try {\n await db.command({ ping: 1 });\n } catch (err) {\n logger.error(\"❌ Failed to connect to MongoDB\", { error: err });\n }\n\n const realtimeService = new MongoRealtimeService(db);\n const driver = new MongoDriver(db, realtimeService, undefined, registry);\n\n const internals: MongoDriverInternals = {\n db,\n client,\n registry,\n realtimeService,\n driver\n };\n\n return {\n driver,\n realtimeProvider: realtimeService,\n collectionRegistry: registry,\n internals\n };\n },\n\n async initializeAuth(config: unknown, driverResult: InitializedDriver): Promise<BootstrappedAuth | undefined> {\n const internals = driverResult.internals as MongoDriverInternals;\n const db = internals.db;\n\n const { ensureAuthCollectionsExist } = await import(\"./auth/ensure-collections\");\n await ensureAuthCollectionsExist(db);\n\n const { createEmailService } = await import(\"@rebasepro/server\");\n const authConfig = config as AuthInitConfig | undefined;\n let emailService: unknown;\n if (authConfig?.email) {\n emailService = createEmailService(authConfig.email);\n }\n\n const userService = new MongoUserService(db);\n const roleService = new MongoRoleService(db);\n const authRepository = new MongoAuthRepository(db);\n\n return {\n userService,\n roleService,\n authRepository,\n emailService\n };\n },\n\n async initializeHistory(config: HistoryConfig, driverResult: InitializedDriver): Promise<{ historyService: unknown } | undefined> {\n if (!config) return undefined;\n\n const internals = driverResult.internals as MongoDriverInternals;\n const db = internals.db;\n\n const { ensureHistoryCollectionExists } = await import(\"./history/ensure-history-collection\");\n await ensureHistoryCollectionExists(db);\n\n const { MongoHistoryService } = await import(\"./services/MongoHistoryService\");\n\n const retention = typeof config === \"object\" ? config.retention : undefined;\n const historyService = new MongoHistoryService(db, retention ? { ttlDays: retention } : undefined);\n\n return { historyService };\n },\n\n async initializeRealtime(_config: unknown, driverResult: InitializedDriver): Promise<RealtimeProvider | undefined> {\n const internals = driverResult.internals as MongoDriverInternals;\n return internals.realtimeService;\n },\n\n getAdmin(driverResult: InitializedDriver): DatabaseAdmin | undefined {\n const internals = driverResult.internals as MongoDriverInternals;\n const db = internals.db;\n\n const admin: DatabaseAdmin = {\n async executeAggregate(pipeline: Record<string, unknown>[]) {\n const firstStage = pipeline[0];\n const collName = (firstStage as { $from?: string })?.$from ?? \"__admin__\";\n const cursor = db.collection(collName).aggregate(pipeline);\n return await cursor.toArray() as Record<string, unknown>[];\n },\n async fetchCollectionStats(collectionName: string) {\n const stats = await db.command({ collStats: collectionName }) as { count: number; size: number };\n return { count: stats.count,\nsizeBytes: stats.size };\n },\n async fetchUnmappedTables(mappedPaths?: string[]) {\n const allCollections = await db.listCollections().toArray();\n const names = allCollections.map(c => c.name).filter(n => !n.startsWith(\"system.\"));\n if (!mappedPaths || mappedPaths.length === 0) return names;\n const mappedSet = new Set(mappedPaths.map(p => p.toLowerCase()));\n return names.filter(n => !mappedSet.has(n.toLowerCase()));\n },\n async fetchTableMetadata(collectionName: string) {\n const sample = await db.collection(collectionName).findOne();\n if (!sample) return { columns: [],\nforeignKeys: [],\njunctions: [],\npolicies: [] };\n const columns = Object.entries(sample).map(([key, value]) => ({\n column_name: key,\n data_type: typeof value,\n udt_name: typeof value,\n is_nullable: \"YES\",\n column_default: null,\n character_maximum_length: null\n }));\n return { columns,\nforeignKeys: [],\njunctions: [],\npolicies: [] };\n }\n };\n\n cachedAdmin = admin;\n return admin;\n },\n\n mountRoutes() {},\n\n // Five parameters, not four. `BackendBootstrapper` declares an\n // `authAdapter` here and `init.ts` passes one; dropping it left the\n // socket with only its built-in JWT verifier, so on a backend using an\n // AuthAdapter every realtime AUTHENTICATE failed with \"Invalid or\n // expired token\" — and nothing on either side could have type-checked\n // the mismatch.\n async initializeWebsockets(server: unknown, realtimeService: RealtimeProvider, driver: DataDriver, config?: unknown, authAdapter?: AuthAdapter): Promise<void> {\n const { createMongoWebSocket } = await import(\"./websocket\");\n createMongoWebSocket(\n server as import(\"http\").Server,\n realtimeService as MongoRealtimeService,\n driver as MongoDriver,\n config as Record<string, unknown> | undefined,\n cachedAdmin,\n authAdapter\n );\n }\n };\n}\n"],"mappings":"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"}