ajoq 0.0.2-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/.prettierrc +7 -0
- package/LICENSE +21 -0
- package/README.md +7 -0
- package/build/index.cjs +116 -0
- package/build/index.cjs.map +1 -0
- package/build/index.d.ts +35 -0
- package/build/index.js +89 -0
- package/build/index.js.map +1 -0
- package/package.json +25 -0
- package/src/index.ts +131 -0
- package/tsconfig.json +22 -0
package/.prettierrc
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019-2024 Ivan Zakharchanka
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
package/build/index.cjs
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: all[name]
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
SORT_EXPR: function() {
|
|
13
|
+
return SORT_EXPR;
|
|
14
|
+
},
|
|
15
|
+
SORT_MAP: function() {
|
|
16
|
+
return SORT_MAP;
|
|
17
|
+
},
|
|
18
|
+
createQuery: function() {
|
|
19
|
+
return createQuery;
|
|
20
|
+
},
|
|
21
|
+
createSort: function() {
|
|
22
|
+
return createSort;
|
|
23
|
+
},
|
|
24
|
+
sortCodeGen: function() {
|
|
25
|
+
return sortCodeGen;
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
const conditions = {
|
|
29
|
+
$eq: (valuePath, value)=>`(${valuePath} === ${value})`,
|
|
30
|
+
$ne: (valuePath, value)=>`(${valuePath} !== ${value})`,
|
|
31
|
+
$gt: (valuePath, value)=>`(${valuePath} > ${value})`,
|
|
32
|
+
$gte: (valuePath, value)=>`(${valuePath} >= ${value})`,
|
|
33
|
+
$lt: (valuePath, value)=>`(${valuePath} < ${value})`,
|
|
34
|
+
$lte: (valuePath, value)=>`(${valuePath} <= ${value})`,
|
|
35
|
+
$in: (valuePath, value)=>`(${value}.includes(${valuePath}))`,
|
|
36
|
+
$nin: (valuePath, value)=>`(!${value}.includes(${valuePath}))`,
|
|
37
|
+
$exists: (valuePath, value)=>`((${valuePath} !== undefined && ${valuePath} !== null) === ${value})`,
|
|
38
|
+
$match: (valuePath, value)=>`${value}.test('' + ${valuePath})`,
|
|
39
|
+
$nmatch: (valuePath, value)=>`!${value}.test('' + ${valuePath})`,
|
|
40
|
+
$bits: (valuePath, value)=>`((${valuePath} & ${value}) !== 0)`,
|
|
41
|
+
$nbits: (valuePath, value)=>`((${valuePath} & ${value}) === 0)`
|
|
42
|
+
};
|
|
43
|
+
const operations = {
|
|
44
|
+
$and: (value)=>`(${value.join(' && ')})`,
|
|
45
|
+
$or: (value)=>`(${value.join(' || ')})`,
|
|
46
|
+
$nor: (value)=>`(!(${value.join(' || ')}))`,
|
|
47
|
+
$not: (value)=>`(!(${value}))`
|
|
48
|
+
};
|
|
49
|
+
const queryCodeGen = (query, valuePath, path)=>{
|
|
50
|
+
if (typeof query === 'object' && query !== null) {
|
|
51
|
+
const condition = Object.entries(query).map(([key, value])=>{
|
|
52
|
+
if (key in conditions) {
|
|
53
|
+
return conditions[key](valuePath, JSON.stringify(value));
|
|
54
|
+
}
|
|
55
|
+
if (key in operations) {
|
|
56
|
+
const values = Array.isArray(value) ? value : [
|
|
57
|
+
value
|
|
58
|
+
];
|
|
59
|
+
return operations[key](values.map((value)=>queryCodeGen(value, valuePath, path)));
|
|
60
|
+
}
|
|
61
|
+
const valueSubPath = `${valuePath}?.[${JSON.stringify(key)}]`;
|
|
62
|
+
if (value instanceof RegExp) {
|
|
63
|
+
return conditions.$match(valueSubPath, value.toString());
|
|
64
|
+
}
|
|
65
|
+
if (typeof value === 'object' && value !== null) {
|
|
66
|
+
return queryCodeGen(value, valueSubPath, path);
|
|
67
|
+
}
|
|
68
|
+
return conditions.$eq(valueSubPath, JSON.stringify(value));
|
|
69
|
+
});
|
|
70
|
+
return `${condition.join(' && ')}`;
|
|
71
|
+
}
|
|
72
|
+
return 'true';
|
|
73
|
+
};
|
|
74
|
+
const createQuery = (query, rootName)=>{
|
|
75
|
+
const code = queryCodeGen(query, 'data', rootName);
|
|
76
|
+
const filter = new Function('data', `return ${code || 'true'};`);
|
|
77
|
+
return (data)=>filter(data);
|
|
78
|
+
};
|
|
79
|
+
const SORT_EXPR = /^(asc|desc|1|-1)$/i;
|
|
80
|
+
const SORT_MAP = {
|
|
81
|
+
asc: 1,
|
|
82
|
+
desc: -1,
|
|
83
|
+
1: 1,
|
|
84
|
+
'-1': -1
|
|
85
|
+
};
|
|
86
|
+
const sortCodeGen = (sort, valuePath, path)=>{
|
|
87
|
+
if (typeof sort === 'object' && sort !== null) {
|
|
88
|
+
const condition = Object.entries(sort).map(([key, value])=>{
|
|
89
|
+
const valueSubPathA = `${valuePath[0]}?.[${JSON.stringify(key)}]`;
|
|
90
|
+
const valueSubPathB = `${valuePath[1]}?.[${JSON.stringify(key)}]`;
|
|
91
|
+
return sortCodeGen(value, [
|
|
92
|
+
valueSubPathA,
|
|
93
|
+
valueSubPathB
|
|
94
|
+
], path);
|
|
95
|
+
});
|
|
96
|
+
return condition.join(' || ');
|
|
97
|
+
}
|
|
98
|
+
if (typeof sort === 'string' && SORT_EXPR.test(sort)) {
|
|
99
|
+
return `(${valuePath[0]}?.localeCompare(${valuePath[1]}) * ${SORT_MAP[sort]})`;
|
|
100
|
+
}
|
|
101
|
+
if (typeof sort === 'number' && SORT_EXPR.test('' + sort)) {
|
|
102
|
+
return `((${valuePath[0]} - ${valuePath[1]}) * ${SORT_MAP[sort]})`;
|
|
103
|
+
}
|
|
104
|
+
return `0`;
|
|
105
|
+
};
|
|
106
|
+
const createSort = (sort, rootName)=>{
|
|
107
|
+
const code = sortCodeGen(sort, [
|
|
108
|
+
'a',
|
|
109
|
+
'b'
|
|
110
|
+
], rootName);
|
|
111
|
+
const filter = new Function('a', 'b', `return ${code || 0};`);
|
|
112
|
+
console.log(filter.toString());
|
|
113
|
+
return (data)=>filter(data);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type Query<T> = {\n [P in keyof T]?: QueryCondition<T[P]>;\n} & Operations<T>;\n\ntype QueryCondition<FieldType> =\n | FieldType\n | Conditions<FieldType>;\n\ninterface Conditions<ValueType> {\n $eq?: ValueType;\n $ne?: ValueType;\n $gt?: ValueType;\n $gte?: ValueType;\n $lt?: ValueType;\n $lte?: ValueType;\n $in?: ValueType[];\n $nin?: ValueType[];\n $exists?: boolean;\n $match?: RegExp | string;\n $nmatch?: RegExp | string;\n $bits?: number;\n $nbits?: number;\n};\n\ntype ConditionKeys = keyof Conditions<unknown>;\n\ninterface Operations<T> {\n $and?: Query<T>[];\n $or?: Query<T>[];\n $nor?: Query<T>[];\n $not?: Query<T>;\n};\n\ntype OperationKeys = keyof Operations<unknown>;\n\nconst conditions: Record<ConditionKeys, any> = {\n $eq: (valuePath: string, value: unknown) => `(${valuePath} === ${value})`,\n $ne: (valuePath: string, value: unknown) => `(${valuePath} !== ${value})`,\n $gt: (valuePath: string, value: unknown) => `(${valuePath} > ${value})`,\n $gte: (valuePath: string, value: unknown) => `(${valuePath} >= ${value})`,\n $lt: (valuePath: string, value: unknown) => `(${valuePath} < ${value})`,\n $lte: (valuePath: string, value: unknown) => `(${valuePath} <= ${value})`,\n $in: (valuePath: string, value: unknown) => `(${value}.includes(${valuePath}))`,\n $nin: (valuePath: string, value: unknown) => `(!${value}.includes(${valuePath}))`,\n $exists: (valuePath: string, value: unknown) => `((${valuePath} !== undefined && ${valuePath} !== null) === ${value})`,\n $match: (valuePath: string, value: unknown) => `${value}.test('' + ${valuePath})`,\n $nmatch: (valuePath: string, value: unknown) => `!${value}.test('' + ${valuePath})`,\n $bits: (valuePath: string, value: unknown) => `((${valuePath} & ${value}) !== 0)`,\n $nbits: (valuePath: string, value: unknown) => `((${valuePath} & ${value}) === 0)`,\n};\n\nconst operations: Record<keyof Operations<unknown>, any> = {\n $and: (value: unknown[]) => `(${value.join(' && ')})`,\n $or: (value: unknown[]) => `(${value.join(' || ')})`,\n $nor: (value: unknown[]) => `(!(${value.join(' || ')}))`,\n $not: (value: unknown) => `(!(${value}))`,\n}\n\n\nconst queryCodeGen = <T extends object>(query: Query<T>, valuePath: string, path: string): string => {\n if (typeof query === 'object' && query !== null) {\n const condition = Object.entries(query).map(([key, value]) => {\n if (key in conditions) {\n return conditions[key as ConditionKeys](valuePath, JSON.stringify(value));\n }\n if (key in operations) {\n const values = Array.isArray(value) ? value : [value];\n return operations[key as OperationKeys](values.map((value) => queryCodeGen(value, valuePath, path)));\n }\n const valueSubPath = `${valuePath}?.[${JSON.stringify(key)}]`;\n if (value instanceof RegExp) {\n return conditions.$match(valueSubPath, value.toString());\n }\n if (typeof value === 'object' && value !== null) {\n return queryCodeGen(value, valueSubPath, path);\n }\n return conditions.$eq(valueSubPath, JSON.stringify(value));\n });\n\n return `${condition.join(' && ')}`;\n }\n return 'true';\n}\n\nexport const createQuery = <T extends object>(query: Query<T>, rootName: string) => {\n const code = queryCodeGen(query, 'data', rootName);\n const filter = new Function('data', `return ${code || 'true'};`);\n return (data: T) => filter(data);\n};\n\nexport type SortType = 'asc' | 'desc' | 1 | -1;\n\nexport type Sort<T> = T extends object\n ? {\n [K in keyof T]?: T[K] extends object ? Sort<T[K]> : SortType;\n }\n : SortType;\n\nexport const SORT_EXPR = /^(asc|desc|1|-1)$/i\n\nexport const SORT_MAP: Record<SortType, 1 | -1> = {\n asc: 1,\n desc: -1,\n 1: 1,\n '-1': -1,\n};\n\nexport const sortCodeGen = <T>(sort: Sort<T>, valuePath: [string, string], path: string): string => {\n if (typeof sort === 'object' && sort !== null) {\n const condition = Object.entries(sort).map(([key, value]) => {\n const valueSubPathA = `${valuePath[0]}?.[${JSON.stringify(key)}]`;\n const valueSubPathB = `${valuePath[1]}?.[${JSON.stringify(key)}]`;\n return sortCodeGen(value, [valueSubPathA, valueSubPathB], path);\n });\n return condition.join(' || ');\n }\n if (typeof sort === 'string' && SORT_EXPR.test(sort)) {\n return `(${valuePath[0]}?.localeCompare(${valuePath[1]}) * ${SORT_MAP[sort as SortType]})`;\n }\n if (typeof sort === 'number' && SORT_EXPR.test('' + sort)) {\n return `((${valuePath[0]} - ${valuePath[1]}) * ${SORT_MAP[sort as SortType]})`;\n }\n return `0`;\n};\n\nexport const createSort = <T>(sort: Sort<T>, rootName: string) => {\n const code = sortCodeGen(sort, ['a', 'b'], rootName);\n const filter = new Function('a', 'b', `return ${code || 0};`);\n console.log(filter.toString());\n return (data: T) => filter(data);\n};\n"],"names":["SORT_EXPR","SORT_MAP","createQuery","createSort","sortCodeGen","conditions","$eq","valuePath","value","$ne","$gt","$gte","$lt","$lte","$in","$nin","$exists","$match","$nmatch","$bits","$nbits","operations","$and","join","$or","$nor","$not","queryCodeGen","query","path","condition","Object","entries","map","key","JSON","stringify","values","Array","isArray","valueSubPath","RegExp","toString","rootName","code","filter","Function","data","asc","desc","sort","valueSubPathA","valueSubPathB","test","console","log"],"mappings":"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"}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export type Query<T> = {
|
|
2
|
+
[P in keyof T]?: QueryCondition<T[P]>;
|
|
3
|
+
} & Operations<T>;
|
|
4
|
+
type QueryCondition<FieldType> = FieldType | Conditions<FieldType>;
|
|
5
|
+
interface Conditions<ValueType> {
|
|
6
|
+
$eq?: ValueType;
|
|
7
|
+
$ne?: ValueType;
|
|
8
|
+
$gt?: ValueType;
|
|
9
|
+
$gte?: ValueType;
|
|
10
|
+
$lt?: ValueType;
|
|
11
|
+
$lte?: ValueType;
|
|
12
|
+
$in?: ValueType[];
|
|
13
|
+
$nin?: ValueType[];
|
|
14
|
+
$exists?: boolean;
|
|
15
|
+
$match?: RegExp | string;
|
|
16
|
+
$nmatch?: RegExp | string;
|
|
17
|
+
$bits?: number;
|
|
18
|
+
$nbits?: number;
|
|
19
|
+
}
|
|
20
|
+
interface Operations<T> {
|
|
21
|
+
$and?: Query<T>[];
|
|
22
|
+
$or?: Query<T>[];
|
|
23
|
+
$nor?: Query<T>[];
|
|
24
|
+
$not?: Query<T>;
|
|
25
|
+
}
|
|
26
|
+
export declare const createQuery: <T extends object>(query: Query<T>, rootName: string) => (data: T) => any;
|
|
27
|
+
export type SortType = 'asc' | 'desc' | 1 | -1;
|
|
28
|
+
export type Sort<T> = T extends object ? {
|
|
29
|
+
[K in keyof T]?: T[K] extends object ? Sort<T[K]> : SortType;
|
|
30
|
+
} : SortType;
|
|
31
|
+
export declare const SORT_EXPR: RegExp;
|
|
32
|
+
export declare const SORT_MAP: Record<SortType, 1 | -1>;
|
|
33
|
+
export declare const sortCodeGen: <T>(sort: Sort<T>, valuePath: [string, string], path: string) => string;
|
|
34
|
+
export declare const createSort: <T>(sort: Sort<T>, rootName: string) => (data: T) => any;
|
|
35
|
+
export {};
|
package/build/index.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
const conditions = {
|
|
2
|
+
$eq: (valuePath, value)=>`(${valuePath} === ${value})`,
|
|
3
|
+
$ne: (valuePath, value)=>`(${valuePath} !== ${value})`,
|
|
4
|
+
$gt: (valuePath, value)=>`(${valuePath} > ${value})`,
|
|
5
|
+
$gte: (valuePath, value)=>`(${valuePath} >= ${value})`,
|
|
6
|
+
$lt: (valuePath, value)=>`(${valuePath} < ${value})`,
|
|
7
|
+
$lte: (valuePath, value)=>`(${valuePath} <= ${value})`,
|
|
8
|
+
$in: (valuePath, value)=>`(${value}.includes(${valuePath}))`,
|
|
9
|
+
$nin: (valuePath, value)=>`(!${value}.includes(${valuePath}))`,
|
|
10
|
+
$exists: (valuePath, value)=>`((${valuePath} !== undefined && ${valuePath} !== null) === ${value})`,
|
|
11
|
+
$match: (valuePath, value)=>`${value}.test('' + ${valuePath})`,
|
|
12
|
+
$nmatch: (valuePath, value)=>`!${value}.test('' + ${valuePath})`,
|
|
13
|
+
$bits: (valuePath, value)=>`((${valuePath} & ${value}) !== 0)`,
|
|
14
|
+
$nbits: (valuePath, value)=>`((${valuePath} & ${value}) === 0)`
|
|
15
|
+
};
|
|
16
|
+
const operations = {
|
|
17
|
+
$and: (value)=>`(${value.join(' && ')})`,
|
|
18
|
+
$or: (value)=>`(${value.join(' || ')})`,
|
|
19
|
+
$nor: (value)=>`(!(${value.join(' || ')}))`,
|
|
20
|
+
$not: (value)=>`(!(${value}))`
|
|
21
|
+
};
|
|
22
|
+
const queryCodeGen = (query, valuePath, path)=>{
|
|
23
|
+
if (typeof query === 'object' && query !== null) {
|
|
24
|
+
const condition = Object.entries(query).map(([key, value])=>{
|
|
25
|
+
if (key in conditions) {
|
|
26
|
+
return conditions[key](valuePath, JSON.stringify(value));
|
|
27
|
+
}
|
|
28
|
+
if (key in operations) {
|
|
29
|
+
const values = Array.isArray(value) ? value : [
|
|
30
|
+
value
|
|
31
|
+
];
|
|
32
|
+
return operations[key](values.map((value)=>queryCodeGen(value, valuePath, path)));
|
|
33
|
+
}
|
|
34
|
+
const valueSubPath = `${valuePath}?.[${JSON.stringify(key)}]`;
|
|
35
|
+
if (value instanceof RegExp) {
|
|
36
|
+
return conditions.$match(valueSubPath, value.toString());
|
|
37
|
+
}
|
|
38
|
+
if (typeof value === 'object' && value !== null) {
|
|
39
|
+
return queryCodeGen(value, valueSubPath, path);
|
|
40
|
+
}
|
|
41
|
+
return conditions.$eq(valueSubPath, JSON.stringify(value));
|
|
42
|
+
});
|
|
43
|
+
return `${condition.join(' && ')}`;
|
|
44
|
+
}
|
|
45
|
+
return 'true';
|
|
46
|
+
};
|
|
47
|
+
export const createQuery = (query, rootName)=>{
|
|
48
|
+
const code = queryCodeGen(query, 'data', rootName);
|
|
49
|
+
const filter = new Function('data', `return ${code || 'true'};`);
|
|
50
|
+
return (data)=>filter(data);
|
|
51
|
+
};
|
|
52
|
+
export const SORT_EXPR = /^(asc|desc|1|-1)$/i;
|
|
53
|
+
export const SORT_MAP = {
|
|
54
|
+
asc: 1,
|
|
55
|
+
desc: -1,
|
|
56
|
+
1: 1,
|
|
57
|
+
'-1': -1
|
|
58
|
+
};
|
|
59
|
+
export const sortCodeGen = (sort, valuePath, path)=>{
|
|
60
|
+
if (typeof sort === 'object' && sort !== null) {
|
|
61
|
+
const condition = Object.entries(sort).map(([key, value])=>{
|
|
62
|
+
const valueSubPathA = `${valuePath[0]}?.[${JSON.stringify(key)}]`;
|
|
63
|
+
const valueSubPathB = `${valuePath[1]}?.[${JSON.stringify(key)}]`;
|
|
64
|
+
return sortCodeGen(value, [
|
|
65
|
+
valueSubPathA,
|
|
66
|
+
valueSubPathB
|
|
67
|
+
], path);
|
|
68
|
+
});
|
|
69
|
+
return condition.join(' || ');
|
|
70
|
+
}
|
|
71
|
+
if (typeof sort === 'string' && SORT_EXPR.test(sort)) {
|
|
72
|
+
return `(${valuePath[0]}?.localeCompare(${valuePath[1]}) * ${SORT_MAP[sort]})`;
|
|
73
|
+
}
|
|
74
|
+
if (typeof sort === 'number' && SORT_EXPR.test('' + sort)) {
|
|
75
|
+
return `((${valuePath[0]} - ${valuePath[1]}) * ${SORT_MAP[sort]})`;
|
|
76
|
+
}
|
|
77
|
+
return `0`;
|
|
78
|
+
};
|
|
79
|
+
export const createSort = (sort, rootName)=>{
|
|
80
|
+
const code = sortCodeGen(sort, [
|
|
81
|
+
'a',
|
|
82
|
+
'b'
|
|
83
|
+
], rootName);
|
|
84
|
+
const filter = new Function('a', 'b', `return ${code || 0};`);
|
|
85
|
+
console.log(filter.toString());
|
|
86
|
+
return (data)=>filter(data);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type Query<T> = {\n [P in keyof T]?: QueryCondition<T[P]>;\n} & Operations<T>;\n\ntype QueryCondition<FieldType> =\n | FieldType\n | Conditions<FieldType>;\n\ninterface Conditions<ValueType> {\n $eq?: ValueType;\n $ne?: ValueType;\n $gt?: ValueType;\n $gte?: ValueType;\n $lt?: ValueType;\n $lte?: ValueType;\n $in?: ValueType[];\n $nin?: ValueType[];\n $exists?: boolean;\n $match?: RegExp | string;\n $nmatch?: RegExp | string;\n $bits?: number;\n $nbits?: number;\n};\n\ntype ConditionKeys = keyof Conditions<unknown>;\n\ninterface Operations<T> {\n $and?: Query<T>[];\n $or?: Query<T>[];\n $nor?: Query<T>[];\n $not?: Query<T>;\n};\n\ntype OperationKeys = keyof Operations<unknown>;\n\nconst conditions: Record<ConditionKeys, any> = {\n $eq: (valuePath: string, value: unknown) => `(${valuePath} === ${value})`,\n $ne: (valuePath: string, value: unknown) => `(${valuePath} !== ${value})`,\n $gt: (valuePath: string, value: unknown) => `(${valuePath} > ${value})`,\n $gte: (valuePath: string, value: unknown) => `(${valuePath} >= ${value})`,\n $lt: (valuePath: string, value: unknown) => `(${valuePath} < ${value})`,\n $lte: (valuePath: string, value: unknown) => `(${valuePath} <= ${value})`,\n $in: (valuePath: string, value: unknown) => `(${value}.includes(${valuePath}))`,\n $nin: (valuePath: string, value: unknown) => `(!${value}.includes(${valuePath}))`,\n $exists: (valuePath: string, value: unknown) => `((${valuePath} !== undefined && ${valuePath} !== null) === ${value})`,\n $match: (valuePath: string, value: unknown) => `${value}.test('' + ${valuePath})`,\n $nmatch: (valuePath: string, value: unknown) => `!${value}.test('' + ${valuePath})`,\n $bits: (valuePath: string, value: unknown) => `((${valuePath} & ${value}) !== 0)`,\n $nbits: (valuePath: string, value: unknown) => `((${valuePath} & ${value}) === 0)`,\n};\n\nconst operations: Record<keyof Operations<unknown>, any> = {\n $and: (value: unknown[]) => `(${value.join(' && ')})`,\n $or: (value: unknown[]) => `(${value.join(' || ')})`,\n $nor: (value: unknown[]) => `(!(${value.join(' || ')}))`,\n $not: (value: unknown) => `(!(${value}))`,\n}\n\n\nconst queryCodeGen = <T extends object>(query: Query<T>, valuePath: string, path: string): string => {\n if (typeof query === 'object' && query !== null) {\n const condition = Object.entries(query).map(([key, value]) => {\n if (key in conditions) {\n return conditions[key as ConditionKeys](valuePath, JSON.stringify(value));\n }\n if (key in operations) {\n const values = Array.isArray(value) ? value : [value];\n return operations[key as OperationKeys](values.map((value) => queryCodeGen(value, valuePath, path)));\n }\n const valueSubPath = `${valuePath}?.[${JSON.stringify(key)}]`;\n if (value instanceof RegExp) {\n return conditions.$match(valueSubPath, value.toString());\n }\n if (typeof value === 'object' && value !== null) {\n return queryCodeGen(value, valueSubPath, path);\n }\n return conditions.$eq(valueSubPath, JSON.stringify(value));\n });\n\n return `${condition.join(' && ')}`;\n }\n return 'true';\n}\n\nexport const createQuery = <T extends object>(query: Query<T>, rootName: string) => {\n const code = queryCodeGen(query, 'data', rootName);\n const filter = new Function('data', `return ${code || 'true'};`);\n return (data: T) => filter(data);\n};\n\nexport type SortType = 'asc' | 'desc' | 1 | -1;\n\nexport type Sort<T> = T extends object\n ? {\n [K in keyof T]?: T[K] extends object ? Sort<T[K]> : SortType;\n }\n : SortType;\n\nexport const SORT_EXPR = /^(asc|desc|1|-1)$/i\n\nexport const SORT_MAP: Record<SortType, 1 | -1> = {\n asc: 1,\n desc: -1,\n 1: 1,\n '-1': -1,\n};\n\nexport const sortCodeGen = <T>(sort: Sort<T>, valuePath: [string, string], path: string): string => {\n if (typeof sort === 'object' && sort !== null) {\n const condition = Object.entries(sort).map(([key, value]) => {\n const valueSubPathA = `${valuePath[0]}?.[${JSON.stringify(key)}]`;\n const valueSubPathB = `${valuePath[1]}?.[${JSON.stringify(key)}]`;\n return sortCodeGen(value, [valueSubPathA, valueSubPathB], path);\n });\n return condition.join(' || ');\n }\n if (typeof sort === 'string' && SORT_EXPR.test(sort)) {\n return `(${valuePath[0]}?.localeCompare(${valuePath[1]}) * ${SORT_MAP[sort as SortType]})`;\n }\n if (typeof sort === 'number' && SORT_EXPR.test('' + sort)) {\n return `((${valuePath[0]} - ${valuePath[1]}) * ${SORT_MAP[sort as SortType]})`;\n }\n return `0`;\n};\n\nexport const createSort = <T>(sort: Sort<T>, rootName: string) => {\n const code = sortCodeGen(sort, ['a', 'b'], rootName);\n const filter = new Function('a', 'b', `return ${code || 0};`);\n console.log(filter.toString());\n return (data: T) => filter(data);\n};\n"],"names":["conditions","$eq","valuePath","value","$ne","$gt","$gte","$lt","$lte","$in","$nin","$exists","$match","$nmatch","$bits","$nbits","operations","$and","join","$or","$nor","$not","queryCodeGen","query","path","condition","Object","entries","map","key","JSON","stringify","values","Array","isArray","valueSubPath","RegExp","toString","createQuery","rootName","code","filter","Function","data","SORT_EXPR","SORT_MAP","asc","desc","sortCodeGen","sort","valueSubPathA","valueSubPathB","test","createSort","console","log"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ajoq",
|
|
3
|
+
"version": "0.0.2-0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"types": "build/index.d.ts",
|
|
7
|
+
"main": "build/index.cjs",
|
|
8
|
+
"module": "build/index.js",
|
|
9
|
+
"exports": {
|
|
10
|
+
"require": "./build/index.cjs",
|
|
11
|
+
"import": "./build/index.js"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [],
|
|
14
|
+
"author": "Ivan Zakharchanka",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/node": "^22.10.0",
|
|
18
|
+
"inop": "^0.7.4",
|
|
19
|
+
"ts-node": "^10.9.2",
|
|
20
|
+
"typescript": "^5.7.2"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "rm -rf build && NODE_ENV=production inop src/ build -i __tests__ && tsc --declaration --emitDeclarationOnly"
|
|
24
|
+
}
|
|
25
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
export type Query<T> = {
|
|
2
|
+
[P in keyof T]?: QueryCondition<T[P]>;
|
|
3
|
+
} & Operations<T>;
|
|
4
|
+
|
|
5
|
+
type QueryCondition<FieldType> =
|
|
6
|
+
| FieldType
|
|
7
|
+
| Conditions<FieldType>;
|
|
8
|
+
|
|
9
|
+
interface Conditions<ValueType> {
|
|
10
|
+
$eq?: ValueType;
|
|
11
|
+
$ne?: ValueType;
|
|
12
|
+
$gt?: ValueType;
|
|
13
|
+
$gte?: ValueType;
|
|
14
|
+
$lt?: ValueType;
|
|
15
|
+
$lte?: ValueType;
|
|
16
|
+
$in?: ValueType[];
|
|
17
|
+
$nin?: ValueType[];
|
|
18
|
+
$exists?: boolean;
|
|
19
|
+
$match?: RegExp | string;
|
|
20
|
+
$nmatch?: RegExp | string;
|
|
21
|
+
$bits?: number;
|
|
22
|
+
$nbits?: number;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type ConditionKeys = keyof Conditions<unknown>;
|
|
26
|
+
|
|
27
|
+
interface Operations<T> {
|
|
28
|
+
$and?: Query<T>[];
|
|
29
|
+
$or?: Query<T>[];
|
|
30
|
+
$nor?: Query<T>[];
|
|
31
|
+
$not?: Query<T>;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
type OperationKeys = keyof Operations<unknown>;
|
|
35
|
+
|
|
36
|
+
const conditions: Record<ConditionKeys, any> = {
|
|
37
|
+
$eq: (valuePath: string, value: unknown) => `(${valuePath} === ${value})`,
|
|
38
|
+
$ne: (valuePath: string, value: unknown) => `(${valuePath} !== ${value})`,
|
|
39
|
+
$gt: (valuePath: string, value: unknown) => `(${valuePath} > ${value})`,
|
|
40
|
+
$gte: (valuePath: string, value: unknown) => `(${valuePath} >= ${value})`,
|
|
41
|
+
$lt: (valuePath: string, value: unknown) => `(${valuePath} < ${value})`,
|
|
42
|
+
$lte: (valuePath: string, value: unknown) => `(${valuePath} <= ${value})`,
|
|
43
|
+
$in: (valuePath: string, value: unknown) => `(${value}.includes(${valuePath}))`,
|
|
44
|
+
$nin: (valuePath: string, value: unknown) => `(!${value}.includes(${valuePath}))`,
|
|
45
|
+
$exists: (valuePath: string, value: unknown) => `((${valuePath} !== undefined && ${valuePath} !== null) === ${value})`,
|
|
46
|
+
$match: (valuePath: string, value: unknown) => `${value}.test('' + ${valuePath})`,
|
|
47
|
+
$nmatch: (valuePath: string, value: unknown) => `!${value}.test('' + ${valuePath})`,
|
|
48
|
+
$bits: (valuePath: string, value: unknown) => `((${valuePath} & ${value}) !== 0)`,
|
|
49
|
+
$nbits: (valuePath: string, value: unknown) => `((${valuePath} & ${value}) === 0)`,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const operations: Record<keyof Operations<unknown>, any> = {
|
|
53
|
+
$and: (value: unknown[]) => `(${value.join(' && ')})`,
|
|
54
|
+
$or: (value: unknown[]) => `(${value.join(' || ')})`,
|
|
55
|
+
$nor: (value: unknown[]) => `(!(${value.join(' || ')}))`,
|
|
56
|
+
$not: (value: unknown) => `(!(${value}))`,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
const queryCodeGen = <T extends object>(query: Query<T>, valuePath: string, path: string): string => {
|
|
61
|
+
if (typeof query === 'object' && query !== null) {
|
|
62
|
+
const condition = Object.entries(query).map(([key, value]) => {
|
|
63
|
+
if (key in conditions) {
|
|
64
|
+
return conditions[key as ConditionKeys](valuePath, JSON.stringify(value));
|
|
65
|
+
}
|
|
66
|
+
if (key in operations) {
|
|
67
|
+
const values = Array.isArray(value) ? value : [value];
|
|
68
|
+
return operations[key as OperationKeys](values.map((value) => queryCodeGen(value, valuePath, path)));
|
|
69
|
+
}
|
|
70
|
+
const valueSubPath = `${valuePath}?.[${JSON.stringify(key)}]`;
|
|
71
|
+
if (value instanceof RegExp) {
|
|
72
|
+
return conditions.$match(valueSubPath, value.toString());
|
|
73
|
+
}
|
|
74
|
+
if (typeof value === 'object' && value !== null) {
|
|
75
|
+
return queryCodeGen(value, valueSubPath, path);
|
|
76
|
+
}
|
|
77
|
+
return conditions.$eq(valueSubPath, JSON.stringify(value));
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
return `${condition.join(' && ')}`;
|
|
81
|
+
}
|
|
82
|
+
return 'true';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const createQuery = <T extends object>(query: Query<T>, rootName: string) => {
|
|
86
|
+
const code = queryCodeGen(query, 'data', rootName);
|
|
87
|
+
const filter = new Function('data', `return ${code || 'true'};`);
|
|
88
|
+
return (data: T) => filter(data);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export type SortType = 'asc' | 'desc' | 1 | -1;
|
|
92
|
+
|
|
93
|
+
export type Sort<T> = T extends object
|
|
94
|
+
? {
|
|
95
|
+
[K in keyof T]?: T[K] extends object ? Sort<T[K]> : SortType;
|
|
96
|
+
}
|
|
97
|
+
: SortType;
|
|
98
|
+
|
|
99
|
+
export const SORT_EXPR = /^(asc|desc|1|-1)$/i
|
|
100
|
+
|
|
101
|
+
export const SORT_MAP: Record<SortType, 1 | -1> = {
|
|
102
|
+
asc: 1,
|
|
103
|
+
desc: -1,
|
|
104
|
+
1: 1,
|
|
105
|
+
'-1': -1,
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export const sortCodeGen = <T>(sort: Sort<T>, valuePath: [string, string], path: string): string => {
|
|
109
|
+
if (typeof sort === 'object' && sort !== null) {
|
|
110
|
+
const condition = Object.entries(sort).map(([key, value]) => {
|
|
111
|
+
const valueSubPathA = `${valuePath[0]}?.[${JSON.stringify(key)}]`;
|
|
112
|
+
const valueSubPathB = `${valuePath[1]}?.[${JSON.stringify(key)}]`;
|
|
113
|
+
return sortCodeGen(value, [valueSubPathA, valueSubPathB], path);
|
|
114
|
+
});
|
|
115
|
+
return condition.join(' || ');
|
|
116
|
+
}
|
|
117
|
+
if (typeof sort === 'string' && SORT_EXPR.test(sort)) {
|
|
118
|
+
return `(${valuePath[0]}?.localeCompare(${valuePath[1]}) * ${SORT_MAP[sort as SortType]})`;
|
|
119
|
+
}
|
|
120
|
+
if (typeof sort === 'number' && SORT_EXPR.test('' + sort)) {
|
|
121
|
+
return `((${valuePath[0]} - ${valuePath[1]}) * ${SORT_MAP[sort as SortType]})`;
|
|
122
|
+
}
|
|
123
|
+
return `0`;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export const createSort = <T>(sort: Sort<T>, rootName: string) => {
|
|
127
|
+
const code = sortCodeGen(sort, ['a', 'b'], rootName);
|
|
128
|
+
const filter = new Function('a', 'b', `return ${code || 0};`);
|
|
129
|
+
console.log(filter.toString());
|
|
130
|
+
return (data: T) => filter(data);
|
|
131
|
+
};
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"strict": true,
|
|
5
|
+
"target": "ESNext",
|
|
6
|
+
"module": "NodeNext",
|
|
7
|
+
"moduleResolution": "NodeNext",
|
|
8
|
+
"declaration": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"lib": ["ESNext"],
|
|
11
|
+
"rootDir": "src",
|
|
12
|
+
"outDir": "build"
|
|
13
|
+
},
|
|
14
|
+
"include": [
|
|
15
|
+
"src/**/*.ts"
|
|
16
|
+
, "src/__benchmark__/index.js" ],
|
|
17
|
+
"exclude": [
|
|
18
|
+
"**/__tests__",
|
|
19
|
+
"node_modules",
|
|
20
|
+
"**/*.tmp.ts"
|
|
21
|
+
]
|
|
22
|
+
}
|