@mastra/cloudflare-d1 0.0.0-switch-to-core-20250424015131 → 0.0.0-vector-query-sources-20250516172905
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/dist/_tsup-dts-rollup.d.cts +44 -8
- package/dist/_tsup-dts-rollup.d.ts +44 -8
- package/dist/index.cjs +178 -33
- package/dist/index.d.cts +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +178 -33
- package/package.json +5 -5
|
@@ -2,15 +2,19 @@ import type { D1Database as D1Database_2 } from '@cloudflare/workers-types';
|
|
|
2
2
|
import type { EvalRow } from '@mastra/core/storage';
|
|
3
3
|
import { MastraStorage } from '@mastra/core/storage';
|
|
4
4
|
import type { MessageType } from '@mastra/core/memory';
|
|
5
|
+
import type { MessageType as MessageType_2 } from '@mastra/core';
|
|
5
6
|
import type { StorageColumn } from '@mastra/core/storage';
|
|
6
7
|
import type { StorageGetMessagesArg } from '@mastra/core/storage';
|
|
7
8
|
import type { StorageThreadType } from '@mastra/core/memory';
|
|
8
9
|
import type { TABLE_NAMES } from '@mastra/core/storage';
|
|
10
|
+
import type { WorkflowRun } from '@mastra/core/storage';
|
|
9
11
|
import type { WorkflowRuns } from '@mastra/core/storage';
|
|
10
12
|
import type { WorkflowRunState } from '@mastra/core/workflows';
|
|
11
13
|
import type { WorkflowRunState as WorkflowRunState_2 } from '@mastra/core';
|
|
12
14
|
|
|
13
|
-
export declare const
|
|
15
|
+
export declare const checkWorkflowSnapshot: (snapshot: WorkflowRunState_2 | string, stepId: string, status: string) => void;
|
|
16
|
+
|
|
17
|
+
export declare const createSampleMessage: (threadId: string) => MessageType_2;
|
|
14
18
|
|
|
15
19
|
export declare const createSampleThread: () => {
|
|
16
20
|
id: string;
|
|
@@ -51,7 +55,11 @@ export declare const createSampleTrace: (name: string, scope?: string, attribute
|
|
|
51
55
|
createdAt: string;
|
|
52
56
|
};
|
|
53
57
|
|
|
54
|
-
export declare const createSampleWorkflowSnapshot: (threadId: string) =>
|
|
58
|
+
export declare const createSampleWorkflowSnapshot: (threadId: string, status: string, createdAt?: Date) => {
|
|
59
|
+
snapshot: WorkflowRunState_2;
|
|
60
|
+
runId: string;
|
|
61
|
+
stepId: string;
|
|
62
|
+
};
|
|
55
63
|
|
|
56
64
|
export declare function createSqlBuilder(): SqlBuilder;
|
|
57
65
|
|
|
@@ -153,21 +161,30 @@ declare class D1Store extends MastraStorage {
|
|
|
153
161
|
tableName: TABLE_NAMES;
|
|
154
162
|
records: Record<string, any>[];
|
|
155
163
|
}): Promise<void>;
|
|
156
|
-
getTraces({ name, scope, page, perPage, attributes, }: {
|
|
164
|
+
getTraces({ name, scope, page, perPage, attributes, fromDate, toDate, }: {
|
|
157
165
|
name?: string;
|
|
158
166
|
scope?: string;
|
|
159
167
|
page: number;
|
|
160
168
|
perPage: number;
|
|
161
169
|
attributes?: Record<string, string>;
|
|
170
|
+
fromDate?: Date;
|
|
171
|
+
toDate?: Date;
|
|
162
172
|
}): Promise<Record<string, any>[]>;
|
|
163
173
|
getEvalsByAgentName(agentName: string, type?: 'test' | 'live'): Promise<EvalRow[]>;
|
|
164
|
-
|
|
174
|
+
private parseWorkflowRun;
|
|
175
|
+
private hasColumn;
|
|
176
|
+
getWorkflowRuns({ workflowName, fromDate, toDate, limit, offset, resourceId, }?: {
|
|
165
177
|
workflowName?: string;
|
|
166
178
|
fromDate?: Date;
|
|
167
179
|
toDate?: Date;
|
|
168
180
|
limit?: number;
|
|
169
181
|
offset?: number;
|
|
182
|
+
resourceId?: string;
|
|
170
183
|
}): Promise<WorkflowRuns>;
|
|
184
|
+
getWorkflowRunById({ runId, workflowName, }: {
|
|
185
|
+
runId: string;
|
|
186
|
+
workflowName?: string;
|
|
187
|
+
}): Promise<WorkflowRun | null>;
|
|
171
188
|
/**
|
|
172
189
|
* Close the database connection
|
|
173
190
|
* No explicit cleanup needed for D1 in either REST or Workers Binding mode
|
|
@@ -196,9 +213,30 @@ declare interface D1WorkersConfig {
|
|
|
196
213
|
export { D1WorkersConfig }
|
|
197
214
|
export { D1WorkersConfig as D1WorkersConfig_alias_1 }
|
|
198
215
|
|
|
216
|
+
/**
|
|
217
|
+
* Parses and returns a valid SQL SELECT column identifier.
|
|
218
|
+
* Allows a single identifier (letters, numbers, underscores), or '*', optionally with 'AS alias'.
|
|
219
|
+
*
|
|
220
|
+
* @param column - The column identifier string to parse.
|
|
221
|
+
* @returns The validated column identifier as a branded type.
|
|
222
|
+
* @throws {Error} If invalid.
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* const col = parseSelectIdentifier('user_id'); // Ok
|
|
226
|
+
* parseSelectIdentifier('user_id AS uid'); // Ok
|
|
227
|
+
* parseSelectIdentifier('*'); // Ok
|
|
228
|
+
* parseSelectIdentifier('user id'); // Throws error
|
|
229
|
+
*/
|
|
230
|
+
export declare function parseSelectIdentifier(column: string): SelectIdentifier;
|
|
231
|
+
|
|
199
232
|
export declare const retryUntil: <T>(fn: () => Promise<T>, condition: (result: T) => boolean, timeout?: number, // REST API needs longer timeout due to higher latency
|
|
200
233
|
interval?: number) => Promise<T>;
|
|
201
234
|
|
|
235
|
+
/** Represents a validated SQL SELECT column identifier (or '*', optionally with 'AS alias'). */
|
|
236
|
+
declare type SelectIdentifier = string & {
|
|
237
|
+
__brand: 'SelectIdentifier';
|
|
238
|
+
};
|
|
239
|
+
|
|
202
240
|
/**
|
|
203
241
|
* SQL Builder class for constructing type-safe SQL queries
|
|
204
242
|
* This helps create maintainable and secure SQL queries with proper parameter handling
|
|
@@ -226,6 +264,7 @@ export declare class SqlBuilder {
|
|
|
226
264
|
orderBy(column: string, direction?: 'ASC' | 'DESC'): SqlBuilder;
|
|
227
265
|
limit(count: number): SqlBuilder;
|
|
228
266
|
offset(count: number): SqlBuilder;
|
|
267
|
+
count(): SqlBuilder;
|
|
229
268
|
/**
|
|
230
269
|
* Insert a row, or update specific columns on conflict (upsert).
|
|
231
270
|
* @param table Table name
|
|
@@ -261,7 +300,6 @@ export declare class SqlBuilder {
|
|
|
261
300
|
* @returns The builder instance
|
|
262
301
|
*/
|
|
263
302
|
createIndex(indexName: string, tableName: string, columnName: string, indexType?: string): SqlBuilder;
|
|
264
|
-
raw(sql: string, ...params: SqlParam[]): SqlBuilder;
|
|
265
303
|
/**
|
|
266
304
|
* Add a LIKE condition to the query
|
|
267
305
|
* @param column The column to check
|
|
@@ -299,7 +337,7 @@ export declare type SqlParam = string | number | boolean | null | undefined;
|
|
|
299
337
|
/**
|
|
300
338
|
* Interface for SQL query options with generic type support
|
|
301
339
|
*/
|
|
302
|
-
declare interface SqlQueryOptions {
|
|
340
|
+
export declare interface SqlQueryOptions {
|
|
303
341
|
/** SQL query to execute */
|
|
304
342
|
sql: string;
|
|
305
343
|
/** Parameters to bind to the query */
|
|
@@ -307,7 +345,5 @@ declare interface SqlQueryOptions {
|
|
|
307
345
|
/** Whether to return only the first result */
|
|
308
346
|
first?: boolean;
|
|
309
347
|
}
|
|
310
|
-
export { SqlQueryOptions }
|
|
311
|
-
export { SqlQueryOptions as SqlQueryOptions_alias_1 }
|
|
312
348
|
|
|
313
349
|
export { }
|
|
@@ -2,15 +2,19 @@ import type { D1Database as D1Database_2 } from '@cloudflare/workers-types';
|
|
|
2
2
|
import type { EvalRow } from '@mastra/core/storage';
|
|
3
3
|
import { MastraStorage } from '@mastra/core/storage';
|
|
4
4
|
import type { MessageType } from '@mastra/core/memory';
|
|
5
|
+
import type { MessageType as MessageType_2 } from '@mastra/core';
|
|
5
6
|
import type { StorageColumn } from '@mastra/core/storage';
|
|
6
7
|
import type { StorageGetMessagesArg } from '@mastra/core/storage';
|
|
7
8
|
import type { StorageThreadType } from '@mastra/core/memory';
|
|
8
9
|
import type { TABLE_NAMES } from '@mastra/core/storage';
|
|
10
|
+
import type { WorkflowRun } from '@mastra/core/storage';
|
|
9
11
|
import type { WorkflowRuns } from '@mastra/core/storage';
|
|
10
12
|
import type { WorkflowRunState } from '@mastra/core/workflows';
|
|
11
13
|
import type { WorkflowRunState as WorkflowRunState_2 } from '@mastra/core';
|
|
12
14
|
|
|
13
|
-
export declare const
|
|
15
|
+
export declare const checkWorkflowSnapshot: (snapshot: WorkflowRunState_2 | string, stepId: string, status: string) => void;
|
|
16
|
+
|
|
17
|
+
export declare const createSampleMessage: (threadId: string) => MessageType_2;
|
|
14
18
|
|
|
15
19
|
export declare const createSampleThread: () => {
|
|
16
20
|
id: string;
|
|
@@ -51,7 +55,11 @@ export declare const createSampleTrace: (name: string, scope?: string, attribute
|
|
|
51
55
|
createdAt: string;
|
|
52
56
|
};
|
|
53
57
|
|
|
54
|
-
export declare const createSampleWorkflowSnapshot: (threadId: string) =>
|
|
58
|
+
export declare const createSampleWorkflowSnapshot: (threadId: string, status: string, createdAt?: Date) => {
|
|
59
|
+
snapshot: WorkflowRunState_2;
|
|
60
|
+
runId: string;
|
|
61
|
+
stepId: string;
|
|
62
|
+
};
|
|
55
63
|
|
|
56
64
|
export declare function createSqlBuilder(): SqlBuilder;
|
|
57
65
|
|
|
@@ -153,21 +161,30 @@ declare class D1Store extends MastraStorage {
|
|
|
153
161
|
tableName: TABLE_NAMES;
|
|
154
162
|
records: Record<string, any>[];
|
|
155
163
|
}): Promise<void>;
|
|
156
|
-
getTraces({ name, scope, page, perPage, attributes, }: {
|
|
164
|
+
getTraces({ name, scope, page, perPage, attributes, fromDate, toDate, }: {
|
|
157
165
|
name?: string;
|
|
158
166
|
scope?: string;
|
|
159
167
|
page: number;
|
|
160
168
|
perPage: number;
|
|
161
169
|
attributes?: Record<string, string>;
|
|
170
|
+
fromDate?: Date;
|
|
171
|
+
toDate?: Date;
|
|
162
172
|
}): Promise<Record<string, any>[]>;
|
|
163
173
|
getEvalsByAgentName(agentName: string, type?: 'test' | 'live'): Promise<EvalRow[]>;
|
|
164
|
-
|
|
174
|
+
private parseWorkflowRun;
|
|
175
|
+
private hasColumn;
|
|
176
|
+
getWorkflowRuns({ workflowName, fromDate, toDate, limit, offset, resourceId, }?: {
|
|
165
177
|
workflowName?: string;
|
|
166
178
|
fromDate?: Date;
|
|
167
179
|
toDate?: Date;
|
|
168
180
|
limit?: number;
|
|
169
181
|
offset?: number;
|
|
182
|
+
resourceId?: string;
|
|
170
183
|
}): Promise<WorkflowRuns>;
|
|
184
|
+
getWorkflowRunById({ runId, workflowName, }: {
|
|
185
|
+
runId: string;
|
|
186
|
+
workflowName?: string;
|
|
187
|
+
}): Promise<WorkflowRun | null>;
|
|
171
188
|
/**
|
|
172
189
|
* Close the database connection
|
|
173
190
|
* No explicit cleanup needed for D1 in either REST or Workers Binding mode
|
|
@@ -196,9 +213,30 @@ declare interface D1WorkersConfig {
|
|
|
196
213
|
export { D1WorkersConfig }
|
|
197
214
|
export { D1WorkersConfig as D1WorkersConfig_alias_1 }
|
|
198
215
|
|
|
216
|
+
/**
|
|
217
|
+
* Parses and returns a valid SQL SELECT column identifier.
|
|
218
|
+
* Allows a single identifier (letters, numbers, underscores), or '*', optionally with 'AS alias'.
|
|
219
|
+
*
|
|
220
|
+
* @param column - The column identifier string to parse.
|
|
221
|
+
* @returns The validated column identifier as a branded type.
|
|
222
|
+
* @throws {Error} If invalid.
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* const col = parseSelectIdentifier('user_id'); // Ok
|
|
226
|
+
* parseSelectIdentifier('user_id AS uid'); // Ok
|
|
227
|
+
* parseSelectIdentifier('*'); // Ok
|
|
228
|
+
* parseSelectIdentifier('user id'); // Throws error
|
|
229
|
+
*/
|
|
230
|
+
export declare function parseSelectIdentifier(column: string): SelectIdentifier;
|
|
231
|
+
|
|
199
232
|
export declare const retryUntil: <T>(fn: () => Promise<T>, condition: (result: T) => boolean, timeout?: number, // REST API needs longer timeout due to higher latency
|
|
200
233
|
interval?: number) => Promise<T>;
|
|
201
234
|
|
|
235
|
+
/** Represents a validated SQL SELECT column identifier (or '*', optionally with 'AS alias'). */
|
|
236
|
+
declare type SelectIdentifier = string & {
|
|
237
|
+
__brand: 'SelectIdentifier';
|
|
238
|
+
};
|
|
239
|
+
|
|
202
240
|
/**
|
|
203
241
|
* SQL Builder class for constructing type-safe SQL queries
|
|
204
242
|
* This helps create maintainable and secure SQL queries with proper parameter handling
|
|
@@ -226,6 +264,7 @@ export declare class SqlBuilder {
|
|
|
226
264
|
orderBy(column: string, direction?: 'ASC' | 'DESC'): SqlBuilder;
|
|
227
265
|
limit(count: number): SqlBuilder;
|
|
228
266
|
offset(count: number): SqlBuilder;
|
|
267
|
+
count(): SqlBuilder;
|
|
229
268
|
/**
|
|
230
269
|
* Insert a row, or update specific columns on conflict (upsert).
|
|
231
270
|
* @param table Table name
|
|
@@ -261,7 +300,6 @@ export declare class SqlBuilder {
|
|
|
261
300
|
* @returns The builder instance
|
|
262
301
|
*/
|
|
263
302
|
createIndex(indexName: string, tableName: string, columnName: string, indexType?: string): SqlBuilder;
|
|
264
|
-
raw(sql: string, ...params: SqlParam[]): SqlBuilder;
|
|
265
303
|
/**
|
|
266
304
|
* Add a LIKE condition to the query
|
|
267
305
|
* @param column The column to check
|
|
@@ -299,7 +337,7 @@ export declare type SqlParam = string | number | boolean | null | undefined;
|
|
|
299
337
|
/**
|
|
300
338
|
* Interface for SQL query options with generic type support
|
|
301
339
|
*/
|
|
302
|
-
declare interface SqlQueryOptions {
|
|
340
|
+
export declare interface SqlQueryOptions {
|
|
303
341
|
/** SQL query to execute */
|
|
304
342
|
sql: string;
|
|
305
343
|
/** Parameters to bind to the query */
|
|
@@ -307,7 +345,5 @@ declare interface SqlQueryOptions {
|
|
|
307
345
|
/** Whether to return only the first result */
|
|
308
346
|
first?: boolean;
|
|
309
347
|
}
|
|
310
|
-
export { SqlQueryOptions }
|
|
311
|
-
export { SqlQueryOptions as SqlQueryOptions_alias_1 }
|
|
312
348
|
|
|
313
349
|
export { }
|
package/dist/index.cjs
CHANGED
|
@@ -2,14 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
var storage = require('@mastra/core/storage');
|
|
4
4
|
var Cloudflare = require('cloudflare');
|
|
5
|
+
var utils = require('@mastra/core/utils');
|
|
5
6
|
|
|
6
7
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
8
|
|
|
8
9
|
var Cloudflare__default = /*#__PURE__*/_interopDefault(Cloudflare);
|
|
9
10
|
|
|
10
11
|
// src/storage/index.ts
|
|
11
|
-
|
|
12
|
-
// src/storage/sql-builder.ts
|
|
13
12
|
var SqlBuilder = class {
|
|
14
13
|
sql = "";
|
|
15
14
|
params = [];
|
|
@@ -19,12 +18,15 @@ var SqlBuilder = class {
|
|
|
19
18
|
if (!columns || Array.isArray(columns) && columns.length === 0) {
|
|
20
19
|
this.sql = "SELECT *";
|
|
21
20
|
} else {
|
|
22
|
-
|
|
21
|
+
const cols = Array.isArray(columns) ? columns : [columns];
|
|
22
|
+
const parsedCols = cols.map((col) => parseSelectIdentifier(col));
|
|
23
|
+
this.sql = `SELECT ${parsedCols.join(", ")}`;
|
|
23
24
|
}
|
|
24
25
|
return this;
|
|
25
26
|
}
|
|
26
27
|
from(table) {
|
|
27
|
-
|
|
28
|
+
const parsedTableName = utils.parseSqlIdentifier(table, "table name");
|
|
29
|
+
this.sql += ` FROM ${parsedTableName}`;
|
|
28
30
|
return this;
|
|
29
31
|
}
|
|
30
32
|
/**
|
|
@@ -61,7 +63,11 @@ var SqlBuilder = class {
|
|
|
61
63
|
return this;
|
|
62
64
|
}
|
|
63
65
|
orderBy(column, direction = "ASC") {
|
|
64
|
-
|
|
66
|
+
const parsedColumn = utils.parseSqlIdentifier(column, "column name");
|
|
67
|
+
if (!["ASC", "DESC"].includes(direction)) {
|
|
68
|
+
throw new Error(`Invalid sort direction: ${direction}`);
|
|
69
|
+
}
|
|
70
|
+
this.sql += ` ORDER BY ${parsedColumn} ${direction}`;
|
|
65
71
|
return this;
|
|
66
72
|
}
|
|
67
73
|
limit(count) {
|
|
@@ -74,6 +80,10 @@ var SqlBuilder = class {
|
|
|
74
80
|
this.params.push(count);
|
|
75
81
|
return this;
|
|
76
82
|
}
|
|
83
|
+
count() {
|
|
84
|
+
this.sql += "SELECT COUNT(*) AS count";
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
77
87
|
/**
|
|
78
88
|
* Insert a row, or update specific columns on conflict (upsert).
|
|
79
89
|
* @param table Table name
|
|
@@ -83,27 +93,33 @@ var SqlBuilder = class {
|
|
|
83
93
|
* @param updateMap Object mapping columns to update to their new value (e.g. { name: 'excluded.name' })
|
|
84
94
|
*/
|
|
85
95
|
insert(table, columns, values, conflictColumns, updateMap) {
|
|
86
|
-
const
|
|
96
|
+
const parsedTableName = utils.parseSqlIdentifier(table, "table name");
|
|
97
|
+
const parsedColumns = columns.map((col) => utils.parseSqlIdentifier(col, "column name"));
|
|
98
|
+
const placeholders = parsedColumns.map(() => "?").join(", ");
|
|
87
99
|
if (conflictColumns && updateMap) {
|
|
100
|
+
const parsedConflictColumns = conflictColumns.map((col) => utils.parseSqlIdentifier(col, "column name"));
|
|
88
101
|
const updateClause = Object.entries(updateMap).map(([col, expr]) => `${col} = ${expr}`).join(", ");
|
|
89
|
-
this.sql = `INSERT INTO ${
|
|
102
|
+
this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders}) ON CONFLICT(${parsedConflictColumns.join(", ")}) DO UPDATE SET ${updateClause}`;
|
|
90
103
|
this.params.push(...values);
|
|
91
104
|
return this;
|
|
92
105
|
}
|
|
93
|
-
this.sql = `INSERT INTO ${
|
|
106
|
+
this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders})`;
|
|
94
107
|
this.params.push(...values);
|
|
95
108
|
return this;
|
|
96
109
|
}
|
|
97
110
|
// Update operations
|
|
98
111
|
update(table, columns, values) {
|
|
99
|
-
const
|
|
100
|
-
|
|
112
|
+
const parsedTableName = utils.parseSqlIdentifier(table, "table name");
|
|
113
|
+
const parsedColumns = columns.map((col) => utils.parseSqlIdentifier(col, "column name"));
|
|
114
|
+
const setClause = parsedColumns.map((col) => `${col} = ?`).join(", ");
|
|
115
|
+
this.sql = `UPDATE ${parsedTableName} SET ${setClause}`;
|
|
101
116
|
this.params.push(...values);
|
|
102
117
|
return this;
|
|
103
118
|
}
|
|
104
119
|
// Delete operations
|
|
105
120
|
delete(table) {
|
|
106
|
-
|
|
121
|
+
const parsedTableName = utils.parseSqlIdentifier(table, "table name");
|
|
122
|
+
this.sql = `DELETE FROM ${parsedTableName}`;
|
|
107
123
|
return this;
|
|
108
124
|
}
|
|
109
125
|
/**
|
|
@@ -114,9 +130,16 @@ var SqlBuilder = class {
|
|
|
114
130
|
* @returns The builder instance
|
|
115
131
|
*/
|
|
116
132
|
createTable(table, columnDefinitions, tableConstraints) {
|
|
117
|
-
const
|
|
133
|
+
const parsedTableName = utils.parseSqlIdentifier(table, "table name");
|
|
134
|
+
const parsedColumnDefinitions = columnDefinitions.map((def) => {
|
|
135
|
+
const colName = def.split(/\s+/)[0];
|
|
136
|
+
if (!colName) throw new Error("Empty column name in definition");
|
|
137
|
+
utils.parseSqlIdentifier(colName, "column name");
|
|
138
|
+
return def;
|
|
139
|
+
});
|
|
140
|
+
const columns = parsedColumnDefinitions.join(", ");
|
|
118
141
|
const constraints = tableConstraints && tableConstraints.length > 0 ? ", " + tableConstraints.join(", ") : "";
|
|
119
|
-
this.sql = `CREATE TABLE IF NOT EXISTS ${
|
|
142
|
+
this.sql = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (${columns}${constraints})`;
|
|
120
143
|
return this;
|
|
121
144
|
}
|
|
122
145
|
/**
|
|
@@ -139,13 +162,10 @@ var SqlBuilder = class {
|
|
|
139
162
|
* @returns The builder instance
|
|
140
163
|
*/
|
|
141
164
|
createIndex(indexName, tableName, columnName, indexType = "") {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
raw(sql, ...params) {
|
|
147
|
-
this.sql = sql;
|
|
148
|
-
this.params.push(...params);
|
|
165
|
+
const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
|
|
166
|
+
const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
|
|
167
|
+
const parsedColumnName = utils.parseSqlIdentifier(columnName, "column name");
|
|
168
|
+
this.sql = `CREATE ${indexType ? indexType + " " : ""}INDEX IF NOT EXISTS ${parsedIndexName} ON ${parsedTableName}(${parsedColumnName})`;
|
|
149
169
|
return this;
|
|
150
170
|
}
|
|
151
171
|
/**
|
|
@@ -155,11 +175,12 @@ var SqlBuilder = class {
|
|
|
155
175
|
* @param exact If true, will not add % wildcards
|
|
156
176
|
*/
|
|
157
177
|
like(column, value, exact = false) {
|
|
178
|
+
const parsedColumnName = utils.parseSqlIdentifier(column, "column name");
|
|
158
179
|
const likeValue = exact ? value : `%${value}%`;
|
|
159
180
|
if (this.whereAdded) {
|
|
160
|
-
this.sql += ` AND ${
|
|
181
|
+
this.sql += ` AND ${parsedColumnName} LIKE ?`;
|
|
161
182
|
} else {
|
|
162
|
-
this.sql += ` WHERE ${
|
|
183
|
+
this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
|
|
163
184
|
this.whereAdded = true;
|
|
164
185
|
}
|
|
165
186
|
this.params.push(likeValue);
|
|
@@ -172,11 +193,13 @@ var SqlBuilder = class {
|
|
|
172
193
|
* @param value The value to match
|
|
173
194
|
*/
|
|
174
195
|
jsonLike(column, key, value) {
|
|
175
|
-
const
|
|
196
|
+
const parsedColumnName = utils.parseSqlIdentifier(column, "column name");
|
|
197
|
+
const parsedKey = utils.parseSqlIdentifier(key, "key name");
|
|
198
|
+
const jsonPattern = `%"${parsedKey}":"${value}"%`;
|
|
176
199
|
if (this.whereAdded) {
|
|
177
|
-
this.sql += ` AND ${
|
|
200
|
+
this.sql += ` AND ${parsedColumnName} LIKE ?`;
|
|
178
201
|
} else {
|
|
179
|
-
this.sql += ` WHERE ${
|
|
202
|
+
this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
|
|
180
203
|
this.whereAdded = true;
|
|
181
204
|
}
|
|
182
205
|
this.params.push(jsonPattern);
|
|
@@ -206,6 +229,15 @@ var SqlBuilder = class {
|
|
|
206
229
|
function createSqlBuilder() {
|
|
207
230
|
return new SqlBuilder();
|
|
208
231
|
}
|
|
232
|
+
var SQL_IDENTIFIER_PATTERN = /^[a-zA-Z0-9_]+(\s+AS\s+[a-zA-Z0-9_]+)?$/;
|
|
233
|
+
function parseSelectIdentifier(column) {
|
|
234
|
+
if (column !== "*" && !SQL_IDENTIFIER_PATTERN.test(column)) {
|
|
235
|
+
throw new Error(
|
|
236
|
+
`Invalid column name: "${column}". Must be "*" or a valid identifier (letters, numbers, underscores), optionally with "AS alias".`
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
return column;
|
|
240
|
+
}
|
|
209
241
|
|
|
210
242
|
// src/storage/index.ts
|
|
211
243
|
function isArrayOfRecords(value) {
|
|
@@ -224,6 +256,9 @@ var D1Store = class extends storage.MastraStorage {
|
|
|
224
256
|
*/
|
|
225
257
|
constructor(config) {
|
|
226
258
|
super({ name: "D1" });
|
|
259
|
+
if (config.tablePrefix && !/^[a-zA-Z0-9_]*$/.test(config.tablePrefix)) {
|
|
260
|
+
throw new Error("Invalid tablePrefix: only letters, numbers, and underscores are allowed.");
|
|
261
|
+
}
|
|
227
262
|
this.tablePrefix = config.tablePrefix || "";
|
|
228
263
|
if ("binding" in config) {
|
|
229
264
|
if (!config.binding) {
|
|
@@ -496,7 +531,8 @@ var D1Store = class extends storage.MastraStorage {
|
|
|
496
531
|
try {
|
|
497
532
|
await this.executeQuery({ sql, params });
|
|
498
533
|
} catch (error) {
|
|
499
|
-
|
|
534
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
535
|
+
this.logger.error(`Error inserting into ${fullTableName}:`, { message });
|
|
500
536
|
throw new Error(`Failed to insert into ${fullTableName}: ${error}`);
|
|
501
537
|
}
|
|
502
538
|
}
|
|
@@ -594,7 +630,8 @@ var D1Store = class extends storage.MastraStorage {
|
|
|
594
630
|
await this.executeQuery({ sql, params });
|
|
595
631
|
return thread;
|
|
596
632
|
} catch (error) {
|
|
597
|
-
|
|
633
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
634
|
+
this.logger.error(`Error saving thread to ${fullTableName}:`, { message });
|
|
598
635
|
throw error;
|
|
599
636
|
}
|
|
600
637
|
}
|
|
@@ -628,7 +665,8 @@ var D1Store = class extends storage.MastraStorage {
|
|
|
628
665
|
updatedAt: /* @__PURE__ */ new Date()
|
|
629
666
|
};
|
|
630
667
|
} catch (error) {
|
|
631
|
-
|
|
668
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
669
|
+
this.logger.error("Error updating thread:", { message });
|
|
632
670
|
throw error;
|
|
633
671
|
}
|
|
634
672
|
}
|
|
@@ -710,7 +748,7 @@ var D1Store = class extends storage.MastraStorage {
|
|
|
710
748
|
m.role,
|
|
711
749
|
m.type,
|
|
712
750
|
m.createdAt,
|
|
713
|
-
m.thread_id AS
|
|
751
|
+
m.thread_id AS threadId
|
|
714
752
|
FROM ordered_messages m
|
|
715
753
|
WHERE m.id IN (${includeIds.map(() => "?").join(",")})
|
|
716
754
|
OR EXISTS (
|
|
@@ -737,7 +775,7 @@ var D1Store = class extends storage.MastraStorage {
|
|
|
737
775
|
if (Array.isArray(includeResult)) messages.push(...includeResult);
|
|
738
776
|
}
|
|
739
777
|
const excludeIds = messages.map((m) => m.id);
|
|
740
|
-
let query = createSqlBuilder().select(["id", "content", "role", "type",
|
|
778
|
+
let query = createSqlBuilder().select(["id", "content", "role", "type", "createdAt", "thread_id AS threadId"]).from(fullTableName).where("thread_id = ?", threadId).andWhere(`id NOT IN (${excludeIds.map(() => "?").join(",")})`, ...excludeIds).orderBy("createdAt", "DESC").limit(limit);
|
|
741
779
|
const { sql, params } = query.build();
|
|
742
780
|
const result = await this.executeQuery({ sql, params });
|
|
743
781
|
if (Array.isArray(result)) messages.push(...result);
|
|
@@ -862,7 +900,9 @@ var D1Store = class extends storage.MastraStorage {
|
|
|
862
900
|
scope,
|
|
863
901
|
page,
|
|
864
902
|
perPage,
|
|
865
|
-
attributes
|
|
903
|
+
attributes,
|
|
904
|
+
fromDate,
|
|
905
|
+
toDate
|
|
866
906
|
}) {
|
|
867
907
|
const fullTableName = this.getTableName(storage.TABLE_TRACES);
|
|
868
908
|
try {
|
|
@@ -878,6 +918,12 @@ var D1Store = class extends storage.MastraStorage {
|
|
|
878
918
|
query.jsonLike("attributes", key, value);
|
|
879
919
|
}
|
|
880
920
|
}
|
|
921
|
+
if (fromDate) {
|
|
922
|
+
query.andWhere("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
|
|
923
|
+
}
|
|
924
|
+
if (toDate) {
|
|
925
|
+
query.andWhere("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
|
|
926
|
+
}
|
|
881
927
|
query.orderBy("startTime", "DESC").limit(perPage).offset((page - 1) * perPage);
|
|
882
928
|
const { sql, params } = query.build();
|
|
883
929
|
const results = await this.executeQuery({ sql, params });
|
|
@@ -929,8 +975,107 @@ var D1Store = class extends storage.MastraStorage {
|
|
|
929
975
|
return [];
|
|
930
976
|
}
|
|
931
977
|
}
|
|
932
|
-
|
|
933
|
-
|
|
978
|
+
parseWorkflowRun(row) {
|
|
979
|
+
let parsedSnapshot = row.snapshot;
|
|
980
|
+
if (typeof parsedSnapshot === "string") {
|
|
981
|
+
try {
|
|
982
|
+
parsedSnapshot = JSON.parse(row.snapshot);
|
|
983
|
+
} catch (e) {
|
|
984
|
+
console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
return {
|
|
988
|
+
workflowName: row.workflow_name,
|
|
989
|
+
runId: row.run_id,
|
|
990
|
+
snapshot: parsedSnapshot,
|
|
991
|
+
createdAt: this.ensureDate(row.createdAt),
|
|
992
|
+
updatedAt: this.ensureDate(row.updatedAt),
|
|
993
|
+
resourceId: row.resourceId
|
|
994
|
+
};
|
|
995
|
+
}
|
|
996
|
+
async hasColumn(table, column) {
|
|
997
|
+
const sql = `PRAGMA table_info(${table});`;
|
|
998
|
+
const result = await this.executeQuery({ sql, params: [] });
|
|
999
|
+
if (!result || !Array.isArray(result)) return false;
|
|
1000
|
+
return result.some((col) => col.name === column || col.name === column.toLowerCase());
|
|
1001
|
+
}
|
|
1002
|
+
async getWorkflowRuns({
|
|
1003
|
+
workflowName,
|
|
1004
|
+
fromDate,
|
|
1005
|
+
toDate,
|
|
1006
|
+
limit,
|
|
1007
|
+
offset,
|
|
1008
|
+
resourceId
|
|
1009
|
+
} = {}) {
|
|
1010
|
+
const fullTableName = this.getTableName(storage.TABLE_WORKFLOW_SNAPSHOT);
|
|
1011
|
+
try {
|
|
1012
|
+
const builder = createSqlBuilder().select().from(fullTableName);
|
|
1013
|
+
const countBuilder = createSqlBuilder().count().from(fullTableName);
|
|
1014
|
+
if (workflowName) builder.whereAnd("workflow_name = ?", workflowName);
|
|
1015
|
+
if (resourceId) {
|
|
1016
|
+
const hasResourceId = await this.hasColumn(fullTableName, "resourceId");
|
|
1017
|
+
if (hasResourceId) {
|
|
1018
|
+
builder.whereAnd("resourceId = ?", resourceId);
|
|
1019
|
+
countBuilder.whereAnd("resourceId = ?", resourceId);
|
|
1020
|
+
} else {
|
|
1021
|
+
console.warn(`[${fullTableName}] resourceId column not found. Skipping resourceId filter.`);
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
if (fromDate) {
|
|
1025
|
+
builder.whereAnd("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
|
|
1026
|
+
countBuilder.whereAnd("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
|
|
1027
|
+
}
|
|
1028
|
+
if (toDate) {
|
|
1029
|
+
builder.whereAnd("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
|
|
1030
|
+
countBuilder.whereAnd("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
|
|
1031
|
+
}
|
|
1032
|
+
builder.orderBy("createdAt", "DESC");
|
|
1033
|
+
if (typeof limit === "number") builder.limit(limit);
|
|
1034
|
+
if (typeof offset === "number") builder.offset(offset);
|
|
1035
|
+
const { sql, params } = builder.build();
|
|
1036
|
+
let total = 0;
|
|
1037
|
+
if (limit !== void 0 && offset !== void 0) {
|
|
1038
|
+
const { sql: countSql, params: countParams } = countBuilder.build();
|
|
1039
|
+
const countResult = await this.executeQuery({ sql: countSql, params: countParams, first: true });
|
|
1040
|
+
total = Number(countResult?.count ?? 0);
|
|
1041
|
+
}
|
|
1042
|
+
const results = await this.executeQuery({ sql, params });
|
|
1043
|
+
const runs = (isArrayOfRecords(results) ? results : []).map((row) => this.parseWorkflowRun(row));
|
|
1044
|
+
return { runs, total: total || runs.length };
|
|
1045
|
+
} catch (error) {
|
|
1046
|
+
this.logger.error("Error getting workflow runs:", {
|
|
1047
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1048
|
+
});
|
|
1049
|
+
throw error;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
async getWorkflowRunById({
|
|
1053
|
+
runId,
|
|
1054
|
+
workflowName
|
|
1055
|
+
}) {
|
|
1056
|
+
const fullTableName = this.getTableName(storage.TABLE_WORKFLOW_SNAPSHOT);
|
|
1057
|
+
try {
|
|
1058
|
+
const conditions = [];
|
|
1059
|
+
const params = [];
|
|
1060
|
+
if (runId) {
|
|
1061
|
+
conditions.push("run_id = ?");
|
|
1062
|
+
params.push(runId);
|
|
1063
|
+
}
|
|
1064
|
+
if (workflowName) {
|
|
1065
|
+
conditions.push("workflow_name = ?");
|
|
1066
|
+
params.push(workflowName);
|
|
1067
|
+
}
|
|
1068
|
+
const whereClause = conditions.length > 0 ? "WHERE " + conditions.join(" AND ") : "";
|
|
1069
|
+
const sql = `SELECT * FROM ${fullTableName} ${whereClause} ORDER BY createdAt DESC LIMIT 1`;
|
|
1070
|
+
const result = await this.executeQuery({ sql, params, first: true });
|
|
1071
|
+
if (!result) return null;
|
|
1072
|
+
return this.parseWorkflowRun(result);
|
|
1073
|
+
} catch (error) {
|
|
1074
|
+
this.logger.error("Error getting workflow run by ID:", {
|
|
1075
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1076
|
+
});
|
|
1077
|
+
throw error;
|
|
1078
|
+
}
|
|
934
1079
|
}
|
|
935
1080
|
/**
|
|
936
1081
|
* Close the database connection
|
package/dist/index.d.cts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { MastraStorage, TABLE_THREADS, TABLE_MESSAGES, TABLE_WORKFLOW_SNAPSHOT, TABLE_TRACES, TABLE_EVALS } from '@mastra/core/storage';
|
|
2
2
|
import Cloudflare from 'cloudflare';
|
|
3
|
+
import { parseSqlIdentifier } from '@mastra/core/utils';
|
|
3
4
|
|
|
4
5
|
// src/storage/index.ts
|
|
5
|
-
|
|
6
|
-
// src/storage/sql-builder.ts
|
|
7
6
|
var SqlBuilder = class {
|
|
8
7
|
sql = "";
|
|
9
8
|
params = [];
|
|
@@ -13,12 +12,15 @@ var SqlBuilder = class {
|
|
|
13
12
|
if (!columns || Array.isArray(columns) && columns.length === 0) {
|
|
14
13
|
this.sql = "SELECT *";
|
|
15
14
|
} else {
|
|
16
|
-
|
|
15
|
+
const cols = Array.isArray(columns) ? columns : [columns];
|
|
16
|
+
const parsedCols = cols.map((col) => parseSelectIdentifier(col));
|
|
17
|
+
this.sql = `SELECT ${parsedCols.join(", ")}`;
|
|
17
18
|
}
|
|
18
19
|
return this;
|
|
19
20
|
}
|
|
20
21
|
from(table) {
|
|
21
|
-
|
|
22
|
+
const parsedTableName = parseSqlIdentifier(table, "table name");
|
|
23
|
+
this.sql += ` FROM ${parsedTableName}`;
|
|
22
24
|
return this;
|
|
23
25
|
}
|
|
24
26
|
/**
|
|
@@ -55,7 +57,11 @@ var SqlBuilder = class {
|
|
|
55
57
|
return this;
|
|
56
58
|
}
|
|
57
59
|
orderBy(column, direction = "ASC") {
|
|
58
|
-
|
|
60
|
+
const parsedColumn = parseSqlIdentifier(column, "column name");
|
|
61
|
+
if (!["ASC", "DESC"].includes(direction)) {
|
|
62
|
+
throw new Error(`Invalid sort direction: ${direction}`);
|
|
63
|
+
}
|
|
64
|
+
this.sql += ` ORDER BY ${parsedColumn} ${direction}`;
|
|
59
65
|
return this;
|
|
60
66
|
}
|
|
61
67
|
limit(count) {
|
|
@@ -68,6 +74,10 @@ var SqlBuilder = class {
|
|
|
68
74
|
this.params.push(count);
|
|
69
75
|
return this;
|
|
70
76
|
}
|
|
77
|
+
count() {
|
|
78
|
+
this.sql += "SELECT COUNT(*) AS count";
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
71
81
|
/**
|
|
72
82
|
* Insert a row, or update specific columns on conflict (upsert).
|
|
73
83
|
* @param table Table name
|
|
@@ -77,27 +87,33 @@ var SqlBuilder = class {
|
|
|
77
87
|
* @param updateMap Object mapping columns to update to their new value (e.g. { name: 'excluded.name' })
|
|
78
88
|
*/
|
|
79
89
|
insert(table, columns, values, conflictColumns, updateMap) {
|
|
80
|
-
const
|
|
90
|
+
const parsedTableName = parseSqlIdentifier(table, "table name");
|
|
91
|
+
const parsedColumns = columns.map((col) => parseSqlIdentifier(col, "column name"));
|
|
92
|
+
const placeholders = parsedColumns.map(() => "?").join(", ");
|
|
81
93
|
if (conflictColumns && updateMap) {
|
|
94
|
+
const parsedConflictColumns = conflictColumns.map((col) => parseSqlIdentifier(col, "column name"));
|
|
82
95
|
const updateClause = Object.entries(updateMap).map(([col, expr]) => `${col} = ${expr}`).join(", ");
|
|
83
|
-
this.sql = `INSERT INTO ${
|
|
96
|
+
this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders}) ON CONFLICT(${parsedConflictColumns.join(", ")}) DO UPDATE SET ${updateClause}`;
|
|
84
97
|
this.params.push(...values);
|
|
85
98
|
return this;
|
|
86
99
|
}
|
|
87
|
-
this.sql = `INSERT INTO ${
|
|
100
|
+
this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders})`;
|
|
88
101
|
this.params.push(...values);
|
|
89
102
|
return this;
|
|
90
103
|
}
|
|
91
104
|
// Update operations
|
|
92
105
|
update(table, columns, values) {
|
|
93
|
-
const
|
|
94
|
-
|
|
106
|
+
const parsedTableName = parseSqlIdentifier(table, "table name");
|
|
107
|
+
const parsedColumns = columns.map((col) => parseSqlIdentifier(col, "column name"));
|
|
108
|
+
const setClause = parsedColumns.map((col) => `${col} = ?`).join(", ");
|
|
109
|
+
this.sql = `UPDATE ${parsedTableName} SET ${setClause}`;
|
|
95
110
|
this.params.push(...values);
|
|
96
111
|
return this;
|
|
97
112
|
}
|
|
98
113
|
// Delete operations
|
|
99
114
|
delete(table) {
|
|
100
|
-
|
|
115
|
+
const parsedTableName = parseSqlIdentifier(table, "table name");
|
|
116
|
+
this.sql = `DELETE FROM ${parsedTableName}`;
|
|
101
117
|
return this;
|
|
102
118
|
}
|
|
103
119
|
/**
|
|
@@ -108,9 +124,16 @@ var SqlBuilder = class {
|
|
|
108
124
|
* @returns The builder instance
|
|
109
125
|
*/
|
|
110
126
|
createTable(table, columnDefinitions, tableConstraints) {
|
|
111
|
-
const
|
|
127
|
+
const parsedTableName = parseSqlIdentifier(table, "table name");
|
|
128
|
+
const parsedColumnDefinitions = columnDefinitions.map((def) => {
|
|
129
|
+
const colName = def.split(/\s+/)[0];
|
|
130
|
+
if (!colName) throw new Error("Empty column name in definition");
|
|
131
|
+
parseSqlIdentifier(colName, "column name");
|
|
132
|
+
return def;
|
|
133
|
+
});
|
|
134
|
+
const columns = parsedColumnDefinitions.join(", ");
|
|
112
135
|
const constraints = tableConstraints && tableConstraints.length > 0 ? ", " + tableConstraints.join(", ") : "";
|
|
113
|
-
this.sql = `CREATE TABLE IF NOT EXISTS ${
|
|
136
|
+
this.sql = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (${columns}${constraints})`;
|
|
114
137
|
return this;
|
|
115
138
|
}
|
|
116
139
|
/**
|
|
@@ -133,13 +156,10 @@ var SqlBuilder = class {
|
|
|
133
156
|
* @returns The builder instance
|
|
134
157
|
*/
|
|
135
158
|
createIndex(indexName, tableName, columnName, indexType = "") {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
raw(sql, ...params) {
|
|
141
|
-
this.sql = sql;
|
|
142
|
-
this.params.push(...params);
|
|
159
|
+
const parsedIndexName = parseSqlIdentifier(indexName, "index name");
|
|
160
|
+
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
161
|
+
const parsedColumnName = parseSqlIdentifier(columnName, "column name");
|
|
162
|
+
this.sql = `CREATE ${indexType ? indexType + " " : ""}INDEX IF NOT EXISTS ${parsedIndexName} ON ${parsedTableName}(${parsedColumnName})`;
|
|
143
163
|
return this;
|
|
144
164
|
}
|
|
145
165
|
/**
|
|
@@ -149,11 +169,12 @@ var SqlBuilder = class {
|
|
|
149
169
|
* @param exact If true, will not add % wildcards
|
|
150
170
|
*/
|
|
151
171
|
like(column, value, exact = false) {
|
|
172
|
+
const parsedColumnName = parseSqlIdentifier(column, "column name");
|
|
152
173
|
const likeValue = exact ? value : `%${value}%`;
|
|
153
174
|
if (this.whereAdded) {
|
|
154
|
-
this.sql += ` AND ${
|
|
175
|
+
this.sql += ` AND ${parsedColumnName} LIKE ?`;
|
|
155
176
|
} else {
|
|
156
|
-
this.sql += ` WHERE ${
|
|
177
|
+
this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
|
|
157
178
|
this.whereAdded = true;
|
|
158
179
|
}
|
|
159
180
|
this.params.push(likeValue);
|
|
@@ -166,11 +187,13 @@ var SqlBuilder = class {
|
|
|
166
187
|
* @param value The value to match
|
|
167
188
|
*/
|
|
168
189
|
jsonLike(column, key, value) {
|
|
169
|
-
const
|
|
190
|
+
const parsedColumnName = parseSqlIdentifier(column, "column name");
|
|
191
|
+
const parsedKey = parseSqlIdentifier(key, "key name");
|
|
192
|
+
const jsonPattern = `%"${parsedKey}":"${value}"%`;
|
|
170
193
|
if (this.whereAdded) {
|
|
171
|
-
this.sql += ` AND ${
|
|
194
|
+
this.sql += ` AND ${parsedColumnName} LIKE ?`;
|
|
172
195
|
} else {
|
|
173
|
-
this.sql += ` WHERE ${
|
|
196
|
+
this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
|
|
174
197
|
this.whereAdded = true;
|
|
175
198
|
}
|
|
176
199
|
this.params.push(jsonPattern);
|
|
@@ -200,6 +223,15 @@ var SqlBuilder = class {
|
|
|
200
223
|
function createSqlBuilder() {
|
|
201
224
|
return new SqlBuilder();
|
|
202
225
|
}
|
|
226
|
+
var SQL_IDENTIFIER_PATTERN = /^[a-zA-Z0-9_]+(\s+AS\s+[a-zA-Z0-9_]+)?$/;
|
|
227
|
+
function parseSelectIdentifier(column) {
|
|
228
|
+
if (column !== "*" && !SQL_IDENTIFIER_PATTERN.test(column)) {
|
|
229
|
+
throw new Error(
|
|
230
|
+
`Invalid column name: "${column}". Must be "*" or a valid identifier (letters, numbers, underscores), optionally with "AS alias".`
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
return column;
|
|
234
|
+
}
|
|
203
235
|
|
|
204
236
|
// src/storage/index.ts
|
|
205
237
|
function isArrayOfRecords(value) {
|
|
@@ -218,6 +250,9 @@ var D1Store = class extends MastraStorage {
|
|
|
218
250
|
*/
|
|
219
251
|
constructor(config) {
|
|
220
252
|
super({ name: "D1" });
|
|
253
|
+
if (config.tablePrefix && !/^[a-zA-Z0-9_]*$/.test(config.tablePrefix)) {
|
|
254
|
+
throw new Error("Invalid tablePrefix: only letters, numbers, and underscores are allowed.");
|
|
255
|
+
}
|
|
221
256
|
this.tablePrefix = config.tablePrefix || "";
|
|
222
257
|
if ("binding" in config) {
|
|
223
258
|
if (!config.binding) {
|
|
@@ -490,7 +525,8 @@ var D1Store = class extends MastraStorage {
|
|
|
490
525
|
try {
|
|
491
526
|
await this.executeQuery({ sql, params });
|
|
492
527
|
} catch (error) {
|
|
493
|
-
|
|
528
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
529
|
+
this.logger.error(`Error inserting into ${fullTableName}:`, { message });
|
|
494
530
|
throw new Error(`Failed to insert into ${fullTableName}: ${error}`);
|
|
495
531
|
}
|
|
496
532
|
}
|
|
@@ -588,7 +624,8 @@ var D1Store = class extends MastraStorage {
|
|
|
588
624
|
await this.executeQuery({ sql, params });
|
|
589
625
|
return thread;
|
|
590
626
|
} catch (error) {
|
|
591
|
-
|
|
627
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
628
|
+
this.logger.error(`Error saving thread to ${fullTableName}:`, { message });
|
|
592
629
|
throw error;
|
|
593
630
|
}
|
|
594
631
|
}
|
|
@@ -622,7 +659,8 @@ var D1Store = class extends MastraStorage {
|
|
|
622
659
|
updatedAt: /* @__PURE__ */ new Date()
|
|
623
660
|
};
|
|
624
661
|
} catch (error) {
|
|
625
|
-
|
|
662
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
663
|
+
this.logger.error("Error updating thread:", { message });
|
|
626
664
|
throw error;
|
|
627
665
|
}
|
|
628
666
|
}
|
|
@@ -704,7 +742,7 @@ var D1Store = class extends MastraStorage {
|
|
|
704
742
|
m.role,
|
|
705
743
|
m.type,
|
|
706
744
|
m.createdAt,
|
|
707
|
-
m.thread_id AS
|
|
745
|
+
m.thread_id AS threadId
|
|
708
746
|
FROM ordered_messages m
|
|
709
747
|
WHERE m.id IN (${includeIds.map(() => "?").join(",")})
|
|
710
748
|
OR EXISTS (
|
|
@@ -731,7 +769,7 @@ var D1Store = class extends MastraStorage {
|
|
|
731
769
|
if (Array.isArray(includeResult)) messages.push(...includeResult);
|
|
732
770
|
}
|
|
733
771
|
const excludeIds = messages.map((m) => m.id);
|
|
734
|
-
let query = createSqlBuilder().select(["id", "content", "role", "type",
|
|
772
|
+
let query = createSqlBuilder().select(["id", "content", "role", "type", "createdAt", "thread_id AS threadId"]).from(fullTableName).where("thread_id = ?", threadId).andWhere(`id NOT IN (${excludeIds.map(() => "?").join(",")})`, ...excludeIds).orderBy("createdAt", "DESC").limit(limit);
|
|
735
773
|
const { sql, params } = query.build();
|
|
736
774
|
const result = await this.executeQuery({ sql, params });
|
|
737
775
|
if (Array.isArray(result)) messages.push(...result);
|
|
@@ -856,7 +894,9 @@ var D1Store = class extends MastraStorage {
|
|
|
856
894
|
scope,
|
|
857
895
|
page,
|
|
858
896
|
perPage,
|
|
859
|
-
attributes
|
|
897
|
+
attributes,
|
|
898
|
+
fromDate,
|
|
899
|
+
toDate
|
|
860
900
|
}) {
|
|
861
901
|
const fullTableName = this.getTableName(TABLE_TRACES);
|
|
862
902
|
try {
|
|
@@ -872,6 +912,12 @@ var D1Store = class extends MastraStorage {
|
|
|
872
912
|
query.jsonLike("attributes", key, value);
|
|
873
913
|
}
|
|
874
914
|
}
|
|
915
|
+
if (fromDate) {
|
|
916
|
+
query.andWhere("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
|
|
917
|
+
}
|
|
918
|
+
if (toDate) {
|
|
919
|
+
query.andWhere("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
|
|
920
|
+
}
|
|
875
921
|
query.orderBy("startTime", "DESC").limit(perPage).offset((page - 1) * perPage);
|
|
876
922
|
const { sql, params } = query.build();
|
|
877
923
|
const results = await this.executeQuery({ sql, params });
|
|
@@ -923,8 +969,107 @@ var D1Store = class extends MastraStorage {
|
|
|
923
969
|
return [];
|
|
924
970
|
}
|
|
925
971
|
}
|
|
926
|
-
|
|
927
|
-
|
|
972
|
+
parseWorkflowRun(row) {
|
|
973
|
+
let parsedSnapshot = row.snapshot;
|
|
974
|
+
if (typeof parsedSnapshot === "string") {
|
|
975
|
+
try {
|
|
976
|
+
parsedSnapshot = JSON.parse(row.snapshot);
|
|
977
|
+
} catch (e) {
|
|
978
|
+
console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
return {
|
|
982
|
+
workflowName: row.workflow_name,
|
|
983
|
+
runId: row.run_id,
|
|
984
|
+
snapshot: parsedSnapshot,
|
|
985
|
+
createdAt: this.ensureDate(row.createdAt),
|
|
986
|
+
updatedAt: this.ensureDate(row.updatedAt),
|
|
987
|
+
resourceId: row.resourceId
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
async hasColumn(table, column) {
|
|
991
|
+
const sql = `PRAGMA table_info(${table});`;
|
|
992
|
+
const result = await this.executeQuery({ sql, params: [] });
|
|
993
|
+
if (!result || !Array.isArray(result)) return false;
|
|
994
|
+
return result.some((col) => col.name === column || col.name === column.toLowerCase());
|
|
995
|
+
}
|
|
996
|
+
async getWorkflowRuns({
|
|
997
|
+
workflowName,
|
|
998
|
+
fromDate,
|
|
999
|
+
toDate,
|
|
1000
|
+
limit,
|
|
1001
|
+
offset,
|
|
1002
|
+
resourceId
|
|
1003
|
+
} = {}) {
|
|
1004
|
+
const fullTableName = this.getTableName(TABLE_WORKFLOW_SNAPSHOT);
|
|
1005
|
+
try {
|
|
1006
|
+
const builder = createSqlBuilder().select().from(fullTableName);
|
|
1007
|
+
const countBuilder = createSqlBuilder().count().from(fullTableName);
|
|
1008
|
+
if (workflowName) builder.whereAnd("workflow_name = ?", workflowName);
|
|
1009
|
+
if (resourceId) {
|
|
1010
|
+
const hasResourceId = await this.hasColumn(fullTableName, "resourceId");
|
|
1011
|
+
if (hasResourceId) {
|
|
1012
|
+
builder.whereAnd("resourceId = ?", resourceId);
|
|
1013
|
+
countBuilder.whereAnd("resourceId = ?", resourceId);
|
|
1014
|
+
} else {
|
|
1015
|
+
console.warn(`[${fullTableName}] resourceId column not found. Skipping resourceId filter.`);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
if (fromDate) {
|
|
1019
|
+
builder.whereAnd("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
|
|
1020
|
+
countBuilder.whereAnd("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
|
|
1021
|
+
}
|
|
1022
|
+
if (toDate) {
|
|
1023
|
+
builder.whereAnd("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
|
|
1024
|
+
countBuilder.whereAnd("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
|
|
1025
|
+
}
|
|
1026
|
+
builder.orderBy("createdAt", "DESC");
|
|
1027
|
+
if (typeof limit === "number") builder.limit(limit);
|
|
1028
|
+
if (typeof offset === "number") builder.offset(offset);
|
|
1029
|
+
const { sql, params } = builder.build();
|
|
1030
|
+
let total = 0;
|
|
1031
|
+
if (limit !== void 0 && offset !== void 0) {
|
|
1032
|
+
const { sql: countSql, params: countParams } = countBuilder.build();
|
|
1033
|
+
const countResult = await this.executeQuery({ sql: countSql, params: countParams, first: true });
|
|
1034
|
+
total = Number(countResult?.count ?? 0);
|
|
1035
|
+
}
|
|
1036
|
+
const results = await this.executeQuery({ sql, params });
|
|
1037
|
+
const runs = (isArrayOfRecords(results) ? results : []).map((row) => this.parseWorkflowRun(row));
|
|
1038
|
+
return { runs, total: total || runs.length };
|
|
1039
|
+
} catch (error) {
|
|
1040
|
+
this.logger.error("Error getting workflow runs:", {
|
|
1041
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1042
|
+
});
|
|
1043
|
+
throw error;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
async getWorkflowRunById({
|
|
1047
|
+
runId,
|
|
1048
|
+
workflowName
|
|
1049
|
+
}) {
|
|
1050
|
+
const fullTableName = this.getTableName(TABLE_WORKFLOW_SNAPSHOT);
|
|
1051
|
+
try {
|
|
1052
|
+
const conditions = [];
|
|
1053
|
+
const params = [];
|
|
1054
|
+
if (runId) {
|
|
1055
|
+
conditions.push("run_id = ?");
|
|
1056
|
+
params.push(runId);
|
|
1057
|
+
}
|
|
1058
|
+
if (workflowName) {
|
|
1059
|
+
conditions.push("workflow_name = ?");
|
|
1060
|
+
params.push(workflowName);
|
|
1061
|
+
}
|
|
1062
|
+
const whereClause = conditions.length > 0 ? "WHERE " + conditions.join(" AND ") : "";
|
|
1063
|
+
const sql = `SELECT * FROM ${fullTableName} ${whereClause} ORDER BY createdAt DESC LIMIT 1`;
|
|
1064
|
+
const result = await this.executeQuery({ sql, params, first: true });
|
|
1065
|
+
if (!result) return null;
|
|
1066
|
+
return this.parseWorkflowRun(result);
|
|
1067
|
+
} catch (error) {
|
|
1068
|
+
this.logger.error("Error getting workflow run by ID:", {
|
|
1069
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1070
|
+
});
|
|
1071
|
+
throw error;
|
|
1072
|
+
}
|
|
928
1073
|
}
|
|
929
1074
|
/**
|
|
930
1075
|
* Close the database connection
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/cloudflare-d1",
|
|
3
|
-
"version": "0.0.0-
|
|
3
|
+
"version": "0.0.0-vector-query-sources-20250516172905",
|
|
4
4
|
"description": "D1 provider for Mastra - includes db storage capabilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -23,19 +23,19 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"cloudflare": "^4.1.0",
|
|
26
|
-
"@mastra/core": "0.0.0-
|
|
26
|
+
"@mastra/core": "0.0.0-vector-query-sources-20250516172905"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@cloudflare/workers-types": "^4.20250417.0",
|
|
30
|
-
"@microsoft/api-extractor": "^7.52.
|
|
30
|
+
"@microsoft/api-extractor": "^7.52.5",
|
|
31
31
|
"@types/node": "^20.17.27",
|
|
32
32
|
"dotenv": "^16.4.7",
|
|
33
33
|
"eslint": "^9.23.0",
|
|
34
34
|
"miniflare": "^4.20250410.1",
|
|
35
35
|
"tsup": "^8.4.0",
|
|
36
36
|
"typescript": "^5.8.2",
|
|
37
|
-
"vitest": "^3.
|
|
38
|
-
"@internal/lint": "0.0.
|
|
37
|
+
"vitest": "^3.1.2",
|
|
38
|
+
"@internal/lint": "0.0.0-vector-query-sources-20250516172905"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting",
|