{
  "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 !== undefined && 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/**\n * Default grace period (ms) the shared client is kept alive after its last reference is\n * released, when no `closeDelay` is configured. Rapid sequential operations reconnect well\n * within this window, so they reuse one pooled client instead of tearing it down and\n * reopening connections per operation (which exhausts local ephemeral ports under load).\n */\nconst DEFAULT_CLOSE_DELAY_MS = 10_000\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 readonly closeDelay: number\n  private closeTimer: ReturnType<typeof setTimeout> | undefined\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.closeDelay = closeDelay ?? DEFAULT_CLOSE_DELAY_MS\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.cancelScheduledClose()\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  /**\n   * Releases a connection reference. The shared client is kept alive and only closed once it\n   * has had no references for `closeDelay` ms, so back-to-back operations reuse a single pooled\n   * client. A reconnect within that window cancels the pending close. Use `closeDelay: 0` for\n   * the legacy behavior of closing immediately on the last release.\n   */\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        if (this.closeDelay <= 0) {\n          await this.client.close()\n          this.connected = false\n          this.removeFromCache()\n        } else {\n          this.scheduleClose()\n        }\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      this.cancelScheduledClose()\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 cancelScheduledClose() {\n    if (this.closeTimer === undefined) {\n      return\n    }\n    clearTimeout(this.closeTimer)\n    this.closeTimer = undefined\n  }\n\n  private async closeIfIdle() {\n    await this.enqueue(async () => {\n      if (this.refCount !== 0 || !this.connected) {\n        return\n      }\n      await this.client.close()\n      this.connected = false\n      this.removeFromCache()\n    })\n  }\n\n  private enqueue<T>(operation: () => Promise<T>): Promise<T> {\n    const result = (async () => {\n      try {\n        await this.operationChain\n      } catch (error: unknown) {\n        void error\n      }\n      return await operation()\n    })()\n    this.operationChain = (async () => {\n      try {\n        await result\n      } catch (error: unknown) {\n        void error\n      }\n    })()\n    return result\n  }\n\n  private removeFromCache() {\n    MongoClientWrapper.clients.delete(this.cacheKey)\n  }\n\n  private scheduleClose() {\n    this.cancelScheduledClose()\n    const timer = setTimeout(() => {\n      this.closeTimer = undefined\n      void this.closeIfIdle()\n    }, this.closeDelay)\n    // Do not let a pending idle-close timer keep the process alive (e.g. at test/CLI exit).\n    if (typeof timer === 'object' && 'unref' in timer) {\n      timer.unref()\n    }\n    this.closeTimer = timer\n  }\n}\n"],
  "mappings": "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",
  "names": []
}
