@agentforge/tools 0.16.39 → 0.16.41

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/dist/index.cjs CHANGED
@@ -4226,6 +4226,59 @@ function scheduleReconnection(context, logger23) {
4226
4226
  }, delay2);
4227
4227
  context.setReconnectionTimer(timer);
4228
4228
  }
4229
+
4230
+ // src/data/relational/connection/connection-cleanup.ts
4231
+ async function cleanupConnectionResources(vendor, client, clearConnection, logger23) {
4232
+ if (!client) {
4233
+ return;
4234
+ }
4235
+ logger23.debug("Cleaning up cancelled connection", {
4236
+ vendor
4237
+ });
4238
+ try {
4239
+ await shutdownClient(vendor, client);
4240
+ } catch (error) {
4241
+ logger23.debug("Error during cancelled connection cleanup", {
4242
+ vendor,
4243
+ error: error instanceof Error ? error.message : String(error)
4244
+ });
4245
+ } finally {
4246
+ clearConnection();
4247
+ }
4248
+ }
4249
+ async function closeManagedConnection(runtime, logger23) {
4250
+ const client = runtime.getClient();
4251
+ if (client) {
4252
+ logger23.info("Closing database connection", {
4253
+ vendor: runtime.vendor,
4254
+ state: runtime.getState()
4255
+ });
4256
+ try {
4257
+ await shutdownClient(runtime.vendor, client);
4258
+ runtime.setState("disconnected" /* DISCONNECTED */);
4259
+ runtime.emitDisconnected();
4260
+ logger23.debug("Database connection closed successfully", {
4261
+ vendor: runtime.vendor,
4262
+ state: runtime.getState()
4263
+ });
4264
+ } catch (error) {
4265
+ logger23.error("Error closing database connection", {
4266
+ vendor: runtime.vendor,
4267
+ error: error instanceof Error ? error.message : String(error),
4268
+ state: runtime.getState()
4269
+ });
4270
+ const normalizedError = error instanceof Error ? error : new Error(String(error));
4271
+ runtime.setState("error" /* ERROR */);
4272
+ runtime.emitError(normalizedError);
4273
+ } finally {
4274
+ runtime.setClient(null);
4275
+ runtime.setDb(null);
4276
+ }
4277
+ } else if (runtime.getState() !== "disconnected" /* DISCONNECTED */) {
4278
+ runtime.setState("disconnected" /* DISCONNECTED */);
4279
+ runtime.emitDisconnected();
4280
+ }
4281
+ }
4229
4282
  var logger7 = core.createLogger("agentforge:tools:data:relational:connection:vendor-init");
4230
4283
  var SAFE_INITIALIZATION_PATTERNS = [
4231
4284
  "Pool max connections must be",
@@ -4381,6 +4434,190 @@ async function initializeSQLiteConnection(connection) {
4381
4434
  return { client, db };
4382
4435
  }
4383
4436
 
4437
+ // src/data/relational/connection/connection-initialization.ts
4438
+ async function initializeManagedConnection(runtime, logger23) {
4439
+ const startTime = Date.now();
4440
+ const currentGeneration = runtime.getConnectionGeneration();
4441
+ if (runtime.getState() === "connected" /* CONNECTED */ && runtime.getClient()) {
4442
+ logger23.warn("Re-initializing an already connected manager; emitting disconnected before cleanup", {
4443
+ vendor: runtime.vendor
4444
+ });
4445
+ runtime.setState("disconnected" /* DISCONNECTED */);
4446
+ runtime.emitDisconnected();
4447
+ await cleanupConnectionResources(
4448
+ runtime.vendor,
4449
+ runtime.getClient(),
4450
+ () => {
4451
+ runtime.setClient(null);
4452
+ runtime.setDb(null);
4453
+ },
4454
+ logger23
4455
+ );
4456
+ }
4457
+ runtime.setState("connecting" /* CONNECTING */);
4458
+ logger23.info("Initializing database connection", {
4459
+ vendor: runtime.vendor,
4460
+ connectionType: typeof runtime.config.connection,
4461
+ state: runtime.getState()
4462
+ });
4463
+ try {
4464
+ const initialized = await initializeVendorConnection(runtime.config);
4465
+ runtime.setClient(initialized.client);
4466
+ runtime.setDb(initialized.db);
4467
+ if (currentGeneration !== runtime.getConnectionGeneration()) {
4468
+ logger23.debug("Connection cancelled during initialization", {
4469
+ vendor: runtime.vendor,
4470
+ currentGeneration,
4471
+ newGeneration: runtime.getConnectionGeneration()
4472
+ });
4473
+ await cleanupConnectionResources(
4474
+ runtime.vendor,
4475
+ runtime.getClient(),
4476
+ () => {
4477
+ runtime.setClient(null);
4478
+ runtime.setDb(null);
4479
+ },
4480
+ logger23
4481
+ );
4482
+ runtime.setState("disconnected" /* DISCONNECTED */);
4483
+ throw new Error("Connection cancelled during initialization");
4484
+ }
4485
+ logger23.debug("Validating connection health", { vendor: runtime.vendor });
4486
+ const healthy = await runtime.isHealthy();
4487
+ if (!healthy) {
4488
+ throw new Error(`Failed to establish healthy connection to ${runtime.vendor} database`);
4489
+ }
4490
+ if (currentGeneration !== runtime.getConnectionGeneration()) {
4491
+ logger23.debug("Connection cancelled during health check", {
4492
+ vendor: runtime.vendor,
4493
+ currentGeneration,
4494
+ newGeneration: runtime.getConnectionGeneration()
4495
+ });
4496
+ await cleanupConnectionResources(
4497
+ runtime.vendor,
4498
+ runtime.getClient(),
4499
+ () => {
4500
+ runtime.setClient(null);
4501
+ runtime.setDb(null);
4502
+ },
4503
+ logger23
4504
+ );
4505
+ runtime.setState("disconnected" /* DISCONNECTED */);
4506
+ throw new Error("Connection cancelled during health check");
4507
+ }
4508
+ runtime.setState("connected" /* CONNECTED */);
4509
+ runtime.emitConnected();
4510
+ runtime.resetReconnectionAttempts();
4511
+ logger23.info("Database connection initialized successfully", {
4512
+ vendor: runtime.vendor,
4513
+ duration: Date.now() - startTime,
4514
+ state: runtime.getState()
4515
+ });
4516
+ } catch (error) {
4517
+ const errorMessage = `Failed to initialize ${runtime.vendor} connection`;
4518
+ const normalizedError = error instanceof Error ? error : new Error(String(error));
4519
+ const isValidationError = SAFE_INITIALIZATION_PATTERNS.some(
4520
+ (pattern) => normalizedError.message.includes(pattern)
4521
+ );
4522
+ if (isValidationError) {
4523
+ runtime.setState("error" /* ERROR */);
4524
+ throw normalizedError;
4525
+ }
4526
+ const isCancellation = normalizedError.message.includes("Connection cancelled");
4527
+ if (!isCancellation) {
4528
+ runtime.setState("error" /* ERROR */);
4529
+ if (runtime.errorListenerCount() > 0) {
4530
+ runtime.emitError(normalizedError);
4531
+ }
4532
+ logger23.error(errorMessage, {
4533
+ vendor: runtime.vendor,
4534
+ error: normalizedError.message,
4535
+ duration: Date.now() - startTime,
4536
+ state: runtime.getState()
4537
+ });
4538
+ await cleanupConnectionResources(
4539
+ runtime.vendor,
4540
+ runtime.getClient(),
4541
+ () => {
4542
+ runtime.setClient(null);
4543
+ runtime.setDb(null);
4544
+ },
4545
+ logger23
4546
+ );
4547
+ if (runtime.getReconnectionConfig().enabled && currentGeneration === runtime.getConnectionGeneration()) {
4548
+ runtime.scheduleReconnection();
4549
+ }
4550
+ } else {
4551
+ logger23.debug("Connection initialization cancelled", {
4552
+ vendor: runtime.vendor,
4553
+ duration: Date.now() - startTime,
4554
+ state: runtime.getState()
4555
+ });
4556
+ }
4557
+ if (error instanceof Error) {
4558
+ throw new Error(errorMessage, { cause: error });
4559
+ }
4560
+ throw new Error(`${errorMessage}: ${String(error)}`);
4561
+ }
4562
+ }
4563
+ function isSqliteNonQueryError(error) {
4564
+ if (!(error instanceof Error)) return false;
4565
+ const isSqliteOrTypeError = error.constructor?.name === "SqliteError" || error.name === "SqliteError" || error instanceof TypeError;
4566
+ const hasNonQueryMessage = error.message.includes("does not return data");
4567
+ return isSqliteOrTypeError && hasNonQueryMessage;
4568
+ }
4569
+ async function checkConnectionHealth(args, logger23) {
4570
+ if (!args.db || !args.client) {
4571
+ logger23.debug("Health check failed: connection not initialized", { vendor: args.vendor });
4572
+ return false;
4573
+ }
4574
+ try {
4575
+ await args.execute(drizzleOrm.sql`SELECT 1`);
4576
+ logger23.debug("Health check passed", { vendor: args.vendor });
4577
+ return true;
4578
+ } catch (error) {
4579
+ logger23.debug("Health check failed", {
4580
+ vendor: args.vendor,
4581
+ error: error instanceof Error ? error.message : String(error)
4582
+ });
4583
+ return false;
4584
+ }
4585
+ }
4586
+ function getConnectionPoolMetrics(vendor, client) {
4587
+ if (!client) {
4588
+ return { totalCount: 0, activeCount: 0, idleCount: 0, waitingCount: 0 };
4589
+ }
4590
+ if (vendor === "postgresql") {
4591
+ const pgClient = client;
4592
+ const totalCount2 = pgClient.totalCount || 0;
4593
+ const idleCount = pgClient.idleCount || 0;
4594
+ const waitingCount = pgClient.waitingCount || 0;
4595
+ const activeCount = Math.max(totalCount2 - idleCount, 0);
4596
+ return {
4597
+ totalCount: totalCount2,
4598
+ activeCount,
4599
+ idleCount,
4600
+ waitingCount
4601
+ };
4602
+ }
4603
+ if (vendor === "mysql") {
4604
+ return {
4605
+ totalCount: 0,
4606
+ activeCount: 0,
4607
+ idleCount: 0,
4608
+ waitingCount: 0
4609
+ };
4610
+ }
4611
+ const sqliteClient = client;
4612
+ const totalCount = sqliteClient.open ? 1 : 0;
4613
+ return {
4614
+ totalCount,
4615
+ activeCount: totalCount,
4616
+ idleCount: 0,
4617
+ waitingCount: 0
4618
+ };
4619
+ }
4620
+
4384
4621
  // src/data/relational/connection/query-execution.ts
4385
4622
  async function executeQuery(context, query, logger23) {
4386
4623
  logger23.debug("Executing SQL query", {
@@ -4400,11 +4637,11 @@ async function executeQuery(context, query, logger23) {
4400
4637
  }
4401
4638
  return context.db.execute(query);
4402
4639
  }
4403
- async function executeSqliteQuery(db, query, isSqliteNonQueryError) {
4640
+ async function executeSqliteQuery(db, query, isSqliteNonQueryError2) {
4404
4641
  try {
4405
4642
  return db.all(query);
4406
4643
  } catch (error) {
4407
- if (isSqliteNonQueryError(error)) {
4644
+ if (isSqliteNonQueryError2(error)) {
4408
4645
  const runResult = db.run(query);
4409
4646
  return { ...runResult, affectedRows: runResult.changes ?? 0 };
4410
4647
  }
@@ -4469,11 +4706,6 @@ var ConnectionManager = class extends events.EventEmitter {
4469
4706
  // Track in-flight connection attempts
4470
4707
  connectionGeneration = 0;
4471
4708
  // Cancellation token for disconnect
4472
- /**
4473
- * Create a new ConnectionManager instance
4474
- * @param config - Connection configuration
4475
- * @param reconnectionConfig - Optional reconnection configuration
4476
- */
4477
4709
  constructor(config, reconnectionConfig) {
4478
4710
  super();
4479
4711
  this.config = config;
@@ -4486,11 +4718,6 @@ var ConnectionManager = class extends events.EventEmitter {
4486
4718
  };
4487
4719
  checkPeerDependency(this.vendor);
4488
4720
  }
4489
- /**
4490
- * Connect to the database
4491
- * Initializes the connection and sets up automatic reconnection if configured
4492
- * @throws {Error} If connection fails or configuration is invalid
4493
- */
4494
4721
  async connect() {
4495
4722
  if (this.state === "connected" /* CONNECTED */) {
4496
4723
  logger8.debug("Already connected", { vendor: this.vendor });
@@ -4514,13 +4741,6 @@ var ConnectionManager = class extends events.EventEmitter {
4514
4741
  });
4515
4742
  return this.connectPromise;
4516
4743
  }
4517
- /**
4518
- * Disconnect from the database
4519
- * Closes the connection and cancels any pending reconnection attempts
4520
- *
4521
- * Note: Event listeners are NOT removed by this method. If you need to dispose
4522
- * of the ConnectionManager instance entirely, call dispose() instead.
4523
- */
4524
4744
  async disconnect() {
4525
4745
  this.connectionGeneration++;
4526
4746
  this.reconnectionTimer = cancelPendingReconnection(
@@ -4538,142 +4758,22 @@ var ConnectionManager = class extends events.EventEmitter {
4538
4758
  );
4539
4759
  await this.close();
4540
4760
  }
4541
- /**
4542
- * Dispose of the ConnectionManager instance
4543
- * Disconnects and removes all event listeners
4544
- * Call this when you're done with the ConnectionManager and won't reuse it
4545
- */
4546
4761
  async dispose() {
4547
4762
  await this.disconnect();
4548
4763
  this.removeAllListeners();
4549
4764
  }
4550
- /**
4551
- * Check if the connection is currently connected
4552
- * @returns true if connected, false otherwise
4553
- */
4554
4765
  isConnected() {
4555
4766
  return this.state === "connected" /* CONNECTED */;
4556
4767
  }
4557
- /**
4558
- * Get the current connection state
4559
- * @returns Current connection state
4560
- */
4561
4768
  getState() {
4562
4769
  return this.state;
4563
4770
  }
4564
- /**
4565
- * Get the configured database vendor.
4566
- */
4567
4771
  getVendor() {
4568
4772
  return this.vendor;
4569
4773
  }
4570
- /**
4571
- * Initialize the database connection
4572
- *
4573
- * This method is public to maintain compatibility with the DatabaseConnection interface
4574
- * and existing code (e.g., relational-query.ts). For new code, prefer using connect()
4575
- * which provides lifecycle management and automatic reconnection.
4576
- *
4577
- * @throws {Error} If connection fails or configuration is invalid
4578
- */
4579
4774
  async initialize() {
4580
- const startTime = Date.now();
4581
- const currentGeneration = this.connectionGeneration;
4582
- if (this.state === "connected" /* CONNECTED */ && this.client) {
4583
- logger8.warn("Re-initializing an already connected manager; emitting disconnected before cleanup", {
4584
- vendor: this.vendor
4585
- });
4586
- this.setState("disconnected" /* DISCONNECTED */);
4587
- this.emit("disconnected");
4588
- await this.cleanupCancelledConnection();
4589
- }
4590
- this.setState("connecting" /* CONNECTING */);
4591
- logger8.info("Initializing database connection", {
4592
- vendor: this.vendor,
4593
- connectionType: typeof this.config.connection,
4594
- state: this.state
4595
- });
4596
- try {
4597
- const initialized = await initializeVendorConnection(this.config);
4598
- this.client = initialized.client;
4599
- this.db = initialized.db;
4600
- if (currentGeneration !== this.connectionGeneration) {
4601
- logger8.debug("Connection cancelled during initialization", {
4602
- vendor: this.vendor,
4603
- currentGeneration,
4604
- newGeneration: this.connectionGeneration
4605
- });
4606
- await this.cleanupCancelledConnection();
4607
- this.setState("disconnected" /* DISCONNECTED */);
4608
- throw new Error("Connection cancelled during initialization");
4609
- }
4610
- logger8.debug("Validating connection health", { vendor: this.vendor });
4611
- const healthy = await this.isHealthy();
4612
- if (!healthy) {
4613
- throw new Error(`Failed to establish healthy connection to ${this.vendor} database`);
4614
- }
4615
- if (currentGeneration !== this.connectionGeneration) {
4616
- logger8.debug("Connection cancelled during health check", {
4617
- vendor: this.vendor,
4618
- currentGeneration,
4619
- newGeneration: this.connectionGeneration
4620
- });
4621
- await this.cleanupCancelledConnection();
4622
- this.setState("disconnected" /* DISCONNECTED */);
4623
- throw new Error("Connection cancelled during health check");
4624
- }
4625
- this.setState("connected" /* CONNECTED */);
4626
- this.emit("connected");
4627
- this.reconnectionAttempts = 0;
4628
- logger8.info("Database connection initialized successfully", {
4629
- vendor: this.vendor,
4630
- duration: Date.now() - startTime,
4631
- state: this.state
4632
- });
4633
- } catch (error) {
4634
- const errorMessage = `Failed to initialize ${this.vendor} connection`;
4635
- const normalizedError = error instanceof Error ? error : new Error(String(error));
4636
- const isValidationError = SAFE_INITIALIZATION_PATTERNS.some(
4637
- (pattern) => normalizedError.message.includes(pattern)
4638
- );
4639
- if (isValidationError) {
4640
- this.setState("error" /* ERROR */);
4641
- throw normalizedError;
4642
- }
4643
- const isCancellation = normalizedError.message.includes("Connection cancelled");
4644
- if (!isCancellation) {
4645
- this.setState("error" /* ERROR */);
4646
- if (this.listenerCount("error") > 0) {
4647
- this.emit("error", normalizedError);
4648
- }
4649
- logger8.error(errorMessage, {
4650
- vendor: this.vendor,
4651
- error: normalizedError.message,
4652
- duration: Date.now() - startTime,
4653
- state: this.state
4654
- });
4655
- await this.cleanupCancelledConnection();
4656
- if (this.reconnectionConfig.enabled && currentGeneration === this.connectionGeneration) {
4657
- this.scheduleReconnection();
4658
- }
4659
- } else {
4660
- logger8.debug("Connection initialization cancelled", {
4661
- vendor: this.vendor,
4662
- duration: Date.now() - startTime,
4663
- state: this.state
4664
- });
4665
- }
4666
- if (error instanceof Error) {
4667
- throw new Error(errorMessage, { cause: error });
4668
- }
4669
- throw new Error(`${errorMessage}: ${String(error)}`);
4670
- }
4775
+ return initializeManagedConnection(this.createRuntimeAdapter(), logger8);
4671
4776
  }
4672
- /**
4673
- * Set the connection state and log the change
4674
- * @param newState - New connection state
4675
- * @private
4676
- */
4677
4777
  setState(newState) {
4678
4778
  const oldState = this.state;
4679
4779
  this.state = newState;
@@ -4685,10 +4785,6 @@ var ConnectionManager = class extends events.EventEmitter {
4685
4785
  });
4686
4786
  }
4687
4787
  }
4688
- /**
4689
- * Schedule a reconnection attempt with exponential backoff
4690
- * @private
4691
- */
4692
4788
  scheduleReconnection() {
4693
4789
  scheduleReconnection(
4694
4790
  {
@@ -4715,40 +4811,39 @@ var ConnectionManager = class extends events.EventEmitter {
4715
4811
  logger8
4716
4812
  );
4717
4813
  }
4718
- /**
4719
- * Determine whether an error thrown by drizzle-orm's better-sqlite3 adapter
4720
- * `.all()` indicates the statement does not return data (i.e. it is DML/DDL,
4721
- * not a SELECT).
4722
- *
4723
- * The adapter may surface this as either a native `SqliteError` or a
4724
- * `TypeError` depending on the drizzle-orm / better-sqlite3 version, so we
4725
- * accept both types while still requiring the distinctive message substring
4726
- * to avoid false positives from unrelated errors.
4727
- */
4728
- isSqliteNonQueryError(error) {
4729
- if (!(error instanceof Error)) return false;
4730
- const isSqliteOrTypeError = error.constructor?.name === "SqliteError" || error.name === "SqliteError" || error instanceof TypeError;
4731
- const hasNonQueryMessage = error.message.includes("does not return data");
4732
- return isSqliteOrTypeError && hasNonQueryMessage;
4814
+ createRuntimeAdapter() {
4815
+ return {
4816
+ vendor: this.vendor,
4817
+ config: this.config,
4818
+ getState: () => this.state,
4819
+ setState: (state) => this.setState(state),
4820
+ getClient: () => this.client,
4821
+ setClient: (client) => {
4822
+ this.client = client;
4823
+ },
4824
+ getDb: () => this.db,
4825
+ setDb: (db) => {
4826
+ this.db = db;
4827
+ },
4828
+ emitConnected: () => {
4829
+ this.emit("connected");
4830
+ },
4831
+ emitDisconnected: () => {
4832
+ this.emit("disconnected");
4833
+ },
4834
+ emitError: (error) => {
4835
+ this.emit("error", error);
4836
+ },
4837
+ errorListenerCount: () => this.listenerCount("error"),
4838
+ getReconnectionConfig: () => this.reconnectionConfig,
4839
+ scheduleReconnection: () => this.scheduleReconnection(),
4840
+ resetReconnectionAttempts: () => {
4841
+ this.reconnectionAttempts = 0;
4842
+ },
4843
+ getConnectionGeneration: () => this.connectionGeneration,
4844
+ isHealthy: () => this.isHealthy()
4845
+ };
4733
4846
  }
4734
- /**
4735
- * Execute a SQL query
4736
- *
4737
- * Executes a parameterized SQL query using Drizzle's SQL template objects.
4738
- * This method provides safe parameter binding to prevent SQL injection.
4739
- *
4740
- * @param query - Drizzle SQL template object with parameter binding
4741
- * @returns Query result
4742
- *
4743
- * @example
4744
- * ```typescript
4745
- * import { sql } from 'drizzle-orm';
4746
- *
4747
- * const result = await manager.execute(
4748
- * sql`SELECT * FROM users WHERE id = ${userId}`
4749
- * );
4750
- * ```
4751
- */
4752
4847
  async execute(query) {
4753
4848
  if (!this.db) {
4754
4849
  throw new Error("Database not initialized. Call initialize() first.");
@@ -4757,18 +4852,12 @@ var ConnectionManager = class extends events.EventEmitter {
4757
4852
  {
4758
4853
  vendor: this.vendor,
4759
4854
  db: this.db,
4760
- isSqliteNonQueryError: (error) => this.isSqliteNonQueryError(error)
4855
+ isSqliteNonQueryError
4761
4856
  },
4762
4857
  query,
4763
4858
  logger8
4764
4859
  );
4765
4860
  }
4766
- /**
4767
- * Execute a callback with a single dedicated database connection/session.
4768
- *
4769
- * This is required for multi-statement transactions so all statements run on
4770
- * the same underlying connection.
4771
- */
4772
4861
  async executeInConnection(callback) {
4773
4862
  if (!this.client || !this.db) {
4774
4863
  throw new Error("Database not initialized. Call connect() first.");
@@ -4778,65 +4867,14 @@ var ConnectionManager = class extends events.EventEmitter {
4778
4867
  vendor: this.vendor,
4779
4868
  client: this.client,
4780
4869
  db: this.db,
4781
- isSqliteNonQueryError: (error) => this.isSqliteNonQueryError(error)
4870
+ isSqliteNonQueryError
4782
4871
  },
4783
4872
  callback
4784
4873
  );
4785
4874
  }
4786
- /**
4787
- * Get connection pool metrics
4788
- *
4789
- * Returns information about the current state of the connection pool.
4790
- * For SQLite, returns basic connection status since it uses a single connection.
4791
- *
4792
- * @returns Pool metrics including total, active, idle, and waiting connections
4793
- */
4794
4875
  getPoolMetrics() {
4795
- if (!this.client) {
4796
- return { totalCount: 0, activeCount: 0, idleCount: 0, waitingCount: 0 };
4797
- }
4798
- if (this.vendor === "postgresql") {
4799
- const totalCount = this.client.totalCount || 0;
4800
- const idleCount = this.client.idleCount || 0;
4801
- const waitingCount = this.client.waitingCount || 0;
4802
- const activeCount = Math.max(totalCount - idleCount, 0);
4803
- return {
4804
- totalCount,
4805
- activeCount,
4806
- idleCount,
4807
- waitingCount
4808
- };
4809
- } else if (this.vendor === "mysql") {
4810
- return {
4811
- totalCount: 0,
4812
- activeCount: 0,
4813
- idleCount: 0,
4814
- waitingCount: 0
4815
- };
4816
- } else {
4817
- const totalCount = this.client.open ? 1 : 0;
4818
- const idleCount = 0;
4819
- const waitingCount = 0;
4820
- const activeCount = this.client.open ? 1 : 0;
4821
- return {
4822
- totalCount,
4823
- activeCount,
4824
- idleCount,
4825
- waitingCount
4826
- };
4827
- }
4876
+ return getConnectionPoolMetrics(this.vendor, this.client);
4828
4877
  }
4829
- /**
4830
- * Close the database connection
4831
- *
4832
- * This method is public to maintain compatibility with the DatabaseConnection interface
4833
- * and existing code (e.g., relational-query.ts). For new code, prefer using disconnect()
4834
- * for connection lifecycle management. Note that disconnect() does NOT clean up event
4835
- * listeners; call dispose() if you need full cleanup including listener removal.
4836
- *
4837
- * Note: This method coordinates with in-flight connect()/initialize() operations
4838
- * and cancels pending reconnection timers to prevent unexpected reconnections.
4839
- */
4840
4878
  async close() {
4841
4879
  this.connectionGeneration++;
4842
4880
  this.reconnectionTimer = cancelPendingReconnection(
@@ -4851,80 +4889,38 @@ var ConnectionManager = class extends events.EventEmitter {
4851
4889
  this.vendor,
4852
4890
  "close"
4853
4891
  );
4854
- if (this.client) {
4855
- logger8.info("Closing database connection", {
4856
- vendor: this.vendor,
4857
- state: this.state
4858
- });
4859
- try {
4860
- await shutdownClient(this.vendor, this.client);
4861
- this.setState("disconnected" /* DISCONNECTED */);
4862
- this.emit("disconnected");
4863
- logger8.debug("Database connection closed successfully", {
4864
- vendor: this.vendor,
4865
- state: this.state
4866
- });
4867
- } catch (error) {
4868
- logger8.error("Error closing database connection", {
4869
- vendor: this.vendor,
4870
- error: error instanceof Error ? error.message : String(error),
4871
- state: this.state
4872
- });
4873
- const normalizedError = error instanceof Error ? error : new Error(String(error));
4874
- this.setState("error" /* ERROR */);
4875
- this.emit("error", normalizedError);
4876
- } finally {
4877
- this.client = null;
4878
- this.db = null;
4879
- }
4880
- } else if (this.state !== "disconnected" /* DISCONNECTED */) {
4881
- this.setState("disconnected" /* DISCONNECTED */);
4882
- this.emit("disconnected");
4883
- }
4884
- }
4885
- /**
4886
- * Clean up resources when a connection is cancelled during initialization
4887
- * This prevents connection leaks when disconnect() is called while initialize() is in-flight
4888
- */
4889
- async cleanupCancelledConnection() {
4890
- if (!this.client) {
4891
- return;
4892
- }
4893
- logger8.debug("Cleaning up cancelled connection", {
4894
- vendor: this.vendor
4895
- });
4896
- try {
4897
- await shutdownClient(this.vendor, this.client);
4898
- } catch (error) {
4899
- logger8.debug("Error during cancelled connection cleanup", {
4892
+ await closeManagedConnection(
4893
+ {
4900
4894
  vendor: this.vendor,
4901
- error: error instanceof Error ? error.message : String(error)
4902
- });
4903
- } finally {
4904
- this.client = null;
4905
- this.db = null;
4906
- }
4895
+ getState: () => this.state,
4896
+ setState: (state) => this.setState(state),
4897
+ getClient: () => this.client,
4898
+ setClient: (client) => {
4899
+ this.client = client;
4900
+ },
4901
+ setDb: (db) => {
4902
+ this.db = db;
4903
+ },
4904
+ emitDisconnected: () => {
4905
+ this.emit("disconnected");
4906
+ },
4907
+ emitError: (error) => {
4908
+ this.emit("error", error);
4909
+ }
4910
+ },
4911
+ logger8
4912
+ );
4907
4913
  }
4908
- /**
4909
- * Check if the connection is healthy
4910
- * @returns true if connection is healthy, false otherwise
4911
- */
4912
4914
  async isHealthy() {
4913
- if (!this.db || !this.client) {
4914
- logger8.debug("Health check failed: connection not initialized", { vendor: this.vendor });
4915
- return false;
4916
- }
4917
- try {
4918
- await this.execute(drizzleOrm.sql`SELECT 1`);
4919
- logger8.debug("Health check passed", { vendor: this.vendor });
4920
- return true;
4921
- } catch (error) {
4922
- logger8.debug("Health check failed", {
4915
+ return checkConnectionHealth(
4916
+ {
4923
4917
  vendor: this.vendor,
4924
- error: error instanceof Error ? error.message : String(error)
4925
- });
4926
- return false;
4927
- }
4918
+ db: this.db,
4919
+ client: this.client,
4920
+ execute: (query) => this.execute(query)
4921
+ },
4922
+ logger8
4923
+ );
4928
4924
  }
4929
4925
  };
4930
4926
  var logger9 = core.createLogger("agentforge:tools:data:relational:query");