{
  "version": 3,
  "sources": ["../../src/Base.ts", "../../src/Wrapper.ts"],
  "sourcesContent": ["import { assertEx } from '@xylabs/assert'\nimport type {\n  BulkWriteOptions,\n  Collection,\n  DeleteResult,\n  Document,\n  Filter,\n  FindCursor,\n  InsertOneOptions,\n  MongoClient,\n  OptionalUnlessRequiredId,\n  ReplaceOptions,\n  UpdateFilter,\n  WithId,\n} from 'mongodb'\n\nimport type { BaseMongoSdkConfig } from './Config.js'\nimport { MongoClientWrapper } from './Wrapper.js'\n\n/** Provides a typed wrapper around common MongoDB collection operations. */\nexport class BaseMongoSdk<T extends Document> {\n  /** The MongoDB SDK configuration for this instance. */\n  config: BaseMongoSdkConfig\n\n  constructor(config: BaseMongoSdkConfig) {\n    this.config = config\n  }\n\n  /** Returns the MongoDB connection URI, either from the config or constructed from URL-encoded credential fields. */\n  get uri() {\n    if (this.config.dbConnectionString) {\n      return this.config.dbConnectionString\n    }\n    const user = encodeURIComponent(this.config.dbUserName ?? '')\n    const password = encodeURIComponent(this.config.dbPassword ?? '')\n    return `mongodb+srv://${user}:${password}@${this.config.dbDomain}.mongodb.net/${this.config.dbName}?retryWrites=true&w=majority`\n  }\n\n  /**\n   * Deletes all documents matching the filter.\n   * @param filter - The query filter to match documents for deletion\n   */\n  async deleteMany(filter: Filter<T>) {\n    return await this.useCollection<DeleteResult>(async (collection: Collection<T>) => {\n      return await collection.deleteMany(filter)\n    })\n  }\n\n  /**\n   * Deletes the first document matching the filter.\n   * @param filter - The query filter to match a document for deletion\n   */\n  async deleteOne(filter: Filter<T>) {\n    return await this.useCollection<DeleteResult>(async (collection: Collection<T>) => {\n      return await collection.deleteOne(filter)\n    })\n  }\n\n  /**\n   * Finds all documents matching the filter and returns a cursor.\n   * @param filter - The query filter\n   */\n  async find(filter: Filter<T>) {\n    return await this.useCollection<FindCursor<WithId<T>>>((collection: Collection<T>) => {\n      return collection.find(filter)\n    })\n  }\n\n  /**\n   * Finds a single document matching the filter.\n   * @param filter - The query filter\n   * @returns The matched document, or `null` if not found\n   */\n  async findOne(filter: Filter<T>) {\n    return await this.useCollection<WithId<T> | null>(async (collection: Collection<T>) => {\n      return await collection.findOne(filter)\n    })\n  }\n\n  /**\n   * Inserts multiple documents into the collection.\n   * @param items - The documents to insert\n   * @param options - Optional bulk write options\n   */\n  async insertMany(items: OptionalUnlessRequiredId<T>[], options?: BulkWriteOptions) {\n    return await this.useCollection(async (collection: Collection<T>) => {\n      return await collection.insertMany(items, options)\n    })\n  }\n\n  /**\n   * Inserts a single document into the collection.\n   * @param item - The document to insert\n   * @param options - Optional insert options\n   */\n  async insertOne(item: OptionalUnlessRequiredId<T>, options?: InsertOneOptions) {\n    return await this.useCollection(async (collection: Collection<T>) => {\n      return await collection.insertOne(item, options)\n    })\n  }\n\n  /**\n   * Replaces a single document matching the filter.\n   * @param filter - The query filter to match the document\n   * @param item - The replacement document\n   * @param options - Optional replace options\n   */\n  async replaceOne(filter: Filter<T>, item: OptionalUnlessRequiredId<T>, options?: ReplaceOptions) {\n    return await this.useCollection(async (collection: Collection<T>) => {\n      return await collection.replaceOne(filter, item, options)\n    })\n  }\n\n  /**\n   * Updates a single document matching the filter without upserting.\n   * @param filter - The query filter to match the document\n   * @param fields - The update operations to apply\n   */\n  async updateOne(filter: Filter<T>, fields: UpdateFilter<T>) {\n    return await this.useCollection(async (collection: Collection<T>) => {\n      return await collection.updateOne(filter, fields, { upsert: false })\n    })\n  }\n\n  /**\n   * Updates a single document matching the filter, inserting it if it does not exist.\n   * @param filter - The query filter to match the document\n   * @param fields - The update operations to apply\n   */\n  async upsertOne(filter: Filter<T>, fields: UpdateFilter<T>) {\n    return await this.useCollection(async (collection: Collection<T>) => {\n      return await collection.updateOne(filter, fields, { upsert: true })\n    })\n  }\n\n  /**\n   * Executes a callback with access to the configured MongoDB collection.\n   * @param func - A callback receiving the typed collection\n   * @returns The result of the callback\n   */\n  async useCollection<R>(func: (collection: Collection<T>) => Promise<R> | R) {\n    return await this.useMongo<R>(async (client: MongoClient) => {\n      return await func(client.db(this.config.dbName).collection<T>(this.config.collection))\n    })\n  }\n\n  /**\n   * Executes a callback with a connected MongoClient, handling connection and disconnection.\n   * @param func - A callback receiving the connected MongoClient\n   * @returns The result of the callback\n   */\n  async useMongo<R>(func: (client: MongoClient) => Promise<R> | R) {\n    const wrapper = MongoClientWrapper.get(this.uri, this.config.maxPoolSize, this.config.closeDelay)\n    const connection = await wrapper.connect()\n    assertEx(connection, () => 'Connection failed')\n    try {\n      return await func(connection)\n    } finally {\n      await wrapper.disconnect()\n    }\n  }\n}\n", "import { MongoClient } from 'mongodb'\n\n/** Manages a shared pool of MongoClient instances, reusing connections by URI and pool options. */\nexport class MongoClientWrapper {\n  /** Global cache of wrapper instances keyed by URI and pool options. */\n  static readonly clients = new Map<string, MongoClientWrapper>()\n\n  private readonly cacheKey: string\n  private readonly client: MongoClient\n  private connected = false\n  private operationChain: Promise<unknown> = Promise.resolve()\n  private refCount = 0\n\n  constructor(uri: string, maxPoolSize?: number, closeDelay?: number, cacheKey?: string) {\n    this.cacheKey = cacheKey ?? MongoClientWrapper.cacheKey(uri, maxPoolSize, closeDelay)\n    this.client = new MongoClient(uri, {\n      maxIdleTimeMS: closeDelay,\n      maxPoolSize,\n    })\n  }\n\n  /**\n   * Builds a cache key from connection URI and pool options.\n   * @param uri - The MongoDB connection URI\n   * @param poolSize - Maximum connection pool size\n   * @param closeDelay - Delay in milliseconds before closing idle connections\n   */\n  static cacheKey(uri: string, poolSize?: number, closeDelay?: number) {\n    return `${uri}\\0${poolSize ?? ''}\\0${closeDelay ?? ''}`\n  }\n\n  /**\n   * Gets or creates a cached MongoClientWrapper for the given URI and pool options.\n   * @param uri - The MongoDB connection URI\n   * @param poolSize - Maximum connection pool size\n   * @param closeDelay - Delay in milliseconds before closing idle connections\n   * @returns A cached or newly created wrapper instance\n   */\n  static get(uri: string, poolSize?: number, closeDelay?: number) {\n    const key = this.cacheKey(uri, poolSize, closeDelay)\n    let wrapper = this.clients.get(key)\n    if (!wrapper) {\n      wrapper = new MongoClientWrapper(uri, poolSize, closeDelay, key)\n      this.clients.set(key, wrapper)\n    }\n    return wrapper\n  }\n\n  /** Connects to MongoDB and returns the underlying MongoClient. */\n  async connect() {\n    return await this.enqueue(async () => {\n      this.refCount++\n      try {\n        if (!this.connected) {\n          await this.client.connect()\n          this.connected = true\n        }\n        return this.client\n      } catch (error) {\n        this.refCount--\n        throw error\n      }\n    })\n  }\n\n  /** Releases a connection reference and closes the client when the last reference is released. */\n  async disconnect() {\n    await this.enqueue(async () => {\n      if (this.refCount <= 0) {\n        return\n      }\n      this.refCount--\n      if (this.refCount === 0 && this.connected) {\n        await this.client.close()\n        this.connected = false\n        this.removeFromCache()\n      }\n    })\n  }\n\n  /** Initiates a graceful close of the connection, regardless of outstanding references. */\n  async initiateClose() {\n    await this.enqueue(async () => {\n      if (this.connected) {\n        await this.client.close()\n        this.connected = false\n      }\n      this.refCount = 0\n      this.removeFromCache()\n    })\n  }\n\n  private enqueue<T>(operation: () => Promise<T>): Promise<T> {\n    const result = this.operationChain.then(operation)\n    this.operationChain = result.catch((error: unknown) => {\n      void error\n    })\n    return result\n  }\n\n  private removeFromCache() {\n    MongoClientWrapper.clients.delete(this.cacheKey)\n  }\n}\n"],
  "mappings": "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",
  "names": []
}
