lamix 4.2.26 → 4.2.27
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 +2 -0
- package/lib/@types/index.d.ts +5 -1
- package/lib/index.js +68 -31
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -173,6 +173,8 @@ const maxId = await User.query().max('id');
|
|
|
173
173
|
# status a column (returns status of boolean values)
|
|
174
174
|
const usersAll = await User.all();
|
|
175
175
|
const users = await User.query().where('status', true).get();
|
|
176
|
+
# Randomize a column (returns Random values without any order)
|
|
177
|
+
const users = await User.query().where('status', true).orderByRaw('RAND()').get();
|
|
176
178
|
const usersorderbyid = await User.query().where('status', true).orderBy('id', 'desc').get();
|
|
177
179
|
const usersorderbycreateAt = await User.query().where('status', true).orderBy('created_at', 'desc').get();
|
|
178
180
|
|
package/lib/@types/index.d.ts
CHANGED
|
@@ -333,6 +333,10 @@ export class QueryBuilder {
|
|
|
333
333
|
having(column: any, operatorOrValue: any, value: any, ...args: any[]): this;
|
|
334
334
|
orHaving(column: any, operatorOrValue: any, value: any, ...args: any[]): this;
|
|
335
335
|
_pushHaving(column: any, op: any, value: any, bool: any): this;
|
|
336
|
+
/**************************************************************************
|
|
337
|
+
* ORDER BY RAW
|
|
338
|
+
**************************************************************************/
|
|
339
|
+
orderByRaw(rawSql: any): this;
|
|
336
340
|
/**************************************************************************
|
|
337
341
|
* ORDER / LIMIT
|
|
338
342
|
**************************************************************************/
|
|
@@ -395,7 +399,7 @@ export class QueryBuilder {
|
|
|
395
399
|
decrement(col: any, by?: number): Promise<any>;
|
|
396
400
|
delete(): Promise<any>;
|
|
397
401
|
truncate(): Promise<boolean>;
|
|
398
|
-
_compileWhereOnly(): string;
|
|
402
|
+
_compileWhereOnly(allowEmpty?: boolean): string;
|
|
399
403
|
/**************************************************************************
|
|
400
404
|
* EAGER LOAD (unchanged except robust checks)
|
|
401
405
|
**************************************************************************/
|
package/lib/index.js
CHANGED
|
@@ -818,7 +818,7 @@ class Validator {
|
|
|
818
818
|
// For strings or arrays, use length
|
|
819
819
|
if (typeof val === 'string' || Array.isArray(val)) {
|
|
820
820
|
if (val.length < limit) {
|
|
821
|
-
this.addError(field, 'min', `${field} must be at least ${limit} characters.`);
|
|
821
|
+
this.addError(field, this.msg(field, 'min', `${field} must be at least ${limit} characters.`));
|
|
822
822
|
}
|
|
823
823
|
}
|
|
824
824
|
// For numbers, compare numerically
|
|
@@ -853,8 +853,15 @@ class Validator {
|
|
|
853
853
|
validateConfirmed(field) {
|
|
854
854
|
const value = this.data[field];
|
|
855
855
|
const confirm = this.data[field + '_confirmation'];
|
|
856
|
-
if (value
|
|
857
|
-
|
|
856
|
+
if (!value) return;
|
|
857
|
+
|
|
858
|
+
const v1 = String(value).trim();
|
|
859
|
+
const v2 = String(confirm ?? '').trim();
|
|
860
|
+
if (v1 !== v2) {
|
|
861
|
+
this.addError(
|
|
862
|
+
field,
|
|
863
|
+
this.msg(field, 'confirmed', `${field} confirmation does not match.`)
|
|
864
|
+
);
|
|
858
865
|
}
|
|
859
866
|
}
|
|
860
867
|
|
|
@@ -1011,14 +1018,31 @@ class Validator {
|
|
|
1011
1018
|
}
|
|
1012
1019
|
}
|
|
1013
1020
|
|
|
1014
|
-
|
|
1021
|
+
validatePhone(field) {
|
|
1015
1022
|
const val = this.data[field];
|
|
1016
1023
|
if (!val) return;
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1024
|
+
let cleaned = String(val).replace(/[\s\-()]/g, '');
|
|
1025
|
+
/*
|
|
1026
|
+
Allow:
|
|
1027
|
+
- +countrycode + number (8–15 digits total)
|
|
1028
|
+
- numbers starting with 0
|
|
1029
|
+
- numbers starting with country code (no +)
|
|
1030
|
+
- 7–15 digits total
|
|
1031
|
+
*/
|
|
1032
|
+
const phoneRegex = /^(\+?\d{8,15}|0\d{8,14})$/;
|
|
1033
|
+
|
|
1034
|
+
if (!phoneRegex.test(cleaned)) {
|
|
1035
|
+
this.addError(
|
|
1036
|
+
field,
|
|
1037
|
+
this.msg(field, 'phone', `${field} must be a valid phone number.`)
|
|
1038
|
+
);
|
|
1039
|
+
return;
|
|
1021
1040
|
}
|
|
1041
|
+
|
|
1042
|
+
// if (!cleaned.startsWith('+')) {
|
|
1043
|
+
// cleaned = '+' + cleaned;
|
|
1044
|
+
// }
|
|
1045
|
+
// this.data[field] = cleaned;
|
|
1022
1046
|
}
|
|
1023
1047
|
|
|
1024
1048
|
validateAlpha(field) {
|
|
@@ -1481,7 +1505,7 @@ class Paginator {
|
|
|
1481
1505
|
}
|
|
1482
1506
|
|
|
1483
1507
|
|
|
1484
|
-
const VALID_OPERATORS = ['=', '<', '<=', '>', '>=', '<>', '!=', 'LIKE', 'ILIKE'];
|
|
1508
|
+
const VALID_OPERATORS = ['=', '<', '<=', '>', '>=', '<>', '!=', 'LIKE', 'ILIKE', 'NOT IN'];
|
|
1485
1509
|
/******************************************************************************
|
|
1486
1510
|
* QueryBuilder (Bug-Free)
|
|
1487
1511
|
*****************************************************************************/
|
|
@@ -1513,7 +1537,7 @@ class QueryBuilder {
|
|
|
1513
1537
|
|
|
1514
1538
|
// Helper to normalize operator
|
|
1515
1539
|
_normalizeOperator(operator) {
|
|
1516
|
-
|
|
1540
|
+
let op = operator ? operator.toUpperCase() : '=';
|
|
1517
1541
|
if (!VALID_OPERATORS.includes(op)) {
|
|
1518
1542
|
throw new DBError('Invalid SQL operator', {
|
|
1519
1543
|
operator,
|
|
@@ -1522,14 +1546,10 @@ class QueryBuilder {
|
|
|
1522
1546
|
});
|
|
1523
1547
|
}
|
|
1524
1548
|
|
|
1525
|
-
// Convert ILIKE to
|
|
1526
|
-
if (op === 'ILIKE' && this.dialect === 'mysql')
|
|
1527
|
-
return 'LIKE';
|
|
1528
|
-
}
|
|
1529
|
-
|
|
1549
|
+
// Convert ILIKE to LIKE for MySQL
|
|
1550
|
+
if (op === 'ILIKE' && this.dialect === 'mysql') op = 'LIKE';
|
|
1530
1551
|
return op;
|
|
1531
1552
|
}
|
|
1532
|
-
|
|
1533
1553
|
/**************************************************************************
|
|
1534
1554
|
* BASIC CONFIG
|
|
1535
1555
|
**************************************************************************/
|
|
@@ -1619,7 +1639,7 @@ class QueryBuilder {
|
|
|
1619
1639
|
|
|
1620
1640
|
const op = this._normalizeOperator(operator);
|
|
1621
1641
|
|
|
1622
|
-
//
|
|
1642
|
+
// Use LOWER() only if original operator was ILIKE on MySQL
|
|
1623
1643
|
const useLower = this.dialect === 'mysql' && operator.toUpperCase() === 'ILIKE';
|
|
1624
1644
|
return this._pushWhere({
|
|
1625
1645
|
type: 'basic',
|
|
@@ -1744,8 +1764,7 @@ class QueryBuilder {
|
|
|
1744
1764
|
/** COMPLETELY FIXED VERSION */
|
|
1745
1765
|
whereNot(column, operatorOrValue, value) {
|
|
1746
1766
|
if (typeof column === 'object' && column !== null) {
|
|
1747
|
-
for (const [k, v] of Object.entries(column))
|
|
1748
|
-
this.whereNot(k, v);
|
|
1767
|
+
for (const [k, v] of Object.entries(column)) this.whereNot(k, v);
|
|
1749
1768
|
return this;
|
|
1750
1769
|
}
|
|
1751
1770
|
|
|
@@ -1760,11 +1779,13 @@ class QueryBuilder {
|
|
|
1760
1779
|
if (operator === '=') operator = '!=';
|
|
1761
1780
|
if (operator.toUpperCase() === 'IN') operator = 'NOT IN';
|
|
1762
1781
|
|
|
1782
|
+
const op = this._normalizeOperator(operator);
|
|
1783
|
+
|
|
1763
1784
|
return this._pushWhere({
|
|
1764
1785
|
type: 'basic',
|
|
1765
1786
|
not: true,
|
|
1766
1787
|
column,
|
|
1767
|
-
operator,
|
|
1788
|
+
operator: op,
|
|
1768
1789
|
value: val,
|
|
1769
1790
|
bindings: [val]
|
|
1770
1791
|
});
|
|
@@ -1906,6 +1927,22 @@ class QueryBuilder {
|
|
|
1906
1927
|
return this;
|
|
1907
1928
|
}
|
|
1908
1929
|
|
|
1930
|
+
/**************************************************************************
|
|
1931
|
+
* ORDER BY RAW
|
|
1932
|
+
**************************************************************************/
|
|
1933
|
+
orderByRaw(rawSql) {
|
|
1934
|
+
if (typeof rawSql !== 'string' || !rawSql.trim()) {
|
|
1935
|
+
throw new DBError('orderByRaw expects a non-empty string', {
|
|
1936
|
+
method: 'orderByRaw',
|
|
1937
|
+
rawSql
|
|
1938
|
+
});
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
// Push as a special type
|
|
1942
|
+
this._orders.push({ raw: rawSql });
|
|
1943
|
+
return this;
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1909
1946
|
/**************************************************************************
|
|
1910
1947
|
* ORDER / LIMIT
|
|
1911
1948
|
**************************************************************************/
|
|
@@ -2092,7 +2129,7 @@ class QueryBuilder {
|
|
|
2092
2129
|
parts.push(
|
|
2093
2130
|
'ORDER BY ' +
|
|
2094
2131
|
this._orders
|
|
2095
|
-
.map((
|
|
2132
|
+
.map(o => (Array.isArray(o) ? `${escapeId(o[0])} ${o[1]}` : o.raw))
|
|
2096
2133
|
.join(', ')
|
|
2097
2134
|
);
|
|
2098
2135
|
}
|
|
@@ -2558,7 +2595,13 @@ class QueryBuilder {
|
|
|
2558
2595
|
}
|
|
2559
2596
|
}
|
|
2560
2597
|
|
|
2561
|
-
_compileWhereOnly() {
|
|
2598
|
+
_compileWhereOnly(allowEmpty = false) {
|
|
2599
|
+
if (!allowEmpty && !this._wheres.length) {
|
|
2600
|
+
throw new DBError('Unsafe query without WHERE clause', {
|
|
2601
|
+
table: this.table,
|
|
2602
|
+
method: '_compileWhereOnly'
|
|
2603
|
+
});
|
|
2604
|
+
}
|
|
2562
2605
|
const w = this._compileWheres();
|
|
2563
2606
|
return w ? w : '';
|
|
2564
2607
|
}
|
|
@@ -2597,10 +2640,9 @@ class QueryBuilder {
|
|
|
2597
2640
|
**************************************************************************/
|
|
2598
2641
|
_clone() {
|
|
2599
2642
|
const c = new QueryBuilder(this.table, this.modelClass);
|
|
2600
|
-
|
|
2601
2643
|
c.tableAlias = this.tableAlias;
|
|
2602
2644
|
c._select = [...this._select];
|
|
2603
|
-
c._joins =
|
|
2645
|
+
c._joins = this._joins.map(j => ({ ...j })); // shallow copy sufficient
|
|
2604
2646
|
c._group = [...this._group];
|
|
2605
2647
|
c._orders = [...this._orders];
|
|
2606
2648
|
c._limit = this._limit;
|
|
@@ -2611,17 +2653,12 @@ class QueryBuilder {
|
|
|
2611
2653
|
c._ignoreSoftDeletes = this._ignoreSoftDeletes;
|
|
2612
2654
|
c._fromRaw = this._fromRaw;
|
|
2613
2655
|
|
|
2614
|
-
//
|
|
2656
|
+
// Rehydrate wheres, CTEs, unions
|
|
2615
2657
|
c._wheres = this._rehydrateWheres(this._wheres);
|
|
2616
|
-
|
|
2617
|
-
// rehydrate CTEs
|
|
2618
2658
|
c._ctes = this._rehydrateCTEs(this._ctes);
|
|
2619
|
-
|
|
2620
|
-
// rehydrate unions
|
|
2621
2659
|
c._unions = this._rehydrateUnions(this._unions);
|
|
2622
2660
|
|
|
2623
|
-
|
|
2624
|
-
c._having = JSON.parse(JSON.stringify(this._having));
|
|
2661
|
+
c._having = this._having.map(h => ({ ...h }));
|
|
2625
2662
|
|
|
2626
2663
|
return c;
|
|
2627
2664
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lamix",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.27",
|
|
4
4
|
"description": "lamix - ORM for Node-express js",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -26,14 +26,14 @@
|
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^20.11.30",
|
|
29
|
+
"chalk": "^4.1.2",
|
|
29
30
|
"typescript": "^5.4.5"
|
|
30
31
|
},
|
|
31
32
|
"dependencies": {
|
|
32
33
|
"bcrypt": "^6.0.0",
|
|
34
|
+
"dotenv": "^17.2.4 ",
|
|
33
35
|
"express-session": "^1.19.0",
|
|
34
|
-
"lru-cache": "^11.2.5"
|
|
35
|
-
"chalk": "^4.1.2",
|
|
36
|
-
"dotenv": "^17.2.4 "
|
|
36
|
+
"lru-cache": "^11.2.5"
|
|
37
37
|
},
|
|
38
38
|
"keywords": [
|
|
39
39
|
"lamix",
|