sqlstack 1.0.0 → 1.0.1

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,3 +1,3 @@
1
1
  import type { Database } from '../registry';
2
- export declare function createMysqlDb(url: string): Database;
2
+ export declare function createMysqlDb(urlOrPool: string | any): Database;
3
3
  //# sourceMappingURL=mysql.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mysql.d.ts","sourceRoot":"","sources":["../../src/adapters/mysql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAgBnD"}
1
+ {"version":3,"file":"mysql.d.ts","sourceRoot":"","sources":["../../src/adapters/mysql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,GAAG,GAAG,QAAQ,CAmC/D"}
@@ -1,20 +1,38 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createMysqlDb = createMysqlDb;
4
- function createMysqlDb(url) {
5
- // Lazy import to keep driver optional
6
- // eslint-disable-next-line @typescript-eslint/no-var-requires
7
- const mysql = require('mysql2/promise');
8
- const pool = mysql.createPool({ uri: url, connectionLimit: 4 });
4
+ function createMysqlDb(urlOrPool) {
5
+ let pool;
6
+ let own = false;
7
+ if (typeof urlOrPool === 'string') {
8
+ // Lazy import to keep driver optional
9
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
10
+ const mysql = require('mysql2/promise');
11
+ pool = mysql.createPool({ uri: urlOrPool, connectionLimit: 4 });
12
+ own = true;
13
+ }
14
+ else {
15
+ pool = urlOrPool;
16
+ }
9
17
  return {
10
18
  dialect: 'mysql',
11
19
  async query(sql, params) {
12
20
  const [rows] = await pool.query(sql, params);
13
- // rows may be RowDataPacket[]; spread into plain objects
14
- return Array.isArray(rows) ? rows.map((r) => ({ ...r })) : [];
21
+ if (Array.isArray(rows)) {
22
+ // SELECT/CTE result
23
+ return rows.map((r) => ({ ...r }));
24
+ }
25
+ // Non-SELECT result header
26
+ const hdr = rows || {};
27
+ const rowsAffected = Number(hdr.affectedRows ?? hdr.changedRows ?? 0);
28
+ const lastInsertId = hdr.insertId;
29
+ return { rowsAffected, lastInsertId };
15
30
  },
31
+ conn: pool,
16
32
  async close() {
17
- await pool.end();
33
+ if (own && typeof pool?.end === 'function') {
34
+ await pool.end();
35
+ }
18
36
  },
19
37
  };
20
38
  }
@@ -1 +1 @@
1
- {"version":3,"file":"mysql.js","sourceRoot":"","sources":["../../src/adapters/mysql.ts"],"names":[],"mappings":";;AAEA,sCAgBC;AAhBD,SAAgB,aAAa,CAAC,GAAW;IACvC,sCAAsC;IACtC,8DAA8D;IAC9D,MAAM,KAAK,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,eAAe,EAAE,CAAC,EAAE,CAAC,CAAC;IAChE,OAAO;QACL,OAAO,EAAE,OAAO;QAChB,KAAK,CAAC,KAAK,CAAC,GAAW,EAAE,MAAiB;YACxC,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAC7C,yDAAyD;YACzD,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAI,CAAS,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACzE,CAAC;QACD,KAAK,CAAC,KAAK;YACT,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;QACnB,CAAC;KACU,CAAC;AAChB,CAAC","sourcesContent":["import type { Database } from '../registry';\n\nexport function createMysqlDb(url: string): Database {\n // Lazy import to keep driver optional\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const mysql = require('mysql2/promise');\n const pool = mysql.createPool({ uri: url, connectionLimit: 4 });\n return {\n dialect: 'mysql',\n async query(sql: string, params: unknown[]) {\n const [rows] = await pool.query(sql, params);\n // rows may be RowDataPacket[]; spread into plain objects\n return Array.isArray(rows) ? rows.map((r) => ({ ...(r as any) })) : [];\n },\n async close() {\n await pool.end();\n },\n } as Database;\n}\n"]}
1
+ {"version":3,"file":"mysql.js","sourceRoot":"","sources":["../../src/adapters/mysql.ts"],"names":[],"mappings":";;AAEA,sCAmCC;AAnCD,SAAgB,aAAa,CAAC,SAAuB;IACnD,IAAI,IAAS,CAAC;IACd,IAAI,GAAG,GAAG,KAAK,CAAC;IAEhB,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;QAClC,sCAAsC;QACtC,8DAA8D;QAC9D,MAAM,KAAK,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;QACxC,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC,EAAE,CAAC,CAAC;QAChE,GAAG,GAAG,IAAI,CAAC;IACb,CAAC;SAAM,CAAC;QACN,IAAI,GAAG,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;QACL,OAAO,EAAE,OAAO;QAChB,KAAK,CAAC,KAAK,CAAC,GAAW,EAAE,MAAiB;YACxC,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAC7C,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBACxB,oBAAoB;gBACpB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;YAC1C,CAAC;YACD,2BAA2B;YAC3B,MAAM,GAAG,GAAQ,IAAI,IAAI,EAAE,CAAC;YAC5B,MAAM,YAAY,GAAG,MAAM,CAAC,GAAG,CAAC,YAAY,IAAI,GAAG,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC;YACtE,MAAM,YAAY,GAAG,GAAG,CAAC,QAAQ,CAAC;YAClC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC;QACxC,CAAC;QACD,IAAI,EAAE,IAAI;QACV,KAAK,CAAC,KAAK;YACT,IAAI,GAAG,IAAI,OAAO,IAAI,EAAE,GAAG,KAAK,UAAU,EAAE,CAAC;gBAC3C,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;YACnB,CAAC;QACH,CAAC;KACU,CAAC;AAChB,CAAC","sourcesContent":["import type { Database } from '../registry';\n\nexport function createMysqlDb(urlOrPool: string | any): Database {\n let pool: any;\n let own = false;\n\n if (typeof urlOrPool === 'string') {\n // Lazy import to keep driver optional\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const mysql = require('mysql2/promise');\n pool = mysql.createPool({ uri: urlOrPool, connectionLimit: 4 });\n own = true;\n } else {\n pool = urlOrPool;\n }\n\n return {\n dialect: 'mysql',\n async query(sql: string, params: unknown[]) {\n const [rows] = await pool.query(sql, params);\n if (Array.isArray(rows)) {\n // SELECT/CTE result\n return rows.map((r: any) => ({ ...r }));\n }\n // Non-SELECT result header\n const hdr: any = rows || {};\n const rowsAffected = Number(hdr.affectedRows ?? hdr.changedRows ?? 0);\n const lastInsertId = hdr.insertId;\n return { rowsAffected, lastInsertId };\n },\n conn: pool,\n async close() {\n if (own && typeof pool?.end === 'function') {\n await pool.end();\n }\n },\n } as Database;\n}\n"]}
@@ -1,3 +1,3 @@
1
1
  import type { Database } from '../registry';
2
- export declare function createPgDb(url: string): Database;
2
+ export declare function createPgDb(urlOrPool: string | any): Database;
3
3
  //# sourceMappingURL=pg.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pg.d.ts","sourceRoot":"","sources":["../../src/adapters/pg.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAehD"}
1
+ {"version":3,"file":"pg.d.ts","sourceRoot":"","sources":["../../src/adapters/pg.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AA6B5C,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,GAAG,GAAG,QAAQ,CAoC5D"}
@@ -1,19 +1,69 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createPgDb = createPgDb;
4
- function createPgDb(url) {
5
- // Lazy import to keep driver optional
6
- // eslint-disable-next-line @typescript-eslint/no-var-requires
7
- const { Pool } = require('pg');
8
- const pool = new Pool({ connectionString: url, max: 4 });
4
+ function stripLeadingComments(sql) {
5
+ let i = 0;
6
+ const n = sql.length;
7
+ // Strip BOM
8
+ if (sql.charCodeAt(0) === 0xfeff)
9
+ i = 1;
10
+ while (i < n) {
11
+ // Skip whitespace
12
+ while (i < n && /\s/.test(sql[i]))
13
+ i++;
14
+ if (i + 1 < n && sql[i] === '-' && sql[i + 1] === '-') {
15
+ // Line comment -- ...\n
16
+ i += 2;
17
+ while (i < n && sql[i] !== '\n')
18
+ i++;
19
+ continue;
20
+ }
21
+ if (i + 1 < n && sql[i] === '/' && sql[i + 1] === '*') {
22
+ // Block comment /* ... */
23
+ i += 2;
24
+ let end = sql.indexOf('*/', i);
25
+ if (end === -1)
26
+ return '';
27
+ i = end + 2;
28
+ continue;
29
+ }
30
+ break;
31
+ }
32
+ return sql.slice(i);
33
+ }
34
+ function createPgDb(urlOrPool) {
35
+ let pool;
36
+ let own = false;
37
+ if (typeof urlOrPool === 'string') {
38
+ // Lazy import to keep driver optional
39
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
40
+ const { Pool } = require('pg');
41
+ pool = new Pool({ connectionString: urlOrPool, max: 4 });
42
+ own = true;
43
+ }
44
+ else {
45
+ pool = urlOrPool;
46
+ }
9
47
  return {
10
48
  dialect: 'postgres',
11
49
  async query(sql, params) {
12
50
  const res = await pool.query(sql, params);
13
- return res.rows;
51
+ const text = stripLeadingComments(String(sql)).trimStart().toUpperCase();
52
+ if (text.startsWith('SELECT') || text.startsWith('WITH')) {
53
+ return res.rows;
54
+ }
55
+ // Non-SELECT: include rowCount and optional returning
56
+ const base = { rowsAffected: Number(res.rowCount ?? 0) };
57
+ if (Array.isArray(res.rows) && res.rows.length > 0) {
58
+ base.returning = res.rows;
59
+ }
60
+ return base;
14
61
  },
62
+ conn: pool,
15
63
  async close() {
16
- await pool.end();
64
+ if (own && typeof pool?.end === 'function') {
65
+ await pool.end();
66
+ }
17
67
  },
18
68
  };
19
69
  }
@@ -1 +1 @@
1
- {"version":3,"file":"pg.js","sourceRoot":"","sources":["../../src/adapters/pg.ts"],"names":[],"mappings":";;AAEA,gCAeC;AAfD,SAAgB,UAAU,CAAC,GAAW;IACpC,sCAAsC;IACtC,8DAA8D;IAC9D,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,gBAAgB,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IACzD,OAAO;QACL,OAAO,EAAE,UAAU;QACnB,KAAK,CAAC,KAAK,CAAC,GAAW,EAAE,MAAiB;YACxC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAC1C,OAAO,GAAG,CAAC,IAAI,CAAC;QAClB,CAAC;QACD,KAAK,CAAC,KAAK;YACT,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;QACnB,CAAC;KACU,CAAC;AAChB,CAAC","sourcesContent":["import type { Database } from '../registry';\n\nexport function createPgDb(url: string): Database {\n // Lazy import to keep driver optional\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const { Pool } = require('pg');\n const pool = new Pool({ connectionString: url, max: 4 });\n return {\n dialect: 'postgres',\n async query(sql: string, params: unknown[]) {\n const res = await pool.query(sql, params);\n return res.rows;\n },\n async close() {\n await pool.end();\n },\n } as Database;\n}\n"]}
1
+ {"version":3,"file":"pg.js","sourceRoot":"","sources":["../../src/adapters/pg.ts"],"names":[],"mappings":";;AA6BA,gCAoCC;AA/DD,SAAS,oBAAoB,CAAC,GAAW;IACvC,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC;IACrB,YAAY;IACZ,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,MAAM;QAAE,CAAC,GAAG,CAAC,CAAC;IACxC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACb,kBAAkB;QAClB,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC;YAAE,CAAC,EAAE,CAAC;QACxC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACtD,wBAAwB;YACxB,CAAC,IAAI,CAAC,CAAC;YACP,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI;gBAAE,CAAC,EAAE,CAAC;YACrC,SAAS;QACX,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACtD,0BAA0B;YAC1B,CAAC,IAAI,CAAC,CAAC;YACP,IAAI,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YAC/B,IAAI,GAAG,KAAK,CAAC,CAAC;gBAAE,OAAO,EAAE,CAAC;YAC1B,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC;YACZ,SAAS;QACX,CAAC;QACD,MAAM;IACR,CAAC;IACD,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACtB,CAAC;AAED,SAAgB,UAAU,CAAC,SAAuB;IAChD,IAAI,IAAS,CAAC;IACd,IAAI,GAAG,GAAG,KAAK,CAAC;IAEhB,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;QAClC,sCAAsC;QACtC,8DAA8D;QAC9D,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,gBAAgB,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACzD,GAAG,GAAG,IAAI,CAAC;IACb,CAAC;SAAM,CAAC;QACN,IAAI,GAAG,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;QACL,OAAO,EAAE,UAAU;QACnB,KAAK,CAAC,KAAK,CAAC,GAAW,EAAE,MAAiB;YACxC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAC1C,MAAM,IAAI,GAAG,oBAAoB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,WAAW,EAAE,CAAC;YACzE,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBACzD,OAAO,GAAG,CAAC,IAAI,CAAC;YAClB,CAAC;YACD,sDAAsD;YACtD,MAAM,IAAI,GAAQ,EAAE,YAAY,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,IAAI,CAAC,CAAC,EAAE,CAAC;YAC9D,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACnD,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC,IAAI,CAAC;YAC5B,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,EAAE,IAAI;QACV,KAAK,CAAC,KAAK;YACT,IAAI,GAAG,IAAI,OAAO,IAAI,EAAE,GAAG,KAAK,UAAU,EAAE,CAAC;gBAC3C,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;YACnB,CAAC;QACH,CAAC;KACU,CAAC;AAChB,CAAC","sourcesContent":["import type { Database } from '../registry';\n\nfunction stripLeadingComments(sql: string): string {\n let i = 0;\n const n = sql.length;\n // Strip BOM\n if (sql.charCodeAt(0) === 0xfeff) i = 1;\n while (i < n) {\n // Skip whitespace\n while (i < n && /\\s/.test(sql[i]!)) i++;\n if (i + 1 < n && sql[i] === '-' && sql[i + 1] === '-') {\n // Line comment -- ...\\n\n i += 2;\n while (i < n && sql[i] !== '\\n') i++;\n continue;\n }\n if (i + 1 < n && sql[i] === '/' && sql[i + 1] === '*') {\n // Block comment /* ... */\n i += 2;\n let end = sql.indexOf('*/', i);\n if (end === -1) return '';\n i = end + 2;\n continue;\n }\n break;\n }\n return sql.slice(i);\n}\n\nexport function createPgDb(urlOrPool: string | any): Database {\n let pool: any;\n let own = false;\n\n if (typeof urlOrPool === 'string') {\n // Lazy import to keep driver optional\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const { Pool } = require('pg');\n pool = new Pool({ connectionString: urlOrPool, max: 4 });\n own = true;\n } else {\n pool = urlOrPool;\n }\n\n return {\n dialect: 'postgres',\n async query(sql: string, params: unknown[]) {\n const res = await pool.query(sql, params);\n const text = stripLeadingComments(String(sql)).trimStart().toUpperCase();\n if (text.startsWith('SELECT') || text.startsWith('WITH')) {\n return res.rows;\n }\n // Non-SELECT: include rowCount and optional returning\n const base: any = { rowsAffected: Number(res.rowCount ?? 0) };\n if (Array.isArray(res.rows) && res.rows.length > 0) {\n base.returning = res.rows;\n }\n return base;\n },\n conn: pool,\n async close() {\n if (own && typeof pool?.end === 'function') {\n await pool.end();\n }\n },\n } as Database;\n}\n"]}
@@ -1,3 +1,3 @@
1
1
  import type { Database } from '../registry';
2
- export declare function createSqliteDb(filename: string): Database;
2
+ export declare function createSqliteDb(filenameOrDb: string | any): Database;
3
3
  //# sourceMappingURL=sqlite.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/adapters/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAyDzD"}
1
+ {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/adapters/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AA2B5C,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,GAAG,GAAG,QAAQ,CA+GnE"}
@@ -1,7 +1,85 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createSqliteDb = createSqliteDb;
4
- function createSqliteDb(filename) {
4
+ function stripLeadingComments(sql) {
5
+ let i = 0;
6
+ const n = sql.length;
7
+ if (sql.charCodeAt(0) === 0xfeff)
8
+ i = 1; // BOM
9
+ while (i < n) {
10
+ while (i < n && /\s/.test(sql[i]))
11
+ i++;
12
+ if (i + 1 < n && sql[i] === '-' && sql[i + 1] === '-') {
13
+ // -- line comment
14
+ i += 2;
15
+ while (i < n && sql[i] !== '\n')
16
+ i++;
17
+ continue;
18
+ }
19
+ if (i + 1 < n && sql[i] === '/' && sql[i + 1] === '*') {
20
+ // /* block comment */
21
+ i += 2;
22
+ const end = sql.indexOf('*/', i);
23
+ if (end === -1)
24
+ return '';
25
+ i = end + 2;
26
+ continue;
27
+ }
28
+ break;
29
+ }
30
+ return sql.slice(i);
31
+ }
32
+ function createSqliteDb(filenameOrDb) {
33
+ // If a pre-created connection is provided
34
+ if (typeof filenameOrDb !== 'string' && filenameOrDb) {
35
+ const db = filenameOrDb;
36
+ // better-sqlite3 shape
37
+ if (typeof db.prepare === 'function') {
38
+ return {
39
+ dialect: 'sqlite',
40
+ async query(sql, params) {
41
+ const stmt = db.prepare(sql);
42
+ const text = stripLeadingComments(String(sql)).trimStart().toUpperCase();
43
+ if (text.startsWith('SELECT') || text.startsWith('WITH')) {
44
+ return stmt.all(...params);
45
+ }
46
+ const info = stmt.run(...params);
47
+ return { rowsAffected: Number(info?.changes ?? 0), lastInsertId: info?.lastInsertRowid };
48
+ },
49
+ conn: db,
50
+ // Do not close externally provided connection
51
+ close() { },
52
+ };
53
+ }
54
+ // sqlite3 shape
55
+ return {
56
+ dialect: 'sqlite',
57
+ async query(sql, params) {
58
+ const text = stripLeadingComments(String(sql)).trimStart().toUpperCase();
59
+ if (text.startsWith('SELECT') || text.startsWith('WITH')) {
60
+ return new Promise((resolve, reject) => {
61
+ db.all(sql, params, (err, rows) => {
62
+ if (err)
63
+ return reject(err);
64
+ resolve(rows || []);
65
+ });
66
+ });
67
+ }
68
+ return new Promise((resolve, reject) => {
69
+ // Use function callback to access `this` for metadata
70
+ db.run(sql, params, function (err) {
71
+ if (err)
72
+ return reject(err);
73
+ resolve({ rowsAffected: Number(this?.changes ?? 0), lastInsertId: this?.lastID });
74
+ });
75
+ });
76
+ },
77
+ conn: db,
78
+ // Do not close externally provided connection
79
+ close() { },
80
+ };
81
+ }
82
+ const filename = filenameOrDb;
5
83
  // Prefer better-sqlite3
6
84
  try {
7
85
  // eslint-disable-next-line @typescript-eslint/no-var-requires
@@ -11,13 +89,14 @@ function createSqliteDb(filename) {
11
89
  dialect: 'sqlite',
12
90
  async query(sql, params) {
13
91
  const stmt = db.prepare(sql);
14
- const text = String(sql).trim().toUpperCase();
92
+ const text = stripLeadingComments(String(sql)).trimStart().toUpperCase();
15
93
  if (text.startsWith('SELECT') || text.startsWith('WITH')) {
16
94
  return stmt.all(...params);
17
95
  }
18
- stmt.run(...params);
19
- return [];
96
+ const info = stmt.run(...params);
97
+ return { rowsAffected: Number(info?.changes ?? 0), lastInsertId: info?.lastInsertRowid };
20
98
  },
99
+ conn: db,
21
100
  close() {
22
101
  db.close();
23
102
  },
@@ -32,7 +111,7 @@ function createSqliteDb(filename) {
32
111
  return {
33
112
  dialect: 'sqlite',
34
113
  async query(sql, params) {
35
- const text = String(sql).trim().toUpperCase();
114
+ const text = stripLeadingComments(String(sql)).trimStart().toUpperCase();
36
115
  if (text.startsWith('SELECT') || text.startsWith('WITH')) {
37
116
  return new Promise((resolve, reject) => {
38
117
  db.all(sql, params, (err, rows) => {
@@ -44,13 +123,14 @@ function createSqliteDb(filename) {
44
123
  }
45
124
  // Non-SELECT statements
46
125
  return new Promise((resolve, reject) => {
47
- db.run(sql, params, (err) => {
126
+ db.run(sql, params, function (err) {
48
127
  if (err)
49
128
  return reject(err);
50
- resolve([]);
129
+ resolve({ rowsAffected: Number(this?.changes ?? 0), lastInsertId: this?.lastID });
51
130
  });
52
131
  });
53
132
  },
133
+ conn: db,
54
134
  close() {
55
135
  return new Promise((resolve, reject) => {
56
136
  db.close((err) => (err ? reject(err) : resolve()));
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../src/adapters/sqlite.ts"],"names":[],"mappings":";;AAEA,wCAyDC;AAzDD,SAAgB,cAAc,CAAC,QAAgB;IAC7C,wBAAwB;IACxB,IAAI,CAAC;QACH,8DAA8D;QAC9D,MAAM,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;QAChD,MAAM,EAAE,GAAG,IAAI,aAAa,CAAC,QAAQ,CAAC,CAAC;QACvC,OAAO;YACL,OAAO,EAAE,QAAQ;YACjB,KAAK,CAAC,KAAK,CAAC,GAAW,EAAE,MAAiB;gBACxC,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;gBAC9C,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;oBACzD,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;gBAC7B,CAAC;gBACD,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;gBACpB,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,KAAK;gBACH,EAAE,CAAC,KAAK,EAAE,CAAC;YACb,CAAC;SACU,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IAEV,sBAAsB;IACtB,IAAI,CAAC;QACH,8DAA8D;QAC9D,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;QACnC,MAAM,EAAE,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC1C,OAAO;YACL,OAAO,EAAE,QAAQ;YACjB,KAAK,CAAC,KAAK,CAAC,GAAW,EAAE,MAAiB;gBACxC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;gBAC9C,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;oBACzD,OAAO,IAAI,OAAO,CAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;wBAC5C,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,GAAiB,EAAE,IAAW,EAAE,EAAE;4BACrD,IAAI,GAAG;gCAAE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;4BAC5B,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;wBACtB,CAAC,CAAC,CAAC;oBACL,CAAC,CAAC,CAAC;gBACL,CAAC;gBACD,wBAAwB;gBACxB,OAAO,IAAI,OAAO,CAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;oBAC5C,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,GAAiB,EAAE,EAAE;wBACxC,IAAI,GAAG;4BAAE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;wBAC5B,OAAO,CAAC,EAAE,CAAC,CAAC;oBACd,CAAC,CAAC,CAAC;gBACL,CAAC,CAAC,CAAC;YACL,CAAC;YACD,KAAK;gBACH,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;oBAC3C,EAAE,CAAC,KAAK,CAAC,CAAC,GAAiB,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;gBACnE,CAAC,CAAC,CAAC;YACL,CAAC;SACU,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IAEV,MAAM,IAAI,KAAK,CAAC,uEAAuE,CAAC,CAAC;AAC3F,CAAC","sourcesContent":["import type { Database } from '../registry';\n\nexport function createSqliteDb(filename: string): Database {\n // Prefer better-sqlite3\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const BetterSqlite3 = require('better-sqlite3');\n const db = new BetterSqlite3(filename);\n return {\n dialect: 'sqlite',\n async query(sql: string, params: unknown[]) {\n const stmt = db.prepare(sql);\n const text = String(sql).trim().toUpperCase();\n if (text.startsWith('SELECT') || text.startsWith('WITH')) {\n return stmt.all(...params);\n }\n stmt.run(...params);\n return [];\n },\n close() {\n db.close();\n },\n } as Database;\n } catch {}\n\n // Fallback to sqlite3\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const sqlite3 = require('sqlite3');\n const db = new sqlite3.Database(filename);\n return {\n dialect: 'sqlite',\n async query(sql: string, params: unknown[]) {\n const text = String(sql).trim().toUpperCase();\n if (text.startsWith('SELECT') || text.startsWith('WITH')) {\n return new Promise<any[]>((resolve, reject) => {\n db.all(sql, params, (err: Error | null, rows: any[]) => {\n if (err) return reject(err);\n resolve(rows || []);\n });\n });\n }\n // Non-SELECT statements\n return new Promise<any[]>((resolve, reject) => {\n db.run(sql, params, (err: Error | null) => {\n if (err) return reject(err);\n resolve([]);\n });\n });\n },\n close() {\n return new Promise<void>((resolve, reject) => {\n db.close((err: Error | null) => (err ? reject(err) : resolve()));\n });\n },\n } as Database;\n } catch {}\n\n throw new Error('No SQLite driver available. Install either better-sqlite3 or sqlite3.');\n}\n"]}
1
+ {"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../../src/adapters/sqlite.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Database } from '../registry';\n\nfunction stripLeadingComments(sql: string): string {\n let i = 0;\n const n = sql.length;\n if (sql.charCodeAt(0) === 0xfeff) i = 1; // BOM\n while (i < n) {\n while (i < n && /\\s/.test(sql[i]!)) i++;\n if (i + 1 < n && sql[i] === '-' && sql[i + 1] === '-') {\n // -- line comment\n i += 2;\n while (i < n && sql[i] !== '\\n') i++;\n continue;\n }\n if (i + 1 < n && sql[i] === '/' && sql[i + 1] === '*') {\n // /* block comment */\n i += 2;\n const end = sql.indexOf('*/', i);\n if (end === -1) return '';\n i = end + 2;\n continue;\n }\n break;\n }\n return sql.slice(i);\n}\n\nexport function createSqliteDb(filenameOrDb: string | any): Database {\n // If a pre-created connection is provided\n if (typeof filenameOrDb !== 'string' && filenameOrDb) {\n const db = filenameOrDb;\n\n // better-sqlite3 shape\n if (typeof db.prepare === 'function') {\n return {\n dialect: 'sqlite',\n async query(sql: string, params: unknown[]) {\n const stmt = db.prepare(sql);\n const text = stripLeadingComments(String(sql)).trimStart().toUpperCase();\n if (text.startsWith('SELECT') || text.startsWith('WITH')) {\n return stmt.all(...params);\n }\n const info = stmt.run(...params);\n return { rowsAffected: Number(info?.changes ?? 0), lastInsertId: info?.lastInsertRowid };\n },\n conn: db,\n // Do not close externally provided connection\n close() {},\n } as Database;\n }\n\n // sqlite3 shape\n return {\n dialect: 'sqlite',\n async query(sql: string, params: unknown[]) {\n const text = stripLeadingComments(String(sql)).trimStart().toUpperCase();\n if (text.startsWith('SELECT') || text.startsWith('WITH')) {\n return new Promise<any[]>((resolve, reject) => {\n db.all(sql, params, (err: Error | null, rows: any[]) => {\n if (err) return reject(err);\n resolve(rows || []);\n });\n });\n }\n return new Promise((resolve, reject) => {\n // Use function callback to access `this` for metadata\n db.run(sql, params, function (this: any, err: Error | null) {\n if (err) return reject(err);\n resolve({ rowsAffected: Number(this?.changes ?? 0), lastInsertId: this?.lastID });\n });\n });\n },\n conn: db,\n // Do not close externally provided connection\n close() {},\n } as Database;\n }\n\n const filename = filenameOrDb as string;\n\n // Prefer better-sqlite3\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const BetterSqlite3 = require('better-sqlite3');\n const db = new BetterSqlite3(filename);\n return {\n dialect: 'sqlite',\n async query(sql: string, params: unknown[]) {\n const stmt = db.prepare(sql);\n const text = stripLeadingComments(String(sql)).trimStart().toUpperCase();\n if (text.startsWith('SELECT') || text.startsWith('WITH')) {\n return stmt.all(...params);\n }\n const info = stmt.run(...params);\n return { rowsAffected: Number(info?.changes ?? 0), lastInsertId: info?.lastInsertRowid };\n },\n conn: db,\n close() {\n db.close();\n },\n } as Database;\n } catch {}\n\n // Fallback to sqlite3\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const sqlite3 = require('sqlite3');\n const db = new sqlite3.Database(filename);\n return {\n dialect: 'sqlite',\n async query(sql: string, params: unknown[]) {\n const text = stripLeadingComments(String(sql)).trimStart().toUpperCase();\n if (text.startsWith('SELECT') || text.startsWith('WITH')) {\n return new Promise<any[]>((resolve, reject) => {\n db.all(sql, params, (err: Error | null, rows: any[]) => {\n if (err) return reject(err);\n resolve(rows || []);\n });\n });\n }\n // Non-SELECT statements\n return new Promise((resolve, reject) => {\n db.run(sql, params, function (this: any, err: Error | null) {\n if (err) return reject(err);\n resolve({ rowsAffected: Number(this?.changes ?? 0), lastInsertId: this?.lastID });\n });\n });\n },\n conn: db,\n close() {\n return new Promise<void>((resolve, reject) => {\n db.close((err: Error | null) => (err ? reject(err) : resolve()));\n });\n },\n } as Database;\n } catch {}\n\n throw new Error('No SQLite driver available. Install either better-sqlite3 or sqlite3.');\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../src/adapters/test.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAI5C,wBAAgB,YAAY,IAAI,QAAQ,CAiDvC"}
1
+ {"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../src/adapters/test.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAI5C,wBAAgB,YAAY,IAAI,QAAQ,CAoDvC"}
@@ -11,7 +11,7 @@ function createTestDb() {
11
11
  // Very small subset for tests: CREATE TABLE users
12
12
  if (s.startsWith('CREATE TABLE USERS')) {
13
13
  ensure('users');
14
- return [];
14
+ return { rowsAffected: 0 };
15
15
  }
16
16
  // INSERT INTO users (id, email, status, ...)
17
17
  if (s.startsWith('INSERT INTO USERS')) {
@@ -19,7 +19,7 @@ function createTestDb() {
19
19
  // naive: params order must match tests
20
20
  const [id, email, status, created_at] = params;
21
21
  users.push({ id, email, status, created_at });
22
- return [];
22
+ return { rowsAffected: 1, lastInsertId: id };
23
23
  }
24
24
  // SELECT id, email, status FROM users WHERE email = ? AND status = ?
25
25
  if (/FROM\s+users/i.test(sql) && /WHERE\s+email\s*=/.test(sql) && /status\s*=/.test(sql)) {
@@ -42,6 +42,9 @@ function createTestDb() {
42
42
  // Fallback: return empty
43
43
  return [];
44
44
  },
45
+ conn: null,
46
+ // Ensure close exists; no-op for in-memory test adapter
47
+ close() { },
45
48
  };
46
49
  }
47
50
  //# sourceMappingURL=test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"test.js","sourceRoot":"","sources":["../../src/adapters/test.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Database } from '../registry';\n\ntype Row = Record<string, any>;\n\nexport function createTestDb(): Database {\n const tables: Record<string, Row[]> = Object.create(null);\n const ensure = (name: string) => (tables[name] ||= []);\n\n return {\n dialect: 'sqlite',\n async query(sql: string, params: unknown[]): Promise<any[]> {\n const s = sql.trim().toUpperCase();\n\n // Very small subset for tests: CREATE TABLE users\n if (s.startsWith('CREATE TABLE USERS')) {\n ensure('users');\n return [];\n }\n\n // INSERT INTO users (id, email, status, ...)\n if (s.startsWith('INSERT INTO USERS')) {\n const users = ensure('users');\n // naive: params order must match tests\n const [id, email, status, created_at] = params as any[];\n users.push({ id, email, status, created_at });\n return [];\n }\n\n // SELECT id, email, status FROM users WHERE email = ? AND status = ?\n if (/FROM\\s+users/i.test(sql) && /WHERE\\s+email\\s*=/.test(sql) && /status\\s*=/.test(sql)) {\n const [email, status] = params as any[];\n return ensure('users').filter((r) => r.email === email && r.status === status);\n }\n\n // SELECT id, email, status FROM users WHERE id = ?\n if (/FROM\\s+users/i.test(sql) && /WHERE\\s+id\\s*=/.test(sql)) {\n const [id] = params as any[];\n return ensure('users').filter((r) => r.id === id).map((r) => ({ id: r.id, email: r.email, status: r.status }));\n }\n\n // SELECT id FROM users WHERE status = ? ORDER BY id\n if (/FROM\\s+users/i.test(sql) && /WHERE\\s+status\\s*=/.test(sql)) {\n const [status] = params as any[];\n return ensure('users')\n .filter((r) => r.status === status)\n .sort((a, b) => String(a.id).localeCompare(String(b.id)))\n .map((r) => ({ id: r.id }));\n }\n\n // Fallback: return empty\n return [];\n },\n };\n}\n"]}
1
+ {"version":3,"file":"test.js","sourceRoot":"","sources":["../../src/adapters/test.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Database } from '../registry';\n\ntype Row = Record<string, any>;\n\nexport function createTestDb(): Database {\n const tables: Record<string, Row[]> = Object.create(null);\n const ensure = (name: string) => (tables[name] ||= []);\n\n return {\n dialect: 'sqlite',\n async query(sql: string, params: unknown[]) {\n const s = sql.trim().toUpperCase();\n\n // Very small subset for tests: CREATE TABLE users\n if (s.startsWith('CREATE TABLE USERS')) {\n ensure('users');\n return { rowsAffected: 0 };\n }\n\n // INSERT INTO users (id, email, status, ...)\n if (s.startsWith('INSERT INTO USERS')) {\n const users = ensure('users');\n // naive: params order must match tests\n const [id, email, status, created_at] = params as any[];\n users.push({ id, email, status, created_at });\n return { rowsAffected: 1, lastInsertId: id };\n }\n\n // SELECT id, email, status FROM users WHERE email = ? AND status = ?\n if (/FROM\\s+users/i.test(sql) && /WHERE\\s+email\\s*=/.test(sql) && /status\\s*=/.test(sql)) {\n const [email, status] = params as any[];\n return ensure('users').filter((r) => r.email === email && r.status === status);\n }\n\n // SELECT id, email, status FROM users WHERE id = ?\n if (/FROM\\s+users/i.test(sql) && /WHERE\\s+id\\s*=/.test(sql)) {\n const [id] = params as any[];\n return ensure('users').filter((r) => r.id === id).map((r) => ({ id: r.id, email: r.email, status: r.status }));\n }\n\n // SELECT id FROM users WHERE status = ? ORDER BY id\n if (/FROM\\s+users/i.test(sql) && /WHERE\\s+status\\s*=/.test(sql)) {\n const [status] = params as any[];\n return ensure('users')\n .filter((r) => r.status === status)\n .sort((a, b) => String(a.id).localeCompare(String(b.id)))\n .map((r) => ({ id: r.id }));\n }\n\n // Fallback: return empty\n return [];\n },\n conn: null,\n // Ensure close exists; no-op for in-memory test adapter\n close() {},\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/decorators/query.ts"],"names":[],"mappings":"AAOA,KAAK,YAAY,GAAG;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;CAC3C,CAAC;AAEF,wBAAgB,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,YAAY,IAC9B,SAAS,GAAG,EAAE,MAAM,MAAM,GAAG,MAAM,EAAE,MAAM,kBAAkB,wBAsE/E"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/decorators/query.ts"],"names":[],"mappings":"AAOA,KAAK,YAAY,GAAG;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;CAC3C,CAAC;AAEF,wBAAgB,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,YAAY,IAC9B,SAAS,GAAG,EAAE,MAAM,MAAM,GAAG,MAAM,EAAE,MAAM,kBAAkB,wBA0E/E"}
@@ -53,20 +53,24 @@ function Query(arg) {
53
53
  const binder = dialect === 'postgres' ? (0, sqlBinder_1.createPostgresBinder)() : dialect === 'mysql' ? (0, sqlBinder_1.createMysqlBinder)() : (0, sqlBinder_1.createSqliteBinder)();
54
54
  const { sql: boundSql, params } = binder.bind(sql, args);
55
55
  // 5) execute query via registry DB
56
- const rows = await db.query(boundSql, params);
57
- // 6) shape result
58
- const shaped = applyShape(rows, labels.shape);
59
- // 7) validate result via JSON Schema, if provided
60
- if (labels.validateSchema && !(0, validator_1.isValidationDisabled)()) {
61
- if (Array.isArray(shaped)) {
62
- for (const row of shaped)
63
- (0, validator_1.validateOutput)(labels.validateSchema, row);
64
- }
65
- else if (shaped !== null && shaped !== undefined) {
66
- (0, validator_1.validateOutput)(labels.validateSchema, shaped);
56
+ const out = await db.query(boundSql, params);
57
+ // 6) If rows were returned (SELECT/CTE), shape + validate
58
+ if (Array.isArray(out)) {
59
+ const shaped = applyShape(out, labels.shape);
60
+ // 7) validate result via JSON Schema, if provided
61
+ if (labels.validateSchema && !(0, validator_1.isValidationDisabled)()) {
62
+ if (Array.isArray(shaped)) {
63
+ for (const row of shaped)
64
+ (0, validator_1.validateOutput)(labels.validateSchema, row);
65
+ }
66
+ else if (shaped !== null && shaped !== undefined) {
67
+ (0, validator_1.validateOutput)(labels.validateSchema, shaped);
68
+ }
67
69
  }
70
+ return shaped;
68
71
  }
69
- return shaped;
72
+ // Non-SELECT: return the write result as-is
73
+ return out;
70
74
  };
71
75
  return desc;
72
76
  };
@@ -1 +1 @@
1
- {"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/decorators/query.ts"],"names":[],"mappings":"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","sourcesContent":["import { getClassLabels, getMethodLabels, setMethodLabels } from '../core/metadata';\nimport { SqlStackError, ExactlyOneRowError, NoRowsError, RowsExistError } from '../core/errors';\nimport { isValidationDisabled, validateOutput } from '../core/validator';\nimport { SqlStackDB } from '../registry';\nimport { createSqliteBinder, createMysqlBinder, createPostgresBinder } from '../binders/sqlBinder';\nimport { coerceRootPath, guessDecoratorDir, resolveSqlFromFs } from '../core/fileResolution';\n\ntype QueryOptions = {\n sql?: string; // optional inline SQL for testing; otherwise, future: load from file\n db?: string; // optional override DB name\n dialect?: 'sqlite' | 'mysql' | 'postgres';\n};\n\nexport function Query(arg?: string | QueryOptions) {\n return function (_target: any, prop: string | symbol, desc: PropertyDescriptor) {\n const options: QueryOptions | undefined = typeof arg === 'string' ? undefined : arg;\n const rootArg: string | undefined = typeof arg === 'string' ? arg : undefined;\n // Store method-level db override if provided\n if (options?.db) {\n setMethodLabels(_target, prop, { db: options.db });\n }\n // Store inline SQL if provided\n if (options?.sql) {\n setMethodLabels(_target, prop, { source: { kind: 'inline', sql: options.sql } });\n } else {\n // FS-based resolution\n const { rootDir } = coerceRootPath(rootArg);\n const dir = rootDir ?? guessDecoratorDir();\n if (dir) {\n setMethodLabels(_target, prop, { source: { kind: 'fs', rootDir: dir } });\n }\n }\n\n const original = desc.value;\n desc.value = async function (...callArgs: any[]) {\n const labels = getMethodLabels(this, prop);\n const classLabels = getClassLabels(this);\n\n // 1) prepare args with defaults\n const rawInput = callArgs.length === 1 ? callArgs[0] : callArgs; // object or positional array\n\n // 2) choose database and dialect\n const dbName = labels.db ?? options?.db ?? classLabels?.db;\n const db = SqlStackDB.get(dbName);\n const dialect = options?.dialect ?? db.dialect ?? 'sqlite';\n\n // 3) load SQL source (inline or fs)\n const sql = labels?.source?.kind === 'inline'\n ? labels.source.sql\n : labels?.source?.kind === 'fs'\n ? resolveSqlFromFs(labels.source.rootDir, String(prop), dialect).sql\n : undefined;\n if (!sql) {\n throw new SqlStackError(`No SQL source provided for method ${String(prop)}. Use @Query() with a file/folder path or inline sql.`);\n }\n\n // 4) bind placeholders\n // Decide argument mode: positional if SQL contains :argN\n const hasPositional = /:arg[1-9][0-9]*/.test(sql);\n const args = hasPositional ? callArgs : applyDefaults(labels.defaults, rawInput);\n const binder =\n dialect === 'postgres' ? createPostgresBinder() : dialect === 'mysql' ? createMysqlBinder() : createSqliteBinder();\n const { sql: boundSql, params } = binder.bind(sql, args as any);\n\n // 5) execute query via registry DB\n const rows = await db.query(boundSql, params);\n\n // 6) shape result\n const shaped = applyShape(rows, labels.shape);\n\n // 7) validate result via JSON Schema, if provided\n if (labels.validateSchema && !isValidationDisabled()) {\n if (Array.isArray(shaped)) {\n for (const row of shaped) validateOutput(labels.validateSchema, row);\n } else if (shaped !== null && shaped !== undefined) {\n validateOutput(labels.validateSchema, shaped);\n }\n }\n\n return shaped;\n };\n\n return desc;\n };\n}\n\nfunction applyDefaults(defaults: Record<string, unknown> | undefined, input: any) {\n if (!defaults) return input;\n if (Array.isArray(input)) return input; // defaults only apply to named-object args\n return { ...defaults, ...(input || {}) };\n}\n\nfunction applyShape<T>(rows: T[], shape?: 'single' | 'only' | 'exist' | 'none'): any {\n switch (shape) {\n case 'single':\n return rows.length > 0 ? rows[0] : null;\n case 'only':\n if (rows.length !== 1) throw new ExactlyOneRowError(rows.length);\n return rows[0];\n case 'exist':\n if (rows.length < 1) throw new NoRowsError();\n return rows[0];\n case 'none':\n if (rows.length > 0) throw new RowsExistError(rows.length);\n return null;\n default:\n return rows;\n }\n}\n"]}
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/decorators/query.ts"],"names":[],"mappings":"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","sourcesContent":["import { getClassLabels, getMethodLabels, setMethodLabels } from '../core/metadata';\nimport { SqlStackError, ExactlyOneRowError, NoRowsError, RowsExistError } from '../core/errors';\nimport { isValidationDisabled, validateOutput } from '../core/validator';\nimport { SqlStackDB } from '../registry';\nimport { createSqliteBinder, createMysqlBinder, createPostgresBinder } from '../binders/sqlBinder';\nimport { coerceRootPath, guessDecoratorDir, resolveSqlFromFs } from '../core/fileResolution';\n\ntype QueryOptions = {\n sql?: string; // optional inline SQL for testing; otherwise, future: load from file\n db?: string; // optional override DB name\n dialect?: 'sqlite' | 'mysql' | 'postgres';\n};\n\nexport function Query(arg?: string | QueryOptions) {\n return function (_target: any, prop: string | symbol, desc: PropertyDescriptor) {\n const options: QueryOptions | undefined = typeof arg === 'string' ? undefined : arg;\n const rootArg: string | undefined = typeof arg === 'string' ? arg : undefined;\n // Store method-level db override if provided\n if (options?.db) {\n setMethodLabels(_target, prop, { db: options.db });\n }\n // Store inline SQL if provided\n if (options?.sql) {\n setMethodLabels(_target, prop, { source: { kind: 'inline', sql: options.sql } });\n } else {\n // FS-based resolution\n const { rootDir } = coerceRootPath(rootArg);\n const dir = rootDir ?? guessDecoratorDir();\n if (dir) {\n setMethodLabels(_target, prop, { source: { kind: 'fs', rootDir: dir } });\n }\n }\n\n const original = desc.value;\n desc.value = async function (...callArgs: any[]) {\n const labels = getMethodLabels(this, prop);\n const classLabels = getClassLabels(this);\n\n // 1) prepare args with defaults\n const rawInput = callArgs.length === 1 ? callArgs[0] : callArgs; // object or positional array\n\n // 2) choose database and dialect\n const dbName = labels.db ?? options?.db ?? classLabels?.db;\n const db = SqlStackDB.get(dbName);\n const dialect = options?.dialect ?? db.dialect ?? 'sqlite';\n\n // 3) load SQL source (inline or fs)\n const sql = labels?.source?.kind === 'inline'\n ? labels.source.sql\n : labels?.source?.kind === 'fs'\n ? resolveSqlFromFs(labels.source.rootDir, String(prop), dialect).sql\n : undefined;\n if (!sql) {\n throw new SqlStackError(`No SQL source provided for method ${String(prop)}. Use @Query() with a file/folder path or inline sql.`);\n }\n\n // 4) bind placeholders\n // Decide argument mode: positional if SQL contains :argN\n const hasPositional = /:arg[1-9][0-9]*/.test(sql);\n const args = hasPositional ? callArgs : applyDefaults(labels.defaults, rawInput);\n const binder =\n dialect === 'postgres' ? createPostgresBinder() : dialect === 'mysql' ? createMysqlBinder() : createSqliteBinder();\n const { sql: boundSql, params } = binder.bind(sql, args as any);\n\n // 5) execute query via registry DB\n const out = await db.query(boundSql, params);\n\n // 6) If rows were returned (SELECT/CTE), shape + validate\n if (Array.isArray(out)) {\n const shaped = applyShape(out, labels.shape);\n\n // 7) validate result via JSON Schema, if provided\n if (labels.validateSchema && !isValidationDisabled()) {\n if (Array.isArray(shaped)) {\n for (const row of shaped) validateOutput(labels.validateSchema, row);\n } else if (shaped !== null && shaped !== undefined) {\n validateOutput(labels.validateSchema, shaped);\n }\n }\n return shaped;\n }\n\n // Non-SELECT: return the write result as-is\n return out;\n };\n\n return desc;\n };\n}\n\nfunction applyDefaults(defaults: Record<string, unknown> | undefined, input: any) {\n if (!defaults) return input;\n if (Array.isArray(input)) return input; // defaults only apply to named-object args\n return { ...defaults, ...(input || {}) };\n}\n\nfunction applyShape<T>(rows: T[], shape?: 'single' | 'only' | 'exist' | 'none'): any {\n switch (shape) {\n case 'single':\n return rows.length > 0 ? rows[0] : null;\n case 'only':\n if (rows.length !== 1) throw new ExactlyOneRowError(rows.length);\n return rows[0];\n case 'exist':\n if (rows.length < 1) throw new NoRowsError();\n return rows[0];\n case 'none':\n if (rows.length > 0) throw new RowsExistError(rows.length);\n return null;\n default:\n return rows;\n }\n}\n"]}
@@ -1,8 +1,17 @@
1
1
  export type Dialect = 'sqlite' | 'mysql' | 'postgres';
2
+ export interface WriteResult {
3
+ rowsAffected: number;
4
+ lastInsertId?: unknown;
5
+ }
6
+ export interface PostgresWriteResult extends WriteResult {
7
+ returning?: any[];
8
+ }
9
+ export type QueryResult = any[] | WriteResult | PostgresWriteResult;
2
10
  export interface Database {
3
11
  dialect: Dialect;
4
- query(sql: string, params: unknown[]): Promise<any[]>;
5
- close?: () => Promise<void> | void;
12
+ query(sql: string, params: unknown[]): Promise<QueryResult>;
13
+ conn: unknown;
14
+ close: () => Promise<void> | void;
6
15
  }
7
16
  declare class Registry {
8
17
  private map;
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;AAEtD,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACtD,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACpC;AAED,cAAM,QAAQ;IACZ,OAAO,CAAC,GAAG,CAA+B;IAC1C,OAAO,CAAC,WAAW,CAAC,CAAS;IAE7B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ;IAKnC,UAAU,CAAC,IAAI,EAAE,MAAM;IAMvB,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ;CAO7B;AAED,eAAO,MAAM,UAAU,UAAiB,CAAC"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;AAGtD,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAGD,MAAM,WAAW,mBAAoB,SAAQ,WAAW;IAEtD,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC;CACnB;AAED,MAAM,MAAM,WAAW,GAAG,GAAG,EAAE,GAAG,WAAW,GAAG,mBAAmB,CAAC;AAEpE,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAE5D,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACnC;AAED,cAAM,QAAQ;IACZ,OAAO,CAAC,GAAG,CAA+B;IAC1C,OAAO,CAAC,WAAW,CAAC,CAAS;IAE7B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ;IAKnC,UAAU,CAAC,IAAI,EAAE,MAAM;IAMvB,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ;CAO7B;AAED,eAAO,MAAM,UAAU,UAAiB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":";;;AAQA,MAAM,QAAQ;IAAd;QACU,QAAG,GAAG,IAAI,GAAG,EAAoB,CAAC;IAqB5C,CAAC;IAlBC,QAAQ,CAAC,IAAY,EAAE,EAAY;QACjC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,UAAU,CAAC,IAAY;QACrB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,IAAI,EAAE,CAAC,CAAC;QACjF,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,GAAG,CAAC,IAAa;QACf,MAAM,GAAG,GAAG,IAAI,IAAI,IAAI,CAAC,WAAW,CAAC;QACrC,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;QACrD,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,GAAG,EAAE,CAAC,CAAC;QAChE,OAAO,EAAE,CAAC;IACZ,CAAC;CACF;AAEY,QAAA,UAAU,GAAG,IAAI,QAAQ,EAAE,CAAC","sourcesContent":["export type Dialect = 'sqlite' | 'mysql' | 'postgres';\n\nexport interface Database {\n dialect: Dialect;\n query(sql: string, params: unknown[]): Promise<any[]>;\n close?: () => Promise<void> | void;\n}\n\nclass Registry {\n private map = new Map<string, Database>();\n private defaultName?: string;\n\n register(name: string, db: Database) {\n this.map.set(name, db);\n return this;\n }\n\n setDefault(name: string) {\n if (!this.map.has(name)) throw new Error(`No database registered under ${name}`);\n this.defaultName = name;\n return this;\n }\n\n get(name?: string): Database {\n const key = name ?? this.defaultName;\n if (!key) throw new Error('No default database set');\n const db = this.map.get(key);\n if (!db) throw new Error(`No database registered under ${key}`);\n return db;\n }\n}\n\nexport const SqlStackDB = new Registry();\n"]}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":";;;AAwBA,MAAM,QAAQ;IAAd;QACU,QAAG,GAAG,IAAI,GAAG,EAAoB,CAAC;IAqB5C,CAAC;IAlBC,QAAQ,CAAC,IAAY,EAAE,EAAY;QACjC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,UAAU,CAAC,IAAY;QACrB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,IAAI,EAAE,CAAC,CAAC;QACjF,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,GAAG,CAAC,IAAa;QACf,MAAM,GAAG,GAAG,IAAI,IAAI,IAAI,CAAC,WAAW,CAAC;QACrC,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;QACrD,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,GAAG,EAAE,CAAC,CAAC;QAChE,OAAO,EAAE,CAAC;IACZ,CAAC;CACF;AAEY,QAAA,UAAU,GAAG,IAAI,QAAQ,EAAE,CAAC","sourcesContent":["export type Dialect = 'sqlite' | 'mysql' | 'postgres';\n\n// Result for non-SELECT statements across adapters\nexport interface WriteResult {\n rowsAffected: number;\n lastInsertId?: unknown;\n}\n\n// Postgres-specific extension when using RETURNING on write statements\nexport interface PostgresWriteResult extends WriteResult {\n // Only present when the SQL includes RETURNING and rows are returned\n returning?: any[];\n}\n\nexport type QueryResult = any[] | WriteResult | PostgresWriteResult;\n\nexport interface Database {\n dialect: Dialect;\n query(sql: string, params: unknown[]): Promise<QueryResult>;\n // Underlying driver connection or pool (driver-specific)\n conn: unknown;\n close: () => Promise<void> | void;\n}\n\nclass Registry {\n private map = new Map<string, Database>();\n private defaultName?: string;\n\n register(name: string, db: Database) {\n this.map.set(name, db);\n return this;\n }\n\n setDefault(name: string) {\n if (!this.map.has(name)) throw new Error(`No database registered under ${name}`);\n this.defaultName = name;\n return this;\n }\n\n get(name?: string): Database {\n const key = name ?? this.defaultName;\n if (!key) throw new Error('No default database set');\n const db = this.map.get(key);\n if (!db) throw new Error(`No database registered under ${key}`);\n return db;\n }\n}\n\nexport const SqlStackDB = new Registry();\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sqlstack",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "",
5
5
  "license": "ISC",
6
6
  "author": "",
package/readme.md CHANGED
@@ -48,6 +48,26 @@ SqlStackDB
48
48
  .setDefault("primary");
49
49
  ```
50
50
 
51
+ Adapters can receive either a connection string or an existing driver instance (pool/connection):
52
+
53
+ ```ts
54
+ import { Pool as PgPool } from 'pg';
55
+ import mysql from 'mysql2/promise';
56
+ import BetterSqlite3 from 'better-sqlite3';
57
+
58
+ // Postgres: pass an existing pool
59
+ const pgPool = new PgPool({ connectionString: process.env.PG_URL, max: 4 });
60
+ SqlStackDB.register('pg-existing', createPgDb(pgPool));
61
+
62
+ // MySQL: pass an existing pool
63
+ const mysqlPool = mysql.createPool({ uri: process.env.MYSQL_URL!, connectionLimit: 4 });
64
+ SqlStackDB.register('mysql-existing', createMysqlDb(mysqlPool));
65
+
66
+ // SQLite: pass an existing better-sqlite3 connection
67
+ const sqlite = new BetterSqlite3(':memory:');
68
+ SqlStackDB.register('sqlite-existing', createSqliteDb(sqlite));
69
+ ```
70
+
51
71
  Then, choose a database per repository (class-level default):
52
72
 
53
73
  ```ts
@@ -374,3 +394,53 @@ The binder placeholder style is inferred from the selected database (method →
374
394
  ## License
375
395
 
376
396
  ISC
397
+
398
+ ## Result Shape
399
+
400
+ - SELECT and WITH CTE queries return an array of rows and can be shaped via decorators.
401
+ - INSERT/UPDATE/DELETE return a common object: `{ rowsAffected: number, lastInsertId?: unknown }`.
402
+ - Postgres-only: if a non-SELECT query includes `RETURNING`, the result also includes an optional `returning` array: `{ rowsAffected, returning: Row[] }`.
403
+
404
+ ### Examples
405
+
406
+ ```ts
407
+ import type { WriteResult } from 'sqlstack';
408
+ import { SqlStackDB } from 'sqlstack/registry';
409
+
410
+ const db = SqlStackDB.get();
411
+
412
+ // A write returns WriteResult
413
+ const write: WriteResult = await db.query(
414
+ 'UPDATE users SET status = ? WHERE id = ?',
415
+ ['inactive', 'u1']
416
+ );
417
+ console.log(write.rowsAffected);
418
+
419
+ // Postgres with RETURNING returns WriteResult & { returning?: any[] }
420
+ // (only has `returning` when RETURNING is present)
421
+ const pgWrite = await db.query(
422
+ 'INSERT INTO users(id,email) VALUES($1,$2) RETURNING id',
423
+ ['u9', 'nine@example.com']
424
+ );
425
+ if (!Array.isArray(pgWrite) && pgWrite.returning) {
426
+ console.log(pgWrite.returning[0].id);
427
+ }
428
+ ```
429
+
430
+ ## Underlying Connections
431
+
432
+ Every `Database` exposes its driver via `conn` so you can drop down to the raw client when needed:
433
+
434
+ ```ts
435
+ import { SqlStackDB } from 'sqlstack/registry';
436
+
437
+ const db = SqlStackDB.get();
438
+ // Driver shape depends on adapter:
439
+ // - Postgres: pg.Pool
440
+ // - MySQL: mysql2/promise Pool
441
+ // - SQLite: better-sqlite3 Database or sqlite3.Database
442
+ const raw = db.conn;
443
+
444
+ // close() only disposes connections created by the adapter itself.
445
+ // If you supplied an existing connection/pool, close() is a no-op.
446
+ ```