@onchaindb/sdk 0.4.4 → 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/.claude/settings.local.json +10 -2
- package/README.md +422 -355
- package/dist/batch.d.ts +1 -10
- package/dist/batch.d.ts.map +1 -1
- package/dist/batch.js +4 -26
- package/dist/batch.js.map +1 -1
- package/dist/client.d.ts +29 -43
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +199 -323
- package/dist/client.js.map +1 -1
- package/dist/database.d.ts +14 -131
- package/dist/database.d.ts.map +1 -1
- package/dist/database.js +35 -131
- package/dist/database.js.map +1 -1
- package/dist/index.d.ts +6 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -15
- package/dist/index.js.map +1 -1
- package/dist/query-sdk/ConditionBuilder.d.ts +3 -11
- package/dist/query-sdk/ConditionBuilder.d.ts.map +1 -1
- package/dist/query-sdk/ConditionBuilder.js +10 -48
- package/dist/query-sdk/ConditionBuilder.js.map +1 -1
- package/dist/query-sdk/NestedBuilders.d.ts +33 -30
- package/dist/query-sdk/NestedBuilders.d.ts.map +1 -1
- package/dist/query-sdk/NestedBuilders.js +46 -43
- package/dist/query-sdk/NestedBuilders.js.map +1 -1
- package/dist/query-sdk/QueryBuilder.d.ts +4 -2
- package/dist/query-sdk/QueryBuilder.d.ts.map +1 -1
- package/dist/query-sdk/QueryBuilder.js +47 -169
- package/dist/query-sdk/QueryBuilder.js.map +1 -1
- package/dist/query-sdk/QueryResult.d.ts +0 -38
- package/dist/query-sdk/QueryResult.d.ts.map +1 -1
- package/dist/query-sdk/QueryResult.js +1 -227
- package/dist/query-sdk/QueryResult.js.map +1 -1
- package/dist/query-sdk/index.d.ts +1 -1
- package/dist/query-sdk/index.d.ts.map +1 -1
- package/dist/query-sdk/index.js.map +1 -1
- package/dist/query-sdk/operators.d.ts +32 -28
- package/dist/query-sdk/operators.d.ts.map +1 -1
- package/dist/query-sdk/operators.js +45 -155
- package/dist/query-sdk/operators.js.map +1 -1
- package/dist/types.d.ts +153 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/jest.config.js +4 -0
- package/package.json +1 -1
- package/skills.md +0 -1
- package/src/client.ts +243 -745
- package/src/database.ts +70 -493
- package/src/index.ts +40 -193
- package/src/query-sdk/ConditionBuilder.ts +37 -89
- package/src/query-sdk/NestedBuilders.ts +90 -92
- package/src/query-sdk/QueryBuilder.ts +59 -218
- package/src/query-sdk/QueryResult.ts +4 -330
- package/src/query-sdk/README.md +214 -583
- package/src/query-sdk/index.ts +1 -1
- package/src/query-sdk/operators.ts +91 -200
- package/src/query-sdk/tests/FieldConditionBuilder.test.ts +70 -71
- package/src/query-sdk/tests/LogicalOperator.test.ts +43 -82
- package/src/query-sdk/tests/NestedBuilders.test.ts +229 -309
- package/src/query-sdk/tests/QueryBuilder.test.ts +5 -5
- package/src/query-sdk/tests/QueryResult.test.ts +41 -435
- package/src/query-sdk/tests/comprehensive.test.ts +4 -185
- package/src/tests/client-requests.test.ts +280 -0
- package/src/tests/client-validation.test.ts +80 -0
- package/src/types.ts +229 -8
- package/src/batch.ts +0 -257
- package/src/query-sdk/dist/ConditionBuilder.d.ts +0 -22
- package/src/query-sdk/dist/ConditionBuilder.js +0 -90
- package/src/query-sdk/dist/FieldConditionBuilder.d.ts +0 -1
- package/src/query-sdk/dist/FieldConditionBuilder.js +0 -6
- package/src/query-sdk/dist/NestedBuilders.d.ts +0 -43
- package/src/query-sdk/dist/NestedBuilders.js +0 -144
- package/src/query-sdk/dist/OnChainDB.d.ts +0 -19
- package/src/query-sdk/dist/OnChainDB.js +0 -123
- package/src/query-sdk/dist/QueryBuilder.d.ts +0 -70
- package/src/query-sdk/dist/QueryBuilder.js +0 -295
- package/src/query-sdk/dist/QueryResult.d.ts +0 -52
- package/src/query-sdk/dist/QueryResult.js +0 -293
- package/src/query-sdk/dist/SelectionBuilder.d.ts +0 -20
- package/src/query-sdk/dist/SelectionBuilder.js +0 -80
- package/src/query-sdk/dist/adapters/HttpClientAdapter.d.ts +0 -27
- package/src/query-sdk/dist/adapters/HttpClientAdapter.js +0 -170
- package/src/query-sdk/dist/index.d.ts +0 -36
- package/src/query-sdk/dist/index.js +0 -27
- package/src/query-sdk/dist/operators.d.ts +0 -56
- package/src/query-sdk/dist/operators.js +0 -289
- package/src/query-sdk/dist/tests/setup.d.ts +0 -15
- package/src/query-sdk/dist/tests/setup.js +0 -46
- package/src/query-sdk/jest.config.js +0 -25
- package/src/query-sdk/package.json +0 -46
- package/src/query-sdk/tests/aggregations.test.ts +0 -653
- package/src/query-sdk/tests/integration.test.ts +0 -608
- package/src/query-sdk/tests/operators.test.ts +0 -327
- package/src/query-sdk/tests/unit.test.ts +0 -794
- package/src/query-sdk/tsconfig.json +0 -26
- package/src/query-sdk/yarn.lock +0 -3092
package/src/query-sdk/index.ts
CHANGED
|
@@ -47,7 +47,7 @@ export {SelectionBuilder} from './SelectionBuilder';
|
|
|
47
47
|
export {ConditionBuilder} from './ConditionBuilder';
|
|
48
48
|
export {FieldConditionBuilder, LogicalOperator, Condition} from './operators';
|
|
49
49
|
export {NestedConditionBuilder, NestedFieldConditionBuilder} from './NestedBuilders';
|
|
50
|
-
export {QueryResult,
|
|
50
|
+
export {QueryResult, createQueryResult} from './QueryResult';
|
|
51
51
|
|
|
52
52
|
// HTTP Client adapters
|
|
53
53
|
export {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BetweenValue, DateRange, Val } from './index';
|
|
2
2
|
|
|
3
|
-
// Condition represents a single field condition
|
|
3
|
+
// Condition represents a single field condition (internal)
|
|
4
4
|
export interface Condition {
|
|
5
5
|
field: string;
|
|
6
6
|
operator: string;
|
|
@@ -18,22 +18,10 @@ export function createNestedQuery(fieldPath: string, operator: string, value: Va
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const pathParts = fieldPath.split('.');
|
|
21
|
-
let result: any = {
|
|
22
|
-
[operator]: value
|
|
23
|
-
};
|
|
21
|
+
let result: any = { [operator]: value };
|
|
24
22
|
|
|
25
|
-
// Build nested structure from inside out
|
|
26
23
|
for (let i = pathParts.length - 1; i >= 0; i--) {
|
|
27
|
-
|
|
28
|
-
if (i === pathParts.length - 1) {
|
|
29
|
-
result = {
|
|
30
|
-
[part]: result
|
|
31
|
-
};
|
|
32
|
-
} else {
|
|
33
|
-
result = {
|
|
34
|
-
[part]: result
|
|
35
|
-
};
|
|
36
|
-
}
|
|
24
|
+
result = { [pathParts[i]]: result };
|
|
37
25
|
}
|
|
38
26
|
|
|
39
27
|
return result;
|
|
@@ -52,8 +40,7 @@ export class LogicalOperator {
|
|
|
52
40
|
public type: LogicalOperatorType,
|
|
53
41
|
public conditions: LogicalOperator[] = [],
|
|
54
42
|
public condition?: Condition
|
|
55
|
-
) {
|
|
56
|
-
}
|
|
43
|
+
) {}
|
|
57
44
|
|
|
58
45
|
static And(conditions: LogicalOperator[]): LogicalOperator {
|
|
59
46
|
return new LogicalOperator(LogicalOperatorType.AND, conditions);
|
|
@@ -71,24 +58,32 @@ export class LogicalOperator {
|
|
|
71
58
|
return new LogicalOperator(LogicalOperatorType.CONDITION, [], condition);
|
|
72
59
|
}
|
|
73
60
|
|
|
61
|
+
// Chain: wrap this operator inside a new AND group with additional conditions
|
|
62
|
+
and(...conditions: LogicalOperator[]): LogicalOperator {
|
|
63
|
+
return LogicalOperator.And([this, ...conditions]);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Chain: wrap this operator inside a new OR group with additional conditions
|
|
67
|
+
or(...conditions: LogicalOperator[]): LogicalOperator {
|
|
68
|
+
return LogicalOperator.Or([this, ...conditions]);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Chain: wrap this operator inside a new NOT group with additional conditions
|
|
72
|
+
not(...conditions: LogicalOperator[]): LogicalOperator {
|
|
73
|
+
return LogicalOperator.Not([this, ...conditions]);
|
|
74
|
+
}
|
|
75
|
+
|
|
74
76
|
// Convert to composable query structure for HTTP requests
|
|
75
77
|
toComposable(): any {
|
|
76
78
|
switch (this.type) {
|
|
77
79
|
case LogicalOperatorType.AND:
|
|
78
|
-
return {
|
|
79
|
-
[LogicalOperatorType.AND]: this.conditions.map(c => c.toComposable())
|
|
80
|
-
};
|
|
80
|
+
return { [LogicalOperatorType.AND]: this.conditions.map(c => c.toComposable()) };
|
|
81
81
|
case LogicalOperatorType.OR:
|
|
82
|
-
return {
|
|
83
|
-
[LogicalOperatorType.OR]: this.conditions.map(c => c.toComposable())
|
|
84
|
-
};
|
|
82
|
+
return { [LogicalOperatorType.OR]: this.conditions.map(c => c.toComposable()) };
|
|
85
83
|
case LogicalOperatorType.NOT:
|
|
86
|
-
return {
|
|
87
|
-
[LogicalOperatorType.NOT]: this.conditions.map(c => c.toComposable())
|
|
88
|
-
};
|
|
84
|
+
return { [LogicalOperatorType.NOT]: this.conditions.map(c => c.toComposable()) };
|
|
89
85
|
case LogicalOperatorType.CONDITION:
|
|
90
86
|
if (!this.condition) throw new Error('Condition is required for CONDITION type');
|
|
91
|
-
// Use dot notation helper to create nested structures
|
|
92
87
|
return createNestedQuery(this.condition.field, this.condition.operator, this.condition.value);
|
|
93
88
|
default:
|
|
94
89
|
throw new Error(`Unknown logical operator type: ${this.type}`);
|
|
@@ -96,240 +91,136 @@ export class LogicalOperator {
|
|
|
96
91
|
}
|
|
97
92
|
}
|
|
98
93
|
|
|
99
|
-
// Field condition builder
|
|
100
|
-
//
|
|
94
|
+
// Field condition builder — all methods return LogicalOperator directly.
|
|
95
|
+
// No need to wrap with LogicalOperator.Condition() at call sites.
|
|
101
96
|
export class FieldConditionBuilder {
|
|
102
|
-
constructor(private fieldName: string) {
|
|
97
|
+
constructor(private fieldName: string) {}
|
|
98
|
+
|
|
99
|
+
private cond(operator: string, value: Val | BetweenValue | DateRange): LogicalOperator {
|
|
100
|
+
return LogicalOperator.Condition({ field: this.fieldName, operator, value });
|
|
103
101
|
}
|
|
104
102
|
|
|
105
|
-
// ===== BASE OPERATORS
|
|
103
|
+
// ===== BASE OPERATORS =====
|
|
106
104
|
|
|
107
|
-
equals(value: Val):
|
|
108
|
-
return
|
|
109
|
-
field: this.fieldName,
|
|
110
|
-
operator: 'is',
|
|
111
|
-
value
|
|
112
|
-
};
|
|
105
|
+
equals(value: Val): LogicalOperator {
|
|
106
|
+
return this.cond('is', value);
|
|
113
107
|
}
|
|
114
108
|
|
|
115
|
-
notEquals(value: Val):
|
|
116
|
-
return
|
|
117
|
-
field: this.fieldName,
|
|
118
|
-
operator: 'isNot',
|
|
119
|
-
value
|
|
120
|
-
};
|
|
109
|
+
notEquals(value: Val): LogicalOperator {
|
|
110
|
+
return this.cond('isNot', value);
|
|
121
111
|
}
|
|
122
112
|
|
|
123
|
-
in(values: Val[]):
|
|
124
|
-
return
|
|
125
|
-
field: this.fieldName,
|
|
126
|
-
operator: 'in',
|
|
127
|
-
value: values as any
|
|
128
|
-
};
|
|
113
|
+
in(values: Val[]): LogicalOperator {
|
|
114
|
+
return this.cond('in', values as any);
|
|
129
115
|
}
|
|
130
116
|
|
|
131
|
-
notIn(values: Val[]):
|
|
132
|
-
return
|
|
133
|
-
field: this.fieldName,
|
|
134
|
-
operator: 'notIn',
|
|
135
|
-
value: values as any
|
|
136
|
-
};
|
|
117
|
+
notIn(values: Val[]): LogicalOperator {
|
|
118
|
+
return this.cond('notIn', values as any);
|
|
137
119
|
}
|
|
138
120
|
|
|
139
|
-
isNull():
|
|
140
|
-
return
|
|
141
|
-
field: this.fieldName,
|
|
142
|
-
operator: 'isNull',
|
|
143
|
-
value: true
|
|
144
|
-
};
|
|
121
|
+
isNull(): LogicalOperator {
|
|
122
|
+
return this.cond('isNull', true);
|
|
145
123
|
}
|
|
146
124
|
|
|
147
|
-
isNotNull():
|
|
148
|
-
return
|
|
149
|
-
field: this.fieldName,
|
|
150
|
-
operator: 'isNull',
|
|
151
|
-
value: false
|
|
152
|
-
};
|
|
125
|
+
isNotNull(): LogicalOperator {
|
|
126
|
+
return this.cond('isNull', false);
|
|
153
127
|
}
|
|
154
128
|
|
|
155
|
-
exists():
|
|
156
|
-
return
|
|
157
|
-
field: this.fieldName,
|
|
158
|
-
operator: 'exists',
|
|
159
|
-
value: true
|
|
160
|
-
};
|
|
129
|
+
exists(): LogicalOperator {
|
|
130
|
+
return this.cond('exists', true);
|
|
161
131
|
}
|
|
162
132
|
|
|
163
|
-
notExists():
|
|
164
|
-
return
|
|
165
|
-
field: this.fieldName,
|
|
166
|
-
operator: 'exists',
|
|
167
|
-
value: false
|
|
168
|
-
};
|
|
133
|
+
notExists(): LogicalOperator {
|
|
134
|
+
return this.cond('exists', false);
|
|
169
135
|
}
|
|
170
136
|
|
|
171
|
-
// ===== STRING OPERATORS
|
|
137
|
+
// ===== STRING OPERATORS =====
|
|
172
138
|
|
|
173
|
-
startsWith(value: string):
|
|
174
|
-
return
|
|
175
|
-
field: this.fieldName,
|
|
176
|
-
operator: 'startsWith',
|
|
177
|
-
value
|
|
178
|
-
};
|
|
139
|
+
startsWith(value: string): LogicalOperator {
|
|
140
|
+
return this.cond('startsWith', value);
|
|
179
141
|
}
|
|
180
142
|
|
|
181
|
-
endsWith(value: string):
|
|
182
|
-
return
|
|
183
|
-
field: this.fieldName,
|
|
184
|
-
operator: 'endsWith',
|
|
185
|
-
value
|
|
186
|
-
};
|
|
143
|
+
endsWith(value: string): LogicalOperator {
|
|
144
|
+
return this.cond('endsWith', value);
|
|
187
145
|
}
|
|
188
146
|
|
|
189
|
-
contains(value: string):
|
|
190
|
-
return
|
|
191
|
-
field: this.fieldName,
|
|
192
|
-
operator: 'includes',
|
|
193
|
-
value
|
|
194
|
-
};
|
|
147
|
+
contains(value: string): LogicalOperator {
|
|
148
|
+
return this.cond('includes', value);
|
|
195
149
|
}
|
|
196
150
|
|
|
197
|
-
regExpMatches(pattern: string):
|
|
198
|
-
return
|
|
199
|
-
field: this.fieldName,
|
|
200
|
-
operator: 'regExpMatches',
|
|
201
|
-
value: pattern
|
|
202
|
-
};
|
|
151
|
+
regExpMatches(pattern: string): LogicalOperator {
|
|
152
|
+
return this.cond('regExpMatches', pattern);
|
|
203
153
|
}
|
|
204
154
|
|
|
205
|
-
includesCaseInsensitive(value: string):
|
|
206
|
-
return
|
|
207
|
-
field: this.fieldName,
|
|
208
|
-
operator: 'includesCaseInsensitive',
|
|
209
|
-
value
|
|
210
|
-
};
|
|
155
|
+
includesCaseInsensitive(value: string): LogicalOperator {
|
|
156
|
+
return this.cond('includesCaseInsensitive', value);
|
|
211
157
|
}
|
|
212
158
|
|
|
213
|
-
startsWithCaseInsensitive(value: string):
|
|
214
|
-
return
|
|
215
|
-
field: this.fieldName,
|
|
216
|
-
operator: 'startsWithCaseInsensitive',
|
|
217
|
-
value
|
|
218
|
-
};
|
|
159
|
+
startsWithCaseInsensitive(value: string): LogicalOperator {
|
|
160
|
+
return this.cond('startsWithCaseInsensitive', value);
|
|
219
161
|
}
|
|
220
162
|
|
|
221
|
-
endsWithCaseInsensitive(value: string):
|
|
222
|
-
return
|
|
223
|
-
field: this.fieldName,
|
|
224
|
-
operator: 'endsWithCaseInsensitive',
|
|
225
|
-
value
|
|
226
|
-
};
|
|
163
|
+
endsWithCaseInsensitive(value: string): LogicalOperator {
|
|
164
|
+
return this.cond('endsWithCaseInsensitive', value);
|
|
227
165
|
}
|
|
228
166
|
|
|
229
|
-
// ===== NUMBER OPERATORS
|
|
167
|
+
// ===== NUMBER OPERATORS =====
|
|
230
168
|
|
|
231
|
-
greaterThan(value: Val):
|
|
232
|
-
return
|
|
233
|
-
field: this.fieldName,
|
|
234
|
-
operator: 'greaterThan',
|
|
235
|
-
value
|
|
236
|
-
};
|
|
169
|
+
greaterThan(value: Val): LogicalOperator {
|
|
170
|
+
return this.cond('greaterThan', value);
|
|
237
171
|
}
|
|
238
172
|
|
|
239
|
-
lessThan(value: Val):
|
|
240
|
-
return
|
|
241
|
-
field: this.fieldName,
|
|
242
|
-
operator: 'lessThan',
|
|
243
|
-
value
|
|
244
|
-
};
|
|
173
|
+
lessThan(value: Val): LogicalOperator {
|
|
174
|
+
return this.cond('lessThan', value);
|
|
245
175
|
}
|
|
246
176
|
|
|
247
|
-
greaterThanOrEqual(value: Val):
|
|
248
|
-
return
|
|
249
|
-
field: this.fieldName,
|
|
250
|
-
operator: 'greaterThanOrEqual',
|
|
251
|
-
value
|
|
252
|
-
};
|
|
177
|
+
greaterThanOrEqual(value: Val): LogicalOperator {
|
|
178
|
+
return this.cond('greaterThanOrEqual', value);
|
|
253
179
|
}
|
|
254
180
|
|
|
255
|
-
lessThanOrEqual(value: Val):
|
|
256
|
-
return
|
|
257
|
-
field: this.fieldName,
|
|
258
|
-
operator: 'lessThanOrEqual',
|
|
259
|
-
value
|
|
260
|
-
};
|
|
181
|
+
lessThanOrEqual(value: Val): LogicalOperator {
|
|
182
|
+
return this.cond('lessThanOrEqual', value);
|
|
261
183
|
}
|
|
262
184
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
isLocalIp(): Condition {
|
|
266
|
-
return {
|
|
267
|
-
field: this.fieldName,
|
|
268
|
-
operator: 'isLocalIp',
|
|
269
|
-
value: true
|
|
270
|
-
};
|
|
185
|
+
between(min: Val, max: Val): LogicalOperator {
|
|
186
|
+
return this.cond('betweenOp', { from: min, to: max } as any);
|
|
271
187
|
}
|
|
272
188
|
|
|
273
|
-
|
|
274
|
-
return {
|
|
275
|
-
field: this.fieldName,
|
|
276
|
-
operator: 'isExternalIp',
|
|
277
|
-
value: true
|
|
278
|
-
};
|
|
279
|
-
}
|
|
189
|
+
// ===== IP OPERATORS =====
|
|
280
190
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
b64(value: string): Condition {
|
|
284
|
-
return {
|
|
285
|
-
field: this.fieldName,
|
|
286
|
-
operator: 'b64',
|
|
287
|
-
value
|
|
288
|
-
};
|
|
191
|
+
isLocalIp(): LogicalOperator {
|
|
192
|
+
return this.cond('isLocalIp', true);
|
|
289
193
|
}
|
|
290
194
|
|
|
291
|
-
|
|
292
|
-
return
|
|
293
|
-
field: this.fieldName,
|
|
294
|
-
operator: 'inDataset',
|
|
295
|
-
value: dataset
|
|
296
|
-
};
|
|
195
|
+
isExternalIp(): LogicalOperator {
|
|
196
|
+
return this.cond('isExternalIp', true);
|
|
297
197
|
}
|
|
298
198
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
value: countryCode
|
|
304
|
-
};
|
|
199
|
+
// ===== MISC OPERATORS =====
|
|
200
|
+
|
|
201
|
+
b64(value: string): LogicalOperator {
|
|
202
|
+
return this.cond('b64', value);
|
|
305
203
|
}
|
|
306
204
|
|
|
307
|
-
|
|
308
|
-
return
|
|
309
|
-
field: this.fieldName,
|
|
310
|
-
operator: 'CIDR',
|
|
311
|
-
value: cidr
|
|
312
|
-
};
|
|
205
|
+
inDataset(dataset: string): LogicalOperator {
|
|
206
|
+
return this.cond('inDataset', dataset);
|
|
313
207
|
}
|
|
314
208
|
|
|
315
|
-
|
|
209
|
+
inCountry(countryCode: string): LogicalOperator {
|
|
210
|
+
return this.cond('inCountry', countryCode);
|
|
211
|
+
}
|
|
316
212
|
|
|
317
|
-
|
|
318
|
-
return
|
|
319
|
-
field: this.fieldName,
|
|
320
|
-
operator: 'betweenOp',
|
|
321
|
-
value: { from: min, to: max } as any
|
|
322
|
-
};
|
|
213
|
+
cidr(cidr: string): LogicalOperator {
|
|
214
|
+
return this.cond('CIDR', cidr);
|
|
323
215
|
}
|
|
324
216
|
|
|
325
|
-
// ===== CONVENIENCE
|
|
217
|
+
// ===== CONVENIENCE =====
|
|
326
218
|
|
|
327
|
-
|
|
328
|
-
isTrue(): Condition {
|
|
219
|
+
isTrue(): LogicalOperator {
|
|
329
220
|
return this.equals(true);
|
|
330
221
|
}
|
|
331
222
|
|
|
332
|
-
isFalse():
|
|
223
|
+
isFalse(): LogicalOperator {
|
|
333
224
|
return this.equals(false);
|
|
334
225
|
}
|
|
335
|
-
}
|
|
226
|
+
}
|
|
@@ -1,84 +1,83 @@
|
|
|
1
1
|
import { FieldConditionBuilder, LogicalOperator } from '../index';
|
|
2
2
|
|
|
3
3
|
describe('FieldConditionBuilder', () => {
|
|
4
|
-
|
|
5
|
-
const builder = new FieldConditionBuilder('name');
|
|
4
|
+
let builder: FieldConditionBuilder;
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
test('should support dot notation in field paths', () => {
|
|
12
|
-
const condition = new FieldConditionBuilder('user.profile.bio').equals('Developer');
|
|
13
|
-
const logicalOp = LogicalOperator.Condition(condition);
|
|
14
|
-
const composable = logicalOp.toComposable();
|
|
15
|
-
|
|
16
|
-
const expected = {
|
|
17
|
-
user: {
|
|
18
|
-
profile: {
|
|
19
|
-
bio: {
|
|
20
|
-
is: 'Developer'
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
expect(composable).toEqual(expected);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
test('should have all required operators available', () => {
|
|
30
|
-
const builder = new FieldConditionBuilder('test_field');
|
|
31
|
-
|
|
32
|
-
// Only test operators that actually exist in the Rust implementation
|
|
33
|
-
const operators = [
|
|
34
|
-
'equals', 'notEquals', 'greaterThan', 'lessThan', 'greaterThanOrEqual', 'lessThanOrEqual',
|
|
35
|
-
'contains', 'startsWith', 'endsWith', 'in', 'notIn', 'exists', 'notExists',
|
|
36
|
-
'isNull', 'isNotNull', 'regExpMatches', 'includesCaseInsensitive',
|
|
37
|
-
'startsWithCaseInsensitive', 'endsWithCaseInsensitive', 'between',
|
|
38
|
-
'isLocalIp', 'isExternalIp', 'b64', 'inDataset', 'inCountry', 'cidr',
|
|
39
|
-
'isTrue', 'isFalse'
|
|
40
|
-
];
|
|
41
|
-
|
|
42
|
-
operators.forEach(op => {
|
|
43
|
-
expect(typeof (builder as any)[op]).toBe('function');
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
builder = new FieldConditionBuilder('field');
|
|
44
8
|
});
|
|
45
|
-
});
|
|
46
9
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
10
|
+
describe('Comparison operators', () => {
|
|
11
|
+
test('equals', () => expect(builder.equals('v').toComposable()).toEqual({ field: { is: 'v' } }));
|
|
12
|
+
test('notEquals', () => expect(builder.notEquals('v').toComposable()).toEqual({ field: { isNot: 'v' } }));
|
|
13
|
+
test('greaterThan', () => expect(builder.greaterThan(10).toComposable()).toEqual({ field: { greaterThan: 10 } }));
|
|
14
|
+
test('lessThan', () => expect(builder.lessThan(10).toComposable()).toEqual({ field: { lessThan: 10 } }));
|
|
15
|
+
test('greaterThanOrEqual', () => expect(builder.greaterThanOrEqual(10).toComposable()).toEqual({ field: { greaterThanOrEqual: 10 } }));
|
|
16
|
+
test('lessThanOrEqual', () => expect(builder.lessThanOrEqual(10).toComposable()).toEqual({ field: { lessThanOrEqual: 10 } }));
|
|
17
|
+
test('between', () => expect(builder.between(10, 20).toComposable()).toEqual({ field: { betweenOp: { from: 10, to: 20 } } }));
|
|
18
|
+
});
|
|
51
19
|
|
|
52
|
-
|
|
53
|
-
|
|
20
|
+
describe('String operators', () => {
|
|
21
|
+
test('contains', () => expect(builder.contains('sub').toComposable()).toEqual({ field: { includes: 'sub' } }));
|
|
22
|
+
test('startsWith', () => expect(builder.startsWith('pre').toComposable()).toEqual({ field: { startsWith: 'pre' } }));
|
|
23
|
+
test('endsWith', () => expect(builder.endsWith('suf').toComposable()).toEqual({ field: { endsWith: 'suf' } }));
|
|
24
|
+
test('regExpMatches', () => expect(builder.regExpMatches('.*').toComposable()).toEqual({ field: { regExpMatches: '.*' } }));
|
|
25
|
+
test('includesCaseInsensitive', () => expect(builder.includesCaseInsensitive('T').toComposable()).toEqual({ field: { includesCaseInsensitive: 'T' } }));
|
|
26
|
+
test('startsWithCaseInsensitive', () => expect(builder.startsWithCaseInsensitive('P').toComposable()).toEqual({ field: { startsWithCaseInsensitive: 'P' } }));
|
|
27
|
+
test('endsWithCaseInsensitive', () => expect(builder.endsWithCaseInsensitive('S').toComposable()).toEqual({ field: { endsWithCaseInsensitive: 'S' } }));
|
|
28
|
+
});
|
|
54
29
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const ipExternalCondition = ipBuilder.isExternalIp();
|
|
60
|
-
expect(ipExternalCondition.operator).toBe('isExternalIp');
|
|
30
|
+
describe('Array operators', () => {
|
|
31
|
+
test('in', () => expect(builder.in([1, 2, 3]).toComposable()).toEqual({ field: { in: [1, 2, 3] } }));
|
|
32
|
+
test('notIn', () => expect(builder.notIn(['a', 'b']).toComposable()).toEqual({ field: { notIn: ['a', 'b'] } }));
|
|
33
|
+
});
|
|
61
34
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
35
|
+
describe('Existence operators', () => {
|
|
36
|
+
test('exists', () => expect(builder.exists().toComposable()).toEqual({ field: { exists: true } }));
|
|
37
|
+
test('notExists', () => expect(builder.notExists().toComposable()).toEqual({ field: { exists: false } }));
|
|
38
|
+
test('isNull', () => expect(builder.isNull().toComposable()).toEqual({ field: { isNull: true } }));
|
|
39
|
+
test('isNotNull', () => expect(builder.isNotNull().toComposable()).toEqual({ field: { isNull: false } }));
|
|
40
|
+
});
|
|
67
41
|
|
|
68
|
-
|
|
69
|
-
|
|
42
|
+
describe('Boolean convenience', () => {
|
|
43
|
+
test('isTrue', () => expect(builder.isTrue().toComposable()).toEqual({ field: { is: true } }));
|
|
44
|
+
test('isFalse', () => expect(builder.isFalse().toComposable()).toEqual({ field: { is: false } }));
|
|
45
|
+
});
|
|
70
46
|
|
|
71
|
-
|
|
72
|
-
|
|
47
|
+
describe('Network / security operators', () => {
|
|
48
|
+
test('isLocalIp', () => expect(builder.isLocalIp().toComposable()).toEqual({ field: { isLocalIp: true } }));
|
|
49
|
+
test('isExternalIp', () => expect(builder.isExternalIp().toComposable()).toEqual({ field: { isExternalIp: true } }));
|
|
50
|
+
test('b64', () => expect(builder.b64('dGVzdA==').toComposable()).toEqual({ field: { b64: 'dGVzdA==' } }));
|
|
51
|
+
test('inDataset', () => expect(builder.inDataset('malware_ips').toComposable()).toEqual({ field: { inDataset: 'malware_ips' } }));
|
|
52
|
+
test('inCountry', () => expect(builder.inCountry('US').toComposable()).toEqual({ field: { inCountry: 'US' } }));
|
|
53
|
+
test('cidr', () => expect(builder.cidr('192.168.1.0/24').toComposable()).toEqual({ field: { CIDR: '192.168.1.0/24' } }));
|
|
54
|
+
});
|
|
73
55
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
56
|
+
describe('Dot-notation fields', () => {
|
|
57
|
+
test('handles nested path', () => {
|
|
58
|
+
expect(new FieldConditionBuilder('user.profile.bio').equals('Dev').toComposable()).toEqual({
|
|
59
|
+
user: { profile: { bio: { is: 'Dev' } } }
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('handles deeply nested path', () => {
|
|
64
|
+
expect(new FieldConditionBuilder('a.b.c.d').greaterThan(10).toComposable()).toEqual({
|
|
65
|
+
a: { b: { c: { d: { greaterThan: 10 } } } }
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
|
77
69
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
70
|
+
describe('Returns LogicalOperator', () => {
|
|
71
|
+
test('each method returns a LogicalOperator with correct condition', () => {
|
|
72
|
+
const lo = new FieldConditionBuilder('name').equals('John');
|
|
73
|
+
expect(lo.type).toBe('condition');
|
|
74
|
+
expect(lo.condition?.field).toBe('name');
|
|
75
|
+
expect(lo.condition?.operator).toBe('is');
|
|
76
|
+
expect(lo.condition?.value).toBe('John');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('empty field name does not throw', () => {
|
|
80
|
+
expect(() => new FieldConditionBuilder('').equals('test').toComposable()).not.toThrow();
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
});
|