@liminalfunctions/framework 1.0.67 → 1.0.69
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/dist/F_Collection.d.ts +2 -0
- package/dist/F_Collection.js +5 -0
- package/dist/F_Collection.js.map +1 -1
- package/dist/F_Compile.js +19 -0
- package/dist/F_Compile.js.map +1 -1
- package/dist/code_generation/templates/utils.ts.mustache +1 -0
- package/dist/utils/complex_query_validator_from_zod.d.ts +20 -0
- package/dist/utils/complex_query_validator_from_zod.js +223 -0
- package/dist/utils/complex_query_validator_from_zod.js.map +1 -0
- package/dist/utils/query_object_to_mongodb_query.d.ts +8 -1
- package/dist/utils/query_object_to_mongodb_query.js +12 -2
- package/dist/utils/query_object_to_mongodb_query.js.map +1 -1
- package/dist/utils/query_validator_from_zod.js +7 -1
- package/dist/utils/query_validator_from_zod.js.map +1 -1
- package/dist/utils/zod_loop_seperator.js +4 -0
- package/dist/utils/zod_loop_seperator.js.map +1 -1
- package/package.json +4 -3
- package/src/F_Collection.ts +5 -0
- package/src/F_Compile.ts +18 -0
- package/src/code_generation/templates/utils.ts.mustache +1 -0
- package/src/utils/complex_query_validator_from_zod.ts +252 -0
- package/src/utils/query_object_to_mongodb_query.ts +11 -3
- package/src/utils/query_validator_from_zod.ts +8 -1
- package/src/utils/zod_loop_seperator.ts +5 -0
- package/test/0_3_query_validator_to_mongodb_query.test.ts +18 -0
- package/test/0_4_query_validator_to_advanced_query.test.ts +402 -0
- package/test/1_0_basic_server.test.ts +30 -0
- package/test/1_4_advanced_queries.test.ts +400 -0
- package/test/2_0_client_library_basic_type_generation.test.ts +2 -2
- package/test/2_0_client_library_query_type_generation.test.ts +12 -0
- package/test/tmp/dist/types/brief_news_category_query.d.ts +3 -0
- package/test/tmp/dist/types/client_query.d.ts +2 -0
- package/test/tmp/dist/types/institution_query.d.ts +2 -0
- package/test/tmp/dist/types/project_query.d.ts +2 -0
- package/test/tmp/dist/utils/utils.js +4 -0
- package/test/tmp/dist/utils/utils.js.map +1 -1
- package/test/tmp/package-lock.json +3 -3
- package/test/tmp/src/types/brief_news_category_query.ts +3 -0
- package/test/tmp/src/types/client_query.ts +2 -0
- package/test/tmp/src/types/institution_query.ts +2 -0
- package/test/tmp/src/types/project_query.ts +2 -0
- package/test/tmp/src/utils/utils.ts +1 -0
- /package/test/{0_4_cache.test.ts → 0_5_cache.test.ts} +0 -0
- /package/test/{0_5_malicious_keys.test.ts → 0_6_malicious_keys.test.ts} +0 -0
- /package/test/{0_6_array_children.test.ts → 0_7_array_children.test.ts} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zod_loop_seperator.js","sourceRoot":"","sources":["../../src/utils/zod_loop_seperator.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"zod_loop_seperator.js","sourceRoot":"","sources":["../../src/utils/zod_loop_seperator.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liminalfunctions/framework",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.69",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -28,12 +28,12 @@
|
|
|
28
28
|
"@types/mocha": "^10.0.10",
|
|
29
29
|
"@types/mustache": "^4.2.6",
|
|
30
30
|
"@types/node": "^22.10.7",
|
|
31
|
+
"got": "^14.6.0",
|
|
31
32
|
"mocha": "^11.1.0",
|
|
32
33
|
"rimraf": "^6.0.1",
|
|
33
34
|
"tsx": "^4.19.3",
|
|
34
35
|
"typescript": "^5.7.3",
|
|
35
|
-
"zod": "^4.0.17"
|
|
36
|
-
"got": "^14.6.0"
|
|
36
|
+
"zod": "^4.0.17"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"express": "^5.1.0",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"copyfiles": "^2.4.1",
|
|
46
|
+
"escape-string-regexp": "^5.0.0",
|
|
46
47
|
"mustache": "^4.2.0"
|
|
47
48
|
}
|
|
48
49
|
}
|
package/src/F_Collection.ts
CHANGED
|
@@ -4,6 +4,7 @@ import mongoose, { Collection, Model, ObjectId } from "mongoose";
|
|
|
4
4
|
import { F_Security_Model } from "./F_Security_Models/F_Security_Model.js";
|
|
5
5
|
import { query_validator_from_zod } from "./utils/query_validator_from_zod.js";
|
|
6
6
|
import { array_children_from_zod } from "./utils/array_children_from_zod.js";
|
|
7
|
+
import { complex_query_validator_from_zod } from "./utils/complex_query_validator_from_zod.js";
|
|
7
8
|
|
|
8
9
|
export type CollectionType<Col extends F_Collection<string, Validator>, Validator extends z.ZodObject> = z.output<Col['validator']>;
|
|
9
10
|
|
|
@@ -24,6 +25,8 @@ export class F_Collection<Collection_ID extends string, ZodSchema extends z.ZodO
|
|
|
24
25
|
|
|
25
26
|
query_validator_server: z.ZodType;
|
|
26
27
|
query_validator_client: z.ZodType;
|
|
28
|
+
advanced_query_validator_server: z.ZodType;
|
|
29
|
+
advanced_query_validator_client: z.ZodType;
|
|
27
30
|
put_validator: ReturnType<ZodSchema['partial']>;
|
|
28
31
|
// TODO: Come back and find a way to select the particular partial I want
|
|
29
32
|
// instead of partialing the whole object.
|
|
@@ -49,6 +52,8 @@ export class F_Collection<Collection_ID extends string, ZodSchema extends z.ZodO
|
|
|
49
52
|
this.mongoose_model = mongoose_from_zod(collection_name, validator, database);
|
|
50
53
|
this.query_validator_server = query_validator_from_zod(validator, 'server');
|
|
51
54
|
this.query_validator_client = query_validator_from_zod(validator, 'client');
|
|
55
|
+
this.advanced_query_validator_server = complex_query_validator_from_zod(validator, 'server').optional()
|
|
56
|
+
this.advanced_query_validator_client = complex_query_validator_from_zod(validator, 'client').optional()
|
|
52
57
|
// TODO: we can make this more closely match the mongoDB PUT operation and allow updates to eg array.3.element fields
|
|
53
58
|
|
|
54
59
|
if(!Object.hasOwn(this.validator._zod.def.shape, '_id')){
|
package/src/F_Compile.ts
CHANGED
|
@@ -127,6 +127,7 @@ export function compile<Collection_ID extends string, ZodSchema extends z.ZodObj
|
|
|
127
127
|
try {
|
|
128
128
|
validated_query_args = collection.query_validator_server.parse(convert_null(req.query));
|
|
129
129
|
} catch(err){
|
|
130
|
+
console.log(err.issues)
|
|
130
131
|
if(err instanceof z.ZodError){
|
|
131
132
|
res.status(400);
|
|
132
133
|
res.json({ error: err.issues });
|
|
@@ -150,6 +151,23 @@ export function compile<Collection_ID extends string, ZodSchema extends z.ZodObj
|
|
|
150
151
|
res.json({ error: `You do not have permission to fetch documents from ${collection.collection_id}.` });
|
|
151
152
|
return;
|
|
152
153
|
}
|
|
154
|
+
|
|
155
|
+
if(validated_query_args.advanced_query){
|
|
156
|
+
let parsed_advanced_query: unknown;
|
|
157
|
+
try { parsed_advanced_query = JSON.parse(validated_query_args.advanced_query); } catch(err) {
|
|
158
|
+
res.status(400);
|
|
159
|
+
res.json({ error: `The advanced_query field was not in JSON format` });
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
let { data: advanced_query, error: parsed_advanced_query_error, success: parsed_advanced_query_success } = collection.advanced_query_validator_server.safeParse(parsed_advanced_query);
|
|
163
|
+
if(!parsed_advanced_query_success){
|
|
164
|
+
res.status(400);
|
|
165
|
+
res.json({ error: parsed_advanced_query_error.issues });
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
find = { $and: [find, advanced_query]}
|
|
169
|
+
}
|
|
170
|
+
|
|
153
171
|
|
|
154
172
|
let documents;
|
|
155
173
|
try {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
export function encode_search_params(params: {[key: string]: string | number | boolean | string[] | Date}): {[key: string]: string | number | boolean }{
|
|
4
4
|
let retval: {[key: string]: string | number | boolean } = {}
|
|
5
5
|
for(let [key, value] of Object.entries(params)){
|
|
6
|
+
if(key === 'advanced_query') { retval[key] = JSON.stringify(value); continue; }
|
|
6
7
|
if(Array.isArray(value)){
|
|
7
8
|
retval[key] = value.join(',')
|
|
8
9
|
} else if(value instanceof Date){
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { z } from "zod/v4"
|
|
2
|
+
import { $ZodLooseShape } from "zod/v4/core";
|
|
3
|
+
import { z_mongodb_id, z_mongodb_id_nullable, z_mongodb_id_optional } from "./mongoose_from_zod.js";
|
|
4
|
+
import { find_loops, validator_group } from './zod_loop_seperator.js'
|
|
5
|
+
|
|
6
|
+
type type_filters = {
|
|
7
|
+
path: string,
|
|
8
|
+
filter: z.ZodType,
|
|
9
|
+
sortable: boolean,
|
|
10
|
+
}[]
|
|
11
|
+
type Mode = 'client' | 'server'
|
|
12
|
+
|
|
13
|
+
export function complex_query_validator_from_zod(zod_definition: z.ZodObject, mode: Mode = 'server'){
|
|
14
|
+
let loops = find_loops(zod_definition as z.ZodType);
|
|
15
|
+
|
|
16
|
+
let object_filter = {} as $ZodLooseShape;
|
|
17
|
+
let object_filters = parse_object(zod_definition._zod.def, '', loops, mode);
|
|
18
|
+
for(let filter of object_filters){
|
|
19
|
+
object_filter[filter.path.slice(1)] = filter.filter;
|
|
20
|
+
}
|
|
21
|
+
let compiled_object_filter = z.object(object_filter)
|
|
22
|
+
|
|
23
|
+
let and = z.object({
|
|
24
|
+
get $and(){ return z.array(z.union([and, or, compiled_object_filter])); },
|
|
25
|
+
});
|
|
26
|
+
let or = z.object({
|
|
27
|
+
get $or(){ return z.array(z.union([and, or, compiled_object_filter])); },
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
return z.union([and, or]);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function parse_any(zod_definition: z.ZodTypeAny, prefix: string, loop_detector: Map<any, validator_group>, mode: Mode = 'server'): type_filters {
|
|
34
|
+
switch (zod_definition._zod.def.type) {
|
|
35
|
+
case "enum":
|
|
36
|
+
return parse_enum(zod_definition._zod.def as z.core.$ZodEnumDef, prefix, mode);
|
|
37
|
+
case "string":
|
|
38
|
+
return parse_string(prefix, mode);
|
|
39
|
+
case "number":
|
|
40
|
+
case "int":
|
|
41
|
+
return parse_number(prefix, mode);
|
|
42
|
+
case "object":
|
|
43
|
+
return parse_object(zod_definition._zod.def as z.core.$ZodObjectDef, prefix, loop_detector, mode);
|
|
44
|
+
case "boolean":
|
|
45
|
+
return parse_boolean(prefix, mode);
|
|
46
|
+
case "date":
|
|
47
|
+
return parse_date(prefix, mode);
|
|
48
|
+
case "array":
|
|
49
|
+
return parse_array(zod_definition._zod.def as z.core.$ZodArrayDef, prefix, loop_detector, mode)
|
|
50
|
+
case "union":
|
|
51
|
+
return parse_union(zod_definition._zod.def as z.core.$ZodUnionDef, prefix, mode)
|
|
52
|
+
case "custom":
|
|
53
|
+
if(!zod_definition.meta()) {
|
|
54
|
+
throw new Error(`could not find custom parser in the magic value dictionary`)
|
|
55
|
+
}
|
|
56
|
+
let { framework_override_type } = zod_definition.meta();
|
|
57
|
+
|
|
58
|
+
if(framework_override_type === 'mongodb_id'){
|
|
59
|
+
return parse_mongodb_id(prefix, mode);
|
|
60
|
+
} else {
|
|
61
|
+
throw new Error(`could not find custom parser for ${framework_override_type} in the magic value dictionary`)
|
|
62
|
+
}
|
|
63
|
+
case "default":
|
|
64
|
+
//@ts-ignore
|
|
65
|
+
return parse_any((zod_definition._zod.def as z.core.$ZodDefaultDef).innerType, prefix, loop_detector, mode)
|
|
66
|
+
default:
|
|
67
|
+
return []
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function parse_array(def: z.core.$ZodArrayDef, prefix: string, loop_detector: Map<any, validator_group>, mode: Mode) {
|
|
72
|
+
let simple_children = ['enum', 'string', 'number', 'int', 'boolean']
|
|
73
|
+
if(simple_children.includes(def.element._zod.def.type)) {
|
|
74
|
+
//@ts-ignore
|
|
75
|
+
return parse_any(def.element, prefix, loop_detector, mode).filter(ele => ele.path == prefix);
|
|
76
|
+
} else if(def.element._zod.def.type === 'custom'){
|
|
77
|
+
//@ts-ignore
|
|
78
|
+
if(!def.element.meta()) {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
//@ts-ignore
|
|
82
|
+
let { framework_override_type } = def.element.meta();
|
|
83
|
+
|
|
84
|
+
if(framework_override_type === 'mongodb_id'){
|
|
85
|
+
return parse_mongodb_id(prefix, mode).filter(ele => ele.path == prefix);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function parse_object(def: z.core.$ZodObjectDef, prefix: string, loop_detector: Map<any, validator_group>, mode: Mode): type_filters {
|
|
93
|
+
if(loop_detector.has(def)) {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let retval = [] as type_filters;
|
|
98
|
+
for(let [key, value] of Object.entries(def.shape)){
|
|
99
|
+
//@ts-ignore
|
|
100
|
+
let filters = parse_any(value, `${prefix}.${key}`, loop_detector, mode);
|
|
101
|
+
retval.push(...filters);
|
|
102
|
+
}
|
|
103
|
+
return retval;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function parse_union(def: z.core.$ZodUnionDef, prefix: string, mode: Mode): type_filters {
|
|
107
|
+
let simple_children = ['enum', 'string', 'number', 'int', 'boolean']
|
|
108
|
+
let filter_queue = def.options.slice().filter(ele => simple_children.includes(ele._zod.def.type));
|
|
109
|
+
if(filter_queue.length === 0){ return []; }
|
|
110
|
+
let root = filter_queue.shift();
|
|
111
|
+
for(let filter of filter_queue){
|
|
112
|
+
//@ts-expect-error
|
|
113
|
+
root = root.or(filter);
|
|
114
|
+
}
|
|
115
|
+
return [
|
|
116
|
+
{
|
|
117
|
+
path: prefix,
|
|
118
|
+
//@ts-expect-error
|
|
119
|
+
filter: root,
|
|
120
|
+
sortable: true,
|
|
121
|
+
}
|
|
122
|
+
];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function parse_string(prefix: string, mode: Mode): type_filters {
|
|
126
|
+
return [
|
|
127
|
+
{
|
|
128
|
+
path: prefix,
|
|
129
|
+
filter: z.union([
|
|
130
|
+
z.object({
|
|
131
|
+
$eq: z.string()
|
|
132
|
+
}),
|
|
133
|
+
z.object({
|
|
134
|
+
$in: z.array(z.string())
|
|
135
|
+
}),
|
|
136
|
+
z.object({
|
|
137
|
+
$nin: z.array(z.string())
|
|
138
|
+
}),
|
|
139
|
+
]).optional(),
|
|
140
|
+
sortable: true,
|
|
141
|
+
},
|
|
142
|
+
];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function parse_enum(definition: z.core.$ZodEnumDef, prefix: string, mode: Mode): type_filters {
|
|
146
|
+
return [
|
|
147
|
+
{
|
|
148
|
+
path: prefix,
|
|
149
|
+
filter: z.union([
|
|
150
|
+
z.object({
|
|
151
|
+
$eq: z.enum(definition.entries)
|
|
152
|
+
}),
|
|
153
|
+
z.object({
|
|
154
|
+
$in: z.array(z.enum(definition.entries))
|
|
155
|
+
}),
|
|
156
|
+
]).optional(),
|
|
157
|
+
sortable: true,
|
|
158
|
+
}
|
|
159
|
+
];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function parse_boolean(prefix: string, mode: Mode): type_filters {
|
|
163
|
+
return [{
|
|
164
|
+
path: prefix,
|
|
165
|
+
filter: z.object({
|
|
166
|
+
$eq: z.boolean()
|
|
167
|
+
}).optional(),
|
|
168
|
+
sortable: true,
|
|
169
|
+
}];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function parse_number(prefix: string, mode: Mode): type_filters {
|
|
173
|
+
return [{
|
|
174
|
+
path: prefix,
|
|
175
|
+
filter: z.union([
|
|
176
|
+
z.object({
|
|
177
|
+
$eq: z.number()
|
|
178
|
+
}),
|
|
179
|
+
z.object({
|
|
180
|
+
$gt: z.number()
|
|
181
|
+
}),
|
|
182
|
+
z.object({
|
|
183
|
+
$lt: z.number()
|
|
184
|
+
}),
|
|
185
|
+
z.object({
|
|
186
|
+
$gte: z.number()
|
|
187
|
+
}),
|
|
188
|
+
z.object({
|
|
189
|
+
$lte: z.number()
|
|
190
|
+
}),
|
|
191
|
+
]).optional(),
|
|
192
|
+
sortable: true,
|
|
193
|
+
}];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function parse_date(prefix: string, mode: Mode): type_filters {
|
|
197
|
+
let date_parser = mode === 'client' ? z.date() : z.coerce.date();
|
|
198
|
+
return [{
|
|
199
|
+
path: prefix,
|
|
200
|
+
filter: z.union([
|
|
201
|
+
z.object({
|
|
202
|
+
$eq: date_parser
|
|
203
|
+
}),
|
|
204
|
+
z.object({
|
|
205
|
+
$gt: date_parser
|
|
206
|
+
}),
|
|
207
|
+
z.object({
|
|
208
|
+
$lt: date_parser
|
|
209
|
+
}),
|
|
210
|
+
z.object({
|
|
211
|
+
$gte: date_parser
|
|
212
|
+
}),
|
|
213
|
+
z.object({
|
|
214
|
+
$lte: date_parser
|
|
215
|
+
}),
|
|
216
|
+
]).optional(),
|
|
217
|
+
sortable: true,
|
|
218
|
+
}];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
function parse_mongodb_id(prefix: string, mode: Mode): type_filters {
|
|
223
|
+
return [
|
|
224
|
+
{
|
|
225
|
+
path: prefix,
|
|
226
|
+
filter: z.union([
|
|
227
|
+
z.object({
|
|
228
|
+
$eq: z_mongodb_id
|
|
229
|
+
}),
|
|
230
|
+
z.object({
|
|
231
|
+
$gt: z_mongodb_id
|
|
232
|
+
}),
|
|
233
|
+
z.object({
|
|
234
|
+
$lt: z_mongodb_id
|
|
235
|
+
}),
|
|
236
|
+
z.object({
|
|
237
|
+
$gte: z_mongodb_id
|
|
238
|
+
}),
|
|
239
|
+
z.object({
|
|
240
|
+
$lte: z_mongodb_id
|
|
241
|
+
}),
|
|
242
|
+
z.object({
|
|
243
|
+
$in: z.array(z_mongodb_id)
|
|
244
|
+
}),
|
|
245
|
+
z.object({
|
|
246
|
+
$nin: z.array(z_mongodb_id)
|
|
247
|
+
}),
|
|
248
|
+
]).optional(),
|
|
249
|
+
sortable: true,
|
|
250
|
+
}
|
|
251
|
+
];
|
|
252
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { QueryWithHelpers } from "mongoose";
|
|
2
|
+
import escapeStringRegexp from 'escape-string-regexp';
|
|
2
3
|
|
|
3
4
|
export let complex_query_map = {
|
|
4
5
|
'_gt': '$gt',
|
|
@@ -12,7 +13,10 @@ export let complex_query_map = {
|
|
|
12
13
|
'_ends_with': (key, value) => {
|
|
13
14
|
return new RegExp(escapeRegExp(value) + '$')
|
|
14
15
|
},*/
|
|
15
|
-
'_in': '$in'
|
|
16
|
+
'_in': '$in',
|
|
17
|
+
'_search': (val: string) => {
|
|
18
|
+
return { $regex: new RegExp(escapeStringRegexp(val)), $options: 'i' }
|
|
19
|
+
}
|
|
16
20
|
}
|
|
17
21
|
|
|
18
22
|
export let query_meta_map = {
|
|
@@ -21,6 +25,7 @@ export let query_meta_map = {
|
|
|
21
25
|
'sort': true,
|
|
22
26
|
'sort_order': true,
|
|
23
27
|
//'projection': true,
|
|
28
|
+
'advanced_query': true,
|
|
24
29
|
}
|
|
25
30
|
|
|
26
31
|
export function convert_null(query_object: any){
|
|
@@ -32,7 +37,7 @@ export function convert_null(query_object: any){
|
|
|
32
37
|
return query_object;
|
|
33
38
|
}
|
|
34
39
|
|
|
35
|
-
export function query_object_to_mongodb_query(query_object:
|
|
40
|
+
export function query_object_to_mongodb_query(query_object: { [key: string]: string | null }){
|
|
36
41
|
let retval = {} as any;
|
|
37
42
|
|
|
38
43
|
for(let [key, value] of Object.entries(query_object)){
|
|
@@ -42,7 +47,10 @@ export function query_object_to_mongodb_query(query_object: any){
|
|
|
42
47
|
let modified_key = key.slice(0, -complex_suffix.length);
|
|
43
48
|
|
|
44
49
|
if (!retval[modified_key]) { retval[modified_key] = {} as any; }
|
|
45
|
-
retval[modified_key][complex_query_map[complex_suffix]] = value
|
|
50
|
+
if(typeof complex_query_map[complex_suffix] ==='string') { retval[modified_key][complex_query_map[complex_suffix]] = value }
|
|
51
|
+
else {
|
|
52
|
+
retval[modified_key] = complex_query_map[complex_suffix](value)
|
|
53
|
+
}
|
|
46
54
|
} else {
|
|
47
55
|
retval[key] = value;
|
|
48
56
|
}
|
|
@@ -2,6 +2,7 @@ import { z } from "zod/v4"
|
|
|
2
2
|
import { $ZodLooseShape } from "zod/v4/core";
|
|
3
3
|
import { z_mongodb_id, z_mongodb_id_nullable, z_mongodb_id_optional } from "./mongoose_from_zod.js";
|
|
4
4
|
import { find_loops, validator_group } from './zod_loop_seperator.js'
|
|
5
|
+
import { complex_query_validator_from_zod } from "./complex_query_validator_from_zod.js";
|
|
5
6
|
|
|
6
7
|
type type_filters = {
|
|
7
8
|
path: string,
|
|
@@ -16,7 +17,8 @@ export function query_validator_from_zod(zod_definition: z.ZodObject, mode: Mode
|
|
|
16
17
|
let retval = {
|
|
17
18
|
limit: z.coerce.number().int().optional(),
|
|
18
19
|
cursor: z_mongodb_id_optional,
|
|
19
|
-
sort_order: z.enum([/*'asc', 'desc', */'ascending', 'descending']).optional()
|
|
20
|
+
sort_order: z.enum([/*'asc', 'desc', */'ascending', 'descending']).optional(),
|
|
21
|
+
advanced_query: z.string().optional(),
|
|
20
22
|
} as $ZodLooseShape;
|
|
21
23
|
|
|
22
24
|
let object_filters = parse_object(zod_definition._zod.def, '', loops, mode);
|
|
@@ -139,6 +141,11 @@ function parse_string(prefix: string, mode: Mode): type_filters {
|
|
|
139
141
|
filter: z.string().optional(),
|
|
140
142
|
sortable: false,
|
|
141
143
|
},
|
|
144
|
+
{
|
|
145
|
+
path: prefix + '_search',
|
|
146
|
+
filter: z.string().optional(),
|
|
147
|
+
sortable: false,
|
|
148
|
+
},
|
|
142
149
|
{
|
|
143
150
|
path: prefix + '_in',
|
|
144
151
|
filter: array_parser.optional(),
|
|
@@ -29,6 +29,11 @@ function discover_loops(
|
|
|
29
29
|
console.error(zod_definition);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
if(!zod_definition._zod) {
|
|
33
|
+
console.error('ISSUE 2');
|
|
34
|
+
console.error(zod_definition);
|
|
35
|
+
}
|
|
36
|
+
|
|
32
37
|
switch (zod_definition._zod.def.type) {
|
|
33
38
|
case "object":
|
|
34
39
|
parse_object(zod_definition as z.ZodObject, validator_groups);
|
|
@@ -174,6 +174,24 @@ describe('query validator to mongodb query', function () {
|
|
|
174
174
|
)
|
|
175
175
|
});
|
|
176
176
|
|
|
177
|
+
it('should be able to transform search', async function () {
|
|
178
|
+
let query_validator = query_validator_from_zod(z.object({
|
|
179
|
+
param: z.string(),
|
|
180
|
+
}))
|
|
181
|
+
|
|
182
|
+
assert.deepEqual(
|
|
183
|
+
query_object_to_mongodb_query(query_validator.parse({
|
|
184
|
+
param_search: 'den'
|
|
185
|
+
})),
|
|
186
|
+
{
|
|
187
|
+
param: {
|
|
188
|
+
$regex: /den/,
|
|
189
|
+
$options: 'i'
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
)
|
|
193
|
+
});
|
|
194
|
+
|
|
177
195
|
it('should be able to discard controllers', async function () {
|
|
178
196
|
let query_validator = query_validator_from_zod(z.object({
|
|
179
197
|
param: z.number(),
|