@mastra/libsql 0.10.4-alpha.1 → 0.11.0-alpha.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/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +27 -0
- package/dist/_tsup-dts-rollup.d.cts +33 -6
- package/dist/_tsup-dts-rollup.d.ts +33 -6
- package/dist/index.cjs +88 -8
- package/dist/index.js +89 -9
- package/package.json +5 -5
- package/src/storage/index.ts +111 -5
- package/src/vector/filter.test.ts +22 -84
- package/src/vector/filter.ts +19 -5
- package/src/vector/index.test.ts +1 -11
- package/src/vector/index.ts +4 -4
- package/src/vector/sql-builder.ts +5 -5
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
|
|
2
|
-
> @mastra/libsql@0.
|
|
2
|
+
> @mastra/libsql@0.11.0-alpha.3 build /home/runner/work/mastra/mastra/stores/libsql
|
|
3
3
|
> tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
6
6
|
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
7
7
|
[34mCLI[39m tsup v8.5.0
|
|
8
8
|
[34mTSC[39m Build start
|
|
9
|
-
[32mTSC[39m ⚡️ Build success in
|
|
9
|
+
[32mTSC[39m ⚡️ Build success in 12207ms
|
|
10
10
|
[34mDTS[39m Build start
|
|
11
11
|
[34mCLI[39m Target: es2022
|
|
12
12
|
Analysis will use the bundled TypeScript version 5.8.3
|
|
13
13
|
[36mWriting package typings: /home/runner/work/mastra/mastra/stores/libsql/dist/_tsup-dts-rollup.d.ts[39m
|
|
14
14
|
Analysis will use the bundled TypeScript version 5.8.3
|
|
15
15
|
[36mWriting package typings: /home/runner/work/mastra/mastra/stores/libsql/dist/_tsup-dts-rollup.d.cts[39m
|
|
16
|
-
[32mDTS[39m ⚡️ Build success in
|
|
16
|
+
[32mDTS[39m ⚡️ Build success in 13745ms
|
|
17
17
|
[34mCLI[39m Cleaning output folder
|
|
18
18
|
[34mESM[39m Build start
|
|
19
19
|
[34mCJS[39m Build start
|
|
20
|
-
[32mESM[39m [1mdist/index.js [22m[
|
|
21
|
-
[32mESM[39m ⚡️ Build success in
|
|
22
|
-
[32mCJS[39m [1mdist/index.cjs [22m[
|
|
23
|
-
[32mCJS[39m ⚡️ Build success in
|
|
20
|
+
[32mESM[39m [1mdist/index.js [22m[32m72.42 KB[39m
|
|
21
|
+
[32mESM[39m ⚡️ Build success in 2002ms
|
|
22
|
+
[32mCJS[39m [1mdist/index.cjs [22m[32m73.39 KB[39m
|
|
23
|
+
[32mCJS[39m ⚡️ Build success in 2005ms
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# @mastra/libsql
|
|
2
2
|
|
|
3
|
+
## 0.11.0-alpha.3
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 8a3bfd2: Update peerdeps to latest core
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [792c4c0]
|
|
12
|
+
- Updated dependencies [502fe05]
|
|
13
|
+
- Updated dependencies [4efcfa0]
|
|
14
|
+
- @mastra/core@0.10.7-alpha.3
|
|
15
|
+
|
|
16
|
+
## 0.10.4-alpha.2
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- 15e9d26: Added per-resource working memory for LibSQL, Upstash, and PG
|
|
21
|
+
- 0fb9d64: [MASTRA-4018] Update saveMessages in storage adapters to upsert messages
|
|
22
|
+
- 144eb0b: [MASTRA-3669] Metadata Filter Types
|
|
23
|
+
- f0150c4: Fix LibSQLStore and PgStore getMessagesPaginated implementation
|
|
24
|
+
- Updated dependencies [15e9d26]
|
|
25
|
+
- Updated dependencies [07d6d88]
|
|
26
|
+
- Updated dependencies [5d74aab]
|
|
27
|
+
- Updated dependencies [144eb0b]
|
|
28
|
+
- @mastra/core@0.10.7-alpha.2
|
|
29
|
+
|
|
3
30
|
## 0.10.4-alpha.1
|
|
4
31
|
|
|
5
32
|
### Patch Changes
|
|
@@ -12,22 +12,25 @@ import type { MastraMessageV2 } from '@mastra/core/agent';
|
|
|
12
12
|
import { MastraStorage } from '@mastra/core/storage';
|
|
13
13
|
import { MastraVector } from '@mastra/core/vector';
|
|
14
14
|
import type { OperatorSupport } from '@mastra/core/vector/filter';
|
|
15
|
+
import type { OperatorValueMap } from '@mastra/core/vector/filter';
|
|
15
16
|
import type { PaginationArgs } from '@mastra/core/storage';
|
|
16
17
|
import type { PaginationInfo } from '@mastra/core/storage';
|
|
17
18
|
import type { QueryResult } from '@mastra/core/vector';
|
|
18
19
|
import type { QueryVectorParams } from '@mastra/core/vector';
|
|
19
20
|
import type { StorageColumn } from '@mastra/core/storage';
|
|
20
21
|
import type { StorageGetMessagesArg } from '@mastra/core/storage';
|
|
22
|
+
import type { StorageResourceType } from '@mastra/core/storage';
|
|
21
23
|
import type { StorageThreadType } from '@mastra/core/memory';
|
|
22
24
|
import type { TABLE_NAMES } from '@mastra/core/storage';
|
|
23
25
|
import type { Trace } from '@mastra/core/telemetry';
|
|
24
26
|
import type { UpdateVectorParams } from '@mastra/core/vector';
|
|
25
27
|
import type { UpsertVectorParams } from '@mastra/core/vector';
|
|
28
|
+
import type { VectorFieldValue } from '@mastra/core/vector/filter';
|
|
26
29
|
import type { VectorFilter } from '@mastra/core/vector/filter';
|
|
27
30
|
import type { WorkflowRun } from '@mastra/core/storage';
|
|
28
31
|
import type { WorkflowRuns } from '@mastra/core/storage';
|
|
29
32
|
|
|
30
|
-
export declare function buildFilterQuery(filter:
|
|
33
|
+
export declare function buildFilterQuery(filter: LibSQLVectorFilter): FilterResult;
|
|
31
34
|
|
|
32
35
|
declare interface FilterResult {
|
|
33
36
|
sql: string;
|
|
@@ -70,13 +73,23 @@ export { LibSQLConfig as LibSQLConfig_alias_1 }
|
|
|
70
73
|
* - Can take either a single condition or an array of conditions
|
|
71
74
|
*
|
|
72
75
|
*/
|
|
73
|
-
export declare class LibSQLFilterTranslator extends BaseFilterTranslator {
|
|
76
|
+
export declare class LibSQLFilterTranslator extends BaseFilterTranslator<LibSQLVectorFilter> {
|
|
74
77
|
protected getSupportedOperators(): OperatorSupport;
|
|
75
|
-
translate(filter?:
|
|
78
|
+
translate(filter?: LibSQLVectorFilter): LibSQLVectorFilter;
|
|
76
79
|
private translateNode;
|
|
77
80
|
}
|
|
78
81
|
|
|
79
|
-
declare
|
|
82
|
+
declare type LibSQLOperatorValueMap = Omit<OperatorValueMap, '$regex' | '$options' | '$in' | '$all' | '$nin' | '$eq' | '$ne'> & {
|
|
83
|
+
$size: number;
|
|
84
|
+
$contains: VectorFieldValue | Record<string, unknown>;
|
|
85
|
+
$all: VectorFieldValue;
|
|
86
|
+
$in: VectorFieldValue;
|
|
87
|
+
$nin: VectorFieldValue;
|
|
88
|
+
$eq: VectorFieldValue;
|
|
89
|
+
$ne: VectorFieldValue;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
declare interface LibSQLQueryVectorParams extends QueryVectorParams<LibSQLVectorFilter> {
|
|
80
93
|
minScore?: number;
|
|
81
94
|
}
|
|
82
95
|
|
|
@@ -87,6 +100,7 @@ declare class LibSQLStore extends MastraStorage {
|
|
|
87
100
|
constructor(config: LibSQLConfig);
|
|
88
101
|
get supports(): {
|
|
89
102
|
selectByIncludeResourceScope: boolean;
|
|
103
|
+
resourceWorkingMemory: boolean;
|
|
90
104
|
};
|
|
91
105
|
private getCreateTableSQL;
|
|
92
106
|
createTable({ tableName, schema, }: {
|
|
@@ -224,6 +238,17 @@ declare class LibSQLStore extends MastraStorage {
|
|
|
224
238
|
runId: string;
|
|
225
239
|
workflowName?: string;
|
|
226
240
|
}): Promise<WorkflowRun | null>;
|
|
241
|
+
getResourceById({ resourceId }: {
|
|
242
|
+
resourceId: string;
|
|
243
|
+
}): Promise<StorageResourceType | null>;
|
|
244
|
+
saveResource({ resource }: {
|
|
245
|
+
resource: StorageResourceType;
|
|
246
|
+
}): Promise<StorageResourceType>;
|
|
247
|
+
updateResource({ resourceId, workingMemory, metadata, }: {
|
|
248
|
+
resourceId: string;
|
|
249
|
+
workingMemory?: string;
|
|
250
|
+
metadata?: Record<string, unknown>;
|
|
251
|
+
}): Promise<StorageResourceType>;
|
|
227
252
|
private hasColumn;
|
|
228
253
|
private parseWorkflowRun;
|
|
229
254
|
}
|
|
@@ -232,13 +257,13 @@ export { LibSQLStore as DefaultStorage_alias_1 }
|
|
|
232
257
|
export { LibSQLStore }
|
|
233
258
|
export { LibSQLStore as LibSQLStore_alias_1 }
|
|
234
259
|
|
|
235
|
-
declare class LibSQLVector extends MastraVector {
|
|
260
|
+
declare class LibSQLVector extends MastraVector<LibSQLVectorFilter> {
|
|
236
261
|
private turso;
|
|
237
262
|
private readonly maxRetries;
|
|
238
263
|
private readonly initialBackoffMs;
|
|
239
264
|
constructor({ connectionUrl, authToken, syncUrl, syncInterval, maxRetries, initialBackoffMs, }: LibSQLVectorConfig);
|
|
240
265
|
private executeWriteOperationWithRetry;
|
|
241
|
-
transformFilter(filter?:
|
|
266
|
+
transformFilter(filter?: LibSQLVectorFilter): LibSQLVectorFilter;
|
|
242
267
|
query({ indexName, queryVector, topK, filter, includeVector, minScore, }: LibSQLQueryVectorParams): Promise<QueryResult[]>;
|
|
243
268
|
upsert(args: UpsertVectorParams): Promise<string[]>;
|
|
244
269
|
private doUpsert;
|
|
@@ -302,4 +327,6 @@ declare interface LibSQLVectorConfig {
|
|
|
302
327
|
export { LibSQLVectorConfig }
|
|
303
328
|
export { LibSQLVectorConfig as LibSQLVectorConfig_alias_1 }
|
|
304
329
|
|
|
330
|
+
export declare type LibSQLVectorFilter = VectorFilter<keyof LibSQLOperatorValueMap, LibSQLOperatorValueMap>;
|
|
331
|
+
|
|
305
332
|
export { }
|
|
@@ -12,22 +12,25 @@ import type { MastraMessageV2 } from '@mastra/core/agent';
|
|
|
12
12
|
import { MastraStorage } from '@mastra/core/storage';
|
|
13
13
|
import { MastraVector } from '@mastra/core/vector';
|
|
14
14
|
import type { OperatorSupport } from '@mastra/core/vector/filter';
|
|
15
|
+
import type { OperatorValueMap } from '@mastra/core/vector/filter';
|
|
15
16
|
import type { PaginationArgs } from '@mastra/core/storage';
|
|
16
17
|
import type { PaginationInfo } from '@mastra/core/storage';
|
|
17
18
|
import type { QueryResult } from '@mastra/core/vector';
|
|
18
19
|
import type { QueryVectorParams } from '@mastra/core/vector';
|
|
19
20
|
import type { StorageColumn } from '@mastra/core/storage';
|
|
20
21
|
import type { StorageGetMessagesArg } from '@mastra/core/storage';
|
|
22
|
+
import type { StorageResourceType } from '@mastra/core/storage';
|
|
21
23
|
import type { StorageThreadType } from '@mastra/core/memory';
|
|
22
24
|
import type { TABLE_NAMES } from '@mastra/core/storage';
|
|
23
25
|
import type { Trace } from '@mastra/core/telemetry';
|
|
24
26
|
import type { UpdateVectorParams } from '@mastra/core/vector';
|
|
25
27
|
import type { UpsertVectorParams } from '@mastra/core/vector';
|
|
28
|
+
import type { VectorFieldValue } from '@mastra/core/vector/filter';
|
|
26
29
|
import type { VectorFilter } from '@mastra/core/vector/filter';
|
|
27
30
|
import type { WorkflowRun } from '@mastra/core/storage';
|
|
28
31
|
import type { WorkflowRuns } from '@mastra/core/storage';
|
|
29
32
|
|
|
30
|
-
export declare function buildFilterQuery(filter:
|
|
33
|
+
export declare function buildFilterQuery(filter: LibSQLVectorFilter): FilterResult;
|
|
31
34
|
|
|
32
35
|
declare interface FilterResult {
|
|
33
36
|
sql: string;
|
|
@@ -70,13 +73,23 @@ export { LibSQLConfig as LibSQLConfig_alias_1 }
|
|
|
70
73
|
* - Can take either a single condition or an array of conditions
|
|
71
74
|
*
|
|
72
75
|
*/
|
|
73
|
-
export declare class LibSQLFilterTranslator extends BaseFilterTranslator {
|
|
76
|
+
export declare class LibSQLFilterTranslator extends BaseFilterTranslator<LibSQLVectorFilter> {
|
|
74
77
|
protected getSupportedOperators(): OperatorSupport;
|
|
75
|
-
translate(filter?:
|
|
78
|
+
translate(filter?: LibSQLVectorFilter): LibSQLVectorFilter;
|
|
76
79
|
private translateNode;
|
|
77
80
|
}
|
|
78
81
|
|
|
79
|
-
declare
|
|
82
|
+
declare type LibSQLOperatorValueMap = Omit<OperatorValueMap, '$regex' | '$options' | '$in' | '$all' | '$nin' | '$eq' | '$ne'> & {
|
|
83
|
+
$size: number;
|
|
84
|
+
$contains: VectorFieldValue | Record<string, unknown>;
|
|
85
|
+
$all: VectorFieldValue;
|
|
86
|
+
$in: VectorFieldValue;
|
|
87
|
+
$nin: VectorFieldValue;
|
|
88
|
+
$eq: VectorFieldValue;
|
|
89
|
+
$ne: VectorFieldValue;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
declare interface LibSQLQueryVectorParams extends QueryVectorParams<LibSQLVectorFilter> {
|
|
80
93
|
minScore?: number;
|
|
81
94
|
}
|
|
82
95
|
|
|
@@ -87,6 +100,7 @@ declare class LibSQLStore extends MastraStorage {
|
|
|
87
100
|
constructor(config: LibSQLConfig);
|
|
88
101
|
get supports(): {
|
|
89
102
|
selectByIncludeResourceScope: boolean;
|
|
103
|
+
resourceWorkingMemory: boolean;
|
|
90
104
|
};
|
|
91
105
|
private getCreateTableSQL;
|
|
92
106
|
createTable({ tableName, schema, }: {
|
|
@@ -224,6 +238,17 @@ declare class LibSQLStore extends MastraStorage {
|
|
|
224
238
|
runId: string;
|
|
225
239
|
workflowName?: string;
|
|
226
240
|
}): Promise<WorkflowRun | null>;
|
|
241
|
+
getResourceById({ resourceId }: {
|
|
242
|
+
resourceId: string;
|
|
243
|
+
}): Promise<StorageResourceType | null>;
|
|
244
|
+
saveResource({ resource }: {
|
|
245
|
+
resource: StorageResourceType;
|
|
246
|
+
}): Promise<StorageResourceType>;
|
|
247
|
+
updateResource({ resourceId, workingMemory, metadata, }: {
|
|
248
|
+
resourceId: string;
|
|
249
|
+
workingMemory?: string;
|
|
250
|
+
metadata?: Record<string, unknown>;
|
|
251
|
+
}): Promise<StorageResourceType>;
|
|
227
252
|
private hasColumn;
|
|
228
253
|
private parseWorkflowRun;
|
|
229
254
|
}
|
|
@@ -232,13 +257,13 @@ export { LibSQLStore as DefaultStorage_alias_1 }
|
|
|
232
257
|
export { LibSQLStore }
|
|
233
258
|
export { LibSQLStore as LibSQLStore_alias_1 }
|
|
234
259
|
|
|
235
|
-
declare class LibSQLVector extends MastraVector {
|
|
260
|
+
declare class LibSQLVector extends MastraVector<LibSQLVectorFilter> {
|
|
236
261
|
private turso;
|
|
237
262
|
private readonly maxRetries;
|
|
238
263
|
private readonly initialBackoffMs;
|
|
239
264
|
constructor({ connectionUrl, authToken, syncUrl, syncInterval, maxRetries, initialBackoffMs, }: LibSQLVectorConfig);
|
|
240
265
|
private executeWriteOperationWithRetry;
|
|
241
|
-
transformFilter(filter?:
|
|
266
|
+
transformFilter(filter?: LibSQLVectorFilter): LibSQLVectorFilter;
|
|
242
267
|
query({ indexName, queryVector, topK, filter, includeVector, minScore, }: LibSQLQueryVectorParams): Promise<QueryResult[]>;
|
|
243
268
|
upsert(args: UpsertVectorParams): Promise<string[]>;
|
|
244
269
|
private doUpsert;
|
|
@@ -302,4 +327,6 @@ declare interface LibSQLVectorConfig {
|
|
|
302
327
|
export { LibSQLVectorConfig }
|
|
303
328
|
export { LibSQLVectorConfig as LibSQLVectorConfig_alias_1 }
|
|
304
329
|
|
|
330
|
+
export declare type LibSQLVectorFilter = VectorFilter<keyof LibSQLOperatorValueMap, LibSQLOperatorValueMap>;
|
|
331
|
+
|
|
305
332
|
export { }
|
package/dist/index.cjs
CHANGED
|
@@ -409,7 +409,7 @@ function buildCondition(key, value, parentPath) {
|
|
|
409
409
|
return handleOperator(key, value);
|
|
410
410
|
}
|
|
411
411
|
function handleLogicalOperator(key, value, parentPath) {
|
|
412
|
-
if (!value || value.length === 0) {
|
|
412
|
+
if (!value || Array.isArray(value) && value.length === 0) {
|
|
413
413
|
switch (key) {
|
|
414
414
|
case "$and":
|
|
415
415
|
case "$nor":
|
|
@@ -433,7 +433,7 @@ function handleLogicalOperator(key, value, parentPath) {
|
|
|
433
433
|
const values = [];
|
|
434
434
|
const joinOperator = key === "$or" || key === "$nor" ? "OR" : "AND";
|
|
435
435
|
const conditions = Array.isArray(value) ? value.map((f) => {
|
|
436
|
-
const entries = Object.entries(f);
|
|
436
|
+
const entries = !!f ? Object.entries(f) : [];
|
|
437
437
|
return entries.map(([k, v]) => buildCondition(k, v));
|
|
438
438
|
}) : [buildCondition(key, value)];
|
|
439
439
|
const joined = conditions.flat().map((c) => {
|
|
@@ -932,7 +932,8 @@ var LibSQLStore = class extends storage.MastraStorage {
|
|
|
932
932
|
}
|
|
933
933
|
get supports() {
|
|
934
934
|
return {
|
|
935
|
-
selectByIncludeResourceScope: true
|
|
935
|
+
selectByIncludeResourceScope: true,
|
|
936
|
+
resourceWorkingMemory: true
|
|
936
937
|
};
|
|
937
938
|
}
|
|
938
939
|
getCreateTableSQL(tableName, schema) {
|
|
@@ -1481,7 +1482,8 @@ var LibSQLStore = class extends storage.MastraStorage {
|
|
|
1481
1482
|
}
|
|
1482
1483
|
async getMessagesPaginated(args) {
|
|
1483
1484
|
const { threadId, format, selectBy } = args;
|
|
1484
|
-
const { page = 0, perPage
|
|
1485
|
+
const { page = 0, perPage: perPageInput, dateRange } = selectBy?.pagination || {};
|
|
1486
|
+
const perPage = perPageInput !== void 0 ? perPageInput : this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
|
|
1485
1487
|
const fromDate = dateRange?.start;
|
|
1486
1488
|
const toDate = dateRange?.end;
|
|
1487
1489
|
const messages = [];
|
|
@@ -1521,7 +1523,7 @@ var LibSQLStore = class extends storage.MastraStorage {
|
|
|
1521
1523
|
args: queryParams
|
|
1522
1524
|
});
|
|
1523
1525
|
const total = Number(countResult.rows?.[0]?.count ?? 0);
|
|
1524
|
-
if (total === 0) {
|
|
1526
|
+
if (total === 0 && messages.length === 0) {
|
|
1525
1527
|
return {
|
|
1526
1528
|
messages: [],
|
|
1527
1529
|
total: 0,
|
|
@@ -1530,9 +1532,11 @@ var LibSQLStore = class extends storage.MastraStorage {
|
|
|
1530
1532
|
hasMore: false
|
|
1531
1533
|
};
|
|
1532
1534
|
}
|
|
1535
|
+
const excludeIds = messages.map((m) => m.id);
|
|
1536
|
+
const excludeIdsParam = excludeIds.map((_, idx) => `$${idx + queryParams.length + 1}`).join(", ");
|
|
1533
1537
|
const dataResult = await this.client.execute({
|
|
1534
|
-
sql: `SELECT id, content, role, type, "createdAt", thread_id FROM ${storage.TABLE_MESSAGES} ${whereClause} ORDER BY "createdAt" DESC LIMIT ? OFFSET ?`,
|
|
1535
|
-
args: [...queryParams, perPage, currentOffset]
|
|
1538
|
+
sql: `SELECT id, content, role, type, "createdAt", "resourceId", "thread_id" FROM ${storage.TABLE_MESSAGES} ${whereClause} ${excludeIds.length ? `AND id NOT IN (${excludeIdsParam})` : ""} ORDER BY "createdAt" DESC LIMIT ? OFFSET ?`,
|
|
1539
|
+
args: [...queryParams, ...excludeIds, perPage, currentOffset]
|
|
1536
1540
|
});
|
|
1537
1541
|
messages.push(...(dataResult.rows || []).map((row) => this.parseRow(row)));
|
|
1538
1542
|
const messagesToReturn = format === "v1" ? new agent.MessageList().add(messages, "memory").get.all.v1() : new agent.MessageList().add(messages, "memory").get.all.v2();
|
|
@@ -1582,7 +1586,14 @@ var LibSQLStore = class extends storage.MastraStorage {
|
|
|
1582
1586
|
}
|
|
1583
1587
|
return {
|
|
1584
1588
|
sql: `INSERT INTO ${storage.TABLE_MESSAGES} (id, thread_id, content, role, type, createdAt, resourceId)
|
|
1585
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1589
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1590
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
1591
|
+
thread_id=excluded.thread_id,
|
|
1592
|
+
content=excluded.content,
|
|
1593
|
+
role=excluded.role,
|
|
1594
|
+
type=excluded.type,
|
|
1595
|
+
resourceId=excluded.resourceId
|
|
1596
|
+
`,
|
|
1586
1597
|
args: [
|
|
1587
1598
|
message.id,
|
|
1588
1599
|
message.threadId,
|
|
@@ -2008,6 +2019,75 @@ var LibSQLStore = class extends storage.MastraStorage {
|
|
|
2008
2019
|
);
|
|
2009
2020
|
}
|
|
2010
2021
|
}
|
|
2022
|
+
async getResourceById({ resourceId }) {
|
|
2023
|
+
const result = await this.load({
|
|
2024
|
+
tableName: storage.TABLE_RESOURCES,
|
|
2025
|
+
keys: { id: resourceId }
|
|
2026
|
+
});
|
|
2027
|
+
if (!result) {
|
|
2028
|
+
return null;
|
|
2029
|
+
}
|
|
2030
|
+
return {
|
|
2031
|
+
...result,
|
|
2032
|
+
// Ensure workingMemory is always returned as a string, even if auto-parsed as JSON
|
|
2033
|
+
workingMemory: typeof result.workingMemory === "object" ? JSON.stringify(result.workingMemory) : result.workingMemory,
|
|
2034
|
+
metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata
|
|
2035
|
+
};
|
|
2036
|
+
}
|
|
2037
|
+
async saveResource({ resource }) {
|
|
2038
|
+
await this.insert({
|
|
2039
|
+
tableName: storage.TABLE_RESOURCES,
|
|
2040
|
+
record: {
|
|
2041
|
+
...resource,
|
|
2042
|
+
metadata: JSON.stringify(resource.metadata)
|
|
2043
|
+
}
|
|
2044
|
+
});
|
|
2045
|
+
return resource;
|
|
2046
|
+
}
|
|
2047
|
+
async updateResource({
|
|
2048
|
+
resourceId,
|
|
2049
|
+
workingMemory,
|
|
2050
|
+
metadata
|
|
2051
|
+
}) {
|
|
2052
|
+
const existingResource = await this.getResourceById({ resourceId });
|
|
2053
|
+
if (!existingResource) {
|
|
2054
|
+
const newResource = {
|
|
2055
|
+
id: resourceId,
|
|
2056
|
+
workingMemory,
|
|
2057
|
+
metadata: metadata || {},
|
|
2058
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
2059
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2060
|
+
};
|
|
2061
|
+
return this.saveResource({ resource: newResource });
|
|
2062
|
+
}
|
|
2063
|
+
const updatedResource = {
|
|
2064
|
+
...existingResource,
|
|
2065
|
+
workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
|
|
2066
|
+
metadata: {
|
|
2067
|
+
...existingResource.metadata,
|
|
2068
|
+
...metadata
|
|
2069
|
+
},
|
|
2070
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2071
|
+
};
|
|
2072
|
+
const updates = [];
|
|
2073
|
+
const values = [];
|
|
2074
|
+
if (workingMemory !== void 0) {
|
|
2075
|
+
updates.push("workingMemory = ?");
|
|
2076
|
+
values.push(workingMemory);
|
|
2077
|
+
}
|
|
2078
|
+
if (metadata) {
|
|
2079
|
+
updates.push("metadata = ?");
|
|
2080
|
+
values.push(JSON.stringify(updatedResource.metadata));
|
|
2081
|
+
}
|
|
2082
|
+
updates.push("updatedAt = ?");
|
|
2083
|
+
values.push(updatedResource.updatedAt.toISOString());
|
|
2084
|
+
values.push(resourceId);
|
|
2085
|
+
await this.client.execute({
|
|
2086
|
+
sql: `UPDATE ${storage.TABLE_RESOURCES} SET ${updates.join(", ")} WHERE id = ?`,
|
|
2087
|
+
args: values
|
|
2088
|
+
});
|
|
2089
|
+
return updatedResource;
|
|
2090
|
+
}
|
|
2011
2091
|
async hasColumn(table, column) {
|
|
2012
2092
|
const result = await this.client.execute({
|
|
2013
2093
|
sql: `PRAGMA table_info(${table})`
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { parseSqlIdentifier, parseFieldKey } from '@mastra/core/utils';
|
|
|
4
4
|
import { MastraVector } from '@mastra/core/vector';
|
|
5
5
|
import { BaseFilterTranslator } from '@mastra/core/vector/filter';
|
|
6
6
|
import { MessageList } from '@mastra/core/agent';
|
|
7
|
-
import { MastraStorage, TABLE_WORKFLOW_SNAPSHOT, TABLE_THREADS, TABLE_MESSAGES, TABLE_EVALS, TABLE_TRACES } from '@mastra/core/storage';
|
|
7
|
+
import { MastraStorage, TABLE_WORKFLOW_SNAPSHOT, TABLE_THREADS, TABLE_MESSAGES, TABLE_EVALS, TABLE_TRACES, TABLE_RESOURCES } from '@mastra/core/storage';
|
|
8
8
|
|
|
9
9
|
// src/vector/index.ts
|
|
10
10
|
var LibSQLFilterTranslator = class extends BaseFilterTranslator {
|
|
@@ -407,7 +407,7 @@ function buildCondition(key, value, parentPath) {
|
|
|
407
407
|
return handleOperator(key, value);
|
|
408
408
|
}
|
|
409
409
|
function handleLogicalOperator(key, value, parentPath) {
|
|
410
|
-
if (!value || value.length === 0) {
|
|
410
|
+
if (!value || Array.isArray(value) && value.length === 0) {
|
|
411
411
|
switch (key) {
|
|
412
412
|
case "$and":
|
|
413
413
|
case "$nor":
|
|
@@ -431,7 +431,7 @@ function handleLogicalOperator(key, value, parentPath) {
|
|
|
431
431
|
const values = [];
|
|
432
432
|
const joinOperator = key === "$or" || key === "$nor" ? "OR" : "AND";
|
|
433
433
|
const conditions = Array.isArray(value) ? value.map((f) => {
|
|
434
|
-
const entries = Object.entries(f);
|
|
434
|
+
const entries = !!f ? Object.entries(f) : [];
|
|
435
435
|
return entries.map(([k, v]) => buildCondition(k, v));
|
|
436
436
|
}) : [buildCondition(key, value)];
|
|
437
437
|
const joined = conditions.flat().map((c) => {
|
|
@@ -930,7 +930,8 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
930
930
|
}
|
|
931
931
|
get supports() {
|
|
932
932
|
return {
|
|
933
|
-
selectByIncludeResourceScope: true
|
|
933
|
+
selectByIncludeResourceScope: true,
|
|
934
|
+
resourceWorkingMemory: true
|
|
934
935
|
};
|
|
935
936
|
}
|
|
936
937
|
getCreateTableSQL(tableName, schema) {
|
|
@@ -1479,7 +1480,8 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
1479
1480
|
}
|
|
1480
1481
|
async getMessagesPaginated(args) {
|
|
1481
1482
|
const { threadId, format, selectBy } = args;
|
|
1482
|
-
const { page = 0, perPage
|
|
1483
|
+
const { page = 0, perPage: perPageInput, dateRange } = selectBy?.pagination || {};
|
|
1484
|
+
const perPage = perPageInput !== void 0 ? perPageInput : this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
|
|
1483
1485
|
const fromDate = dateRange?.start;
|
|
1484
1486
|
const toDate = dateRange?.end;
|
|
1485
1487
|
const messages = [];
|
|
@@ -1519,7 +1521,7 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
1519
1521
|
args: queryParams
|
|
1520
1522
|
});
|
|
1521
1523
|
const total = Number(countResult.rows?.[0]?.count ?? 0);
|
|
1522
|
-
if (total === 0) {
|
|
1524
|
+
if (total === 0 && messages.length === 0) {
|
|
1523
1525
|
return {
|
|
1524
1526
|
messages: [],
|
|
1525
1527
|
total: 0,
|
|
@@ -1528,9 +1530,11 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
1528
1530
|
hasMore: false
|
|
1529
1531
|
};
|
|
1530
1532
|
}
|
|
1533
|
+
const excludeIds = messages.map((m) => m.id);
|
|
1534
|
+
const excludeIdsParam = excludeIds.map((_, idx) => `$${idx + queryParams.length + 1}`).join(", ");
|
|
1531
1535
|
const dataResult = await this.client.execute({
|
|
1532
|
-
sql: `SELECT id, content, role, type, "createdAt", thread_id FROM ${TABLE_MESSAGES} ${whereClause} ORDER BY "createdAt" DESC LIMIT ? OFFSET ?`,
|
|
1533
|
-
args: [...queryParams, perPage, currentOffset]
|
|
1536
|
+
sql: `SELECT id, content, role, type, "createdAt", "resourceId", "thread_id" FROM ${TABLE_MESSAGES} ${whereClause} ${excludeIds.length ? `AND id NOT IN (${excludeIdsParam})` : ""} ORDER BY "createdAt" DESC LIMIT ? OFFSET ?`,
|
|
1537
|
+
args: [...queryParams, ...excludeIds, perPage, currentOffset]
|
|
1534
1538
|
});
|
|
1535
1539
|
messages.push(...(dataResult.rows || []).map((row) => this.parseRow(row)));
|
|
1536
1540
|
const messagesToReturn = format === "v1" ? new MessageList().add(messages, "memory").get.all.v1() : new MessageList().add(messages, "memory").get.all.v2();
|
|
@@ -1580,7 +1584,14 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
1580
1584
|
}
|
|
1581
1585
|
return {
|
|
1582
1586
|
sql: `INSERT INTO ${TABLE_MESSAGES} (id, thread_id, content, role, type, createdAt, resourceId)
|
|
1583
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1587
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1588
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
1589
|
+
thread_id=excluded.thread_id,
|
|
1590
|
+
content=excluded.content,
|
|
1591
|
+
role=excluded.role,
|
|
1592
|
+
type=excluded.type,
|
|
1593
|
+
resourceId=excluded.resourceId
|
|
1594
|
+
`,
|
|
1584
1595
|
args: [
|
|
1585
1596
|
message.id,
|
|
1586
1597
|
message.threadId,
|
|
@@ -2006,6 +2017,75 @@ var LibSQLStore = class extends MastraStorage {
|
|
|
2006
2017
|
);
|
|
2007
2018
|
}
|
|
2008
2019
|
}
|
|
2020
|
+
async getResourceById({ resourceId }) {
|
|
2021
|
+
const result = await this.load({
|
|
2022
|
+
tableName: TABLE_RESOURCES,
|
|
2023
|
+
keys: { id: resourceId }
|
|
2024
|
+
});
|
|
2025
|
+
if (!result) {
|
|
2026
|
+
return null;
|
|
2027
|
+
}
|
|
2028
|
+
return {
|
|
2029
|
+
...result,
|
|
2030
|
+
// Ensure workingMemory is always returned as a string, even if auto-parsed as JSON
|
|
2031
|
+
workingMemory: typeof result.workingMemory === "object" ? JSON.stringify(result.workingMemory) : result.workingMemory,
|
|
2032
|
+
metadata: typeof result.metadata === "string" ? JSON.parse(result.metadata) : result.metadata
|
|
2033
|
+
};
|
|
2034
|
+
}
|
|
2035
|
+
async saveResource({ resource }) {
|
|
2036
|
+
await this.insert({
|
|
2037
|
+
tableName: TABLE_RESOURCES,
|
|
2038
|
+
record: {
|
|
2039
|
+
...resource,
|
|
2040
|
+
metadata: JSON.stringify(resource.metadata)
|
|
2041
|
+
}
|
|
2042
|
+
});
|
|
2043
|
+
return resource;
|
|
2044
|
+
}
|
|
2045
|
+
async updateResource({
|
|
2046
|
+
resourceId,
|
|
2047
|
+
workingMemory,
|
|
2048
|
+
metadata
|
|
2049
|
+
}) {
|
|
2050
|
+
const existingResource = await this.getResourceById({ resourceId });
|
|
2051
|
+
if (!existingResource) {
|
|
2052
|
+
const newResource = {
|
|
2053
|
+
id: resourceId,
|
|
2054
|
+
workingMemory,
|
|
2055
|
+
metadata: metadata || {},
|
|
2056
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
2057
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2058
|
+
};
|
|
2059
|
+
return this.saveResource({ resource: newResource });
|
|
2060
|
+
}
|
|
2061
|
+
const updatedResource = {
|
|
2062
|
+
...existingResource,
|
|
2063
|
+
workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
|
|
2064
|
+
metadata: {
|
|
2065
|
+
...existingResource.metadata,
|
|
2066
|
+
...metadata
|
|
2067
|
+
},
|
|
2068
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2069
|
+
};
|
|
2070
|
+
const updates = [];
|
|
2071
|
+
const values = [];
|
|
2072
|
+
if (workingMemory !== void 0) {
|
|
2073
|
+
updates.push("workingMemory = ?");
|
|
2074
|
+
values.push(workingMemory);
|
|
2075
|
+
}
|
|
2076
|
+
if (metadata) {
|
|
2077
|
+
updates.push("metadata = ?");
|
|
2078
|
+
values.push(JSON.stringify(updatedResource.metadata));
|
|
2079
|
+
}
|
|
2080
|
+
updates.push("updatedAt = ?");
|
|
2081
|
+
values.push(updatedResource.updatedAt.toISOString());
|
|
2082
|
+
values.push(resourceId);
|
|
2083
|
+
await this.client.execute({
|
|
2084
|
+
sql: `UPDATE ${TABLE_RESOURCES} SET ${updates.join(", ")} WHERE id = ?`,
|
|
2085
|
+
args: values
|
|
2086
|
+
});
|
|
2087
|
+
return updatedResource;
|
|
2088
|
+
}
|
|
2009
2089
|
async hasColumn(table, column) {
|
|
2010
2090
|
const result = await this.client.execute({
|
|
2011
2091
|
sql: `PRAGMA table_info(${table})`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/libsql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0-alpha.3",
|
|
4
4
|
"description": "Libsql provider for Mastra - includes both vector and db storage capabilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -25,16 +25,16 @@
|
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@microsoft/api-extractor": "^7.52.8",
|
|
27
27
|
"@types/node": "^20.19.0",
|
|
28
|
-
"eslint": "^9.
|
|
28
|
+
"eslint": "^9.29.0",
|
|
29
29
|
"tsup": "^8.5.0",
|
|
30
30
|
"typescript": "^5.8.3",
|
|
31
31
|
"vitest": "^3.2.3",
|
|
32
|
+
"@internal/lint": "0.0.13",
|
|
32
33
|
"@internal/storage-test-utils": "0.0.9",
|
|
33
|
-
"@mastra/core": "0.10.7-alpha.
|
|
34
|
-
"@internal/lint": "0.0.13"
|
|
34
|
+
"@mastra/core": "0.10.7-alpha.3"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"@mastra/core": ">=0.10.
|
|
37
|
+
"@mastra/core": ">=0.10.7-0 <0.11.0-0"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting",
|
package/src/storage/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
TABLE_MESSAGES,
|
|
12
12
|
TABLE_THREADS,
|
|
13
13
|
TABLE_TRACES,
|
|
14
|
+
TABLE_RESOURCES,
|
|
14
15
|
TABLE_WORKFLOW_SNAPSHOT,
|
|
15
16
|
} from '@mastra/core/storage';
|
|
16
17
|
import type {
|
|
@@ -19,6 +20,7 @@ import type {
|
|
|
19
20
|
PaginationInfo,
|
|
20
21
|
StorageColumn,
|
|
21
22
|
StorageGetMessagesArg,
|
|
23
|
+
StorageResourceType,
|
|
22
24
|
TABLE_NAMES,
|
|
23
25
|
WorkflowRun,
|
|
24
26
|
WorkflowRuns,
|
|
@@ -84,9 +86,11 @@ export class LibSQLStore extends MastraStorage {
|
|
|
84
86
|
|
|
85
87
|
public get supports(): {
|
|
86
88
|
selectByIncludeResourceScope: boolean;
|
|
89
|
+
resourceWorkingMemory: boolean;
|
|
87
90
|
} {
|
|
88
91
|
return {
|
|
89
92
|
selectByIncludeResourceScope: true,
|
|
93
|
+
resourceWorkingMemory: true,
|
|
90
94
|
};
|
|
91
95
|
}
|
|
92
96
|
|
|
@@ -748,7 +752,9 @@ export class LibSQLStore extends MastraStorage {
|
|
|
748
752
|
},
|
|
749
753
|
): Promise<PaginationInfo & { messages: MastraMessageV1[] | MastraMessageV2[] }> {
|
|
750
754
|
const { threadId, format, selectBy } = args;
|
|
751
|
-
const { page = 0, perPage
|
|
755
|
+
const { page = 0, perPage: perPageInput, dateRange } = selectBy?.pagination || {};
|
|
756
|
+
const perPage =
|
|
757
|
+
perPageInput !== undefined ? perPageInput : this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
|
|
752
758
|
const fromDate = dateRange?.start;
|
|
753
759
|
const toDate = dateRange?.end;
|
|
754
760
|
|
|
@@ -795,7 +801,7 @@ export class LibSQLStore extends MastraStorage {
|
|
|
795
801
|
});
|
|
796
802
|
const total = Number(countResult.rows?.[0]?.count ?? 0);
|
|
797
803
|
|
|
798
|
-
if (total === 0) {
|
|
804
|
+
if (total === 0 && messages.length === 0) {
|
|
799
805
|
return {
|
|
800
806
|
messages: [],
|
|
801
807
|
total: 0,
|
|
@@ -805,9 +811,12 @@ export class LibSQLStore extends MastraStorage {
|
|
|
805
811
|
};
|
|
806
812
|
}
|
|
807
813
|
|
|
814
|
+
const excludeIds = messages.map(m => m.id);
|
|
815
|
+
const excludeIdsParam = excludeIds.map((_, idx) => `$${idx + queryParams.length + 1}`).join(', ');
|
|
816
|
+
|
|
808
817
|
const dataResult = await this.client.execute({
|
|
809
|
-
sql: `SELECT id, content, role, type, "createdAt", thread_id FROM ${TABLE_MESSAGES} ${whereClause} ORDER BY "createdAt" DESC LIMIT ? OFFSET ?`,
|
|
810
|
-
args: [...queryParams, perPage, currentOffset],
|
|
818
|
+
sql: `SELECT id, content, role, type, "createdAt", "resourceId", "thread_id" FROM ${TABLE_MESSAGES} ${whereClause} ${excludeIds.length ? `AND id NOT IN (${excludeIdsParam})` : ''} ORDER BY "createdAt" DESC LIMIT ? OFFSET ?`,
|
|
819
|
+
args: [...queryParams, ...excludeIds, perPage, currentOffset],
|
|
811
820
|
});
|
|
812
821
|
|
|
813
822
|
messages.push(...(dataResult.rows || []).map((row: any) => this.parseRow(row)));
|
|
@@ -871,7 +880,14 @@ export class LibSQLStore extends MastraStorage {
|
|
|
871
880
|
}
|
|
872
881
|
return {
|
|
873
882
|
sql: `INSERT INTO ${TABLE_MESSAGES} (id, thread_id, content, role, type, createdAt, resourceId)
|
|
874
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
883
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
884
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
885
|
+
thread_id=excluded.thread_id,
|
|
886
|
+
content=excluded.content,
|
|
887
|
+
role=excluded.role,
|
|
888
|
+
type=excluded.type,
|
|
889
|
+
resourceId=excluded.resourceId
|
|
890
|
+
`,
|
|
875
891
|
args: [
|
|
876
892
|
message.id,
|
|
877
893
|
message.threadId!,
|
|
@@ -1416,6 +1432,96 @@ export class LibSQLStore extends MastraStorage {
|
|
|
1416
1432
|
}
|
|
1417
1433
|
}
|
|
1418
1434
|
|
|
1435
|
+
async getResourceById({ resourceId }: { resourceId: string }): Promise<StorageResourceType | null> {
|
|
1436
|
+
const result = await this.load<StorageResourceType>({
|
|
1437
|
+
tableName: TABLE_RESOURCES,
|
|
1438
|
+
keys: { id: resourceId },
|
|
1439
|
+
});
|
|
1440
|
+
|
|
1441
|
+
if (!result) {
|
|
1442
|
+
return null;
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
return {
|
|
1446
|
+
...result,
|
|
1447
|
+
// Ensure workingMemory is always returned as a string, even if auto-parsed as JSON
|
|
1448
|
+
workingMemory:
|
|
1449
|
+
typeof result.workingMemory === 'object' ? JSON.stringify(result.workingMemory) : result.workingMemory,
|
|
1450
|
+
metadata: typeof result.metadata === 'string' ? JSON.parse(result.metadata) : result.metadata,
|
|
1451
|
+
};
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
async saveResource({ resource }: { resource: StorageResourceType }): Promise<StorageResourceType> {
|
|
1455
|
+
await this.insert({
|
|
1456
|
+
tableName: TABLE_RESOURCES,
|
|
1457
|
+
record: {
|
|
1458
|
+
...resource,
|
|
1459
|
+
metadata: JSON.stringify(resource.metadata),
|
|
1460
|
+
},
|
|
1461
|
+
});
|
|
1462
|
+
|
|
1463
|
+
return resource;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
async updateResource({
|
|
1467
|
+
resourceId,
|
|
1468
|
+
workingMemory,
|
|
1469
|
+
metadata,
|
|
1470
|
+
}: {
|
|
1471
|
+
resourceId: string;
|
|
1472
|
+
workingMemory?: string;
|
|
1473
|
+
metadata?: Record<string, unknown>;
|
|
1474
|
+
}): Promise<StorageResourceType> {
|
|
1475
|
+
const existingResource = await this.getResourceById({ resourceId });
|
|
1476
|
+
|
|
1477
|
+
if (!existingResource) {
|
|
1478
|
+
// Create new resource if it doesn't exist
|
|
1479
|
+
const newResource: StorageResourceType = {
|
|
1480
|
+
id: resourceId,
|
|
1481
|
+
workingMemory,
|
|
1482
|
+
metadata: metadata || {},
|
|
1483
|
+
createdAt: new Date(),
|
|
1484
|
+
updatedAt: new Date(),
|
|
1485
|
+
};
|
|
1486
|
+
return this.saveResource({ resource: newResource });
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
const updatedResource = {
|
|
1490
|
+
...existingResource,
|
|
1491
|
+
workingMemory: workingMemory !== undefined ? workingMemory : existingResource.workingMemory,
|
|
1492
|
+
metadata: {
|
|
1493
|
+
...existingResource.metadata,
|
|
1494
|
+
...metadata,
|
|
1495
|
+
},
|
|
1496
|
+
updatedAt: new Date(),
|
|
1497
|
+
};
|
|
1498
|
+
|
|
1499
|
+
const updates: string[] = [];
|
|
1500
|
+
const values: InValue[] = [];
|
|
1501
|
+
|
|
1502
|
+
if (workingMemory !== undefined) {
|
|
1503
|
+
updates.push('workingMemory = ?');
|
|
1504
|
+
values.push(workingMemory);
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
if (metadata) {
|
|
1508
|
+
updates.push('metadata = ?');
|
|
1509
|
+
values.push(JSON.stringify(updatedResource.metadata));
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
updates.push('updatedAt = ?');
|
|
1513
|
+
values.push(updatedResource.updatedAt.toISOString());
|
|
1514
|
+
|
|
1515
|
+
values.push(resourceId);
|
|
1516
|
+
|
|
1517
|
+
await this.client.execute({
|
|
1518
|
+
sql: `UPDATE ${TABLE_RESOURCES} SET ${updates.join(', ')} WHERE id = ?`,
|
|
1519
|
+
args: values,
|
|
1520
|
+
});
|
|
1521
|
+
|
|
1522
|
+
return updatedResource;
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1419
1525
|
private async hasColumn(table: string, column: string): Promise<boolean> {
|
|
1420
1526
|
const result = await this.client.execute({
|
|
1421
1527
|
sql: `PRAGMA table_info(${table})`,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
2
|
|
|
3
|
+
import type { LibSQLVectorFilter } from './filter';
|
|
3
4
|
import { LibSQLFilterTranslator } from './filter';
|
|
4
5
|
|
|
5
6
|
describe('LibSQLFilterTranslator', () => {
|
|
@@ -17,14 +18,8 @@ describe('LibSQLFilterTranslator', () => {
|
|
|
17
18
|
});
|
|
18
19
|
});
|
|
19
20
|
|
|
20
|
-
it('translates array to $in', () => {
|
|
21
|
-
expect(translator.translate({ field: ['a', 'b'] })).toEqual({
|
|
22
|
-
field: { $in: ['a', 'b'] },
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
|
|
26
21
|
it('preserves comparison operators', () => {
|
|
27
|
-
const filter = {
|
|
22
|
+
const filter: LibSQLVectorFilter = {
|
|
28
23
|
field1: { $eq: 'value' },
|
|
29
24
|
field2: { $ne: 'value' },
|
|
30
25
|
field3: { $gt: 5 },
|
|
@@ -55,7 +50,7 @@ describe('LibSQLFilterTranslator', () => {
|
|
|
55
50
|
nested: {
|
|
56
51
|
field: 'value',
|
|
57
52
|
},
|
|
58
|
-
}),
|
|
53
|
+
} as any),
|
|
59
54
|
).toEqual({
|
|
60
55
|
'nested.field': { $eq: 'value' },
|
|
61
56
|
});
|
|
@@ -65,7 +60,8 @@ describe('LibSQLFilterTranslator', () => {
|
|
|
65
60
|
// Array Operations
|
|
66
61
|
describe('array operations', () => {
|
|
67
62
|
it('translates array to $in', () => {
|
|
68
|
-
|
|
63
|
+
const filter: LibSQLVectorFilter = { field: ['a', 'b'] };
|
|
64
|
+
expect(translator.translate(filter)).toEqual({
|
|
69
65
|
field: { $in: ['a', 'b'] },
|
|
70
66
|
});
|
|
71
67
|
});
|
|
@@ -82,7 +78,7 @@ describe('LibSQLFilterTranslator', () => {
|
|
|
82
78
|
it('handles empty arrays', () => {
|
|
83
79
|
expect(
|
|
84
80
|
translator.translate({
|
|
85
|
-
field: [],
|
|
81
|
+
field: [] as any,
|
|
86
82
|
}),
|
|
87
83
|
).toEqual({
|
|
88
84
|
field: { $in: [] },
|
|
@@ -128,7 +124,7 @@ describe('LibSQLFilterTranslator', () => {
|
|
|
128
124
|
});
|
|
129
125
|
|
|
130
126
|
// Array Operator Normalization
|
|
131
|
-
describe('
|
|
127
|
+
describe('Array operator normalization', () => {
|
|
132
128
|
it('normalizes single values for $all', () => {
|
|
133
129
|
expect(
|
|
134
130
|
translator.translate({
|
|
@@ -173,7 +169,7 @@ describe('LibSQLFilterTranslator', () => {
|
|
|
173
169
|
// Logical Operators
|
|
174
170
|
describe('Logical Operators', () => {
|
|
175
171
|
it('handles logical operators', () => {
|
|
176
|
-
const filter = {
|
|
172
|
+
const filter: LibSQLVectorFilter = {
|
|
177
173
|
$and: [{ field1: { $eq: 'value1' } }, { field2: { $eq: 'value2' } }],
|
|
178
174
|
$or: [{ field3: { $eq: 'value3' } }, { field4: { $eq: 'value4' } }],
|
|
179
175
|
};
|
|
@@ -457,16 +453,6 @@ describe('LibSQLFilterTranslator', () => {
|
|
|
457
453
|
field: { $eq: '' },
|
|
458
454
|
});
|
|
459
455
|
});
|
|
460
|
-
|
|
461
|
-
it('handles empty arrays', () => {
|
|
462
|
-
expect(
|
|
463
|
-
translator.translate({
|
|
464
|
-
field: [],
|
|
465
|
-
}),
|
|
466
|
-
).toEqual({
|
|
467
|
-
field: { $in: [] },
|
|
468
|
-
});
|
|
469
|
-
});
|
|
470
456
|
});
|
|
471
457
|
|
|
472
458
|
// Complex Filter Structures
|
|
@@ -540,53 +526,10 @@ describe('LibSQLFilterTranslator', () => {
|
|
|
540
526
|
});
|
|
541
527
|
});
|
|
542
528
|
|
|
543
|
-
// Array Operator Normalization
|
|
544
|
-
describe('Array Operator Normalization', () => {
|
|
545
|
-
it('normalizes single values for $all', () => {
|
|
546
|
-
expect(
|
|
547
|
-
translator.translate({
|
|
548
|
-
field: { $all: 'value' },
|
|
549
|
-
}),
|
|
550
|
-
).toEqual({
|
|
551
|
-
field: { $all: ['value'] },
|
|
552
|
-
});
|
|
553
|
-
});
|
|
554
|
-
|
|
555
|
-
it('normalizes single values for $in', () => {
|
|
556
|
-
expect(
|
|
557
|
-
translator.translate({
|
|
558
|
-
field: { $in: 'value' },
|
|
559
|
-
}),
|
|
560
|
-
).toEqual({
|
|
561
|
-
field: { $in: ['value'] },
|
|
562
|
-
});
|
|
563
|
-
});
|
|
564
|
-
|
|
565
|
-
it('normalizes single values for $nin', () => {
|
|
566
|
-
expect(
|
|
567
|
-
translator.translate({
|
|
568
|
-
field: { $nin: 'value' },
|
|
569
|
-
}),
|
|
570
|
-
).toEqual({
|
|
571
|
-
field: { $nin: ['value'] },
|
|
572
|
-
});
|
|
573
|
-
});
|
|
574
|
-
|
|
575
|
-
it('preserves arrays for array operators', () => {
|
|
576
|
-
expect(
|
|
577
|
-
translator.translate({
|
|
578
|
-
field: { $all: ['value1', 'value2'] },
|
|
579
|
-
}),
|
|
580
|
-
).toEqual({
|
|
581
|
-
field: { $all: ['value1', 'value2'] },
|
|
582
|
-
});
|
|
583
|
-
});
|
|
584
|
-
});
|
|
585
|
-
|
|
586
529
|
// Operator Support Validation
|
|
587
530
|
describe('Operator Support Validation', () => {
|
|
588
531
|
it('ensure all operator filters are supported', () => {
|
|
589
|
-
const supportedFilters = [
|
|
532
|
+
const supportedFilters: LibSQLVectorFilter[] = [
|
|
590
533
|
// Basic comparison operators
|
|
591
534
|
{ field: { $eq: 'value' } },
|
|
592
535
|
{ field: { $ne: 'value' } },
|
|
@@ -612,12 +555,7 @@ describe('LibSQLFilterTranslator', () => {
|
|
|
612
555
|
{ $or: [{ field1: 'value1' }, { field2: 'value2' }] },
|
|
613
556
|
{ $nor: [{ field1: 'value1' }, { field2: 'value2' }] },
|
|
614
557
|
|
|
615
|
-
{ $and: {
|
|
616
|
-
{ $or: { field: 'value' } },
|
|
617
|
-
{ $nor: { field: 'value' } },
|
|
618
|
-
{ $not: { field: 'value' } },
|
|
619
|
-
|
|
620
|
-
{ $or: [{ $and: { field1: 'value1' } }, { $not: { field2: 'value2' } }] },
|
|
558
|
+
{ $or: [{ $and: [{ field1: 'value1' }] }, { $not: { field2: 'value2' } }] },
|
|
621
559
|
|
|
622
560
|
{ field: { $not: { $eq: 'value' } } },
|
|
623
561
|
{ field: { $not: { $in: ['value1', 'value2'] } } },
|
|
@@ -634,7 +572,7 @@ describe('LibSQLFilterTranslator', () => {
|
|
|
634
572
|
|
|
635
573
|
it('throws error for $not if not an object', () => {
|
|
636
574
|
expect(() => translator.translate({ $not: 'value' })).toThrow();
|
|
637
|
-
expect(() => translator.translate({ $not: [{ field: 'value' }] })).toThrow();
|
|
575
|
+
expect(() => translator.translate({ $not: [{ field: 'value' }] } as any)).toThrow();
|
|
638
576
|
});
|
|
639
577
|
it('throws error for $not if empty', () => {
|
|
640
578
|
expect(() => translator.translate({ $not: {} })).toThrow();
|
|
@@ -646,21 +584,21 @@ describe('LibSQLFilterTranslator', () => {
|
|
|
646
584
|
expect(() =>
|
|
647
585
|
translator.translate({
|
|
648
586
|
field: { $and: [{ $eq: 'value1' }, { $eq: 'value2' }] },
|
|
649
|
-
}),
|
|
587
|
+
} as any),
|
|
650
588
|
).toThrow();
|
|
651
589
|
|
|
652
590
|
// $or cannot be used in field conditions
|
|
653
591
|
expect(() =>
|
|
654
592
|
translator.translate({
|
|
655
593
|
field: { $or: [{ $eq: 'value1' }, { $eq: 'value2' }] },
|
|
656
|
-
}),
|
|
594
|
+
} as any),
|
|
657
595
|
).toThrow();
|
|
658
596
|
|
|
659
597
|
// $nor cannot be used in field conditions
|
|
660
598
|
expect(() =>
|
|
661
599
|
translator.translate({
|
|
662
600
|
field: { $nor: [{ $eq: 'value1' }, { $eq: 'value2' }] },
|
|
663
|
-
}),
|
|
601
|
+
} as any),
|
|
664
602
|
).toThrow();
|
|
665
603
|
});
|
|
666
604
|
|
|
@@ -709,7 +647,7 @@ describe('LibSQLFilterTranslator', () => {
|
|
|
709
647
|
field: {
|
|
710
648
|
$gt: {
|
|
711
649
|
$or: [{ subfield: 'value1' }, { subfield: 'value2' }],
|
|
712
|
-
},
|
|
650
|
+
} as any,
|
|
713
651
|
},
|
|
714
652
|
}),
|
|
715
653
|
).toThrow();
|
|
@@ -723,17 +661,17 @@ describe('LibSQLFilterTranslator', () => {
|
|
|
723
661
|
$and: [{ subfield: 'value1' }, { subfield: 'value2' }],
|
|
724
662
|
},
|
|
725
663
|
],
|
|
726
|
-
},
|
|
664
|
+
} as any,
|
|
727
665
|
}),
|
|
728
666
|
).toThrow();
|
|
729
667
|
});
|
|
730
668
|
|
|
731
669
|
it('validates $not operator structure', () => {
|
|
732
670
|
// $not must be an object
|
|
733
|
-
expect(() => translator.translate({ field: { $not: 'value' } })).toThrow();
|
|
671
|
+
expect(() => translator.translate({ field: { $not: 'value' } } as any)).toThrow();
|
|
734
672
|
expect(() => translator.translate({ field: { $not: ['value'] } })).toThrow();
|
|
735
673
|
expect(() => translator.translate({ $not: 'value' })).toThrow();
|
|
736
|
-
expect(() => translator.translate({ $not: ['value'] })).toThrow();
|
|
674
|
+
expect(() => translator.translate({ $not: ['value'] } as any)).toThrow();
|
|
737
675
|
});
|
|
738
676
|
|
|
739
677
|
it('validates $not operator nesting', () => {
|
|
@@ -793,7 +731,7 @@ describe('LibSQLFilterTranslator', () => {
|
|
|
793
731
|
const invalidFilters = [{ $gt: 100 }, { $in: ['value1', 'value2'] }, { $eq: true }];
|
|
794
732
|
|
|
795
733
|
invalidFilters.forEach(filter => {
|
|
796
|
-
expect(() => translator.translate(filter)).toThrow(/Invalid top-level operator/);
|
|
734
|
+
expect(() => translator.translate(filter as any)).toThrow(/Invalid top-level operator/);
|
|
797
735
|
});
|
|
798
736
|
});
|
|
799
737
|
it('allows logical operators at top level', () => {
|
|
@@ -815,13 +753,13 @@ describe('LibSQLFilterTranslator', () => {
|
|
|
815
753
|
// Should throw for non-object values
|
|
816
754
|
expect(() =>
|
|
817
755
|
translator.translate({
|
|
818
|
-
field: { $elemMatch: 'value' },
|
|
756
|
+
field: { $elemMatch: 'value' } as any,
|
|
819
757
|
}),
|
|
820
758
|
).toThrow('$elemMatch requires an object with conditions');
|
|
821
759
|
|
|
822
760
|
expect(() =>
|
|
823
761
|
translator.translate({
|
|
824
|
-
field: { $elemMatch: ['value'] },
|
|
762
|
+
field: { $elemMatch: ['value'] } as any,
|
|
825
763
|
}),
|
|
826
764
|
).toThrow('$elemMatch requires an object with conditions');
|
|
827
765
|
});
|
|
@@ -962,7 +900,7 @@ describe('LibSQLFilterTranslator', () => {
|
|
|
962
900
|
// Unsupported Operations
|
|
963
901
|
describe('unsupported operators', () => {
|
|
964
902
|
it('throws on unsupported operators', () => {
|
|
965
|
-
expect(() => translator.translate({ field: { $regex: 'value' } })).toThrow();
|
|
903
|
+
expect(() => translator.translate({ field: { $regex: 'value' } } as any)).toThrow();
|
|
966
904
|
});
|
|
967
905
|
});
|
|
968
906
|
});
|
package/src/vector/filter.ts
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { BaseFilterTranslator } from '@mastra/core/vector/filter';
|
|
2
|
-
import type {
|
|
2
|
+
import type { VectorFilter, OperatorSupport, OperatorValueMap, VectorFieldValue } from '@mastra/core/vector/filter';
|
|
3
|
+
|
|
4
|
+
type LibSQLOperatorValueMap = Omit<
|
|
5
|
+
OperatorValueMap,
|
|
6
|
+
'$regex' | '$options' | '$in' | '$all' | '$nin' | '$eq' | '$ne'
|
|
7
|
+
> & {
|
|
8
|
+
$size: number;
|
|
9
|
+
$contains: VectorFieldValue | Record<string, unknown>;
|
|
10
|
+
$all: VectorFieldValue;
|
|
11
|
+
$in: VectorFieldValue;
|
|
12
|
+
$nin: VectorFieldValue;
|
|
13
|
+
$eq: VectorFieldValue;
|
|
14
|
+
$ne: VectorFieldValue;
|
|
15
|
+
};
|
|
16
|
+
export type LibSQLVectorFilter = VectorFilter<keyof LibSQLOperatorValueMap, LibSQLOperatorValueMap>;
|
|
3
17
|
|
|
4
18
|
/**
|
|
5
19
|
* Translates MongoDB-style filters to LibSQL compatible filters.
|
|
@@ -11,7 +25,7 @@ import type { FieldCondition, VectorFilter, OperatorSupport } from '@mastra/core
|
|
|
11
25
|
* - Can take either a single condition or an array of conditions
|
|
12
26
|
*
|
|
13
27
|
*/
|
|
14
|
-
export class LibSQLFilterTranslator extends BaseFilterTranslator {
|
|
28
|
+
export class LibSQLFilterTranslator extends BaseFilterTranslator<LibSQLVectorFilter> {
|
|
15
29
|
protected override getSupportedOperators(): OperatorSupport {
|
|
16
30
|
return {
|
|
17
31
|
...BaseFilterTranslator.DEFAULT_OPERATORS,
|
|
@@ -20,7 +34,7 @@ export class LibSQLFilterTranslator extends BaseFilterTranslator {
|
|
|
20
34
|
};
|
|
21
35
|
}
|
|
22
36
|
|
|
23
|
-
translate(filter?:
|
|
37
|
+
translate(filter?: LibSQLVectorFilter): LibSQLVectorFilter {
|
|
24
38
|
if (this.isEmpty(filter)) {
|
|
25
39
|
return filter;
|
|
26
40
|
}
|
|
@@ -28,7 +42,7 @@ export class LibSQLFilterTranslator extends BaseFilterTranslator {
|
|
|
28
42
|
return this.translateNode(filter);
|
|
29
43
|
}
|
|
30
44
|
|
|
31
|
-
private translateNode(node:
|
|
45
|
+
private translateNode(node: LibSQLVectorFilter, currentPath: string = ''): any {
|
|
32
46
|
if (this.isRegex(node)) {
|
|
33
47
|
throw new Error('Direct regex pattern format is not supported in LibSQL');
|
|
34
48
|
}
|
|
@@ -74,7 +88,7 @@ export class LibSQLFilterTranslator extends BaseFilterTranslator {
|
|
|
74
88
|
|
|
75
89
|
if (this.isLogicalOperator(key)) {
|
|
76
90
|
result[key] = Array.isArray(value)
|
|
77
|
-
? value.map((filter:
|
|
91
|
+
? value.map((filter: LibSQLVectorFilter) => this.translateNode(filter))
|
|
78
92
|
: this.translateNode(value);
|
|
79
93
|
} else if (this.isOperator(key)) {
|
|
80
94
|
if (this.isArrayOperator(key) && !Array.isArray(value) && key !== '$elemMatch') {
|
package/src/vector/index.test.ts
CHANGED
|
@@ -1130,7 +1130,7 @@ describe('LibSQLVector', () => {
|
|
|
1130
1130
|
vectorDB.query({
|
|
1131
1131
|
indexName,
|
|
1132
1132
|
queryVector: [1, 0, 0],
|
|
1133
|
-
filter: { price: { $invalid: 100 } },
|
|
1133
|
+
filter: { price: { $invalid: 100 } } as any,
|
|
1134
1134
|
}),
|
|
1135
1135
|
).rejects.toThrow('Unsupported operator: $invalid');
|
|
1136
1136
|
});
|
|
@@ -1360,16 +1360,6 @@ describe('LibSQLVector', () => {
|
|
|
1360
1360
|
});
|
|
1361
1361
|
});
|
|
1362
1362
|
|
|
1363
|
-
it('should throw error for invalid operator', async () => {
|
|
1364
|
-
await expect(
|
|
1365
|
-
vectorDB.query({
|
|
1366
|
-
indexName,
|
|
1367
|
-
queryVector: [1, 0, 0],
|
|
1368
|
-
filter: { price: { $invalid: 100 } },
|
|
1369
|
-
}),
|
|
1370
|
-
).rejects.toThrow('Unsupported operator: $invalid');
|
|
1371
|
-
});
|
|
1372
|
-
|
|
1373
1363
|
it('should handle multiple logical operators at root level', async () => {
|
|
1374
1364
|
const results = await vectorDB.query({
|
|
1375
1365
|
indexName,
|
package/src/vector/index.ts
CHANGED
|
@@ -15,11 +15,11 @@ import type {
|
|
|
15
15
|
DeleteVectorParams,
|
|
16
16
|
UpdateVectorParams,
|
|
17
17
|
} from '@mastra/core/vector';
|
|
18
|
-
import type {
|
|
18
|
+
import type { LibSQLVectorFilter } from './filter';
|
|
19
19
|
import { LibSQLFilterTranslator } from './filter';
|
|
20
20
|
import { buildFilterQuery } from './sql-builder';
|
|
21
21
|
|
|
22
|
-
interface LibSQLQueryVectorParams extends QueryVectorParams {
|
|
22
|
+
interface LibSQLQueryVectorParams extends QueryVectorParams<LibSQLVectorFilter> {
|
|
23
23
|
minScore?: number;
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -41,7 +41,7 @@ export interface LibSQLVectorConfig {
|
|
|
41
41
|
initialBackoffMs?: number;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
export class LibSQLVector extends MastraVector {
|
|
44
|
+
export class LibSQLVector extends MastraVector<LibSQLVectorFilter> {
|
|
45
45
|
private turso: TursoClient;
|
|
46
46
|
private readonly maxRetries: number;
|
|
47
47
|
private readonly initialBackoffMs: number;
|
|
@@ -109,7 +109,7 @@ export class LibSQLVector extends MastraVector {
|
|
|
109
109
|
throw new Error('LibSQLVector: Max retries reached, but no error was re-thrown from the loop.');
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
transformFilter(filter?:
|
|
112
|
+
transformFilter(filter?: LibSQLVectorFilter) {
|
|
113
113
|
const translator = new LibSQLFilterTranslator();
|
|
114
114
|
return translator.translate(filter);
|
|
115
115
|
}
|
|
@@ -6,8 +6,8 @@ import type {
|
|
|
6
6
|
ArrayOperator,
|
|
7
7
|
ElementOperator,
|
|
8
8
|
LogicalOperator,
|
|
9
|
-
VectorFilter,
|
|
10
9
|
} from '@mastra/core/vector/filter';
|
|
10
|
+
import type { LibSQLVectorFilter } from './filter';
|
|
11
11
|
|
|
12
12
|
type OperatorType =
|
|
13
13
|
| BasicOperator
|
|
@@ -372,7 +372,7 @@ function escapeLikePattern(str: string): string {
|
|
|
372
372
|
return str.replace(/([%_\\])/g, '\\$1');
|
|
373
373
|
}
|
|
374
374
|
|
|
375
|
-
export function buildFilterQuery(filter:
|
|
375
|
+
export function buildFilterQuery(filter: LibSQLVectorFilter): FilterResult {
|
|
376
376
|
if (!filter) {
|
|
377
377
|
return { sql: '', values: [] };
|
|
378
378
|
}
|
|
@@ -428,11 +428,11 @@ function buildCondition(key: string, value: any, parentPath: string): FilterResu
|
|
|
428
428
|
|
|
429
429
|
function handleLogicalOperator(
|
|
430
430
|
key: '$and' | '$or' | '$not' | '$nor',
|
|
431
|
-
value:
|
|
431
|
+
value: LibSQLVectorFilter[] | LibSQLVectorFilter,
|
|
432
432
|
parentPath: string,
|
|
433
433
|
): FilterResult {
|
|
434
434
|
// Handle empty conditions
|
|
435
|
-
if (!value || value.length === 0) {
|
|
435
|
+
if (!value || (Array.isArray(value) && value.length === 0)) {
|
|
436
436
|
switch (key) {
|
|
437
437
|
case '$and':
|
|
438
438
|
case '$nor':
|
|
@@ -460,7 +460,7 @@ function handleLogicalOperator(
|
|
|
460
460
|
const joinOperator = key === '$or' || key === '$nor' ? 'OR' : 'AND';
|
|
461
461
|
const conditions = Array.isArray(value)
|
|
462
462
|
? value.map(f => {
|
|
463
|
-
const entries = Object.entries(f);
|
|
463
|
+
const entries = !!f ? Object.entries(f) : [];
|
|
464
464
|
return entries.map(([k, v]) => buildCondition(k, v, key));
|
|
465
465
|
})
|
|
466
466
|
: [buildCondition(key, value, parentPath)];
|