@furlow/pipes 1.0.2 → 1.0.3

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.
@@ -1,4 +1,10 @@
1
1
  // src/database/index.ts
2
+ function escapeIdentifier(name) {
3
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
4
+ throw new Error(`Invalid SQL identifier: ${name}`);
5
+ }
6
+ return `"${name}"`;
7
+ }
2
8
  var DatabasePipe = class {
3
9
  name;
4
10
  type = "database";
@@ -22,6 +28,18 @@ var DatabasePipe = class {
22
28
  const BetterSqlite3 = (await import("better-sqlite3")).default;
23
29
  const connectionString = typeof this.config.connection === "string" ? this.config.connection : ":memory:";
24
30
  this.db = new BetterSqlite3(connectionString);
31
+ } else if (this.config.adapter === "postgres") {
32
+ const pgModuleName = "pg";
33
+ const pg = await new Function("m", "return import(m)")(pgModuleName).catch(() => null);
34
+ if (!pg) {
35
+ throw new Error('PostgreSQL adapter requires the "pg" package. Install it with: npm install pg');
36
+ }
37
+ const { Pool } = pg;
38
+ const connectionString = typeof this.config.connection === "string" ? this.config.connection : void 0;
39
+ const connectionOptions = typeof this.config.connection === "object" ? this.config.connection : void 0;
40
+ this.db = new PostgresWrapper(
41
+ connectionString ? new Pool({ connectionString }) : new Pool(connectionOptions)
42
+ );
25
43
  } else if (this.config.adapter === "memory") {
26
44
  this.db = new MemoryDatabase();
27
45
  } else {
@@ -48,6 +66,8 @@ var DatabasePipe = class {
48
66
  try {
49
67
  if (this.config.adapter === "sqlite" && this.db) {
50
68
  this.db.close();
69
+ } else if (this.config.adapter === "postgres" && this.db) {
70
+ await this.db.close();
51
71
  }
52
72
  this.db = null;
53
73
  this.connected = false;
@@ -84,6 +104,9 @@ var DatabasePipe = class {
84
104
  const result = stmt.run(...params);
85
105
  return { success: true, data: result };
86
106
  }
107
+ } else if (this.config.adapter === "postgres") {
108
+ const result = await this.db.query(sql, params);
109
+ return { success: true, data: result };
87
110
  } else if (this.config.adapter === "memory") {
88
111
  const result = this.db.query(sql, params);
89
112
  return { success: true, data: result };
@@ -105,11 +128,15 @@ var DatabasePipe = class {
105
128
  const columns = Object.keys(data);
106
129
  const values = Object.values(data);
107
130
  const placeholders = columns.map(() => "?").join(", ");
108
- const sql = `INSERT INTO ${table} (${columns.join(", ")}) VALUES (${placeholders})`;
131
+ const escapedTable = escapeIdentifier(table);
132
+ const escapedColumns = columns.map((c) => escapeIdentifier(c)).join(", ");
133
+ const sql = `INSERT INTO ${escapedTable} (${escapedColumns}) VALUES (${placeholders})`;
109
134
  let result = {};
110
135
  if (this.config.adapter === "sqlite") {
111
136
  const stmt = this.db.prepare(sql);
112
137
  result = stmt.run(...values);
138
+ } else if (this.config.adapter === "postgres") {
139
+ result = await this.db.insert(table, data);
113
140
  } else if (this.config.adapter === "memory") {
114
141
  result = this.db.insert(table, data);
115
142
  }
@@ -134,14 +161,17 @@ var DatabasePipe = class {
134
161
  return { success: false, error: "Not connected" };
135
162
  }
136
163
  try {
137
- const setClauses = Object.keys(data).map((key) => `${key} = ?`).join(", ");
138
- const whereClauses = Object.keys(where).map((key) => `${key} = ?`).join(" AND ");
139
- const sql = `UPDATE ${table} SET ${setClauses} WHERE ${whereClauses}`;
164
+ const escapedTable = escapeIdentifier(table);
165
+ const setClauses = Object.keys(data).map((key) => `${escapeIdentifier(key)} = ?`).join(", ");
166
+ const whereClauses = Object.keys(where).map((key) => `${escapeIdentifier(key)} = ?`).join(" AND ");
167
+ const sql = `UPDATE ${escapedTable} SET ${setClauses} WHERE ${whereClauses}`;
140
168
  const params = [...Object.values(data), ...Object.values(where)];
141
169
  let result = {};
142
170
  if (this.config.adapter === "sqlite") {
143
171
  const stmt = this.db.prepare(sql);
144
172
  result = stmt.run(...params);
173
+ } else if (this.config.adapter === "postgres") {
174
+ result = await this.db.update(table, where, data);
145
175
  } else if (this.config.adapter === "memory") {
146
176
  result = this.db.update(table, where, data);
147
177
  }
@@ -167,13 +197,16 @@ var DatabasePipe = class {
167
197
  return { success: false, error: "Not connected" };
168
198
  }
169
199
  try {
170
- const whereClauses = Object.keys(where).map((key) => `${key} = ?`).join(" AND ");
171
- const sql = `DELETE FROM ${table} WHERE ${whereClauses}`;
200
+ const escapedTable = escapeIdentifier(table);
201
+ const whereClauses = Object.keys(where).map((key) => `${escapeIdentifier(key)} = ?`).join(" AND ");
202
+ const sql = `DELETE FROM ${escapedTable} WHERE ${whereClauses}`;
172
203
  const params = Object.values(where);
173
204
  let result = {};
174
205
  if (this.config.adapter === "sqlite") {
175
206
  const stmt = this.db.prepare(sql);
176
207
  result = stmt.run(...params);
208
+ } else if (this.config.adapter === "postgres") {
209
+ result = await this.db.delete(table, where);
177
210
  } else if (this.config.adapter === "memory") {
178
211
  result = this.db.delete(table, where);
179
212
  }
@@ -225,14 +258,68 @@ var DatabasePipe = class {
225
258
  var MemoryDatabase = class {
226
259
  tables = /* @__PURE__ */ new Map();
227
260
  autoIncrements = /* @__PURE__ */ new Map();
228
- query(sql, _params) {
229
- const selectMatch = sql.match(/SELECT \* FROM (\w+)/i);
261
+ query(sql, params) {
262
+ const selectMatch = sql.match(
263
+ /SELECT\s+(\*|[\w,\s]+)\s+FROM\s+["']?(\w+)["']?(?:\s+WHERE\s+(.+?))?(?:\s+ORDER\s+BY\s+([\w\s,]+?)(?:\s+(ASC|DESC))?)?(?:\s+LIMIT\s+(\d+))?(?:\s+OFFSET\s+(\d+))?$/i
264
+ );
230
265
  if (selectMatch) {
231
- const table = selectMatch[1];
232
- return this.tables.get(table) ?? [];
266
+ const [, columns, table, whereClause, orderBy, orderDir, limitStr, offsetStr] = selectMatch;
267
+ let rows = [...this.tables.get(table) ?? []];
268
+ if (whereClause) {
269
+ const where = this.parseWhereClause(whereClause, params);
270
+ rows = rows.filter((row) => this.matchesWhere(row, where));
271
+ }
272
+ if (orderBy) {
273
+ const col = orderBy.trim();
274
+ const direction = orderDir?.toUpperCase() === "DESC" ? -1 : 1;
275
+ rows.sort((a, b) => {
276
+ const aVal = a[col];
277
+ const bVal = b[col];
278
+ if (aVal === bVal) return 0;
279
+ if (aVal === null || aVal === void 0) return direction;
280
+ if (bVal === null || bVal === void 0) return -direction;
281
+ return (aVal < bVal ? -1 : 1) * direction;
282
+ });
283
+ }
284
+ if (offsetStr) {
285
+ const offset = parseInt(offsetStr, 10);
286
+ rows = rows.slice(offset);
287
+ }
288
+ if (limitStr) {
289
+ const limit = parseInt(limitStr, 10);
290
+ rows = rows.slice(0, limit);
291
+ }
292
+ if (columns !== "*") {
293
+ const cols = columns.split(",").map((c) => c.trim());
294
+ rows = rows.map((row) => {
295
+ const result = {};
296
+ for (const col of cols) {
297
+ result[col] = row[col];
298
+ }
299
+ return result;
300
+ });
301
+ }
302
+ return rows;
233
303
  }
304
+ console.warn(`MemoryDatabase: Unsupported query: ${sql}`);
234
305
  return [];
235
306
  }
307
+ /**
308
+ * Parse a simple WHERE clause into key-value pairs
309
+ * Supports: col = ? AND col2 = ?
310
+ */
311
+ parseWhereClause(whereClause, params) {
312
+ const where = {};
313
+ let paramIndex = 0;
314
+ const conditions = whereClause.split(/\s+AND\s+/i);
315
+ for (const condition of conditions) {
316
+ const match = condition.match(/["']?(\w+)["']?\s*=\s*\?/);
317
+ if (match) {
318
+ where[match[1]] = params[paramIndex++];
319
+ }
320
+ }
321
+ return where;
322
+ }
236
323
  insert(table, data) {
237
324
  if (!this.tables.has(table)) {
238
325
  this.tables.set(table, []);
@@ -270,6 +357,54 @@ var MemoryDatabase = class {
270
357
  return true;
271
358
  }
272
359
  };
360
+ var PostgresWrapper = class {
361
+ pool;
362
+ constructor(pool) {
363
+ this.pool = pool;
364
+ }
365
+ async query(sql, params) {
366
+ let paramIndex = 0;
367
+ const pgSql = sql.replace(/\?/g, () => `$${++paramIndex}`);
368
+ const result = await this.pool.query(pgSql, params);
369
+ return result.rows;
370
+ }
371
+ async insert(table, data) {
372
+ const columns = Object.keys(data);
373
+ const values = Object.values(data);
374
+ const placeholders = columns.map((_, i) => `$${i + 1}`).join(", ");
375
+ const escapedTable = escapeIdentifier(table);
376
+ const escapedColumns = columns.map((c) => escapeIdentifier(c)).join(", ");
377
+ const result = await this.pool.query(
378
+ `INSERT INTO ${escapedTable} (${escapedColumns}) VALUES (${placeholders}) RETURNING id`,
379
+ values
380
+ );
381
+ const row = result.rows[0];
382
+ return { lastInsertRowid: row?.id };
383
+ }
384
+ async update(table, where, data) {
385
+ const escapedTable = escapeIdentifier(table);
386
+ const setClauses = Object.keys(data).map((key, i) => `${escapeIdentifier(key)} = $${i + 1}`).join(", ");
387
+ const whereClauses = Object.keys(where).map((key, i) => `${escapeIdentifier(key)} = $${Object.keys(data).length + i + 1}`).join(" AND ");
388
+ const params = [...Object.values(data), ...Object.values(where)];
389
+ const result = await this.pool.query(
390
+ `UPDATE ${escapedTable} SET ${setClauses} WHERE ${whereClauses}`,
391
+ params
392
+ );
393
+ return { changes: result.rowCount ?? 0 };
394
+ }
395
+ async delete(table, where) {
396
+ const escapedTable = escapeIdentifier(table);
397
+ const whereClauses = Object.keys(where).map((key, i) => `${escapeIdentifier(key)} = $${i + 1}`).join(" AND ");
398
+ const result = await this.pool.query(
399
+ `DELETE FROM ${escapedTable} WHERE ${whereClauses}`,
400
+ Object.values(where)
401
+ );
402
+ return { changes: result.rowCount ?? 0 };
403
+ }
404
+ async close() {
405
+ await this.pool.end();
406
+ }
407
+ };
273
408
  function createDatabasePipe(options) {
274
409
  return new DatabasePipe(options);
275
410
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/database/index.ts"],"sourcesContent":["/**\n * Database pipe for reactive database operations\n */\n\nimport type {\n Pipe,\n PipeResponse,\n DatabasePipeConfig,\n DatabaseEvent,\n DatabaseEventType,\n} from '../types.js';\n\nexport interface DatabasePipeOptions {\n name: string;\n config: DatabasePipeConfig;\n}\n\nexport type DatabaseEventHandler = (event: DatabaseEvent) => void | Promise<void>;\n\nexport class DatabasePipe implements Pipe {\n public readonly name: string;\n public readonly type = 'database';\n private config: DatabasePipeConfig;\n private connected = false;\n private db: any = null;\n private eventHandlers: Map<string, DatabaseEventHandler[]> = new Map();\n\n constructor(options: DatabasePipeOptions) {\n this.name = options.name;\n this.config = options.config;\n }\n\n /**\n * Connect to the database\n */\n async connect(): Promise<void> {\n if (this.connected) {\n return;\n }\n\n try {\n if (this.config.adapter === 'sqlite') {\n // Dynamic import to avoid bundling issues\n const BetterSqlite3 = (await import('better-sqlite3')).default;\n const connectionString =\n typeof this.config.connection === 'string'\n ? this.config.connection\n : ':memory:';\n this.db = new BetterSqlite3(connectionString);\n } else if (this.config.adapter === 'memory') {\n // In-memory storage using a simple object\n this.db = new MemoryDatabase();\n } else {\n throw new Error(`Unsupported adapter: ${this.config.adapter}`);\n }\n\n this.connected = true;\n this.emit('connected', {\n type: 'insert',\n table: '',\n data: {},\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to connect to database: ${message}`);\n }\n }\n\n /**\n * Disconnect from the database\n */\n async disconnect(): Promise<void> {\n if (!this.connected) {\n return;\n }\n\n try {\n if (this.config.adapter === 'sqlite' && this.db) {\n this.db.close();\n }\n this.db = null;\n this.connected = false;\n this.emit('disconnected', {\n type: 'delete',\n table: '',\n data: {},\n });\n } catch {\n // Ignore close errors\n this.db = null;\n this.connected = false;\n }\n }\n\n /**\n * Check if connected\n */\n isConnected(): boolean {\n return this.connected;\n }\n\n /**\n * Execute a raw SQL query\n */\n async query<T = Record<string, unknown>[]>(\n sql: string,\n params: unknown[] = []\n ): Promise<PipeResponse<T>> {\n if (!this.isConnected()) {\n return { success: false, error: 'Not connected' };\n }\n\n try {\n if (this.config.adapter === 'sqlite') {\n const stmt = this.db.prepare(sql);\n // Check if it's a SELECT query\n if (sql.trim().toUpperCase().startsWith('SELECT')) {\n const rows = stmt.all(...params);\n return { success: true, data: rows as T };\n } else {\n const result = stmt.run(...params);\n return { success: true, data: result as T };\n }\n } else if (this.config.adapter === 'memory') {\n const result = this.db.query(sql, params);\n return { success: true, data: result as T };\n }\n\n return { success: false, error: 'Unsupported adapter' };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { success: false, error: message };\n }\n }\n\n /**\n * Insert a row into a table\n */\n async insert(\n table: string,\n data: Record<string, unknown>\n ): Promise<PipeResponse<{ lastInsertRowid?: number | bigint }>> {\n if (!this.isConnected()) {\n return { success: false, error: 'Not connected' };\n }\n\n try {\n const columns = Object.keys(data);\n const values = Object.values(data);\n const placeholders = columns.map(() => '?').join(', ');\n const sql = `INSERT INTO ${table} (${columns.join(', ')}) VALUES (${placeholders})`;\n\n let result: { lastInsertRowid?: number | bigint } = {};\n\n if (this.config.adapter === 'sqlite') {\n const stmt = this.db.prepare(sql);\n result = stmt.run(...values);\n } else if (this.config.adapter === 'memory') {\n result = this.db.insert(table, data);\n }\n\n // Emit insert event\n const event: DatabaseEvent = {\n type: 'insert',\n table,\n data,\n };\n this.emit('insert', event);\n this.emit('change', event);\n\n return { success: true, data: result };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { success: false, error: message };\n }\n }\n\n /**\n * Update rows in a table\n */\n async update(\n table: string,\n where: Record<string, unknown>,\n data: Record<string, unknown>\n ): Promise<PipeResponse<{ changes?: number }>> {\n if (!this.isConnected()) {\n return { success: false, error: 'Not connected' };\n }\n\n try {\n const setClauses = Object.keys(data)\n .map((key) => `${key} = ?`)\n .join(', ');\n const whereClauses = Object.keys(where)\n .map((key) => `${key} = ?`)\n .join(' AND ');\n const sql = `UPDATE ${table} SET ${setClauses} WHERE ${whereClauses}`;\n const params = [...Object.values(data), ...Object.values(where)];\n\n let result: { changes?: number } = {};\n\n if (this.config.adapter === 'sqlite') {\n const stmt = this.db.prepare(sql);\n result = stmt.run(...params);\n } else if (this.config.adapter === 'memory') {\n result = this.db.update(table, where, data);\n }\n\n // Emit update event\n const event: DatabaseEvent = {\n type: 'update',\n table,\n data,\n oldData: where,\n };\n this.emit('update', event);\n this.emit('change', event);\n\n return { success: true, data: result };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { success: false, error: message };\n }\n }\n\n /**\n * Delete rows from a table\n */\n async delete(\n table: string,\n where: Record<string, unknown>\n ): Promise<PipeResponse<{ changes?: number }>> {\n if (!this.isConnected()) {\n return { success: false, error: 'Not connected' };\n }\n\n try {\n const whereClauses = Object.keys(where)\n .map((key) => `${key} = ?`)\n .join(' AND ');\n const sql = `DELETE FROM ${table} WHERE ${whereClauses}`;\n const params = Object.values(where);\n\n let result: { changes?: number } = {};\n\n if (this.config.adapter === 'sqlite') {\n const stmt = this.db.prepare(sql);\n result = stmt.run(...params);\n } else if (this.config.adapter === 'memory') {\n result = this.db.delete(table, where);\n }\n\n // Emit delete event\n const event: DatabaseEvent = {\n type: 'delete',\n table,\n data: where,\n };\n this.emit('delete', event);\n this.emit('change', event);\n\n return { success: true, data: result };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { success: false, error: message };\n }\n }\n\n /**\n * Register an event handler\n */\n on(event: DatabaseEventType | 'change' | 'connected' | 'disconnected', handler: DatabaseEventHandler): void {\n const handlers = this.eventHandlers.get(event) ?? [];\n handlers.push(handler);\n this.eventHandlers.set(event, handlers);\n }\n\n /**\n * Remove an event handler\n */\n off(event: DatabaseEventType | 'change' | 'connected' | 'disconnected', handler: DatabaseEventHandler): void {\n const handlers = this.eventHandlers.get(event) ?? [];\n const index = handlers.indexOf(handler);\n if (index !== -1) {\n handlers.splice(index, 1);\n }\n }\n\n /**\n * Emit an event\n */\n private emit(event: string, data: DatabaseEvent): void {\n const handlers = this.eventHandlers.get(event) ?? [];\n for (const handler of handlers) {\n try {\n handler(data);\n } catch (error) {\n console.error(`Database handler error for \"${event}\":`, error);\n }\n }\n }\n}\n\n/**\n * Simple in-memory database for testing\n */\nclass MemoryDatabase {\n private tables: Map<string, Record<string, unknown>[]> = new Map();\n private autoIncrements: Map<string, number> = new Map();\n\n query(sql: string, _params: unknown[]): Record<string, unknown>[] {\n // Very basic SQL parsing for memory adapter\n const selectMatch = sql.match(/SELECT \\* FROM (\\w+)/i);\n if (selectMatch) {\n const table = selectMatch[1]!;\n return this.tables.get(table) ?? [];\n }\n return [];\n }\n\n insert(table: string, data: Record<string, unknown>): { lastInsertRowid: number } {\n if (!this.tables.has(table)) {\n this.tables.set(table, []);\n }\n\n const id = (this.autoIncrements.get(table) ?? 0) + 1;\n this.autoIncrements.set(table, id);\n\n const row = { id, ...data };\n this.tables.get(table)!.push(row);\n\n return { lastInsertRowid: id };\n }\n\n update(\n table: string,\n where: Record<string, unknown>,\n data: Record<string, unknown>\n ): { changes: number } {\n const rows = this.tables.get(table) ?? [];\n let changes = 0;\n\n for (const row of rows) {\n if (this.matchesWhere(row, where)) {\n Object.assign(row, data);\n changes++;\n }\n }\n\n return { changes };\n }\n\n delete(table: string, where: Record<string, unknown>): { changes: number } {\n const rows = this.tables.get(table) ?? [];\n const initialLength = rows.length;\n\n const remaining = rows.filter((row) => !this.matchesWhere(row, where));\n this.tables.set(table, remaining);\n\n return { changes: initialLength - remaining.length };\n }\n\n private matchesWhere(\n row: Record<string, unknown>,\n where: Record<string, unknown>\n ): boolean {\n for (const [key, value] of Object.entries(where)) {\n if (row[key] !== value) {\n return false;\n }\n }\n return true;\n }\n}\n\n/**\n * Create a database pipe\n */\nexport function createDatabasePipe(options: DatabasePipeOptions): DatabasePipe {\n return new DatabasePipe(options);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/database/index.ts"],"sourcesContent":["/**\n * Database pipe for reactive database operations\n */\n\nimport type {\n Pipe,\n PipeResponse,\n DatabasePipeConfig,\n DatabaseEvent,\n DatabaseEventType,\n} from '../types.js';\n\n// Pool type for pg (dynamically imported at runtime)\ntype PgPool = {\n query: (sql: string, params?: unknown[]) => Promise<{ rows: unknown[]; rowCount: number | null }>;\n end: () => Promise<void>;\n};\n\nexport interface DatabasePipeOptions {\n name: string;\n config: DatabasePipeConfig;\n}\n\n/**\n * Escape a SQL identifier (table/column name) to prevent SQL injection\n */\nfunction escapeIdentifier(name: string): string {\n // Validate identifier - only allow alphanumeric and underscores\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {\n throw new Error(`Invalid SQL identifier: ${name}`);\n }\n return `\"${name}\"`;\n}\n\nexport type DatabaseEventHandler = (event: DatabaseEvent) => void | Promise<void>;\n\nexport class DatabasePipe implements Pipe {\n public readonly name: string;\n public readonly type = 'database';\n private config: DatabasePipeConfig;\n private connected = false;\n private db: any = null;\n private eventHandlers: Map<string, DatabaseEventHandler[]> = new Map();\n\n constructor(options: DatabasePipeOptions) {\n this.name = options.name;\n this.config = options.config;\n }\n\n /**\n * Connect to the database\n */\n async connect(): Promise<void> {\n if (this.connected) {\n return;\n }\n\n try {\n if (this.config.adapter === 'sqlite') {\n // Dynamic import to avoid bundling issues\n const BetterSqlite3 = (await import('better-sqlite3')).default;\n const connectionString =\n typeof this.config.connection === 'string'\n ? this.config.connection\n : ':memory:';\n this.db = new BetterSqlite3(connectionString);\n } else if (this.config.adapter === 'postgres') {\n // Dynamic import to avoid bundling issues - use Function constructor to hide from TS\n const pgModuleName = 'pg';\n const pg = await (new Function('m', 'return import(m)')(pgModuleName) as Promise<{ Pool: new (config?: unknown) => PgPool }>).catch(() => null);\n if (!pg) {\n throw new Error('PostgreSQL adapter requires the \"pg\" package. Install it with: npm install pg');\n }\n const { Pool } = pg;\n const connectionString =\n typeof this.config.connection === 'string'\n ? this.config.connection\n : undefined;\n const connectionOptions =\n typeof this.config.connection === 'object'\n ? this.config.connection\n : undefined;\n this.db = new PostgresWrapper(\n connectionString\n ? new Pool({ connectionString })\n : new Pool(connectionOptions)\n );\n } else if (this.config.adapter === 'memory') {\n // In-memory storage using a simple object\n this.db = new MemoryDatabase();\n } else {\n throw new Error(`Unsupported adapter: ${this.config.adapter}`);\n }\n\n this.connected = true;\n this.emit('connected', {\n type: 'insert',\n table: '',\n data: {},\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to connect to database: ${message}`);\n }\n }\n\n /**\n * Disconnect from the database\n */\n async disconnect(): Promise<void> {\n if (!this.connected) {\n return;\n }\n\n try {\n if (this.config.adapter === 'sqlite' && this.db) {\n this.db.close();\n } else if (this.config.adapter === 'postgres' && this.db) {\n await this.db.close();\n }\n this.db = null;\n this.connected = false;\n this.emit('disconnected', {\n type: 'delete',\n table: '',\n data: {},\n });\n } catch {\n // Ignore close errors\n this.db = null;\n this.connected = false;\n }\n }\n\n /**\n * Check if connected\n */\n isConnected(): boolean {\n return this.connected;\n }\n\n /**\n * Execute a raw SQL query\n */\n async query<T = Record<string, unknown>[]>(\n sql: string,\n params: unknown[] = []\n ): Promise<PipeResponse<T>> {\n if (!this.isConnected()) {\n return { success: false, error: 'Not connected' };\n }\n\n try {\n if (this.config.adapter === 'sqlite') {\n const stmt = this.db.prepare(sql);\n // Check if it's a SELECT query\n if (sql.trim().toUpperCase().startsWith('SELECT')) {\n const rows = stmt.all(...params);\n return { success: true, data: rows as T };\n } else {\n const result = stmt.run(...params);\n return { success: true, data: result as T };\n }\n } else if (this.config.adapter === 'postgres') {\n const result = await this.db.query(sql, params);\n return { success: true, data: result as T };\n } else if (this.config.adapter === 'memory') {\n const result = this.db.query(sql, params);\n return { success: true, data: result as T };\n }\n\n return { success: false, error: 'Unsupported adapter' };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { success: false, error: message };\n }\n }\n\n /**\n * Insert a row into a table\n */\n async insert(\n table: string,\n data: Record<string, unknown>\n ): Promise<PipeResponse<{ lastInsertRowid?: number | bigint }>> {\n if (!this.isConnected()) {\n return { success: false, error: 'Not connected' };\n }\n\n try {\n const columns = Object.keys(data);\n const values = Object.values(data);\n const placeholders = columns.map(() => '?').join(', ');\n const escapedTable = escapeIdentifier(table);\n const escapedColumns = columns.map(c => escapeIdentifier(c)).join(', ');\n const sql = `INSERT INTO ${escapedTable} (${escapedColumns}) VALUES (${placeholders})`;\n\n let result: { lastInsertRowid?: number | bigint } = {};\n\n if (this.config.adapter === 'sqlite') {\n const stmt = this.db.prepare(sql);\n result = stmt.run(...values);\n } else if (this.config.adapter === 'postgres') {\n result = await this.db.insert(table, data);\n } else if (this.config.adapter === 'memory') {\n result = this.db.insert(table, data);\n }\n\n // Emit insert event\n const event: DatabaseEvent = {\n type: 'insert',\n table,\n data,\n };\n this.emit('insert', event);\n this.emit('change', event);\n\n return { success: true, data: result };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { success: false, error: message };\n }\n }\n\n /**\n * Update rows in a table\n */\n async update(\n table: string,\n where: Record<string, unknown>,\n data: Record<string, unknown>\n ): Promise<PipeResponse<{ changes?: number }>> {\n if (!this.isConnected()) {\n return { success: false, error: 'Not connected' };\n }\n\n try {\n const escapedTable = escapeIdentifier(table);\n const setClauses = Object.keys(data)\n .map((key) => `${escapeIdentifier(key)} = ?`)\n .join(', ');\n const whereClauses = Object.keys(where)\n .map((key) => `${escapeIdentifier(key)} = ?`)\n .join(' AND ');\n const sql = `UPDATE ${escapedTable} SET ${setClauses} WHERE ${whereClauses}`;\n const params = [...Object.values(data), ...Object.values(where)];\n\n let result: { changes?: number } = {};\n\n if (this.config.adapter === 'sqlite') {\n const stmt = this.db.prepare(sql);\n result = stmt.run(...params);\n } else if (this.config.adapter === 'postgres') {\n result = await this.db.update(table, where, data);\n } else if (this.config.adapter === 'memory') {\n result = this.db.update(table, where, data);\n }\n\n // Emit update event\n const event: DatabaseEvent = {\n type: 'update',\n table,\n data,\n oldData: where,\n };\n this.emit('update', event);\n this.emit('change', event);\n\n return { success: true, data: result };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { success: false, error: message };\n }\n }\n\n /**\n * Delete rows from a table\n */\n async delete(\n table: string,\n where: Record<string, unknown>\n ): Promise<PipeResponse<{ changes?: number }>> {\n if (!this.isConnected()) {\n return { success: false, error: 'Not connected' };\n }\n\n try {\n const escapedTable = escapeIdentifier(table);\n const whereClauses = Object.keys(where)\n .map((key) => `${escapeIdentifier(key)} = ?`)\n .join(' AND ');\n const sql = `DELETE FROM ${escapedTable} WHERE ${whereClauses}`;\n const params = Object.values(where);\n\n let result: { changes?: number } = {};\n\n if (this.config.adapter === 'sqlite') {\n const stmt = this.db.prepare(sql);\n result = stmt.run(...params);\n } else if (this.config.adapter === 'postgres') {\n result = await this.db.delete(table, where);\n } else if (this.config.adapter === 'memory') {\n result = this.db.delete(table, where);\n }\n\n // Emit delete event\n const event: DatabaseEvent = {\n type: 'delete',\n table,\n data: where,\n };\n this.emit('delete', event);\n this.emit('change', event);\n\n return { success: true, data: result };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return { success: false, error: message };\n }\n }\n\n /**\n * Register an event handler\n */\n on(event: DatabaseEventType | 'change' | 'connected' | 'disconnected', handler: DatabaseEventHandler): void {\n const handlers = this.eventHandlers.get(event) ?? [];\n handlers.push(handler);\n this.eventHandlers.set(event, handlers);\n }\n\n /**\n * Remove an event handler\n */\n off(event: DatabaseEventType | 'change' | 'connected' | 'disconnected', handler: DatabaseEventHandler): void {\n const handlers = this.eventHandlers.get(event) ?? [];\n const index = handlers.indexOf(handler);\n if (index !== -1) {\n handlers.splice(index, 1);\n }\n }\n\n /**\n * Emit an event\n */\n private emit(event: string, data: DatabaseEvent): void {\n const handlers = this.eventHandlers.get(event) ?? [];\n for (const handler of handlers) {\n try {\n handler(data);\n } catch (error) {\n console.error(`Database handler error for \"${event}\":`, error);\n }\n }\n }\n}\n\n/**\n * Simple in-memory database for testing and development\n * Supports basic SQL operations: SELECT with WHERE, ORDER BY, LIMIT\n *\n * Limitations:\n * - No JOIN support\n * - No complex expressions in WHERE (only simple equality)\n * - No aggregate functions\n * - For production, use SQLite or PostgreSQL\n */\nclass MemoryDatabase {\n private tables: Map<string, Record<string, unknown>[]> = new Map();\n private autoIncrements: Map<string, number> = new Map();\n\n query(sql: string, params: unknown[]): Record<string, unknown>[] {\n // Parse SELECT queries with basic WHERE, ORDER BY, LIMIT support\n const selectMatch = sql.match(\n /SELECT\\s+(\\*|[\\w,\\s]+)\\s+FROM\\s+[\"']?(\\w+)[\"']?(?:\\s+WHERE\\s+(.+?))?(?:\\s+ORDER\\s+BY\\s+([\\w\\s,]+?)(?:\\s+(ASC|DESC))?)?(?:\\s+LIMIT\\s+(\\d+))?(?:\\s+OFFSET\\s+(\\d+))?$/i\n );\n\n if (selectMatch) {\n const [, columns, table, whereClause, orderBy, orderDir, limitStr, offsetStr] = selectMatch;\n let rows = [...(this.tables.get(table!) ?? [])];\n\n // Apply WHERE clause\n if (whereClause) {\n const where = this.parseWhereClause(whereClause, params);\n rows = rows.filter((row) => this.matchesWhere(row, where));\n }\n\n // Apply ORDER BY\n if (orderBy) {\n const col = orderBy.trim();\n const direction = orderDir?.toUpperCase() === 'DESC' ? -1 : 1;\n rows.sort((a, b) => {\n const aVal = a[col];\n const bVal = b[col];\n if (aVal === bVal) return 0;\n if (aVal === null || aVal === undefined) return direction;\n if (bVal === null || bVal === undefined) return -direction;\n return (aVal < bVal ? -1 : 1) * direction;\n });\n }\n\n // Apply OFFSET\n if (offsetStr) {\n const offset = parseInt(offsetStr, 10);\n rows = rows.slice(offset);\n }\n\n // Apply LIMIT\n if (limitStr) {\n const limit = parseInt(limitStr, 10);\n rows = rows.slice(0, limit);\n }\n\n // Select specific columns if not *\n if (columns !== '*') {\n const cols = columns!.split(',').map((c) => c.trim());\n rows = rows.map((row) => {\n const result: Record<string, unknown> = {};\n for (const col of cols) {\n result[col] = row[col];\n }\n return result;\n });\n }\n\n return rows;\n }\n\n // Fallback for unsupported queries\n console.warn(`MemoryDatabase: Unsupported query: ${sql}`);\n return [];\n }\n\n /**\n * Parse a simple WHERE clause into key-value pairs\n * Supports: col = ? AND col2 = ?\n */\n private parseWhereClause(\n whereClause: string,\n params: unknown[]\n ): Record<string, unknown> {\n const where: Record<string, unknown> = {};\n let paramIndex = 0;\n\n // Split by AND (case insensitive)\n const conditions = whereClause.split(/\\s+AND\\s+/i);\n\n for (const condition of conditions) {\n // Match: column = ? or \"column\" = ?\n const match = condition.match(/[\"']?(\\w+)[\"']?\\s*=\\s*\\?/);\n if (match) {\n where[match[1]!] = params[paramIndex++];\n }\n }\n\n return where;\n }\n\n insert(table: string, data: Record<string, unknown>): { lastInsertRowid: number } {\n if (!this.tables.has(table)) {\n this.tables.set(table, []);\n }\n\n const id = (this.autoIncrements.get(table) ?? 0) + 1;\n this.autoIncrements.set(table, id);\n\n const row = { id, ...data };\n this.tables.get(table)!.push(row);\n\n return { lastInsertRowid: id };\n }\n\n update(\n table: string,\n where: Record<string, unknown>,\n data: Record<string, unknown>\n ): { changes: number } {\n const rows = this.tables.get(table) ?? [];\n let changes = 0;\n\n for (const row of rows) {\n if (this.matchesWhere(row, where)) {\n Object.assign(row, data);\n changes++;\n }\n }\n\n return { changes };\n }\n\n delete(table: string, where: Record<string, unknown>): { changes: number } {\n const rows = this.tables.get(table) ?? [];\n const initialLength = rows.length;\n\n const remaining = rows.filter((row) => !this.matchesWhere(row, where));\n this.tables.set(table, remaining);\n\n return { changes: initialLength - remaining.length };\n }\n\n private matchesWhere(\n row: Record<string, unknown>,\n where: Record<string, unknown>\n ): boolean {\n for (const [key, value] of Object.entries(where)) {\n if (row[key] !== value) {\n return false;\n }\n }\n return true;\n }\n}\n\n/**\n * PostgreSQL wrapper for consistent interface with other adapters\n */\nclass PostgresWrapper {\n private pool: PgPool;\n\n constructor(pool: PgPool) {\n this.pool = pool;\n }\n\n async query(sql: string, params: unknown[]): Promise<Record<string, unknown>[]> {\n // Convert ? placeholders to $1, $2, etc for pg\n let paramIndex = 0;\n const pgSql = sql.replace(/\\?/g, () => `$${++paramIndex}`);\n const result = await this.pool.query(pgSql, params);\n return result.rows as Record<string, unknown>[];\n }\n\n async insert(\n table: string,\n data: Record<string, unknown>\n ): Promise<{ lastInsertRowid?: number }> {\n const columns = Object.keys(data);\n const values = Object.values(data);\n const placeholders = columns.map((_, i) => `$${i + 1}`).join(', ');\n const escapedTable = escapeIdentifier(table);\n const escapedColumns = columns.map(c => escapeIdentifier(c)).join(', ');\n\n const result = await this.pool.query(\n `INSERT INTO ${escapedTable} (${escapedColumns}) VALUES (${placeholders}) RETURNING id`,\n values\n );\n\n const row = result.rows[0] as Record<string, unknown> | undefined;\n return { lastInsertRowid: row?.id as number | undefined };\n }\n\n async update(\n table: string,\n where: Record<string, unknown>,\n data: Record<string, unknown>\n ): Promise<{ changes: number }> {\n const escapedTable = escapeIdentifier(table);\n const setClauses = Object.keys(data)\n .map((key, i) => `${escapeIdentifier(key)} = $${i + 1}`)\n .join(', ');\n const whereClauses = Object.keys(where)\n .map((key, i) => `${escapeIdentifier(key)} = $${Object.keys(data).length + i + 1}`)\n .join(' AND ');\n\n const params = [...Object.values(data), ...Object.values(where)];\n const result = await this.pool.query(\n `UPDATE ${escapedTable} SET ${setClauses} WHERE ${whereClauses}`,\n params\n );\n\n return { changes: result.rowCount ?? 0 };\n }\n\n async delete(\n table: string,\n where: Record<string, unknown>\n ): Promise<{ changes: number }> {\n const escapedTable = escapeIdentifier(table);\n const whereClauses = Object.keys(where)\n .map((key, i) => `${escapeIdentifier(key)} = $${i + 1}`)\n .join(' AND ');\n\n const result = await this.pool.query(\n `DELETE FROM ${escapedTable} WHERE ${whereClauses}`,\n Object.values(where)\n );\n\n return { changes: result.rowCount ?? 0 };\n }\n\n async close(): Promise<void> {\n await this.pool.end();\n }\n}\n\n/**\n * Create a database pipe\n */\nexport function createDatabasePipe(options: DatabasePipeOptions): DatabasePipe {\n return new DatabasePipe(options);\n}\n"],"mappings":"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","names":[]}