@elizaos/plugin-sql 1.0.0-alpha.7 → 1.0.0-beta.0

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,31 +9,19 @@ 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:
15
+
28
16
  ```typescript
29
17
  VECTOR_DIMS = {
30
- SMALL: 384,
31
- MEDIUM: 512,
32
- LARGE: 768,
33
- XL: 1024,
34
- XXL: 1536,
35
- XXXL: 3072
36
- }
18
+ SMALL: 384,
19
+ MEDIUM: 512,
20
+ LARGE: 768,
21
+ XL: 1024,
22
+ XXL: 1536,
23
+ XXXL: 3072,
24
+ };
37
25
  ```
38
26
 
39
27
  Important Note: Once an agent is initialized with a specific embedding dimension, it cannot be changed. Attempting to change the dimension will result in an error: "Cannot change embedding dimension for agent"
@@ -49,33 +37,57 @@ Important Note: Once an agent is initialized with a specific embedding dimension
49
37
  - Room and participant management
50
38
  - Goal tracking system
51
39
 
40
+ ## Database Schema
41
+
42
+ The plugin uses a structured schema with the following main tables:
43
+
44
+ ### Core Tables
45
+
46
+ - **Agent**: Stores agent information and configurations
47
+ - **Room**: Manages conversation rooms and their settings
48
+ - **Participant**: Tracks participants in rooms
49
+ - **Memory**: Stores agent memories with vector embeddings for semantic search
50
+ - **Embedding**: Manages vector embeddings for various entities
51
+ - **Entity**: Represents entities that agents can interact with
52
+ - **Relationship**: Tracks relationships between entities
53
+ - **Component**: Stores agent components and their configurations
54
+ - **Tasks**: Manages tasks and goals for agents
55
+ - **Log**: Stores system logs
56
+ - **Cache**: Provides a caching mechanism for frequently accessed data
57
+ - **World**: Manages world settings and configurations
58
+
59
+ 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.
60
+
52
61
  ## Usage
53
62
 
54
63
  The adapter is typically used as part of the ElizaOS runtime:
55
64
 
56
65
  ```typescript
57
66
  async function findDatabaseAdapter(runtime: IAgentRuntime) {
58
- let adapter = runtime.getDatabaseAdapter();
59
-
67
+ let adapter = runtime;
68
+
69
+ if (!adapter) {
70
+ const drizzleAdapterPlugin = await import('@elizaos/plugin-sql');
71
+ const drizzleAdapterPluginDefault = drizzleAdapterPlugin.default;
72
+ adapter = drizzleAdapterPluginDefault.adapter;
60
73
  if (!adapter) {
61
- const drizzleAdapterPlugin = await import('@elizaos/plugin-sql');
62
- const drizzleAdapterPluginDefault = drizzleAdapterPlugin.default;
63
- adapter = drizzleAdapterPluginDefault.adapter;
64
- if (!adapter) {
65
- throw new Error("Internal error: No database adapter found for default plugin-sql");
66
- }
67
- } else if (!adapter) {
68
- throw new Error("Multiple database adapters found. You must have no more than one. Adjust your plugins configuration.");
74
+ throw new Error('Internal error: No database adapter found for default plugin-sql');
69
75
  }
70
-
71
- const adapterInterface = await adapter?.init(runtime);
72
- return adapterInterface;
76
+ } else if (!adapter) {
77
+ throw new Error(
78
+ 'Multiple database adapters found. You must have no more than one. Adjust your plugins configuration.'
79
+ );
80
+ }
81
+
82
+ const adapterInterface = await adapter?.init(runtime);
83
+ return adapterInterface;
73
84
  }
74
85
  ```
75
86
 
76
87
  ## Error Handling Configuration
77
88
 
78
89
  The adapter implements the following error handling configurations:
90
+
79
91
  ```typescript
80
92
  {
81
93
  failureThreshold: 5,
@@ -94,9 +106,20 @@ The adapter implements the following error handling configurations:
94
106
  - PostgreSQL with vector extension installed
95
107
  - Node.js or Bun (≥1.2.2)
96
108
 
109
+ ## Environment Variables
110
+
111
+ The plugin uses the following environment variables:
112
+
113
+ - `POSTGRES_URL`: Connection string for PostgreSQL database (e.g., `postgresql://user:password@localhost:5432/dbname`)
114
+ - If not provided, the plugin will use PGlite as a fallback
115
+ - `PGLITE_DATA_DIR`: (Optional) Directory for PGlite data storage (default: `../../pglite`)
116
+
117
+ These variables should be defined in a `.env` file at the root of your project.
118
+
97
119
  ## Database Pool Configuration
98
120
 
99
121
  Default pool configuration:
122
+
100
123
  ```typescript
101
124
  {
102
125
  max: 20,
@@ -110,38 +133,45 @@ Default pool configuration:
110
133
  The adapter supports two approaches to managing database schema:
111
134
 
112
135
  ### 1. Initial Setup
136
+
113
137
  Migrations are automatically run during initialization if:
138
+
114
139
  - Database tables don't exist
115
140
  - Vector extension is not found
116
141
 
117
142
  This is handled internally by:
143
+
118
144
  ```typescript
119
145
  await runMigrations(pgPool);
120
146
  ```
121
147
 
122
148
  ### 2. Schema Updates
149
+
123
150
  To update the schema:
124
151
 
125
152
  1. Install drizzle-kit (if not already installed):
153
+
126
154
  ```bash
127
- pnpm add -D drizzle-kit
155
+ bun add -D drizzle-kit
128
156
  ```
129
157
 
130
158
  2. Create or update your schema files (e.g., `schema/account.ts`):
159
+
131
160
  ```typescript
132
- import { pgTable, text, uuid } from "drizzle-orm/pg-core";
133
- import { sql } from "drizzle-orm";
134
-
135
- export const accountTable = pgTable("accounts", {
136
- id: uuid("id").primaryKey().notNull(),
137
- name: text("name"),
138
- email: text("email").notNull(),
139
- // Add new fields here
140
- newField: text("newField"),
161
+ import { pgTable, text, uuid } from 'drizzle-orm/pg-core';
162
+ import { sql } from 'drizzle-orm';
163
+
164
+ export const accountTable = pgTable('accounts', {
165
+ id: uuid('id').primaryKey().notNull(),
166
+ name: text('name'),
167
+ email: text('email').notNull(),
168
+ // Add new fields here
169
+ newField: text('newField'),
141
170
  });
142
171
  ```
143
172
 
144
173
  3. Generate migrations:
174
+
145
175
  ```bash
146
176
  npx drizzle-kit generate:pg
147
177
  ```
@@ -151,25 +181,108 @@ This will create SQL migration files in your migrations directory.
151
181
  4. Apply migrations using one of these methods:
152
182
 
153
183
  a. Using drizzle-kit:
184
+
154
185
  ```bash
155
186
  npx drizzle-kit push:pg
156
187
  ```
157
188
 
158
189
  b. Through your application code:
190
+
159
191
  ```typescript
160
- import { migrate } from "drizzle-orm/node-postgres/migrator";
192
+ import { migrate } from 'drizzle-orm/node-postgres/migrator';
193
+
194
+ await migrate(db, { migrationsFolder: './drizzle' });
195
+ ```
196
+
197
+ c. Using the provided migration script:
198
+
199
+ ```bash
200
+ npm run migrate
201
+ # or
202
+ bun migrate
203
+ ```
204
+
205
+ d. Using drizzle-kit migrate command:
206
+
207
+ ```bash
208
+ npx drizzle-kit migrate
209
+ ```
210
+
211
+ 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.
212
+
213
+ ### Migration Configuration
214
+
215
+ The plugin uses a `drizzle.config.ts` file to configure migrations:
161
216
 
162
- await migrate(db, { migrationsFolder: "./drizzle" });
217
+ ```typescript
218
+ import { config } from 'dotenv';
219
+ import { defineConfig } from 'drizzle-kit';
220
+
221
+ config({ path: '../../.env' });
222
+
223
+ export default defineConfig({
224
+ dialect: 'postgresql',
225
+ schema: './src/schema/index.ts',
226
+ out: './drizzle/migrations',
227
+ dbCredentials: {
228
+ url: process.env.POSTGRES_URL || 'file://../../pglite',
229
+ },
230
+ breakpoints: true,
231
+ });
163
232
  ```
164
233
 
234
+ ### Database Support
235
+
236
+ The plugin supports two database backends:
237
+
238
+ 1. **PostgreSQL**: Used when `POSTGRES_URL` environment variable is provided
239
+ 2. **PGlite**: Used as a fallback when no PostgreSQL URL is provided
240
+
241
+ Both backends use the same migration files, ensuring consistent schema across environments.
242
+
165
243
  ### Note on Vector Support
244
+
166
245
  Make sure the PostgreSQL vector extension is installed before running migrations. The adapter will validate vector setup during initialization.
167
246
 
168
247
  ## Clean Shutdown
169
248
 
170
249
  The adapter implements cleanup handlers for:
250
+
171
251
  - SIGINT
172
252
  - SIGTERM
173
253
  - beforeExit
174
254
 
175
255
  These ensure proper closing of database connections when the application shuts down.
256
+
257
+ ## Implementation Details
258
+
259
+ ### Connection Management
260
+
261
+ The plugin uses a global singleton pattern to manage database connections. This approach ensures that:
262
+
263
+ 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.
264
+
265
+ 2. **Resource Efficiency**: Prevents multiple connection pools to the same database, which could lead to resource exhaustion.
266
+
267
+ 3. **Consistent State**: Ensures all parts of the application share the same database connection state.
268
+
269
+ 4. **Proper Cleanup**: Facilitates proper cleanup of database connections during application shutdown, preventing connection leaks.
270
+
271
+ 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.
272
+
273
+ ```typescript
274
+ // Example of the singleton pattern implementation
275
+ const GLOBAL_SINGLETONS = Symbol.for('@elizaos/plugin-sql/global-singletons');
276
+
277
+ // Store managers in a global symbol registry
278
+ if (!globalSymbols[GLOBAL_SINGLETONS]) {
279
+ globalSymbols[GLOBAL_SINGLETONS] = {};
280
+ }
281
+
282
+ // Reuse existing managers or create new ones when needed
283
+ if (!globalSingletons.postgresConnectionManager) {
284
+ globalSingletons.postgresConnectionManager = new PostgresConnectionManager(config.postgresUrl);
285
+ }
286
+ ```
287
+
288
+ This approach is especially critical for PGlite connections, which require careful management to ensure proper shutdown and prevent resource leaks.
@@ -0,0 +1,40 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
9
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
10
+ }) : x)(function(x) {
11
+ if (typeof require !== "undefined") return require.apply(this, arguments);
12
+ throw Error('Dynamic require of "' + x + '" is not supported');
13
+ });
14
+ var __commonJS = (cb, mod) => function __require2() {
15
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
16
+ };
17
+ var __copyProps = (to, from, except, desc) => {
18
+ if (from && typeof from === "object" || typeof from === "function") {
19
+ for (let key of __getOwnPropNames(from))
20
+ if (!__hasOwnProp.call(to, key) && key !== except)
21
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
22
+ }
23
+ return to;
24
+ };
25
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
26
+ // If the importer is in node compatibility mode or this is not an ESM
27
+ // file that has been converted to a CommonJS file using a Babel-
28
+ // compatible transform (i.e. "__esModule" has not been set), then set
29
+ // "default" to the CommonJS "module.exports" for node compatibility.
30
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
31
+ mod
32
+ ));
33
+
34
+ export {
35
+ __name,
36
+ __require,
37
+ __commonJS,
38
+ __toESM
39
+ };
40
+ //# sourceMappingURL=chunk-I3JSTNED.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourceRoot":"./","sourcesContent":[],"mappings":"","names":[]}
@@ -1,8 +1,9 @@
1
- var __defProp = Object.defineProperty;
2
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
1
+ import {
2
+ __name
3
+ } from "./chunk-I3JSTNED.js";
3
4
 
4
- // src/pg-lite/manager.ts
5
- import path from "node:path";
5
+ // src/pglite/manager.ts
6
+ import { dirname as pathDirname, resolve as pathResolve } from "node:path";
6
7
  import { fileURLToPath } from "node:url";
7
8
  import { PGlite } from "@electric-sql/pglite";
8
9
  import { fuzzystrmatch } from "@electric-sql/pglite/contrib/fuzzystrmatch";
@@ -10,8 +11,6 @@ import { vector } from "@electric-sql/pglite/vector";
10
11
  import { logger } from "@elizaos/core";
11
12
  import { drizzle } from "drizzle-orm/pglite";
12
13
  import { migrate } from "drizzle-orm/pglite/migrator";
13
- var __filename = fileURLToPath(import.meta.url);
14
- var __dirname = path.dirname(__filename);
15
14
  var PGliteClientManager = class {
16
15
  static {
17
16
  __name(this, "PGliteClientManager");
@@ -61,9 +60,7 @@ var PGliteClientManager = class {
61
60
  this.shuttingDown = true;
62
61
  logger.info("Starting graceful shutdown of PGlite client...");
63
62
  const timeout = setTimeout(() => {
64
- logger.warn(
65
- "Shutdown timeout reached, forcing database connection closure..."
66
- );
63
+ logger.warn("Shutdown timeout reached, forcing database connection closure...");
67
64
  this.client.close().finally(() => {
68
65
  process.exit(1);
69
66
  });
@@ -129,31 +126,33 @@ var PGliteClientManager = class {
129
126
  /**
130
127
  * Asynchronously runs database migrations using Drizzle.
131
128
  *
129
+ * Drizzle will first check if the migrations are already applied.
130
+ * If there is a diff between database schema and migrations, it will apply the migrations.
131
+ * If they are already applied, it will skip them.
132
+ *
132
133
  * @returns {Promise<void>} A Promise that resolves once the migrations are completed successfully.
133
134
  */
134
135
  async runMigrations() {
135
136
  try {
136
137
  const db = drizzle(this.client);
138
+ const __filename = fileURLToPath(import.meta.url);
139
+ const __dirname = pathDirname(__filename);
137
140
  await migrate(db, {
138
- migrationsFolder: path.resolve(__dirname, "../drizzle/migrations")
141
+ migrationsFolder: pathResolve(__dirname, "../drizzle/migrations")
139
142
  });
140
- logger.info("Migrations completed successfully!");
141
143
  } catch (error) {
142
- logger.error("Failed to run database migrations:", error);
144
+ logger.error("Failed to run database migrations (pglite):", error);
143
145
  }
144
146
  }
145
147
  };
146
148
 
147
149
  // src/pg/manager.ts
148
- import path2 from "node:path";
149
- import { fileURLToPath as fileURLToPath2 } from "node:url";
150
150
  import { logger as logger2 } from "@elizaos/core";
151
151
  import { drizzle as drizzle2 } from "drizzle-orm/node-postgres";
152
- import { migrate as migrate2 } from "drizzle-orm/node-postgres/migrator";
152
+ import { dirname as pathDirname2, resolve as pathResolve2 } from "node:path";
153
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
153
154
  import pkg from "pg";
154
155
  var { Pool } = pkg;
155
- var __filename2 = fileURLToPath2(import.meta.url);
156
- var __dirname2 = path2.dirname(__filename2);
157
156
  var PostgresConnectionManager = class {
158
157
  static {
159
158
  __name(this, "PostgresConnectionManager");
@@ -165,7 +164,7 @@ var PostgresConnectionManager = class {
165
164
  * Constructor for creating a connection pool.
166
165
  * @param {string} connectionString - The connection string used to connect to the database.
167
166
  */
168
- constructor(connectionString) {
167
+ constructor(db, connectionString) {
169
168
  const defaultConfig = {
170
169
  max: 20,
171
170
  idleTimeoutMillis: 3e4,
@@ -180,7 +179,6 @@ var PostgresConnectionManager = class {
180
179
  this.handlePoolError(err);
181
180
  });
182
181
  this.setupPoolErrorHandling();
183
- this.testConnection();
184
182
  }
185
183
  /**
186
184
  * Handles a pool error by attempting to reconnect the pool.
@@ -221,9 +219,7 @@ var PostgresConnectionManager = class {
221
219
  return true;
222
220
  } catch (error) {
223
221
  logger2.error("Database connection test failed:", error);
224
- throw new Error(
225
- `Failed to connect to database: ${error.message}`
226
- );
222
+ throw new Error(`Failed to connect to database: ${error.message}`);
227
223
  } finally {
228
224
  if (client) client.release();
229
225
  }
@@ -282,8 +278,6 @@ var PostgresConnectionManager = class {
282
278
  */
283
279
  async initialize() {
284
280
  try {
285
- await this.testConnection();
286
- logger2.info("PostgreSQL connection manager initialized successfully");
287
281
  } catch (error) {
288
282
  logger2.error("Failed to initialize connection manager:", error);
289
283
  throw error;
@@ -309,26 +303,37 @@ var PostgresConnectionManager = class {
309
303
  }
310
304
  }
311
305
  /**
312
- * Asynchronously runs database migrations using the Drizzle library.
306
+ * Asynchronously runs database migrations using the Drizzle library and ensures
307
+ * the database schema is always up to date with the current schema definition.
313
308
  *
314
- * @returns {Promise<void>} A Promise that resolves once the migrations are completed successfully.
309
+ * First applies existing migrations, then uses drizzle-kit push to ensure
310
+ * the database schema matches the current schema definition.
311
+ *
312
+ * @returns {Promise<void>} A Promise that resolves once the schema updates are completed successfully.
315
313
  */
316
314
  async runMigrations() {
317
- try {
318
- const db = drizzle2(this.pool);
319
- await migrate2(db, {
320
- migrationsFolder: path2.resolve(__dirname2, "../drizzle/migrations")
321
- });
322
- logger2.info("Migrations completed successfully!");
323
- } catch (error) {
324
- logger2.error("Failed to run database migrations:", error);
325
- }
315
+ const { existsSync } = await import("fs");
316
+ const db = drizzle2(this.pool);
317
+ const packagePath = (await import.meta.resolve("@elizaos/plugin-sql")).replace("file://", "");
318
+ const packageMigrationsPath = pathResolve2(pathDirname2(packagePath), "../drizzle/migrations");
319
+ console.log("packageMigrationsPath", packageMigrationsPath);
320
+ const __filename = fileURLToPath2(import.meta.url);
321
+ const __dirname = pathDirname2(__filename);
322
+ const appMigrationPath = pathResolve2(__dirname, "../drizzle/migrations");
323
+ console.log("appMigrationPath", appMigrationPath);
324
+ const migrationsFolder = existsSync(packageMigrationsPath) ? packageMigrationsPath : appMigrationPath;
325
+ console.log("migrationsFolder", migrationsFolder);
326
+ const { execa } = await import("./execa-I2XBH3EI.js");
327
+ await execa("npx", ["drizzle-kit", "push"], {
328
+ cwd: migrationsFolder
329
+ });
330
+ console.log("migrated");
331
+ await new Promise((resolve) => setTimeout(resolve, 2e3));
326
332
  }
327
333
  };
328
334
 
329
335
  export {
330
- __name,
331
336
  PGliteClientManager,
332
337
  PostgresConnectionManager
333
338
  };
334
- //# sourceMappingURL=chunk-LLEN3JTK.js.map
339
+ //# sourceMappingURL=chunk-PSEXCDLP.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 private client: PGlite;\n private shuttingDown = false;\n private readonly shutdownTimeout = 500;\n\n /**\n * Constructor for creating a new instance of PGlite with the provided options.\n * Initializes the PGlite client with additional extensions.\n * @param {PGliteOptions} options - The options to configure the PGlite client.\n */\n constructor(options: PGliteOptions) {\n this.client = new PGlite({\n ...options,\n extensions: {\n vector,\n fuzzystrmatch,\n },\n });\n this.setupShutdownHandlers();\n }\n\n /**\n * Retrieves the PostgreSQL lite connection.\n *\n * @returns {PGlite} The PostgreSQL lite connection.\n * @throws {Error} If the client manager is currently shutting down.\n */\n public getConnection(): PGlite {\n if (this.shuttingDown) {\n throw new Error('Client manager is shutting down');\n }\n return this.client;\n }\n\n /**\n * Initiates a graceful shutdown of the PGlite client.\n * Checks if the client is already in the process of shutting down.\n * Logs the start of shutdown process and sets shuttingDown flag to true.\n * Sets a timeout for the shutdown process and forces closure of database connection if timeout is reached.\n * Handles the shutdown process, closes the client connection, clears the timeout, and logs the completion of shutdown.\n * Logs any errors that occur during the shutdown process.\n */\n private async gracefulShutdown() {\n if (this.shuttingDown) {\n return;\n }\n\n this.shuttingDown = true;\n logger.info('Starting graceful shutdown of PGlite client...');\n\n const timeout = setTimeout(() => {\n logger.warn('Shutdown timeout reached, forcing database connection closure...');\n this.client.close().finally(() => {\n process.exit(1);\n });\n }, this.shutdownTimeout);\n\n try {\n await this.client.close();\n clearTimeout(timeout);\n logger.info('PGlite client shutdown completed successfully');\n process.exit(0);\n } catch (error) {\n logger.error('Error during graceful shutdown:', error);\n process.exit(1);\n }\n }\n\n /**\n * Sets up shutdown handlers for SIGINT, SIGTERM, and beforeExit events to gracefully shutdown the application.\n * @private\n */\n private setupShutdownHandlers() {\n process.on('SIGINT', async () => {\n await this.gracefulShutdown();\n });\n\n process.on('SIGTERM', async () => {\n await this.gracefulShutdown();\n });\n\n process.on('beforeExit', async () => {\n await this.gracefulShutdown();\n });\n }\n\n /**\n * Initializes the client for PGlite.\n *\n * @returns {Promise<void>} A Promise that resolves when the client is initialized successfully\n */\n public async initialize(): Promise<void> {\n try {\n await this.client.waitReady;\n logger.info('PGlite client initialized successfully');\n } catch (error) {\n logger.error('Failed to initialize PGlite client:', error);\n throw error;\n }\n }\n\n /**\n * Asynchronously closes the resource. If the resource is not already shutting down,\n * it performs a graceful shutdown before closing.\n *\n * @returns A promise that resolves once the resource has been closed.\n */\n public async close(): Promise<void> {\n if (!this.shuttingDown) {\n await this.gracefulShutdown();\n }\n }\n\n /**\n * Check if the system is currently shutting down.\n *\n * @returns {boolean} True if the system is shutting down, false otherwise.\n */\n public isShuttingDown(): boolean {\n return this.shuttingDown;\n }\n\n /**\n * Asynchronously runs database migrations using Drizzle.\n *\n * Drizzle will first check if the migrations are already applied.\n * If there is a diff between database schema and migrations, it will apply the migrations.\n * If they are already applied, it will skip them.\n *\n * @returns {Promise<void>} A Promise that resolves once the migrations are completed successfully.\n */\n async runMigrations(): Promise<void> {\n try {\n const db = drizzle(this.client);\n\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = pathDirname(__filename);\n\n await migrate(db, {\n migrationsFolder: pathResolve(__dirname, '../drizzle/migrations'),\n });\n } catch (error) {\n logger.error('Failed to run database migrations (pglite):', error);\n }\n }\n}\n","import { logger } from '@elizaos/core';\nimport { migrate } from 'drizzle-orm/node-postgres/migrator';\nimport { drizzle } from 'drizzle-orm/node-postgres';\nimport { dirname as pathDirname, resolve as pathResolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\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 implements IDatabaseClientManager<PgPool> {\n private pool: PgPool;\n private isShuttingDown = false;\n private readonly connectionTimeout: number = 5000;\n\n /**\n * Constructor for creating a connection pool.\n * @param {string} connectionString - The connection string used to connect to the database.\n */\n constructor(db: PgPool, connectionString: string) {\n const defaultConfig = {\n max: 20,\n idleTimeoutMillis: 30000,\n connectionTimeoutMillis: this.connectionTimeout,\n };\n\n this.pool = new Pool({\n ...defaultConfig,\n connectionString,\n });\n\n this.pool.on('error', (err) => {\n logger.error('Unexpected pool error', err);\n this.handlePoolError(err);\n });\n\n this.setupPoolErrorHandling();\n // this.testConnection();\n }\n\n /**\n * Handles a pool error by attempting to reconnect the pool.\n *\n * @param {Error} error The error that occurred in the pool.\n * @throws {Error} If failed to reconnect the pool.\n */\n private async handlePoolError(error: Error) {\n logger.error('Pool error occurred, attempting to reconnect', {\n error: error.message,\n });\n\n try {\n await this.pool.end();\n\n this.pool = new Pool({\n ...this.pool.options,\n connectionTimeoutMillis: this.connectionTimeout,\n });\n\n await this.testConnection();\n logger.success('Pool reconnection successful');\n } catch (reconnectError) {\n logger.error('Failed to reconnect pool', {\n error: reconnectError instanceof Error ? reconnectError.message : String(reconnectError),\n });\n throw reconnectError;\n }\n }\n\n /**\n * Asynchronously tests the database connection by executing a query to get the current timestamp.\n *\n * @returns {Promise<boolean>} - A Promise that resolves to true if the database connection test is successful.\n */\n async testConnection(): Promise<boolean> {\n let client: pkg.PoolClient | null = null;\n try {\n client = await this.pool.connect();\n const result = await client.query('SELECT NOW()');\n logger.success('Database connection test successful:', result.rows[0]);\n return true;\n } catch (error) {\n logger.error('Database connection test failed:', error);\n throw new Error(`Failed to connect to database: ${(error as Error).message}`);\n } finally {\n if (client) client.release();\n }\n }\n\n /**\n * Sets up event listeners to handle pool cleanup on SIGINT, SIGTERM, and beforeExit events.\n */\n private setupPoolErrorHandling() {\n process.on('SIGINT', async () => {\n await this.cleanup();\n process.exit(0);\n });\n\n process.on('SIGTERM', async () => {\n await this.cleanup();\n process.exit(0);\n });\n\n process.on('beforeExit', async () => {\n await this.cleanup();\n });\n }\n\n /**\n * Get the connection pool.\n * @returns {PgPool} The connection pool\n * @throws {Error} If the connection manager is shutting down or an error occurs when trying to get the connection from the pool\n */\n public getConnection(): PgPool {\n if (this.isShuttingDown) {\n throw new Error('Connection manager is shutting down');\n }\n\n try {\n return this.pool;\n } catch (error) {\n logger.error('Failed to get connection from pool:', error);\n throw error;\n }\n }\n\n /**\n * Asynchronously acquires a database client from the connection pool.\n *\n * @returns {Promise<pkg.PoolClient>} A Promise that resolves with the acquired database client.\n * @throws {Error} If an error occurs while acquiring the database client.\n */\n public async getClient(): Promise<pkg.PoolClient> {\n try {\n return await this.pool.connect();\n } catch (error) {\n logger.error('Failed to acquire a database client:', error);\n throw error;\n }\n }\n\n /**\n * Initializes the PostgreSQL connection manager by testing the connection and logging the result.\n *\n * @returns {Promise<void>} A Promise that resolves once the manager is successfully initialized\n * @throws {Error} If there is an error initializing the connection manager\n */\n public async initialize(): Promise<void> {\n try {\n // await this.testConnection();\n // logger.debug('PostgreSQL connection manager initialized successfully');\n } catch (error) {\n logger.error('Failed to initialize connection manager:', error);\n throw error;\n }\n }\n\n /**\n * Asynchronously close the current process by executing a cleanup function.\n * @returns A promise that resolves once the cleanup is complete.\n */\n public async close(): Promise<void> {\n await this.cleanup();\n }\n\n /**\n * Cleans up and closes the database pool.\n * @returns {Promise<void>} A Promise that resolves when the database pool is closed.\n */\n async cleanup(): Promise<void> {\n try {\n await this.pool.end();\n logger.info('Database pool closed');\n } catch (error) {\n logger.error('Error closing database pool:', error);\n }\n }\n\n /**\n * Asynchronously runs database migrations using the Drizzle library and ensures\n * the database schema is always up to date with the current schema definition.\n *\n * First applies existing migrations, then uses drizzle-kit push to ensure\n * the database schema matches the current schema definition.\n *\n * @returns {Promise<void>} A Promise that resolves once the schema updates are completed successfully.\n */\n async runMigrations(): Promise<void> {\n // try {\n const { existsSync } = await import('fs');\n const db = drizzle(this.pool);\n\n const packagePath = (await import.meta.resolve('@elizaos/plugin-sql')).replace('file://', '');\n\n const packageMigrationsPath = pathResolve(pathDirname(packagePath), '../drizzle/migrations');\n console.log('packageMigrationsPath', packageMigrationsPath);\n\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = pathDirname(__filename);\n const appMigrationPath = pathResolve(__dirname, '../drizzle/migrations');\n\n console.log('appMigrationPath', appMigrationPath);\n\n // if packageMigrationsPath exists, use it, otherwise use appMigrationPath\n const migrationsFolder = existsSync(packageMigrationsPath)\n ? packageMigrationsPath\n : appMigrationPath;\n\n console.log('migrationsFolder', migrationsFolder);\n // await migrate(db, {\n // migrationsFolder,\n // });\n // run npx drizzle-kit push on migrationsFolder cwd\n const { execa } = await import('execa');\n await execa('npx', ['drizzle-kit', 'push'], {\n cwd: migrationsFolder,\n });\n console.log('migrated');\n // wait 2 seconds\n await new Promise((resolve) => setTimeout(resolve, 2000));\n // } catch (error) {\n // logger.error('Failed to run database migrations or schema push (pg):', error);\n // throw error;\n // }\n }\n}\n"],"mappings":"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","names":["logger","drizzle","pathDirname","pathResolve","fileURLToPath","logger","drizzle","pathResolve","pathDirname","fileURLToPath"]}