ajoq 0.0.2-0 → 0.0.3

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/build/index.cjs CHANGED
@@ -9,22 +9,25 @@ function _export(target, all) {
9
9
  });
10
10
  }
11
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;
12
+ createFilter: function() {
13
+ return createFilter;
20
14
  },
21
15
  createSort: function() {
22
16
  return createSort;
23
17
  },
24
- sortCodeGen: function() {
25
- return sortCodeGen;
18
+ stringify: function() {
19
+ return stringify;
26
20
  }
27
21
  });
22
+ const stringify = (value)=>{
23
+ if (value === undefined) {
24
+ return 'undefined';
25
+ }
26
+ if (value instanceof RegExp) {
27
+ return `/${value.source}/${value.flags}`;
28
+ }
29
+ return JSON.stringify(value);
30
+ };
28
31
  const conditions = {
29
32
  $eq: (valuePath, value)=>`(${valuePath} === ${value})`,
30
33
  $ne: (valuePath, value)=>`(${valuePath} !== ${value})`,
@@ -32,48 +35,84 @@ const conditions = {
32
35
  $gte: (valuePath, value)=>`(${valuePath} >= ${value})`,
33
36
  $lt: (valuePath, value)=>`(${valuePath} < ${value})`,
34
37
  $lte: (valuePath, value)=>`(${valuePath} <= ${value})`,
35
- $in: (valuePath, value)=>`(${value}.includes(${valuePath}))`,
36
- $nin: (valuePath, value)=>`(!${value}.includes(${valuePath}))`,
38
+ $in: (valuePath, value, context)=>{
39
+ const name = context.register(`new Set(${value})`);
40
+ return `${name}.has(${valuePath})`;
41
+ },
42
+ $nin: (valuePath, value, context)=>`!${conditions.$in(valuePath, value, context)}`,
43
+ $sub: (valuePath, value, context)=>{
44
+ const name = context.register(`new Set(${value})`);
45
+ return `${valuePath}.every((value) => ${name}.has(value))`;
46
+ },
47
+ $nsub: (valuePath, value, context)=>`!${conditions.$sub(valuePath, value, context)}`,
48
+ $sup: (valuePath, value, context)=>{
49
+ const name = context.register(`new Set(${valuePath})`);
50
+ return `${value}.every((value) => ${name}.has(value))`;
51
+ },
52
+ $nsup: (valuePath, value, context)=>`!${conditions.$sup(valuePath, value, context)}`,
53
+ $con: (valuePath, value)=>`${valuePath}.includes(${value})`,
54
+ $ncon: (valuePath, value)=>`!${valuePath}.includes(${value})`,
37
55
  $exists: (valuePath, value)=>`((${valuePath} !== undefined && ${valuePath} !== null) === ${value})`,
38
56
  $match: (valuePath, value)=>`${value}.test('' + ${valuePath})`,
39
57
  $nmatch: (valuePath, value)=>`!${value}.test('' + ${valuePath})`,
40
58
  $bits: (valuePath, value)=>`((${valuePath} & ${value}) !== 0)`,
41
- $nbits: (valuePath, value)=>`((${valuePath} & ${value}) === 0)`
59
+ $nbits: (valuePath, value)=>`((${valuePath} & ${value}) === 0)`,
60
+ $typeof: (valuePath, value)=>`(typeof ${valuePath} === ${value})`,
61
+ $ntypeof: (valuePath, value)=>`(typeof ${valuePath} !== ${value})`
42
62
  };
43
63
  const operations = {
44
64
  $and: (value)=>`(${value.join(' && ')})`,
45
65
  $or: (value)=>`(${value.join(' || ')})`,
46
- $nor: (value)=>`(!(${value.join(' || ')}))`,
47
- $not: (value)=>`(!(${value}))`
66
+ $nor: (value)=>`!(${value.join(' || ')})`,
67
+ $not: (value)=>`!(${value})`
48
68
  };
49
- const queryCodeGen = (query, valuePath, path)=>{
69
+ class Context {
70
+ counter = 0;
71
+ values = [];
72
+ cacheMap = new Map();
73
+ register(code) {
74
+ if (!this.cacheMap.has(code)) {
75
+ const name = `v$$${this.counter++}`;
76
+ this.values.push(`const ${name} = ${code};`);
77
+ this.cacheMap.set(code, name);
78
+ }
79
+ return this.cacheMap.get(code);
80
+ }
81
+ toString() {
82
+ return this.values.join('\n');
83
+ }
84
+ }
85
+ const filterCodeGen = (query, valuePath, context)=>{
50
86
  if (typeof query === 'object' && query !== null) {
51
87
  const condition = Object.entries(query).map(([key, value])=>{
52
88
  if (key in conditions) {
53
- return conditions[key](valuePath, JSON.stringify(value));
89
+ return conditions[key](valuePath, stringify(value), context);
54
90
  }
55
91
  if (key in operations) {
56
92
  const values = Array.isArray(value) ? value : [
57
93
  value
58
94
  ];
59
- return operations[key](values.map((value)=>queryCodeGen(value, valuePath, path)));
95
+ return operations[key](values.map((v)=>filterCodeGen(v, valuePath, context)));
60
96
  }
61
- const valueSubPath = `${valuePath}?.[${JSON.stringify(key)}]`;
97
+ const valueSubPath = `${valuePath}?.[${stringify(key)}]`;
62
98
  if (value instanceof RegExp) {
63
- return conditions.$match(valueSubPath, value.toString());
99
+ return conditions.$match(valueSubPath, value.toString(), context);
64
100
  }
65
101
  if (typeof value === 'object' && value !== null) {
66
- return queryCodeGen(value, valueSubPath, path);
102
+ return filterCodeGen(value, valueSubPath, context);
67
103
  }
68
- return conditions.$eq(valueSubPath, JSON.stringify(value));
104
+ return conditions.$eq(valueSubPath, JSON.stringify(value), context);
69
105
  });
70
- return `${condition.join(' && ')}`;
106
+ if (condition.length > 0) {
107
+ return `${condition.join(' && ')}`;
108
+ }
71
109
  }
72
110
  return 'true';
73
111
  };
74
- const createQuery = (query, rootName)=>{
75
- const code = queryCodeGen(query, 'data', rootName);
76
- const filter = new Function('data', `return ${code || 'true'};`);
112
+ const createFilter = (query)=>{
113
+ const context = new Context();
114
+ const code = filterCodeGen(query, 'data', context);
115
+ const filter = new Function('data', `${context}\nreturn ${code};`);
77
116
  return (data)=>filter(data);
78
117
  };
79
118
  const SORT_EXPR = /^(asc|desc|1|-1)$/i;
@@ -83,7 +122,7 @@ const SORT_MAP = {
83
122
  1: 1,
84
123
  '-1': -1
85
124
  };
86
- const sortCodeGen = (sort, valuePath, path)=>{
125
+ const sortCodeGen = (sort, valuePath)=>{
87
126
  if (typeof sort === 'object' && sort !== null) {
88
127
  const condition = Object.entries(sort).map(([key, value])=>{
89
128
  const valueSubPathA = `${valuePath[0]}?.[${JSON.stringify(key)}]`;
@@ -91,9 +130,11 @@ const sortCodeGen = (sort, valuePath, path)=>{
91
130
  return sortCodeGen(value, [
92
131
  valueSubPathA,
93
132
  valueSubPathB
94
- ], path);
133
+ ]);
95
134
  });
96
- return condition.join(' || ');
135
+ if (condition.length > 0) {
136
+ return condition.join(' || ');
137
+ }
97
138
  }
98
139
  if (typeof sort === 'string' && SORT_EXPR.test(sort)) {
99
140
  return `(${valuePath[0]}?.localeCompare(${valuePath[1]}) * ${SORT_MAP[sort]})`;
@@ -103,14 +144,13 @@ const sortCodeGen = (sort, valuePath, path)=>{
103
144
  }
104
145
  return `0`;
105
146
  };
106
- const createSort = (sort, rootName)=>{
147
+ const createSort = (sort)=>{
107
148
  const code = sortCodeGen(sort, [
108
149
  'a',
109
150
  'b'
110
- ], rootName);
111
- const filter = new Function('a', 'b', `return ${code || 0};`);
112
- console.log(filter.toString());
113
- return (data)=>filter(data);
151
+ ]);
152
+ const sortFn = new Function('a', 'b', `return ${code};`);
153
+ return (a, b)=>sortFn(a, b);
114
154
  };
115
155
 
116
156
  //# sourceMappingURL=index.cjs.map
@@ -1 +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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"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type Query<T> = {\n [P in keyof T]?: QueryCondition<T[P]>;\n} & Operations<T>;\n\nexport type QueryCondition<FieldType> = FieldType | Conditions<FieldType> | Operations<QueryCondition<FieldType>>;\n\nexport interface 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 $sub?: ValueType[];\n $nsub?: ValueType[];\n $sup?: ValueType[];\n $nsup?: ValueType[];\n $con?: ValueType;\n $ncon?: ValueType;\n $exists?: boolean;\n $match?: RegExp | string;\n $nmatch?: RegExp | string;\n $bits?: number;\n $nbits?: number;\n $typeof: string;\n $ntypeof: string;\n}\n\nexport type ConditionKeys = keyof Conditions<unknown>;\n\nexport interface Operations<T> {\n $and?: Query<T>[];\n $or?: Query<T>[];\n $nor?: Query<T>[];\n $not?: Query<T>;\n}\n\nexport type OperationKeys = keyof Operations<unknown>;\n\nexport const stringify = (value: unknown) => {\n if (value === undefined) {\n return 'undefined';\n }\n if (value instanceof RegExp) {\n return `/${value.source}/${value.flags}`;\n }\n return JSON.stringify(value);\n};\n\nexport interface Condition {\n (valuePath: string, value: any, context: Context): string;\n}\n\nconst conditions: Record<ConditionKeys, Condition> = {\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, context: Context) => {\n const name = context.register(`new Set(${value})`);\n return `${name}.has(${valuePath})`;\n },\n $nin: (valuePath: string, value: unknown, context: Context) => `!${conditions.$in(valuePath, value, context)}`,\n $sub: (valuePath: string, value: unknown, context: Context) => {\n const name = context.register(`new Set(${value})`);\n return `${valuePath}.every((value) => ${name}.has(value))`;\n },\n $nsub: (valuePath: string, value: unknown, context: Context) => `!${conditions.$sub(valuePath, value, context)}`,\n $sup: (valuePath: string, value: unknown, context: Context) => {\n const name = context.register(`new Set(${valuePath})`);\n return `${value}.every((value) => ${name}.has(value))`;\n },\n $nsup: (valuePath: string, value: unknown, context: Context) => `!${conditions.$sup(valuePath, value, context)}`,\n $con: (valuePath: string, value: unknown) => `${valuePath}.includes(${value})`,\n $ncon: (valuePath: string, value: unknown) => `!${valuePath}.includes(${value})`,\n $exists: (valuePath: string, value: boolean) => `((${valuePath} !== undefined && ${valuePath} !== null) === ${value})`,\n $match: (valuePath: string, value: RegExp) => `${value}.test('' + ${valuePath})`,\n $nmatch: (valuePath: string, value: RegExp) => `!${value}.test('' + ${valuePath})`,\n $bits: (valuePath: string, value: unknown) => `((${valuePath} & ${value}) !== 0)`,\n $nbits: (valuePath: string, value: unknown) => `((${valuePath} & ${value}) === 0)`,\n $typeof: (valuePath: string, value: unknown) => `(typeof ${valuePath} === ${value})`,\n $ntypeof: (valuePath: string, value: unknown) => `(typeof ${valuePath} !== ${value})`,\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\nclass Context {\n private counter = 0;\n private values: string[] = [];\n private cacheMap = new Map<string, string>();\n\n register(code: string) {\n if (!this.cacheMap.has(code)) {\n const name = `v$$${this.counter++}`;\n this.values.push(`const ${name} = ${code};`);\n this.cacheMap.set(code, name);\n }\n return this.cacheMap.get(code)!;\n }\n toString() {\n return this.values.join('\\n');\n }\n}\n\nconst filterCodeGen = <T extends object>(query: Query<T> | undefined, valuePath: string, context: Context): 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, stringify(value), context);\n }\n if (key in operations) {\n const values = Array.isArray(value) ? value : [value];\n return operations[key as OperationKeys](values.map((v) => filterCodeGen(v, valuePath, context)));\n }\n const valueSubPath = `${valuePath}?.[${stringify(key)}]`;\n if (value instanceof RegExp) {\n return conditions.$match(valueSubPath, value.toString(), context);\n }\n if (typeof value === 'object' && value !== null) {\n return filterCodeGen(value, valueSubPath, context);\n }\n return conditions.$eq(valueSubPath, JSON.stringify(value), context);\n });\n if (condition.length > 0) {\n return `${condition.join(' && ')}`;\n }\n }\n return 'true';\n};\n\nexport const createFilter = <T extends object>(query?: Query<T>) => {\n const context = new Context();\n const code = filterCodeGen(query, 'data', context);\n const filter = new Function('data', `${context}\\nreturn ${code};`);\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\nconst SORT_EXPR = /^(asc|desc|1|-1)$/i;\n\nconst SORT_MAP: Record<SortType, 1 | -1> = {\n asc: 1,\n desc: -1,\n 1: 1,\n '-1': -1,\n};\n\nconst sortCodeGen = <T>(sort: Sort<T>, valuePath: [string, 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]);\n });\n if (condition.length > 0) {\n return condition.join(' || ');\n }\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>) => {\n const code = sortCodeGen(sort, ['a', 'b']);\n const sortFn = new Function('a', 'b', `return ${code};`);\n return (a: T, b: T) => sortFn(a, b);\n};\n"],"names":["createFilter","createSort","stringify","value","undefined","RegExp","source","flags","JSON","conditions","$eq","valuePath","$ne","$gt","$gte","$lt","$lte","$in","context","name","register","$nin","$sub","$nsub","$sup","$nsup","$con","$ncon","$exists","$match","$nmatch","$bits","$nbits","$typeof","$ntypeof","operations","$and","join","$or","$nor","$not","Context","counter","values","cacheMap","Map","code","has","push","set","get","toString","filterCodeGen","query","condition","Object","entries","map","key","Array","isArray","v","valueSubPath","length","filter","Function","data","SORT_EXPR","SORT_MAP","asc","desc","sortCodeGen","sort","valueSubPathA","valueSubPathB","test","sortFn","a","b"],"mappings":"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"}
package/build/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  export type Query<T> = {
2
2
  [P in keyof T]?: QueryCondition<T[P]>;
3
3
  } & Operations<T>;
4
- type QueryCondition<FieldType> = FieldType | Conditions<FieldType>;
5
- interface Conditions<ValueType> {
4
+ export type QueryCondition<FieldType> = FieldType | Conditions<FieldType> | Operations<QueryCondition<FieldType>>;
5
+ export interface Conditions<ValueType> {
6
6
  $eq?: ValueType;
7
7
  $ne?: ValueType;
8
8
  $gt?: ValueType;
@@ -11,25 +11,43 @@ interface Conditions<ValueType> {
11
11
  $lte?: ValueType;
12
12
  $in?: ValueType[];
13
13
  $nin?: ValueType[];
14
+ $sub?: ValueType[];
15
+ $nsub?: ValueType[];
16
+ $sup?: ValueType[];
17
+ $nsup?: ValueType[];
18
+ $con?: ValueType;
19
+ $ncon?: ValueType;
14
20
  $exists?: boolean;
15
21
  $match?: RegExp | string;
16
22
  $nmatch?: RegExp | string;
17
23
  $bits?: number;
18
24
  $nbits?: number;
25
+ $typeof: string;
26
+ $ntypeof: string;
19
27
  }
20
- interface Operations<T> {
28
+ export type ConditionKeys = keyof Conditions<unknown>;
29
+ export interface Operations<T> {
21
30
  $and?: Query<T>[];
22
31
  $or?: Query<T>[];
23
32
  $nor?: Query<T>[];
24
33
  $not?: Query<T>;
25
34
  }
26
- export declare const createQuery: <T extends object>(query: Query<T>, rootName: string) => (data: T) => any;
35
+ export type OperationKeys = keyof Operations<unknown>;
36
+ export declare const stringify: (value: unknown) => string;
37
+ export interface Condition {
38
+ (valuePath: string, value: any, context: Context): string;
39
+ }
40
+ declare class Context {
41
+ private counter;
42
+ private values;
43
+ private cacheMap;
44
+ register(code: string): string;
45
+ toString(): string;
46
+ }
47
+ export declare const createFilter: <T extends object>(query?: Query<T>) => (data: T) => any;
27
48
  export type SortType = 'asc' | 'desc' | 1 | -1;
28
49
  export type Sort<T> = T extends object ? {
29
50
  [K in keyof T]?: T[K] extends object ? Sort<T[K]> : SortType;
30
51
  } : 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;
52
+ export declare const createSort: <T>(sort: Sort<T>) => (a: T, b: T) => any;
35
53
  export {};
package/build/index.js CHANGED
@@ -1,3 +1,12 @@
1
+ export const stringify = (value)=>{
2
+ if (value === undefined) {
3
+ return 'undefined';
4
+ }
5
+ if (value instanceof RegExp) {
6
+ return `/${value.source}/${value.flags}`;
7
+ }
8
+ return JSON.stringify(value);
9
+ };
1
10
  const conditions = {
2
11
  $eq: (valuePath, value)=>`(${valuePath} === ${value})`,
3
12
  $ne: (valuePath, value)=>`(${valuePath} !== ${value})`,
@@ -5,58 +14,94 @@ const conditions = {
5
14
  $gte: (valuePath, value)=>`(${valuePath} >= ${value})`,
6
15
  $lt: (valuePath, value)=>`(${valuePath} < ${value})`,
7
16
  $lte: (valuePath, value)=>`(${valuePath} <= ${value})`,
8
- $in: (valuePath, value)=>`(${value}.includes(${valuePath}))`,
9
- $nin: (valuePath, value)=>`(!${value}.includes(${valuePath}))`,
17
+ $in: (valuePath, value, context)=>{
18
+ const name = context.register(`new Set(${value})`);
19
+ return `${name}.has(${valuePath})`;
20
+ },
21
+ $nin: (valuePath, value, context)=>`!${conditions.$in(valuePath, value, context)}`,
22
+ $sub: (valuePath, value, context)=>{
23
+ const name = context.register(`new Set(${value})`);
24
+ return `${valuePath}.every((value) => ${name}.has(value))`;
25
+ },
26
+ $nsub: (valuePath, value, context)=>`!${conditions.$sub(valuePath, value, context)}`,
27
+ $sup: (valuePath, value, context)=>{
28
+ const name = context.register(`new Set(${valuePath})`);
29
+ return `${value}.every((value) => ${name}.has(value))`;
30
+ },
31
+ $nsup: (valuePath, value, context)=>`!${conditions.$sup(valuePath, value, context)}`,
32
+ $con: (valuePath, value)=>`${valuePath}.includes(${value})`,
33
+ $ncon: (valuePath, value)=>`!${valuePath}.includes(${value})`,
10
34
  $exists: (valuePath, value)=>`((${valuePath} !== undefined && ${valuePath} !== null) === ${value})`,
11
35
  $match: (valuePath, value)=>`${value}.test('' + ${valuePath})`,
12
36
  $nmatch: (valuePath, value)=>`!${value}.test('' + ${valuePath})`,
13
37
  $bits: (valuePath, value)=>`((${valuePath} & ${value}) !== 0)`,
14
- $nbits: (valuePath, value)=>`((${valuePath} & ${value}) === 0)`
38
+ $nbits: (valuePath, value)=>`((${valuePath} & ${value}) === 0)`,
39
+ $typeof: (valuePath, value)=>`(typeof ${valuePath} === ${value})`,
40
+ $ntypeof: (valuePath, value)=>`(typeof ${valuePath} !== ${value})`
15
41
  };
16
42
  const operations = {
17
43
  $and: (value)=>`(${value.join(' && ')})`,
18
44
  $or: (value)=>`(${value.join(' || ')})`,
19
- $nor: (value)=>`(!(${value.join(' || ')}))`,
20
- $not: (value)=>`(!(${value}))`
45
+ $nor: (value)=>`!(${value.join(' || ')})`,
46
+ $not: (value)=>`!(${value})`
21
47
  };
22
- const queryCodeGen = (query, valuePath, path)=>{
48
+ class Context {
49
+ counter = 0;
50
+ values = [];
51
+ cacheMap = new Map();
52
+ register(code) {
53
+ if (!this.cacheMap.has(code)) {
54
+ const name = `v$$${this.counter++}`;
55
+ this.values.push(`const ${name} = ${code};`);
56
+ this.cacheMap.set(code, name);
57
+ }
58
+ return this.cacheMap.get(code);
59
+ }
60
+ toString() {
61
+ return this.values.join('\n');
62
+ }
63
+ }
64
+ const filterCodeGen = (query, valuePath, context)=>{
23
65
  if (typeof query === 'object' && query !== null) {
24
66
  const condition = Object.entries(query).map(([key, value])=>{
25
67
  if (key in conditions) {
26
- return conditions[key](valuePath, JSON.stringify(value));
68
+ return conditions[key](valuePath, stringify(value), context);
27
69
  }
28
70
  if (key in operations) {
29
71
  const values = Array.isArray(value) ? value : [
30
72
  value
31
73
  ];
32
- return operations[key](values.map((value)=>queryCodeGen(value, valuePath, path)));
74
+ return operations[key](values.map((v)=>filterCodeGen(v, valuePath, context)));
33
75
  }
34
- const valueSubPath = `${valuePath}?.[${JSON.stringify(key)}]`;
76
+ const valueSubPath = `${valuePath}?.[${stringify(key)}]`;
35
77
  if (value instanceof RegExp) {
36
- return conditions.$match(valueSubPath, value.toString());
78
+ return conditions.$match(valueSubPath, value.toString(), context);
37
79
  }
38
80
  if (typeof value === 'object' && value !== null) {
39
- return queryCodeGen(value, valueSubPath, path);
81
+ return filterCodeGen(value, valueSubPath, context);
40
82
  }
41
- return conditions.$eq(valueSubPath, JSON.stringify(value));
83
+ return conditions.$eq(valueSubPath, JSON.stringify(value), context);
42
84
  });
43
- return `${condition.join(' && ')}`;
85
+ if (condition.length > 0) {
86
+ return `${condition.join(' && ')}`;
87
+ }
44
88
  }
45
89
  return 'true';
46
90
  };
47
- export const createQuery = (query, rootName)=>{
48
- const code = queryCodeGen(query, 'data', rootName);
49
- const filter = new Function('data', `return ${code || 'true'};`);
91
+ export const createFilter = (query)=>{
92
+ const context = new Context();
93
+ const code = filterCodeGen(query, 'data', context);
94
+ const filter = new Function('data', `${context}\nreturn ${code};`);
50
95
  return (data)=>filter(data);
51
96
  };
52
- export const SORT_EXPR = /^(asc|desc|1|-1)$/i;
53
- export const SORT_MAP = {
97
+ const SORT_EXPR = /^(asc|desc|1|-1)$/i;
98
+ const SORT_MAP = {
54
99
  asc: 1,
55
100
  desc: -1,
56
101
  1: 1,
57
102
  '-1': -1
58
103
  };
59
- export const sortCodeGen = (sort, valuePath, path)=>{
104
+ const sortCodeGen = (sort, valuePath)=>{
60
105
  if (typeof sort === 'object' && sort !== null) {
61
106
  const condition = Object.entries(sort).map(([key, value])=>{
62
107
  const valueSubPathA = `${valuePath[0]}?.[${JSON.stringify(key)}]`;
@@ -64,9 +109,11 @@ export const sortCodeGen = (sort, valuePath, path)=>{
64
109
  return sortCodeGen(value, [
65
110
  valueSubPathA,
66
111
  valueSubPathB
67
- ], path);
112
+ ]);
68
113
  });
69
- return condition.join(' || ');
114
+ if (condition.length > 0) {
115
+ return condition.join(' || ');
116
+ }
70
117
  }
71
118
  if (typeof sort === 'string' && SORT_EXPR.test(sort)) {
72
119
  return `(${valuePath[0]}?.localeCompare(${valuePath[1]}) * ${SORT_MAP[sort]})`;
@@ -76,14 +123,13 @@ export const sortCodeGen = (sort, valuePath, path)=>{
76
123
  }
77
124
  return `0`;
78
125
  };
79
- export const createSort = (sort, rootName)=>{
126
+ export const createSort = (sort)=>{
80
127
  const code = sortCodeGen(sort, [
81
128
  'a',
82
129
  'b'
83
- ], rootName);
84
- const filter = new Function('a', 'b', `return ${code || 0};`);
85
- console.log(filter.toString());
86
- return (data)=>filter(data);
130
+ ]);
131
+ const sortFn = new Function('a', 'b', `return ${code};`);
132
+ return (a, b)=>sortFn(a, b);
87
133
  };
88
134
 
89
135
  //# sourceMappingURL=index.js.map
@@ -1 +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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"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export type Query<T> = {\n [P in keyof T]?: QueryCondition<T[P]>;\n} & Operations<T>;\n\nexport type QueryCondition<FieldType> = FieldType | Conditions<FieldType> | Operations<QueryCondition<FieldType>>;\n\nexport interface 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 $sub?: ValueType[];\n $nsub?: ValueType[];\n $sup?: ValueType[];\n $nsup?: ValueType[];\n $con?: ValueType;\n $ncon?: ValueType;\n $exists?: boolean;\n $match?: RegExp | string;\n $nmatch?: RegExp | string;\n $bits?: number;\n $nbits?: number;\n $typeof: string;\n $ntypeof: string;\n}\n\nexport type ConditionKeys = keyof Conditions<unknown>;\n\nexport interface Operations<T> {\n $and?: Query<T>[];\n $or?: Query<T>[];\n $nor?: Query<T>[];\n $not?: Query<T>;\n}\n\nexport type OperationKeys = keyof Operations<unknown>;\n\nexport const stringify = (value: unknown) => {\n if (value === undefined) {\n return 'undefined';\n }\n if (value instanceof RegExp) {\n return `/${value.source}/${value.flags}`;\n }\n return JSON.stringify(value);\n};\n\nexport interface Condition {\n (valuePath: string, value: any, context: Context): string;\n}\n\nconst conditions: Record<ConditionKeys, Condition> = {\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, context: Context) => {\n const name = context.register(`new Set(${value})`);\n return `${name}.has(${valuePath})`;\n },\n $nin: (valuePath: string, value: unknown, context: Context) => `!${conditions.$in(valuePath, value, context)}`,\n $sub: (valuePath: string, value: unknown, context: Context) => {\n const name = context.register(`new Set(${value})`);\n return `${valuePath}.every((value) => ${name}.has(value))`;\n },\n $nsub: (valuePath: string, value: unknown, context: Context) => `!${conditions.$sub(valuePath, value, context)}`,\n $sup: (valuePath: string, value: unknown, context: Context) => {\n const name = context.register(`new Set(${valuePath})`);\n return `${value}.every((value) => ${name}.has(value))`;\n },\n $nsup: (valuePath: string, value: unknown, context: Context) => `!${conditions.$sup(valuePath, value, context)}`,\n $con: (valuePath: string, value: unknown) => `${valuePath}.includes(${value})`,\n $ncon: (valuePath: string, value: unknown) => `!${valuePath}.includes(${value})`,\n $exists: (valuePath: string, value: boolean) => `((${valuePath} !== undefined && ${valuePath} !== null) === ${value})`,\n $match: (valuePath: string, value: RegExp) => `${value}.test('' + ${valuePath})`,\n $nmatch: (valuePath: string, value: RegExp) => `!${value}.test('' + ${valuePath})`,\n $bits: (valuePath: string, value: unknown) => `((${valuePath} & ${value}) !== 0)`,\n $nbits: (valuePath: string, value: unknown) => `((${valuePath} & ${value}) === 0)`,\n $typeof: (valuePath: string, value: unknown) => `(typeof ${valuePath} === ${value})`,\n $ntypeof: (valuePath: string, value: unknown) => `(typeof ${valuePath} !== ${value})`,\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\nclass Context {\n private counter = 0;\n private values: string[] = [];\n private cacheMap = new Map<string, string>();\n\n register(code: string) {\n if (!this.cacheMap.has(code)) {\n const name = `v$$${this.counter++}`;\n this.values.push(`const ${name} = ${code};`);\n this.cacheMap.set(code, name);\n }\n return this.cacheMap.get(code)!;\n }\n toString() {\n return this.values.join('\\n');\n }\n}\n\nconst filterCodeGen = <T extends object>(query: Query<T> | undefined, valuePath: string, context: Context): 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, stringify(value), context);\n }\n if (key in operations) {\n const values = Array.isArray(value) ? value : [value];\n return operations[key as OperationKeys](values.map((v) => filterCodeGen(v, valuePath, context)));\n }\n const valueSubPath = `${valuePath}?.[${stringify(key)}]`;\n if (value instanceof RegExp) {\n return conditions.$match(valueSubPath, value.toString(), context);\n }\n if (typeof value === 'object' && value !== null) {\n return filterCodeGen(value, valueSubPath, context);\n }\n return conditions.$eq(valueSubPath, JSON.stringify(value), context);\n });\n if (condition.length > 0) {\n return `${condition.join(' && ')}`;\n }\n }\n return 'true';\n};\n\nexport const createFilter = <T extends object>(query?: Query<T>) => {\n const context = new Context();\n const code = filterCodeGen(query, 'data', context);\n const filter = new Function('data', `${context}\\nreturn ${code};`);\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\nconst SORT_EXPR = /^(asc|desc|1|-1)$/i;\n\nconst SORT_MAP: Record<SortType, 1 | -1> = {\n asc: 1,\n desc: -1,\n 1: 1,\n '-1': -1,\n};\n\nconst sortCodeGen = <T>(sort: Sort<T>, valuePath: [string, 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]);\n });\n if (condition.length > 0) {\n return condition.join(' || ');\n }\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>) => {\n const code = sortCodeGen(sort, ['a', 'b']);\n const sortFn = new Function('a', 'b', `return ${code};`);\n return (a: T, b: T) => sortFn(a, b);\n};\n"],"names":["stringify","value","undefined","RegExp","source","flags","JSON","conditions","$eq","valuePath","$ne","$gt","$gte","$lt","$lte","$in","context","name","register","$nin","$sub","$nsub","$sup","$nsup","$con","$ncon","$exists","$match","$nmatch","$bits","$nbits","$typeof","$ntypeof","operations","$and","join","$or","$nor","$not","Context","counter","values","cacheMap","Map","code","has","push","set","get","toString","filterCodeGen","query","condition","Object","entries","map","key","Array","isArray","v","valueSubPath","length","createFilter","filter","Function","data","SORT_EXPR","SORT_MAP","asc","desc","sortCodeGen","sort","valueSubPathA","valueSubPathB","test","createSort","sortFn","a","b"],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ import prettier from "eslint-plugin-prettier";
2
+ import typescriptEslint from "@typescript-eslint/eslint-plugin";
3
+ import tsParser from "@typescript-eslint/parser";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import js from "@eslint/js";
7
+ import { FlatCompat } from "@eslint/eslintrc";
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+ const compat = new FlatCompat({
12
+ baseDirectory: __dirname,
13
+ recommendedConfig: js.configs.recommended,
14
+ allConfig: js.configs.all
15
+ });
16
+
17
+ export default [{
18
+ ignores: [
19
+ "**/docs",
20
+ "**/build",
21
+ "**/coverage",
22
+ "**/node_modules",
23
+ "**/scripts",
24
+ "src/*.tmp.ts",
25
+ "src/__tests__/*",
26
+ ],
27
+ }, ...compat.extends("eslint-config-airbnb-base").map(config => ({
28
+ ...config,
29
+ files: ["**/*.js"],
30
+ })), {
31
+ files: ["**/*.js"],
32
+
33
+ plugins: {
34
+ prettier,
35
+ },
36
+
37
+ languageOptions: {
38
+ globals: {},
39
+ },
40
+
41
+ rules: {
42
+ "prettier/prettier": "error",
43
+ },
44
+ }, ...compat.extends(
45
+ "plugin:@typescript-eslint/eslint-recommended",
46
+ "plugin:@typescript-eslint/recommended",
47
+ ).map(config => ({
48
+ ...config,
49
+ files: ["**/*.ts"],
50
+ })), {
51
+ files: ["**/*.ts"],
52
+
53
+ plugins: {
54
+ "@typescript-eslint": typescriptEslint,
55
+ prettier,
56
+ },
57
+
58
+ languageOptions: {
59
+ globals: {},
60
+ parser: tsParser,
61
+ ecmaVersion: 5,
62
+ sourceType: "script",
63
+
64
+ parserOptions: {
65
+ project: "./tsconfig.json",
66
+ },
67
+ },
68
+
69
+ rules: {
70
+ "prettier/prettier": "error",
71
+ "@typescript-eslint/no-explicit-any": 0,
72
+ "@typescript-eslint/no-floating-promises": 1,
73
+ "@typescript-eslint/no-unused-vars": 2,
74
+ "@typescript-eslint/no-unsafe-declaration-merging": 0,
75
+ "@typescript-eslint/ban-ts-comment": 1,
76
+ "@typescript-eslint/ban-types": 0,
77
+ "@typescript-eslint/no-empty-object-type": 0
78
+ },
79
+ }];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ajoq",
3
- "version": "0.0.2-0",
3
+ "version": "0.0.3",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "types": "build/index.d.ts",
@@ -14,12 +14,27 @@
14
14
  "author": "Ivan Zakharchanka",
15
15
  "license": "MIT",
16
16
  "devDependencies": {
17
+ "@swc/jest": "^0.2.37",
18
+ "@types/jest": "^29.5.14",
17
19
  "@types/node": "^22.10.0",
20
+ "@typescript-eslint/eslint-plugin": "^8.16.0",
21
+ "@typescript-eslint/parser": "^8.16.0",
22
+ "@typescript-eslint/typescript-estree": "^8.16.0",
23
+ "eslint": "^9.15.0",
24
+ "eslint-config-airbnb-base": "^15.0.0",
25
+ "eslint-config-prettier": "^9.1.0",
26
+ "eslint-plugin-import": "^2.31.0",
27
+ "eslint-plugin-prettier": "^5.2.1",
28
+ "husky": "^9.1.7",
18
29
  "inop": "^0.7.4",
30
+ "jest": "^29.7.0",
31
+ "prettier": "^3.4.1",
19
32
  "ts-node": "^10.9.2",
20
33
  "typescript": "^5.7.2"
21
34
  },
22
35
  "scripts": {
23
- "build": "rm -rf build && NODE_ENV=production inop src/ build -i __tests__ && tsc --declaration --emitDeclarationOnly"
36
+ "build": "rm -rf build && NODE_ENV=production inop src/ build -i __tests__ && tsc --declaration --emitDeclarationOnly",
37
+ "test": "jest",
38
+ "lint": "eslint src"
24
39
  }
25
40
  }
package/src/index.ts CHANGED
@@ -2,11 +2,9 @@ export type Query<T> = {
2
2
  [P in keyof T]?: QueryCondition<T[P]>;
3
3
  } & Operations<T>;
4
4
 
5
- type QueryCondition<FieldType> =
6
- | FieldType
7
- | Conditions<FieldType>;
5
+ export type QueryCondition<FieldType> = FieldType | Conditions<FieldType> | Operations<QueryCondition<FieldType>>;
8
6
 
9
- interface Conditions<ValueType> {
7
+ export interface Conditions<ValueType> {
10
8
  $eq?: ValueType;
11
9
  $ne?: ValueType;
12
10
  $gt?: ValueType;
@@ -15,76 +13,134 @@ interface Conditions<ValueType> {
15
13
  $lte?: ValueType;
16
14
  $in?: ValueType[];
17
15
  $nin?: ValueType[];
16
+ $sub?: ValueType[];
17
+ $nsub?: ValueType[];
18
+ $sup?: ValueType[];
19
+ $nsup?: ValueType[];
20
+ $con?: ValueType;
21
+ $ncon?: ValueType;
18
22
  $exists?: boolean;
19
23
  $match?: RegExp | string;
20
24
  $nmatch?: RegExp | string;
21
25
  $bits?: number;
22
26
  $nbits?: number;
23
- };
27
+ $typeof: string;
28
+ $ntypeof: string;
29
+ }
24
30
 
25
- type ConditionKeys = keyof Conditions<unknown>;
31
+ export type ConditionKeys = keyof Conditions<unknown>;
26
32
 
27
- interface Operations<T> {
33
+ export interface Operations<T> {
28
34
  $and?: Query<T>[];
29
35
  $or?: Query<T>[];
30
36
  $nor?: Query<T>[];
31
37
  $not?: Query<T>;
38
+ }
39
+
40
+ export type OperationKeys = keyof Operations<unknown>;
41
+
42
+ export const stringify = (value: unknown) => {
43
+ if (value === undefined) {
44
+ return 'undefined';
45
+ }
46
+ if (value instanceof RegExp) {
47
+ return `/${value.source}/${value.flags}`;
48
+ }
49
+ return JSON.stringify(value);
32
50
  };
33
51
 
34
- type OperationKeys = keyof Operations<unknown>;
52
+ export interface Condition {
53
+ (valuePath: string, value: any, context: Context): string;
54
+ }
35
55
 
36
- const conditions: Record<ConditionKeys, any> = {
56
+ const conditions: Record<ConditionKeys, Condition> = {
37
57
  $eq: (valuePath: string, value: unknown) => `(${valuePath} === ${value})`,
38
58
  $ne: (valuePath: string, value: unknown) => `(${valuePath} !== ${value})`,
39
59
  $gt: (valuePath: string, value: unknown) => `(${valuePath} > ${value})`,
40
60
  $gte: (valuePath: string, value: unknown) => `(${valuePath} >= ${value})`,
41
61
  $lt: (valuePath: string, value: unknown) => `(${valuePath} < ${value})`,
42
62
  $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})`,
63
+ $in: (valuePath: string, value: unknown, context: Context) => {
64
+ const name = context.register(`new Set(${value})`);
65
+ return `${name}.has(${valuePath})`;
66
+ },
67
+ $nin: (valuePath: string, value: unknown, context: Context) => `!${conditions.$in(valuePath, value, context)}`,
68
+ $sub: (valuePath: string, value: unknown, context: Context) => {
69
+ const name = context.register(`new Set(${value})`);
70
+ return `${valuePath}.every((value) => ${name}.has(value))`;
71
+ },
72
+ $nsub: (valuePath: string, value: unknown, context: Context) => `!${conditions.$sub(valuePath, value, context)}`,
73
+ $sup: (valuePath: string, value: unknown, context: Context) => {
74
+ const name = context.register(`new Set(${valuePath})`);
75
+ return `${value}.every((value) => ${name}.has(value))`;
76
+ },
77
+ $nsup: (valuePath: string, value: unknown, context: Context) => `!${conditions.$sup(valuePath, value, context)}`,
78
+ $con: (valuePath: string, value: unknown) => `${valuePath}.includes(${value})`,
79
+ $ncon: (valuePath: string, value: unknown) => `!${valuePath}.includes(${value})`,
80
+ $exists: (valuePath: string, value: boolean) => `((${valuePath} !== undefined && ${valuePath} !== null) === ${value})`,
81
+ $match: (valuePath: string, value: RegExp) => `${value}.test('' + ${valuePath})`,
82
+ $nmatch: (valuePath: string, value: RegExp) => `!${value}.test('' + ${valuePath})`,
48
83
  $bits: (valuePath: string, value: unknown) => `((${valuePath} & ${value}) !== 0)`,
49
84
  $nbits: (valuePath: string, value: unknown) => `((${valuePath} & ${value}) === 0)`,
85
+ $typeof: (valuePath: string, value: unknown) => `(typeof ${valuePath} === ${value})`,
86
+ $ntypeof: (valuePath: string, value: unknown) => `(typeof ${valuePath} !== ${value})`,
50
87
  };
51
88
 
52
89
  const operations: Record<keyof Operations<unknown>, any> = {
53
90
  $and: (value: unknown[]) => `(${value.join(' && ')})`,
54
91
  $or: (value: unknown[]) => `(${value.join(' || ')})`,
55
- $nor: (value: unknown[]) => `(!(${value.join(' || ')}))`,
56
- $not: (value: unknown) => `(!(${value}))`,
57
- }
92
+ $nor: (value: unknown[]) => `!(${value.join(' || ')})`,
93
+ $not: (value: unknown[]) => `!(${value})`,
94
+ };
58
95
 
96
+ class Context {
97
+ private counter = 0;
98
+ private values: string[] = [];
99
+ private cacheMap = new Map<string, string>();
100
+
101
+ register(code: string) {
102
+ if (!this.cacheMap.has(code)) {
103
+ const name = `v$$${this.counter++}`;
104
+ this.values.push(`const ${name} = ${code};`);
105
+ this.cacheMap.set(code, name);
106
+ }
107
+ return this.cacheMap.get(code)!;
108
+ }
109
+ toString() {
110
+ return this.values.join('\n');
111
+ }
112
+ }
59
113
 
60
- const queryCodeGen = <T extends object>(query: Query<T>, valuePath: string, path: string): string => {
114
+ const filterCodeGen = <T extends object>(query: Query<T> | undefined, valuePath: string, context: Context): string => {
61
115
  if (typeof query === 'object' && query !== null) {
62
116
  const condition = Object.entries(query).map(([key, value]) => {
63
117
  if (key in conditions) {
64
- return conditions[key as ConditionKeys](valuePath, JSON.stringify(value));
118
+ return conditions[key as ConditionKeys](valuePath, stringify(value), context);
65
119
  }
66
120
  if (key in operations) {
67
121
  const values = Array.isArray(value) ? value : [value];
68
- return operations[key as OperationKeys](values.map((value) => queryCodeGen(value, valuePath, path)));
122
+ return operations[key as OperationKeys](values.map((v) => filterCodeGen(v, valuePath, context)));
69
123
  }
70
- const valueSubPath = `${valuePath}?.[${JSON.stringify(key)}]`;
124
+ const valueSubPath = `${valuePath}?.[${stringify(key)}]`;
71
125
  if (value instanceof RegExp) {
72
- return conditions.$match(valueSubPath, value.toString());
126
+ return conditions.$match(valueSubPath, value.toString(), context);
73
127
  }
74
128
  if (typeof value === 'object' && value !== null) {
75
- return queryCodeGen(value, valueSubPath, path);
129
+ return filterCodeGen(value, valueSubPath, context);
76
130
  }
77
- return conditions.$eq(valueSubPath, JSON.stringify(value));
131
+ return conditions.$eq(valueSubPath, JSON.stringify(value), context);
78
132
  });
79
-
80
- return `${condition.join(' && ')}`;
133
+ if (condition.length > 0) {
134
+ return `${condition.join(' && ')}`;
135
+ }
81
136
  }
82
137
  return 'true';
83
- }
138
+ };
84
139
 
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'};`);
140
+ export const createFilter = <T extends object>(query?: Query<T>) => {
141
+ const context = new Context();
142
+ const code = filterCodeGen(query, 'data', context);
143
+ const filter = new Function('data', `${context}\nreturn ${code};`);
88
144
  return (data: T) => filter(data);
89
145
  };
90
146
 
@@ -92,27 +148,29 @@ export type SortType = 'asc' | 'desc' | 1 | -1;
92
148
 
93
149
  export type Sort<T> = T extends object
94
150
  ? {
95
- [K in keyof T]?: T[K] extends object ? Sort<T[K]> : SortType;
96
- }
151
+ [K in keyof T]?: T[K] extends object ? Sort<T[K]> : SortType;
152
+ }
97
153
  : SortType;
98
154
 
99
- export const SORT_EXPR = /^(asc|desc|1|-1)$/i
155
+ const SORT_EXPR = /^(asc|desc|1|-1)$/i;
100
156
 
101
- export const SORT_MAP: Record<SortType, 1 | -1> = {
157
+ const SORT_MAP: Record<SortType, 1 | -1> = {
102
158
  asc: 1,
103
159
  desc: -1,
104
160
  1: 1,
105
161
  '-1': -1,
106
162
  };
107
163
 
108
- export const sortCodeGen = <T>(sort: Sort<T>, valuePath: [string, string], path: string): string => {
164
+ const sortCodeGen = <T>(sort: Sort<T>, valuePath: [string, string]): string => {
109
165
  if (typeof sort === 'object' && sort !== null) {
110
166
  const condition = Object.entries(sort).map(([key, value]) => {
111
167
  const valueSubPathA = `${valuePath[0]}?.[${JSON.stringify(key)}]`;
112
168
  const valueSubPathB = `${valuePath[1]}?.[${JSON.stringify(key)}]`;
113
- return sortCodeGen(value, [valueSubPathA, valueSubPathB], path);
169
+ return sortCodeGen(value, [valueSubPathA, valueSubPathB]);
114
170
  });
115
- return condition.join(' || ');
171
+ if (condition.length > 0) {
172
+ return condition.join(' || ');
173
+ }
116
174
  }
117
175
  if (typeof sort === 'string' && SORT_EXPR.test(sort)) {
118
176
  return `(${valuePath[0]}?.localeCompare(${valuePath[1]}) * ${SORT_MAP[sort as SortType]})`;
@@ -123,9 +181,8 @@ export const sortCodeGen = <T>(sort: Sort<T>, valuePath: [string, string], path:
123
181
  return `0`;
124
182
  };
125
183
 
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);
184
+ export const createSort = <T>(sort: Sort<T>) => {
185
+ const code = sortCodeGen(sort, ['a', 'b']);
186
+ const sortFn = new Function('a', 'b', `return ${code};`);
187
+ return (a: T, b: T) => sortFn(a, b);
131
188
  };
package/.prettierrc DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "tabWidth": 2,
3
- "useTabs": false,
4
- "semi": true,
5
- "singleQuote": true,
6
- "printWidth": 160
7
- }
package/tsconfig.json DELETED
@@ -1,22 +0,0 @@
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
- }