@elizaos/plugin-sql 1.0.0-alpha.6 → 1.0.0-alpha.60

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/README.md CHANGED
@@ -9,19 +9,6 @@ A PostgreSQL database adapter built with Drizzle ORM for the ElizaOS ecosystem.
9
9
  bun add @elizaos/plugin-sql
10
10
  ```
11
11
 
12
- ## Database Schema
13
-
14
- The adapter includes the following tables:
15
- - accounts
16
- - cache
17
- - embeddings
18
- - goals
19
- - logs
20
- - memories
21
- - participants
22
- - relationships
23
- - rooms
24
-
25
12
  ## Vector Dimensions
26
13
 
27
14
  The adapter supports the following vector dimensions:
@@ -49,25 +36,42 @@ Important Note: Once an agent is initialized with a specific embedding dimension
49
36
  - Room and participant management
50
37
  - Goal tracking system
51
38
 
39
+ ## Database Schema
40
+
41
+ The plugin uses a structured schema with the following main tables:
42
+
43
+ ### Core Tables
44
+ - **Agent**: Stores agent information and configurations
45
+ - **Room**: Manages conversation rooms and their settings
46
+ - **Participant**: Tracks participants in rooms
47
+ - **Memory**: Stores agent memories with vector embeddings for semantic search
48
+ - **Embedding**: Manages vector embeddings for various entities
49
+ - **Entity**: Represents entities that agents can interact with
50
+ - **Relationship**: Tracks relationships between entities
51
+ - **Component**: Stores agent components and their configurations
52
+ - **Tasks**: Manages tasks and goals for agents
53
+ - **Log**: Stores system logs
54
+ - **Cache**: Provides a caching mechanism for frequently accessed data
55
+ - **World**: Manages world settings and configurations
56
+
57
+ Each table is defined using Drizzle ORM schema definitions in the `src/schema` directory. The schema is designed to support the ElizaOS ecosystem's requirements for agent-based systems.
58
+
52
59
  ## Usage
53
60
 
54
61
  The adapter is typically used as part of the ElizaOS runtime:
55
62
 
56
63
  ```typescript
57
64
  async function findDatabaseAdapter(runtime: IAgentRuntime) {
58
- const { adapters } = runtime;
59
- let adapter: Adapter | undefined;
65
+ let adapter = runtime;
60
66
 
61
- if (adapters.length === 0) {
67
+ if (!adapter) {
62
68
  const drizzleAdapterPlugin = await import('@elizaos/plugin-sql');
63
69
  const drizzleAdapterPluginDefault = drizzleAdapterPlugin.default;
64
- adapter = drizzleAdapterPluginDefault.adapters[0];
70
+ adapter = drizzleAdapterPluginDefault.adapter;
65
71
  if (!adapter) {
66
72
  throw new Error("Internal error: No database adapter found for default plugin-sql");
67
73
  }
68
- } else if (adapters.length === 1) {
69
- adapter = adapters[0];
70
- } else {
74
+ } else if (!adapter) {
71
75
  throw new Error("Multiple database adapters found. You must have no more than one. Adjust your plugins configuration.");
72
76
  }
73
77
 
@@ -97,6 +101,16 @@ The adapter implements the following error handling configurations:
97
101
  - PostgreSQL with vector extension installed
98
102
  - Node.js or Bun (≥1.2.2)
99
103
 
104
+ ## Environment Variables
105
+
106
+ The plugin uses the following environment variables:
107
+
108
+ - `POSTGRES_URL`: Connection string for PostgreSQL database (e.g., `postgresql://user:password@localhost:5432/dbname`)
109
+ - If not provided, the plugin will use PGlite as a fallback
110
+ - `PGLITE_DATA_DIR`: (Optional) Directory for PGlite data storage (default: `../../pglite`)
111
+
112
+ These variables should be defined in a `.env` file at the root of your project.
113
+
100
114
  ## Database Pool Configuration
101
115
 
102
116
  Default pool configuration:
@@ -165,6 +179,50 @@ import { migrate } from "drizzle-orm/node-postgres/migrator";
165
179
  await migrate(db, { migrationsFolder: "./drizzle" });
166
180
  ```
167
181
 
182
+ c. Using the provided migration script:
183
+ ```bash
184
+ npm run migrate
185
+ # or
186
+ pnpm migrate
187
+ ```
188
+
189
+ d. Using drizzle-kit migrate command:
190
+ ```bash
191
+ npx drizzle-kit migrate
192
+ ```
193
+
194
+ This command will read the configuration from `drizzle.config.ts` and pull the PostgreSQL URI from the `.env` file. Make sure your `.env` file contains the `POSTGRES_URL` variable with the correct connection string.
195
+
196
+ ### Migration Configuration
197
+
198
+ The plugin uses a `drizzle.config.ts` file to configure migrations:
199
+
200
+ ```typescript
201
+ import { config } from "dotenv";
202
+ import { defineConfig } from "drizzle-kit";
203
+
204
+ config({ path: "../../.env" });
205
+
206
+ export default defineConfig({
207
+ dialect: "postgresql",
208
+ schema: "./src/schema/index.ts",
209
+ out: "./drizzle/migrations",
210
+ dbCredentials: {
211
+ url: process.env.POSTGRES_URL || "file://../../pglite",
212
+ },
213
+ breakpoints: true,
214
+ });
215
+ ```
216
+
217
+ ### Database Support
218
+
219
+ The plugin supports two database backends:
220
+
221
+ 1. **PostgreSQL**: Used when `POSTGRES_URL` environment variable is provided
222
+ 2. **PGlite**: Used as a fallback when no PostgreSQL URL is provided
223
+
224
+ Both backends use the same migration files, ensuring consistent schema across environments.
225
+
168
226
  ### Note on Vector Support
169
227
  Make sure the PostgreSQL vector extension is installed before running migrations. The adapter will validate vector setup during initialization.
170
228
 
@@ -176,3 +234,36 @@ The adapter implements cleanup handlers for:
176
234
  - beforeExit
177
235
 
178
236
  These ensure proper closing of database connections when the application shuts down.
237
+
238
+ ## Implementation Details
239
+
240
+ ### Connection Management
241
+
242
+ The plugin uses a global singleton pattern to manage database connections. This approach ensures that:
243
+
244
+ 1. **Single Connection Per Process**: Only one connection manager instance exists per Node.js process, regardless of how many times the package is imported or initialized.
245
+
246
+ 2. **Resource Efficiency**: Prevents multiple connection pools to the same database, which could lead to resource exhaustion.
247
+
248
+ 3. **Consistent State**: Ensures all parts of the application share the same database connection state.
249
+
250
+ 4. **Proper Cleanup**: Facilitates proper cleanup of database connections during application shutdown, preventing connection leaks.
251
+
252
+ This pattern is particularly important in monorepo setups or when the package is used by multiple modules within the same process. The implementation uses JavaScript Symbols to create a global registry that persists across module boundaries.
253
+
254
+ ```typescript
255
+ // Example of the singleton pattern implementation
256
+ const GLOBAL_SINGLETONS = Symbol.for("@elizaos/plugin-sql/global-singletons");
257
+
258
+ // Store managers in a global symbol registry
259
+ if (!globalSymbols[GLOBAL_SINGLETONS]) {
260
+ globalSymbols[GLOBAL_SINGLETONS] = {};
261
+ }
262
+
263
+ // Reuse existing managers or create new ones when needed
264
+ if (!globalSingletons.postgresConnectionManager) {
265
+ globalSingletons.postgresConnectionManager = new PostgresConnectionManager(config.postgresUrl);
266
+ }
267
+ ```
268
+
269
+ This approach is especially critical for PGlite connections, which require careful management to ensure proper shutdown and prevent resource leaks.
@@ -0,0 +1,342 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ // src/pglite/manager.ts
5
+ import { dirname as pathDirname, resolve as pathResolve } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ import { PGlite } from "@electric-sql/pglite";
8
+ import { fuzzystrmatch } from "@electric-sql/pglite/contrib/fuzzystrmatch";
9
+ import { vector } from "@electric-sql/pglite/vector";
10
+ import { logger } from "@elizaos/core";
11
+ import { drizzle } from "drizzle-orm/pglite";
12
+ import { migrate } from "drizzle-orm/pglite/migrator";
13
+ var PGliteClientManager = class {
14
+ static {
15
+ __name(this, "PGliteClientManager");
16
+ }
17
+ client;
18
+ shuttingDown = false;
19
+ shutdownTimeout = 500;
20
+ /**
21
+ * Constructor for creating a new instance of PGlite with the provided options.
22
+ * Initializes the PGlite client with additional extensions.
23
+ * @param {PGliteOptions} options - The options to configure the PGlite client.
24
+ */
25
+ constructor(options) {
26
+ this.client = new PGlite({
27
+ ...options,
28
+ extensions: {
29
+ vector,
30
+ fuzzystrmatch
31
+ }
32
+ });
33
+ this.setupShutdownHandlers();
34
+ }
35
+ /**
36
+ * Retrieves the PostgreSQL lite connection.
37
+ *
38
+ * @returns {PGlite} The PostgreSQL lite connection.
39
+ * @throws {Error} If the client manager is currently shutting down.
40
+ */
41
+ getConnection() {
42
+ if (this.shuttingDown) {
43
+ throw new Error("Client manager is shutting down");
44
+ }
45
+ return this.client;
46
+ }
47
+ /**
48
+ * Initiates a graceful shutdown of the PGlite client.
49
+ * Checks if the client is already in the process of shutting down.
50
+ * Logs the start of shutdown process and sets shuttingDown flag to true.
51
+ * Sets a timeout for the shutdown process and forces closure of database connection if timeout is reached.
52
+ * Handles the shutdown process, closes the client connection, clears the timeout, and logs the completion of shutdown.
53
+ * Logs any errors that occur during the shutdown process.
54
+ */
55
+ async gracefulShutdown() {
56
+ if (this.shuttingDown) {
57
+ return;
58
+ }
59
+ this.shuttingDown = true;
60
+ logger.info("Starting graceful shutdown of PGlite client...");
61
+ const timeout = setTimeout(() => {
62
+ logger.warn(
63
+ "Shutdown timeout reached, forcing database connection closure..."
64
+ );
65
+ this.client.close().finally(() => {
66
+ process.exit(1);
67
+ });
68
+ }, this.shutdownTimeout);
69
+ try {
70
+ await this.client.close();
71
+ clearTimeout(timeout);
72
+ logger.info("PGlite client shutdown completed successfully");
73
+ process.exit(0);
74
+ } catch (error) {
75
+ logger.error("Error during graceful shutdown:", error);
76
+ process.exit(1);
77
+ }
78
+ }
79
+ /**
80
+ * Sets up shutdown handlers for SIGINT, SIGTERM, and beforeExit events to gracefully shutdown the application.
81
+ * @private
82
+ */
83
+ setupShutdownHandlers() {
84
+ process.on("SIGINT", async () => {
85
+ await this.gracefulShutdown();
86
+ });
87
+ process.on("SIGTERM", async () => {
88
+ await this.gracefulShutdown();
89
+ });
90
+ process.on("beforeExit", async () => {
91
+ await this.gracefulShutdown();
92
+ });
93
+ }
94
+ /**
95
+ * Initializes the client for PGlite.
96
+ *
97
+ * @returns {Promise<void>} A Promise that resolves when the client is initialized successfully
98
+ */
99
+ async initialize() {
100
+ try {
101
+ await this.client.waitReady;
102
+ logger.info("PGlite client initialized successfully");
103
+ } catch (error) {
104
+ logger.error("Failed to initialize PGlite client:", error);
105
+ throw error;
106
+ }
107
+ }
108
+ /**
109
+ * Asynchronously closes the resource. If the resource is not already shutting down,
110
+ * it performs a graceful shutdown before closing.
111
+ *
112
+ * @returns A promise that resolves once the resource has been closed.
113
+ */
114
+ async close() {
115
+ if (!this.shuttingDown) {
116
+ await this.gracefulShutdown();
117
+ }
118
+ }
119
+ /**
120
+ * Check if the system is currently shutting down.
121
+ *
122
+ * @returns {boolean} True if the system is shutting down, false otherwise.
123
+ */
124
+ isShuttingDown() {
125
+ return this.shuttingDown;
126
+ }
127
+ /**
128
+ * Asynchronously runs database migrations using Drizzle.
129
+ *
130
+ * Drizzle will first check if the migrations are already applied.
131
+ * If there is a diff between database schema and migrations, it will apply the migrations.
132
+ * If they are already applied, it will skip them.
133
+ *
134
+ * @returns {Promise<void>} A Promise that resolves once the migrations are completed successfully.
135
+ */
136
+ async runMigrations() {
137
+ try {
138
+ const db = drizzle(this.client);
139
+ const __filename = fileURLToPath(import.meta.url);
140
+ const __dirname = pathDirname(__filename);
141
+ await migrate(db, {
142
+ migrationsFolder: pathResolve(__dirname, "../drizzle/migrations")
143
+ });
144
+ } catch (error) {
145
+ logger.error("Failed to run database migrations (pglite):", error);
146
+ console.trace(error);
147
+ }
148
+ }
149
+ };
150
+
151
+ // src/pg/manager.ts
152
+ import path from "node:path";
153
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
154
+ import { logger as logger2 } from "@elizaos/core";
155
+ import { drizzle as drizzle2 } from "drizzle-orm/node-postgres";
156
+ import { migrate as migrate2 } from "drizzle-orm/node-postgres/migrator";
157
+ import pkg from "pg";
158
+ var { Pool } = pkg;
159
+ var PostgresConnectionManager = class {
160
+ static {
161
+ __name(this, "PostgresConnectionManager");
162
+ }
163
+ pool;
164
+ isShuttingDown = false;
165
+ connectionTimeout = 5e3;
166
+ /**
167
+ * Constructor for creating a connection pool.
168
+ * @param {string} connectionString - The connection string used to connect to the database.
169
+ */
170
+ constructor(connectionString) {
171
+ const defaultConfig = {
172
+ max: 20,
173
+ idleTimeoutMillis: 3e4,
174
+ connectionTimeoutMillis: this.connectionTimeout
175
+ };
176
+ this.pool = new Pool({
177
+ ...defaultConfig,
178
+ connectionString
179
+ });
180
+ this.pool.on("error", (err) => {
181
+ logger2.error("Unexpected pool error", err);
182
+ this.handlePoolError(err);
183
+ });
184
+ this.setupPoolErrorHandling();
185
+ this.testConnection();
186
+ }
187
+ /**
188
+ * Handles a pool error by attempting to reconnect the pool.
189
+ *
190
+ * @param {Error} error The error that occurred in the pool.
191
+ * @throws {Error} If failed to reconnect the pool.
192
+ */
193
+ async handlePoolError(error) {
194
+ logger2.error("Pool error occurred, attempting to reconnect", {
195
+ error: error.message
196
+ });
197
+ try {
198
+ await this.pool.end();
199
+ this.pool = new Pool({
200
+ ...this.pool.options,
201
+ connectionTimeoutMillis: this.connectionTimeout
202
+ });
203
+ await this.testConnection();
204
+ logger2.success("Pool reconnection successful");
205
+ } catch (reconnectError) {
206
+ logger2.error("Failed to reconnect pool", {
207
+ error: reconnectError instanceof Error ? reconnectError.message : String(reconnectError)
208
+ });
209
+ throw reconnectError;
210
+ }
211
+ }
212
+ /**
213
+ * Asynchronously tests the database connection by executing a query to get the current timestamp.
214
+ *
215
+ * @returns {Promise<boolean>} - A Promise that resolves to true if the database connection test is successful.
216
+ */
217
+ async testConnection() {
218
+ let client = null;
219
+ try {
220
+ client = await this.pool.connect();
221
+ const result = await client.query("SELECT NOW()");
222
+ logger2.success("Database connection test successful:", result.rows[0]);
223
+ return true;
224
+ } catch (error) {
225
+ logger2.error("Database connection test failed:", error);
226
+ throw new Error(
227
+ `Failed to connect to database: ${error.message}`
228
+ );
229
+ } finally {
230
+ if (client) client.release();
231
+ }
232
+ }
233
+ /**
234
+ * Sets up event listeners to handle pool cleanup on SIGINT, SIGTERM, and beforeExit events.
235
+ */
236
+ setupPoolErrorHandling() {
237
+ process.on("SIGINT", async () => {
238
+ await this.cleanup();
239
+ process.exit(0);
240
+ });
241
+ process.on("SIGTERM", async () => {
242
+ await this.cleanup();
243
+ process.exit(0);
244
+ });
245
+ process.on("beforeExit", async () => {
246
+ await this.cleanup();
247
+ });
248
+ }
249
+ /**
250
+ * Get the connection pool.
251
+ * @returns {PgPool} The connection pool
252
+ * @throws {Error} If the connection manager is shutting down or an error occurs when trying to get the connection from the pool
253
+ */
254
+ getConnection() {
255
+ if (this.isShuttingDown) {
256
+ throw new Error("Connection manager is shutting down");
257
+ }
258
+ try {
259
+ return this.pool;
260
+ } catch (error) {
261
+ logger2.error("Failed to get connection from pool:", error);
262
+ throw error;
263
+ }
264
+ }
265
+ /**
266
+ * Asynchronously acquires a database client from the connection pool.
267
+ *
268
+ * @returns {Promise<pkg.PoolClient>} A Promise that resolves with the acquired database client.
269
+ * @throws {Error} If an error occurs while acquiring the database client.
270
+ */
271
+ async getClient() {
272
+ try {
273
+ return await this.pool.connect();
274
+ } catch (error) {
275
+ logger2.error("Failed to acquire a database client:", error);
276
+ throw error;
277
+ }
278
+ }
279
+ /**
280
+ * Initializes the PostgreSQL connection manager by testing the connection and logging the result.
281
+ *
282
+ * @returns {Promise<void>} A Promise that resolves once the manager is successfully initialized
283
+ * @throws {Error} If there is an error initializing the connection manager
284
+ */
285
+ async initialize() {
286
+ try {
287
+ await this.testConnection();
288
+ logger2.info("PostgreSQL connection manager initialized successfully");
289
+ } catch (error) {
290
+ logger2.error("Failed to initialize connection manager:", error);
291
+ throw error;
292
+ }
293
+ }
294
+ /**
295
+ * Asynchronously close the current process by executing a cleanup function.
296
+ * @returns A promise that resolves once the cleanup is complete.
297
+ */
298
+ async close() {
299
+ await this.cleanup();
300
+ }
301
+ /**
302
+ * Cleans up and closes the database pool.
303
+ * @returns {Promise<void>} A Promise that resolves when the database pool is closed.
304
+ */
305
+ async cleanup() {
306
+ try {
307
+ await this.pool.end();
308
+ logger2.info("Database pool closed");
309
+ } catch (error) {
310
+ logger2.error("Error closing database pool:", error);
311
+ }
312
+ }
313
+ /**
314
+ * Asynchronously runs database migrations using the Drizzle library.
315
+ *
316
+ * Drizzle will first check if the migrations are already applied.
317
+ * If there is a diff between database schema and migrations, it will apply the migrations.
318
+ * If they are already applied, it will skip them.
319
+ *
320
+ * @returns {Promise<void>} A Promise that resolves once the migrations are completed successfully.
321
+ */
322
+ async runMigrations() {
323
+ try {
324
+ const db = drizzle2(this.pool);
325
+ const __filename = fileURLToPath2(import.meta.url);
326
+ const __dirname = path.dirname(__filename);
327
+ await migrate2(db, {
328
+ migrationsFolder: path.resolve(__dirname, "../drizzle/migrations")
329
+ });
330
+ } catch (error) {
331
+ logger2.error("Failed to run database migrations (pg):", error);
332
+ console.trace(error);
333
+ }
334
+ }
335
+ };
336
+
337
+ export {
338
+ __name,
339
+ PGliteClientManager,
340
+ PostgresConnectionManager
341
+ };
342
+ //# sourceMappingURL=chunk-JA2K6HCO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pglite/manager.ts","../src/pg/manager.ts"],"sourceRoot":"./","sourcesContent":["import { dirname as pathDirname, resolve as pathResolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { PGlite, type PGliteOptions } from \"@electric-sql/pglite\";\nimport { fuzzystrmatch } from \"@electric-sql/pglite/contrib/fuzzystrmatch\";\nimport { vector } from \"@electric-sql/pglite/vector\";\nimport { logger } from \"@elizaos/core\";\nimport { drizzle } from \"drizzle-orm/pglite\";\nimport { migrate } from \"drizzle-orm/pglite/migrator\";\nimport type { IDatabaseClientManager } from \"../types\";\n\n/**\n * Class representing a database client manager for PGlite.\n * @implements { IDatabaseClientManager }\n */\nexport class PGliteClientManager implements IDatabaseClientManager<PGlite> {\n\tprivate client: PGlite;\n\tprivate shuttingDown = false;\n\tprivate readonly shutdownTimeout = 500;\n\n\t/**\n\t * Constructor for creating a new instance of PGlite with the provided options.\n\t * Initializes the PGlite client with additional extensions.\n\t * @param {PGliteOptions} options - The options to configure the PGlite client.\n\t */\n\tconstructor(options: PGliteOptions) {\n\t\tthis.client = new PGlite({\n\t\t\t...options,\n\t\t\textensions: {\n\t\t\t\tvector,\n\t\t\t\tfuzzystrmatch,\n\t\t\t},\n\t\t});\n\t\tthis.setupShutdownHandlers();\n\t}\n\n\t/**\n\t * Retrieves the PostgreSQL lite connection.\n\t *\n\t * @returns {PGlite} The PostgreSQL lite connection.\n\t * @throws {Error} If the client manager is currently shutting down.\n\t */\n\tpublic getConnection(): PGlite {\n\t\tif (this.shuttingDown) {\n\t\t\tthrow new Error(\"Client manager is shutting down\");\n\t\t}\n\t\treturn this.client;\n\t}\n\n\t/**\n\t * Initiates a graceful shutdown of the PGlite client.\n\t * Checks if the client is already in the process of shutting down.\n\t * Logs the start of shutdown process and sets shuttingDown flag to true.\n\t * Sets a timeout for the shutdown process and forces closure of database connection if timeout is reached.\n\t * Handles the shutdown process, closes the client connection, clears the timeout, and logs the completion of shutdown.\n\t * Logs any errors that occur during the shutdown process.\n\t */\n\tprivate async gracefulShutdown() {\n\t\tif (this.shuttingDown) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.shuttingDown = true;\n\t\tlogger.info(\"Starting graceful shutdown of PGlite client...\");\n\n\t\tconst timeout = setTimeout(() => {\n\t\t\tlogger.warn(\n\t\t\t\t\"Shutdown timeout reached, forcing database connection closure...\",\n\t\t\t);\n\t\t\tthis.client.close().finally(() => {\n\t\t\t\tprocess.exit(1);\n\t\t\t});\n\t\t}, this.shutdownTimeout);\n\n\t\ttry {\n\t\t\tawait this.client.close();\n\t\t\tclearTimeout(timeout);\n\t\t\tlogger.info(\"PGlite client shutdown completed successfully\");\n\t\t\tprocess.exit(0);\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Error during graceful shutdown:\", error);\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\t/**\n\t * Sets up shutdown handlers for SIGINT, SIGTERM, and beforeExit events to gracefully shutdown the application.\n\t * @private\n\t */\n\tprivate setupShutdownHandlers() {\n\t\tprocess.on(\"SIGINT\", async () => {\n\t\t\tawait this.gracefulShutdown();\n\t\t});\n\n\t\tprocess.on(\"SIGTERM\", async () => {\n\t\t\tawait this.gracefulShutdown();\n\t\t});\n\n\t\tprocess.on(\"beforeExit\", async () => {\n\t\t\tawait this.gracefulShutdown();\n\t\t});\n\t}\n\n\t/**\n\t * Initializes the client for PGlite.\n\t *\n\t * @returns {Promise<void>} A Promise that resolves when the client is initialized successfully\n\t */\n\tpublic async initialize(): Promise<void> {\n\t\ttry {\n\t\t\tawait this.client.waitReady;\n\t\t\tlogger.info(\"PGlite client initialized successfully\");\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Failed to initialize PGlite client:\", error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Asynchronously closes the resource. If the resource is not already shutting down,\n\t * it performs a graceful shutdown before closing.\n\t *\n\t * @returns A promise that resolves once the resource has been closed.\n\t */\n\tpublic async close(): Promise<void> {\n\t\tif (!this.shuttingDown) {\n\t\t\tawait this.gracefulShutdown();\n\t\t}\n\t}\n\n\t/**\n\t * Check if the system is currently shutting down.\n\t *\n\t * @returns {boolean} True if the system is shutting down, false otherwise.\n\t */\n\tpublic isShuttingDown(): boolean {\n\t\treturn this.shuttingDown;\n\t}\n\n\t/**\n\t * Asynchronously runs database migrations using Drizzle.\n\t * \n\t * Drizzle will first check if the migrations are already applied.\n\t * If there is a diff between database schema and migrations, it will apply the migrations.\n\t * If they are already applied, it will skip them.\n\t *\n\t * @returns {Promise<void>} A Promise that resolves once the migrations are completed successfully.\n\t */\n\tasync runMigrations(): Promise<void> {\n\t\ttry {\n\t\t\tconst db = drizzle(this.client);\n\n\t\t\tconst __filename = fileURLToPath(import.meta.url);\n\t\t\tconst __dirname = pathDirname(__filename);\n\n\t\t\tawait migrate(db, {\n\t\t\t\tmigrationsFolder: pathResolve(__dirname, \"../drizzle/migrations\"),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Failed to run database migrations (pglite):\", error);\n\t\t\t// throw error;\n\t\t\tconsole.trace(error);\n\t\t}\n\t}\n}\n","import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { logger } from \"@elizaos/core\";\nimport { drizzle } from \"drizzle-orm/node-postgres\";\nimport { migrate } from \"drizzle-orm/node-postgres/migrator\";\nimport pkg, { type Pool as PgPool } from \"pg\";\nimport type { IDatabaseClientManager } from \"../types\";\n\nconst { Pool } = pkg;\n\n/**\n * Manages connections to a PostgreSQL database using a connection pool.\n * Implements IDatabaseClientManager interface.\n */\n\nexport class PostgresConnectionManager\n\timplements IDatabaseClientManager<PgPool>\n{\n\tprivate pool: PgPool;\n\tprivate isShuttingDown = false;\n\tprivate readonly connectionTimeout: number = 5000;\n\n\t/**\n\t * Constructor for creating a connection pool.\n\t * @param {string} connectionString - The connection string used to connect to the database.\n\t */\n\tconstructor(connectionString: string) {\n\t\tconst defaultConfig = {\n\t\t\tmax: 20,\n\t\t\tidleTimeoutMillis: 30000,\n\t\t\tconnectionTimeoutMillis: this.connectionTimeout,\n\t\t};\n\n\t\tthis.pool = new Pool({\n\t\t\t...defaultConfig,\n\t\t\tconnectionString,\n\t\t});\n\n\t\tthis.pool.on(\"error\", (err) => {\n\t\t\tlogger.error(\"Unexpected pool error\", err);\n\t\t\tthis.handlePoolError(err);\n\t\t});\n\n\t\tthis.setupPoolErrorHandling();\n\t\tthis.testConnection();\n\t}\n\n\t/**\n\t * Handles a pool error by attempting to reconnect the pool.\n\t *\n\t * @param {Error} error The error that occurred in the pool.\n\t * @throws {Error} If failed to reconnect the pool.\n\t */\n\tprivate async handlePoolError(error: Error) {\n\t\tlogger.error(\"Pool error occurred, attempting to reconnect\", {\n\t\t\terror: error.message,\n\t\t});\n\n\t\ttry {\n\t\t\tawait this.pool.end();\n\n\t\t\tthis.pool = new Pool({\n\t\t\t\t...this.pool.options,\n\t\t\t\tconnectionTimeoutMillis: this.connectionTimeout,\n\t\t\t});\n\n\t\t\tawait this.testConnection();\n\t\t\tlogger.success(\"Pool reconnection successful\");\n\t\t} catch (reconnectError) {\n\t\t\tlogger.error(\"Failed to reconnect pool\", {\n\t\t\t\terror:\n\t\t\t\t\treconnectError instanceof Error\n\t\t\t\t\t\t? reconnectError.message\n\t\t\t\t\t\t: String(reconnectError),\n\t\t\t});\n\t\t\tthrow reconnectError;\n\t\t}\n\t}\n\n\t/**\n\t * Asynchronously tests the database connection by executing a query to get the current timestamp.\n\t *\n\t * @returns {Promise<boolean>} - A Promise that resolves to true if the database connection test is successful.\n\t */\n\tasync testConnection(): Promise<boolean> {\n\t\tlet client: pkg.PoolClient | null = null;\n\t\ttry {\n\t\t\tclient = await this.pool.connect();\n\t\t\tconst result = await client.query(\"SELECT NOW()\");\n\t\t\tlogger.success(\"Database connection test successful:\", result.rows[0]);\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Database connection test failed:\", error);\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to connect to database: ${(error as Error).message}`,\n\t\t\t);\n\t\t} finally {\n\t\t\tif (client) client.release();\n\t\t}\n\t}\n\n\t/**\n\t * Sets up event listeners to handle pool cleanup on SIGINT, SIGTERM, and beforeExit events.\n\t */\n\tprivate setupPoolErrorHandling() {\n\t\tprocess.on(\"SIGINT\", async () => {\n\t\t\tawait this.cleanup();\n\t\t\tprocess.exit(0);\n\t\t});\n\n\t\tprocess.on(\"SIGTERM\", async () => {\n\t\t\tawait this.cleanup();\n\t\t\tprocess.exit(0);\n\t\t});\n\n\t\tprocess.on(\"beforeExit\", async () => {\n\t\t\tawait this.cleanup();\n\t\t});\n\t}\n\n\t/**\n\t * Get the connection pool.\n\t * @returns {PgPool} The connection pool\n\t * @throws {Error} If the connection manager is shutting down or an error occurs when trying to get the connection from the pool\n\t */\n\tpublic getConnection(): PgPool {\n\t\tif (this.isShuttingDown) {\n\t\t\tthrow new Error(\"Connection manager is shutting down\");\n\t\t}\n\n\t\ttry {\n\t\t\treturn this.pool;\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Failed to get connection from pool:\", error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Asynchronously acquires a database client from the connection pool.\n\t *\n\t * @returns {Promise<pkg.PoolClient>} A Promise that resolves with the acquired database client.\n\t * @throws {Error} If an error occurs while acquiring the database client.\n\t */\n\tpublic async getClient(): Promise<pkg.PoolClient> {\n\t\ttry {\n\t\t\treturn await this.pool.connect();\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Failed to acquire a database client:\", error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Initializes the PostgreSQL connection manager by testing the connection and logging the result.\n\t *\n\t * @returns {Promise<void>} A Promise that resolves once the manager is successfully initialized\n\t * @throws {Error} If there is an error initializing the connection manager\n\t */\n\tpublic async initialize(): Promise<void> {\n\t\ttry {\n\t\t\tawait this.testConnection();\n\t\t\tlogger.info(\"PostgreSQL connection manager initialized successfully\");\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Failed to initialize connection manager:\", error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Asynchronously close the current process by executing a cleanup function.\n\t * @returns A promise that resolves once the cleanup is complete.\n\t */\n\tpublic async close(): Promise<void> {\n\t\tawait this.cleanup();\n\t}\n\n\t/**\n\t * Cleans up and closes the database pool.\n\t * @returns {Promise<void>} A Promise that resolves when the database pool is closed.\n\t */\n\tasync cleanup(): Promise<void> {\n\t\ttry {\n\t\t\tawait this.pool.end();\n\t\t\tlogger.info(\"Database pool closed\");\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Error closing database pool:\", error);\n\t\t}\n\t}\n\n\t/**\n\t * Asynchronously runs database migrations using the Drizzle library.\n\t *\n\t * Drizzle will first check if the migrations are already applied.\n\t * If there is a diff between database schema and migrations, it will apply the migrations.\n\t * If they are already applied, it will skip them.\n\t *\n\t * @returns {Promise<void>} A Promise that resolves once the migrations are completed successfully.\n\t */\n\tasync runMigrations(): Promise<void> {\n\t\ttry {\n\t\t\tconst db = drizzle(this.pool);\n\n\t\t\tconst __filename = fileURLToPath(import.meta.url);\n\t\t\tconst __dirname = path.dirname(__filename);\n\n\t\t\tawait migrate(db, {\n\t\t\t\tmigrationsFolder: path.resolve(__dirname, \"../drizzle/migrations\"),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Failed to run database migrations (pg):\", error);\n\t\t\tconsole.trace(error);\n\t\t}\n\t}\n}\n"],"mappings":"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","names":["fileURLToPath","logger","drizzle","migrate","logger","drizzle","fileURLToPath","migrate"]}