@devbro/neko-sql 0.1.32 → 0.1.35

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.
Files changed (48) hide show
  1. package/README.md +23 -3
  2. package/dist/{Blueprint-D3WHeqRS.d.mts → Blueprint-DjP_Sfrr.d.mts} +10 -4
  3. package/dist/Blueprint.d.mts +1 -1
  4. package/dist/Connection.d.mts +1 -1
  5. package/dist/Expression.d.mts +1 -1
  6. package/dist/Migration.d.mts +1 -1
  7. package/dist/Query.d.mts +1 -1
  8. package/dist/Query.mjs +3 -1
  9. package/dist/Query.mjs.map +1 -1
  10. package/dist/QueryGrammar.d.mts +1 -1
  11. package/dist/QueryGrammar.mjs +28 -8
  12. package/dist/QueryGrammar.mjs.map +1 -1
  13. package/dist/Schema.d.mts +1 -1
  14. package/dist/SchemaGrammar.d.mts +1 -1
  15. package/dist/databases/index.d.mts +5 -1
  16. package/dist/databases/index.mjs +1 -0
  17. package/dist/databases/index.mjs.map +1 -1
  18. package/dist/databases/mysql/MysqlConnection.d.mts +41 -0
  19. package/dist/databases/mysql/MysqlConnection.mjs +208 -0
  20. package/dist/databases/mysql/MysqlConnection.mjs.map +1 -0
  21. package/dist/databases/mysql/MysqlQueryGrammar.d.mts +13 -0
  22. package/dist/databases/mysql/MysqlQueryGrammar.mjs +22 -0
  23. package/dist/databases/mysql/MysqlQueryGrammar.mjs.map +1 -0
  24. package/dist/databases/mysql/MysqlSchemaGrammar.d.mts +8 -0
  25. package/dist/databases/mysql/MysqlSchemaGrammar.mjs +58 -0
  26. package/dist/databases/mysql/MysqlSchemaGrammar.mjs.map +1 -0
  27. package/dist/databases/mysql/index.d.mts +6 -0
  28. package/dist/databases/mysql/index.mjs +4 -0
  29. package/dist/databases/mysql/index.mjs.map +1 -0
  30. package/dist/databases/postgresql/PostgresqlConnection.d.mts +1 -1
  31. package/dist/databases/postgresql/PostgresqlConnection.mjs +17 -3
  32. package/dist/databases/postgresql/PostgresqlConnection.mjs.map +1 -1
  33. package/dist/databases/postgresql/PostgresqlQueryGrammar.d.mts +3 -3
  34. package/dist/databases/postgresql/PostgresqlQueryGrammar.mjs.map +1 -1
  35. package/dist/databases/postgresql/PostgresqlSchemaGrammar.d.mts +1 -1
  36. package/dist/databases/postgresql/index.d.mts +1 -1
  37. package/dist/databases/sqlite/SqliteConnection.d.mts +1 -1
  38. package/dist/databases/sqlite/SqliteQueryGrammar.d.mts +3 -3
  39. package/dist/databases/sqlite/SqliteQueryGrammar.mjs.map +1 -1
  40. package/dist/databases/sqlite/SqliteSchemaGrammar.d.mts +2 -1
  41. package/dist/databases/sqlite/SqliteSchemaGrammar.mjs +46 -0
  42. package/dist/databases/sqlite/SqliteSchemaGrammar.mjs.map +1 -1
  43. package/dist/databases/sqlite/index.d.mts +1 -1
  44. package/dist/index.d.mts +5 -1
  45. package/dist/index.js +371 -12
  46. package/dist/index.js.map +1 -1
  47. package/dist/types.d.mts +1 -1
  48. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -37,6 +37,9 @@ __export(index_exports, {
37
37
  ForeignKeyConstraint: () => ForeignKeyConstraint,
38
38
  IndexConstraint: () => IndexConstraint,
39
39
  Migration: () => Migration,
40
+ MysqlConnection: () => MysqlConnection,
41
+ MysqlQueryGrammar: () => MysqlQueryGrammar,
42
+ MysqlSchemaGrammar: () => MysqlSchemaGrammar,
40
43
  PostgresqlConnection: () => PostgresqlConnection,
41
44
  PostgresqlQueryGrammar: () => PostgresqlQueryGrammar,
42
45
  PostgresqlSchemaGrammar: () => PostgresqlSchemaGrammar,
@@ -201,7 +204,9 @@ var Query = class _Query {
201
204
  }
202
205
  async insertGetId(data, options = { primaryKey: ["id"] }) {
203
206
  const csql = this.grammar.compileInsertGetId(this, data, options);
204
- return await this.connection?.runQuery(csql);
207
+ let rc = await this.connection?.runQuery(csql);
208
+ rc = this.grammar.postProcessGetInsertId(rc);
209
+ return rc;
205
210
  }
206
211
  async update(data) {
207
212
  const csql = this.grammar.compileUpdate(this, data);
@@ -481,22 +486,34 @@ var QueryGrammar = class {
481
486
  }
482
487
  compileInsert(query, data) {
483
488
  let parts = ["insert", "into", query.parts.table, "("];
484
- const columns = [];
485
489
  const bindings = [];
486
- const values = [];
487
- for (const [k, v] of Object.entries(data)) {
490
+ const dataArray = Array.isArray(data) ? data : [data];
491
+ if (dataArray.length === 0) {
492
+ throw new Error("Cannot insert empty array");
493
+ }
494
+ const firstEntry = dataArray[0];
495
+ const columns = Object.keys(firstEntry);
496
+ if (columns.length === 0) {
497
+ throw new Error("Cannot insert object with no properties");
498
+ }
499
+ for (const k of columns) {
488
500
  parts.push(k);
489
501
  parts.push(",");
490
502
  }
491
503
  parts.pop();
492
- parts = parts.concat([")", "values", "("]);
493
- for (const [k, v] of Object.entries(data)) {
494
- parts.push("?");
495
- bindings.push(v);
504
+ parts = parts.concat([")", "values"]);
505
+ for (let i = 0; i < dataArray.length; i++) {
506
+ parts.push("(");
507
+ for (const k of columns) {
508
+ parts.push("?");
509
+ bindings.push(dataArray[i][k]);
510
+ parts.push(",");
511
+ }
512
+ parts.pop();
513
+ parts.push(")");
496
514
  parts.push(",");
497
515
  }
498
516
  parts.pop();
499
- parts.push(")");
500
517
  return { sql: parts.join(" "), parts, bindings };
501
518
  }
502
519
  compileUpdate(query, data) {
@@ -587,6 +604,14 @@ var QueryGrammar = class {
587
604
  bindings: w.bindings
588
605
  };
589
606
  }
607
+ /**
608
+ * post process result from database
609
+ * @param result result from database
610
+ * @returns post processed result
611
+ */
612
+ postProcessGetInsertId(result) {
613
+ return result;
614
+ }
590
615
  };
591
616
 
592
617
  // src/databases/postgresql/PostgresqlQueryGrammar.mts
@@ -1316,7 +1341,9 @@ var PostgresqlConnection = class _PostgresqlConnection extends Connection {
1316
1341
  }
1317
1342
  async createDatabase(name) {
1318
1343
  if (this.isConnected()) {
1319
- throw new Error("Cannot create database while connected.");
1344
+ const safeName2 = this.validateAndEscapeIdentifier(name);
1345
+ await this.runQuery(`CREATE DATABASE ${safeName2}`);
1346
+ return;
1320
1347
  }
1321
1348
  const conn = new import_pg.Client({
1322
1349
  ..._PostgresqlConnection.pool.options,
@@ -1329,7 +1356,9 @@ var PostgresqlConnection = class _PostgresqlConnection extends Connection {
1329
1356
  }
1330
1357
  async dropDatabase(name) {
1331
1358
  if (this.isConnected()) {
1332
- throw new Error("Cannot drop database while connected.");
1359
+ const safeName2 = this.validateAndEscapeIdentifier(name);
1360
+ await this.runQuery(`DROP DATABASE ${safeName2}`);
1361
+ return;
1333
1362
  }
1334
1363
  const conn = new import_pg.Client({
1335
1364
  ..._PostgresqlConnection.pool.options,
@@ -1352,7 +1381,17 @@ var PostgresqlConnection = class _PostgresqlConnection extends Connection {
1352
1381
  }
1353
1382
  async existsDatabase(name) {
1354
1383
  if (!this.isConnected()) {
1355
- await this.connect();
1384
+ const conn = new import_pg.Client({
1385
+ ..._PostgresqlConnection.pool.options,
1386
+ database: "postgres"
1387
+ });
1388
+ await conn.connect();
1389
+ const safeName = this.validateAndEscapeIdentifier(name);
1390
+ const result2 = await conn.query("SELECT 1 FROM pg_database WHERE datname = $1", [
1391
+ safeName
1392
+ ]);
1393
+ await conn.end();
1394
+ return result2.rows.length > 0;
1356
1395
  }
1357
1396
  const result = await this.connection.query("SELECT 1 FROM pg_database WHERE datname = $1", [
1358
1397
  name
@@ -1404,6 +1443,52 @@ var SqliteSchemaGrammar = class extends SchemaGrammar {
1404
1443
  static {
1405
1444
  __name(this, "SqliteSchemaGrammar");
1406
1445
  }
1446
+ compileColumn(column) {
1447
+ const rc = [`${column.columnName}`];
1448
+ if (column.properties.type === "string") {
1449
+ rc.push("varchar(" + column.properties.length + ")");
1450
+ } else if (column.properties.type === "char") {
1451
+ rc.push("char");
1452
+ } else if (column.properties.type === "boolean") {
1453
+ rc.push("boolean");
1454
+ } else if (column.properties.type === "integer") {
1455
+ rc.push("integer");
1456
+ } else if (column.properties.type === "text") {
1457
+ rc.push("text");
1458
+ } else if (column.properties.type === "timestamp") {
1459
+ rc.push("timestamp");
1460
+ } else if (column.properties.type === "timestampz") {
1461
+ rc.push("timestamp with time zone");
1462
+ } else if (column.properties.type === "serial") {
1463
+ rc.push("INTEGER");
1464
+ } else if (column.properties.type === "float") {
1465
+ rc.push("float");
1466
+ } else if (column.properties.type === "double") {
1467
+ rc.push("double precision");
1468
+ } else if (column.properties.type === "date") {
1469
+ rc.push("date");
1470
+ } else if (column.properties.type === "json") {
1471
+ rc.push("json");
1472
+ } else if (column.properties.type === "jsonb") {
1473
+ rc.push("jsonb");
1474
+ } else if (column.properties.type === "raw") {
1475
+ return column.columnName;
1476
+ } else {
1477
+ throw new Error("Unknown column type: " + column.properties.type);
1478
+ }
1479
+ if (column.properties.nullable) {
1480
+ rc.push("null");
1481
+ } else {
1482
+ rc.push("not null");
1483
+ }
1484
+ if (column.properties.unique) {
1485
+ rc.push("unique");
1486
+ }
1487
+ if (column.properties.default !== null) {
1488
+ rc.push("default " + this.escape(column.properties.default));
1489
+ }
1490
+ return rc.join(" ");
1491
+ }
1407
1492
  };
1408
1493
 
1409
1494
  // src/databases/sqlite/SqliteConnection.mts
@@ -1638,6 +1723,277 @@ var SqliteConnection = class _SqliteConnection extends Connection {
1638
1723
  }
1639
1724
  };
1640
1725
 
1726
+ // src/databases/mysql/MysqlConnection.mts
1727
+ var import_promise = __toESM(require("mysql2/promise"), 1);
1728
+
1729
+ // src/databases/mysql/MysqlQueryGrammar.mts
1730
+ var MysqlQueryGrammar = class extends QueryGrammar {
1731
+ static {
1732
+ __name(this, "MysqlQueryGrammar");
1733
+ }
1734
+ compileInsertGetId(query, data, options = { primaryKey: ["id"] }) {
1735
+ return super.compileInsert(query, data);
1736
+ }
1737
+ postProcessGetInsertId(result) {
1738
+ let rc = [];
1739
+ for (let i = 0; i < result.affectedRows; i++) {
1740
+ rc.push({ id: result.insertId + i });
1741
+ }
1742
+ return rc;
1743
+ }
1744
+ };
1745
+
1746
+ // src/databases/mysql/MysqlSchemaGrammar.mts
1747
+ var MysqlSchemaGrammar = class extends SchemaGrammar {
1748
+ static {
1749
+ __name(this, "MysqlSchemaGrammar");
1750
+ }
1751
+ compileColumn(column) {
1752
+ const rc = [`${column.columnName}`];
1753
+ if (column.properties.type === "string") {
1754
+ rc.push("varchar(" + column.properties.length + ")");
1755
+ } else if (column.properties.type === "char") {
1756
+ rc.push("char");
1757
+ } else if (column.properties.type === "boolean") {
1758
+ rc.push("boolean");
1759
+ } else if (column.properties.type === "integer") {
1760
+ rc.push("integer");
1761
+ } else if (column.properties.type === "text") {
1762
+ rc.push("text");
1763
+ } else if (column.properties.type === "timestamp") {
1764
+ rc.push("TIMESTAMP");
1765
+ } else if (column.properties.type === "timestampz") {
1766
+ rc.push("TIMESTAMP");
1767
+ } else if (column.properties.type === "serial") {
1768
+ rc.push("INT AUTO_INCREMENT");
1769
+ } else if (column.properties.type === "float") {
1770
+ rc.push("float");
1771
+ } else if (column.properties.type === "double") {
1772
+ rc.push("double precision");
1773
+ } else if (column.properties.type === "date") {
1774
+ rc.push("date");
1775
+ } else if (column.properties.type === "json") {
1776
+ rc.push("json");
1777
+ } else if (column.properties.type === "jsonb") {
1778
+ rc.push("jsonb");
1779
+ } else if (column.properties.type === "raw") {
1780
+ return column.columnName;
1781
+ } else {
1782
+ throw new Error("Unknown column type: " + column.properties.type);
1783
+ }
1784
+ if (column.properties.nullable) {
1785
+ rc.push("null");
1786
+ } else {
1787
+ rc.push("NOT NULL");
1788
+ }
1789
+ if (column.properties.unique) {
1790
+ rc.push("unique");
1791
+ }
1792
+ if (column.properties.default !== null) {
1793
+ rc.push("default " + this.escape(column.properties.default));
1794
+ }
1795
+ return rc.join(" ");
1796
+ }
1797
+ };
1798
+
1799
+ // src/databases/mysql/MysqlConnection.mts
1800
+ var import_neko_helper6 = require("@devbro/neko-helper");
1801
+ var MysqlConnection = class _MysqlConnection extends Connection {
1802
+ static {
1803
+ __name(this, "MysqlConnection");
1804
+ }
1805
+ eventManager = new import_neko_helper6.EventManager();
1806
+ on(event, listener) {
1807
+ this.eventManager.on(event, listener);
1808
+ return this;
1809
+ }
1810
+ off(event, listener) {
1811
+ this.eventManager.off(event, listener);
1812
+ return this;
1813
+ }
1814
+ emit(event, ...args) {
1815
+ return this.eventManager.emit(event, ...args);
1816
+ }
1817
+ connection;
1818
+ static pool;
1819
+ static poolConfig;
1820
+ static defaults = {
1821
+ port: 3306,
1822
+ connectionLimit: 20,
1823
+ waitForConnections: true,
1824
+ queueLimit: 0,
1825
+ enableKeepAlive: true,
1826
+ keepAliveInitialDelay: 0
1827
+ };
1828
+ constructor(params) {
1829
+ super();
1830
+ if (!_MysqlConnection.pool) {
1831
+ _MysqlConnection.poolConfig = { ..._MysqlConnection.defaults, ...params };
1832
+ _MysqlConnection.pool = import_promise.default.createPool(_MysqlConnection.poolConfig);
1833
+ }
1834
+ }
1835
+ async connect() {
1836
+ this.eventManager.emit("connect").catch(() => {
1837
+ });
1838
+ this.connection = await _MysqlConnection.pool.getConnection();
1839
+ return true;
1840
+ }
1841
+ async runQuery(sql) {
1842
+ if (typeof sql === "string") {
1843
+ sql = { sql, bindings: [], parts: [sql] };
1844
+ }
1845
+ this.eventManager.emit("query", { sql: sql.sql, bindings: sql.bindings }).catch(() => {
1846
+ });
1847
+ if (!this.isConnected()) {
1848
+ await this.connect();
1849
+ }
1850
+ const result = await this.connection.query(sql.sql, sql.bindings);
1851
+ return result[0];
1852
+ }
1853
+ async runCursor(sql) {
1854
+ return this.runQuery(sql);
1855
+ }
1856
+ async disconnect() {
1857
+ if (this.connection === void 0) {
1858
+ return true;
1859
+ }
1860
+ this.connection.release();
1861
+ this.connection = void 0;
1862
+ this.eventManager.emit("disconnect").catch(() => {
1863
+ });
1864
+ return true;
1865
+ }
1866
+ getQuery() {
1867
+ return new Query(this, new MysqlQueryGrammar());
1868
+ }
1869
+ getSchema() {
1870
+ return new Schema(this, new MysqlSchemaGrammar());
1871
+ }
1872
+ getQueryGrammar() {
1873
+ return new MysqlQueryGrammar();
1874
+ }
1875
+ getSchemaGrammar() {
1876
+ return new MysqlSchemaGrammar();
1877
+ }
1878
+ async beginTransaction() {
1879
+ await this.runQuery({
1880
+ sql: "BEGIN",
1881
+ bindings: [],
1882
+ parts: ["BEGIN"]
1883
+ });
1884
+ }
1885
+ async commit() {
1886
+ await this.runQuery({ sql: "COMMIT", bindings: [], parts: ["COMMIT"] });
1887
+ }
1888
+ async rollback() {
1889
+ await this.runQuery({ sql: "ROLLBACK", bindings: [], parts: ["ROLLBACK"] });
1890
+ }
1891
+ static destroy() {
1892
+ return _MysqlConnection.pool.end();
1893
+ }
1894
+ isConnected() {
1895
+ return this.connection !== void 0;
1896
+ }
1897
+ /**
1898
+ * Validates and escapes a MySQL identifier (database name, table name, etc.)
1899
+ * Uses a whitelist approach to ensure only safe characters are allowed
1900
+ */
1901
+ validateAndEscapeIdentifier(name) {
1902
+ const validIdentifierPattern = /^[a-zA-Z0-9_$]+$/;
1903
+ if (!validIdentifierPattern.test(name)) {
1904
+ throw new Error(
1905
+ `Invalid identifier: "${name}". Identifiers must contain only letters, digits, underscores, and dollar signs.`
1906
+ );
1907
+ }
1908
+ const reservedKeywords = /* @__PURE__ */ new Set([
1909
+ "database",
1910
+ "table",
1911
+ "user",
1912
+ "order",
1913
+ "group",
1914
+ "select",
1915
+ "insert",
1916
+ "update",
1917
+ "delete",
1918
+ "from",
1919
+ "where",
1920
+ "index",
1921
+ "key"
1922
+ ]);
1923
+ if (reservedKeywords.has(name.toLowerCase())) {
1924
+ const escapedName = name.replace(/`/g, "``");
1925
+ return `\`${escapedName}\``;
1926
+ }
1927
+ return name;
1928
+ }
1929
+ async createDatabase(name) {
1930
+ if (!this.isConnected()) {
1931
+ const tempConn2 = await import_promise.default.createConnection({
1932
+ host: _MysqlConnection.poolConfig.host,
1933
+ user: _MysqlConnection.poolConfig.user,
1934
+ password: _MysqlConnection.poolConfig.password,
1935
+ port: _MysqlConnection.poolConfig.port
1936
+ });
1937
+ const safeName2 = this.validateAndEscapeIdentifier(name);
1938
+ console.log(safeName2);
1939
+ let [rows] = await tempConn2.query(`CREATE DATABASE ${safeName2}`);
1940
+ await tempConn2.end();
1941
+ return;
1942
+ }
1943
+ const tempConn = await import_promise.default.createConnection({
1944
+ host: _MysqlConnection.poolConfig.host,
1945
+ user: _MysqlConnection.poolConfig.user,
1946
+ password: _MysqlConnection.poolConfig.password,
1947
+ port: _MysqlConnection.poolConfig.port
1948
+ });
1949
+ const safeName = this.validateAndEscapeIdentifier(name);
1950
+ await tempConn.query(`CREATE DATABASE ${safeName}`);
1951
+ await tempConn.end();
1952
+ }
1953
+ async dropDatabase(name) {
1954
+ if (this.isConnected()) {
1955
+ throw new Error("Cannot drop database while connected.");
1956
+ }
1957
+ const tempConn = await import_promise.default.createConnection({
1958
+ host: _MysqlConnection.poolConfig.host,
1959
+ user: _MysqlConnection.poolConfig.user,
1960
+ password: _MysqlConnection.poolConfig.password,
1961
+ port: _MysqlConnection.poolConfig.port
1962
+ });
1963
+ const safeName = this.validateAndEscapeIdentifier(name);
1964
+ await tempConn.query(`DROP DATABASE ${safeName}`);
1965
+ await tempConn.end();
1966
+ }
1967
+ async listDatabases() {
1968
+ if (!this.isConnected()) {
1969
+ await this.connect();
1970
+ }
1971
+ const [rows] = await this.connection.query("SHOW DATABASES");
1972
+ return rows.map((row) => row.Database);
1973
+ }
1974
+ async existsDatabase(name) {
1975
+ if (!this.isConnected()) {
1976
+ const tempConn = await import_promise.default.createConnection({
1977
+ host: _MysqlConnection.poolConfig.host,
1978
+ user: _MysqlConnection.poolConfig.user,
1979
+ password: _MysqlConnection.poolConfig.password,
1980
+ port: _MysqlConnection.poolConfig.port
1981
+ });
1982
+ let [rows2] = await tempConn.query(
1983
+ "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = ?",
1984
+ [name]
1985
+ );
1986
+ await tempConn.end();
1987
+ return rows2.length > 0;
1988
+ }
1989
+ const [rows] = await this.connection.query(
1990
+ "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = ?",
1991
+ [name]
1992
+ );
1993
+ return rows.length > 0;
1994
+ }
1995
+ };
1996
+
1641
1997
  // src/Migration.mts
1642
1998
  var Migration = class {
1643
1999
  static {
@@ -1652,6 +2008,9 @@ var Migration = class {
1652
2008
  ForeignKeyConstraint,
1653
2009
  IndexConstraint,
1654
2010
  Migration,
2011
+ MysqlConnection,
2012
+ MysqlQueryGrammar,
2013
+ MysqlSchemaGrammar,
1655
2014
  PostgresqlConnection,
1656
2015
  PostgresqlQueryGrammar,
1657
2016
  PostgresqlSchemaGrammar,