@promakeai/orm 1.0.0
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 +154 -0
- package/dist/ORM.d.ts +190 -0
- package/dist/adapters/IDataAdapter.d.ts +134 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.js +1054 -0
- package/dist/schema/defineSchema.d.ts +50 -0
- package/dist/schema/fieldBuilder.d.ts +152 -0
- package/dist/schema/helpers.d.ts +54 -0
- package/dist/schema/index.d.ts +9 -0
- package/dist/schema/schemaHelpers.d.ts +55 -0
- package/dist/schema/validator.d.ts +45 -0
- package/dist/types.d.ts +192 -0
- package/dist/utils/jsonConverter.d.ts +29 -0
- package/dist/utils/populateResolver.d.ts +57 -0
- package/dist/utils/translationQuery.d.ts +50 -0
- package/dist/utils/whereBuilder.d.ts +38 -0
- package/package.json +48 -0
- package/src/ORM.ts +398 -0
- package/src/adapters/IDataAdapter.ts +196 -0
- package/src/index.ts +148 -0
- package/src/schema/defineSchema.ts +164 -0
- package/src/schema/fieldBuilder.ts +244 -0
- package/src/schema/helpers.ts +171 -0
- package/src/schema/index.ts +47 -0
- package/src/schema/schemaHelpers.ts +123 -0
- package/src/schema/validator.ts +189 -0
- package/src/types.ts +243 -0
- package/src/utils/jsonConverter.ts +94 -0
- package/src/utils/populateResolver.ts +322 -0
- package/src/utils/translationQuery.ts +306 -0
- package/src/utils/whereBuilder.ts +154 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MongoDB-style Where Clause Builder
|
|
3
|
+
*
|
|
4
|
+
* Converts MongoDB/Mongoose-style query objects into SQL WHERE clauses
|
|
5
|
+
* with parameterized values.
|
|
6
|
+
*
|
|
7
|
+
* Supported operators:
|
|
8
|
+
* - Comparison: $eq, $ne, $gt, $gte, $lt, $lte
|
|
9
|
+
* - Array: $in, $nin
|
|
10
|
+
* - String: $like, $notLike
|
|
11
|
+
* - Range: $between
|
|
12
|
+
* - Null: $isNull
|
|
13
|
+
* - Negation: $not (field-level)
|
|
14
|
+
* - Logical: $and, $or, $nor
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export interface WhereResult {
|
|
18
|
+
sql: string;
|
|
19
|
+
params: unknown[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const COMPARISON_OPERATORS: Record<string, string> = {
|
|
23
|
+
$eq: "=",
|
|
24
|
+
$ne: "!=",
|
|
25
|
+
$gt: ">",
|
|
26
|
+
$gte: ">=",
|
|
27
|
+
$lt: "<",
|
|
28
|
+
$lte: "<=",
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const LOGICAL_OPERATORS = new Set(["$and", "$or", "$nor"]);
|
|
32
|
+
|
|
33
|
+
function processFieldOperators(
|
|
34
|
+
field: string,
|
|
35
|
+
operators: Record<string, unknown>
|
|
36
|
+
): WhereResult {
|
|
37
|
+
const parts: string[] = [];
|
|
38
|
+
const params: unknown[] = [];
|
|
39
|
+
|
|
40
|
+
for (const [op, value] of Object.entries(operators)) {
|
|
41
|
+
if (op in COMPARISON_OPERATORS) {
|
|
42
|
+
parts.push(`${field} ${COMPARISON_OPERATORS[op]} ?`);
|
|
43
|
+
params.push(value);
|
|
44
|
+
} else if (op === "$in" || op === "$nin") {
|
|
45
|
+
if (!Array.isArray(value)) {
|
|
46
|
+
throw new Error(`${op} operator requires an array value`);
|
|
47
|
+
}
|
|
48
|
+
if (value.length === 0) {
|
|
49
|
+
// $in with empty array = always false, $nin with empty array = always true
|
|
50
|
+
parts.push(op === "$in" ? "0 = 1" : "1 = 1");
|
|
51
|
+
} else {
|
|
52
|
+
const placeholders = value.map(() => "?").join(", ");
|
|
53
|
+
const keyword = op === "$in" ? "IN" : "NOT IN";
|
|
54
|
+
parts.push(`${field} ${keyword} (${placeholders})`);
|
|
55
|
+
params.push(...value);
|
|
56
|
+
}
|
|
57
|
+
} else if (op === "$like") {
|
|
58
|
+
parts.push(`${field} LIKE ?`);
|
|
59
|
+
params.push(value);
|
|
60
|
+
} else if (op === "$notLike") {
|
|
61
|
+
parts.push(`${field} NOT LIKE ?`);
|
|
62
|
+
params.push(value);
|
|
63
|
+
} else if (op === "$between") {
|
|
64
|
+
if (!Array.isArray(value) || value.length !== 2) {
|
|
65
|
+
throw new Error("$between operator requires an array of exactly 2 values");
|
|
66
|
+
}
|
|
67
|
+
parts.push(`${field} BETWEEN ? AND ?`);
|
|
68
|
+
params.push(value[0], value[1]);
|
|
69
|
+
} else if (op === "$isNull") {
|
|
70
|
+
parts.push(value ? `${field} IS NULL` : `${field} IS NOT NULL`);
|
|
71
|
+
} else if (op === "$not") {
|
|
72
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
73
|
+
throw new Error("$not operator requires an object with operators");
|
|
74
|
+
}
|
|
75
|
+
const inner = processFieldOperators(field, value as Record<string, unknown>);
|
|
76
|
+
parts.push(`NOT (${inner.sql})`);
|
|
77
|
+
params.push(...inner.params);
|
|
78
|
+
} else {
|
|
79
|
+
throw new Error(`Unknown operator: ${op}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const sql = parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`;
|
|
84
|
+
return { sql, params };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function processFieldCondition(field: string, value: unknown): WhereResult {
|
|
88
|
+
if (value === null) {
|
|
89
|
+
return { sql: `${field} IS NULL`, params: [] };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
93
|
+
return { sql: `${field} = ?`, params: [value] };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Object with operators
|
|
97
|
+
return processFieldOperators(field, value as Record<string, unknown>);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function processCondition(where: Record<string, unknown>): WhereResult {
|
|
101
|
+
const parts: string[] = [];
|
|
102
|
+
const params: unknown[] = [];
|
|
103
|
+
|
|
104
|
+
for (const [key, value] of Object.entries(where)) {
|
|
105
|
+
if (LOGICAL_OPERATORS.has(key)) {
|
|
106
|
+
if (!Array.isArray(value)) {
|
|
107
|
+
throw new Error(`${key} operator requires an array of conditions`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const subResults = value.map((sub: Record<string, unknown>) =>
|
|
111
|
+
processCondition(sub)
|
|
112
|
+
);
|
|
113
|
+
const joiner = key === "$and" ? " AND " : " OR ";
|
|
114
|
+
const combined = subResults.map((r) => r.sql).join(joiner);
|
|
115
|
+
|
|
116
|
+
if (combined) {
|
|
117
|
+
const wrappedSql = key === "$nor" ? `NOT (${combined})` : `(${combined})`;
|
|
118
|
+
parts.push(wrappedSql);
|
|
119
|
+
subResults.forEach((r) => params.push(...r.params));
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
const result = processFieldCondition(key, value);
|
|
123
|
+
parts.push(result.sql);
|
|
124
|
+
params.push(...result.params);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return { sql: parts.join(" AND "), params };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Build a SQL WHERE clause from a MongoDB-style query object.
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* // Simple equality
|
|
136
|
+
* buildWhereClause({ active: 1 })
|
|
137
|
+
* // => { sql: "active = ?", params: [1] }
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* // Comparison operators
|
|
141
|
+
* buildWhereClause({ age: { $gte: 18, $lt: 65 } })
|
|
142
|
+
* // => { sql: "(age >= ? AND age < ?)", params: [18, 65] }
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* // Logical operators
|
|
146
|
+
* buildWhereClause({ $or: [{ active: 1 }, { role: "admin" }] })
|
|
147
|
+
* // => { sql: "(active = ? OR role = ?)", params: [1, "admin"] }
|
|
148
|
+
*/
|
|
149
|
+
export function buildWhereClause(where?: Record<string, unknown>): WhereResult {
|
|
150
|
+
if (!where || Object.keys(where).length === 0) {
|
|
151
|
+
return { sql: "", params: [] };
|
|
152
|
+
}
|
|
153
|
+
return processCondition(where);
|
|
154
|
+
}
|