@mastra/libsql 0.0.1-alpha.1
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/.turbo/turbo-build.log +23 -0
- package/CHANGELOG.md +15 -0
- package/LICENSE.md +7 -0
- package/README.md +144 -0
- package/dist/_tsup-dts-rollup.d.cts +192 -0
- package/dist/_tsup-dts-rollup.d.ts +192 -0
- package/dist/index.cjs +1143 -0
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +1139 -0
- package/eslint.config.js +6 -0
- package/package.json +42 -0
- package/src/index.ts +2 -0
- package/src/storage/index.test.ts +15 -0
- package/src/storage/index.ts +624 -0
- package/src/vector/filter.test.ts +968 -0
- package/src/vector/filter.ts +117 -0
- package/src/vector/index.test.ts +1702 -0
- package/src/vector/index.ts +344 -0
- package/src/vector/sql-builder.ts +462 -0
- package/tsconfig.json +5 -0
- package/vitest.config.ts +11 -0
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
import type { InValue } from '@libsql/client';
|
|
2
|
+
import type {
|
|
3
|
+
VectorFilter,
|
|
4
|
+
BasicOperator,
|
|
5
|
+
NumericOperator,
|
|
6
|
+
ArrayOperator,
|
|
7
|
+
ElementOperator,
|
|
8
|
+
LogicalOperator,
|
|
9
|
+
RegexOperator,
|
|
10
|
+
} from '@mastra/core/vector/filter';
|
|
11
|
+
|
|
12
|
+
export type OperatorType =
|
|
13
|
+
| BasicOperator
|
|
14
|
+
| NumericOperator
|
|
15
|
+
| ArrayOperator
|
|
16
|
+
| ElementOperator
|
|
17
|
+
| LogicalOperator
|
|
18
|
+
| '$contains'
|
|
19
|
+
| Exclude<RegexOperator, '$options'>;
|
|
20
|
+
|
|
21
|
+
type FilterOperator = {
|
|
22
|
+
sql: string;
|
|
23
|
+
needsValue: boolean;
|
|
24
|
+
transformValue?: (value: any) => any;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
type OperatorFn = (key: string, value?: any) => FilterOperator;
|
|
28
|
+
|
|
29
|
+
// Helper functions to create operators
|
|
30
|
+
const createBasicOperator = (symbol: string) => {
|
|
31
|
+
return (key: string): FilterOperator => ({
|
|
32
|
+
sql: `CASE
|
|
33
|
+
WHEN ? IS NULL THEN json_extract(metadata, '$."${handleKey(key)}"') IS ${symbol === '=' ? '' : 'NOT'} NULL
|
|
34
|
+
ELSE json_extract(metadata, '$."${handleKey(key)}"') ${symbol} ?
|
|
35
|
+
END`,
|
|
36
|
+
needsValue: true,
|
|
37
|
+
transformValue: (value: any) => {
|
|
38
|
+
// Return the values directly, not in an object
|
|
39
|
+
return [value, value];
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
const createNumericOperator = (symbol: string) => {
|
|
44
|
+
return (key: string): FilterOperator => ({
|
|
45
|
+
sql: `CAST(json_extract(metadata, '$."${handleKey(key)}"') AS NUMERIC) ${symbol} ?`,
|
|
46
|
+
needsValue: true,
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const validateJsonArray = (key: string) =>
|
|
51
|
+
`json_valid(json_extract(metadata, '$."${handleKey(key)}"'))
|
|
52
|
+
AND json_type(json_extract(metadata, '$."${handleKey(key)}"')) = 'array'`;
|
|
53
|
+
|
|
54
|
+
// Define all filter operators
|
|
55
|
+
export const FILTER_OPERATORS: Record<string, OperatorFn> = {
|
|
56
|
+
$eq: createBasicOperator('='),
|
|
57
|
+
$ne: createBasicOperator('!='),
|
|
58
|
+
$gt: createNumericOperator('>'),
|
|
59
|
+
$gte: createNumericOperator('>='),
|
|
60
|
+
$lt: createNumericOperator('<'),
|
|
61
|
+
$lte: createNumericOperator('<='),
|
|
62
|
+
|
|
63
|
+
// Array Operators
|
|
64
|
+
$in: (key: string, value: any) => ({
|
|
65
|
+
sql: `json_extract(metadata, '$."${handleKey(key)}"') IN (${value.map(() => '?').join(',')})`,
|
|
66
|
+
needsValue: true,
|
|
67
|
+
}),
|
|
68
|
+
|
|
69
|
+
$nin: (key: string, value: any) => ({
|
|
70
|
+
sql: `json_extract(metadata, '$."${handleKey(key)}"') NOT IN (${value.map(() => '?').join(',')})`,
|
|
71
|
+
needsValue: true,
|
|
72
|
+
}),
|
|
73
|
+
$all: (key: string) => ({
|
|
74
|
+
sql: `json_extract(metadata, '$."${handleKey(key)}"') = ?`,
|
|
75
|
+
needsValue: true,
|
|
76
|
+
transformValue: (value: any) => {
|
|
77
|
+
const arrayValue = Array.isArray(value) ? value : [value];
|
|
78
|
+
if (arrayValue.length === 0) {
|
|
79
|
+
return {
|
|
80
|
+
sql: '1 = 0',
|
|
81
|
+
values: [],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
sql: `(
|
|
87
|
+
CASE
|
|
88
|
+
WHEN ${validateJsonArray(key)} THEN
|
|
89
|
+
NOT EXISTS (
|
|
90
|
+
SELECT value
|
|
91
|
+
FROM json_each(?)
|
|
92
|
+
WHERE value NOT IN (
|
|
93
|
+
SELECT value
|
|
94
|
+
FROM json_each(json_extract(metadata, '$."${handleKey(key)}"'))
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
ELSE FALSE
|
|
98
|
+
END
|
|
99
|
+
)`,
|
|
100
|
+
values: [JSON.stringify(arrayValue)],
|
|
101
|
+
};
|
|
102
|
+
},
|
|
103
|
+
}),
|
|
104
|
+
$elemMatch: (key: string) => ({
|
|
105
|
+
sql: `json_extract(metadata, '$."${handleKey(key)}"') = ?`,
|
|
106
|
+
needsValue: true,
|
|
107
|
+
transformValue: (value: any) => {
|
|
108
|
+
if (typeof value !== 'object' || Array.isArray(value)) {
|
|
109
|
+
throw new Error('$elemMatch requires an object with conditions');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// For nested object conditions
|
|
113
|
+
const conditions = Object.entries(value).map(([field, fieldValue]) => {
|
|
114
|
+
if (field.startsWith('$')) {
|
|
115
|
+
// Direct operators on array elements ($in, $gt, etc)
|
|
116
|
+
const { sql, values } = buildCondition('elem.value', { [field]: fieldValue }, '');
|
|
117
|
+
// Replace the metadata path with elem.value
|
|
118
|
+
const pattern = /json_extract\(metadata, '\$\."[^"]*"(\."[^"]*")*'\)/g;
|
|
119
|
+
const elemSql = sql.replace(pattern, 'elem.value');
|
|
120
|
+
return { sql: elemSql, values };
|
|
121
|
+
} else if (typeof fieldValue === 'object' && !Array.isArray(fieldValue)) {
|
|
122
|
+
// Nested field with operators (count: { $gt: 20 })
|
|
123
|
+
const { sql, values } = buildCondition(field, fieldValue, '');
|
|
124
|
+
// Replace the field path with elem.value path
|
|
125
|
+
const pattern = /json_extract\(metadata, '\$\."[^"]*"(\."[^"]*")*'\)/g;
|
|
126
|
+
const elemSql = sql.replace(pattern, `json_extract(elem.value, '$."${field}"')`);
|
|
127
|
+
return { sql: elemSql, values };
|
|
128
|
+
} else {
|
|
129
|
+
// Simple field equality (warehouse: 'A')
|
|
130
|
+
return {
|
|
131
|
+
sql: `json_extract(elem.value, '$."${field}"') = ?`,
|
|
132
|
+
values: [fieldValue],
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
sql: `(
|
|
139
|
+
CASE
|
|
140
|
+
WHEN ${validateJsonArray(key)} THEN
|
|
141
|
+
EXISTS (
|
|
142
|
+
SELECT 1
|
|
143
|
+
FROM json_each(json_extract(metadata, '$."${handleKey(key)}"')) as elem
|
|
144
|
+
WHERE ${conditions.map(c => c.sql).join(' AND ')}
|
|
145
|
+
)
|
|
146
|
+
ELSE FALSE
|
|
147
|
+
END
|
|
148
|
+
)`,
|
|
149
|
+
values: conditions.flatMap(c => c.values),
|
|
150
|
+
};
|
|
151
|
+
},
|
|
152
|
+
}),
|
|
153
|
+
|
|
154
|
+
// Element Operators
|
|
155
|
+
$exists: (key: string) => ({
|
|
156
|
+
sql: `json_extract(metadata, '$."${handleKey(key)}"') IS NOT NULL`,
|
|
157
|
+
needsValue: false,
|
|
158
|
+
}),
|
|
159
|
+
|
|
160
|
+
// Logical Operators
|
|
161
|
+
$and: (key: string) => ({
|
|
162
|
+
sql: `(${key})`,
|
|
163
|
+
needsValue: false,
|
|
164
|
+
}),
|
|
165
|
+
$or: (key: string) => ({
|
|
166
|
+
sql: `(${key})`,
|
|
167
|
+
needsValue: false,
|
|
168
|
+
}),
|
|
169
|
+
$not: key => ({ sql: `NOT (${key})`, needsValue: false }),
|
|
170
|
+
$nor: (key: string) => ({
|
|
171
|
+
sql: `NOT (${key})`,
|
|
172
|
+
needsValue: false,
|
|
173
|
+
}),
|
|
174
|
+
$size: (key: string, paramIndex: number) => ({
|
|
175
|
+
sql: `(
|
|
176
|
+
CASE
|
|
177
|
+
WHEN json_type(json_extract(metadata, '$."${handleKey(key)}"')) = 'array' THEN
|
|
178
|
+
json_array_length(json_extract(metadata, '$."${handleKey(key)}"')) = $${paramIndex}
|
|
179
|
+
ELSE FALSE
|
|
180
|
+
END
|
|
181
|
+
)`,
|
|
182
|
+
needsValue: true,
|
|
183
|
+
}),
|
|
184
|
+
// /**
|
|
185
|
+
// * Regex Operators
|
|
186
|
+
// * Supports case insensitive and multiline
|
|
187
|
+
// */
|
|
188
|
+
// $regex: (key: string): FilterOperator => ({
|
|
189
|
+
// sql: `json_extract(metadata, '$."${handleKey(key)}"') = ?`,
|
|
190
|
+
// needsValue: true,
|
|
191
|
+
// transformValue: (value: any) => {
|
|
192
|
+
// const pattern = typeof value === 'object' ? value.$regex : value;
|
|
193
|
+
// const options = typeof value === 'object' ? value.$options || '' : '';
|
|
194
|
+
// let sql = `json_extract(metadata, '$."${handleKey(key)}"')`;
|
|
195
|
+
|
|
196
|
+
// // Handle multiline
|
|
197
|
+
// // if (options.includes('m')) {
|
|
198
|
+
// // sql = `REPLACE(${sql}, CHAR(10), '\n')`;
|
|
199
|
+
// // }
|
|
200
|
+
|
|
201
|
+
// // let finalPattern = pattern;
|
|
202
|
+
// // if (options) {
|
|
203
|
+
// // finalPattern = `(\\?${options})${pattern}`;
|
|
204
|
+
// // }
|
|
205
|
+
|
|
206
|
+
// // // Handle case insensitivity
|
|
207
|
+
// // if (options.includes('i')) {
|
|
208
|
+
// // sql = `LOWER(${sql}) REGEXP LOWER(?)`;
|
|
209
|
+
// // } else {
|
|
210
|
+
// // sql = `${sql} REGEXP ?`;
|
|
211
|
+
// // }
|
|
212
|
+
|
|
213
|
+
// if (options.includes('m')) {
|
|
214
|
+
// sql = `EXISTS (
|
|
215
|
+
// SELECT 1
|
|
216
|
+
// FROM json_each(
|
|
217
|
+
// json_array(
|
|
218
|
+
// ${sql},
|
|
219
|
+
// REPLACE(${sql}, CHAR(10), CHAR(13))
|
|
220
|
+
// )
|
|
221
|
+
// ) as lines
|
|
222
|
+
// WHERE lines.value REGEXP ?
|
|
223
|
+
// )`;
|
|
224
|
+
// } else {
|
|
225
|
+
// sql = `${sql} REGEXP ?`;
|
|
226
|
+
// }
|
|
227
|
+
|
|
228
|
+
// // Handle case insensitivity
|
|
229
|
+
// if (options.includes('i')) {
|
|
230
|
+
// sql = sql.replace('REGEXP ?', 'REGEXP LOWER(?)');
|
|
231
|
+
// sql = sql.replace('value REGEXP', 'LOWER(value) REGEXP');
|
|
232
|
+
// }
|
|
233
|
+
|
|
234
|
+
// // Handle extended - allows whitespace and comments in pattern
|
|
235
|
+
// if (options.includes('x')) {
|
|
236
|
+
// // Remove whitespace and comments from pattern
|
|
237
|
+
// const cleanPattern = pattern.replace(/\s+|#.*$/gm, '');
|
|
238
|
+
// return {
|
|
239
|
+
// sql,
|
|
240
|
+
// values: [cleanPattern],
|
|
241
|
+
// };
|
|
242
|
+
// }
|
|
243
|
+
|
|
244
|
+
// return {
|
|
245
|
+
// sql,
|
|
246
|
+
// values: [pattern],
|
|
247
|
+
// };
|
|
248
|
+
// },
|
|
249
|
+
// }),
|
|
250
|
+
$contains: (key: string) => ({
|
|
251
|
+
sql: `json_extract(metadata, '$."${handleKey(key)}"') = ?`,
|
|
252
|
+
needsValue: true,
|
|
253
|
+
transformValue: (value: any) => {
|
|
254
|
+
// Array containment
|
|
255
|
+
if (Array.isArray(value)) {
|
|
256
|
+
return {
|
|
257
|
+
sql: `(
|
|
258
|
+
SELECT ${validateJsonArray(key)}
|
|
259
|
+
AND EXISTS (
|
|
260
|
+
SELECT 1
|
|
261
|
+
FROM json_each(json_extract(metadata, '$."${handleKey(key)}"')) as m
|
|
262
|
+
WHERE m.value IN (SELECT value FROM json_each(?))
|
|
263
|
+
)
|
|
264
|
+
)`,
|
|
265
|
+
values: [JSON.stringify(value)],
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Nested object traversal
|
|
270
|
+
if (value && typeof value === 'object') {
|
|
271
|
+
const paths: string[] = [];
|
|
272
|
+
const values: any[] = [];
|
|
273
|
+
|
|
274
|
+
function traverse(obj: any, path: string[] = []) {
|
|
275
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
276
|
+
const currentPath = [...path, k];
|
|
277
|
+
if (v && typeof v === 'object' && !Array.isArray(v)) {
|
|
278
|
+
traverse(v, currentPath);
|
|
279
|
+
} else {
|
|
280
|
+
paths.push(currentPath.join('.'));
|
|
281
|
+
values.push(v);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
traverse(value);
|
|
287
|
+
return {
|
|
288
|
+
sql: `(${paths.map(path => `json_extract(metadata, '$."${handleKey(key)}"."${path}"') = ?`).join(' AND ')})`,
|
|
289
|
+
values,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return value;
|
|
294
|
+
},
|
|
295
|
+
}),
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
export interface FilterResult {
|
|
299
|
+
sql: string;
|
|
300
|
+
values: InValue[];
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export const handleKey = (key: string) => {
|
|
304
|
+
return key.replace(/\./g, '"."');
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
export function buildFilterQuery(filter: VectorFilter): FilterResult {
|
|
308
|
+
if (!filter) {
|
|
309
|
+
return { sql: '', values: [] };
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const values: InValue[] = [];
|
|
313
|
+
const conditions = Object.entries(filter)
|
|
314
|
+
.map(([key, value]) => {
|
|
315
|
+
const condition = buildCondition(key, value, '');
|
|
316
|
+
values.push(...condition.values);
|
|
317
|
+
return condition.sql;
|
|
318
|
+
})
|
|
319
|
+
.join(' AND ');
|
|
320
|
+
|
|
321
|
+
return {
|
|
322
|
+
sql: conditions ? `WHERE ${conditions}` : '',
|
|
323
|
+
values,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function buildCondition(key: string, value: any, parentPath: string): FilterResult {
|
|
328
|
+
// Handle logical operators ($and/$or)
|
|
329
|
+
if (['$and', '$or', '$not', '$nor'].includes(key)) {
|
|
330
|
+
return handleLogicalOperator(key as '$and' | '$or' | '$not' | '$nor', value, parentPath);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// If condition is not a FilterCondition object, assume it's an equality check
|
|
334
|
+
if (!value || typeof value !== 'object') {
|
|
335
|
+
return {
|
|
336
|
+
sql: `json_extract(metadata, '$."${key.replace(/\./g, '"."')}"') = ?`,
|
|
337
|
+
values: [value],
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
//TODO: Add regex support
|
|
342
|
+
// if ('$regex' in value) {
|
|
343
|
+
// return handleRegexOperator(key, value);
|
|
344
|
+
// }
|
|
345
|
+
|
|
346
|
+
// Handle operator conditions
|
|
347
|
+
return handleOperator(key, value);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// function handleRegexOperator(key: string, value: any): FilterResult {
|
|
351
|
+
// const operatorFn = FILTER_OPERATORS['$regex']!;
|
|
352
|
+
// const operatorResult = operatorFn(key, value);
|
|
353
|
+
// const transformed = operatorResult.transformValue ? operatorResult.transformValue(value) : value;
|
|
354
|
+
|
|
355
|
+
// return {
|
|
356
|
+
// sql: transformed.sql,
|
|
357
|
+
// values: transformed.values,
|
|
358
|
+
// };
|
|
359
|
+
// }
|
|
360
|
+
|
|
361
|
+
function handleLogicalOperator(
|
|
362
|
+
key: '$and' | '$or' | '$not' | '$nor',
|
|
363
|
+
value: VectorFilter[] | VectorFilter,
|
|
364
|
+
parentPath: string,
|
|
365
|
+
): FilterResult {
|
|
366
|
+
// Handle empty conditions
|
|
367
|
+
if (!value || value.length === 0) {
|
|
368
|
+
switch (key) {
|
|
369
|
+
case '$and':
|
|
370
|
+
case '$nor':
|
|
371
|
+
return { sql: 'true', values: [] };
|
|
372
|
+
case '$or':
|
|
373
|
+
return { sql: 'false', values: [] };
|
|
374
|
+
case '$not':
|
|
375
|
+
throw new Error('$not operator cannot be empty');
|
|
376
|
+
default:
|
|
377
|
+
return { sql: 'true', values: [] };
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (key === '$not') {
|
|
382
|
+
// For top-level $not
|
|
383
|
+
const entries = Object.entries(value);
|
|
384
|
+
const conditions = entries.map(([fieldKey, fieldValue]) => buildCondition(fieldKey, fieldValue, key));
|
|
385
|
+
return {
|
|
386
|
+
sql: `NOT (${conditions.map(c => c.sql).join(' AND ')})`,
|
|
387
|
+
values: conditions.flatMap(c => c.values),
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const values: InValue[] = [];
|
|
392
|
+
const joinOperator = key === '$or' || key === '$nor' ? 'OR' : 'AND';
|
|
393
|
+
const conditions = Array.isArray(value)
|
|
394
|
+
? value.map(f => {
|
|
395
|
+
const entries = Object.entries(f);
|
|
396
|
+
return entries.map(([k, v]) => buildCondition(k, v, key));
|
|
397
|
+
})
|
|
398
|
+
: [buildCondition(key, value, parentPath)];
|
|
399
|
+
|
|
400
|
+
const joined = conditions
|
|
401
|
+
.flat()
|
|
402
|
+
.map(c => {
|
|
403
|
+
values.push(...c.values);
|
|
404
|
+
return c.sql;
|
|
405
|
+
})
|
|
406
|
+
.join(` ${joinOperator} `);
|
|
407
|
+
|
|
408
|
+
return {
|
|
409
|
+
sql: key === '$nor' ? `NOT (${joined})` : `(${joined})`,
|
|
410
|
+
values,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function handleOperator(key: string, value: any): FilterResult {
|
|
415
|
+
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
416
|
+
const entries = Object.entries(value);
|
|
417
|
+
const results = entries.map(([operator, operatorValue]) =>
|
|
418
|
+
operator === '$not'
|
|
419
|
+
? {
|
|
420
|
+
sql: `NOT (${Object.entries(operatorValue as Record<string, any>)
|
|
421
|
+
.map(([op, val]) => processOperator(key, op, val).sql)
|
|
422
|
+
.join(' AND ')})`,
|
|
423
|
+
values: Object.entries(operatorValue as Record<string, any>).flatMap(
|
|
424
|
+
([op, val]) => processOperator(key, op, val).values,
|
|
425
|
+
),
|
|
426
|
+
}
|
|
427
|
+
: processOperator(key, operator, operatorValue),
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
return {
|
|
431
|
+
sql: `(${results.map(r => r.sql).join(' AND ')})`,
|
|
432
|
+
values: results.flatMap(r => r.values),
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Handle single operator
|
|
437
|
+
const [[operator, operatorValue] = []] = Object.entries(value);
|
|
438
|
+
return processOperator(key, operator as string, operatorValue);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const processOperator = (key: string, operator: string, operatorValue: any): FilterResult => {
|
|
442
|
+
if (!operator.startsWith('$') || !FILTER_OPERATORS[operator]) {
|
|
443
|
+
throw new Error(`Invalid operator: ${operator}`);
|
|
444
|
+
}
|
|
445
|
+
const operatorFn = FILTER_OPERATORS[operator]!;
|
|
446
|
+
const operatorResult = operatorFn(key, operatorValue);
|
|
447
|
+
|
|
448
|
+
if (!operatorResult.needsValue) {
|
|
449
|
+
return { sql: operatorResult.sql, values: [] };
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const transformed = operatorResult.transformValue ? operatorResult.transformValue(operatorValue) : operatorValue;
|
|
453
|
+
|
|
454
|
+
if (transformed && typeof transformed === 'object' && 'sql' in transformed) {
|
|
455
|
+
return transformed;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return {
|
|
459
|
+
sql: operatorResult.sql,
|
|
460
|
+
values: Array.isArray(transformed) ? transformed : [transformed],
|
|
461
|
+
};
|
|
462
|
+
};
|
package/tsconfig.json
ADDED