@liminalfunctions/framework 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/.mocharc.json +5 -0
- package/dist/F_Client_Collection_Registry.d.ts +18 -0
- package/dist/F_Client_Collection_Registry.js +36 -0
- package/dist/F_Client_Collection_Registry.js.map +1 -0
- package/dist/F_Collection.d.ts +21 -0
- package/dist/F_Collection.js +36 -0
- package/dist/F_Collection.js.map +1 -0
- package/dist/F_Collection_Registry.d.ts +11 -0
- package/dist/F_Collection_Registry.js +18 -0
- package/dist/F_Collection_Registry.js.map +1 -0
- package/dist/F_Compile.d.ts +4 -0
- package/dist/F_Compile.js +298 -0
- package/dist/F_Compile.js.map +1 -0
- package/dist/F_Security_Models/F_SM_Open_Access.d.ts +11 -0
- package/dist/F_Security_Models/F_SM_Open_Access.js +14 -0
- package/dist/F_Security_Models/F_SM_Open_Access.js.map +1 -0
- package/dist/F_Security_Models/F_SM_Ownership.d.ts +12 -0
- package/dist/F_Security_Models/F_SM_Ownership.js +46 -0
- package/dist/F_Security_Models/F_SM_Ownership.js.map +1 -0
- package/dist/F_Security_Models/F_SM_Role_Membership.d.ts +19 -0
- package/dist/F_Security_Models/F_SM_Role_Membership.js +73 -0
- package/dist/F_Security_Models/F_SM_Role_Membership.js.map +1 -0
- package/dist/F_Security_Models/F_Security_Model.d.ts +41 -0
- package/dist/F_Security_Models/F_Security_Model.js +29 -0
- package/dist/F_Security_Models/F_Security_Model.js.map +1 -0
- package/dist/code_generation/generate_client_library.d.ts +4 -0
- package/dist/code_generation/generate_client_library.js +158 -0
- package/dist/code_generation/generate_client_library.js.map +1 -0
- package/dist/code_generation/templates/.gitignore.mustache +383 -0
- package/dist/code_generation/templates/collection.mustache +106 -0
- package/dist/code_generation/templates/main.mustache +24 -0
- package/dist/code_generation/templates/package.json.mustache +18 -0
- package/dist/code_generation/templates/tsconfig.json.mustache +14 -0
- package/dist/code_generation/templates/types.mustache +4 -0
- package/dist/code_generation/templates/utils.ts.mustache +17 -0
- package/dist/code_generation/utils/tab_indent.d.ts +1 -0
- package/dist/code_generation/utils/tab_indent.js +4 -0
- package/dist/code_generation/utils/tab_indent.js.map +1 -0
- package/dist/code_generation/utils/type_from_zod.d.ts +2 -0
- package/dist/code_generation/utils/type_from_zod.js +102 -0
- package/dist/code_generation/utils/type_from_zod.js.map +1 -0
- package/dist/utils/cache.d.ts +13 -0
- package/dist/utils/cache.js +101 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/mongoose_from_zod.d.ts +13 -0
- package/dist/utils/mongoose_from_zod.js +164 -0
- package/dist/utils/mongoose_from_zod.js.map +1 -0
- package/dist/utils/pretty_print_zod.d.ts +2 -0
- package/dist/utils/pretty_print_zod.js +63 -0
- package/dist/utils/pretty_print_zod.js.map +1 -0
- package/dist/utils/query_object_to_mongodb_query.d.ts +3 -0
- package/dist/utils/query_object_to_mongodb_query.js +61 -0
- package/dist/utils/query_object_to_mongodb_query.js.map +1 -0
- package/dist/utils/query_validator_from_zod.d.ts +6 -0
- package/dist/utils/query_validator_from_zod.js +216 -0
- package/dist/utils/query_validator_from_zod.js.map +1 -0
- package/package.json +36 -0
- package/src/F_Collection.ts +50 -0
- package/src/F_Collection_Registry.ts +29 -0
- package/src/F_Compile.ts +368 -0
- package/src/F_Security_Models/F_SM_Open_Access.ts +21 -0
- package/src/F_Security_Models/F_SM_Ownership.ts +72 -0
- package/src/F_Security_Models/F_SM_Role_Membership.ts +87 -0
- package/src/F_Security_Models/F_Security_Model.ts +85 -0
- package/src/code_generation/generate_client_library.ts +197 -0
- package/src/code_generation/templates/.gitignore.mustache +383 -0
- package/src/code_generation/templates/collection.mustache +106 -0
- package/src/code_generation/templates/main.mustache +24 -0
- package/src/code_generation/templates/package.json.mustache +18 -0
- package/src/code_generation/templates/tsconfig.json.mustache +14 -0
- package/src/code_generation/templates/types.mustache +4 -0
- package/src/code_generation/templates/utils.ts.mustache +17 -0
- package/src/code_generation/utils/tab_indent.ts +3 -0
- package/src/code_generation/utils/type_from_zod.ts +140 -0
- package/src/utils/cache.ts +149 -0
- package/src/utils/mongoose_from_zod.ts +191 -0
- package/src/utils/pretty_print_zod.ts +75 -0
- package/src/utils/query_object_to_mongodb_query.ts +73 -0
- package/src/utils/query_validator_from_zod.ts +246 -0
- package/test/0_0_mongoose_from_zod.test.ts +260 -0
- package/test/0_1_query_validator_from_zod.test.ts +518 -0
- package/test/0_2_query_validator_to_mongodb_query.test.ts +365 -0
- package/test/0_3_cache.test.ts +204 -0
- package/test/1_0_basic_server.test.ts +530 -0
- package/test/1_1_security_ownership.test.ts +328 -0
- package/test/1_2_role_membership.test.ts +731 -0
- package/test/2_0_client_library_basic_type_generation.test.ts +444 -0
- package/test/2_0_client_library_query_type_generation.test.ts +352 -0
- package/test/2_1_client_library_generation.test.ts +255 -0
- package/test/tmp/dist/Brief_News_Category.d.ts +16 -0
- package/test/tmp/dist/Brief_News_Category.js +85 -0
- package/test/tmp/dist/Brief_News_Category.js.map +1 -0
- package/test/tmp/dist/Client.d.ts +19 -0
- package/test/tmp/dist/Client.js +97 -0
- package/test/tmp/dist/Client.js.map +1 -0
- package/test/tmp/dist/Institution.d.ts +18 -0
- package/test/tmp/dist/Institution.js +94 -0
- package/test/tmp/dist/Institution.js.map +1 -0
- package/test/tmp/dist/Project.d.ts +16 -0
- package/test/tmp/dist/Project.js +85 -0
- package/test/tmp/dist/Project.js.map +1 -0
- package/test/tmp/dist/index.d.ts +4 -0
- package/test/tmp/dist/index.js +14 -0
- package/test/tmp/dist/index.js.map +1 -0
- package/test/tmp/dist/types/brief_news_category.d.ts +7 -0
- package/test/tmp/dist/types/brief_news_category.js +2 -0
- package/test/tmp/dist/types/brief_news_category.js.map +1 -0
- package/test/tmp/dist/types/brief_news_category_post.d.ts +7 -0
- package/test/tmp/dist/types/brief_news_category_post.js +2 -0
- package/test/tmp/dist/types/brief_news_category_post.js.map +1 -0
- package/test/tmp/dist/types/brief_news_category_put.d.ts +7 -0
- package/test/tmp/dist/types/brief_news_category_put.js +2 -0
- package/test/tmp/dist/types/brief_news_category_put.js.map +1 -0
- package/test/tmp/dist/types/brief_news_category_query.d.ts +26 -0
- package/test/tmp/dist/types/brief_news_category_query.js +2 -0
- package/test/tmp/dist/types/brief_news_category_query.js.map +1 -0
- package/test/tmp/dist/types/client.d.ts +5 -0
- package/test/tmp/dist/types/client.js +2 -0
- package/test/tmp/dist/types/client.js.map +1 -0
- package/test/tmp/dist/types/client_post.d.ts +5 -0
- package/test/tmp/dist/types/client_post.js +2 -0
- package/test/tmp/dist/types/client_post.js.map +1 -0
- package/test/tmp/dist/types/client_put.d.ts +5 -0
- package/test/tmp/dist/types/client_put.js +2 -0
- package/test/tmp/dist/types/client_put.js.map +1 -0
- package/test/tmp/dist/types/client_query.d.ts +18 -0
- package/test/tmp/dist/types/client_query.js +2 -0
- package/test/tmp/dist/types/client_query.js.map +1 -0
- package/test/tmp/dist/types/institution.d.ts +4 -0
- package/test/tmp/dist/types/institution.js +2 -0
- package/test/tmp/dist/types/institution.js.map +1 -0
- package/test/tmp/dist/types/institution_post.d.ts +4 -0
- package/test/tmp/dist/types/institution_post.js +2 -0
- package/test/tmp/dist/types/institution_post.js.map +1 -0
- package/test/tmp/dist/types/institution_put.d.ts +4 -0
- package/test/tmp/dist/types/institution_put.js +2 -0
- package/test/tmp/dist/types/institution_put.js.map +1 -0
- package/test/tmp/dist/types/institution_query.d.ts +14 -0
- package/test/tmp/dist/types/institution_query.js +2 -0
- package/test/tmp/dist/types/institution_query.js.map +1 -0
- package/test/tmp/dist/types/project.d.ts +7 -0
- package/test/tmp/dist/types/project.js +2 -0
- package/test/tmp/dist/types/project.js.map +1 -0
- package/test/tmp/dist/types/project_post.d.ts +7 -0
- package/test/tmp/dist/types/project_post.js +2 -0
- package/test/tmp/dist/types/project_post.js.map +1 -0
- package/test/tmp/dist/types/project_put.d.ts +7 -0
- package/test/tmp/dist/types/project_put.js +2 -0
- package/test/tmp/dist/types/project_put.js.map +1 -0
- package/test/tmp/dist/types/project_query.d.ts +27 -0
- package/test/tmp/dist/types/project_query.js +2 -0
- package/test/tmp/dist/types/project_query.js.map +1 -0
- package/test/tmp/dist/utils/utils.d.ts +11 -0
- package/test/tmp/dist/utils/utils.js +13 -0
- package/test/tmp/dist/utils/utils.js.map +1 -0
- package/test/tmp/package-lock.json +573 -0
- package/test/tmp/package.json +18 -0
- package/test/tmp/src/Brief_News_Category.ts +94 -0
- package/test/tmp/src/Client.ts +106 -0
- package/test/tmp/src/Institution.ts +103 -0
- package/test/tmp/src/Project.ts +94 -0
- package/test/tmp/src/index.ts +20 -0
- package/test/tmp/src/types/brief_news_category.ts +7 -0
- package/test/tmp/src/types/brief_news_category_post.ts +7 -0
- package/test/tmp/src/types/brief_news_category_put.ts +7 -0
- package/test/tmp/src/types/brief_news_category_query.ts +26 -0
- package/test/tmp/src/types/client.ts +5 -0
- package/test/tmp/src/types/client_post.ts +5 -0
- package/test/tmp/src/types/client_put.ts +5 -0
- package/test/tmp/src/types/client_query.ts +18 -0
- package/test/tmp/src/types/institution.ts +4 -0
- package/test/tmp/src/types/institution_post.ts +4 -0
- package/test/tmp/src/types/institution_put.ts +4 -0
- package/test/tmp/src/types/institution_query.ts +14 -0
- package/test/tmp/src/types/project.ts +7 -0
- package/test/tmp/src/types/project_post.ts +7 -0
- package/test/tmp/src/types/project_put.ts +7 -0
- package/test/tmp/src/types/project_query.ts +27 -0
- package/test/tmp/src/utils/utils.ts +17 -0
- package/test/tmp/tsconfig.json +14 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { QueryWithHelpers } from "mongoose";
|
|
2
|
+
|
|
3
|
+
let complex_query_map = {
|
|
4
|
+
'_gt': '$gt',
|
|
5
|
+
'_lt': '$lt',
|
|
6
|
+
'_gte': '$gte',
|
|
7
|
+
'_lte': '$lte',
|
|
8
|
+
/*'_ne': '$ne',
|
|
9
|
+
'_starts_with': (key, value) => {
|
|
10
|
+
return new RegExp('^' + escapeRegExp(value))
|
|
11
|
+
},
|
|
12
|
+
'_ends_with': (key, value) => {
|
|
13
|
+
return new RegExp(escapeRegExp(value) + '$')
|
|
14
|
+
},*/
|
|
15
|
+
'_in': '$in'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let query_meta_map = {
|
|
19
|
+
'limit': true,
|
|
20
|
+
'cursor': true,
|
|
21
|
+
'sort': true,
|
|
22
|
+
'sort_order': true,
|
|
23
|
+
//'projection': true,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function query_object_to_mongodb_query(query_object: any){
|
|
27
|
+
let retval = {} as any;
|
|
28
|
+
|
|
29
|
+
for(let [key, value] of Object.entries(query_object)){
|
|
30
|
+
if(Object.keys(query_meta_map).includes(key)){ continue; }
|
|
31
|
+
let complex_suffix = Object.keys(complex_query_map).find(ele => key.endsWith(ele)) as keyof typeof complex_query_map;
|
|
32
|
+
if (complex_suffix) {
|
|
33
|
+
let modified_key = key.slice(0, -complex_suffix.length);
|
|
34
|
+
|
|
35
|
+
if (!retval[modified_key]) { retval[modified_key] = {} as any; }
|
|
36
|
+
retval[modified_key][complex_query_map[complex_suffix]] = value;
|
|
37
|
+
} else {
|
|
38
|
+
retval[key] = value;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return retval;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function query_object_to_mongodb_limits(query: QueryWithHelpers<any, any>, query_object: any, max_limit = 100) {
|
|
46
|
+
if (query_object.sort) {
|
|
47
|
+
let sort = {} as any;
|
|
48
|
+
sort[query_object.sort] = query_object.sort_order ? query_object.sort_order : 'ascending';
|
|
49
|
+
query.sort(sort)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (query_object.sort_order && !query_object.sort) {
|
|
53
|
+
query.sort({ '_id': query_object.sort_order })
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (query_object.limit) {
|
|
57
|
+
query.limit(Math.min(Number.parseInt(query_object.limit), max_limit));
|
|
58
|
+
} else {
|
|
59
|
+
query.limit(max_limit);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (query_object.cursor) {
|
|
63
|
+
if (query_object.sort === '_id' && query_object.sort_order === 'descending') {
|
|
64
|
+
query.sort({ '_id': 'descending' });
|
|
65
|
+
query.lt('_id', query_object.cursor);
|
|
66
|
+
} else {
|
|
67
|
+
query.sort({ '_id': 'ascending' });
|
|
68
|
+
query.gt('_id', query_object.cursor);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return query;
|
|
73
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { z } from "zod/v4"
|
|
2
|
+
import { $ZodLooseShape } from "zod/v4/core";
|
|
3
|
+
import { magic_values, z_mongodb_id } from "./mongoose_from_zod.js";
|
|
4
|
+
|
|
5
|
+
type type_filters = {
|
|
6
|
+
path: string,
|
|
7
|
+
filter: z.ZodType,
|
|
8
|
+
sortable: boolean,
|
|
9
|
+
}[]
|
|
10
|
+
type Mode = 'client' | 'server'
|
|
11
|
+
|
|
12
|
+
export function query_validator_from_zod(zod_definition: z.ZodObject, mode: Mode = 'server'){
|
|
13
|
+
let retval = {
|
|
14
|
+
limit: z.coerce.number().int().optional(),
|
|
15
|
+
cursor: z_mongodb_id.optional(),
|
|
16
|
+
sort_order: z.enum([/*'asc', 'desc', */'ascending', 'descending']).optional()
|
|
17
|
+
} as $ZodLooseShape;
|
|
18
|
+
let object_filters = parse_object(zod_definition._zod.def, '', new Set(), mode);
|
|
19
|
+
for(let filter of object_filters){
|
|
20
|
+
retval[filter.path.slice(1)] = filter.filter;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
retval.sort = z.enum(object_filters.filter(ele => ele.sortable).map(ele => ele.path.slice(1))).optional()
|
|
24
|
+
|
|
25
|
+
return z.object(retval).strict();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function parse_any(zod_definition: z.ZodTypeAny, prefix: string, loop_detector: Set<any> = new Set(), mode: Mode = 'server'): type_filters {
|
|
29
|
+
switch (zod_definition._zod.def.type) {
|
|
30
|
+
case "enum":
|
|
31
|
+
return parse_enum(zod_definition._zod.def as z.core.$ZodEnumDef, prefix, mode);
|
|
32
|
+
case "string":
|
|
33
|
+
return parse_string(prefix, mode);
|
|
34
|
+
case "number":
|
|
35
|
+
case "int":
|
|
36
|
+
return parse_number(prefix, mode);
|
|
37
|
+
case "object":
|
|
38
|
+
return parse_object(zod_definition._zod.def as z.core.$ZodObjectDef, prefix, loop_detector, mode);
|
|
39
|
+
case "boolean":
|
|
40
|
+
return parse_boolean(prefix, mode);
|
|
41
|
+
case "date":
|
|
42
|
+
return parse_date(prefix, mode);
|
|
43
|
+
case "array":
|
|
44
|
+
return parse_array(zod_definition._zod.def as z.core.$ZodArrayDef, prefix, loop_detector, mode)
|
|
45
|
+
case "union":
|
|
46
|
+
return parse_union(zod_definition._zod.def as z.core.$ZodUnionDef, prefix, mode)
|
|
47
|
+
case "custom":
|
|
48
|
+
if(!magic_values.has(zod_definition)) {
|
|
49
|
+
throw new Error(`could not find custom parser in the magic value dictionary`)
|
|
50
|
+
}
|
|
51
|
+
let { override_type } = magic_values.get(zod_definition);
|
|
52
|
+
|
|
53
|
+
if(override_type === 'mongodb_id'){
|
|
54
|
+
return parse_mongodb_id(prefix, mode);
|
|
55
|
+
} else {
|
|
56
|
+
throw new Error(`could not find custom parser for ${override_type} in the magic value dictionary`)
|
|
57
|
+
}
|
|
58
|
+
case "default":
|
|
59
|
+
//@ts-ignore
|
|
60
|
+
return parse_any((zod_definition._zod.def as z.core.$ZodDefaultDef).innerType, prefix, loop_detector, mode)
|
|
61
|
+
default:
|
|
62
|
+
return []
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function parse_array(def: z.core.$ZodArrayDef, prefix: string, loop_detector: Set<any>, mode: Mode) {
|
|
67
|
+
let simple_children = ['enum', 'string', 'number', 'int', 'boolean']
|
|
68
|
+
if(simple_children.includes(def.element._zod.def.type)) {
|
|
69
|
+
//@ts-ignore
|
|
70
|
+
return parse_any(def.element, prefix, loop_detector, mode).filter(ele => ele.path == prefix)
|
|
71
|
+
} else if(def.element._zod.def.type === 'custom'){
|
|
72
|
+
if(!magic_values.has(def.element)) {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
let { override_type } = magic_values.get(def.element);
|
|
76
|
+
|
|
77
|
+
if(override_type === 'mongodb_id'){
|
|
78
|
+
return parse_mongodb_id(prefix, mode).filter(ele => ele.path == prefix);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function parse_object(def: z.core.$ZodObjectDef, prefix: string, loop_detector: Set<any>, mode: Mode): type_filters {
|
|
86
|
+
if(loop_detector.has(def)) {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
loop_detector.add(def);
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
let retval = [] as type_filters;
|
|
93
|
+
for(let [key, value] of Object.entries(def.shape)){
|
|
94
|
+
//@ts-ignore
|
|
95
|
+
let filters = parse_any(value, `${prefix}.${key}`, loop_detector, mode);
|
|
96
|
+
retval.push(...filters);
|
|
97
|
+
}
|
|
98
|
+
return retval;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function parse_union(def: z.core.$ZodUnionDef, prefix: string, mode: Mode): type_filters {
|
|
102
|
+
let simple_children = ['enum', 'string', 'number', 'int', 'boolean']
|
|
103
|
+
let filter_queue = def.options.slice().filter(ele => simple_children.includes(ele._zod.def.type));
|
|
104
|
+
let root = filter_queue.shift();
|
|
105
|
+
for(let filter of filter_queue){
|
|
106
|
+
//@ts-expect-error
|
|
107
|
+
root = root.or(filter);
|
|
108
|
+
}
|
|
109
|
+
return [
|
|
110
|
+
{
|
|
111
|
+
path: prefix,
|
|
112
|
+
//@ts-expect-error
|
|
113
|
+
filter: root,
|
|
114
|
+
sortable: true,
|
|
115
|
+
}
|
|
116
|
+
];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function parse_string(prefix: string, mode: Mode): type_filters {
|
|
120
|
+
let array_parser = mode === 'client' ? z.array(z.string()) : z.string().transform(val => val.split(',').filter(ele => ele.length > 0));
|
|
121
|
+
return [
|
|
122
|
+
{
|
|
123
|
+
path: prefix,
|
|
124
|
+
filter: z.string().optional(),
|
|
125
|
+
sortable: true,
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
path: prefix + '_gt',
|
|
129
|
+
filter: z.string().optional(),
|
|
130
|
+
sortable: false,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
path: prefix + '_lt',
|
|
134
|
+
filter: z.string().optional(),
|
|
135
|
+
sortable: false,
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
path: prefix + '_in',
|
|
139
|
+
filter: array_parser.optional(),
|
|
140
|
+
sortable: false,
|
|
141
|
+
},
|
|
142
|
+
];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function parse_enum(definition: z.core.$ZodEnumDef, prefix: string, mode: Mode): type_filters {
|
|
146
|
+
// TODO: figure out how to validate on the server such that it's a member of the enu,
|
|
147
|
+
let array_parser = mode === 'client' ? z.array(z.enum(definition.entries)) : z.string().transform(val => val.split(',').filter(ele => ele.length > 0));
|
|
148
|
+
return [
|
|
149
|
+
{
|
|
150
|
+
path: prefix,
|
|
151
|
+
filter: z.enum(definition.entries).optional(),
|
|
152
|
+
sortable: true,
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
path: prefix + '_in',
|
|
156
|
+
filter: array_parser.optional(),
|
|
157
|
+
sortable: false,
|
|
158
|
+
},
|
|
159
|
+
];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function parse_boolean(prefix: string, mode: Mode): type_filters {
|
|
163
|
+
let boolean_parser = mode === 'client' ? z.boolean() : z.stringbool();
|
|
164
|
+
return [{
|
|
165
|
+
path: prefix,
|
|
166
|
+
filter: boolean_parser.optional(),
|
|
167
|
+
sortable: true,
|
|
168
|
+
}];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function parse_number(prefix: string, mode: Mode): type_filters {
|
|
172
|
+
let number_parser = mode === 'client' ? z.number() : z.coerce.number();
|
|
173
|
+
return [
|
|
174
|
+
{
|
|
175
|
+
path: prefix,
|
|
176
|
+
filter: number_parser.optional(),
|
|
177
|
+
sortable: true,
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
path: prefix + '_gt',
|
|
181
|
+
filter: number_parser.optional(),
|
|
182
|
+
sortable: false,
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
path: prefix + '_gte',
|
|
186
|
+
filter: number_parser.optional(),
|
|
187
|
+
sortable: false,
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
path: prefix + '_lt',
|
|
191
|
+
filter: number_parser.optional(),
|
|
192
|
+
sortable: false,
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
path: prefix + '_lte',
|
|
196
|
+
filter: number_parser.optional(),
|
|
197
|
+
sortable: false,
|
|
198
|
+
},
|
|
199
|
+
];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function parse_date(prefix: string, mode: Mode): type_filters {
|
|
203
|
+
let date_parser = mode === 'client' ? z.date() : z.coerce.date();
|
|
204
|
+
return [{
|
|
205
|
+
path: prefix,
|
|
206
|
+
filter: date_parser.optional(),
|
|
207
|
+
sortable: true,
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
path: prefix + '_gt',
|
|
211
|
+
filter: date_parser.optional(),
|
|
212
|
+
sortable: false,
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
path: prefix + '_lt',
|
|
216
|
+
filter: date_parser.optional(),
|
|
217
|
+
sortable: false,
|
|
218
|
+
}];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
function parse_mongodb_id(prefix: string, mode: Mode): type_filters {
|
|
223
|
+
let array_parser = mode === 'client' ? z.array(z_mongodb_id) :z.string().transform(val => val.split(',').filter(ele => ele.length > 0));
|
|
224
|
+
return [
|
|
225
|
+
{
|
|
226
|
+
path: prefix,
|
|
227
|
+
filter: z_mongodb_id.optional(),
|
|
228
|
+
sortable: true,
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
path: prefix + '_gt',
|
|
232
|
+
filter: z_mongodb_id.optional(),
|
|
233
|
+
sortable: false,
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
path: prefix + '_lt',
|
|
237
|
+
filter: z_mongodb_id.optional(),
|
|
238
|
+
sortable: false,
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
path: prefix + '_in',
|
|
242
|
+
filter: array_parser.optional(),
|
|
243
|
+
sortable: false,
|
|
244
|
+
},
|
|
245
|
+
];
|
|
246
|
+
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import assert from "assert";
|
|
2
|
+
import { z, ZodBoolean, ZodDate, ZodNumber, ZodString } from 'zod'
|
|
3
|
+
|
|
4
|
+
import { schema_from_zod, z_mongodb_id } from '../dist/utils/mongoose_from_zod.js';
|
|
5
|
+
import { Schema } from 'mongoose'
|
|
6
|
+
import { required } from "zod/mini";
|
|
7
|
+
|
|
8
|
+
describe('Mongoose from Zod', function () {
|
|
9
|
+
|
|
10
|
+
it('should convert an empty object', function () {
|
|
11
|
+
let zodSchema = z.object({ })
|
|
12
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
13
|
+
assert.deepEqual({}, mongooseSchema)
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
let basic_types = [{
|
|
17
|
+
label: 'string',
|
|
18
|
+
mongoose_type: String,
|
|
19
|
+
zod_function: z.string,
|
|
20
|
+
default_val: 'todd',
|
|
21
|
+
}, {
|
|
22
|
+
label: 'number',
|
|
23
|
+
mongoose_type: Number,
|
|
24
|
+
zod_function: z.number,
|
|
25
|
+
default_val: 42,
|
|
26
|
+
}, {
|
|
27
|
+
label: 'boolean',
|
|
28
|
+
mongoose_type: Boolean,
|
|
29
|
+
zod_function: z.boolean,
|
|
30
|
+
default_val: true,
|
|
31
|
+
}, {
|
|
32
|
+
label: 'date',
|
|
33
|
+
mongoose_type: Date,
|
|
34
|
+
zod_function: z.date,
|
|
35
|
+
default_val: new Date().toISOString(),
|
|
36
|
+
}, {
|
|
37
|
+
label: 'objectid',
|
|
38
|
+
mongoose_type: Schema.Types.ObjectId,
|
|
39
|
+
zod_function: () => z_mongodb_id,
|
|
40
|
+
default_val: '65add375755c5e0415b69803',
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
/*
|
|
45
|
+
Basic type conversions
|
|
46
|
+
*/
|
|
47
|
+
for(let basic_type of basic_types) {
|
|
48
|
+
it(`should convert ${basic_type.label} to mongoose type`, function () {
|
|
49
|
+
let zodSchema = z.object({ test_value: basic_type.zod_function() })
|
|
50
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
51
|
+
assert.deepEqual({ test_value: { type: basic_type.mongoose_type, required: true } }, mongooseSchema)
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it(`should convert ${basic_type.label} to mongoose type with optional`, function () {
|
|
55
|
+
let zodSchema = z.object({ test_value: basic_type.zod_function().optional() })
|
|
56
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
57
|
+
assert.deepEqual({ test_value: { type: basic_type.mongoose_type, required: false } }, mongooseSchema)
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it(`should convert ${basic_type.label} to mongoose type with default values`, function () {
|
|
61
|
+
//@ts-ignore
|
|
62
|
+
let zodSchema = z.object({ test_value: basic_type.zod_function().default(basic_type.default_val) })
|
|
63
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
64
|
+
assert.deepEqual({ test_value: { type: basic_type.mongoose_type, required: true, default: basic_type.default_val } }, mongooseSchema)
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it(`should convert ${basic_type.label} to mongoose type with nullable`, function () {
|
|
68
|
+
let zodSchema = z.object({ test_value: basic_type.zod_function().nullable() })
|
|
69
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
70
|
+
assert.deepEqual({ test_value: { type: basic_type.mongoose_type, required: true } }, mongooseSchema)
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it(`should convert ${basic_type.label} to mongoose type with optional AND default values`, function () {
|
|
74
|
+
//@ts-ignore
|
|
75
|
+
let zodSchema = z.object({ test_value: basic_type.zod_function().default(basic_type.default_val).optional() })
|
|
76
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
77
|
+
assert.deepEqual({ test_value: { type: basic_type.mongoose_type, required: false, default: basic_type.default_val } }, mongooseSchema)
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/*
|
|
82
|
+
nested type conversions
|
|
83
|
+
*/
|
|
84
|
+
for(let basic_type of basic_types) {
|
|
85
|
+
it(`should convert a nested object containing a ${basic_type.label} property to mongoose type`, function () {
|
|
86
|
+
let zodSchema = z.object({ test_value: z.object({test_value: basic_type.zod_function() }) })
|
|
87
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
88
|
+
assert.deepEqual({ test_value: { type: {test_value: { type: basic_type.mongoose_type, required: true }}, required: true} }, mongooseSchema)
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it(`should convert a nested object containing a ${basic_type.label} property to mongoose type with optional`, function () {
|
|
92
|
+
let zodSchema = z.object({ test_value: z.object({test_value: basic_type.zod_function().optional() }) })
|
|
93
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
94
|
+
assert.deepEqual({ test_value: { type: {test_value: { type: basic_type.mongoose_type, required: false }}, required: true } }, mongooseSchema)
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it(`should convert a nested object containing a ${basic_type.label} property to mongoose type with default values`, function () {
|
|
98
|
+
//@ts-ignore
|
|
99
|
+
let zodSchema = z.object({ test_value: z.object({test_value: basic_type.zod_function().default(basic_type.default_val) }) })
|
|
100
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
101
|
+
assert.deepEqual({ test_value: { type: {test_value: { type: basic_type.mongoose_type, required: true, default: basic_type.default_val }}, required: true } }, mongooseSchema)
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/*
|
|
106
|
+
array type conversions
|
|
107
|
+
*/
|
|
108
|
+
for(let basic_type of basic_types) {
|
|
109
|
+
it(`should convert ${basic_type.label} array to mongoose type`, function () {
|
|
110
|
+
let zodSchema = z.object({ test_value: z.array(basic_type.zod_function()) })
|
|
111
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
112
|
+
assert.deepEqual({ test_value: {type: [{ type: basic_type.mongoose_type, required: true }], required: true} }, mongooseSchema)
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it(`should convert ${basic_type.label} array to mongoose type with optional`, function () {
|
|
116
|
+
let zodSchema = z.object({ test_value: z.array(basic_type.zod_function()).optional() })
|
|
117
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
118
|
+
assert.deepEqual({ test_value: {type: [{ type: basic_type.mongoose_type, required: true }], required: false} }, mongooseSchema)
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it(`should convert ${basic_type.label} array to mongoose type with default values`, function () {
|
|
122
|
+
let zodSchema = z.object({ test_value: z.array(basic_type.zod_function()).default([]) })
|
|
123
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
124
|
+
assert.deepEqual({ test_value: {type: [{ type: basic_type.mongoose_type, required: true }], required: true, default: []} }, mongooseSchema)
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/*
|
|
129
|
+
array inside an object conversions
|
|
130
|
+
*/
|
|
131
|
+
for(let basic_type of basic_types) {
|
|
132
|
+
it(`should convert ${basic_type.label} array to mongoose type`, function () {
|
|
133
|
+
let zodSchema = z.object({ test_value: z.array(basic_type.zod_function()) })
|
|
134
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
135
|
+
assert.deepEqual({ test_value: {type: [{ type: basic_type.mongoose_type, required: true }], required: true} }, mongooseSchema)
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it(`should convert ${basic_type.label} array to mongoose type with optional`, function () {
|
|
139
|
+
let zodSchema = z.object({ test_value: z.array(basic_type.zod_function()).optional() })
|
|
140
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
141
|
+
assert.deepEqual({ test_value: {type: [{ type: basic_type.mongoose_type, required: true }], required: false} }, mongooseSchema)
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it(`should convert ${basic_type.label} array to mongoose type with default values`, function () {
|
|
145
|
+
let zodSchema = z.object({ test_value: z.array(basic_type.zod_function()).default([]) })
|
|
146
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
147
|
+
assert.deepEqual({ test_value: {type: [{ type: basic_type.mongoose_type, required: true }], required: true, default: []} }, mongooseSchema)
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/*
|
|
152
|
+
map conversions
|
|
153
|
+
*/
|
|
154
|
+
for(let basic_type of basic_types) {
|
|
155
|
+
it(`should convert map of ${basic_type.label} to mongoose type`, function () {
|
|
156
|
+
let zodSchema = z.object({ test_value: z.map(z.string(), basic_type.zod_function()) })
|
|
157
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
158
|
+
assert.deepEqual({ test_value: {type: Schema.Types.Map, of: { type: basic_type.mongoose_type, required: true }, required: true} }, mongooseSchema)
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it(`should convert map of ${basic_type.label} to mongoose type with optional`, function () {
|
|
162
|
+
let zodSchema = z.object({ test_value: z.map(z.string(), basic_type.zod_function()).optional() })
|
|
163
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
164
|
+
assert.deepEqual({ test_value: {type: Schema.Types.Map, of: { type: basic_type.mongoose_type, required: true }, required: false} }, mongooseSchema)
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it(`should convert map of ${basic_type.label} to mongoose type with default values`, function () {
|
|
168
|
+
//@ts-ignore
|
|
169
|
+
let zodSchema = z.object({ test_value: z.map(z.string(), basic_type.zod_function()).default({}) })
|
|
170
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
171
|
+
assert.deepEqual({ test_value: {type: Schema.Types.Map, of: { type: basic_type.mongoose_type, required: true }, required: true, default: {}} }, mongooseSchema)
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/*
|
|
176
|
+
enum conversions
|
|
177
|
+
*/
|
|
178
|
+
|
|
179
|
+
it(`should convert enums to mongoose type`, function () {
|
|
180
|
+
let zodSchema = z.object({ test_value: z.enum(['chunky', 'funky', 'monkey']) })
|
|
181
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
182
|
+
assert.deepEqual({ test_value: {type: String, required: true} }, mongooseSchema)
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it(`should convert enums to mongoose type with optional`, function () {
|
|
186
|
+
let zodSchema = z.object({ test_value: z.enum(['chunky', 'funky', 'monkey']).optional() })
|
|
187
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
188
|
+
assert.deepEqual({ test_value: {type: String, required: false} }, mongooseSchema)
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it(`should convert enums to mongoose type with default values`, function () {
|
|
192
|
+
let zodSchema = z.object({ test_value: z.enum(['chunky', 'funky', 'monkey']).default('chunky') })
|
|
193
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
194
|
+
assert.deepEqual({ test_value: {type: String, required: true, default: 'chunky'} }, mongooseSchema)
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it(`should convert union types to a mixed schema`, function () {
|
|
198
|
+
let zodSchema = z.object({
|
|
199
|
+
test_value: z.number().or(z.string())
|
|
200
|
+
})
|
|
201
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
202
|
+
assert.deepEqual({ test_value: {type: Schema.Types.Mixed, required: true} }, mongooseSchema)
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it(`should convert union types to a mixed schema with default values`, function () {
|
|
206
|
+
let zodSchema = z.object({
|
|
207
|
+
test_value: z.number().or(z.string()).default(2)
|
|
208
|
+
})
|
|
209
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
210
|
+
assert.deepEqual({ test_value: {type: Schema.Types.Mixed, required: true, default: 2} }, mongooseSchema)
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it(`should convert union types to a mixed schema with optional`, function () {
|
|
214
|
+
let zodSchema = z.object({
|
|
215
|
+
test_value: z.number().or(z.string()).optional()
|
|
216
|
+
})
|
|
217
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
218
|
+
assert.deepEqual({ test_value: {type: Schema.Types.Mixed, required: false} }, mongooseSchema)
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it(`should convert recursive schemas`, function () {
|
|
222
|
+
let zodSchema = z.object({
|
|
223
|
+
type: z.enum(['group']),
|
|
224
|
+
operator: z.enum(['all', 'any']),
|
|
225
|
+
get children() {
|
|
226
|
+
return z.array(zodSchema)
|
|
227
|
+
},
|
|
228
|
+
locked: z.boolean().optional()
|
|
229
|
+
})
|
|
230
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
231
|
+
|
|
232
|
+
assert.deepEqual({
|
|
233
|
+
type: {type: String, required: true },
|
|
234
|
+
operator: {type: String, required: true },
|
|
235
|
+
children: {type: [{ type: Schema.Types.Mixed, required: true }], required: true },
|
|
236
|
+
locked: {type: Boolean, required: false },
|
|
237
|
+
}, mongooseSchema)
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
it(`should not treat similar schemas as recursive`, function () {
|
|
241
|
+
let zodSchema = z.object({
|
|
242
|
+
a: z.object({
|
|
243
|
+
name: z.string()
|
|
244
|
+
}),
|
|
245
|
+
b: z.object({
|
|
246
|
+
name: z.string()
|
|
247
|
+
}),
|
|
248
|
+
c: z.object({
|
|
249
|
+
name: z.string()
|
|
250
|
+
}),
|
|
251
|
+
})
|
|
252
|
+
let mongooseSchema = schema_from_zod(zodSchema)
|
|
253
|
+
|
|
254
|
+
assert.deepEqual({
|
|
255
|
+
a: { type: { name: { type: String, required: true } }, required: true },
|
|
256
|
+
b: { type: { name: { type: String, required: true } }, required: true },
|
|
257
|
+
c: { type: { name: { type: String, required: true } }, required: true },
|
|
258
|
+
}, mongooseSchema)
|
|
259
|
+
})
|
|
260
|
+
});
|