sql-love 1.1.2 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,13 +1,13 @@
1
- <img alt="SQL Love Logo" src="https://github.com/kensnyder/sql-love/raw/main/assets/sql-love-logo.png?v=1.1.2" width="250" height="208" />
1
+ <img alt="SQL Love Logo" src="https://github.com/kensnyder/sql-love/raw/main/assets/sql-love-logo.png?v=1.1.4" width="250" height="208" />
2
2
 
3
3
  # sql-love
4
4
 
5
- [![Language](https://badgen.net/static/language/TS?v=1.1.2)](https://github.com/search?q=repo:kensnyder/sql-love++language:TypeScript&type=code)
6
- [![Code Coverage](https://codecov.io/gh/kensnyder/sql-love/branch/main/graph/badge.svg?v=1.1.2)](https://codecov.io/gh/kensnyder/sql-love)
5
+ [![Language](https://badgen.net/static/language/TS?v=1.1.4)](https://github.com/search?q=repo:kensnyder/sql-love++language:TypeScript&type=code)
6
+ [![Code Coverage](https://codecov.io/gh/kensnyder/sql-love/branch/main/graph/badge.svg?v=1.1.4)](https://codecov.io/gh/kensnyder/sql-love)
7
7
  ![Dependencies](https://badgen.net/static/dependencies/0/green)
8
8
  ![Tree shakeable](https://badgen.net/static/tree%20shakeable/Yes/green)
9
9
  ![Gzip Size](https://badgen.net/static/gzip%20size/9kb/blue)
10
- [![ISC License](https://badgen.net/static/license/ISC/green?v=1.1.2)](https://opensource.org/licenses/ISC)
10
+ [![ISC License](https://badgen.net/static/license/ISC/green?v=1.1.4)](https://opensource.org/licenses/ISC)
11
11
 
12
12
  Classes for parsing and building SQL select queries in Node/Bun/Deno.
13
13
 
@@ -189,7 +189,7 @@ Use the following methods to build queries. And see
189
189
  - `query.having(columnValueRecord)` - Add multiple conditions
190
190
  - `query.orHaving(expressions)` - Multiple `having()`s joined by OR
191
191
  - `query.orderBy(column, direction)` - Add ORDER BY clause
192
- - `query.sortField(column, mapNames, modifier)` - Add ORDER BY clause with mapNames
192
+ - `query.sortField(field, mapNames, modifier)` - Add ORDER BY clause for an allowed field in mapNames
193
193
  - `query.limit(num)` - Limit by the given number
194
194
  - `query.offset(num)` - Specify an offset
195
195
  - `query.page(num)` - Automatically calculate offset based on limit and page
@@ -316,8 +316,11 @@ query.orderBy('modified_at DESC NULLS LAST'); // ORDER BY modified_at DESC NULLS
316
316
  query.orderBy('modified_at USING >'); // ORDER BY modified_at USING >
317
317
 
318
318
  // Sorting with support for hyphens and mapping
319
- query.sortField('-modified_at'); // ORDER BY modified_at DESC
319
+ // Only fields present in mapNames are allowed, so field names may safely
320
+ // come from user input (e.g. a "?sort=-created" query string param).
321
+ // Unknown fields emit a console.warn and add no ORDER BY clause.
320
322
  query.sortField('-created', { created: 'created_at' }); // ORDER BY created_at DESC
323
+ query.sortField('-foo', { created: 'created_at' }); // console.warn; no ORDER BY added
321
324
  query.sortField(
322
325
  '-created_at',
323
326
  {
package/dist/index.cjs CHANGED
@@ -859,7 +859,7 @@ UNION
859
859
  if (numArgs === 1) {
860
860
  collection.push(column);
861
861
  return this;
862
- } else if (numArgs === 2 && Array.isArray(operator) && operator.length > 0 && substrCount(column, "?") === operator.length) {
862
+ } else if (numArgs === 2 && Array.isArray(operator) && substrCount(column, "?") === operator.length) {
863
863
  const values = operator;
864
864
  const sql = column.replace(/\?/g, () => {
865
865
  this._bindings.push(values.shift());
@@ -1035,13 +1035,16 @@ UNION
1035
1035
  return this;
1036
1036
  }
1037
1037
  /**
1038
- * Sort by the given column, with a map of columns to translate
1039
- * @param fieldName The file name such as 'created_at' or '-created_at' for descending
1040
- * @param [mapNames] Column names to translate from field to column expression
1038
+ * Sort by the given field, with a map of allowed fields to column expressions.
1039
+ * Only field names present in mapNames are allowed; anything else emits a
1040
+ * console.warn and adds no ORDER BY clause. This prevents SQL injection when
1041
+ * fieldName comes from user input.
1042
+ * @param fieldName The field name such as 'created' or '-created' for descending
1043
+ * @param mapNames Allowed field names mapped to column expressions
1041
1044
  * @param [modifier] Additional clause to append such as 'NULLS LAST' or 'COLLATE "en_US"'
1042
1045
  * @example
1043
- * query.sortField('-modified_at'); // ORDER BY modified_at DESC
1044
1046
  * query.sortField('-created', {'created': 'created_at'}); // ORDER BY created_at DESC
1047
+ * query.sortField('-foo', {'created': 'created_at'}); // console.warn; no ORDER BY added
1045
1048
  * query.sortField('-created', {
1046
1049
  * 'created': 'created_at NULLS LAST',
1047
1050
  * '-created': 'created_at DESC NULLS LAST'
@@ -1064,7 +1067,13 @@ UNION
1064
1067
  direction = "DESC";
1065
1068
  fieldName = fieldName.slice(1);
1066
1069
  }
1067
- const column = mapNames[fieldName] || fieldName;
1070
+ if (!Object.prototype.hasOwnProperty.call(mapNames, fieldName)) {
1071
+ console.warn(
1072
+ `SelectBuilder: sortField() ignoring unknown field "${fieldName.slice(0, 100)}"`
1073
+ );
1074
+ return this;
1075
+ }
1076
+ const column = mapNames[fieldName];
1068
1077
  if (modifier) {
1069
1078
  direction += ` ${modifier}`;
1070
1079
  }
package/dist/index.d.ts CHANGED
@@ -359,13 +359,16 @@ export declare class SelectBuilder {
359
359
  */
360
360
  orderBy(column: string, direction?: "ASC" | "DESC"): this;
361
361
  /**
362
- * Sort by the given column, with a map of columns to translate
363
- * @param fieldName The file name such as 'created_at' or '-created_at' for descending
364
- * @param [mapNames] Column names to translate from field to column expression
362
+ * Sort by the given field, with a map of allowed fields to column expressions.
363
+ * Only field names present in mapNames are allowed; anything else emits a
364
+ * console.warn and adds no ORDER BY clause. This prevents SQL injection when
365
+ * fieldName comes from user input.
366
+ * @param fieldName The field name such as 'created' or '-created' for descending
367
+ * @param mapNames Allowed field names mapped to column expressions
365
368
  * @param [modifier] Additional clause to append such as 'NULLS LAST' or 'COLLATE "en_US"'
366
369
  * @example
367
- * query.sortField('-modified_at'); // ORDER BY modified_at DESC
368
370
  * query.sortField('-created', {'created': 'created_at'}); // ORDER BY created_at DESC
371
+ * query.sortField('-foo', {'created': 'created_at'}); // console.warn; no ORDER BY added
369
372
  * query.sortField('-created', {
370
373
  * 'created': 'created_at NULLS LAST',
371
374
  * '-created': 'created_at DESC NULLS LAST'
package/dist/index.mjs CHANGED
@@ -819,7 +819,7 @@ UNION
819
819
  if (numArgs === 1) {
820
820
  collection.push(column);
821
821
  return this;
822
- } else if (numArgs === 2 && Array.isArray(operator) && operator.length > 0 && substrCount(column, "?") === operator.length) {
822
+ } else if (numArgs === 2 && Array.isArray(operator) && substrCount(column, "?") === operator.length) {
823
823
  const values = operator;
824
824
  const sql = column.replace(/\?/g, () => {
825
825
  this._bindings.push(values.shift());
@@ -995,13 +995,16 @@ UNION
995
995
  return this;
996
996
  }
997
997
  /**
998
- * Sort by the given column, with a map of columns to translate
999
- * @param fieldName The file name such as 'created_at' or '-created_at' for descending
1000
- * @param [mapNames] Column names to translate from field to column expression
998
+ * Sort by the given field, with a map of allowed fields to column expressions.
999
+ * Only field names present in mapNames are allowed; anything else emits a
1000
+ * console.warn and adds no ORDER BY clause. This prevents SQL injection when
1001
+ * fieldName comes from user input.
1002
+ * @param fieldName The field name such as 'created' or '-created' for descending
1003
+ * @param mapNames Allowed field names mapped to column expressions
1001
1004
  * @param [modifier] Additional clause to append such as 'NULLS LAST' or 'COLLATE "en_US"'
1002
1005
  * @example
1003
- * query.sortField('-modified_at'); // ORDER BY modified_at DESC
1004
1006
  * query.sortField('-created', {'created': 'created_at'}); // ORDER BY created_at DESC
1007
+ * query.sortField('-foo', {'created': 'created_at'}); // console.warn; no ORDER BY added
1005
1008
  * query.sortField('-created', {
1006
1009
  * 'created': 'created_at NULLS LAST',
1007
1010
  * '-created': 'created_at DESC NULLS LAST'
@@ -1024,7 +1027,13 @@ UNION
1024
1027
  direction = "DESC";
1025
1028
  fieldName = fieldName.slice(1);
1026
1029
  }
1027
- const column = mapNames[fieldName] || fieldName;
1030
+ if (!Object.prototype.hasOwnProperty.call(mapNames, fieldName)) {
1031
+ console.warn(
1032
+ `SelectBuilder: sortField() ignoring unknown field "${fieldName.slice(0, 100)}"`
1033
+ );
1034
+ return this;
1035
+ }
1036
+ const column = mapNames[fieldName];
1028
1037
  if (modifier) {
1029
1038
  direction += ` ${modifier}`;
1030
1039
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sql-love",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Build SQL queries with JavaScript",
5
5
  "type": "module",
6
6
  "engines": {
@@ -55,7 +55,7 @@
55
55
  "@types/bun": "latest",
56
56
  "@types/node": "latest",
57
57
  "dts-bundle-generator": "9.5.1",
58
- "prettier": "3.9.6",
58
+ "prettier": "3.9.9",
59
59
  "prettier-plugin-organize-imports": "4.3.0",
60
60
  "typescript": "5.9.3"
61
61
  }