@onchaindb/sdk 0.4.0 → 0.4.2
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/.DS_Store +0 -0
- package/.claude/settings.local.json +8 -0
- package/.gitignore +5 -0
- package/.idea/.gitignore +5 -0
- package/.idea/compiler.xml +6 -0
- package/.idea/inspectionProfiles/Project_Default.xml +6 -0
- package/.idea/jsLinters/eslint.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/prettier.xml +7 -0
- package/.idea/sdk.iml +12 -0
- package/.idea/vcs.xml +6 -0
- package/.idea/workspace.xml +257 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +11 -3
- package/dist/client.js.map +1 -1
- package/dist/database.d.ts +0 -20
- package/dist/database.d.ts.map +1 -1
- package/dist/database.js +0 -40
- package/dist/database.js.map +1 -1
- package/dist/query-sdk/tests/setup.d.ts +16 -0
- package/dist/query-sdk/tests/setup.d.ts.map +1 -0
- package/dist/query-sdk/tests/setup.js +49 -0
- package/dist/query-sdk/tests/setup.js.map +1 -0
- package/examples/basic-usage.ts +136 -0
- package/examples/blob-upload-example.ts +140 -0
- package/examples/collection-schema-example.ts +304 -0
- package/examples/server-side-joins.ts +201 -0
- package/examples/tweet-self-joins-example.ts +352 -0
- package/package-lock.json +3823 -0
- package/package.json +1 -1
- package/skills.md +1096 -0
- package/src/.env +1 -0
- package/src/batch.d.ts +121 -0
- package/src/batch.js +205 -0
- package/src/batch.ts +257 -0
- package/src/client.ts +1856 -0
- package/src/database.d.ts +268 -0
- package/src/database.js +294 -0
- package/src/database.ts +695 -0
- package/src/index.d.ts +160 -0
- package/src/index.js +186 -0
- package/src/index.ts +253 -0
- package/src/query-sdk/ConditionBuilder.ts +103 -0
- package/src/query-sdk/FieldConditionBuilder.ts +2 -0
- package/src/query-sdk/NestedBuilders.ts +186 -0
- package/src/query-sdk/OnChainDB.ts +294 -0
- package/src/query-sdk/QueryBuilder.ts +1191 -0
- package/src/query-sdk/QueryResult.ts +375 -0
- package/src/query-sdk/README.md +866 -0
- package/src/query-sdk/SelectionBuilder.ts +94 -0
- package/src/query-sdk/adapters/HttpClientAdapter.ts +249 -0
- package/src/query-sdk/dist/ConditionBuilder.d.ts +22 -0
- package/src/query-sdk/dist/ConditionBuilder.js +90 -0
- package/src/query-sdk/dist/FieldConditionBuilder.d.ts +1 -0
- package/src/query-sdk/dist/FieldConditionBuilder.js +6 -0
- package/src/query-sdk/dist/NestedBuilders.d.ts +43 -0
- package/src/query-sdk/dist/NestedBuilders.js +144 -0
- package/src/query-sdk/dist/OnChainDB.d.ts +19 -0
- package/src/query-sdk/dist/OnChainDB.js +123 -0
- package/src/query-sdk/dist/QueryBuilder.d.ts +70 -0
- package/src/query-sdk/dist/QueryBuilder.js +295 -0
- package/src/query-sdk/dist/QueryResult.d.ts +52 -0
- package/src/query-sdk/dist/QueryResult.js +293 -0
- package/src/query-sdk/dist/SelectionBuilder.d.ts +20 -0
- package/src/query-sdk/dist/SelectionBuilder.js +80 -0
- package/src/query-sdk/dist/adapters/HttpClientAdapter.d.ts +27 -0
- package/src/query-sdk/dist/adapters/HttpClientAdapter.js +170 -0
- package/src/query-sdk/dist/index.d.ts +36 -0
- package/src/query-sdk/dist/index.js +27 -0
- package/src/query-sdk/dist/operators.d.ts +56 -0
- package/src/query-sdk/dist/operators.js +289 -0
- package/src/query-sdk/dist/tests/setup.d.ts +15 -0
- package/src/query-sdk/dist/tests/setup.js +46 -0
- package/src/query-sdk/index.ts +59 -0
- package/src/query-sdk/jest.config.js +25 -0
- package/src/query-sdk/operators.ts +335 -0
- package/src/query-sdk/package.json +46 -0
- package/src/query-sdk/tests/FieldConditionBuilder.test.ts +84 -0
- package/src/query-sdk/tests/LogicalOperator.test.ts +85 -0
- package/src/query-sdk/tests/NestedBuilders.test.ts +321 -0
- package/src/query-sdk/tests/QueryBuilder.test.ts +348 -0
- package/src/query-sdk/tests/QueryResult.test.ts +464 -0
- package/src/query-sdk/tests/aggregations.test.ts +653 -0
- package/src/query-sdk/tests/comprehensive.test.ts +279 -0
- package/src/query-sdk/tests/integration.test.ts +608 -0
- package/src/query-sdk/tests/operators.test.ts +327 -0
- package/src/query-sdk/tests/setup.ts +59 -0
- package/src/query-sdk/tests/unit.test.ts +794 -0
- package/src/query-sdk/tsconfig.json +26 -0
- package/src/query-sdk/yarn.lock +3092 -0
- package/src/types.d.ts +131 -0
- package/src/types.js +46 -0
- package/src/types.ts +534 -0
- package/src/x402/index.ts +12 -0
- package/src/x402/types.ts +250 -0
- package/src/x402/utils.ts +332 -0
- package/tsconfig.json +20 -0
- package/yarn.lock +2309 -0
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import { BetweenValue, DateRange, Val } from './index';
|
|
2
|
+
|
|
3
|
+
// Condition represents a single field condition
|
|
4
|
+
export interface Condition {
|
|
5
|
+
field: string;
|
|
6
|
+
operator: string;
|
|
7
|
+
value: Val | BetweenValue | DateRange;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Helper function to convert dot notation paths to nested structures
|
|
11
|
+
export function createNestedQuery(fieldPath: string, operator: string, value: Val | BetweenValue | DateRange): any {
|
|
12
|
+
if (!fieldPath.includes('.')) {
|
|
13
|
+
return {
|
|
14
|
+
[fieldPath]: {
|
|
15
|
+
[operator]: value
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const pathParts = fieldPath.split('.');
|
|
21
|
+
let result: any = {
|
|
22
|
+
[operator]: value
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Build nested structure from inside out
|
|
26
|
+
for (let i = pathParts.length - 1; i >= 0; i--) {
|
|
27
|
+
const part = pathParts[i];
|
|
28
|
+
if (i === pathParts.length - 1) {
|
|
29
|
+
result = {
|
|
30
|
+
[part]: result
|
|
31
|
+
};
|
|
32
|
+
} else {
|
|
33
|
+
result = {
|
|
34
|
+
[part]: result
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Logical operators for combining conditions
|
|
43
|
+
export enum LogicalOperatorType {
|
|
44
|
+
AND = 'and',
|
|
45
|
+
OR = 'or',
|
|
46
|
+
NOT = 'not',
|
|
47
|
+
CONDITION = 'condition'
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export class LogicalOperator {
|
|
51
|
+
constructor(
|
|
52
|
+
public type: LogicalOperatorType,
|
|
53
|
+
public conditions: LogicalOperator[] = [],
|
|
54
|
+
public condition?: Condition
|
|
55
|
+
) {
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static And(conditions: LogicalOperator[]): LogicalOperator {
|
|
59
|
+
return new LogicalOperator(LogicalOperatorType.AND, conditions);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
static Or(conditions: LogicalOperator[]): LogicalOperator {
|
|
63
|
+
return new LogicalOperator(LogicalOperatorType.OR, conditions);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static Not(conditions: LogicalOperator[]): LogicalOperator {
|
|
67
|
+
return new LogicalOperator(LogicalOperatorType.NOT, conditions);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static Condition(condition: Condition): LogicalOperator {
|
|
71
|
+
return new LogicalOperator(LogicalOperatorType.CONDITION, [], condition);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Convert to composable query structure for HTTP requests
|
|
75
|
+
toComposable(): any {
|
|
76
|
+
switch (this.type) {
|
|
77
|
+
case LogicalOperatorType.AND:
|
|
78
|
+
return {
|
|
79
|
+
[LogicalOperatorType.AND]: this.conditions.map(c => c.toComposable())
|
|
80
|
+
};
|
|
81
|
+
case LogicalOperatorType.OR:
|
|
82
|
+
return {
|
|
83
|
+
[LogicalOperatorType.OR]: this.conditions.map(c => c.toComposable())
|
|
84
|
+
};
|
|
85
|
+
case LogicalOperatorType.NOT:
|
|
86
|
+
return {
|
|
87
|
+
[LogicalOperatorType.NOT]: this.conditions.map(c => c.toComposable())
|
|
88
|
+
};
|
|
89
|
+
case LogicalOperatorType.CONDITION:
|
|
90
|
+
if (!this.condition) throw new Error('Condition is required for CONDITION type');
|
|
91
|
+
// Use dot notation helper to create nested structures
|
|
92
|
+
return createNestedQuery(this.condition.field, this.condition.operator, this.condition.value);
|
|
93
|
+
default:
|
|
94
|
+
throw new Error(`Unknown logical operator type: ${this.type}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Field condition builder for creating individual field conditions
|
|
100
|
+
// Only includes operators that actually exist in the Rust implementation
|
|
101
|
+
export class FieldConditionBuilder {
|
|
102
|
+
constructor(private fieldName: string) {
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ===== BASE OPERATORS (BaseOperator) =====
|
|
106
|
+
|
|
107
|
+
equals(value: Val): Condition {
|
|
108
|
+
return {
|
|
109
|
+
field: this.fieldName,
|
|
110
|
+
operator: 'is',
|
|
111
|
+
value
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
notEquals(value: Val): Condition {
|
|
116
|
+
return {
|
|
117
|
+
field: this.fieldName,
|
|
118
|
+
operator: 'isNot',
|
|
119
|
+
value
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
in(values: Val[]): Condition {
|
|
124
|
+
return {
|
|
125
|
+
field: this.fieldName,
|
|
126
|
+
operator: 'in',
|
|
127
|
+
value: values as any
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
notIn(values: Val[]): Condition {
|
|
132
|
+
return {
|
|
133
|
+
field: this.fieldName,
|
|
134
|
+
operator: 'notIn',
|
|
135
|
+
value: values as any
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
isNull(): Condition {
|
|
140
|
+
return {
|
|
141
|
+
field: this.fieldName,
|
|
142
|
+
operator: 'isNull',
|
|
143
|
+
value: true
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
isNotNull(): Condition {
|
|
148
|
+
return {
|
|
149
|
+
field: this.fieldName,
|
|
150
|
+
operator: 'isNull',
|
|
151
|
+
value: false
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
exists(): Condition {
|
|
156
|
+
return {
|
|
157
|
+
field: this.fieldName,
|
|
158
|
+
operator: 'exists',
|
|
159
|
+
value: true
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
notExists(): Condition {
|
|
164
|
+
return {
|
|
165
|
+
field: this.fieldName,
|
|
166
|
+
operator: 'exists',
|
|
167
|
+
value: false
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ===== STRING OPERATORS (StringOperator) =====
|
|
172
|
+
|
|
173
|
+
startsWith(value: string): Condition {
|
|
174
|
+
return {
|
|
175
|
+
field: this.fieldName,
|
|
176
|
+
operator: 'startsWith',
|
|
177
|
+
value
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
endsWith(value: string): Condition {
|
|
182
|
+
return {
|
|
183
|
+
field: this.fieldName,
|
|
184
|
+
operator: 'endsWith',
|
|
185
|
+
value
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
contains(value: string): Condition {
|
|
190
|
+
return {
|
|
191
|
+
field: this.fieldName,
|
|
192
|
+
operator: 'includes',
|
|
193
|
+
value
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
regExpMatches(pattern: string): Condition {
|
|
198
|
+
return {
|
|
199
|
+
field: this.fieldName,
|
|
200
|
+
operator: 'regExpMatches',
|
|
201
|
+
value: pattern
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
includesCaseInsensitive(value: string): Condition {
|
|
206
|
+
return {
|
|
207
|
+
field: this.fieldName,
|
|
208
|
+
operator: 'includesCaseInsensitive',
|
|
209
|
+
value
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
startsWithCaseInsensitive(value: string): Condition {
|
|
214
|
+
return {
|
|
215
|
+
field: this.fieldName,
|
|
216
|
+
operator: 'startsWithCaseInsensitive',
|
|
217
|
+
value
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
endsWithCaseInsensitive(value: string): Condition {
|
|
222
|
+
return {
|
|
223
|
+
field: this.fieldName,
|
|
224
|
+
operator: 'endsWithCaseInsensitive',
|
|
225
|
+
value
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ===== NUMBER OPERATORS (NumberOperator) =====
|
|
230
|
+
|
|
231
|
+
greaterThan(value: Val): Condition {
|
|
232
|
+
return {
|
|
233
|
+
field: this.fieldName,
|
|
234
|
+
operator: 'greaterThan',
|
|
235
|
+
value
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
lessThan(value: Val): Condition {
|
|
240
|
+
return {
|
|
241
|
+
field: this.fieldName,
|
|
242
|
+
operator: 'lessThan',
|
|
243
|
+
value
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
greaterThanOrEqual(value: Val): Condition {
|
|
248
|
+
return {
|
|
249
|
+
field: this.fieldName,
|
|
250
|
+
operator: 'greaterThanOrEqual',
|
|
251
|
+
value
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
lessThanOrEqual(value: Val): Condition {
|
|
256
|
+
return {
|
|
257
|
+
field: this.fieldName,
|
|
258
|
+
operator: 'lessThanOrEqual',
|
|
259
|
+
value
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ===== IP OPERATORS (IpOperator) =====
|
|
264
|
+
|
|
265
|
+
isLocalIp(): Condition {
|
|
266
|
+
return {
|
|
267
|
+
field: this.fieldName,
|
|
268
|
+
operator: 'isLocalIp',
|
|
269
|
+
value: true
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
isExternalIp(): Condition {
|
|
274
|
+
return {
|
|
275
|
+
field: this.fieldName,
|
|
276
|
+
operator: 'isExternalIp',
|
|
277
|
+
value: true
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ===== MISC OPERATORS (MiscOperator) =====
|
|
282
|
+
|
|
283
|
+
b64(value: string): Condition {
|
|
284
|
+
return {
|
|
285
|
+
field: this.fieldName,
|
|
286
|
+
operator: 'b64',
|
|
287
|
+
value
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
inDataset(dataset: string): Condition {
|
|
292
|
+
return {
|
|
293
|
+
field: this.fieldName,
|
|
294
|
+
operator: 'inDataset',
|
|
295
|
+
value: dataset
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
inCountry(countryCode: string): Condition {
|
|
300
|
+
return {
|
|
301
|
+
field: this.fieldName,
|
|
302
|
+
operator: 'inCountry',
|
|
303
|
+
value: countryCode
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
cidr(cidr: string): Condition {
|
|
308
|
+
return {
|
|
309
|
+
field: this.fieldName,
|
|
310
|
+
operator: 'CIDR',
|
|
311
|
+
value: cidr
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// ===== BETWEEN OPERATOR =====
|
|
316
|
+
|
|
317
|
+
between(min: Val, max: Val): Condition {
|
|
318
|
+
return {
|
|
319
|
+
field: this.fieldName,
|
|
320
|
+
operator: 'betweenOp',
|
|
321
|
+
value: { from: min, to: max } as any
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// ===== CONVENIENCE METHODS =====
|
|
326
|
+
|
|
327
|
+
// Boolean checks (using base operators)
|
|
328
|
+
isTrue(): Condition {
|
|
329
|
+
return this.equals(true);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
isFalse(): Condition {
|
|
333
|
+
return this.equals(false);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@scepter/typescript-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "TypeScript SDK for Scepter JSON query engine",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "jest",
|
|
9
|
+
"test:watch": "jest --watch",
|
|
10
|
+
"test:coverage": "jest --coverage",
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"lint": "eslint src/**/*.ts",
|
|
13
|
+
"prepare": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"query",
|
|
17
|
+
"json",
|
|
18
|
+
"database",
|
|
19
|
+
"typescript",
|
|
20
|
+
"sdk"
|
|
21
|
+
],
|
|
22
|
+
"author": "Scepter Team",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/jest": "^30.0.0",
|
|
26
|
+
"@types/node": "^20.8.0",
|
|
27
|
+
"@typescript-eslint/eslint-plugin": "^6.7.0",
|
|
28
|
+
"@typescript-eslint/parser": "^6.7.0",
|
|
29
|
+
"eslint": "^8.50.0",
|
|
30
|
+
"jest": "^29.7.0",
|
|
31
|
+
"ts-jest": "^29.4.4",
|
|
32
|
+
"typescript": "^5.2.0",
|
|
33
|
+
"yest": "^0.0.1"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"axios": "^1.5.0"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"axios": "^1.5.0"
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"dist/**/*",
|
|
43
|
+
"index.d.ts",
|
|
44
|
+
"README.md"
|
|
45
|
+
]
|
|
46
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { FieldConditionBuilder, LogicalOperator } from '../index';
|
|
2
|
+
|
|
3
|
+
describe('FieldConditionBuilder', () => {
|
|
4
|
+
test('should use correct operators for basic methods', () => {
|
|
5
|
+
const builder = new FieldConditionBuilder('name');
|
|
6
|
+
|
|
7
|
+
expect(builder.equals('John').operator).toBe('is');
|
|
8
|
+
expect(builder.contains('test').operator).toBe('includes');
|
|
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');
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('should support advanced operators (string, IP, misc)', () => {
|
|
48
|
+
const stringBuilder = new FieldConditionBuilder('text');
|
|
49
|
+
const regexCondition = stringBuilder.regExpMatches('^test.*');
|
|
50
|
+
expect(regexCondition.operator).toBe('regExpMatches');
|
|
51
|
+
|
|
52
|
+
const caseInsensitiveCondition = stringBuilder.includesCaseInsensitive('Test');
|
|
53
|
+
expect(caseInsensitiveCondition.operator).toBe('includesCaseInsensitive');
|
|
54
|
+
|
|
55
|
+
const ipBuilder = new FieldConditionBuilder('ip_address');
|
|
56
|
+
const ipLocalCondition = ipBuilder.isLocalIp();
|
|
57
|
+
expect(ipLocalCondition.operator).toBe('isLocalIp');
|
|
58
|
+
|
|
59
|
+
const ipExternalCondition = ipBuilder.isExternalIp();
|
|
60
|
+
expect(ipExternalCondition.operator).toBe('isExternalIp');
|
|
61
|
+
|
|
62
|
+
const b64Condition = ipBuilder.b64('dGVzdA==');
|
|
63
|
+
expect(b64Condition.operator).toBe('b64');
|
|
64
|
+
|
|
65
|
+
const countryCondition = ipBuilder.inCountry('US');
|
|
66
|
+
expect(countryCondition.operator).toBe('inCountry');
|
|
67
|
+
|
|
68
|
+
const cidrCondition = ipBuilder.cidr('192.168.1.0/24');
|
|
69
|
+
expect(cidrCondition.operator).toBe('CIDR');
|
|
70
|
+
|
|
71
|
+
const datasetCondition = ipBuilder.inDataset('malware_ips');
|
|
72
|
+
expect(datasetCondition.operator).toBe('inDataset');
|
|
73
|
+
|
|
74
|
+
const betweenCondition = stringBuilder.between(5, 15);
|
|
75
|
+
expect(betweenCondition.operator).toBe('betweenOp');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('should handle empty field names gracefully', () => {
|
|
79
|
+
expect(() => {
|
|
80
|
+
const condition = new FieldConditionBuilder('').equals('test');
|
|
81
|
+
LogicalOperator.Condition(condition).toComposable();
|
|
82
|
+
}).not.toThrow();
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { FieldConditionBuilder, LogicalOperator } from '../index';
|
|
2
|
+
|
|
3
|
+
describe('LogicalOperator', () => {
|
|
4
|
+
test('should combine conditions with AND operator', () => {
|
|
5
|
+
const condition1 = new FieldConditionBuilder('user.name').equals('John');
|
|
6
|
+
const condition2 = new FieldConditionBuilder('user.age').greaterThan(25);
|
|
7
|
+
|
|
8
|
+
const andOp = LogicalOperator.And([
|
|
9
|
+
LogicalOperator.Condition(condition1),
|
|
10
|
+
LogicalOperator.Condition(condition2)
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
const composable = andOp.toComposable();
|
|
14
|
+
|
|
15
|
+
const expected = {
|
|
16
|
+
and: [
|
|
17
|
+
{
|
|
18
|
+
user: {
|
|
19
|
+
name: {
|
|
20
|
+
is: 'John'
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
user: {
|
|
26
|
+
age: {
|
|
27
|
+
greaterThan: 25
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
expect(composable).toEqual(expected);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('should combine conditions with OR operator', () => {
|
|
38
|
+
const condition1 = new FieldConditionBuilder('status').equals('active');
|
|
39
|
+
const condition2 = new FieldConditionBuilder('status').equals('pending');
|
|
40
|
+
|
|
41
|
+
const orOp = LogicalOperator.Or([
|
|
42
|
+
LogicalOperator.Condition(condition1),
|
|
43
|
+
LogicalOperator.Condition(condition2)
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
const composable = orOp.toComposable();
|
|
47
|
+
|
|
48
|
+
const expected = {
|
|
49
|
+
or: [
|
|
50
|
+
{
|
|
51
|
+
status: {
|
|
52
|
+
is: 'active'
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
status: {
|
|
57
|
+
is: 'pending'
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
expect(composable).toEqual(expected);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('should handle nested logical operations', () => {
|
|
67
|
+
const condition1 = new FieldConditionBuilder('name').equals('John');
|
|
68
|
+
const condition2 = new FieldConditionBuilder('age').greaterThan(25);
|
|
69
|
+
const condition3 = new FieldConditionBuilder('status').equals('active');
|
|
70
|
+
|
|
71
|
+
const nestedOp = LogicalOperator.Or([
|
|
72
|
+
LogicalOperator.And([
|
|
73
|
+
LogicalOperator.Condition(condition1),
|
|
74
|
+
LogicalOperator.Condition(condition2)
|
|
75
|
+
]),
|
|
76
|
+
LogicalOperator.Condition(condition3)
|
|
77
|
+
]);
|
|
78
|
+
|
|
79
|
+
const composable = nestedOp.toComposable();
|
|
80
|
+
|
|
81
|
+
expect(composable).toHaveProperty('or');
|
|
82
|
+
expect(composable.or).toHaveLength(2);
|
|
83
|
+
expect(composable.or[0]).toHaveProperty('and');
|
|
84
|
+
});
|
|
85
|
+
});
|