@pineliner/odb-client 1.0.8 → 1.0.9

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 +1 @@
1
- {"version":3,"file":"bun-sqlite.d.ts","sourceRoot":"","sources":["../../../src/database/adapters/bun-sqlite.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EAGV,eAAe,EAChB,MAAM,UAAU,CAAA;AAYjB;;;GAGG;AACH,qBAAa,gBAAiB,YAAW,eAAe;IACtD,QAAQ,CAAC,IAAI,EAAG,YAAY,CAAS;IACrC,OAAO,CAAC,MAAM,CAAiB;gBAEnB,MAAM,EAAE,eAAe;IAI7B,OAAO,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC;IASrD,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK5C,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAapC"}
1
+ {"version":3,"file":"bun-sqlite.d.ts","sourceRoot":"","sources":["../../../src/database/adapters/bun-sqlite.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EAGV,eAAe,EAEhB,MAAM,UAAU,CAAA;AAsDjB;;;GAGG;AACH,qBAAa,gBAAiB,YAAW,eAAe;IACtD,QAAQ,CAAC,IAAI,EAAG,YAAY,CAAS;IACrC,OAAO,CAAC,MAAM,CAAiB;gBAEnB,MAAM,EAAE,eAAe;IAI7B,OAAO,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC;IASrD,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK5C,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAapC"}
@@ -1 +1 @@
1
- {"version":3,"file":"libsql.d.ts","sourceRoot":"","sources":["../../../src/database/adapters/libsql.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EAGV,YAAY,EACb,MAAM,UAAU,CAAA;AAYjB;;;GAGG;AACH,qBAAa,aAAc,YAAW,eAAe;IACnD,QAAQ,CAAC,IAAI,EAAG,QAAQ,CAAS;IACjC,OAAO,CAAC,MAAM,CAAc;gBAEhB,MAAM,EAAE,YAAY;IAI1B,OAAO,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC;IAUlD,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK5C,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAYpC"}
1
+ {"version":3,"file":"libsql.d.ts","sourceRoot":"","sources":["../../../src/database/adapters/libsql.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EAGV,YAAY,EAEb,MAAM,UAAU,CAAA;AAsDjB;;;GAGG;AACH,qBAAa,aAAc,YAAW,eAAe;IACnD,QAAQ,CAAC,IAAI,EAAG,QAAQ,CAAS;IACjC,OAAO,CAAC,MAAM,CAAc;gBAEhB,MAAM,EAAE,YAAY;IAI1B,OAAO,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC;IAUlD,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK5C,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAYpC"}
@@ -1 +1 @@
1
- {"version":3,"file":"odblite.d.ts","sourceRoot":"","sources":["../../../src/database/adapters/odblite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAA;AAE5D,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EAGV,aAAa,EACd,MAAM,UAAU,CAAA;AAYjB;;;GAGG;AACH,qBAAa,cAAe,YAAW,eAAe;IACpD,QAAQ,CAAC,IAAI,EAAG,SAAS,CAAS;IAClC,OAAO,CAAC,MAAM,CAAe;IACtB,aAAa,EAAE,aAAa,CAAA;gBAEvB,MAAM,EAAE,aAAa;IAQ3B,OAAO,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC;IAgCzC,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa5C,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAQpC"}
1
+ {"version":3,"file":"odblite.d.ts","sourceRoot":"","sources":["../../../src/database/adapters/odblite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAA;AAE5D,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EAGV,aAAa,EAEd,MAAM,UAAU,CAAA;AAsDjB;;;GAGG;AACH,qBAAa,cAAe,YAAW,eAAe;IACpD,QAAQ,CAAC,IAAI,EAAG,SAAS,CAAS;IAClC,OAAO,CAAC,MAAM,CAAe;IACtB,aAAa,EAAE,aAAa,CAAA;gBAEvB,MAAM,EAAE,aAAa;IAQ3B,OAAO,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC;IAgCzC,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa5C,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAQpC"}
@@ -14,6 +14,30 @@ export interface QueryResult<T = any> {
14
14
  rowsAffected?: number;
15
15
  lastInsertRowid?: number | bigint;
16
16
  }
17
+ /**
18
+ * Query options for fine-grained control
19
+ */
20
+ export interface QueryOptions {
21
+ /**
22
+ * List of column names that should be auto-parsed as JSON when reading
23
+ * Only applies to columns that contain stringified JSON in SELECT queries
24
+ *
25
+ * @example
26
+ * await conn.query('SELECT * FROM suppliers', [], { jsonColumns: ['connections', 'config'] })
27
+ */
28
+ jsonColumns?: string[];
29
+ /**
30
+ * Automatically stringify parameters for JSON columns in UPDATE/INSERT queries
31
+ * Maps column names to parameter indices (0-based)
32
+ *
33
+ * @example
34
+ * // UPDATE suppliers SET connections = ?, updated_at = ? WHERE id = ?
35
+ * await conn.execute(query, [connections, now, id], {
36
+ * stringifyParams: { connections: 0 } // Auto-stringify first parameter
37
+ * })
38
+ */
39
+ stringifyParams?: Record<string, number>;
40
+ }
17
41
  /**
18
42
  * Unified database connection interface
19
43
  * All backends implement this through adapters
@@ -26,11 +50,11 @@ export interface QueryResult<T = any> {
26
50
  export interface Connection {
27
51
  sql<T = any>(strings: TemplateStringsArray, ...values: any[]): Promise<T[]>;
28
52
  sql(value: any, ...keys: string[]): any;
29
- query<T = any>(sql: string, params?: any[]): Promise<QueryResult<T>>;
53
+ query<T = any>(sql: string, params?: any[], options?: QueryOptions): Promise<QueryResult<T>>;
30
54
  execute(sql: string | {
31
55
  sql: string;
32
56
  args?: any[];
33
- }, params?: any[]): Promise<QueryResult>;
57
+ }, params?: any[], options?: QueryOptions): Promise<QueryResult>;
34
58
  prepare(sql: string): PreparedStatement;
35
59
  transaction<T>(fn: (tx: Connection) => Promise<T>): Promise<T>;
36
60
  begin<T>(fn: (tx: Connection) => Promise<T>): Promise<T>;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/database/types.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,MAAM,WAAW,GACnB,WAAW,GACX,cAAc,GACd,WAAW,GACX,WAAW,CAAA;AAEf;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,QAAQ,GAAG,SAAS,CAAA;AAE7D;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,GAAG;IAClC,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAClC;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU;IAGzB,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAA;IAG3E,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,CAAA;IAGvC,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAA;IACpE,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAA;KAAE,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IAC1F,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAAA;IAGvC,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;IAC9D,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;IAGxD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAGtB,SAAS,CAAC,IAAI,GAAG,CAAA;IAGjB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IAC7C,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;IACnC,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAA;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;IAG1B,OAAO,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;IACzC,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAG5C,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,MAAM,CAAA;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IAEpC,OAAO,EAAE,WAAW,CAAA;IAGpB,YAAY,EAAE,MAAM,CAAA;IAGpB,MAAM,CAAC,EAAE;QACP,aAAa,CAAC,EAAE,MAAM,CAAA;QACtB,SAAS,CAAC,EAAE,MAAM,CAAA;KACnB,CAAA;IAED,OAAO,CAAC,EAAE;QACR,UAAU,EAAE,MAAM,CAAA;QAClB,MAAM,EAAE,MAAM,CAAA;QACd,MAAM,CAAC,EAAE,MAAM,CAAA;KAChB,CAAA;IAGD,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,IAAI,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAC/B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/database/types.ts"],"names":[],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -1350,6 +1350,26 @@ var __webpack_exports__ = {};
1350
1350
  args: values
1351
1351
  };
1352
1352
  }
1353
+ function parseJsonColumns(rows, jsonColumns) {
1354
+ if (!jsonColumns || 0 === jsonColumns.length) return rows;
1355
+ return rows.map((row)=>{
1356
+ const parsed = {
1357
+ ...row
1358
+ };
1359
+ for (const col of jsonColumns)if (col in parsed && 'string' == typeof parsed[col]) try {
1360
+ parsed[col] = JSON.parse(parsed[col]);
1361
+ } catch {}
1362
+ return parsed;
1363
+ });
1364
+ }
1365
+ function stringifyJsonParams(params, stringifyParams) {
1366
+ if (!stringifyParams || 0 === Object.keys(stringifyParams).length) return params;
1367
+ const result = [
1368
+ ...params
1369
+ ];
1370
+ for (const [_columnName, index] of Object.entries(stringifyParams))if (index < result.length && null != result[index] && 'object' == typeof result[index]) result[index] = JSON.stringify(result[index]);
1371
+ return result;
1372
+ }
1353
1373
  class BunSQLiteAdapter {
1354
1374
  type = 'bun-sqlite';
1355
1375
  config;
@@ -1407,10 +1427,10 @@ var __webpack_exports__ = {};
1407
1427
  this.sql.set = set;
1408
1428
  this.sql.where = sql_template_where;
1409
1429
  }
1410
- async query(sql, params = []) {
1411
- return this.execute(sql, params);
1430
+ async query(sql, params = [], options) {
1431
+ return this.execute(sql, params, options);
1412
1432
  }
1413
- async execute(sql, params = []) {
1433
+ async execute(sql, params = [], options) {
1414
1434
  try {
1415
1435
  let sqlStr;
1416
1436
  let sqlParams;
@@ -1429,12 +1449,14 @@ var __webpack_exports__ = {};
1429
1449
  sqlStr = sql;
1430
1450
  sqlParams = params;
1431
1451
  }
1452
+ if (options?.stringifyParams) sqlParams = stringifyJsonParams(sqlParams, options.stringifyParams);
1432
1453
  const stmt = this.db.query(sqlStr);
1433
1454
  const isSelect = sqlStr.trim().toUpperCase().startsWith('SELECT') || sqlStr.trim().toUpperCase().includes('RETURNING');
1434
1455
  if (isSelect) {
1435
- const rows = stmt.all(...sqlParams);
1456
+ let rows = stmt.all(...sqlParams);
1457
+ if (options?.jsonColumns) rows = parseJsonColumns(rows, options.jsonColumns);
1436
1458
  return {
1437
- rows: rows,
1459
+ rows,
1438
1460
  rowsAffected: 0
1439
1461
  };
1440
1462
  }
@@ -1502,6 +1524,26 @@ var __webpack_exports__ = {};
1502
1524
  }
1503
1525
  }
1504
1526
  const external_libsql_client_namespaceObject = require("@libsql/client");
1527
+ function libsql_parseJsonColumns(rows, jsonColumns) {
1528
+ if (!jsonColumns || 0 === jsonColumns.length) return rows;
1529
+ return rows.map((row)=>{
1530
+ const parsed = {
1531
+ ...row
1532
+ };
1533
+ for (const col of jsonColumns)if (col in parsed && 'string' == typeof parsed[col]) try {
1534
+ parsed[col] = JSON.parse(parsed[col]);
1535
+ } catch {}
1536
+ return parsed;
1537
+ });
1538
+ }
1539
+ function libsql_stringifyJsonParams(params, stringifyParams) {
1540
+ if (!stringifyParams || 0 === Object.keys(stringifyParams).length) return params;
1541
+ const result = [
1542
+ ...params
1543
+ ];
1544
+ for (const [_columnName, index] of Object.entries(stringifyParams))if (index < result.length && null != result[index] && 'object' == typeof result[index]) result[index] = JSON.stringify(result[index]);
1545
+ return result;
1546
+ }
1505
1547
  class LibSQLAdapter {
1506
1548
  type = 'libsql';
1507
1549
  config;
@@ -1559,10 +1601,10 @@ var __webpack_exports__ = {};
1559
1601
  this.sql.set = set;
1560
1602
  this.sql.where = sql_template_where;
1561
1603
  }
1562
- async query(sql, params = []) {
1563
- return this.execute(sql, params);
1604
+ async query(sql, params = [], options) {
1605
+ return this.execute(sql, params, options);
1564
1606
  }
1565
- async execute(sql, params = []) {
1607
+ async execute(sql, params = [], options) {
1566
1608
  try {
1567
1609
  const target = this.txClient || this.client;
1568
1610
  let query;
@@ -1571,23 +1613,29 @@ var __webpack_exports__ = {};
1571
1613
  console.log('[LibSQL] Executing SQL:', sql);
1572
1614
  console.log('[LibSQL] With params:', params);
1573
1615
  }
1616
+ let args = params;
1617
+ if (options?.stringifyParams) args = libsql_stringifyJsonParams(args, options.stringifyParams);
1574
1618
  query = {
1575
1619
  sql,
1576
- args: params
1620
+ args
1577
1621
  };
1578
1622
  } else {
1579
1623
  if (process.env.DEBUG_SQL) {
1580
1624
  console.log('[LibSQL] Executing SQL:', sql.sql);
1581
1625
  console.log('[LibSQL] With args:', sql.args);
1582
1626
  }
1627
+ let args = sql.args || [];
1628
+ if (options?.stringifyParams) args = libsql_stringifyJsonParams(args, options.stringifyParams);
1583
1629
  query = {
1584
1630
  sql: sql.sql,
1585
- args: sql.args || []
1631
+ args
1586
1632
  };
1587
1633
  }
1588
1634
  const result = await target.execute(query);
1635
+ let rows = result.rows;
1636
+ if (options?.jsonColumns) rows = libsql_parseJsonColumns(rows, options.jsonColumns);
1589
1637
  return {
1590
- rows: result.rows,
1638
+ rows,
1591
1639
  rowsAffected: Number(result.rowsAffected),
1592
1640
  lastInsertRowid: result.lastInsertRowid ? BigInt(result.lastInsertRowid.toString()) : void 0
1593
1641
  };
@@ -1629,6 +1677,26 @@ var __webpack_exports__ = {};
1629
1677
  return createORM(this);
1630
1678
  }
1631
1679
  }
1680
+ function odblite_parseJsonColumns(rows, jsonColumns) {
1681
+ if (!jsonColumns || 0 === jsonColumns.length) return rows;
1682
+ return rows.map((row)=>{
1683
+ const parsed = {
1684
+ ...row
1685
+ };
1686
+ for (const col of jsonColumns)if (col in parsed && 'string' == typeof parsed[col]) try {
1687
+ parsed[col] = JSON.parse(parsed[col]);
1688
+ } catch {}
1689
+ return parsed;
1690
+ });
1691
+ }
1692
+ function odblite_stringifyJsonParams(params, stringifyParams) {
1693
+ if (!stringifyParams || 0 === Object.keys(stringifyParams).length) return params;
1694
+ const result = [
1695
+ ...params
1696
+ ];
1697
+ for (const [_columnName, index] of Object.entries(stringifyParams))if (index < result.length && null != result[index] && 'object' == typeof result[index]) result[index] = JSON.stringify(result[index]);
1698
+ return result;
1699
+ }
1632
1700
  class ODBLiteAdapter {
1633
1701
  type = 'odblite';
1634
1702
  config;
@@ -1715,32 +1783,42 @@ var __webpack_exports__ = {};
1715
1783
  this.sql.set = set;
1716
1784
  this.sql.where = sql_template_where;
1717
1785
  }
1718
- async query(sql, params = []) {
1719
- return this.execute(sql, params);
1786
+ async query(sql, params = [], options) {
1787
+ return this.execute(sql, params, options);
1720
1788
  }
1721
- async execute(sql, params = []) {
1789
+ async execute(sql, params = [], options) {
1722
1790
  try {
1791
+ let rows;
1792
+ let rowsAffected;
1793
+ let lastInsertRowid;
1723
1794
  if ('object' == typeof sql) {
1724
1795
  if (process.env.DEBUG_SQL) {
1725
1796
  console.log('[ODBLite] Executing SQL:', sql.sql);
1726
1797
  console.log('[ODBLite] With args:', sql.args);
1727
1798
  }
1728
- const result = await this.client.sql.execute(sql.sql, sql.args || []);
1729
- return {
1730
- rows: result.rows,
1731
- rowsAffected: result.rowsAffected || 0,
1732
- lastInsertRowid: result.lastInsertRowid
1733
- };
1734
- }
1735
- if (process.env.DEBUG_SQL) {
1736
- console.log('[ODBLite] Executing SQL:', sql);
1737
- console.log('[ODBLite] With params:', params);
1799
+ let args = sql.args || [];
1800
+ if (options?.stringifyParams) args = odblite_stringifyJsonParams(args, options.stringifyParams);
1801
+ const result = await this.client.sql.execute(sql.sql, args);
1802
+ rows = result.rows;
1803
+ rowsAffected = result.rowsAffected || 0;
1804
+ lastInsertRowid = result.lastInsertRowid;
1805
+ } else {
1806
+ if (process.env.DEBUG_SQL) {
1807
+ console.log('[ODBLite] Executing SQL:', sql);
1808
+ console.log('[ODBLite] With params:', params);
1809
+ }
1810
+ let args = params;
1811
+ if (options?.stringifyParams) args = odblite_stringifyJsonParams(args, options.stringifyParams);
1812
+ const result = await this.client.sql.execute(sql, args);
1813
+ rows = result.rows;
1814
+ rowsAffected = result.rowsAffected || 0;
1815
+ lastInsertRowid = result.lastInsertRowid;
1738
1816
  }
1739
- const result = await this.client.sql.execute(sql, params);
1817
+ if (options?.jsonColumns) rows = odblite_parseJsonColumns(rows, options.jsonColumns);
1740
1818
  return {
1741
- rows: result.rows,
1742
- rowsAffected: result.rowsAffected || 0,
1743
- lastInsertRowid: result.lastInsertRowid
1819
+ rows,
1820
+ rowsAffected,
1821
+ lastInsertRowid
1744
1822
  };
1745
1823
  } catch (error) {
1746
1824
  throw error;
package/dist/index.js CHANGED
@@ -1289,6 +1289,26 @@ function sql_template_where(conditions) {
1289
1289
  args: values
1290
1290
  };
1291
1291
  }
1292
+ function parseJsonColumns(rows, jsonColumns) {
1293
+ if (!jsonColumns || 0 === jsonColumns.length) return rows;
1294
+ return rows.map((row)=>{
1295
+ const parsed = {
1296
+ ...row
1297
+ };
1298
+ for (const col of jsonColumns)if (col in parsed && 'string' == typeof parsed[col]) try {
1299
+ parsed[col] = JSON.parse(parsed[col]);
1300
+ } catch {}
1301
+ return parsed;
1302
+ });
1303
+ }
1304
+ function stringifyJsonParams(params, stringifyParams) {
1305
+ if (!stringifyParams || 0 === Object.keys(stringifyParams).length) return params;
1306
+ const result = [
1307
+ ...params
1308
+ ];
1309
+ for (const [_columnName, index] of Object.entries(stringifyParams))if (index < result.length && null != result[index] && 'object' == typeof result[index]) result[index] = JSON.stringify(result[index]);
1310
+ return result;
1311
+ }
1292
1312
  class BunSQLiteAdapter {
1293
1313
  type = 'bun-sqlite';
1294
1314
  config;
@@ -1346,10 +1366,10 @@ class BunSQLiteConnection {
1346
1366
  this.sql.set = set;
1347
1367
  this.sql.where = sql_template_where;
1348
1368
  }
1349
- async query(sql, params = []) {
1350
- return this.execute(sql, params);
1369
+ async query(sql, params = [], options) {
1370
+ return this.execute(sql, params, options);
1351
1371
  }
1352
- async execute(sql, params = []) {
1372
+ async execute(sql, params = [], options) {
1353
1373
  try {
1354
1374
  let sqlStr;
1355
1375
  let sqlParams;
@@ -1368,12 +1388,14 @@ class BunSQLiteConnection {
1368
1388
  sqlStr = sql;
1369
1389
  sqlParams = params;
1370
1390
  }
1391
+ if (options?.stringifyParams) sqlParams = stringifyJsonParams(sqlParams, options.stringifyParams);
1371
1392
  const stmt = this.db.query(sqlStr);
1372
1393
  const isSelect = sqlStr.trim().toUpperCase().startsWith('SELECT') || sqlStr.trim().toUpperCase().includes('RETURNING');
1373
1394
  if (isSelect) {
1374
- const rows = stmt.all(...sqlParams);
1395
+ let rows = stmt.all(...sqlParams);
1396
+ if (options?.jsonColumns) rows = parseJsonColumns(rows, options.jsonColumns);
1375
1397
  return {
1376
- rows: rows,
1398
+ rows,
1377
1399
  rowsAffected: 0
1378
1400
  };
1379
1401
  }
@@ -1440,6 +1462,26 @@ class BunSQLiteConnection {
1440
1462
  return createORM(this);
1441
1463
  }
1442
1464
  }
1465
+ function libsql_parseJsonColumns(rows, jsonColumns) {
1466
+ if (!jsonColumns || 0 === jsonColumns.length) return rows;
1467
+ return rows.map((row)=>{
1468
+ const parsed = {
1469
+ ...row
1470
+ };
1471
+ for (const col of jsonColumns)if (col in parsed && 'string' == typeof parsed[col]) try {
1472
+ parsed[col] = JSON.parse(parsed[col]);
1473
+ } catch {}
1474
+ return parsed;
1475
+ });
1476
+ }
1477
+ function libsql_stringifyJsonParams(params, stringifyParams) {
1478
+ if (!stringifyParams || 0 === Object.keys(stringifyParams).length) return params;
1479
+ const result = [
1480
+ ...params
1481
+ ];
1482
+ for (const [_columnName, index] of Object.entries(stringifyParams))if (index < result.length && null != result[index] && 'object' == typeof result[index]) result[index] = JSON.stringify(result[index]);
1483
+ return result;
1484
+ }
1443
1485
  class LibSQLAdapter {
1444
1486
  type = 'libsql';
1445
1487
  config;
@@ -1497,10 +1539,10 @@ class LibSQLConnection {
1497
1539
  this.sql.set = set;
1498
1540
  this.sql.where = sql_template_where;
1499
1541
  }
1500
- async query(sql, params = []) {
1501
- return this.execute(sql, params);
1542
+ async query(sql, params = [], options) {
1543
+ return this.execute(sql, params, options);
1502
1544
  }
1503
- async execute(sql, params = []) {
1545
+ async execute(sql, params = [], options) {
1504
1546
  try {
1505
1547
  const target = this.txClient || this.client;
1506
1548
  let query;
@@ -1509,23 +1551,29 @@ class LibSQLConnection {
1509
1551
  console.log('[LibSQL] Executing SQL:', sql);
1510
1552
  console.log('[LibSQL] With params:', params);
1511
1553
  }
1554
+ let args = params;
1555
+ if (options?.stringifyParams) args = libsql_stringifyJsonParams(args, options.stringifyParams);
1512
1556
  query = {
1513
1557
  sql,
1514
- args: params
1558
+ args
1515
1559
  };
1516
1560
  } else {
1517
1561
  if (process.env.DEBUG_SQL) {
1518
1562
  console.log('[LibSQL] Executing SQL:', sql.sql);
1519
1563
  console.log('[LibSQL] With args:', sql.args);
1520
1564
  }
1565
+ let args = sql.args || [];
1566
+ if (options?.stringifyParams) args = libsql_stringifyJsonParams(args, options.stringifyParams);
1521
1567
  query = {
1522
1568
  sql: sql.sql,
1523
- args: sql.args || []
1569
+ args
1524
1570
  };
1525
1571
  }
1526
1572
  const result = await target.execute(query);
1573
+ let rows = result.rows;
1574
+ if (options?.jsonColumns) rows = libsql_parseJsonColumns(rows, options.jsonColumns);
1527
1575
  return {
1528
- rows: result.rows,
1576
+ rows,
1529
1577
  rowsAffected: Number(result.rowsAffected),
1530
1578
  lastInsertRowid: result.lastInsertRowid ? BigInt(result.lastInsertRowid.toString()) : void 0
1531
1579
  };
@@ -1567,6 +1615,26 @@ class LibSQLConnection {
1567
1615
  return createORM(this);
1568
1616
  }
1569
1617
  }
1618
+ function odblite_parseJsonColumns(rows, jsonColumns) {
1619
+ if (!jsonColumns || 0 === jsonColumns.length) return rows;
1620
+ return rows.map((row)=>{
1621
+ const parsed = {
1622
+ ...row
1623
+ };
1624
+ for (const col of jsonColumns)if (col in parsed && 'string' == typeof parsed[col]) try {
1625
+ parsed[col] = JSON.parse(parsed[col]);
1626
+ } catch {}
1627
+ return parsed;
1628
+ });
1629
+ }
1630
+ function odblite_stringifyJsonParams(params, stringifyParams) {
1631
+ if (!stringifyParams || 0 === Object.keys(stringifyParams).length) return params;
1632
+ const result = [
1633
+ ...params
1634
+ ];
1635
+ for (const [_columnName, index] of Object.entries(stringifyParams))if (index < result.length && null != result[index] && 'object' == typeof result[index]) result[index] = JSON.stringify(result[index]);
1636
+ return result;
1637
+ }
1570
1638
  class ODBLiteAdapter {
1571
1639
  type = 'odblite';
1572
1640
  config;
@@ -1653,32 +1721,42 @@ class ODBLiteConnection {
1653
1721
  this.sql.set = set;
1654
1722
  this.sql.where = sql_template_where;
1655
1723
  }
1656
- async query(sql, params = []) {
1657
- return this.execute(sql, params);
1724
+ async query(sql, params = [], options) {
1725
+ return this.execute(sql, params, options);
1658
1726
  }
1659
- async execute(sql, params = []) {
1727
+ async execute(sql, params = [], options) {
1660
1728
  try {
1729
+ let rows;
1730
+ let rowsAffected;
1731
+ let lastInsertRowid;
1661
1732
  if ('object' == typeof sql) {
1662
1733
  if (process.env.DEBUG_SQL) {
1663
1734
  console.log('[ODBLite] Executing SQL:', sql.sql);
1664
1735
  console.log('[ODBLite] With args:', sql.args);
1665
1736
  }
1666
- const result = await this.client.sql.execute(sql.sql, sql.args || []);
1667
- return {
1668
- rows: result.rows,
1669
- rowsAffected: result.rowsAffected || 0,
1670
- lastInsertRowid: result.lastInsertRowid
1671
- };
1672
- }
1673
- if (process.env.DEBUG_SQL) {
1674
- console.log('[ODBLite] Executing SQL:', sql);
1675
- console.log('[ODBLite] With params:', params);
1676
- }
1677
- const result = await this.client.sql.execute(sql, params);
1737
+ let args = sql.args || [];
1738
+ if (options?.stringifyParams) args = odblite_stringifyJsonParams(args, options.stringifyParams);
1739
+ const result = await this.client.sql.execute(sql.sql, args);
1740
+ rows = result.rows;
1741
+ rowsAffected = result.rowsAffected || 0;
1742
+ lastInsertRowid = result.lastInsertRowid;
1743
+ } else {
1744
+ if (process.env.DEBUG_SQL) {
1745
+ console.log('[ODBLite] Executing SQL:', sql);
1746
+ console.log('[ODBLite] With params:', params);
1747
+ }
1748
+ let args = params;
1749
+ if (options?.stringifyParams) args = odblite_stringifyJsonParams(args, options.stringifyParams);
1750
+ const result = await this.client.sql.execute(sql, args);
1751
+ rows = result.rows;
1752
+ rowsAffected = result.rowsAffected || 0;
1753
+ lastInsertRowid = result.lastInsertRowid;
1754
+ }
1755
+ if (options?.jsonColumns) rows = odblite_parseJsonColumns(rows, options.jsonColumns);
1678
1756
  return {
1679
- rows: result.rows,
1680
- rowsAffected: result.rowsAffected || 0,
1681
- lastInsertRowid: result.lastInsertRowid
1757
+ rows,
1758
+ rowsAffected,
1759
+ lastInsertRowid
1682
1760
  };
1683
1761
  } catch (error) {
1684
1762
  throw error;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pineliner/odb-client",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "Isomorphic client for ODB-Lite with postgres.js-like template string SQL support",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -5,6 +5,7 @@ import type {
5
5
  QueryResult,
6
6
  PreparedStatement,
7
7
  BunSQLiteConfig,
8
+ QueryOptions,
8
9
  } from '../types'
9
10
  import {
10
11
  convertTemplateToQuery,
@@ -17,6 +18,48 @@ import {
17
18
  where
18
19
  } from '../sql-template'
19
20
 
21
+ /**
22
+ * Parse JSON columns in query results
23
+ * Only parses if the value is a string (to avoid double-parsing)
24
+ */
25
+ function parseJsonColumns<T = any>(rows: any[], jsonColumns?: string[]): T[] {
26
+ if (!jsonColumns || jsonColumns.length === 0) {
27
+ return rows
28
+ }
29
+
30
+ return rows.map(row => {
31
+ const parsed = { ...row }
32
+ for (const col of jsonColumns) {
33
+ if (col in parsed && typeof parsed[col] === 'string') {
34
+ try {
35
+ parsed[col] = JSON.parse(parsed[col])
36
+ } catch {
37
+ // Keep original value if parsing fails
38
+ }
39
+ }
40
+ }
41
+ return parsed
42
+ })
43
+ }
44
+
45
+ /**
46
+ * Stringify JSON parameters for INSERT/UPDATE queries
47
+ * Only stringifies if the value is an object/array (not already a string)
48
+ */
49
+ function stringifyJsonParams(params: any[], stringifyParams?: Record<string, number>): any[] {
50
+ if (!stringifyParams || Object.keys(stringifyParams).length === 0) {
51
+ return params
52
+ }
53
+
54
+ const result = [...params]
55
+ for (const [_columnName, index] of Object.entries(stringifyParams)) {
56
+ if (index < result.length && result[index] != null && typeof result[index] === 'object') {
57
+ result[index] = JSON.stringify(result[index])
58
+ }
59
+ }
60
+ return result
61
+ }
62
+
20
63
  /**
21
64
  * Bun SQLite adapter for DatabaseManager
22
65
  * Wraps bun:sqlite with Connection interface
@@ -117,14 +160,14 @@ class BunSQLiteConnection implements Connection {
117
160
  /**
118
161
  * Query with SQL string and parameters (alias for execute)
119
162
  */
120
- async query<T = any>(sql: string, params: any[] = []): Promise<QueryResult<T>> {
121
- return this.execute(sql, params) as Promise<QueryResult<T>>
163
+ async query<T = any>(sql: string, params: any[] = [], options?: QueryOptions): Promise<QueryResult<T>> {
164
+ return this.execute(sql, params, options) as Promise<QueryResult<T>>
122
165
  }
123
166
 
124
167
  /**
125
168
  * Execute SQL with parameters
126
169
  */
127
- async execute(sql: string | { sql: string; args?: any[] }, params: any[] = []): Promise<QueryResult> {
170
+ async execute(sql: string | { sql: string; args?: any[] }, params: any[] = [], options?: QueryOptions): Promise<QueryResult> {
128
171
  try {
129
172
  // Handle object format { sql, args }
130
173
  let sqlStr: string
@@ -147,14 +190,25 @@ class BunSQLiteConnection implements Connection {
147
190
  sqlParams = params
148
191
  }
149
192
 
193
+ // Stringify JSON parameters if specified
194
+ if (options?.stringifyParams) {
195
+ sqlParams = stringifyJsonParams(sqlParams, options.stringifyParams)
196
+ }
197
+
150
198
  const stmt = this.db.query(sqlStr)
151
199
  const isSelect = sqlStr.trim().toUpperCase().startsWith('SELECT') ||
152
200
  sqlStr.trim().toUpperCase().includes('RETURNING')
153
201
 
154
202
  if (isSelect) {
155
- const rows = stmt.all(...sqlParams)
203
+ let rows = stmt.all(...sqlParams) as any[]
204
+
205
+ // Parse JSON columns if specified
206
+ if (options?.jsonColumns) {
207
+ rows = parseJsonColumns(rows, options.jsonColumns)
208
+ }
209
+
156
210
  return {
157
- rows: rows as any[],
211
+ rows,
158
212
  rowsAffected: 0,
159
213
  }
160
214
  } else {
@@ -5,6 +5,7 @@ import type {
5
5
  QueryResult,
6
6
  PreparedStatement,
7
7
  LibSQLConfig,
8
+ QueryOptions,
8
9
  } from '../types'
9
10
  import {
10
11
  convertTemplateToQuery,
@@ -17,6 +18,48 @@ import {
17
18
  where
18
19
  } from '../sql-template'
19
20
 
21
+ /**
22
+ * Parse JSON columns in query results
23
+ * Only parses if the value is a string (to avoid double-parsing)
24
+ */
25
+ function parseJsonColumns<T = any>(rows: any[], jsonColumns?: string[]): T[] {
26
+ if (!jsonColumns || jsonColumns.length === 0) {
27
+ return rows
28
+ }
29
+
30
+ return rows.map(row => {
31
+ const parsed = { ...row }
32
+ for (const col of jsonColumns) {
33
+ if (col in parsed && typeof parsed[col] === 'string') {
34
+ try {
35
+ parsed[col] = JSON.parse(parsed[col])
36
+ } catch {
37
+ // Keep original value if parsing fails
38
+ }
39
+ }
40
+ }
41
+ return parsed
42
+ })
43
+ }
44
+
45
+ /**
46
+ * Stringify JSON parameters for INSERT/UPDATE queries
47
+ * Only stringifies if the value is an object/array (not already a string)
48
+ */
49
+ function stringifyJsonParams(params: any[], stringifyParams?: Record<string, number>): any[] {
50
+ if (!stringifyParams || Object.keys(stringifyParams).length === 0) {
51
+ return params
52
+ }
53
+
54
+ const result = [...params]
55
+ for (const [_columnName, index] of Object.entries(stringifyParams)) {
56
+ if (index < result.length && result[index] != null && typeof result[index] === 'object') {
57
+ result[index] = JSON.stringify(result[index])
58
+ }
59
+ }
60
+ return result
61
+ }
62
+
20
63
  /**
21
64
  * LibSQL adapter for DatabaseManager
22
65
  * Wraps @libsql/client with Connection interface
@@ -117,15 +160,15 @@ class LibSQLConnection implements Connection {
117
160
  /**
118
161
  * Query with SQL string and parameters (alias for execute)
119
162
  */
120
- async query<T = any>(sql: string, params: any[] = []): Promise<QueryResult<T>> {
121
- return this.execute(sql, params) as Promise<QueryResult<T>>
163
+ async query<T = any>(sql: string, params: any[] = [], options?: QueryOptions): Promise<QueryResult<T>> {
164
+ return this.execute(sql, params, options) as Promise<QueryResult<T>>
122
165
  }
123
166
 
124
167
  /**
125
168
  * Execute SQL with parameters
126
169
  * Supports both formats: execute(sql, params) and execute({sql, args})
127
170
  */
128
- async execute(sql: string | { sql: string; args?: any[] }, params: any[] = []): Promise<QueryResult> {
171
+ async execute(sql: string | { sql: string; args?: any[] }, params: any[] = [], options?: QueryOptions): Promise<QueryResult> {
129
172
  try {
130
173
  const target = this.txClient || this.client
131
174
 
@@ -137,19 +180,36 @@ class LibSQLConnection implements Connection {
137
180
  console.log('[LibSQL] Executing SQL:', sql)
138
181
  console.log('[LibSQL] With params:', params)
139
182
  }
140
- query = { sql, args: params }
183
+ let args = params
184
+ // Stringify JSON parameters if specified
185
+ if (options?.stringifyParams) {
186
+ args = stringifyJsonParams(args, options.stringifyParams)
187
+ }
188
+ query = { sql, args }
141
189
  } else {
142
190
  if (process.env.DEBUG_SQL) {
143
191
  console.log('[LibSQL] Executing SQL:', sql.sql)
144
192
  console.log('[LibSQL] With args:', sql.args)
145
193
  }
146
- query = { sql: sql.sql, args: sql.args || [] }
194
+ let args = sql.args || []
195
+ // Stringify JSON parameters if specified
196
+ if (options?.stringifyParams) {
197
+ args = stringifyJsonParams(args, options.stringifyParams)
198
+ }
199
+ query = { sql: sql.sql, args }
147
200
  }
148
201
 
149
202
  const result = await target.execute(query)
150
203
 
204
+ let rows = result.rows as any[]
205
+
206
+ // Parse JSON columns if specified
207
+ if (options?.jsonColumns) {
208
+ rows = parseJsonColumns(rows, options.jsonColumns)
209
+ }
210
+
151
211
  return {
152
- rows: result.rows as any[],
212
+ rows,
153
213
  rowsAffected: Number(result.rowsAffected),
154
214
  lastInsertRowid: result.lastInsertRowid
155
215
  ? BigInt(result.lastInsertRowid.toString())
@@ -6,6 +6,7 @@ import type {
6
6
  QueryResult,
7
7
  PreparedStatement,
8
8
  ODBLiteConfig,
9
+ QueryOptions,
9
10
  } from '../types'
10
11
  import {
11
12
  convertTemplateToQuery,
@@ -18,6 +19,48 @@ import {
18
19
  where
19
20
  } from '../sql-template'
20
21
 
22
+ /**
23
+ * Parse JSON columns in query results
24
+ * Only parses if the value is a string (to avoid double-parsing)
25
+ */
26
+ function parseJsonColumns<T = any>(rows: any[], jsonColumns?: string[]): T[] {
27
+ if (!jsonColumns || jsonColumns.length === 0) {
28
+ return rows
29
+ }
30
+
31
+ return rows.map(row => {
32
+ const parsed = { ...row }
33
+ for (const col of jsonColumns) {
34
+ if (col in parsed && typeof parsed[col] === 'string') {
35
+ try {
36
+ parsed[col] = JSON.parse(parsed[col])
37
+ } catch {
38
+ // Keep original value if parsing fails
39
+ }
40
+ }
41
+ }
42
+ return parsed
43
+ })
44
+ }
45
+
46
+ /**
47
+ * Stringify JSON parameters for INSERT/UPDATE queries
48
+ * Only stringifies if the value is an object/array (not already a string)
49
+ */
50
+ function stringifyJsonParams(params: any[], stringifyParams?: Record<string, number>): any[] {
51
+ if (!stringifyParams || Object.keys(stringifyParams).length === 0) {
52
+ return params
53
+ }
54
+
55
+ const result = [...params]
56
+ for (const [_columnName, index] of Object.entries(stringifyParams)) {
57
+ if (index < result.length && result[index] != null && typeof result[index] === 'object') {
58
+ result[index] = JSON.stringify(result[index])
59
+ }
60
+ }
61
+ return result
62
+ }
63
+
21
64
  /**
22
65
  * ODB-Lite adapter for DatabaseManager
23
66
  * Wraps ServiceClient and ODBLiteClient with Connection interface
@@ -162,15 +205,19 @@ class ODBLiteConnection implements Connection {
162
205
  /**
163
206
  * Query with SQL string and parameters (alias for execute)
164
207
  */
165
- async query<T = any>(sql: string, params: any[] = []): Promise<QueryResult<T>> {
166
- return this.execute(sql, params) as Promise<QueryResult<T>>
208
+ async query<T = any>(sql: string, params: any[] = [], options?: QueryOptions): Promise<QueryResult<T>> {
209
+ return this.execute(sql, params, options) as Promise<QueryResult<T>>
167
210
  }
168
211
 
169
212
  /**
170
213
  * Execute SQL with parameters
171
214
  */
172
- async execute(sql: string | { sql: string; args?: any[] }, params: any[] = []): Promise<QueryResult> {
215
+ async execute(sql: string | { sql: string; args?: any[] }, params: any[] = [], options?: QueryOptions): Promise<QueryResult> {
173
216
  try {
217
+ let rows: any[]
218
+ let rowsAffected: number
219
+ let lastInsertRowid: any
220
+
174
221
  // Handle object format { sql, args }
175
222
  if (typeof sql === 'object') {
176
223
  // Debug logging for SQL errors
@@ -178,25 +225,41 @@ class ODBLiteConnection implements Connection {
178
225
  console.log('[ODBLite] Executing SQL:', sql.sql)
179
226
  console.log('[ODBLite] With args:', sql.args)
180
227
  }
181
- const result = await this.client.sql.execute(sql.sql, sql.args || [])
182
- return {
183
- rows: result.rows,
184
- rowsAffected: result.rowsAffected || 0,
185
- lastInsertRowid: (result as any).lastInsertRowid,
228
+ let args = sql.args || []
229
+ // Stringify JSON parameters if specified
230
+ if (options?.stringifyParams) {
231
+ args = stringifyJsonParams(args, options.stringifyParams)
232
+ }
233
+ const result = await this.client.sql.execute(sql.sql, args)
234
+ rows = result.rows
235
+ rowsAffected = result.rowsAffected || 0
236
+ lastInsertRowid = (result as any).lastInsertRowid
237
+ } else {
238
+ // Handle string format
239
+ if (process.env.DEBUG_SQL) {
240
+ console.log('[ODBLite] Executing SQL:', sql)
241
+ console.log('[ODBLite] With params:', params)
242
+ }
243
+ let args = params
244
+ // Stringify JSON parameters if specified
245
+ if (options?.stringifyParams) {
246
+ args = stringifyJsonParams(args, options.stringifyParams)
186
247
  }
248
+ const result = await this.client.sql.execute(sql, args)
249
+ rows = result.rows
250
+ rowsAffected = result.rowsAffected || 0
251
+ lastInsertRowid = (result as any).lastInsertRowid
187
252
  }
188
253
 
189
- // Handle string format
190
- if (process.env.DEBUG_SQL) {
191
- console.log('[ODBLite] Executing SQL:', sql)
192
- console.log('[ODBLite] With params:', params)
254
+ // Parse JSON columns if specified
255
+ if (options?.jsonColumns) {
256
+ rows = parseJsonColumns(rows, options.jsonColumns)
193
257
  }
194
- const result = await this.client.sql.execute(sql, params)
195
258
 
196
259
  return {
197
- rows: result.rows,
198
- rowsAffected: result.rowsAffected || 0,
199
- lastInsertRowid: (result as any).lastInsertRowid,
260
+ rows,
261
+ rowsAffected,
262
+ lastInsertRowid,
200
263
  }
201
264
  } catch (error: any) {
202
265
  // Re-throw the original error to let the application handle it
@@ -25,6 +25,32 @@ export interface QueryResult<T = any> {
25
25
  lastInsertRowid?: number | bigint
26
26
  }
27
27
 
28
+ /**
29
+ * Query options for fine-grained control
30
+ */
31
+ export interface QueryOptions {
32
+ /**
33
+ * List of column names that should be auto-parsed as JSON when reading
34
+ * Only applies to columns that contain stringified JSON in SELECT queries
35
+ *
36
+ * @example
37
+ * await conn.query('SELECT * FROM suppliers', [], { jsonColumns: ['connections', 'config'] })
38
+ */
39
+ jsonColumns?: string[]
40
+
41
+ /**
42
+ * Automatically stringify parameters for JSON columns in UPDATE/INSERT queries
43
+ * Maps column names to parameter indices (0-based)
44
+ *
45
+ * @example
46
+ * // UPDATE suppliers SET connections = ?, updated_at = ? WHERE id = ?
47
+ * await conn.execute(query, [connections, now, id], {
48
+ * stringifyParams: { connections: 0 } // Auto-stringify first parameter
49
+ * })
50
+ */
51
+ stringifyParams?: Record<string, number>
52
+ }
53
+
28
54
  /**
29
55
  * Unified database connection interface
30
56
  * All backends implement this through adapters
@@ -43,8 +69,8 @@ export interface Connection {
43
69
  sql(value: any, ...keys: string[]): any
44
70
 
45
71
  // Standard query methods (return QueryResult)
46
- query<T = any>(sql: string, params?: any[]): Promise<QueryResult<T>>
47
- execute(sql: string | { sql: string; args?: any[] }, params?: any[]): Promise<QueryResult>
72
+ query<T = any>(sql: string, params?: any[], options?: QueryOptions): Promise<QueryResult<T>>
73
+ execute(sql: string | { sql: string; args?: any[] }, params?: any[], options?: QueryOptions): Promise<QueryResult>
48
74
  prepare(sql: string): PreparedStatement
49
75
 
50
76
  // Transaction support