@warlock.js/cascade 4.6.0 → 4.6.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.
- package/CHANGELOG.md +7 -0
- package/cjs/index.cjs +95 -30
- package/cjs/index.cjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-driver.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-driver.mjs +5 -4
- package/esm/drivers/mongodb/mongodb-driver.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-driver.d.mts +53 -13
- package/esm/drivers/postgres/postgres-driver.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-driver.mjs +90 -26
- package/esm/drivers/postgres/postgres-driver.mjs.map +1 -1
- package/llms-full.txt +11 -7
- package/llms.txt +1 -1
- package/package.json +4 -4
- package/skills/manage-transactions/SKILL.md +11 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mongodb-driver.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/cascade/src/drivers/mongodb/mongodb-driver.ts"],"mappings":";;;;;;;;;;;;;iBAoGgB,qBAAA;;;;;;;cAsBH,aAAA,YAAyB,cAAA;EAAA,iBAyDjB,MAAA;EAAA,iBAWA,aAAA;EAAA,iBAnEF,MAAA;EACV,MAAA,GAAS,WAAA;EACT,QAAA,GAAW,EAAA;EAAA,QACV,SAAA;EAAA,QACA,mBAAA;EAAA,QACA,uBAAA;EAAA,iBACS,kBAAA;EAAA,QACT,mBAAA;EAAA,QACA,UAAA;EAAA,IAEG,SAAA,IAAa,uBAAA;EA2BO;;;EAAA,SAhBf,IAAA;EAuHS;;;EAAA,UAlHf,aAAA;EAoP8B;;;;;;;;;EAAA,SAzOxB,aAAA,EAAe,OAAA,CAAQ,aAAA;EAkS7B;;;;;;cA/QS,MAAA;IACf,QAAA;IACA,GAAA;IACA,IAAA;IACA,IAAA;IACA,QAAA;IACA,QAAA;IACA,UAAA;IACA,OAAA;IACA,aAAA,GAAgB,kBAAA;EAAA,GAED,aAAA,GAAgB,kBAAA;EAoUxB;;;EAAA,IAzTA,YAAA;EAqVA;;;EAAA,QA1UH,mBAAA;EA0VI;;;EAAA,IA/UD,WAAA;EAkWC;;;;;;;;;;;;EAlVL,WAAA,IAAe,EAAA;EAkbgD;;;;;;;;;;;;;;;EA1Z/D,cAAA,IAAkB,mBAAA;
|
|
1
|
+
{"version":3,"file":"mongodb-driver.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/cascade/src/drivers/mongodb/mongodb-driver.ts"],"mappings":"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"}
|
|
@@ -481,7 +481,10 @@ var MongoDbDriver = class {
|
|
|
481
481
|
* @throws {Error} If transaction fails, is explicitly rolled back, or replica set not configured
|
|
482
482
|
*/
|
|
483
483
|
async transaction(fn, options) {
|
|
484
|
-
|
|
484
|
+
const ctx = { rollback(reason) {
|
|
485
|
+
throw new TransactionRollbackError(reason);
|
|
486
|
+
} };
|
|
487
|
+
if (databaseTransactionContext.hasActiveTransaction()) return fn(ctx);
|
|
485
488
|
await this.ensureReplicaSetAvailable();
|
|
486
489
|
const session = this.getClientInstance().startSession();
|
|
487
490
|
try {
|
|
@@ -491,9 +494,7 @@ var MongoDbDriver = class {
|
|
|
491
494
|
});
|
|
492
495
|
databaseTransactionContext.enter({ session });
|
|
493
496
|
try {
|
|
494
|
-
const result = await fn(
|
|
495
|
-
throw new TransactionRollbackError(reason);
|
|
496
|
-
} });
|
|
497
|
+
const result = await fn(ctx);
|
|
497
498
|
await session.commitTransaction();
|
|
498
499
|
return result;
|
|
499
500
|
} catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mongodb-driver.mjs","names":[],"sources":["../../../../../../../../@warlock.js/cascade/src/drivers/mongodb/mongodb-driver.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\nimport { log } from \"@warlock.js/logger\";\nimport type {\n BulkWriteOptions,\n ClientSession,\n Db,\n DeleteOptions,\n FindOneAndDeleteOptions,\n FindOneAndUpdateOptions,\n InsertManyResult,\n InsertOneOptions,\n MongoClient,\n MongoClientOptions,\n TransactionOptions,\n UpdateFilter,\n UpdateOptions,\n} from \"mongodb\";\nimport { EventEmitter } from \"node:events\";\nimport { databaseTransactionContext } from \"../../context/database-transaction-context\";\nimport type {\n DriverBlueprintContract,\n DriverContract,\n DriverEvent,\n DriverEventListener,\n DriverTransactionContract,\n IdGeneratorContract,\n InsertResult,\n MigrationDriverContract,\n QueryBuilderContract,\n RawQueryResult,\n SyncAdapterContract,\n TransactionContext,\n UpdateOperations,\n UpdateResult,\n} from \"../../contracts\";\nimport { dataSourceRegistry } from \"../../data-source/data-source-registry\";\nimport { DatabaseDirtyTracker } from \"../../database-dirty-tracker\";\nimport { TransactionRollbackError } from \"../../errors/transaction-rollback.error\";\nimport { type SQLSerializer } from \"../../migration/sql-serializer\";\nimport type { ModelDefaults } from \"../../types\";\nimport { isValidDateValue } from \"../../utils/is-valid-date-value\";\nimport { MongoDBBlueprint } from \"./mongodb-blueprint\";\nimport { MongoIdGenerator } from \"./mongodb-id-generator\";\nimport { MongoMigrationDriver } from \"./mongodb-migration-driver\";\nimport { MongoQueryBuilder } from \"./mongodb-query-builder\";\nimport { MongoSyncAdapter } from \"./mongodb-sync-adapter\";\nimport type { MongoDriverOptions } from \"./types\";\n\nconst DEFAULT_TRANSACTION_OPTIONS: TransactionOptions = {\n readPreference: \"primary\",\n readConcern: { level: \"local\" },\n writeConcern: { w: \"majority\" },\n};\n\n// ============================================================\n// Lazy-loaded MongoDB SDK Types\n// ============================================================\n\n/**\n * Cached MongoDB module (loaded once, reused)\n */\nlet MongoDBClient: typeof import(\"mongodb\");\n\nlet ObjectId: typeof import(\"mongodb\").ObjectId;\n\nlet isModuleExists: boolean | null = null;\n\nlet loadingPromise: Promise<any>;\n\n/**\n * Installation instructions for MongoDB package\n */\nconst MONGODB_INSTALL_INSTRUCTIONS = `\nMongoDB driver requires the mongodb package.\nInstall it with:\n\n npm install mongodb\n\nOr with your preferred package manager:\n\n pnpm add mongodb\n yarn add mongodb\n`.trim();\n\n/**\n * Load MongoDB module\n */\nasync function loadMongoDB() {\n try {\n loadingPromise = import(\"mongodb\");\n MongoDBClient = await loadingPromise;\n ObjectId = MongoDBClient.ObjectId;\n isModuleExists = true;\n } catch {\n isModuleExists = false;\n }\n}\n\nloadMongoDB();\n\nexport function isMongoDBDriverLoaded() {\n return isModuleExists;\n}\n\nasync function assertModuleIsLoaded() {\n if (isModuleExists === false) {\n throw new Error(MONGODB_INSTALL_INSTRUCTIONS);\n }\n\n if (isModuleExists === null) {\n await loadingPromise;\n\n return await assertModuleIsLoaded();\n }\n}\n\n/**\n * MongoDB driver implementation that fulfils the Cascade driver contract.\n *\n * It encapsulates the native Mongo client, exposes lifecycle events, and\n * provides helpers for CRUD, transactions, atomic updates, and sync adapters.\n */\nexport class MongoDbDriver implements DriverContract {\n private readonly events = new EventEmitter();\n public client?: MongoClient;\n public database?: Db;\n private connected = false;\n private syncAdapterInstance?: MongoSyncAdapter;\n private migrationDriverInstance?: MigrationDriverContract;\n private readonly transactionOptions: TransactionOptions;\n private idGeneratorInstance?: IdGeneratorContract;\n private _blueprint?: DriverBlueprintContract;\n\n public get blueprint(): DriverBlueprintContract {\n if (!this._blueprint) {\n this._blueprint = new MongoDBBlueprint(this.database!);\n }\n\n return this._blueprint!;\n }\n\n /**\n * The name of this driver.\n */\n public readonly name = \"mongodb\";\n\n /**\n * Current database name\n */\n protected _databaseName?: string;\n\n /**\n * MongoDB driver model defaults.\n *\n * MongoDB follows NoSQL conventions:\n * - camelCase naming for fields (createdAt, updatedAt, deletedAt)\n * - Manual ID generation (auto-increment id field separate from _id)\n * - Timestamps enabled by default\n * - Trash delete strategy with per-collection trash tables\n */\n public readonly modelDefaults: Partial<ModelDefaults> = {\n namingConvention: \"camelCase\",\n createdAtColumn: \"createdAt\",\n updatedAtColumn: \"updatedAt\",\n deletedAtColumn: \"deletedAt\",\n timestamps: true,\n autoGenerateId: true, // MongoDB needs manual ID generation\n strictMode: \"strip\",\n deleteStrategy: \"trash\",\n trashTable: (table) => `${table}Trash`, // Per-collection trash (usersTrash, productsTrash)\n };\n\n /**\n * Create a new MongoDB driver using the supplied connection options.\n *\n * @param config - Connection configuration\n * @param driverOptions - Driver-specific options\n */\n public constructor(\n private readonly config: {\n database: string;\n uri?: string;\n host?: string;\n port?: number;\n username?: string;\n password?: string;\n authSource?: string;\n logging?: boolean;\n clientOptions?: MongoClientOptions;\n },\n private readonly driverOptions?: MongoDriverOptions,\n ) {\n this.transactionOptions = {\n ...DEFAULT_TRANSACTION_OPTIONS,\n ...driverOptions?.transactionOptions,\n };\n }\n\n /**\n * Get data base name\n */\n public get databaseName(): string | undefined {\n if (!this._databaseName) {\n this.resolveDatabaseName();\n }\n\n return this._databaseName;\n }\n\n /**\n * Resolve database name either from config or uri\n */\n private resolveDatabaseName() {\n if (this.config.database) {\n this._databaseName = this.config.database;\n } else if (this.config.uri) {\n this._databaseName = this.config.uri.split(\"/\").pop()?.split(\"?\")?.[0];\n }\n }\n\n /**\n * Indicates whether the driver currently maintains an active connection.\n */\n public get isConnected(): boolean {\n return this.connected;\n }\n\n /**\n * Get the MongoDB database instance.\n *\n * @returns The MongoDB Db instance\n * @throws {Error} If not connected\n *\n * @example\n * ```typescript\n * const db = driver.getDatabase();\n * const collection = db.collection(\"users\");\n * ```\n */\n public getDatabase(): Db {\n if (!this.database) {\n throw new Error(\n \"Database not available. Ensure the driver is connected before accessing the database.\",\n );\n }\n return this.database;\n }\n\n /**\n * Get the ID generator instance for this driver.\n *\n * Creates a MongoIdGenerator on first access if autoGenerateId is enabled.\n *\n * @returns The ID generator instance, or undefined if disabled\n *\n * @example\n * ```typescript\n * const idGenerator = driver.getIdGenerator();\n * if (idGenerator) {\n * const id = await idGenerator.generateNextId({ table: \"users\" });\n * }\n * ```\n */\n public getIdGenerator(): IdGeneratorContract | undefined {\n // Return undefined if ID generation is disabled\n if (this.driverOptions?.autoGenerateId === false) {\n return undefined;\n }\n\n // Create ID generator lazily on first access\n if (!this.idGeneratorInstance) {\n this.idGeneratorInstance = new MongoIdGenerator(this, this.driverOptions?.counterCollection);\n }\n\n return this.idGeneratorInstance;\n }\n\n /**\n * Establish a MongoDB connection using the configured options.\n * Throws if the connection attempt fails.\n */\n public async connect(): Promise<void> {\n if (this.connected) {\n return;\n }\n\n await assertModuleIsLoaded();\n\n const uri = this.resolveUri();\n\n const { MongoClient, ObjectId: ObjectIdMongoDB } = MongoDBClient;\n\n ObjectId = ObjectIdMongoDB;\n\n const client = new MongoClient(uri, this.buildClientOptions());\n\n try {\n log.info(\n \"database.mongodb\",\n \"connection\",\n `Connecting to database ${colors.bold(colors.yellowBright(this.databaseName))}`,\n );\n await client.connect();\n this.client = client;\n this.database = client.db(this.databaseName);\n\n this.connected = true;\n log.success(\"database.mongodb\", \"connection\", \"Connected to database\");\n\n client.on(\"close\", () => {\n if (this.connected) {\n this.connected = false;\n this.emit(\"disconnected\");\n log.warn(\"database.mongodb\", \"connection\", \"Disconnected from database\");\n }\n });\n\n if (this.config.logging) {\n const ignoredCommands = [\"isMaster\", \"hello\", \"ping\", \"saslStart\", \"saslContinue\"];\n\n client.on(\"commandStarted\", (event: any) => {\n if (ignoredCommands.includes(event.commandName)) return;\n\n let cmdStr = JSON.stringify(event.command);\n if (cmdStr.length > 300) {\n cmdStr = cmdStr.substring(0, 300) + \"...\";\n }\n\n log.info({\n module: \"database.mongodb\",\n action: \"query.executing\",\n message: `[${event.commandName}] ${cmdStr}`,\n context: { command: event.command },\n });\n });\n\n client.on(\"commandSucceeded\", (event: any) => {\n if (ignoredCommands.includes(event.commandName)) return;\n\n log.success({\n module: \"database.mongodb\",\n action: \"query.executed\",\n message: `[${event.duration.toFixed(2)}ms] [${event.commandName}]`,\n });\n });\n\n client.on(\"commandFailed\", (event: any) => {\n if (ignoredCommands.includes(event.commandName)) return;\n\n log.error({\n module: \"database.mongodb\",\n action: \"query.error\",\n message: `[${event.duration.toFixed(2)}ms] [${event.commandName}]`,\n context: { failure: event.failure },\n });\n });\n }\n\n this.emit(\"connected\");\n } catch (error: any) {\n await client.close().catch(() => undefined);\n this.emit(\"disconnected\");\n // Boot-time database connection failure is unrecoverable in every\n // realistic caller (app boot, CLI migrations, workers) — `fatal` makes\n // \"page on fatal only\" alerting clean. Per-query failures stay at error.\n log.fatal(\n \"database.mongodb\",\n \"connection\",\n `Failed to connect to database: ${error.message}`,\n );\n throw error;\n }\n }\n\n /**\n * Close the underlying MongoDB connection.\n */\n public async disconnect(): Promise<void> {\n if (!this.client) {\n return;\n }\n\n try {\n await this.client.close();\n } finally {\n this.connected = false;\n this.emit(\"disconnected\");\n }\n }\n\n /**\n * Subscribe to driver lifecycle events.\n */\n public on(event: DriverEvent, listener: DriverEventListener): void {\n this.events.on(event, listener);\n }\n\n /**\n * Insert a single document into the given collection.\n */\n public async insert(\n table: string,\n document: Record<string, unknown>,\n options?: Record<string, unknown>,\n ): Promise<InsertResult> {\n const collection = this.getDatabaseInstance().collection(table);\n const mongoOptions = this.withSession<InsertOneOptions>(options);\n const result = await collection.insertOne(document, mongoOptions);\n\n return {\n document: {\n ...document,\n _id: result.insertedId,\n },\n };\n }\n\n /**\n * Insert multiple documents into the given collection.\n */\n public async insertMany(\n table: string,\n documents: Record<string, unknown>[],\n options?: Record<string, unknown>,\n ): Promise<InsertResult[]> {\n const collection = this.getDatabaseInstance().collection(table);\n const mongoOptions = this.withSession<BulkWriteOptions>(options);\n const result: InsertManyResult<Record<string, unknown>> = await collection.insertMany(\n documents,\n mongoOptions,\n );\n\n return documents.map((document, index) => {\n const insertedId = result.insertedIds[index as unknown as keyof typeof result.insertedIds];\n\n return {\n document: {\n ...document,\n _id: insertedId,\n },\n };\n });\n }\n\n /**\n * Update a single document that matches the provided filter.\n */\n public async update(\n table: string,\n filter: Record<string, unknown>,\n update: Record<string, unknown>,\n options?: Record<string, unknown>,\n ): Promise<UpdateResult> {\n const collection = this.getDatabaseInstance().collection(table);\n const mongoOptions = this.withSession<UpdateOptions>(options);\n const result = await collection.updateOne(\n filter,\n update as UpdateFilter<Record<string, unknown>>,\n mongoOptions,\n );\n\n return { modifiedCount: result.modifiedCount };\n }\n\n /**\n * Replace a single document that matches the provided filter.\n */\n public async replace<T = unknown>(\n table: string,\n filter: Record<string, unknown>,\n document: Record<string, unknown>,\n options?: Record<string, unknown>,\n ): Promise<T | null> {\n const collection = this.getDatabaseInstance().collection(table);\n const result = await collection.findOneAndReplace(filter, document as Record<string, unknown>);\n\n return result?.value as T | null;\n }\n\n /**\n * Find one and update a single document that matches the provided filter and return the updated document\n */\n public async findOneAndUpdate<T = unknown>(\n table: string,\n filter: Record<string, unknown>,\n update: UpdateOperations,\n options?: Record<string, unknown>,\n ): Promise<T | null> {\n const collection = this.getDatabaseInstance().collection(table);\n const mongoOptions = this.withSession<FindOneAndUpdateOptions>(options);\n const result = await collection.findOneAndUpdate(filter, update as Record<string, unknown>, {\n returnDocument: \"after\",\n ...mongoOptions,\n });\n\n return result as T | null;\n }\n\n /**\n * Upsert (insert or update) a single document.\n *\n * Uses MongoDB's findOneAndUpdate with upsert option.\n *\n * @param table - Target collection name\n * @param filter - Filter conditions to find existing document\n * @param document - Document data to insert or update\n * @param options - Optional upsert options\n * @returns The upserted document\n */\n public async upsert<T = unknown>(\n table: string,\n filter: Record<string, unknown>,\n document: Record<string, unknown>,\n options?: Record<string, unknown>,\n ): Promise<T> {\n const collection = this.getDatabaseInstance().collection(table);\n const mongoOptions = this.withSession<FindOneAndUpdateOptions>(options);\n\n // Use $set to update all fields from document\n const update = { $set: document };\n\n const result = await collection.findOneAndUpdate(filter, update, {\n upsert: true,\n returnDocument: \"after\",\n ...mongoOptions,\n });\n\n return result as T;\n }\n\n /**\n * Find one and delete a single document that matches the provided filter and return the deleted document.\n *\n * @param table - Target collection name\n * @param filter - Filter conditions\n * @param options - Optional delete options\n * @returns The deleted document or null if not found\n */\n public async findOneAndDelete<T = unknown>(\n table: string,\n filter: Record<string, unknown>,\n options?: Record<string, unknown>,\n ): Promise<T | null> {\n const collection = this.getDatabaseInstance().collection(table);\n const mongoOptions = this.withSession<FindOneAndDeleteOptions>(options);\n\n const result = await collection.findOneAndDelete(filter, mongoOptions || {});\n\n return result as T | null;\n }\n\n /**\n * Update multiple documents that match the provided filter.\n */\n public async updateMany(\n table: string,\n filter: Record<string, unknown>,\n update: UpdateOperations,\n options?: Record<string, unknown>,\n ): Promise<UpdateResult> {\n const collection = this.getDatabaseInstance().collection(table);\n const mongoOptions = this.withSession<UpdateOptions>(options);\n const result = await collection.updateMany(\n filter,\n update as UpdateFilter<Record<string, unknown>>,\n mongoOptions,\n );\n\n return { modifiedCount: result.modifiedCount };\n }\n\n /**\n * Delete a single document that matches the provided filter.\n */\n public async delete(\n table: string,\n filter: Record<string, unknown> = {},\n options?: Record<string, unknown>,\n ): Promise<number> {\n const collection = this.getDatabaseInstance().collection(table);\n const mongoOptions = this.withSession<DeleteOptions>(options);\n const result = await collection.deleteOne(filter, mongoOptions);\n\n return result.deletedCount > 0 ? 1 : 0;\n }\n\n /**\n * Delete documents that match the provided filter.\n */\n public async deleteMany(\n table: string,\n filter: Record<string, unknown> = {},\n options?: Record<string, unknown>,\n ): Promise<number> {\n const collection = this.getDatabaseInstance().collection(table);\n const mongoOptions = this.withSession<DeleteOptions>(options);\n\n const result = await collection.deleteMany(filter, mongoOptions);\n\n return result.deletedCount ?? 0;\n }\n\n /**\n * Remove all records from a collection.\n *\n * This uses deleteMany with an empty filter to remove all documents.\n * For very large collections, consider using the migration driver's\n * dropTable + createTable approach for better performance.\n */\n public async truncateTable(table: string, options?: Record<string, unknown>): Promise<number> {\n const collection = this.getDatabaseInstance().collection(table);\n const mongoOptions = this.withSession<DeleteOptions>(options);\n const result = await collection.deleteMany({}, mongoOptions);\n\n return result.deletedCount ?? 0;\n }\n\n /**\n * Serialize the given data\n */\n public serialize(data: Record<string, unknown>): Record<string, unknown> {\n const serialized: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(data)) {\n if (value === undefined) {\n continue; // Skip undefined values\n }\n\n if (value instanceof ObjectId) {\n serialized[key] = value.toString();\n } else if (value instanceof Date) {\n serialized[key] = value.toISOString();\n } else if (typeof value === \"bigint\") {\n serialized[key] = value.toString();\n } else if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n // Nested objects will be stored as JSONB\n serialized[key] = value;\n } else {\n serialized[key] = value;\n }\n }\n\n return serialized;\n }\n\n /**\n * Get the dirty tracker for this driver.\n */\n public getDirtyTracker(data: Record<string, unknown>): DatabaseDirtyTracker {\n return new DatabaseDirtyTracker(data);\n }\n\n /**\n * Deserialize the given data\n */\n public deserialize(data: Record<string, unknown>): Record<string, unknown> {\n if (data._id && typeof data._id === \"string\") {\n data._id = new ObjectId(data._id);\n }\n\n for (const [key, value] of Object.entries(data)) {\n // Only re-inflate strings — Mongo driver already returns Date objects from DB reads\n if (typeof value === \"string\" && isValidDateValue(value)) {\n data[key] = new Date(value);\n }\n }\n\n return data;\n }\n\n /**\n * Provide a Mongo-backed query builder instance for the given collection.\n */\n public queryBuilder<T = unknown>(table: string): QueryBuilderContract<T> {\n return new MongoQueryBuilder(table, dataSourceRegistry.get());\n }\n\n /**\n * Begin a MongoDB transaction, returning commit/rollback helpers.\n */\n public async beginTransaction(): Promise<DriverTransactionContract<ClientSession>> {\n const client = this.getClientInstance();\n const session = client.startSession();\n\n await session.startTransaction(this.transactionOptions);\n databaseTransactionContext.enter({ session });\n let finished = false;\n\n const finalize = async (operation: () => Promise<void>): Promise<void> => {\n if (finished) return;\n\n try {\n await operation();\n } finally {\n finished = true;\n databaseTransactionContext.exit();\n await session.endSession().catch(() => undefined);\n }\n };\n\n return {\n context: session,\n commit: async () => {\n await finalize(async () => {\n try {\n await session.commitTransaction();\n } catch (error) {\n await session.abortTransaction().catch(() => undefined);\n throw error;\n }\n });\n },\n rollback: async () => {\n await finalize(async () => {\n await session.abortTransaction();\n });\n },\n };\n }\n\n /**\n * Execute a function within a transaction scope (recommended pattern).\n *\n * Automatically commits on success, rolls back on any error, and guarantees\n * resource cleanup. This is the recommended way to use transactions.\n *\n * **MongoDB Requirements:**\n * - Requires MongoDB 4.0+ with replica set or sharded cluster\n * - Standalone MongoDB instances do not support transactions\n *\n * @param fn - Async function to execute within transaction\n * @param options - Transaction options (read preference, write concern, etc.)\n * @returns The return value of the callback function\n * @throws {Error} If transaction fails, is explicitly rolled back, or replica set not configured\n */\n public async transaction<T>(\n fn: (ctx: TransactionContext) => Promise<T>,\n options?: Record<string, unknown>,\n ): Promise<T> {\n // Prevent nested transaction() calls\n if (databaseTransactionContext.hasActiveTransaction()) {\n throw new Error(\n \"Nested transaction() calls are not supported. \" +\n \"Use beginTransaction() with savepoints for advanced transaction patterns.\",\n );\n }\n\n // Check if MongoDB is running as a replica set (required for transactions)\n await this.ensureReplicaSetAvailable();\n\n const client = this.getClientInstance();\n const session = client.startSession();\n\n try {\n await session.startTransaction({\n ...this.transactionOptions,\n ...(options as TransactionOptions),\n });\n\n // Set transaction context for queries within callback\n databaseTransactionContext.enter({ session });\n\n try {\n // Create transaction context with rollback method\n const ctx: TransactionContext = {\n rollback(reason?: string): never {\n throw new TransactionRollbackError(reason);\n },\n };\n\n // Execute callback\n const result = await fn(ctx);\n\n // Auto-commit on success\n await session.commitTransaction();\n\n return result;\n } catch (error) {\n // Auto-rollback on any error (including explicit rollback)\n await session.abortTransaction().catch(() => undefined);\n throw error;\n } finally {\n // Guaranteed context cleanup\n databaseTransactionContext.exit();\n }\n } finally {\n // Guaranteed session cleanup\n await session.endSession().catch(() => undefined);\n }\n }\n\n /**\n * Execute atomic operations (typically $inc/$set style updates) against documents.\n *\n * Uses `updateMany` so callers can atomically modify any set of documents.\n */\n public async atomic(\n table: string,\n filter: Record<string, unknown>,\n operations: Record<string, unknown>,\n options?: Record<string, unknown>,\n ): Promise<UpdateResult> {\n const collection = this.getDatabaseInstance().collection(table);\n const mongoOptions = this.withSession<UpdateOptions>(options);\n const result = await collection.updateMany(\n filter,\n operations as UpdateFilter<Record<string, unknown>>,\n mongoOptions,\n );\n\n return { modifiedCount: result.modifiedCount };\n }\n\n /**\n * Lazily create (and cache) the Mongo sync adapter.\n * The adapter uses this driver instance to ensure all operations\n * participate in active transactions via the session context.\n */\n public syncAdapter(): SyncAdapterContract {\n if (!this.syncAdapterInstance) {\n this.syncAdapterInstance = new MongoSyncAdapter(this);\n }\n\n return this.syncAdapterInstance;\n }\n\n /**\n * Lazily create (and cache) the Mongo migration driver.\n * The migration driver handles schema operations like indexes, collections, etc.\n */\n public migrationDriver(): MigrationDriverContract {\n if (!this.migrationDriverInstance) {\n this.migrationDriverInstance = new MongoMigrationDriver(this);\n }\n\n return this.migrationDriverInstance!;\n }\n\n /**\n * Expose the underlying Mongo client for advanced consumers.\n */\n public getClient<Client = MongoClient>(): Client {\n return this.getClientInstance() as Client;\n }\n\n /**\n * Retrieve the active Mongo client, throwing if the driver is disconnected.\n */\n private getClientInstance(): MongoClient {\n if (!this.client) {\n throw new Error(\"Mongo driver is not connected.\");\n }\n\n return this.client;\n }\n\n /**\n * Retrieve the active Mongo database, throwing if the driver is disconnected.\n * @private\n */\n private getDatabaseInstance(): Db {\n if (!this.database) {\n throw new Error(\"Mongo driver is not connected to a database.\");\n }\n\n return this.database;\n }\n\n /**\n * Resolve the Mongo connection string based on provided options.\n */\n private resolveUri(): string {\n if (this.config.uri) {\n return this.config.uri;\n }\n\n const host = this.config.host ?? \"localhost\";\n const port = this.config.port ?? 27017;\n\n return `mongodb://${host}:${port}`;\n }\n\n /**\n * Build the Mongo client options derived from the driver configuration.\n */\n private buildClientOptions(): MongoClientOptions {\n const baseOptions: MongoClientOptions = {\n ...(this.config.clientOptions ?? {}),\n };\n\n if (this.config.logging) {\n baseOptions.monitorCommands = true;\n }\n\n if (this.config.username && !baseOptions.auth) {\n baseOptions.auth = {\n username: this.config.username,\n password: this.config.password,\n };\n }\n\n if (this.config.authSource && !baseOptions.authSource) {\n baseOptions.authSource = this.config.authSource;\n }\n\n return baseOptions;\n }\n\n /**\n * Emit a driver lifecycle event.\n */\n private emit(event: DriverEvent, ...args: unknown[]): void {\n this.events.emit(event, ...args);\n }\n\n /**\n * Ensure MongoDB is running as a replica set (required for transactions).\n *\n * @throws {Error} If MongoDB is running as a standalone instance\n */\n private async ensureReplicaSetAvailable(): Promise<void> {\n try {\n const admin = this.database!.admin();\n const status = await admin.serverStatus();\n\n if (!status.repl) {\n throw new Error(\n \"MongoDB transactions require a replica set or sharded cluster. \" +\n \"Standalone MongoDB instances do not support transactions.\\n\\n\" +\n \"For local development:\\n\" +\n \" - Run MongoDB with --replSet flag: mongod --replSet rs0\\n\" +\n \" - Or use Docker with replica set configuration\\n\" +\n \" - Or use MongoDB Atlas (cloud) which provides replica sets by default\",\n );\n }\n } catch (error: any) {\n if (error.message?.includes(\"replica set\")) {\n throw error;\n }\n throw new Error(`Failed to check MongoDB replica set status: ${error.message}`);\n }\n }\n\n /**\n * Attach the active transaction session (when available) to Mongo options.\n */\n private withSession<TOptions extends { session?: ClientSession }>(\n options?: Record<string, unknown>,\n ): TOptions | undefined {\n const session = databaseTransactionContext.getSession<ClientSession>();\n\n if (!session) {\n return options as TOptions | undefined;\n }\n\n const baseOptions = options ? ({ ...options } as TOptions) : ({} as TOptions);\n\n baseOptions.session = session;\n\n return baseOptions;\n }\n\n // ============================================================\n // SQL Compatibility Operations (Not supported in MongoDB)\n // ============================================================\n\n /**\n * Return a SQL serializer for this driver's dialect.\n * Not supported for MongoDB.\n */\n public getSQLSerializer(): SQLSerializer {\n throw new Error(\"MongoDB driver does not support SQL serialization.\");\n }\n\n /**\n * Execute a raw SQL query.\n * Not supported for MongoDB.\n */\n public async query<T = Record<string, unknown>>(\n _sql: string,\n _params?: unknown[],\n ): Promise<RawQueryResult<T>> {\n throw new Error(\"MongoDB driver does not support raw SQL queries.\");\n }\n\n // ============================================================\n // Database Lifecycle Operations\n // ============================================================\n\n /**\n * Create a new database.\n *\n * In MongoDB, databases are created automatically when data is first written.\n * This method creates an empty collection to ensure the database exists.\n *\n * @param name - Database name to create\n * @returns true if created, false if already exists\n */\n public async createDatabase(name: string): Promise<boolean> {\n const client = this.getClientInstance();\n\n // Check if database already exists\n if (await this.databaseExists(name)) {\n return false;\n }\n\n try {\n // MongoDB creates databases on first write, so create a system collection\n const db = client.db(name);\n await db.createCollection(\"__init__\");\n // Drop the temp collection\n await db.collection(\"__init__\").drop();\n\n log.success(\"database\", \"lifecycle\", `Created database ${name}`);\n return true;\n } catch (error) {\n log.error(\"database\", \"lifecycle\", `Failed to create database ${name}: ${error}`);\n throw error;\n }\n }\n\n /**\n * Drop a database.\n *\n * @param name - Database name to drop\n * @returns true if dropped, false if didn't exist\n */\n public async dropDatabase(name: string): Promise<boolean> {\n const client = this.getClientInstance();\n\n // Check if database exists\n if (!(await this.databaseExists(name))) {\n return false;\n }\n\n try {\n await client.db(name).dropDatabase();\n log.success(\"database\", \"lifecycle\", `Dropped database ${name}`);\n return true;\n } catch (error) {\n log.error(\"database\", \"lifecycle\", `Failed to drop database ${name}: ${error}`);\n throw error;\n }\n }\n\n /**\n * Check if a database exists.\n *\n * @param name - Database name to check\n * @returns true if database exists\n */\n public async databaseExists(name: string): Promise<boolean> {\n const client = this.getClientInstance();\n\n const result = await client.db(\"admin\").admin().listDatabases();\n return result.databases.some((db) => db.name === name);\n }\n\n /**\n * List all databases.\n *\n * @returns Array of database names\n */\n public async listDatabases(): Promise<string[]> {\n const client = this.getClientInstance();\n\n const result = await client.db(\"admin\").admin().listDatabases();\n return result.databases\n .map((db) => db.name)\n .filter((name) => ![\"admin\", \"local\", \"config\"].includes(name));\n }\n\n // ============================================================\n // Table/Collection Management Operations\n // ============================================================\n\n /**\n * Drop a collection.\n *\n * @param name - Collection name to drop\n * @throws Error if collection doesn't exist\n */\n public async dropTable(name: string): Promise<void> {\n const db = this.getDatabaseInstance();\n await db.collection(name).drop();\n log.success(\"database\", \"collection\", `Dropped collection ${name}`);\n }\n\n /**\n * Drop a collection if it exists.\n *\n * @param name - Collection name to drop\n */\n public async dropTableIfExists(name: string): Promise<void> {\n if (await this.blueprint.tableExists(name)) {\n await this.dropTable(name);\n }\n }\n\n /**\n * Drop all collections in the current database.\n *\n * Useful for `migrate:fresh` command.\n */\n public async dropAllTables(): Promise<void> {\n const collections = await this.blueprint.listTables();\n\n if (collections.length === 0) {\n return;\n }\n\n const db = this.getDatabaseInstance();\n\n for (const collection of collections) {\n await db.collection(collection).drop();\n }\n\n log.success(\"database\", \"collection\", `Dropped ${collections.length} collections`);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mongodb-driver.mjs","names":[],"sources":["../../../../../../../../@warlock.js/cascade/src/drivers/mongodb/mongodb-driver.ts"],"sourcesContent":["import { colors } from \"@mongez/copper\";\nimport { log } from \"@warlock.js/logger\";\nimport type {\n BulkWriteOptions,\n ClientSession,\n Db,\n DeleteOptions,\n FindOneAndDeleteOptions,\n FindOneAndUpdateOptions,\n InsertManyResult,\n InsertOneOptions,\n MongoClient,\n MongoClientOptions,\n TransactionOptions,\n UpdateFilter,\n UpdateOptions,\n} from \"mongodb\";\nimport { EventEmitter } from \"node:events\";\nimport { databaseTransactionContext } from \"../../context/database-transaction-context\";\nimport type {\n DriverBlueprintContract,\n DriverContract,\n DriverEvent,\n DriverEventListener,\n DriverTransactionContract,\n IdGeneratorContract,\n InsertResult,\n MigrationDriverContract,\n QueryBuilderContract,\n RawQueryResult,\n SyncAdapterContract,\n TransactionContext,\n UpdateOperations,\n UpdateResult,\n} from \"../../contracts\";\nimport { dataSourceRegistry } from \"../../data-source/data-source-registry\";\nimport { DatabaseDirtyTracker } from \"../../database-dirty-tracker\";\nimport { TransactionRollbackError } from \"../../errors/transaction-rollback.error\";\nimport { type SQLSerializer } from \"../../migration/sql-serializer\";\nimport type { ModelDefaults } from \"../../types\";\nimport { isValidDateValue } from \"../../utils/is-valid-date-value\";\nimport { MongoDBBlueprint } from \"./mongodb-blueprint\";\nimport { MongoIdGenerator } from \"./mongodb-id-generator\";\nimport { MongoMigrationDriver } from \"./mongodb-migration-driver\";\nimport { MongoQueryBuilder } from \"./mongodb-query-builder\";\nimport { MongoSyncAdapter } from \"./mongodb-sync-adapter\";\nimport type { MongoDriverOptions } from \"./types\";\n\nconst DEFAULT_TRANSACTION_OPTIONS: TransactionOptions = {\n readPreference: \"primary\",\n readConcern: { level: \"local\" },\n writeConcern: { w: \"majority\" },\n};\n\n// ============================================================\n// Lazy-loaded MongoDB SDK Types\n// ============================================================\n\n/**\n * Cached MongoDB module (loaded once, reused)\n */\nlet MongoDBClient: typeof import(\"mongodb\");\n\nlet ObjectId: typeof import(\"mongodb\").ObjectId;\n\nlet isModuleExists: boolean | null = null;\n\nlet loadingPromise: Promise<any>;\n\n/**\n * Installation instructions for MongoDB package\n */\nconst MONGODB_INSTALL_INSTRUCTIONS = `\nMongoDB driver requires the mongodb package.\nInstall it with:\n\n npm install mongodb\n\nOr with your preferred package manager:\n\n pnpm add mongodb\n yarn add mongodb\n`.trim();\n\n/**\n * Load MongoDB module\n */\nasync function loadMongoDB() {\n try {\n loadingPromise = import(\"mongodb\");\n MongoDBClient = await loadingPromise;\n ObjectId = MongoDBClient.ObjectId;\n isModuleExists = true;\n } catch {\n isModuleExists = false;\n }\n}\n\nloadMongoDB();\n\nexport function isMongoDBDriverLoaded() {\n return isModuleExists;\n}\n\nasync function assertModuleIsLoaded() {\n if (isModuleExists === false) {\n throw new Error(MONGODB_INSTALL_INSTRUCTIONS);\n }\n\n if (isModuleExists === null) {\n await loadingPromise;\n\n return await assertModuleIsLoaded();\n }\n}\n\n/**\n * MongoDB driver implementation that fulfils the Cascade driver contract.\n *\n * It encapsulates the native Mongo client, exposes lifecycle events, and\n * provides helpers for CRUD, transactions, atomic updates, and sync adapters.\n */\nexport class MongoDbDriver implements DriverContract {\n private readonly events = new EventEmitter();\n public client?: MongoClient;\n public database?: Db;\n private connected = false;\n private syncAdapterInstance?: MongoSyncAdapter;\n private migrationDriverInstance?: MigrationDriverContract;\n private readonly transactionOptions: TransactionOptions;\n private idGeneratorInstance?: IdGeneratorContract;\n private _blueprint?: DriverBlueprintContract;\n\n public get blueprint(): DriverBlueprintContract {\n if (!this._blueprint) {\n this._blueprint = new MongoDBBlueprint(this.database!);\n }\n\n return this._blueprint!;\n }\n\n /**\n * The name of this driver.\n */\n public readonly name = \"mongodb\";\n\n /**\n * Current database name\n */\n protected _databaseName?: string;\n\n /**\n * MongoDB driver model defaults.\n *\n * MongoDB follows NoSQL conventions:\n * - camelCase naming for fields (createdAt, updatedAt, deletedAt)\n * - Manual ID generation (auto-increment id field separate from _id)\n * - Timestamps enabled by default\n * - Trash delete strategy with per-collection trash tables\n */\n public readonly modelDefaults: Partial<ModelDefaults> = {\n namingConvention: \"camelCase\",\n createdAtColumn: \"createdAt\",\n updatedAtColumn: \"updatedAt\",\n deletedAtColumn: \"deletedAt\",\n timestamps: true,\n autoGenerateId: true, // MongoDB needs manual ID generation\n strictMode: \"strip\",\n deleteStrategy: \"trash\",\n trashTable: (table) => `${table}Trash`, // Per-collection trash (usersTrash, productsTrash)\n };\n\n /**\n * Create a new MongoDB driver using the supplied connection options.\n *\n * @param config - Connection configuration\n * @param driverOptions - Driver-specific options\n */\n public constructor(\n private readonly config: {\n database: string;\n uri?: string;\n host?: string;\n port?: number;\n username?: string;\n password?: string;\n authSource?: string;\n logging?: boolean;\n clientOptions?: MongoClientOptions;\n },\n private readonly driverOptions?: MongoDriverOptions,\n ) {\n this.transactionOptions = {\n ...DEFAULT_TRANSACTION_OPTIONS,\n ...driverOptions?.transactionOptions,\n };\n }\n\n /**\n * Get data base name\n */\n public get databaseName(): string | undefined {\n if (!this._databaseName) {\n this.resolveDatabaseName();\n }\n\n return this._databaseName;\n }\n\n /**\n * Resolve database name either from config or uri\n */\n private resolveDatabaseName() {\n if (this.config.database) {\n this._databaseName = this.config.database;\n } else if (this.config.uri) {\n this._databaseName = this.config.uri.split(\"/\").pop()?.split(\"?\")?.[0];\n }\n }\n\n /**\n * Indicates whether the driver currently maintains an active connection.\n */\n public get isConnected(): boolean {\n return this.connected;\n }\n\n /**\n * Get the MongoDB database instance.\n *\n * @returns The MongoDB Db instance\n * @throws {Error} If not connected\n *\n * @example\n * ```typescript\n * const db = driver.getDatabase();\n * const collection = db.collection(\"users\");\n * ```\n */\n public getDatabase(): Db {\n if (!this.database) {\n throw new Error(\n \"Database not available. Ensure the driver is connected before accessing the database.\",\n );\n }\n return this.database;\n }\n\n /**\n * Get the ID generator instance for this driver.\n *\n * Creates a MongoIdGenerator on first access if autoGenerateId is enabled.\n *\n * @returns The ID generator instance, or undefined if disabled\n *\n * @example\n * ```typescript\n * const idGenerator = driver.getIdGenerator();\n * if (idGenerator) {\n * const id = await idGenerator.generateNextId({ table: \"users\" });\n * }\n * ```\n */\n public getIdGenerator(): IdGeneratorContract | undefined {\n // Return undefined if ID generation is disabled\n if (this.driverOptions?.autoGenerateId === false) {\n return undefined;\n }\n\n // Create ID generator lazily on first access\n if (!this.idGeneratorInstance) {\n this.idGeneratorInstance = new MongoIdGenerator(this, this.driverOptions?.counterCollection);\n }\n\n return this.idGeneratorInstance;\n }\n\n /**\n * Establish a MongoDB connection using the configured options.\n * Throws if the connection attempt fails.\n */\n public async connect(): Promise<void> {\n if (this.connected) {\n return;\n }\n\n await assertModuleIsLoaded();\n\n const uri = this.resolveUri();\n\n const { MongoClient, ObjectId: ObjectIdMongoDB } = MongoDBClient;\n\n ObjectId = ObjectIdMongoDB;\n\n const client = new MongoClient(uri, this.buildClientOptions());\n\n try {\n log.info(\n \"database.mongodb\",\n \"connection\",\n `Connecting to database ${colors.bold(colors.yellowBright(this.databaseName))}`,\n );\n await client.connect();\n this.client = client;\n this.database = client.db(this.databaseName);\n\n this.connected = true;\n log.success(\"database.mongodb\", \"connection\", \"Connected to database\");\n\n client.on(\"close\", () => {\n if (this.connected) {\n this.connected = false;\n this.emit(\"disconnected\");\n log.warn(\"database.mongodb\", \"connection\", \"Disconnected from database\");\n }\n });\n\n if (this.config.logging) {\n const ignoredCommands = [\"isMaster\", \"hello\", \"ping\", \"saslStart\", \"saslContinue\"];\n\n client.on(\"commandStarted\", (event: any) => {\n if (ignoredCommands.includes(event.commandName)) return;\n\n let cmdStr = JSON.stringify(event.command);\n if (cmdStr.length > 300) {\n cmdStr = cmdStr.substring(0, 300) + \"...\";\n }\n\n log.info({\n module: \"database.mongodb\",\n action: \"query.executing\",\n message: `[${event.commandName}] ${cmdStr}`,\n context: { command: event.command },\n });\n });\n\n client.on(\"commandSucceeded\", (event: any) => {\n if (ignoredCommands.includes(event.commandName)) return;\n\n log.success({\n module: \"database.mongodb\",\n action: \"query.executed\",\n message: `[${event.duration.toFixed(2)}ms] [${event.commandName}]`,\n });\n });\n\n client.on(\"commandFailed\", (event: any) => {\n if (ignoredCommands.includes(event.commandName)) return;\n\n log.error({\n module: \"database.mongodb\",\n action: \"query.error\",\n message: `[${event.duration.toFixed(2)}ms] [${event.commandName}]`,\n context: { failure: event.failure },\n });\n });\n }\n\n this.emit(\"connected\");\n } catch (error: any) {\n await client.close().catch(() => undefined);\n this.emit(\"disconnected\");\n // Boot-time database connection failure is unrecoverable in every\n // realistic caller (app boot, CLI migrations, workers) — `fatal` makes\n // \"page on fatal only\" alerting clean. Per-query failures stay at error.\n log.fatal(\n \"database.mongodb\",\n \"connection\",\n `Failed to connect to database: ${error.message}`,\n );\n throw error;\n }\n }\n\n /**\n * Close the underlying MongoDB connection.\n */\n public async disconnect(): Promise<void> {\n if (!this.client) {\n return;\n }\n\n try {\n await this.client.close();\n } finally {\n this.connected = false;\n this.emit(\"disconnected\");\n }\n }\n\n /**\n * Subscribe to driver lifecycle events.\n */\n public on(event: DriverEvent, listener: DriverEventListener): void {\n this.events.on(event, listener);\n }\n\n /**\n * Insert a single document into the given collection.\n */\n public async insert(\n table: string,\n document: Record<string, unknown>,\n options?: Record<string, unknown>,\n ): Promise<InsertResult> {\n const collection = this.getDatabaseInstance().collection(table);\n const mongoOptions = this.withSession<InsertOneOptions>(options);\n const result = await collection.insertOne(document, mongoOptions);\n\n return {\n document: {\n ...document,\n _id: result.insertedId,\n },\n };\n }\n\n /**\n * Insert multiple documents into the given collection.\n */\n public async insertMany(\n table: string,\n documents: Record<string, unknown>[],\n options?: Record<string, unknown>,\n ): Promise<InsertResult[]> {\n const collection = this.getDatabaseInstance().collection(table);\n const mongoOptions = this.withSession<BulkWriteOptions>(options);\n const result: InsertManyResult<Record<string, unknown>> = await collection.insertMany(\n documents,\n mongoOptions,\n );\n\n return documents.map((document, index) => {\n const insertedId = result.insertedIds[index as unknown as keyof typeof result.insertedIds];\n\n return {\n document: {\n ...document,\n _id: insertedId,\n },\n };\n });\n }\n\n /**\n * Update a single document that matches the provided filter.\n */\n public async update(\n table: string,\n filter: Record<string, unknown>,\n update: Record<string, unknown>,\n options?: Record<string, unknown>,\n ): Promise<UpdateResult> {\n const collection = this.getDatabaseInstance().collection(table);\n const mongoOptions = this.withSession<UpdateOptions>(options);\n const result = await collection.updateOne(\n filter,\n update as UpdateFilter<Record<string, unknown>>,\n mongoOptions,\n );\n\n return { modifiedCount: result.modifiedCount };\n }\n\n /**\n * Replace a single document that matches the provided filter.\n */\n public async replace<T = unknown>(\n table: string,\n filter: Record<string, unknown>,\n document: Record<string, unknown>,\n options?: Record<string, unknown>,\n ): Promise<T | null> {\n const collection = this.getDatabaseInstance().collection(table);\n const result = await collection.findOneAndReplace(filter, document as Record<string, unknown>);\n\n return result?.value as T | null;\n }\n\n /**\n * Find one and update a single document that matches the provided filter and return the updated document\n */\n public async findOneAndUpdate<T = unknown>(\n table: string,\n filter: Record<string, unknown>,\n update: UpdateOperations,\n options?: Record<string, unknown>,\n ): Promise<T | null> {\n const collection = this.getDatabaseInstance().collection(table);\n const mongoOptions = this.withSession<FindOneAndUpdateOptions>(options);\n const result = await collection.findOneAndUpdate(filter, update as Record<string, unknown>, {\n returnDocument: \"after\",\n ...mongoOptions,\n });\n\n return result as T | null;\n }\n\n /**\n * Upsert (insert or update) a single document.\n *\n * Uses MongoDB's findOneAndUpdate with upsert option.\n *\n * @param table - Target collection name\n * @param filter - Filter conditions to find existing document\n * @param document - Document data to insert or update\n * @param options - Optional upsert options\n * @returns The upserted document\n */\n public async upsert<T = unknown>(\n table: string,\n filter: Record<string, unknown>,\n document: Record<string, unknown>,\n options?: Record<string, unknown>,\n ): Promise<T> {\n const collection = this.getDatabaseInstance().collection(table);\n const mongoOptions = this.withSession<FindOneAndUpdateOptions>(options);\n\n // Use $set to update all fields from document\n const update = { $set: document };\n\n const result = await collection.findOneAndUpdate(filter, update, {\n upsert: true,\n returnDocument: \"after\",\n ...mongoOptions,\n });\n\n return result as T;\n }\n\n /**\n * Find one and delete a single document that matches the provided filter and return the deleted document.\n *\n * @param table - Target collection name\n * @param filter - Filter conditions\n * @param options - Optional delete options\n * @returns The deleted document or null if not found\n */\n public async findOneAndDelete<T = unknown>(\n table: string,\n filter: Record<string, unknown>,\n options?: Record<string, unknown>,\n ): Promise<T | null> {\n const collection = this.getDatabaseInstance().collection(table);\n const mongoOptions = this.withSession<FindOneAndDeleteOptions>(options);\n\n const result = await collection.findOneAndDelete(filter, mongoOptions || {});\n\n return result as T | null;\n }\n\n /**\n * Update multiple documents that match the provided filter.\n */\n public async updateMany(\n table: string,\n filter: Record<string, unknown>,\n update: UpdateOperations,\n options?: Record<string, unknown>,\n ): Promise<UpdateResult> {\n const collection = this.getDatabaseInstance().collection(table);\n const mongoOptions = this.withSession<UpdateOptions>(options);\n const result = await collection.updateMany(\n filter,\n update as UpdateFilter<Record<string, unknown>>,\n mongoOptions,\n );\n\n return { modifiedCount: result.modifiedCount };\n }\n\n /**\n * Delete a single document that matches the provided filter.\n */\n public async delete(\n table: string,\n filter: Record<string, unknown> = {},\n options?: Record<string, unknown>,\n ): Promise<number> {\n const collection = this.getDatabaseInstance().collection(table);\n const mongoOptions = this.withSession<DeleteOptions>(options);\n const result = await collection.deleteOne(filter, mongoOptions);\n\n return result.deletedCount > 0 ? 1 : 0;\n }\n\n /**\n * Delete documents that match the provided filter.\n */\n public async deleteMany(\n table: string,\n filter: Record<string, unknown> = {},\n options?: Record<string, unknown>,\n ): Promise<number> {\n const collection = this.getDatabaseInstance().collection(table);\n const mongoOptions = this.withSession<DeleteOptions>(options);\n\n const result = await collection.deleteMany(filter, mongoOptions);\n\n return result.deletedCount ?? 0;\n }\n\n /**\n * Remove all records from a collection.\n *\n * This uses deleteMany with an empty filter to remove all documents.\n * For very large collections, consider using the migration driver's\n * dropTable + createTable approach for better performance.\n */\n public async truncateTable(table: string, options?: Record<string, unknown>): Promise<number> {\n const collection = this.getDatabaseInstance().collection(table);\n const mongoOptions = this.withSession<DeleteOptions>(options);\n const result = await collection.deleteMany({}, mongoOptions);\n\n return result.deletedCount ?? 0;\n }\n\n /**\n * Serialize the given data\n */\n public serialize(data: Record<string, unknown>): Record<string, unknown> {\n const serialized: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(data)) {\n if (value === undefined) {\n continue; // Skip undefined values\n }\n\n if (value instanceof ObjectId) {\n serialized[key] = value.toString();\n } else if (value instanceof Date) {\n serialized[key] = value.toISOString();\n } else if (typeof value === \"bigint\") {\n serialized[key] = value.toString();\n } else if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n // Nested objects will be stored as JSONB\n serialized[key] = value;\n } else {\n serialized[key] = value;\n }\n }\n\n return serialized;\n }\n\n /**\n * Get the dirty tracker for this driver.\n */\n public getDirtyTracker(data: Record<string, unknown>): DatabaseDirtyTracker {\n return new DatabaseDirtyTracker(data);\n }\n\n /**\n * Deserialize the given data\n */\n public deserialize(data: Record<string, unknown>): Record<string, unknown> {\n if (data._id && typeof data._id === \"string\") {\n data._id = new ObjectId(data._id);\n }\n\n for (const [key, value] of Object.entries(data)) {\n // Only re-inflate strings — Mongo driver already returns Date objects from DB reads\n if (typeof value === \"string\" && isValidDateValue(value)) {\n data[key] = new Date(value);\n }\n }\n\n return data;\n }\n\n /**\n * Provide a Mongo-backed query builder instance for the given collection.\n */\n public queryBuilder<T = unknown>(table: string): QueryBuilderContract<T> {\n return new MongoQueryBuilder(table, dataSourceRegistry.get());\n }\n\n /**\n * Begin a MongoDB transaction, returning commit/rollback helpers.\n */\n public async beginTransaction(): Promise<DriverTransactionContract<ClientSession>> {\n const client = this.getClientInstance();\n const session = client.startSession();\n\n await session.startTransaction(this.transactionOptions);\n databaseTransactionContext.enter({ session });\n let finished = false;\n\n const finalize = async (operation: () => Promise<void>): Promise<void> => {\n if (finished) return;\n\n try {\n await operation();\n } finally {\n finished = true;\n databaseTransactionContext.exit();\n await session.endSession().catch(() => undefined);\n }\n };\n\n return {\n context: session,\n commit: async () => {\n await finalize(async () => {\n try {\n await session.commitTransaction();\n } catch (error) {\n await session.abortTransaction().catch(() => undefined);\n throw error;\n }\n });\n },\n rollback: async () => {\n await finalize(async () => {\n await session.abortTransaction();\n });\n },\n };\n }\n\n /**\n * Execute a function within a transaction scope (recommended pattern).\n *\n * Automatically commits on success, rolls back on any error, and guarantees\n * resource cleanup. This is the recommended way to use transactions.\n *\n * **MongoDB Requirements:**\n * - Requires MongoDB 4.0+ with replica set or sharded cluster\n * - Standalone MongoDB instances do not support transactions\n *\n * @param fn - Async function to execute within transaction\n * @param options - Transaction options (read preference, write concern, etc.)\n * @returns The return value of the callback function\n * @throws {Error} If transaction fails, is explicitly rolled back, or replica set not configured\n */\n public async transaction<T>(\n fn: (ctx: TransactionContext) => Promise<T>,\n options?: Record<string, unknown>,\n ): Promise<T> {\n const ctx: TransactionContext = {\n rollback(reason?: string): never {\n throw new TransactionRollbackError(reason);\n },\n };\n\n // Flat nesting: a transaction() called while one is already active JOINS it\n // instead of throwing (or opening a second, independent one). The outermost\n // transaction owns commit / abort; the inner block runs on the same session\n // so it sees the outer's writes — a service that opens its own transaction\n // then works both standalone and when called inside an outer transaction\n // (e.g. a seeder). Mirrors `PostgresDriver.transaction`.\n if (databaseTransactionContext.hasActiveTransaction()) {\n return fn(ctx);\n }\n\n // Check if MongoDB is running as a replica set (required for transactions)\n await this.ensureReplicaSetAvailable();\n\n const client = this.getClientInstance();\n const session = client.startSession();\n\n try {\n await session.startTransaction({\n ...this.transactionOptions,\n ...(options as TransactionOptions),\n });\n\n // Set transaction context for queries within callback\n databaseTransactionContext.enter({ session });\n\n try {\n // Execute callback\n const result = await fn(ctx);\n\n // Auto-commit on success\n await session.commitTransaction();\n\n return result;\n } catch (error) {\n // Auto-rollback on any error (including explicit rollback)\n await session.abortTransaction().catch(() => undefined);\n throw error;\n } finally {\n // Guaranteed context cleanup\n databaseTransactionContext.exit();\n }\n } finally {\n // Guaranteed session cleanup\n await session.endSession().catch(() => undefined);\n }\n }\n\n /**\n * Execute atomic operations (typically $inc/$set style updates) against documents.\n *\n * Uses `updateMany` so callers can atomically modify any set of documents.\n */\n public async atomic(\n table: string,\n filter: Record<string, unknown>,\n operations: Record<string, unknown>,\n options?: Record<string, unknown>,\n ): Promise<UpdateResult> {\n const collection = this.getDatabaseInstance().collection(table);\n const mongoOptions = this.withSession<UpdateOptions>(options);\n const result = await collection.updateMany(\n filter,\n operations as UpdateFilter<Record<string, unknown>>,\n mongoOptions,\n );\n\n return { modifiedCount: result.modifiedCount };\n }\n\n /**\n * Lazily create (and cache) the Mongo sync adapter.\n * The adapter uses this driver instance to ensure all operations\n * participate in active transactions via the session context.\n */\n public syncAdapter(): SyncAdapterContract {\n if (!this.syncAdapterInstance) {\n this.syncAdapterInstance = new MongoSyncAdapter(this);\n }\n\n return this.syncAdapterInstance;\n }\n\n /**\n * Lazily create (and cache) the Mongo migration driver.\n * The migration driver handles schema operations like indexes, collections, etc.\n */\n public migrationDriver(): MigrationDriverContract {\n if (!this.migrationDriverInstance) {\n this.migrationDriverInstance = new MongoMigrationDriver(this);\n }\n\n return this.migrationDriverInstance!;\n }\n\n /**\n * Expose the underlying Mongo client for advanced consumers.\n */\n public getClient<Client = MongoClient>(): Client {\n return this.getClientInstance() as Client;\n }\n\n /**\n * Retrieve the active Mongo client, throwing if the driver is disconnected.\n */\n private getClientInstance(): MongoClient {\n if (!this.client) {\n throw new Error(\"Mongo driver is not connected.\");\n }\n\n return this.client;\n }\n\n /**\n * Retrieve the active Mongo database, throwing if the driver is disconnected.\n * @private\n */\n private getDatabaseInstance(): Db {\n if (!this.database) {\n throw new Error(\"Mongo driver is not connected to a database.\");\n }\n\n return this.database;\n }\n\n /**\n * Resolve the Mongo connection string based on provided options.\n */\n private resolveUri(): string {\n if (this.config.uri) {\n return this.config.uri;\n }\n\n const host = this.config.host ?? \"localhost\";\n const port = this.config.port ?? 27017;\n\n return `mongodb://${host}:${port}`;\n }\n\n /**\n * Build the Mongo client options derived from the driver configuration.\n */\n private buildClientOptions(): MongoClientOptions {\n const baseOptions: MongoClientOptions = {\n ...(this.config.clientOptions ?? {}),\n };\n\n if (this.config.logging) {\n baseOptions.monitorCommands = true;\n }\n\n if (this.config.username && !baseOptions.auth) {\n baseOptions.auth = {\n username: this.config.username,\n password: this.config.password,\n };\n }\n\n if (this.config.authSource && !baseOptions.authSource) {\n baseOptions.authSource = this.config.authSource;\n }\n\n return baseOptions;\n }\n\n /**\n * Emit a driver lifecycle event.\n */\n private emit(event: DriverEvent, ...args: unknown[]): void {\n this.events.emit(event, ...args);\n }\n\n /**\n * Ensure MongoDB is running as a replica set (required for transactions).\n *\n * @throws {Error} If MongoDB is running as a standalone instance\n */\n private async ensureReplicaSetAvailable(): Promise<void> {\n try {\n const admin = this.database!.admin();\n const status = await admin.serverStatus();\n\n if (!status.repl) {\n throw new Error(\n \"MongoDB transactions require a replica set or sharded cluster. \" +\n \"Standalone MongoDB instances do not support transactions.\\n\\n\" +\n \"For local development:\\n\" +\n \" - Run MongoDB with --replSet flag: mongod --replSet rs0\\n\" +\n \" - Or use Docker with replica set configuration\\n\" +\n \" - Or use MongoDB Atlas (cloud) which provides replica sets by default\",\n );\n }\n } catch (error: any) {\n if (error.message?.includes(\"replica set\")) {\n throw error;\n }\n throw new Error(`Failed to check MongoDB replica set status: ${error.message}`);\n }\n }\n\n /**\n * Attach the active transaction session (when available) to Mongo options.\n */\n private withSession<TOptions extends { session?: ClientSession }>(\n options?: Record<string, unknown>,\n ): TOptions | undefined {\n const session = databaseTransactionContext.getSession<ClientSession>();\n\n if (!session) {\n return options as TOptions | undefined;\n }\n\n const baseOptions = options ? ({ ...options } as TOptions) : ({} as TOptions);\n\n baseOptions.session = session;\n\n return baseOptions;\n }\n\n // ============================================================\n // SQL Compatibility Operations (Not supported in MongoDB)\n // ============================================================\n\n /**\n * Return a SQL serializer for this driver's dialect.\n * Not supported for MongoDB.\n */\n public getSQLSerializer(): SQLSerializer {\n throw new Error(\"MongoDB driver does not support SQL serialization.\");\n }\n\n /**\n * Execute a raw SQL query.\n * Not supported for MongoDB.\n */\n public async query<T = Record<string, unknown>>(\n _sql: string,\n _params?: unknown[],\n ): Promise<RawQueryResult<T>> {\n throw new Error(\"MongoDB driver does not support raw SQL queries.\");\n }\n\n // ============================================================\n // Database Lifecycle Operations\n // ============================================================\n\n /**\n * Create a new database.\n *\n * In MongoDB, databases are created automatically when data is first written.\n * This method creates an empty collection to ensure the database exists.\n *\n * @param name - Database name to create\n * @returns true if created, false if already exists\n */\n public async createDatabase(name: string): Promise<boolean> {\n const client = this.getClientInstance();\n\n // Check if database already exists\n if (await this.databaseExists(name)) {\n return false;\n }\n\n try {\n // MongoDB creates databases on first write, so create a system collection\n const db = client.db(name);\n await db.createCollection(\"__init__\");\n // Drop the temp collection\n await db.collection(\"__init__\").drop();\n\n log.success(\"database\", \"lifecycle\", `Created database ${name}`);\n return true;\n } catch (error) {\n log.error(\"database\", \"lifecycle\", `Failed to create database ${name}: ${error}`);\n throw error;\n }\n }\n\n /**\n * Drop a database.\n *\n * @param name - Database name to drop\n * @returns true if dropped, false if didn't exist\n */\n public async dropDatabase(name: string): Promise<boolean> {\n const client = this.getClientInstance();\n\n // Check if database exists\n if (!(await this.databaseExists(name))) {\n return false;\n }\n\n try {\n await client.db(name).dropDatabase();\n log.success(\"database\", \"lifecycle\", `Dropped database ${name}`);\n return true;\n } catch (error) {\n log.error(\"database\", \"lifecycle\", `Failed to drop database ${name}: ${error}`);\n throw error;\n }\n }\n\n /**\n * Check if a database exists.\n *\n * @param name - Database name to check\n * @returns true if database exists\n */\n public async databaseExists(name: string): Promise<boolean> {\n const client = this.getClientInstance();\n\n const result = await client.db(\"admin\").admin().listDatabases();\n return result.databases.some((db) => db.name === name);\n }\n\n /**\n * List all databases.\n *\n * @returns Array of database names\n */\n public async listDatabases(): Promise<string[]> {\n const client = this.getClientInstance();\n\n const result = await client.db(\"admin\").admin().listDatabases();\n return result.databases\n .map((db) => db.name)\n .filter((name) => ![\"admin\", \"local\", \"config\"].includes(name));\n }\n\n // ============================================================\n // Table/Collection Management Operations\n // ============================================================\n\n /**\n * Drop a collection.\n *\n * @param name - Collection name to drop\n * @throws Error if collection doesn't exist\n */\n public async dropTable(name: string): Promise<void> {\n const db = this.getDatabaseInstance();\n await db.collection(name).drop();\n log.success(\"database\", \"collection\", `Dropped collection ${name}`);\n }\n\n /**\n * Drop a collection if it exists.\n *\n * @param name - Collection name to drop\n */\n public async dropTableIfExists(name: string): Promise<void> {\n if (await this.blueprint.tableExists(name)) {\n await this.dropTable(name);\n }\n }\n\n /**\n * Drop all collections in the current database.\n *\n * Useful for `migrate:fresh` command.\n */\n public async dropAllTables(): Promise<void> {\n const collections = await this.blueprint.listTables();\n\n if (collections.length === 0) {\n return;\n }\n\n const db = this.getDatabaseInstance();\n\n for (const collection of collections) {\n await db.collection(collection).drop();\n }\n\n log.success(\"database\", \"collection\", `Dropped ${collections.length} collections`);\n }\n}\n"],"mappings":"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"}
|
|
@@ -90,12 +90,21 @@ declare class PostgresDriver implements DriverContract {
|
|
|
90
90
|
*/
|
|
91
91
|
private _syncAdapter;
|
|
92
92
|
/**
|
|
93
|
-
*
|
|
94
|
-
* (`JSONB[]`, `TEXT[]`, …) and must NOT be JSON-text
|
|
93
|
+
* Explicit, table-agnostic override list of column names that hold native
|
|
94
|
+
* PostgreSQL arrays (`JSONB[]`, `TEXT[]`, …) and must NOT be JSON-text
|
|
95
|
+
* encoded. Merged with (and superseded per-table by) the schema
|
|
96
|
+
* introspection below; kept as a manual escape hatch.
|
|
95
97
|
*
|
|
96
98
|
* @see PostgresPoolConfig.nativeArrayColumns
|
|
97
99
|
*/
|
|
98
100
|
private readonly _nativeArrayColumns;
|
|
101
|
+
/**
|
|
102
|
+
* Native-array columns discovered by introspecting the live schema on
|
|
103
|
+
* connect, keyed `table → { column, … }`. Authoritative and table-scoped, so
|
|
104
|
+
* a column that is `TEXT[]` in one table and `jsonb` in another is encoded
|
|
105
|
+
* correctly for each — no app configuration required.
|
|
106
|
+
*/
|
|
107
|
+
private _introspectedArrayColumns;
|
|
99
108
|
/**
|
|
100
109
|
* Create a new PostgreSQL driver instance.
|
|
101
110
|
*
|
|
@@ -148,9 +157,11 @@ declare class PostgresDriver implements DriverContract {
|
|
|
148
157
|
* that need special handling for PostgreSQL storage.
|
|
149
158
|
*
|
|
150
159
|
* @param data - The data object to serialize
|
|
160
|
+
* @param table - Optional table name; when given, columns introspected as
|
|
161
|
+
* native arrays on that table are bound raw (see {@link serializeValue}).
|
|
151
162
|
* @returns Serialized data ready for PostgreSQL
|
|
152
163
|
*/
|
|
153
|
-
serialize(data: Record<string, unknown
|
|
164
|
+
serialize(data: Record<string, unknown>, table?: string): Record<string, unknown>;
|
|
154
165
|
/**
|
|
155
166
|
* Serialize a single column value into a node-pg bindable parameter.
|
|
156
167
|
*
|
|
@@ -167,25 +178,54 @@ declare class PostgresDriver implements DriverContract {
|
|
|
167
178
|
* `JSON.stringify`. node-pg renders a raw JS array as a PostgreSQL array
|
|
168
179
|
* literal `{...}` (and `[]` as `{}`), which a `json` / `jsonb` column
|
|
169
180
|
* rejects — so we bind the value as JSON text instead, the form those
|
|
170
|
-
* columns accept. Columns
|
|
171
|
-
*
|
|
172
|
-
*
|
|
181
|
+
* columns accept. Columns known to be native arrays — via schema
|
|
182
|
+
* introspection or the `nativeArrayColumns` config — are exempt: their raw
|
|
183
|
+
* array is passed through so node-pg emits the `{...}` literal a genuine
|
|
184
|
+
* `JSONB[]` / `TEXT[]` column needs.
|
|
173
185
|
* - plain object → `JSON.stringify`. Equivalent to node-pg's own object
|
|
174
186
|
* handling, made explicit so both write paths agree.
|
|
175
187
|
* - everything else (scalars: string, number, boolean, null) → untouched.
|
|
176
188
|
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
189
|
+
* Distinguishing native-array from `json` / `jsonb` columns: a value alone
|
|
190
|
+
* can't tell them apart, so the driver introspects the live schema on connect
|
|
191
|
+
* (see {@link loadNativeArrayColumns}) and consults that per-table map here
|
|
192
|
+
* via {@link isNativeArrayColumn}. The explicit `nativeArrayColumns` config
|
|
193
|
+
* still works as a table-agnostic override. No `::jsonb` placeholder cast is
|
|
194
|
+
* added: a JSON-text string binds correctly to `json` / `jsonb` without one,
|
|
195
|
+
* and a blind cast would misfire on columns we cannot positively identify as
|
|
196
|
+
* jsonb.
|
|
183
197
|
*
|
|
184
|
-
* @param key - Column name (used to
|
|
198
|
+
* @param key - Column name (used to resolve native-array columns)
|
|
185
199
|
* @param value - The raw value to serialize (never `undefined`)
|
|
200
|
+
* @param table - Optional table name; enables the per-table native-array lookup
|
|
186
201
|
* @returns The value ready to bind as a query parameter
|
|
187
202
|
*/
|
|
188
203
|
private serializeValue;
|
|
204
|
+
/**
|
|
205
|
+
* Whether `column` on `table` is a native PostgreSQL array. True when the
|
|
206
|
+
* connect-time schema introspection saw it as `data_type = 'ARRAY'` for that
|
|
207
|
+
* table (authoritative, per-table), or when it's listed in the table-agnostic
|
|
208
|
+
* `nativeArrayColumns` config override.
|
|
209
|
+
*/
|
|
210
|
+
private isNativeArrayColumn;
|
|
211
|
+
/**
|
|
212
|
+
* Introspect the live schema for native-array columns so array values bind
|
|
213
|
+
* correctly with zero app configuration.
|
|
214
|
+
*
|
|
215
|
+
* A JS array must be bound two opposite ways depending on the column: as JSON
|
|
216
|
+
* text for a `json` / `jsonb` column, but as a raw array (which node-pg
|
|
217
|
+
* renders `{...}`) for a native `TEXT[]` / `JSONB[]` / `INTEGER[]` column. The
|
|
218
|
+
* serializer sees values, not types, so without this it JSON-stringifies
|
|
219
|
+
* every array — which a native-array column rejects with "malformed array
|
|
220
|
+
* literal". One `information_schema` query at connect, cached for the
|
|
221
|
+
* connection lifetime, removes the need to hand-list `nativeArrayColumns`.
|
|
222
|
+
*
|
|
223
|
+
* Best-effort: any failure (e.g. restricted catalog access) is logged and
|
|
224
|
+
* leaves the map empty so the config override still applies — it never blocks
|
|
225
|
+
* connect. A schema change made within a live connection isn't reflected
|
|
226
|
+
* until the next connect.
|
|
227
|
+
*/
|
|
228
|
+
private loadNativeArrayColumns;
|
|
189
229
|
/**
|
|
190
230
|
* Get the dirty tracker for this driver.
|
|
191
231
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"postgres-driver.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/cascade/src/drivers/postgres/postgres-driver.ts"],"mappings":";;;;;;;;;;;;;AAgD2C;AA0D3C;;AA1D2C,KADtC,MAAA,gBAAsB,IAAI;AAAA,KAC1B,YAAA,gBAA4B,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA0D9B,cAAA,YAA0B,cAAA;EAAA,
|
|
1
|
+
{"version":3,"file":"postgres-driver.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/cascade/src/drivers/postgres/postgres-driver.ts"],"mappings":"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"}
|
|
@@ -125,13 +125,22 @@ var PostgresDriver = class {
|
|
|
125
125
|
*/
|
|
126
126
|
_syncAdapter;
|
|
127
127
|
/**
|
|
128
|
-
*
|
|
129
|
-
* (`JSONB[]`, `TEXT[]`, …) and must NOT be JSON-text
|
|
128
|
+
* Explicit, table-agnostic override list of column names that hold native
|
|
129
|
+
* PostgreSQL arrays (`JSONB[]`, `TEXT[]`, …) and must NOT be JSON-text
|
|
130
|
+
* encoded. Merged with (and superseded per-table by) the schema
|
|
131
|
+
* introspection below; kept as a manual escape hatch.
|
|
130
132
|
*
|
|
131
133
|
* @see PostgresPoolConfig.nativeArrayColumns
|
|
132
134
|
*/
|
|
133
135
|
_nativeArrayColumns;
|
|
134
136
|
/**
|
|
137
|
+
* Native-array columns discovered by introspecting the live schema on
|
|
138
|
+
* connect, keyed `table → { column, … }`. Authoritative and table-scoped, so
|
|
139
|
+
* a column that is `TEXT[]` in one table and `jsonb` in another is encoded
|
|
140
|
+
* correctly for each — no app configuration required.
|
|
141
|
+
*/
|
|
142
|
+
_introspectedArrayColumns = /* @__PURE__ */ new Map();
|
|
143
|
+
/**
|
|
135
144
|
* Create a new PostgreSQL driver instance.
|
|
136
145
|
*
|
|
137
146
|
* @param config - PostgreSQL connection configuration
|
|
@@ -197,6 +206,7 @@ var PostgresDriver = class {
|
|
|
197
206
|
(await this._pool.connect()).release();
|
|
198
207
|
log.success("database.postgres", "connection", `Connected to database ${colors.bold(colors.yellowBright(this.config.database))}`);
|
|
199
208
|
this._isConnected = true;
|
|
209
|
+
await this.loadNativeArrayColumns();
|
|
200
210
|
this.emit("connected");
|
|
201
211
|
} catch (error) {
|
|
202
212
|
log.fatal("database.postgres", "connection", "Failed to connect to database");
|
|
@@ -233,13 +243,15 @@ var PostgresDriver = class {
|
|
|
233
243
|
* that need special handling for PostgreSQL storage.
|
|
234
244
|
*
|
|
235
245
|
* @param data - The data object to serialize
|
|
246
|
+
* @param table - Optional table name; when given, columns introspected as
|
|
247
|
+
* native arrays on that table are bound raw (see {@link serializeValue}).
|
|
236
248
|
* @returns Serialized data ready for PostgreSQL
|
|
237
249
|
*/
|
|
238
|
-
serialize(data) {
|
|
250
|
+
serialize(data, table) {
|
|
239
251
|
const serialized = {};
|
|
240
252
|
for (const [key, value] of Object.entries(data)) {
|
|
241
253
|
if (value === void 0) continue;
|
|
242
|
-
serialized[key] = this.serializeValue(key, value);
|
|
254
|
+
serialized[key] = this.serializeValue(key, value, table);
|
|
243
255
|
}
|
|
244
256
|
return serialized;
|
|
245
257
|
}
|
|
@@ -259,36 +271,87 @@ var PostgresDriver = class {
|
|
|
259
271
|
* `JSON.stringify`. node-pg renders a raw JS array as a PostgreSQL array
|
|
260
272
|
* literal `{...}` (and `[]` as `{}`), which a `json` / `jsonb` column
|
|
261
273
|
* rejects — so we bind the value as JSON text instead, the form those
|
|
262
|
-
* columns accept. Columns
|
|
263
|
-
*
|
|
264
|
-
*
|
|
274
|
+
* columns accept. Columns known to be native arrays — via schema
|
|
275
|
+
* introspection or the `nativeArrayColumns` config — are exempt: their raw
|
|
276
|
+
* array is passed through so node-pg emits the `{...}` literal a genuine
|
|
277
|
+
* `JSONB[]` / `TEXT[]` column needs.
|
|
265
278
|
* - plain object → `JSON.stringify`. Equivalent to node-pg's own object
|
|
266
279
|
* handling, made explicit so both write paths agree.
|
|
267
280
|
* - everything else (scalars: string, number, boolean, null) → untouched.
|
|
268
281
|
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
282
|
+
* Distinguishing native-array from `json` / `jsonb` columns: a value alone
|
|
283
|
+
* can't tell them apart, so the driver introspects the live schema on connect
|
|
284
|
+
* (see {@link loadNativeArrayColumns}) and consults that per-table map here
|
|
285
|
+
* via {@link isNativeArrayColumn}. The explicit `nativeArrayColumns` config
|
|
286
|
+
* still works as a table-agnostic override. No `::jsonb` placeholder cast is
|
|
287
|
+
* added: a JSON-text string binds correctly to `json` / `jsonb` without one,
|
|
288
|
+
* and a blind cast would misfire on columns we cannot positively identify as
|
|
289
|
+
* jsonb.
|
|
275
290
|
*
|
|
276
|
-
* @param key - Column name (used to
|
|
291
|
+
* @param key - Column name (used to resolve native-array columns)
|
|
277
292
|
* @param value - The raw value to serialize (never `undefined`)
|
|
293
|
+
* @param table - Optional table name; enables the per-table native-array lookup
|
|
278
294
|
* @returns The value ready to bind as a query parameter
|
|
279
295
|
*/
|
|
280
|
-
serializeValue(key, value) {
|
|
296
|
+
serializeValue(key, value, table) {
|
|
281
297
|
if (value instanceof Date) return value.toISOString();
|
|
282
298
|
if (typeof value === "bigint") return value.toString();
|
|
283
299
|
if (Array.isArray(value)) {
|
|
284
300
|
if (value.length > 0 && value.every((v) => typeof v === "number")) return `[${value.join(",")}]`;
|
|
285
|
-
if (this.
|
|
301
|
+
if (this.isNativeArrayColumn(table, key)) return value;
|
|
286
302
|
return JSON.stringify(value);
|
|
287
303
|
}
|
|
288
304
|
if (typeof value === "object" && value !== null) return JSON.stringify(value);
|
|
289
305
|
return value;
|
|
290
306
|
}
|
|
291
307
|
/**
|
|
308
|
+
* Whether `column` on `table` is a native PostgreSQL array. True when the
|
|
309
|
+
* connect-time schema introspection saw it as `data_type = 'ARRAY'` for that
|
|
310
|
+
* table (authoritative, per-table), or when it's listed in the table-agnostic
|
|
311
|
+
* `nativeArrayColumns` config override.
|
|
312
|
+
*/
|
|
313
|
+
isNativeArrayColumn(table, column) {
|
|
314
|
+
if (table && this._introspectedArrayColumns.get(table)?.has(column)) return true;
|
|
315
|
+
return this._nativeArrayColumns.has(column);
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Introspect the live schema for native-array columns so array values bind
|
|
319
|
+
* correctly with zero app configuration.
|
|
320
|
+
*
|
|
321
|
+
* A JS array must be bound two opposite ways depending on the column: as JSON
|
|
322
|
+
* text for a `json` / `jsonb` column, but as a raw array (which node-pg
|
|
323
|
+
* renders `{...}`) for a native `TEXT[]` / `JSONB[]` / `INTEGER[]` column. The
|
|
324
|
+
* serializer sees values, not types, so without this it JSON-stringifies
|
|
325
|
+
* every array — which a native-array column rejects with "malformed array
|
|
326
|
+
* literal". One `information_schema` query at connect, cached for the
|
|
327
|
+
* connection lifetime, removes the need to hand-list `nativeArrayColumns`.
|
|
328
|
+
*
|
|
329
|
+
* Best-effort: any failure (e.g. restricted catalog access) is logged and
|
|
330
|
+
* leaves the map empty so the config override still applies — it never blocks
|
|
331
|
+
* connect. A schema change made within a live connection isn't reflected
|
|
332
|
+
* until the next connect.
|
|
333
|
+
*/
|
|
334
|
+
async loadNativeArrayColumns() {
|
|
335
|
+
try {
|
|
336
|
+
const result = await this.query(`SELECT table_name, column_name
|
|
337
|
+
FROM information_schema.columns
|
|
338
|
+
WHERE table_schema = ANY (current_schemas(false))
|
|
339
|
+
AND data_type = 'ARRAY'`);
|
|
340
|
+
const map = /* @__PURE__ */ new Map();
|
|
341
|
+
for (const { table_name, column_name } of result.rows) {
|
|
342
|
+
let columns = map.get(table_name);
|
|
343
|
+
if (!columns) {
|
|
344
|
+
columns = /* @__PURE__ */ new Set();
|
|
345
|
+
map.set(table_name, columns);
|
|
346
|
+
}
|
|
347
|
+
columns.add(column_name);
|
|
348
|
+
}
|
|
349
|
+
this._introspectedArrayColumns = map;
|
|
350
|
+
} catch {
|
|
351
|
+
log.warn("database.postgres", "introspection", "Could not introspect native-array columns; using the nativeArrayColumns config only");
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
292
355
|
* Get the dirty tracker for this driver.
|
|
293
356
|
*/
|
|
294
357
|
getDirtyTracker(data) {
|
|
@@ -337,7 +400,7 @@ var PostgresDriver = class {
|
|
|
337
400
|
* @returns The inserted document
|
|
338
401
|
*/
|
|
339
402
|
async insert(table, document, _options) {
|
|
340
|
-
const serialized = this.serialize(document);
|
|
403
|
+
const serialized = this.serialize(document, table);
|
|
341
404
|
const filteredData = Object.fromEntries(Object.entries(serialized).filter(([key, value]) => {
|
|
342
405
|
if (key === "id" && (value === null || value === void 0)) return false;
|
|
343
406
|
return true;
|
|
@@ -364,7 +427,7 @@ var PostgresDriver = class {
|
|
|
364
427
|
if (documents.length === 0) return [];
|
|
365
428
|
const allColumns = /* @__PURE__ */ new Set();
|
|
366
429
|
for (const doc of documents) {
|
|
367
|
-
const serialized = this.serialize(doc);
|
|
430
|
+
const serialized = this.serialize(doc, table);
|
|
368
431
|
Object.keys(serialized).forEach((key) => allColumns.add(key));
|
|
369
432
|
}
|
|
370
433
|
const columns = Array.from(allColumns);
|
|
@@ -374,7 +437,7 @@ var PostgresDriver = class {
|
|
|
374
437
|
const params = [];
|
|
375
438
|
let paramIndex = 1;
|
|
376
439
|
for (const doc of documents) {
|
|
377
|
-
const serialized = this.serialize(doc);
|
|
440
|
+
const serialized = this.serialize(doc, table);
|
|
378
441
|
const rowPlaceholders = [];
|
|
379
442
|
for (const col of columns) if (col in serialized) {
|
|
380
443
|
rowPlaceholders.push(this.dialect.placeholder(paramIndex++));
|
|
@@ -441,7 +504,7 @@ var PostgresDriver = class {
|
|
|
441
504
|
* @returns The replaced document or null
|
|
442
505
|
*/
|
|
443
506
|
async replace(table, filter, document, _options) {
|
|
444
|
-
const serialized = this.serialize(document);
|
|
507
|
+
const serialized = this.serialize(document, table);
|
|
445
508
|
const columns = Object.keys(serialized);
|
|
446
509
|
const values = Object.values(serialized);
|
|
447
510
|
const quotedTable = this.dialect.quoteIdentifier(table);
|
|
@@ -463,7 +526,7 @@ var PostgresDriver = class {
|
|
|
463
526
|
* @returns The upserted row
|
|
464
527
|
*/
|
|
465
528
|
async upsert(table, filter, document, options) {
|
|
466
|
-
const serialized = this.serialize(document);
|
|
529
|
+
const serialized = this.serialize(document, table);
|
|
467
530
|
const columns = Object.keys(serialized);
|
|
468
531
|
const values = Object.values(serialized);
|
|
469
532
|
if (columns.length === 0) throw new Error("Cannot upsert empty document");
|
|
@@ -589,13 +652,14 @@ var PostgresDriver = class {
|
|
|
589
652
|
* @throws {Error} If transaction fails or is explicitly rolled back
|
|
590
653
|
*/
|
|
591
654
|
async transaction(fn, options) {
|
|
592
|
-
|
|
655
|
+
const ctx = { rollback(reason) {
|
|
656
|
+
throw new TransactionRollbackError(reason);
|
|
657
|
+
} };
|
|
658
|
+
if (databaseTransactionContext.hasActiveTransaction()) return fn(ctx);
|
|
593
659
|
const tx = await this.beginTransaction(options);
|
|
594
660
|
databaseTransactionContext.enter({ session: tx.context });
|
|
595
661
|
try {
|
|
596
|
-
const result = await fn(
|
|
597
|
-
throw new TransactionRollbackError(reason);
|
|
598
|
-
} });
|
|
662
|
+
const result = await fn(ctx);
|
|
599
663
|
await tx.commit();
|
|
600
664
|
return result;
|
|
601
665
|
} catch (error) {
|
|
@@ -757,7 +821,7 @@ var PostgresDriver = class {
|
|
|
757
821
|
let paramIndex = 1;
|
|
758
822
|
if (update.$set) for (const [key, value] of Object.entries(update.$set)) {
|
|
759
823
|
setClauses.push(`${this.dialect.quoteIdentifier(key)} = ${this.dialect.placeholder(paramIndex++)}`);
|
|
760
|
-
params.push(value === void 0 ? value : this.serializeValue(key, value));
|
|
824
|
+
params.push(value === void 0 ? value : this.serializeValue(key, value, table));
|
|
761
825
|
}
|
|
762
826
|
if (update.$unset) for (const key of Object.keys(update.$unset)) setClauses.push(`${this.dialect.quoteIdentifier(key)} = NULL`);
|
|
763
827
|
if (update.$inc) for (const [key, amount] of Object.entries(update.$inc)) {
|