@mastra/cloudflare-d1 1.1.1 → 1.2.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/reference-storage-cloudflare-d1.md +1 -1
- package/dist/index.cjs +2417 -2516
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2388 -2505
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/package.json +18 -17
package/dist/index.js
CHANGED
|
@@ -1,1500 +1,1414 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { BackgroundTasksStorage,
|
|
3
|
-
import Cloudflare from
|
|
4
|
-
import { MastraBase } from
|
|
5
|
-
import { parseSqlIdentifier } from
|
|
6
|
-
import { MessageList } from
|
|
7
|
-
import { saveScorePayloadSchema } from
|
|
8
|
-
|
|
9
|
-
// src/storage/index.ts
|
|
10
|
-
|
|
11
|
-
// src/storage/domains/utils.ts
|
|
1
|
+
import { ErrorCategory, ErrorDomain, MastraError } from "@mastra/core/error";
|
|
2
|
+
import { BackgroundTasksStorage, MastraCompositeStore, MemoryStorage, ScoresStorage, TABLE_BACKGROUND_TASKS, TABLE_MESSAGES, TABLE_RESOURCES, TABLE_SCHEMAS, TABLE_SCORERS, TABLE_THREADS, TABLE_WORKFLOW_SNAPSHOT, WorkflowsStorage, calculatePagination, createStorageErrorId, ensureDate, getDefaultValue, getSqlType, normalizePerPage, serializeDate, transformScoreRow, validateStorageMetadataFilter } from "@mastra/core/storage";
|
|
3
|
+
import Cloudflare from "cloudflare";
|
|
4
|
+
import { MastraBase } from "@mastra/core/base";
|
|
5
|
+
import { parseSqlIdentifier } from "@mastra/core/utils";
|
|
6
|
+
import { MessageList } from "@mastra/core/agent";
|
|
7
|
+
import { saveScorePayloadSchema } from "@mastra/core/evals";
|
|
8
|
+
//#region src/storage/domains/utils.ts
|
|
12
9
|
function isArrayOfRecords(value) {
|
|
13
|
-
|
|
10
|
+
return value && Array.isArray(value) && value.length > 0;
|
|
14
11
|
}
|
|
15
12
|
function deserializeValue(value, type) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
13
|
+
if (value === null || value === void 0) return null;
|
|
14
|
+
if (type === "date" && typeof value === "string") return new Date(value);
|
|
15
|
+
if (type === "jsonb" && typeof value === "string") try {
|
|
16
|
+
return JSON.parse(value);
|
|
17
|
+
} catch {
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) try {
|
|
21
|
+
return JSON.parse(value);
|
|
22
|
+
} catch {
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
25
26
|
}
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/storage/sql-builder.ts
|
|
29
|
+
/**
|
|
30
|
+
* SQL Builder class for constructing type-safe SQL queries
|
|
31
|
+
* This helps create maintainable and secure SQL queries with proper parameter handling
|
|
32
|
+
*/
|
|
26
33
|
var SqlBuilder = class {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
return {
|
|
228
|
-
sql: this.sql,
|
|
229
|
-
params: this.params
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* Reset the builder for reuse
|
|
234
|
-
* @returns The reset builder instance
|
|
235
|
-
*/
|
|
236
|
-
reset() {
|
|
237
|
-
this.sql = "";
|
|
238
|
-
this.params = [];
|
|
239
|
-
this.whereAdded = false;
|
|
240
|
-
return this;
|
|
241
|
-
}
|
|
34
|
+
sql = "";
|
|
35
|
+
params = [];
|
|
36
|
+
whereAdded = false;
|
|
37
|
+
select(columns) {
|
|
38
|
+
if (!columns || Array.isArray(columns) && columns.length === 0) this.sql = "SELECT *";
|
|
39
|
+
else {
|
|
40
|
+
const parsedCols = (Array.isArray(columns) ? columns : [columns]).map((col) => parseSelectIdentifier(col));
|
|
41
|
+
this.sql = `SELECT ${parsedCols.join(", ")}`;
|
|
42
|
+
}
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
from(table) {
|
|
46
|
+
const parsedTableName = parseSqlIdentifier(table, "table name");
|
|
47
|
+
this.sql += ` FROM ${parsedTableName}`;
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Add a WHERE clause to the query
|
|
52
|
+
* @param condition The condition to add
|
|
53
|
+
* @param params Parameters to bind to the condition
|
|
54
|
+
*/
|
|
55
|
+
where(condition, ...params) {
|
|
56
|
+
this.sql += ` WHERE ${condition}`;
|
|
57
|
+
this.params.push(...params);
|
|
58
|
+
this.whereAdded = true;
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Add a WHERE clause if it hasn't been added yet, otherwise add an AND clause
|
|
63
|
+
* @param condition The condition to add
|
|
64
|
+
* @param params Parameters to bind to the condition
|
|
65
|
+
*/
|
|
66
|
+
whereAnd(condition, ...params) {
|
|
67
|
+
if (this.whereAdded) return this.andWhere(condition, ...params);
|
|
68
|
+
else return this.where(condition, ...params);
|
|
69
|
+
}
|
|
70
|
+
andWhere(condition, ...params) {
|
|
71
|
+
this.sql += ` AND ${condition}`;
|
|
72
|
+
this.params.push(...params);
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
75
|
+
orWhere(condition, ...params) {
|
|
76
|
+
this.sql += ` OR ${condition}`;
|
|
77
|
+
this.params.push(...params);
|
|
78
|
+
return this;
|
|
79
|
+
}
|
|
80
|
+
orderBy(column, direction = "ASC") {
|
|
81
|
+
const parsedColumn = parseSqlIdentifier(column, "column name");
|
|
82
|
+
if (!["ASC", "DESC"].includes(direction)) throw new Error(`Invalid sort direction: ${direction}`);
|
|
83
|
+
this.sql += ` ORDER BY ${parsedColumn} ${direction}`;
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
limit(count) {
|
|
87
|
+
this.sql += ` LIMIT ?`;
|
|
88
|
+
this.params.push(count);
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
91
|
+
offset(count) {
|
|
92
|
+
this.sql += ` OFFSET ?`;
|
|
93
|
+
this.params.push(count);
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
count() {
|
|
97
|
+
this.sql += "SELECT COUNT(*) AS count";
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Insert a row, or update specific columns on conflict (upsert).
|
|
102
|
+
* @param table Table name
|
|
103
|
+
* @param columns Columns to insert
|
|
104
|
+
* @param values Values to insert
|
|
105
|
+
* @param conflictColumns Columns to check for conflict (usually PK or UNIQUE)
|
|
106
|
+
* @param updateMap Object mapping columns to update to their new value (e.g. { name: 'excluded.name' })
|
|
107
|
+
*/
|
|
108
|
+
insert(table, columns, values, conflictColumns, updateMap) {
|
|
109
|
+
const parsedTableName = parseSqlIdentifier(table, "table name");
|
|
110
|
+
const parsedColumns = columns.map((col) => parseSqlIdentifier(col, "column name"));
|
|
111
|
+
const placeholders = parsedColumns.map(() => "?").join(", ");
|
|
112
|
+
if (conflictColumns && updateMap) {
|
|
113
|
+
const parsedConflictColumns = conflictColumns.map((col) => parseSqlIdentifier(col, "column name"));
|
|
114
|
+
const updateClause = Object.entries(updateMap).map(([col, expr]) => `${col} = ${expr}`).join(", ");
|
|
115
|
+
this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders}) ON CONFLICT(${parsedConflictColumns.join(", ")}) DO UPDATE SET ${updateClause}`;
|
|
116
|
+
this.params.push(...values);
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders})`;
|
|
120
|
+
this.params.push(...values);
|
|
121
|
+
return this;
|
|
122
|
+
}
|
|
123
|
+
update(table, columns, values) {
|
|
124
|
+
const parsedTableName = parseSqlIdentifier(table, "table name");
|
|
125
|
+
const setClause = columns.map((col) => parseSqlIdentifier(col, "column name")).map((col) => `${col} = ?`).join(", ");
|
|
126
|
+
this.sql = `UPDATE ${parsedTableName} SET ${setClause}`;
|
|
127
|
+
this.params.push(...values);
|
|
128
|
+
return this;
|
|
129
|
+
}
|
|
130
|
+
delete(table) {
|
|
131
|
+
const parsedTableName = parseSqlIdentifier(table, "table name");
|
|
132
|
+
this.sql = `DELETE FROM ${parsedTableName}`;
|
|
133
|
+
return this;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Create a table if it doesn't exist
|
|
137
|
+
* @param table The table name
|
|
138
|
+
* @param columnDefinitions The column definitions as an array of strings
|
|
139
|
+
* @param tableConstraints Optional constraints for the table
|
|
140
|
+
* @returns The builder instance
|
|
141
|
+
*/
|
|
142
|
+
createTable(table, columnDefinitions, tableConstraints) {
|
|
143
|
+
const parsedTableName = parseSqlIdentifier(table, "table name");
|
|
144
|
+
const columns = columnDefinitions.map((def) => {
|
|
145
|
+
const colName = def.split(/\s+/)[0];
|
|
146
|
+
if (!colName) throw new Error("Empty column name in definition");
|
|
147
|
+
parseSqlIdentifier(colName, "column name");
|
|
148
|
+
return def;
|
|
149
|
+
}).join(", ");
|
|
150
|
+
const constraints = tableConstraints && tableConstraints.length > 0 ? ", " + tableConstraints.join(", ") : "";
|
|
151
|
+
this.sql = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (${columns}${constraints})`;
|
|
152
|
+
return this;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Check if an index exists in the database
|
|
156
|
+
* @param indexName The name of the index to check
|
|
157
|
+
* @param tableName The table the index is on
|
|
158
|
+
* @returns The builder instance
|
|
159
|
+
*/
|
|
160
|
+
checkIndexExists(indexName, tableName) {
|
|
161
|
+
this.sql = `SELECT name FROM sqlite_master WHERE type='index' AND name=? AND tbl_name=?`;
|
|
162
|
+
this.params.push(indexName, tableName);
|
|
163
|
+
return this;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Create an index if it doesn't exist
|
|
167
|
+
* @param indexName The name of the index to create
|
|
168
|
+
* @param tableName The table to create the index on
|
|
169
|
+
* @param columnName The column to index
|
|
170
|
+
* @param indexType Optional index type (e.g., 'UNIQUE')
|
|
171
|
+
* @returns The builder instance
|
|
172
|
+
*/
|
|
173
|
+
createIndex(indexName, tableName, columnName, indexType = "") {
|
|
174
|
+
const parsedIndexName = parseSqlIdentifier(indexName, "index name");
|
|
175
|
+
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
176
|
+
const parsedColumnName = parseSqlIdentifier(columnName, "column name");
|
|
177
|
+
this.sql = `CREATE ${indexType ? indexType + " " : ""}INDEX IF NOT EXISTS ${parsedIndexName} ON ${parsedTableName}(${parsedColumnName})`;
|
|
178
|
+
return this;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Add a LIKE condition to the query
|
|
182
|
+
* @param column The column to check
|
|
183
|
+
* @param value The value to match (will be wrapped with % for LIKE)
|
|
184
|
+
* @param exact If true, will not add % wildcards
|
|
185
|
+
*/
|
|
186
|
+
like(column, value, exact = false) {
|
|
187
|
+
const parsedColumnName = parseSqlIdentifier(column, "column name");
|
|
188
|
+
const likeValue = exact ? value : `%${value}%`;
|
|
189
|
+
if (this.whereAdded) this.sql += ` AND ${parsedColumnName} LIKE ?`;
|
|
190
|
+
else {
|
|
191
|
+
this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
|
|
192
|
+
this.whereAdded = true;
|
|
193
|
+
}
|
|
194
|
+
this.params.push(likeValue);
|
|
195
|
+
return this;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Add a JSON LIKE condition for searching in JSON fields
|
|
199
|
+
* @param column The JSON column to search in
|
|
200
|
+
* @param key The JSON key to match
|
|
201
|
+
* @param value The value to match
|
|
202
|
+
*/
|
|
203
|
+
jsonLike(column, key, value) {
|
|
204
|
+
const parsedColumnName = parseSqlIdentifier(column, "column name");
|
|
205
|
+
const jsonPattern = `%"${parseSqlIdentifier(key, "key name")}":"${value}"%`;
|
|
206
|
+
if (this.whereAdded) this.sql += ` AND ${parsedColumnName} LIKE ?`;
|
|
207
|
+
else {
|
|
208
|
+
this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
|
|
209
|
+
this.whereAdded = true;
|
|
210
|
+
}
|
|
211
|
+
this.params.push(jsonPattern);
|
|
212
|
+
return this;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Get the built query
|
|
216
|
+
* @returns Object containing the SQL string and parameters array
|
|
217
|
+
*/
|
|
218
|
+
build() {
|
|
219
|
+
return {
|
|
220
|
+
sql: this.sql,
|
|
221
|
+
params: this.params
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Reset the builder for reuse
|
|
226
|
+
* @returns The reset builder instance
|
|
227
|
+
*/
|
|
228
|
+
reset() {
|
|
229
|
+
this.sql = "";
|
|
230
|
+
this.params = [];
|
|
231
|
+
this.whereAdded = false;
|
|
232
|
+
return this;
|
|
233
|
+
}
|
|
242
234
|
};
|
|
243
235
|
function createSqlBuilder() {
|
|
244
|
-
|
|
236
|
+
return new SqlBuilder();
|
|
245
237
|
}
|
|
246
|
-
|
|
238
|
+
const SQL_IDENTIFIER_PATTERN = /^[a-zA-Z0-9_]+(\s+AS\s+[a-zA-Z0-9_]+)?$/;
|
|
239
|
+
/**
|
|
240
|
+
* Parses and returns a valid SQL SELECT column identifier.
|
|
241
|
+
* Allows a single identifier (letters, numbers, underscores), or '*', optionally with 'AS alias'.
|
|
242
|
+
*
|
|
243
|
+
* @param column - The column identifier string to parse.
|
|
244
|
+
* @returns The validated column identifier as a branded type.
|
|
245
|
+
* @throws {Error} If invalid.
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* const col = parseSelectIdentifier('user_id'); // Ok
|
|
249
|
+
* parseSelectIdentifier('user_id AS uid'); // Ok
|
|
250
|
+
* parseSelectIdentifier('*'); // Ok
|
|
251
|
+
* parseSelectIdentifier('user id'); // Throws error
|
|
252
|
+
*/
|
|
247
253
|
function parseSelectIdentifier(column) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
`Invalid column name: "${column}". Must be "*" or a valid identifier (letters, numbers, underscores), optionally with "AS alias".`
|
|
251
|
-
);
|
|
252
|
-
}
|
|
253
|
-
return column;
|
|
254
|
+
if (column !== "*" && !SQL_IDENTIFIER_PATTERN.test(column)) throw new Error(`Invalid column name: "${column}". Must be "*" or a valid identifier (letters, numbers, underscores), optionally with "AS alias".`);
|
|
255
|
+
return column;
|
|
254
256
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
+
//#endregion
|
|
258
|
+
//#region src/storage/db/index.ts
|
|
259
|
+
/**
|
|
260
|
+
* Resolves D1DomainConfig to D1DBConfig.
|
|
261
|
+
* Handles creating a new D1 client if apiToken is provided.
|
|
262
|
+
*/
|
|
257
263
|
function resolveD1Config(config) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
params
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
},
|
|
281
|
-
tablePrefix: config.tablePrefix
|
|
282
|
-
};
|
|
264
|
+
if ("client" in config) return {
|
|
265
|
+
client: config.client,
|
|
266
|
+
tablePrefix: config.tablePrefix
|
|
267
|
+
};
|
|
268
|
+
if ("binding" in config) return {
|
|
269
|
+
binding: config.binding,
|
|
270
|
+
tablePrefix: config.tablePrefix
|
|
271
|
+
};
|
|
272
|
+
const cfClient = new Cloudflare({ apiToken: config.apiToken });
|
|
273
|
+
return {
|
|
274
|
+
client: { query: ({ sql, params }) => {
|
|
275
|
+
return cfClient.d1.database.query(config.databaseId, {
|
|
276
|
+
account_id: config.accountId,
|
|
277
|
+
sql,
|
|
278
|
+
params
|
|
279
|
+
});
|
|
280
|
+
} },
|
|
281
|
+
tablePrefix: config.tablePrefix
|
|
282
|
+
};
|
|
283
283
|
}
|
|
284
284
|
var D1DB = class extends MastraBase {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
if (firstKey) {
|
|
634
|
-
query.where(`${key} = ?`, value);
|
|
635
|
-
firstKey = false;
|
|
636
|
-
} else {
|
|
637
|
-
query.andWhere(`${key} = ?`, value);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
query.orderBy("createdAt", "DESC");
|
|
641
|
-
query.limit(1);
|
|
642
|
-
const { sql, params } = query.build();
|
|
643
|
-
const result = await this.executeQuery({ sql, params, first: true });
|
|
644
|
-
if (!result) {
|
|
645
|
-
return null;
|
|
646
|
-
}
|
|
647
|
-
const deserializedResult = {};
|
|
648
|
-
for (const [key, value] of Object.entries(result)) {
|
|
649
|
-
deserializedResult[key] = deserializeValue(value);
|
|
650
|
-
}
|
|
651
|
-
return deserializedResult;
|
|
652
|
-
} catch (error) {
|
|
653
|
-
throw new MastraError(
|
|
654
|
-
{
|
|
655
|
-
id: createStorageErrorId("CLOUDFLARE_D1", "LOAD", "FAILED"),
|
|
656
|
-
domain: ErrorDomain.STORAGE,
|
|
657
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
658
|
-
details: { tableName }
|
|
659
|
-
},
|
|
660
|
-
error
|
|
661
|
-
);
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
async processRecord(record) {
|
|
665
|
-
const processed = {};
|
|
666
|
-
for (const [key, value] of Object.entries(record)) {
|
|
667
|
-
processed[key] = this.serializeValue(value);
|
|
668
|
-
}
|
|
669
|
-
return processed;
|
|
670
|
-
}
|
|
671
|
-
/**
|
|
672
|
-
* Upsert multiple records in a batch operation
|
|
673
|
-
* @param tableName The table to insert into
|
|
674
|
-
* @param records The records to insert
|
|
675
|
-
*/
|
|
676
|
-
async batchUpsert({ tableName, records }) {
|
|
677
|
-
if (records.length === 0) return;
|
|
678
|
-
const fullTableName = this.getTableName(tableName);
|
|
679
|
-
try {
|
|
680
|
-
const batchSize = 50;
|
|
681
|
-
for (let i = 0; i < records.length; i += batchSize) {
|
|
682
|
-
const batch = records.slice(i, i + batchSize);
|
|
683
|
-
const recordsToInsert = batch;
|
|
684
|
-
if (recordsToInsert.length > 0) {
|
|
685
|
-
const filteredRecords = await Promise.all(
|
|
686
|
-
recordsToInsert.map((r) => this.filterRecordToKnownColumns(fullTableName, r || {}))
|
|
687
|
-
);
|
|
688
|
-
for (const record of filteredRecords) {
|
|
689
|
-
const columns = Object.keys(record);
|
|
690
|
-
if (columns.length === 0) continue;
|
|
691
|
-
const values = columns.map((col) => {
|
|
692
|
-
const value = record[col];
|
|
693
|
-
return this.serializeValue(value);
|
|
694
|
-
});
|
|
695
|
-
const recordToUpsert = columns.reduce(
|
|
696
|
-
(acc, col) => {
|
|
697
|
-
if (col !== "createdAt") acc[col] = `excluded.${col}`;
|
|
698
|
-
return acc;
|
|
699
|
-
},
|
|
700
|
-
{}
|
|
701
|
-
);
|
|
702
|
-
const query = createSqlBuilder().insert(fullTableName, columns, values, ["id"], recordToUpsert);
|
|
703
|
-
const { sql, params } = query.build();
|
|
704
|
-
await this.executeQuery({ sql, params });
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
this.logger.debug(
|
|
708
|
-
`Processed batch ${Math.floor(i / batchSize) + 1} of ${Math.ceil(records.length / batchSize)}`
|
|
709
|
-
);
|
|
710
|
-
}
|
|
711
|
-
this.logger.debug(`Successfully batch upserted ${records.length} records into ${tableName}`);
|
|
712
|
-
} catch (error) {
|
|
713
|
-
throw new MastraError(
|
|
714
|
-
{
|
|
715
|
-
id: createStorageErrorId("CLOUDFLARE_D1", "BATCH_UPSERT", "FAILED"),
|
|
716
|
-
domain: ErrorDomain.STORAGE,
|
|
717
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
718
|
-
text: `Failed to batch upsert into ${tableName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
719
|
-
details: { tableName }
|
|
720
|
-
},
|
|
721
|
-
error
|
|
722
|
-
);
|
|
723
|
-
}
|
|
724
|
-
}
|
|
285
|
+
client;
|
|
286
|
+
binding;
|
|
287
|
+
tablePrefix;
|
|
288
|
+
/** Cache of actual table columns: tableName -> Promise<Set<columnName>> (stores in-flight promise to coalesce concurrent calls) */
|
|
289
|
+
tableColumnsCache = /* @__PURE__ */ new Map();
|
|
290
|
+
constructor(config) {
|
|
291
|
+
super({
|
|
292
|
+
component: "STORAGE",
|
|
293
|
+
name: "D1_DB"
|
|
294
|
+
});
|
|
295
|
+
this.client = config.client;
|
|
296
|
+
this.binding = config.binding;
|
|
297
|
+
this.tablePrefix = config.tablePrefix || "";
|
|
298
|
+
}
|
|
299
|
+
async hasColumn(table, column) {
|
|
300
|
+
const sql = `PRAGMA table_info(${table.startsWith(this.tablePrefix) ? table : `${this.tablePrefix}${table}`});`;
|
|
301
|
+
const result = await this.executeQuery({
|
|
302
|
+
sql,
|
|
303
|
+
params: []
|
|
304
|
+
});
|
|
305
|
+
if (!result || !Array.isArray(result)) return false;
|
|
306
|
+
return result.some((col) => col.name === column || col.name === column.toLowerCase());
|
|
307
|
+
}
|
|
308
|
+
getTableName(tableName) {
|
|
309
|
+
return `${this.tablePrefix}${tableName}`;
|
|
310
|
+
}
|
|
311
|
+
formatSqlParams(params) {
|
|
312
|
+
return params.map((p) => p === void 0 || p === null ? null : p);
|
|
313
|
+
}
|
|
314
|
+
async executeWorkersBindingQuery({ sql, params = [], first = false }) {
|
|
315
|
+
if (!this.binding) throw new Error("Workers binding is not configured");
|
|
316
|
+
try {
|
|
317
|
+
const statement = this.binding.prepare(sql);
|
|
318
|
+
const formattedParams = this.formatSqlParams(params);
|
|
319
|
+
let result;
|
|
320
|
+
if (formattedParams.length > 0) if (first) {
|
|
321
|
+
result = await statement.bind(...formattedParams).first();
|
|
322
|
+
if (!result) return null;
|
|
323
|
+
return result;
|
|
324
|
+
} else {
|
|
325
|
+
result = await statement.bind(...formattedParams).all();
|
|
326
|
+
return result.results || [];
|
|
327
|
+
}
|
|
328
|
+
else if (first) {
|
|
329
|
+
result = await statement.first();
|
|
330
|
+
if (!result) return null;
|
|
331
|
+
return result;
|
|
332
|
+
} else {
|
|
333
|
+
result = await statement.all();
|
|
334
|
+
return result.results || [];
|
|
335
|
+
}
|
|
336
|
+
} catch (error) {
|
|
337
|
+
throw new MastraError({
|
|
338
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "WORKERS_BINDING_QUERY", "FAILED"),
|
|
339
|
+
domain: ErrorDomain.STORAGE,
|
|
340
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
341
|
+
details: { sql }
|
|
342
|
+
}, error);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
async executeRestQuery({ sql, params = [], first = false }) {
|
|
346
|
+
if (!this.client) throw new Error("D1 client is not configured");
|
|
347
|
+
try {
|
|
348
|
+
const formattedParams = this.formatSqlParams(params);
|
|
349
|
+
const results = ((await this.client.query({
|
|
350
|
+
sql,
|
|
351
|
+
params: formattedParams
|
|
352
|
+
})).result || []).flatMap((r) => r.results || []);
|
|
353
|
+
if (first) return results[0] || null;
|
|
354
|
+
return results;
|
|
355
|
+
} catch (error) {
|
|
356
|
+
throw new MastraError({
|
|
357
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "REST_QUERY", "FAILED"),
|
|
358
|
+
domain: ErrorDomain.STORAGE,
|
|
359
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
360
|
+
details: { sql }
|
|
361
|
+
}, error);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
async executeQuery(options) {
|
|
365
|
+
if (this.binding) return this.executeWorkersBindingQuery(options);
|
|
366
|
+
else if (this.client) return this.executeRestQuery(options);
|
|
367
|
+
else throw new Error("Neither binding nor client is configured");
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Gets the set of column names that actually exist in the database table.
|
|
371
|
+
* Results are cached; the cache is invalidated when alterTable() adds new columns.
|
|
372
|
+
*/
|
|
373
|
+
async getKnownColumnNames(tableName) {
|
|
374
|
+
const cached = this.tableColumnsCache.get(tableName);
|
|
375
|
+
if (cached) return cached;
|
|
376
|
+
const promise = this.getTableColumns(tableName).then((columns) => {
|
|
377
|
+
const names = new Set(columns.map((c) => c.name));
|
|
378
|
+
if (names.size === 0) this.tableColumnsCache.delete(tableName);
|
|
379
|
+
return names;
|
|
380
|
+
});
|
|
381
|
+
this.tableColumnsCache.set(tableName, promise);
|
|
382
|
+
return promise;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Filters a record to only include columns that exist in the actual database table.
|
|
386
|
+
* Unknown columns are silently dropped to ensure forward compatibility.
|
|
387
|
+
*/
|
|
388
|
+
async filterRecordToKnownColumns(tableName, record) {
|
|
389
|
+
const knownColumns = await this.getKnownColumnNames(tableName);
|
|
390
|
+
if (knownColumns.size === 0) return record;
|
|
391
|
+
const filtered = {};
|
|
392
|
+
for (const [key, value] of Object.entries(record)) if (knownColumns.has(key)) filtered[key] = value;
|
|
393
|
+
return filtered;
|
|
394
|
+
}
|
|
395
|
+
async getTableColumns(tableName) {
|
|
396
|
+
try {
|
|
397
|
+
const sql = `PRAGMA table_info(${tableName})`;
|
|
398
|
+
const result = await this.executeQuery({ sql });
|
|
399
|
+
if (!result || !Array.isArray(result)) return [];
|
|
400
|
+
return result.map((row) => ({
|
|
401
|
+
name: row.name,
|
|
402
|
+
type: row.type
|
|
403
|
+
}));
|
|
404
|
+
} catch (error) {
|
|
405
|
+
this.logger.warn(`Failed to get table columns for ${tableName}:`, error);
|
|
406
|
+
return [];
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
serializeValue(value) {
|
|
410
|
+
if (value === null || value === void 0) return null;
|
|
411
|
+
if (value instanceof Date) return value.toISOString();
|
|
412
|
+
if (typeof value === "object") return JSON.stringify(value);
|
|
413
|
+
return value;
|
|
414
|
+
}
|
|
415
|
+
getSqlType(type) {
|
|
416
|
+
switch (type) {
|
|
417
|
+
case "bigint": return "INTEGER";
|
|
418
|
+
case "jsonb": return "TEXT";
|
|
419
|
+
case "boolean": return "INTEGER";
|
|
420
|
+
default: return getSqlType(type);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
getDefaultValue(type) {
|
|
424
|
+
return getDefaultValue(type);
|
|
425
|
+
}
|
|
426
|
+
async createTable({ tableName, schema }) {
|
|
427
|
+
try {
|
|
428
|
+
const fullTableName = this.getTableName(tableName);
|
|
429
|
+
const columnDefinitions = Object.entries(schema).map(([colName, colDef]) => {
|
|
430
|
+
return `${colName} ${this.getSqlType(colDef.type)} ${colDef.nullable === false ? "NOT NULL" : ""} ${colDef.primaryKey ? "PRIMARY KEY" : ""}`.trim();
|
|
431
|
+
});
|
|
432
|
+
const tableConstraints = [];
|
|
433
|
+
if (tableName === TABLE_WORKFLOW_SNAPSHOT) tableConstraints.push("UNIQUE (workflow_name, run_id)");
|
|
434
|
+
const { sql, params } = createSqlBuilder().createTable(fullTableName, columnDefinitions, tableConstraints).build();
|
|
435
|
+
await this.executeQuery({
|
|
436
|
+
sql,
|
|
437
|
+
params
|
|
438
|
+
});
|
|
439
|
+
this.logger.debug(`Created table ${fullTableName}`);
|
|
440
|
+
this.tableColumnsCache.delete(fullTableName);
|
|
441
|
+
} catch (error) {
|
|
442
|
+
throw new MastraError({
|
|
443
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "CREATE_TABLE", "FAILED"),
|
|
444
|
+
domain: ErrorDomain.STORAGE,
|
|
445
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
446
|
+
details: { tableName }
|
|
447
|
+
}, error);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
async clearTable({ tableName }) {
|
|
451
|
+
try {
|
|
452
|
+
const fullTableName = this.getTableName(tableName);
|
|
453
|
+
const { sql, params } = createSqlBuilder().delete(fullTableName).build();
|
|
454
|
+
await this.executeQuery({
|
|
455
|
+
sql,
|
|
456
|
+
params
|
|
457
|
+
});
|
|
458
|
+
this.logger.debug(`Cleared table ${fullTableName}`);
|
|
459
|
+
} catch (error) {
|
|
460
|
+
throw new MastraError({
|
|
461
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "CLEAR_TABLE", "FAILED"),
|
|
462
|
+
domain: ErrorDomain.STORAGE,
|
|
463
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
464
|
+
details: { tableName }
|
|
465
|
+
}, error);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
async dropTable({ tableName }) {
|
|
469
|
+
const fullTableName = this.getTableName(tableName);
|
|
470
|
+
try {
|
|
471
|
+
const sql = `DROP TABLE IF EXISTS ${fullTableName}`;
|
|
472
|
+
await this.executeQuery({ sql });
|
|
473
|
+
this.logger.debug(`Dropped table ${fullTableName}`);
|
|
474
|
+
} catch (error) {
|
|
475
|
+
throw new MastraError({
|
|
476
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "DROP_TABLE", "FAILED"),
|
|
477
|
+
domain: ErrorDomain.STORAGE,
|
|
478
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
479
|
+
details: { tableName }
|
|
480
|
+
}, error);
|
|
481
|
+
} finally {
|
|
482
|
+
this.tableColumnsCache.delete(fullTableName);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
async alterTable(args) {
|
|
486
|
+
const fullTableName = this.getTableName(args.tableName);
|
|
487
|
+
try {
|
|
488
|
+
const existingColumns = await this.getTableColumns(fullTableName);
|
|
489
|
+
const existingColumnNames = new Set(existingColumns.map((col) => col.name));
|
|
490
|
+
for (const [columnName, column] of Object.entries(args.schema)) if (!existingColumnNames.has(columnName) && args.ifNotExists.includes(columnName)) {
|
|
491
|
+
const sql = `ALTER TABLE ${fullTableName} ADD COLUMN ${columnName} ${this.getSqlType(column.type)} ${this.getDefaultValue(column.type)}`;
|
|
492
|
+
await this.executeQuery({ sql });
|
|
493
|
+
this.logger.debug(`Added column ${columnName} to table ${fullTableName}`);
|
|
494
|
+
}
|
|
495
|
+
} catch (error) {
|
|
496
|
+
throw new MastraError({
|
|
497
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "ALTER_TABLE", "FAILED"),
|
|
498
|
+
domain: ErrorDomain.STORAGE,
|
|
499
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
500
|
+
details: { tableName: args.tableName }
|
|
501
|
+
}, error);
|
|
502
|
+
} finally {
|
|
503
|
+
this.tableColumnsCache.delete(fullTableName);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
async insert({ tableName, record }) {
|
|
507
|
+
try {
|
|
508
|
+
const fullTableName = this.getTableName(tableName);
|
|
509
|
+
const processedRecord = await this.processRecord(record);
|
|
510
|
+
const filteredRecord = await this.filterRecordToKnownColumns(fullTableName, processedRecord);
|
|
511
|
+
const columns = Object.keys(filteredRecord);
|
|
512
|
+
if (columns.length === 0) return;
|
|
513
|
+
const values = Object.values(filteredRecord);
|
|
514
|
+
const { sql, params } = createSqlBuilder().insert(fullTableName, columns, values).build();
|
|
515
|
+
await this.executeQuery({
|
|
516
|
+
sql,
|
|
517
|
+
params
|
|
518
|
+
});
|
|
519
|
+
} catch (error) {
|
|
520
|
+
throw new MastraError({
|
|
521
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "INSERT", "FAILED"),
|
|
522
|
+
domain: ErrorDomain.STORAGE,
|
|
523
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
524
|
+
details: { tableName }
|
|
525
|
+
}, error);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
async batchInsert({ tableName, records }) {
|
|
529
|
+
try {
|
|
530
|
+
if (records.length === 0) return;
|
|
531
|
+
const fullTableName = this.getTableName(tableName);
|
|
532
|
+
const processedRecords = await Promise.all(records.map((record) => this.processRecord(record)));
|
|
533
|
+
const filteredRecords = await Promise.all(processedRecords.map((r) => this.filterRecordToKnownColumns(fullTableName, r)));
|
|
534
|
+
for (const record of filteredRecords) {
|
|
535
|
+
const columns = Object.keys(record);
|
|
536
|
+
if (columns.length === 0) continue;
|
|
537
|
+
const values = Object.values(record);
|
|
538
|
+
const { sql, params } = createSqlBuilder().insert(fullTableName, columns, values).build();
|
|
539
|
+
await this.executeQuery({
|
|
540
|
+
sql,
|
|
541
|
+
params
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
} catch (error) {
|
|
545
|
+
throw new MastraError({
|
|
546
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "BATCH_INSERT", "FAILED"),
|
|
547
|
+
domain: ErrorDomain.STORAGE,
|
|
548
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
549
|
+
details: { tableName }
|
|
550
|
+
}, error);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
async load({ tableName, keys }) {
|
|
554
|
+
try {
|
|
555
|
+
const fullTableName = this.getTableName(tableName);
|
|
556
|
+
const query = createSqlBuilder().select("*").from(fullTableName);
|
|
557
|
+
let firstKey = true;
|
|
558
|
+
for (const [key, value] of Object.entries(keys)) if (firstKey) {
|
|
559
|
+
query.where(`${key} = ?`, value);
|
|
560
|
+
firstKey = false;
|
|
561
|
+
} else query.andWhere(`${key} = ?`, value);
|
|
562
|
+
query.orderBy("createdAt", "DESC");
|
|
563
|
+
query.limit(1);
|
|
564
|
+
const { sql, params } = query.build();
|
|
565
|
+
const result = await this.executeQuery({
|
|
566
|
+
sql,
|
|
567
|
+
params,
|
|
568
|
+
first: true
|
|
569
|
+
});
|
|
570
|
+
if (!result) return null;
|
|
571
|
+
const deserializedResult = {};
|
|
572
|
+
for (const [key, value] of Object.entries(result)) deserializedResult[key] = deserializeValue(value);
|
|
573
|
+
return deserializedResult;
|
|
574
|
+
} catch (error) {
|
|
575
|
+
throw new MastraError({
|
|
576
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "LOAD", "FAILED"),
|
|
577
|
+
domain: ErrorDomain.STORAGE,
|
|
578
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
579
|
+
details: { tableName }
|
|
580
|
+
}, error);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
async processRecord(record) {
|
|
584
|
+
const processed = {};
|
|
585
|
+
for (const [key, value] of Object.entries(record)) processed[key] = this.serializeValue(value);
|
|
586
|
+
return processed;
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Upsert multiple records in a batch operation
|
|
590
|
+
* @param tableName The table to insert into
|
|
591
|
+
* @param records The records to insert
|
|
592
|
+
*/
|
|
593
|
+
async batchUpsert({ tableName, records }) {
|
|
594
|
+
if (records.length === 0) return;
|
|
595
|
+
const fullTableName = this.getTableName(tableName);
|
|
596
|
+
try {
|
|
597
|
+
const batchSize = 50;
|
|
598
|
+
for (let i = 0; i < records.length; i += batchSize) {
|
|
599
|
+
const recordsToInsert = records.slice(i, i + batchSize);
|
|
600
|
+
if (recordsToInsert.length > 0) {
|
|
601
|
+
const filteredRecords = await Promise.all(recordsToInsert.map((r) => this.filterRecordToKnownColumns(fullTableName, r || {})));
|
|
602
|
+
for (const record of filteredRecords) {
|
|
603
|
+
const columns = Object.keys(record);
|
|
604
|
+
if (columns.length === 0) continue;
|
|
605
|
+
const values = columns.map((col) => {
|
|
606
|
+
const value = record[col];
|
|
607
|
+
return this.serializeValue(value);
|
|
608
|
+
});
|
|
609
|
+
const recordToUpsert = columns.reduce((acc, col) => {
|
|
610
|
+
if (col !== "createdAt") acc[col] = `excluded.${col}`;
|
|
611
|
+
return acc;
|
|
612
|
+
}, {});
|
|
613
|
+
const { sql, params } = createSqlBuilder().insert(fullTableName, columns, values, ["id"], recordToUpsert).build();
|
|
614
|
+
await this.executeQuery({
|
|
615
|
+
sql,
|
|
616
|
+
params
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
this.logger.debug(`Processed batch ${Math.floor(i / batchSize) + 1} of ${Math.ceil(records.length / batchSize)}`);
|
|
621
|
+
}
|
|
622
|
+
this.logger.debug(`Successfully batch upserted ${records.length} records into ${tableName}`);
|
|
623
|
+
} catch (error) {
|
|
624
|
+
throw new MastraError({
|
|
625
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "BATCH_UPSERT", "FAILED"),
|
|
626
|
+
domain: ErrorDomain.STORAGE,
|
|
627
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
628
|
+
text: `Failed to batch upsert into ${tableName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
629
|
+
details: { tableName }
|
|
630
|
+
}, error);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
725
633
|
};
|
|
726
|
-
|
|
727
|
-
|
|
634
|
+
//#endregion
|
|
635
|
+
//#region src/storage/domains/background-tasks/index.ts
|
|
728
636
|
function serializeJson(v) {
|
|
729
|
-
|
|
730
|
-
|
|
637
|
+
if (typeof v === "object" && v != null) return JSON.stringify(v);
|
|
638
|
+
return v ?? null;
|
|
731
639
|
}
|
|
732
640
|
function rowToTask(row) {
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
completedAt: row.completedAt ? new Date(row.completedAt) : void 0
|
|
764
|
-
};
|
|
641
|
+
const parseJson = (val) => {
|
|
642
|
+
if (val == null || val === "") return void 0;
|
|
643
|
+
if (typeof val === "string") try {
|
|
644
|
+
return JSON.parse(val);
|
|
645
|
+
} catch {
|
|
646
|
+
return val;
|
|
647
|
+
}
|
|
648
|
+
return val;
|
|
649
|
+
};
|
|
650
|
+
return {
|
|
651
|
+
id: row.id,
|
|
652
|
+
status: row.status,
|
|
653
|
+
toolName: row.tool_name,
|
|
654
|
+
toolCallId: row.tool_call_id,
|
|
655
|
+
args: parseJson(row.args) ?? {},
|
|
656
|
+
agentId: row.agent_id,
|
|
657
|
+
threadId: row.thread_id || void 0,
|
|
658
|
+
resourceId: row.resource_id || void 0,
|
|
659
|
+
runId: row.run_id ?? "",
|
|
660
|
+
result: parseJson(row.result),
|
|
661
|
+
error: parseJson(row.error),
|
|
662
|
+
suspendPayload: parseJson(row.suspend_payload),
|
|
663
|
+
retryCount: Number(row.retry_count ?? 0),
|
|
664
|
+
maxRetries: Number(row.max_retries ?? 0),
|
|
665
|
+
timeoutMs: Number(row.timeout_ms ?? 3e5),
|
|
666
|
+
createdAt: new Date(row.createdAt),
|
|
667
|
+
startedAt: row.startedAt ? new Date(row.startedAt) : void 0,
|
|
668
|
+
suspendedAt: row.suspendedAt ? new Date(row.suspendedAt) : void 0,
|
|
669
|
+
completedAt: row.completedAt ? new Date(row.completedAt) : void 0
|
|
670
|
+
};
|
|
765
671
|
}
|
|
766
672
|
var BackgroundTasksStorageD1 = class extends BackgroundTasksStorage {
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
673
|
+
#db;
|
|
674
|
+
constructor(config) {
|
|
675
|
+
super();
|
|
676
|
+
const resolved = resolveD1Config(config);
|
|
677
|
+
this.#db = new D1DB(resolved);
|
|
678
|
+
}
|
|
679
|
+
async init() {
|
|
680
|
+
await this.#db.createTable({
|
|
681
|
+
tableName: TABLE_BACKGROUND_TASKS,
|
|
682
|
+
schema: TABLE_SCHEMAS[TABLE_BACKGROUND_TASKS]
|
|
683
|
+
});
|
|
684
|
+
await this.#db.alterTable({
|
|
685
|
+
tableName: TABLE_BACKGROUND_TASKS,
|
|
686
|
+
schema: TABLE_SCHEMAS[TABLE_BACKGROUND_TASKS],
|
|
687
|
+
ifNotExists: ["suspend_payload", "suspendedAt"]
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
async dangerouslyClearAll() {
|
|
691
|
+
await this.#db.clearTable({ tableName: TABLE_BACKGROUND_TASKS });
|
|
692
|
+
}
|
|
693
|
+
async createTask(task) {
|
|
694
|
+
const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
|
|
695
|
+
const { sql, params } = createSqlBuilder().insert(fullTableName, [
|
|
696
|
+
"id",
|
|
697
|
+
"tool_call_id",
|
|
698
|
+
"tool_name",
|
|
699
|
+
"agent_id",
|
|
700
|
+
"thread_id",
|
|
701
|
+
"resource_id",
|
|
702
|
+
"run_id",
|
|
703
|
+
"status",
|
|
704
|
+
"args",
|
|
705
|
+
"result",
|
|
706
|
+
"error",
|
|
707
|
+
"suspend_payload",
|
|
708
|
+
"retry_count",
|
|
709
|
+
"max_retries",
|
|
710
|
+
"timeout_ms",
|
|
711
|
+
"createdAt",
|
|
712
|
+
"startedAt",
|
|
713
|
+
"suspendedAt",
|
|
714
|
+
"completedAt"
|
|
715
|
+
], [
|
|
716
|
+
task.id,
|
|
717
|
+
task.toolCallId,
|
|
718
|
+
task.toolName,
|
|
719
|
+
task.agentId,
|
|
720
|
+
task.threadId ?? null,
|
|
721
|
+
task.resourceId ?? null,
|
|
722
|
+
task.runId,
|
|
723
|
+
task.status,
|
|
724
|
+
serializeJson(task.args),
|
|
725
|
+
serializeJson(task.result),
|
|
726
|
+
serializeJson(task.error),
|
|
727
|
+
serializeJson(task.suspendPayload),
|
|
728
|
+
task.retryCount,
|
|
729
|
+
task.maxRetries,
|
|
730
|
+
task.timeoutMs,
|
|
731
|
+
task.createdAt.toISOString(),
|
|
732
|
+
task.startedAt?.toISOString() ?? null,
|
|
733
|
+
task.suspendedAt?.toISOString() ?? null,
|
|
734
|
+
task.completedAt?.toISOString() ?? null
|
|
735
|
+
]).build();
|
|
736
|
+
await this.#db.executeQuery({
|
|
737
|
+
sql,
|
|
738
|
+
params
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
async updateTask(taskId, update) {
|
|
742
|
+
const sets = [];
|
|
743
|
+
const params = [];
|
|
744
|
+
if ("status" in update) {
|
|
745
|
+
sets.push("status = ?");
|
|
746
|
+
params.push(update.status);
|
|
747
|
+
}
|
|
748
|
+
if ("result" in update) {
|
|
749
|
+
sets.push("result = ?");
|
|
750
|
+
params.push(serializeJson(update.result));
|
|
751
|
+
}
|
|
752
|
+
if ("error" in update) {
|
|
753
|
+
sets.push("error = ?");
|
|
754
|
+
params.push(serializeJson(update.error));
|
|
755
|
+
}
|
|
756
|
+
if ("suspendPayload" in update) {
|
|
757
|
+
sets.push("suspend_payload = ?");
|
|
758
|
+
params.push(serializeJson(update.suspendPayload));
|
|
759
|
+
}
|
|
760
|
+
if ("retryCount" in update) {
|
|
761
|
+
sets.push("retry_count = ?");
|
|
762
|
+
params.push(update.retryCount);
|
|
763
|
+
}
|
|
764
|
+
if ("startedAt" in update) {
|
|
765
|
+
sets.push("startedAt = ?");
|
|
766
|
+
params.push(update.startedAt?.toISOString() ?? null);
|
|
767
|
+
}
|
|
768
|
+
if ("suspendedAt" in update) {
|
|
769
|
+
sets.push("suspendedAt = ?");
|
|
770
|
+
params.push(update.suspendedAt?.toISOString() ?? null);
|
|
771
|
+
}
|
|
772
|
+
if ("completedAt" in update) {
|
|
773
|
+
sets.push("completedAt = ?");
|
|
774
|
+
params.push(update.completedAt?.toISOString() ?? null);
|
|
775
|
+
}
|
|
776
|
+
if (sets.length === 0) return;
|
|
777
|
+
params.push(taskId);
|
|
778
|
+
const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
|
|
779
|
+
await this.#db.executeQuery({
|
|
780
|
+
sql: `UPDATE ${fullTableName} SET ${sets.join(", ")} WHERE id = ?`,
|
|
781
|
+
params
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
async getTask(taskId) {
|
|
785
|
+
const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
|
|
786
|
+
const { sql, params } = createSqlBuilder().select("*").from(fullTableName).where("id = ?", taskId).build();
|
|
787
|
+
const row = await this.#db.executeQuery({
|
|
788
|
+
sql,
|
|
789
|
+
params,
|
|
790
|
+
first: true
|
|
791
|
+
});
|
|
792
|
+
return row ? rowToTask(row) : null;
|
|
793
|
+
}
|
|
794
|
+
async listTasks(filter) {
|
|
795
|
+
const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
|
|
796
|
+
let builder = createSqlBuilder().select("*").from(fullTableName);
|
|
797
|
+
let countBuilder = createSqlBuilder().count().from(fullTableName);
|
|
798
|
+
if (filter.status) {
|
|
799
|
+
const statuses = Array.isArray(filter.status) ? filter.status : [filter.status];
|
|
800
|
+
builder = builder.where(`status IN (${statuses.map(() => "?").join(",")})`, ...statuses);
|
|
801
|
+
countBuilder = countBuilder.where(`status IN (${statuses.map(() => "?").join(",")})`, ...statuses);
|
|
802
|
+
}
|
|
803
|
+
if (filter.agentId) {
|
|
804
|
+
builder = builder.whereAnd("agent_id = ?", filter.agentId);
|
|
805
|
+
countBuilder = countBuilder.whereAnd("agent_id = ?", filter.agentId);
|
|
806
|
+
}
|
|
807
|
+
if (filter.threadId) {
|
|
808
|
+
builder = builder.whereAnd("thread_id = ?", filter.threadId);
|
|
809
|
+
countBuilder = countBuilder.whereAnd("thread_id = ?", filter.threadId);
|
|
810
|
+
}
|
|
811
|
+
if (filter.runId) {
|
|
812
|
+
builder = builder.whereAnd("run_id = ?", filter.runId);
|
|
813
|
+
countBuilder = countBuilder.whereAnd("run_id = ?", filter.runId);
|
|
814
|
+
}
|
|
815
|
+
if (filter.toolName) {
|
|
816
|
+
builder = builder.whereAnd("tool_name = ?", filter.toolName);
|
|
817
|
+
countBuilder = countBuilder.whereAnd("tool_name = ?", filter.toolName);
|
|
818
|
+
}
|
|
819
|
+
if (filter.toolCallId) {
|
|
820
|
+
builder = builder.whereAnd("tool_call_id = ?", filter.toolCallId);
|
|
821
|
+
countBuilder = countBuilder.whereAnd("tool_call_id = ?", filter.toolCallId);
|
|
822
|
+
}
|
|
823
|
+
const dateCol = filter.dateFilterBy === "startedAt" ? "startedAt" : filter.dateFilterBy === "suspendedAt" ? "suspendedAt" : filter.dateFilterBy === "completedAt" ? "completedAt" : "createdAt";
|
|
824
|
+
if (filter.fromDate) {
|
|
825
|
+
builder = builder.whereAnd(`${dateCol} >= ?`, filter.fromDate.toISOString());
|
|
826
|
+
countBuilder = countBuilder.whereAnd(`${dateCol} >= ?`, filter.fromDate.toISOString());
|
|
827
|
+
}
|
|
828
|
+
if (filter.toDate) {
|
|
829
|
+
builder = builder.whereAnd(`${dateCol} < ?`, filter.toDate.toISOString());
|
|
830
|
+
countBuilder = countBuilder.whereAnd(`${dateCol} < ?`, filter.toDate.toISOString());
|
|
831
|
+
}
|
|
832
|
+
const { sql: countSql, params: countParams } = countBuilder.build();
|
|
833
|
+
const countRow = await this.#db.executeQuery({
|
|
834
|
+
sql: countSql,
|
|
835
|
+
params: countParams,
|
|
836
|
+
first: true
|
|
837
|
+
});
|
|
838
|
+
const total = Number(countRow?.count ?? 0);
|
|
839
|
+
const orderCol = filter.orderBy === "startedAt" ? "startedAt" : filter.orderBy === "suspendedAt" ? "suspendedAt" : filter.orderBy === "completedAt" ? "completedAt" : "createdAt";
|
|
840
|
+
builder = builder.orderBy(orderCol, filter.orderDirection === "desc" ? "DESC" : "ASC");
|
|
841
|
+
if (filter.perPage != null) {
|
|
842
|
+
builder = builder.limit(filter.perPage);
|
|
843
|
+
if (filter.page != null) builder = builder.offset(filter.page * filter.perPage);
|
|
844
|
+
}
|
|
845
|
+
const { sql, params } = builder.build();
|
|
846
|
+
return {
|
|
847
|
+
tasks: (await this.#db.executeQuery({
|
|
848
|
+
sql,
|
|
849
|
+
params
|
|
850
|
+
})).map(rowToTask),
|
|
851
|
+
total
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
async deleteTask(taskId) {
|
|
855
|
+
const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
|
|
856
|
+
await this.#db.executeQuery({
|
|
857
|
+
sql: `DELETE FROM ${fullTableName} WHERE id = ?`,
|
|
858
|
+
params: [taskId]
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
async deleteTasks(filter) {
|
|
862
|
+
const conditions = [];
|
|
863
|
+
const params = [];
|
|
864
|
+
if (filter.status) {
|
|
865
|
+
const statuses = Array.isArray(filter.status) ? filter.status : [filter.status];
|
|
866
|
+
conditions.push(`status IN (${statuses.map(() => "?").join(",")})`);
|
|
867
|
+
params.push(...statuses);
|
|
868
|
+
}
|
|
869
|
+
const dateCol = filter.dateFilterBy === "startedAt" ? "startedAt" : filter.dateFilterBy === "suspendedAt" ? "suspendedAt" : filter.dateFilterBy === "completedAt" ? "completedAt" : "createdAt";
|
|
870
|
+
if (filter.fromDate) {
|
|
871
|
+
conditions.push(`${dateCol} >= ?`);
|
|
872
|
+
params.push(filter.fromDate.toISOString());
|
|
873
|
+
}
|
|
874
|
+
if (filter.toDate) {
|
|
875
|
+
conditions.push(`${dateCol} < ?`);
|
|
876
|
+
params.push(filter.toDate.toISOString());
|
|
877
|
+
}
|
|
878
|
+
if (filter.agentId) {
|
|
879
|
+
conditions.push("agent_id = ?");
|
|
880
|
+
params.push(filter.agentId);
|
|
881
|
+
}
|
|
882
|
+
if (filter.runId) {
|
|
883
|
+
conditions.push("run_id = ?");
|
|
884
|
+
params.push(filter.runId);
|
|
885
|
+
}
|
|
886
|
+
if (filter.toolCallId) {
|
|
887
|
+
conditions.push("tool_call_id = ?");
|
|
888
|
+
params.push(filter.toolCallId);
|
|
889
|
+
}
|
|
890
|
+
if (conditions.length === 0) return;
|
|
891
|
+
const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
|
|
892
|
+
await this.#db.executeQuery({
|
|
893
|
+
sql: `DELETE FROM ${fullTableName} WHERE ${conditions.join(" AND ")}`,
|
|
894
|
+
params
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
async getRunningCount() {
|
|
898
|
+
const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
|
|
899
|
+
const row = await this.#db.executeQuery({
|
|
900
|
+
sql: `SELECT COUNT(*) as count FROM ${fullTableName} WHERE status = 'running'`,
|
|
901
|
+
params: [],
|
|
902
|
+
first: true
|
|
903
|
+
});
|
|
904
|
+
return Number(row?.count ?? 0);
|
|
905
|
+
}
|
|
906
|
+
async getRunningCountByAgent(agentId) {
|
|
907
|
+
const fullTableName = this.#db.getTableName(TABLE_BACKGROUND_TASKS);
|
|
908
|
+
const row = await this.#db.executeQuery({
|
|
909
|
+
sql: `SELECT COUNT(*) as count FROM ${fullTableName} WHERE status = 'running' AND agent_id = ?`,
|
|
910
|
+
params: [agentId],
|
|
911
|
+
first: true
|
|
912
|
+
});
|
|
913
|
+
return Number(row?.count ?? 0);
|
|
914
|
+
}
|
|
993
915
|
};
|
|
916
|
+
//#endregion
|
|
917
|
+
//#region src/storage/domains/memory/index.ts
|
|
918
|
+
function addSqliteMetadataFilter(conditions, params, metadataFilter) {
|
|
919
|
+
if (!metadataFilter) return;
|
|
920
|
+
for (const [key, value] of Object.entries(metadataFilter)) {
|
|
921
|
+
const path = `$.metadata.${key}`;
|
|
922
|
+
conditions.push(`CASE WHEN json_valid(content) THEN json_type(content, ?) IS NOT NULL ELSE 0 END`);
|
|
923
|
+
params.push(path);
|
|
924
|
+
addSqliteMetadataValuePredicate(conditions, params, path, value);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
function addSqliteMetadataValuePredicate(conditions, params, path, value) {
|
|
928
|
+
if (value === null) {
|
|
929
|
+
conditions.push(`CASE WHEN json_valid(content) THEN json_type(content, ?) = 'null' ELSE 0 END`);
|
|
930
|
+
params.push(path);
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
if (typeof value === "string") {
|
|
934
|
+
conditions.push(`CASE WHEN json_valid(content) THEN json_type(content, ?) = 'text' AND json_extract(content, ?) = ? ELSE 0 END`);
|
|
935
|
+
params.push(path, path, value);
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
if (typeof value === "number") {
|
|
939
|
+
conditions.push(`CASE WHEN json_valid(content) THEN json_type(content, ?) IN ('integer', 'real') AND json_extract(content, ?) = ? ELSE 0 END`);
|
|
940
|
+
params.push(path, path, value);
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
conditions.push(`CASE WHEN json_valid(content) THEN json_type(content, ?) = ? AND json_extract(content, ?) = ? ELSE 0 END`);
|
|
944
|
+
params.push(path, value ? "true" : "false", path, value ? 1 : 0);
|
|
945
|
+
}
|
|
994
946
|
var MemoryStorageD1 = class extends MemoryStorage {
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
}
|
|
1461
|
-
async _getIncludedMessages(include) {
|
|
1462
|
-
if (!include || include.length === 0) return null;
|
|
1463
|
-
const tableName = this.#db.getTableName(TABLE_MESSAGES);
|
|
1464
|
-
const targetIds = include.map((inc) => inc.id).filter(Boolean);
|
|
1465
|
-
if (targetIds.length === 0) return null;
|
|
1466
|
-
const idPlaceholders = targetIds.map(() => "?").join(", ");
|
|
1467
|
-
const targetResult = await this.#db.executeQuery({
|
|
1468
|
-
sql: `SELECT id, thread_id, createdAt FROM ${tableName} WHERE id IN (${idPlaceholders})`,
|
|
1469
|
-
params: targetIds
|
|
1470
|
-
});
|
|
1471
|
-
if (!Array.isArray(targetResult) || targetResult.length === 0) return null;
|
|
1472
|
-
const targetMap = new Map(
|
|
1473
|
-
targetResult.map((r) => [r.id, { threadId: r.thread_id, createdAt: r.createdAt }])
|
|
1474
|
-
);
|
|
1475
|
-
const MAX_UNION_TERMS = 5;
|
|
1476
|
-
const unionQueries = [];
|
|
1477
|
-
const unionParams = [];
|
|
1478
|
-
const allMessages = [];
|
|
1479
|
-
const flushBatch = async () => {
|
|
1480
|
-
if (unionQueries.length === 0) return;
|
|
1481
|
-
const sql = unionQueries.length === 1 ? unionQueries[0] : `${unionQueries.join(" UNION ALL ")} ORDER BY createdAt ASC, id ASC`;
|
|
1482
|
-
const result = await this.#db.executeQuery({ sql, params: unionParams });
|
|
1483
|
-
if (Array.isArray(result)) {
|
|
1484
|
-
allMessages.push(...result);
|
|
1485
|
-
}
|
|
1486
|
-
unionQueries.length = 0;
|
|
1487
|
-
unionParams.length = 0;
|
|
1488
|
-
};
|
|
1489
|
-
for (const inc of include) {
|
|
1490
|
-
const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
|
|
1491
|
-
const target = targetMap.get(id);
|
|
1492
|
-
if (!target) continue;
|
|
1493
|
-
const termsNeeded = withNextMessages > 0 ? 2 : 1;
|
|
1494
|
-
if (unionQueries.length + termsNeeded > MAX_UNION_TERMS) {
|
|
1495
|
-
await flushBatch();
|
|
1496
|
-
}
|
|
1497
|
-
unionQueries.push(`SELECT * FROM (
|
|
947
|
+
#db;
|
|
948
|
+
constructor(config) {
|
|
949
|
+
super();
|
|
950
|
+
this.#db = new D1DB(resolveD1Config(config));
|
|
951
|
+
}
|
|
952
|
+
async init() {
|
|
953
|
+
await this.#db.createTable({
|
|
954
|
+
tableName: TABLE_THREADS,
|
|
955
|
+
schema: TABLE_SCHEMAS[TABLE_THREADS]
|
|
956
|
+
});
|
|
957
|
+
await this.#db.createTable({
|
|
958
|
+
tableName: TABLE_MESSAGES,
|
|
959
|
+
schema: TABLE_SCHEMAS[TABLE_MESSAGES]
|
|
960
|
+
});
|
|
961
|
+
await this.#db.createTable({
|
|
962
|
+
tableName: TABLE_RESOURCES,
|
|
963
|
+
schema: TABLE_SCHEMAS[TABLE_RESOURCES]
|
|
964
|
+
});
|
|
965
|
+
await this.#db.alterTable({
|
|
966
|
+
tableName: TABLE_MESSAGES,
|
|
967
|
+
schema: TABLE_SCHEMAS[TABLE_MESSAGES],
|
|
968
|
+
ifNotExists: ["resourceId"]
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
async dangerouslyClearAll() {
|
|
972
|
+
await this.#db.clearTable({ tableName: TABLE_MESSAGES });
|
|
973
|
+
await this.#db.clearTable({ tableName: TABLE_THREADS });
|
|
974
|
+
await this.#db.clearTable({ tableName: TABLE_RESOURCES });
|
|
975
|
+
}
|
|
976
|
+
async getResourceById({ resourceId }) {
|
|
977
|
+
const resource = await this.#db.load({
|
|
978
|
+
tableName: TABLE_RESOURCES,
|
|
979
|
+
keys: { id: resourceId }
|
|
980
|
+
});
|
|
981
|
+
if (!resource) return null;
|
|
982
|
+
try {
|
|
983
|
+
return {
|
|
984
|
+
...resource,
|
|
985
|
+
createdAt: ensureDate(resource.createdAt),
|
|
986
|
+
updatedAt: ensureDate(resource.updatedAt),
|
|
987
|
+
metadata: typeof resource.metadata === "string" ? JSON.parse(resource.metadata || "{}") : resource.metadata
|
|
988
|
+
};
|
|
989
|
+
} catch (error) {
|
|
990
|
+
const mastraError = new MastraError({
|
|
991
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "GET_RESOURCE_BY_ID", "FAILED"),
|
|
992
|
+
domain: ErrorDomain.STORAGE,
|
|
993
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
994
|
+
text: `Error processing resource ${resourceId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
995
|
+
details: { resourceId }
|
|
996
|
+
}, error);
|
|
997
|
+
this.logger?.error(mastraError.toString());
|
|
998
|
+
this.logger?.trackException(mastraError);
|
|
999
|
+
return null;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
async saveResource({ resource }) {
|
|
1003
|
+
const fullTableName = this.#db.getTableName(TABLE_RESOURCES);
|
|
1004
|
+
const resourceToSave = {
|
|
1005
|
+
id: resource.id,
|
|
1006
|
+
workingMemory: resource.workingMemory,
|
|
1007
|
+
metadata: resource.metadata ? JSON.stringify(resource.metadata) : null,
|
|
1008
|
+
createdAt: resource.createdAt,
|
|
1009
|
+
updatedAt: resource.updatedAt
|
|
1010
|
+
};
|
|
1011
|
+
const processedRecord = await this.#db.processRecord(resourceToSave);
|
|
1012
|
+
const columns = Object.keys(processedRecord);
|
|
1013
|
+
const values = Object.values(processedRecord);
|
|
1014
|
+
const { sql, params } = createSqlBuilder().insert(fullTableName, columns, values, ["id"], {
|
|
1015
|
+
workingMemory: "excluded.workingMemory",
|
|
1016
|
+
metadata: "excluded.metadata",
|
|
1017
|
+
createdAt: "excluded.createdAt",
|
|
1018
|
+
updatedAt: "excluded.updatedAt"
|
|
1019
|
+
}).build();
|
|
1020
|
+
try {
|
|
1021
|
+
await this.#db.executeQuery({
|
|
1022
|
+
sql,
|
|
1023
|
+
params
|
|
1024
|
+
});
|
|
1025
|
+
return resource;
|
|
1026
|
+
} catch (error) {
|
|
1027
|
+
throw new MastraError({
|
|
1028
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "SAVE_RESOURCE", "FAILED"),
|
|
1029
|
+
domain: ErrorDomain.STORAGE,
|
|
1030
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1031
|
+
text: `Failed to save resource to ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1032
|
+
details: { resourceId: resource.id }
|
|
1033
|
+
}, error);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
async updateResource({ resourceId, workingMemory, metadata }) {
|
|
1037
|
+
const existingResource = await this.getResourceById({ resourceId });
|
|
1038
|
+
if (!existingResource) {
|
|
1039
|
+
const newResource = {
|
|
1040
|
+
id: resourceId,
|
|
1041
|
+
workingMemory,
|
|
1042
|
+
metadata: metadata || {},
|
|
1043
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
1044
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1045
|
+
};
|
|
1046
|
+
return this.saveResource({ resource: newResource });
|
|
1047
|
+
}
|
|
1048
|
+
const updatedAt = /* @__PURE__ */ new Date();
|
|
1049
|
+
const updatedResource = {
|
|
1050
|
+
...existingResource,
|
|
1051
|
+
workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
|
|
1052
|
+
metadata: {
|
|
1053
|
+
...existingResource.metadata,
|
|
1054
|
+
...metadata
|
|
1055
|
+
},
|
|
1056
|
+
updatedAt
|
|
1057
|
+
};
|
|
1058
|
+
const fullTableName = this.#db.getTableName(TABLE_RESOURCES);
|
|
1059
|
+
const columns = [
|
|
1060
|
+
"workingMemory",
|
|
1061
|
+
"metadata",
|
|
1062
|
+
"updatedAt"
|
|
1063
|
+
];
|
|
1064
|
+
const values = [
|
|
1065
|
+
updatedResource.workingMemory,
|
|
1066
|
+
JSON.stringify(updatedResource.metadata),
|
|
1067
|
+
updatedAt.toISOString()
|
|
1068
|
+
];
|
|
1069
|
+
const { sql, params } = createSqlBuilder().update(fullTableName, columns, values).where("id = ?", resourceId).build();
|
|
1070
|
+
try {
|
|
1071
|
+
await this.#db.executeQuery({
|
|
1072
|
+
sql,
|
|
1073
|
+
params
|
|
1074
|
+
});
|
|
1075
|
+
return updatedResource;
|
|
1076
|
+
} catch (error) {
|
|
1077
|
+
throw new MastraError({
|
|
1078
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "UPDATE_RESOURCE", "FAILED"),
|
|
1079
|
+
domain: ErrorDomain.STORAGE,
|
|
1080
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1081
|
+
text: `Failed to update resource ${resourceId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1082
|
+
details: { resourceId }
|
|
1083
|
+
}, error);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
async getThreadById({ threadId, resourceId }) {
|
|
1087
|
+
const thread = await this.#db.load({
|
|
1088
|
+
tableName: TABLE_THREADS,
|
|
1089
|
+
keys: { id: threadId }
|
|
1090
|
+
});
|
|
1091
|
+
if (!thread || resourceId !== void 0 && thread.resourceId !== resourceId) return null;
|
|
1092
|
+
try {
|
|
1093
|
+
return {
|
|
1094
|
+
...thread,
|
|
1095
|
+
createdAt: ensureDate(thread.createdAt),
|
|
1096
|
+
updatedAt: ensureDate(thread.updatedAt),
|
|
1097
|
+
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata || "{}") : thread.metadata || {}
|
|
1098
|
+
};
|
|
1099
|
+
} catch (error) {
|
|
1100
|
+
const mastraError = new MastraError({
|
|
1101
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "GET_THREAD_BY_ID", "FAILED"),
|
|
1102
|
+
domain: ErrorDomain.STORAGE,
|
|
1103
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1104
|
+
text: `Error processing thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1105
|
+
details: { threadId }
|
|
1106
|
+
}, error);
|
|
1107
|
+
this.logger?.error(mastraError.toString());
|
|
1108
|
+
this.logger?.trackException(mastraError);
|
|
1109
|
+
return null;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
async listThreads(args) {
|
|
1113
|
+
const { page = 0, perPage: perPageInput, orderBy, filter } = args;
|
|
1114
|
+
try {
|
|
1115
|
+
this.validatePaginationInput(page, perPageInput ?? 100);
|
|
1116
|
+
} catch (error) {
|
|
1117
|
+
throw new MastraError({
|
|
1118
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "LIST_THREADS", "INVALID_PAGE"),
|
|
1119
|
+
domain: ErrorDomain.STORAGE,
|
|
1120
|
+
category: ErrorCategory.USER,
|
|
1121
|
+
details: {
|
|
1122
|
+
page,
|
|
1123
|
+
...perPageInput !== void 0 && { perPage: perPageInput }
|
|
1124
|
+
}
|
|
1125
|
+
}, error instanceof Error ? error : /* @__PURE__ */ new Error("Invalid pagination parameters"));
|
|
1126
|
+
}
|
|
1127
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
1128
|
+
try {
|
|
1129
|
+
this.validateMetadataKeys(filter?.metadata);
|
|
1130
|
+
} catch (error) {
|
|
1131
|
+
throw new MastraError({
|
|
1132
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "LIST_THREADS", "INVALID_METADATA_KEY"),
|
|
1133
|
+
domain: ErrorDomain.STORAGE,
|
|
1134
|
+
category: ErrorCategory.USER,
|
|
1135
|
+
details: { metadataKeys: filter?.metadata ? Object.keys(filter.metadata).join(", ") : "" }
|
|
1136
|
+
}, error instanceof Error ? error : /* @__PURE__ */ new Error("Invalid metadata key"));
|
|
1137
|
+
}
|
|
1138
|
+
const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1139
|
+
const { field, direction } = this.parseOrderBy(orderBy);
|
|
1140
|
+
const fullTableName = this.#db.getTableName(TABLE_THREADS);
|
|
1141
|
+
const mapRowToStorageThreadType = (row) => ({
|
|
1142
|
+
...row,
|
|
1143
|
+
createdAt: ensureDate(row.createdAt),
|
|
1144
|
+
updatedAt: ensureDate(row.updatedAt),
|
|
1145
|
+
metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata || "{}") : row.metadata || {}
|
|
1146
|
+
});
|
|
1147
|
+
try {
|
|
1148
|
+
let countQuery = createSqlBuilder().count().from(fullTableName);
|
|
1149
|
+
let selectQuery = createSqlBuilder().select("*").from(fullTableName);
|
|
1150
|
+
if (filter?.resourceId) {
|
|
1151
|
+
countQuery = countQuery.whereAnd("resourceId = ?", filter.resourceId);
|
|
1152
|
+
selectQuery = selectQuery.whereAnd("resourceId = ?", filter.resourceId);
|
|
1153
|
+
}
|
|
1154
|
+
if (filter?.metadata && Object.keys(filter.metadata).length > 0) for (const [key, value] of Object.entries(filter.metadata)) {
|
|
1155
|
+
if (value !== null && typeof value === "object") throw new MastraError({
|
|
1156
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "LIST_THREADS", "INVALID_METADATA_VALUE"),
|
|
1157
|
+
domain: ErrorDomain.STORAGE,
|
|
1158
|
+
category: ErrorCategory.USER,
|
|
1159
|
+
text: `Metadata filter value for key "${key}" must be a scalar type (string, number, boolean, or null), got ${Array.isArray(value) ? "array" : "object"}`,
|
|
1160
|
+
details: {
|
|
1161
|
+
key,
|
|
1162
|
+
valueType: Array.isArray(value) ? "array" : "object"
|
|
1163
|
+
}
|
|
1164
|
+
}, /* @__PURE__ */ new Error("Invalid metadata filter value type"));
|
|
1165
|
+
if (value === null) {
|
|
1166
|
+
const condition = `json_extract(metadata, '$.${key}') IS NULL`;
|
|
1167
|
+
countQuery = countQuery.whereAnd(condition);
|
|
1168
|
+
selectQuery = selectQuery.whereAnd(condition);
|
|
1169
|
+
} else {
|
|
1170
|
+
const condition = `json_extract(metadata, '$.${key}') = ?`;
|
|
1171
|
+
const filterValue = value;
|
|
1172
|
+
countQuery = countQuery.whereAnd(condition, filterValue);
|
|
1173
|
+
selectQuery = selectQuery.whereAnd(condition, filterValue);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
const countResult = await this.#db.executeQuery(countQuery.build());
|
|
1177
|
+
const total = Number(countResult?.[0]?.count ?? 0);
|
|
1178
|
+
if (total === 0) return {
|
|
1179
|
+
threads: [],
|
|
1180
|
+
total: 0,
|
|
1181
|
+
page,
|
|
1182
|
+
perPage: perPageForResponse,
|
|
1183
|
+
hasMore: false
|
|
1184
|
+
};
|
|
1185
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
1186
|
+
selectQuery = selectQuery.orderBy(field, direction).limit(limitValue).offset(offset);
|
|
1187
|
+
return {
|
|
1188
|
+
threads: (await this.#db.executeQuery(selectQuery.build())).map(mapRowToStorageThreadType),
|
|
1189
|
+
total,
|
|
1190
|
+
page,
|
|
1191
|
+
perPage: perPageForResponse,
|
|
1192
|
+
hasMore: perPageInput === false ? false : offset + perPage < total
|
|
1193
|
+
};
|
|
1194
|
+
} catch (error) {
|
|
1195
|
+
if (error instanceof MastraError && error.category === ErrorCategory.USER) throw error;
|
|
1196
|
+
const mastraError = new MastraError({
|
|
1197
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "LIST_THREADS", "FAILED"),
|
|
1198
|
+
domain: ErrorDomain.STORAGE,
|
|
1199
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1200
|
+
text: `Error listing threads: ${error instanceof Error ? error.message : String(error)}`,
|
|
1201
|
+
details: {
|
|
1202
|
+
...filter?.resourceId && { resourceId: filter.resourceId },
|
|
1203
|
+
hasMetadataFilter: !!filter?.metadata
|
|
1204
|
+
}
|
|
1205
|
+
}, error);
|
|
1206
|
+
this.logger?.error(mastraError.toString());
|
|
1207
|
+
this.logger?.trackException(mastraError);
|
|
1208
|
+
return {
|
|
1209
|
+
threads: [],
|
|
1210
|
+
total: 0,
|
|
1211
|
+
page,
|
|
1212
|
+
perPage: perPageForResponse,
|
|
1213
|
+
hasMore: false
|
|
1214
|
+
};
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
async saveThread({ thread }) {
|
|
1218
|
+
const fullTableName = this.#db.getTableName(TABLE_THREADS);
|
|
1219
|
+
const threadToSave = {
|
|
1220
|
+
id: thread.id,
|
|
1221
|
+
resourceId: thread.resourceId,
|
|
1222
|
+
title: thread.title,
|
|
1223
|
+
metadata: thread.metadata ? JSON.stringify(thread.metadata) : null,
|
|
1224
|
+
createdAt: thread.createdAt.toISOString(),
|
|
1225
|
+
updatedAt: thread.updatedAt.toISOString()
|
|
1226
|
+
};
|
|
1227
|
+
const processedRecord = await this.#db.processRecord(threadToSave);
|
|
1228
|
+
const columns = Object.keys(processedRecord);
|
|
1229
|
+
const values = Object.values(processedRecord);
|
|
1230
|
+
const { sql, params } = createSqlBuilder().insert(fullTableName, columns, values, ["id"], {
|
|
1231
|
+
resourceId: "excluded.resourceId",
|
|
1232
|
+
title: "excluded.title",
|
|
1233
|
+
metadata: "excluded.metadata",
|
|
1234
|
+
createdAt: "excluded.createdAt",
|
|
1235
|
+
updatedAt: "excluded.updatedAt"
|
|
1236
|
+
}).build();
|
|
1237
|
+
try {
|
|
1238
|
+
await this.#db.executeQuery({
|
|
1239
|
+
sql,
|
|
1240
|
+
params
|
|
1241
|
+
});
|
|
1242
|
+
return thread;
|
|
1243
|
+
} catch (error) {
|
|
1244
|
+
throw new MastraError({
|
|
1245
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "SAVE_THREAD", "FAILED"),
|
|
1246
|
+
domain: ErrorDomain.STORAGE,
|
|
1247
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1248
|
+
text: `Failed to save thread to ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1249
|
+
details: { threadId: thread.id }
|
|
1250
|
+
}, error);
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
async updateThread({ id, title, metadata }) {
|
|
1254
|
+
const thread = await this.getThreadById({ threadId: id });
|
|
1255
|
+
try {
|
|
1256
|
+
if (!thread) throw new Error(`Thread ${id} not found`);
|
|
1257
|
+
const fullTableName = this.#db.getTableName(TABLE_THREADS);
|
|
1258
|
+
const mergedMetadata = {
|
|
1259
|
+
...typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
1260
|
+
...metadata
|
|
1261
|
+
};
|
|
1262
|
+
const updatedAt = /* @__PURE__ */ new Date();
|
|
1263
|
+
const columns = [
|
|
1264
|
+
"title",
|
|
1265
|
+
"metadata",
|
|
1266
|
+
"updatedAt"
|
|
1267
|
+
];
|
|
1268
|
+
const values = [
|
|
1269
|
+
title,
|
|
1270
|
+
JSON.stringify(mergedMetadata),
|
|
1271
|
+
updatedAt.toISOString()
|
|
1272
|
+
];
|
|
1273
|
+
const { sql, params } = createSqlBuilder().update(fullTableName, columns, values).where("id = ?", id).build();
|
|
1274
|
+
await this.#db.executeQuery({
|
|
1275
|
+
sql,
|
|
1276
|
+
params
|
|
1277
|
+
});
|
|
1278
|
+
return {
|
|
1279
|
+
...thread,
|
|
1280
|
+
title,
|
|
1281
|
+
metadata: {
|
|
1282
|
+
...typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
1283
|
+
...metadata
|
|
1284
|
+
},
|
|
1285
|
+
updatedAt
|
|
1286
|
+
};
|
|
1287
|
+
} catch (error) {
|
|
1288
|
+
throw new MastraError({
|
|
1289
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "UPDATE_THREAD", "FAILED"),
|
|
1290
|
+
domain: ErrorDomain.STORAGE,
|
|
1291
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1292
|
+
text: `Failed to update thread ${id}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1293
|
+
details: { threadId: id }
|
|
1294
|
+
}, error);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
async deleteThread({ threadId }) {
|
|
1298
|
+
const fullTableName = this.#db.getTableName(TABLE_THREADS);
|
|
1299
|
+
try {
|
|
1300
|
+
const { sql: threadSql, params: threadParams } = createSqlBuilder().delete(fullTableName).where("id = ?", threadId).build();
|
|
1301
|
+
await this.#db.executeQuery({
|
|
1302
|
+
sql: threadSql,
|
|
1303
|
+
params: threadParams
|
|
1304
|
+
});
|
|
1305
|
+
const messagesTableName = this.#db.getTableName(TABLE_MESSAGES);
|
|
1306
|
+
const { sql: messagesSql, params: messagesParams } = createSqlBuilder().delete(messagesTableName).where("thread_id = ?", threadId).build();
|
|
1307
|
+
await this.#db.executeQuery({
|
|
1308
|
+
sql: messagesSql,
|
|
1309
|
+
params: messagesParams
|
|
1310
|
+
});
|
|
1311
|
+
} catch (error) {
|
|
1312
|
+
throw new MastraError({
|
|
1313
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "DELETE_THREAD", "FAILED"),
|
|
1314
|
+
domain: ErrorDomain.STORAGE,
|
|
1315
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1316
|
+
text: `Failed to delete thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1317
|
+
details: { threadId }
|
|
1318
|
+
}, error);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
async saveMessages(args) {
|
|
1322
|
+
const { messages } = args;
|
|
1323
|
+
if (messages.length === 0) return { messages: [] };
|
|
1324
|
+
try {
|
|
1325
|
+
const now = /* @__PURE__ */ new Date();
|
|
1326
|
+
const threadId = messages[0]?.threadId;
|
|
1327
|
+
for (const [i, message] of messages.entries()) {
|
|
1328
|
+
if (!message.id) throw new Error(`Message at index ${i} missing id`);
|
|
1329
|
+
if (!message.threadId) throw new Error(`Message at index ${i} missing threadId`);
|
|
1330
|
+
if (!message.content) throw new Error(`Message at index ${i} missing content`);
|
|
1331
|
+
if (!message.role) throw new Error(`Message at index ${i} missing role`);
|
|
1332
|
+
if (!message.resourceId) throw new Error(`Message at index ${i} missing resourceId`);
|
|
1333
|
+
if (!await this.getThreadById({ threadId: message.threadId })) throw new Error(`Thread ${message.threadId} not found`);
|
|
1334
|
+
}
|
|
1335
|
+
const messagesToInsert = messages.map((message) => {
|
|
1336
|
+
const createdAt = message.createdAt ? new Date(message.createdAt) : now;
|
|
1337
|
+
return {
|
|
1338
|
+
id: message.id,
|
|
1339
|
+
thread_id: message.threadId,
|
|
1340
|
+
content: typeof message.content === "string" ? message.content : JSON.stringify(message.content),
|
|
1341
|
+
createdAt: createdAt.toISOString(),
|
|
1342
|
+
role: message.role,
|
|
1343
|
+
type: message.type || "v2",
|
|
1344
|
+
resourceId: message.resourceId
|
|
1345
|
+
};
|
|
1346
|
+
});
|
|
1347
|
+
await Promise.all([this.#db.batchUpsert({
|
|
1348
|
+
tableName: TABLE_MESSAGES,
|
|
1349
|
+
records: messagesToInsert
|
|
1350
|
+
}), this.#db.executeQuery({
|
|
1351
|
+
sql: `UPDATE ${this.#db.getTableName(TABLE_THREADS)} SET updatedAt = ? WHERE id = ?`,
|
|
1352
|
+
params: [now.toISOString(), threadId]
|
|
1353
|
+
})]);
|
|
1354
|
+
this.logger.debug(`Saved ${messages.length} messages`);
|
|
1355
|
+
return { messages: new MessageList().add(messages, "memory").get.all.db() };
|
|
1356
|
+
} catch (error) {
|
|
1357
|
+
throw new MastraError({
|
|
1358
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "SAVE_MESSAGES", "FAILED"),
|
|
1359
|
+
domain: ErrorDomain.STORAGE,
|
|
1360
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1361
|
+
text: `Failed to save messages: ${error instanceof Error ? error.message : String(error)}`
|
|
1362
|
+
}, error);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
_sortMessages(messages, field, direction) {
|
|
1366
|
+
return messages.sort((a, b) => {
|
|
1367
|
+
const isDateField = field === "createdAt" || field === "updatedAt";
|
|
1368
|
+
const aValue = isDateField ? new Date(a[field]).getTime() : a[field];
|
|
1369
|
+
const bValue = isDateField ? new Date(b[field]).getTime() : b[field];
|
|
1370
|
+
if (aValue === bValue) return a.id.localeCompare(b.id);
|
|
1371
|
+
if (typeof aValue === "number" && typeof bValue === "number") return direction === "ASC" ? aValue - bValue : bValue - aValue;
|
|
1372
|
+
return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1375
|
+
async _getIncludedMessages(include) {
|
|
1376
|
+
if (!include || include.length === 0) return null;
|
|
1377
|
+
const tableName = this.#db.getTableName(TABLE_MESSAGES);
|
|
1378
|
+
const targetIds = include.map((inc) => inc.id).filter(Boolean);
|
|
1379
|
+
if (targetIds.length === 0) return null;
|
|
1380
|
+
const idPlaceholders = targetIds.map(() => "?").join(", ");
|
|
1381
|
+
const targetResult = await this.#db.executeQuery({
|
|
1382
|
+
sql: `SELECT id, thread_id, createdAt FROM ${tableName} WHERE id IN (${idPlaceholders})`,
|
|
1383
|
+
params: targetIds
|
|
1384
|
+
});
|
|
1385
|
+
if (!Array.isArray(targetResult) || targetResult.length === 0) return null;
|
|
1386
|
+
const targetMap = new Map(targetResult.map((r) => [r.id, {
|
|
1387
|
+
threadId: r.thread_id,
|
|
1388
|
+
createdAt: r.createdAt
|
|
1389
|
+
}]));
|
|
1390
|
+
const MAX_UNION_TERMS = 5;
|
|
1391
|
+
const unionQueries = [];
|
|
1392
|
+
const unionParams = [];
|
|
1393
|
+
const allMessages = [];
|
|
1394
|
+
const flushBatch = async () => {
|
|
1395
|
+
if (unionQueries.length === 0) return;
|
|
1396
|
+
const sql = unionQueries.length === 1 ? unionQueries[0] : `${unionQueries.join(" UNION ALL ")} ORDER BY createdAt ASC, id ASC`;
|
|
1397
|
+
const result = await this.#db.executeQuery({
|
|
1398
|
+
sql,
|
|
1399
|
+
params: unionParams
|
|
1400
|
+
});
|
|
1401
|
+
if (Array.isArray(result)) allMessages.push(...result);
|
|
1402
|
+
unionQueries.length = 0;
|
|
1403
|
+
unionParams.length = 0;
|
|
1404
|
+
};
|
|
1405
|
+
for (const inc of include) {
|
|
1406
|
+
const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
|
|
1407
|
+
const target = targetMap.get(id);
|
|
1408
|
+
if (!target) continue;
|
|
1409
|
+
const termsNeeded = withNextMessages > 0 ? 2 : 1;
|
|
1410
|
+
if (unionQueries.length + termsNeeded > MAX_UNION_TERMS) await flushBatch();
|
|
1411
|
+
unionQueries.push(`SELECT * FROM (
|
|
1498
1412
|
SELECT id, content, role, type, createdAt, thread_id AS threadId, resourceId
|
|
1499
1413
|
FROM ${tableName}
|
|
1500
1414
|
WHERE thread_id = ?
|
|
@@ -1502,9 +1416,9 @@ var MemoryStorageD1 = class extends MemoryStorage {
|
|
|
1502
1416
|
ORDER BY createdAt DESC, id DESC
|
|
1503
1417
|
LIMIT ?
|
|
1504
1418
|
)`);
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1419
|
+
unionParams.push(target.threadId, target.createdAt, withPreviousMessages + 1);
|
|
1420
|
+
if (withNextMessages > 0) {
|
|
1421
|
+
unionQueries.push(`SELECT * FROM (
|
|
1508
1422
|
SELECT id, content, role, type, createdAt, thread_id AS threadId, resourceId
|
|
1509
1423
|
FROM ${tableName}
|
|
1510
1424
|
WHERE thread_id = ?
|
|
@@ -1512,1044 +1426,1013 @@ var MemoryStorageD1 = class extends MemoryStorage {
|
|
|
1512
1426
|
ORDER BY createdAt ASC, id ASC
|
|
1513
1427
|
LIMIT ?
|
|
1514
1428
|
)`);
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
const fullTableName = this.#db.getTableName(TABLE_MESSAGES);
|
|
1601
|
-
let query = `
|
|
1429
|
+
unionParams.push(target.threadId, target.createdAt, withNextMessages);
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
await flushBatch();
|
|
1433
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1434
|
+
return allMessages.filter((message) => {
|
|
1435
|
+
const id = message.id;
|
|
1436
|
+
if (seen.has(id)) return false;
|
|
1437
|
+
seen.add(id);
|
|
1438
|
+
return true;
|
|
1439
|
+
}).map((message) => {
|
|
1440
|
+
const processedMsg = {};
|
|
1441
|
+
for (const [key, value] of Object.entries(message)) {
|
|
1442
|
+
if (key === `type` && value === `v2`) continue;
|
|
1443
|
+
processedMsg[key] = deserializeValue(value);
|
|
1444
|
+
}
|
|
1445
|
+
return processedMsg;
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
async listMessagesById({ messageIds }) {
|
|
1449
|
+
if (messageIds.length === 0) return { messages: [] };
|
|
1450
|
+
const fullTableName = this.#db.getTableName(TABLE_MESSAGES);
|
|
1451
|
+
const messages = [];
|
|
1452
|
+
try {
|
|
1453
|
+
const query = createSqlBuilder().select([
|
|
1454
|
+
"id",
|
|
1455
|
+
"content",
|
|
1456
|
+
"role",
|
|
1457
|
+
"type",
|
|
1458
|
+
"createdAt",
|
|
1459
|
+
"thread_id AS threadId",
|
|
1460
|
+
"resourceId"
|
|
1461
|
+
]).from(fullTableName).where(`id in (${messageIds.map(() => "?").join(",")})`, ...messageIds);
|
|
1462
|
+
query.orderBy("createdAt", "DESC");
|
|
1463
|
+
const { sql, params } = query.build();
|
|
1464
|
+
const result = await this.#db.executeQuery({
|
|
1465
|
+
sql,
|
|
1466
|
+
params
|
|
1467
|
+
});
|
|
1468
|
+
if (Array.isArray(result)) messages.push(...result);
|
|
1469
|
+
const processedMessages = messages.map((message) => {
|
|
1470
|
+
const processedMsg = {};
|
|
1471
|
+
for (const [key, value] of Object.entries(message)) {
|
|
1472
|
+
if (key === `type` && value === `v2`) continue;
|
|
1473
|
+
processedMsg[key] = deserializeValue(value);
|
|
1474
|
+
}
|
|
1475
|
+
return processedMsg;
|
|
1476
|
+
});
|
|
1477
|
+
this.logger.debug(`Retrieved ${messages.length} messages`);
|
|
1478
|
+
return { messages: new MessageList().add(processedMessages, "memory").get.all.db() };
|
|
1479
|
+
} catch (error) {
|
|
1480
|
+
const mastraError = new MastraError({
|
|
1481
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "LIST_MESSAGES_BY_ID", "FAILED"),
|
|
1482
|
+
domain: ErrorDomain.STORAGE,
|
|
1483
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1484
|
+
text: `Failed to retrieve messages by ID: ${error instanceof Error ? error.message : String(error)}`,
|
|
1485
|
+
details: { messageIds: JSON.stringify(messageIds) }
|
|
1486
|
+
}, error);
|
|
1487
|
+
this.logger?.error(mastraError.toString());
|
|
1488
|
+
this.logger?.trackException(mastraError);
|
|
1489
|
+
throw mastraError;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
async listMessages(args) {
|
|
1493
|
+
const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
|
|
1494
|
+
const threadIds = Array.isArray(threadId) ? threadId : [threadId];
|
|
1495
|
+
if (threadIds.length === 0 || threadIds.some((id) => !id.trim())) throw new MastraError({
|
|
1496
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "LIST_MESSAGES", "INVALID_THREAD_ID"),
|
|
1497
|
+
domain: ErrorDomain.STORAGE,
|
|
1498
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1499
|
+
details: { threadId: Array.isArray(threadId) ? threadId.join(",") : threadId }
|
|
1500
|
+
}, /* @__PURE__ */ new Error("threadId must be a non-empty string or array of non-empty strings"));
|
|
1501
|
+
if (page < 0) throw new MastraError({
|
|
1502
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "LIST_MESSAGES", "INVALID_PAGE"),
|
|
1503
|
+
domain: ErrorDomain.STORAGE,
|
|
1504
|
+
category: ErrorCategory.USER,
|
|
1505
|
+
details: { page }
|
|
1506
|
+
}, /* @__PURE__ */ new Error("page must be >= 0"));
|
|
1507
|
+
const perPage = normalizePerPage(perPageInput, 40);
|
|
1508
|
+
const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1509
|
+
const metadataFilter = validateStorageMetadataFilter(filter?.metadata);
|
|
1510
|
+
try {
|
|
1511
|
+
const fullTableName = this.#db.getTableName(TABLE_MESSAGES);
|
|
1512
|
+
const threadPlaceholders = threadIds.map(() => "?").join(", ");
|
|
1513
|
+
let query = `
|
|
1602
1514
|
SELECT id, content, role, type, createdAt, thread_id AS threadId, resourceId
|
|
1603
1515
|
FROM ${fullTableName}
|
|
1604
|
-
WHERE thread_id
|
|
1516
|
+
WHERE thread_id IN (${threadPlaceholders})
|
|
1605
1517
|
`;
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1518
|
+
const queryParams = [...threadIds];
|
|
1519
|
+
if (resourceId) {
|
|
1520
|
+
query += ` AND resourceId = ?`;
|
|
1521
|
+
queryParams.push(resourceId);
|
|
1522
|
+
}
|
|
1523
|
+
const dateRange = filter?.dateRange;
|
|
1524
|
+
if (dateRange?.start) {
|
|
1525
|
+
const startDate = dateRange.start instanceof Date ? serializeDate(dateRange.start) : serializeDate(new Date(dateRange.start));
|
|
1526
|
+
const startOp = dateRange.startExclusive ? ">" : ">=";
|
|
1527
|
+
query += ` AND createdAt ${startOp} ?`;
|
|
1528
|
+
queryParams.push(startDate);
|
|
1529
|
+
}
|
|
1530
|
+
if (dateRange?.end) {
|
|
1531
|
+
const endDate = dateRange.end instanceof Date ? serializeDate(dateRange.end) : serializeDate(new Date(dateRange.end));
|
|
1532
|
+
const endOp = dateRange.endExclusive ? "<" : "<=";
|
|
1533
|
+
query += ` AND createdAt ${endOp} ?`;
|
|
1534
|
+
queryParams.push(endDate);
|
|
1535
|
+
}
|
|
1536
|
+
const metadataConditions = [];
|
|
1537
|
+
addSqliteMetadataFilter(metadataConditions, queryParams, metadataFilter);
|
|
1538
|
+
if (metadataConditions.length > 0) query += ` AND ${metadataConditions.join(" AND ")}`;
|
|
1539
|
+
const { field, direction } = this.parseOrderBy(orderBy, "ASC");
|
|
1540
|
+
if (perPage === 0 && (!include || include.length === 0)) return {
|
|
1541
|
+
messages: [],
|
|
1542
|
+
total: 0,
|
|
1543
|
+
page,
|
|
1544
|
+
perPage: perPageForResponse,
|
|
1545
|
+
hasMore: false
|
|
1546
|
+
};
|
|
1547
|
+
if (perPage === 0 && include && include.length > 0) {
|
|
1548
|
+
const includeResult = await this._getIncludedMessages(include);
|
|
1549
|
+
if (!Array.isArray(includeResult) || includeResult.length === 0) return {
|
|
1550
|
+
messages: [],
|
|
1551
|
+
total: 0,
|
|
1552
|
+
page,
|
|
1553
|
+
perPage: perPageForResponse,
|
|
1554
|
+
hasMore: false
|
|
1555
|
+
};
|
|
1556
|
+
const list = new MessageList().add(includeResult, "memory");
|
|
1557
|
+
return {
|
|
1558
|
+
messages: this._sortMessages(list.get.all.db(), field, direction),
|
|
1559
|
+
total: 0,
|
|
1560
|
+
page,
|
|
1561
|
+
perPage: perPageForResponse,
|
|
1562
|
+
hasMore: false
|
|
1563
|
+
};
|
|
1564
|
+
}
|
|
1565
|
+
query += ` ORDER BY "${field}" ${direction}`;
|
|
1566
|
+
if (perPage !== Number.MAX_SAFE_INTEGER) {
|
|
1567
|
+
query += ` LIMIT ? OFFSET ?`;
|
|
1568
|
+
queryParams.push(perPage, offset);
|
|
1569
|
+
}
|
|
1570
|
+
const results = await this.#db.executeQuery({
|
|
1571
|
+
sql: query,
|
|
1572
|
+
params: queryParams
|
|
1573
|
+
});
|
|
1574
|
+
const paginatedMessages = (isArrayOfRecords(results) ? results : []).map((message) => {
|
|
1575
|
+
const processedMsg = {};
|
|
1576
|
+
for (const [key, value] of Object.entries(message)) {
|
|
1577
|
+
if (key === `type` && value === `v2`) continue;
|
|
1578
|
+
processedMsg[key] = deserializeValue(value);
|
|
1579
|
+
}
|
|
1580
|
+
return processedMsg;
|
|
1581
|
+
});
|
|
1582
|
+
const paginatedCount = paginatedMessages.length;
|
|
1583
|
+
let countQuery = `SELECT count() as count FROM ${fullTableName} WHERE thread_id IN (${threadPlaceholders})`;
|
|
1584
|
+
const countParams = [...threadIds];
|
|
1585
|
+
if (resourceId) {
|
|
1586
|
+
countQuery += ` AND resourceId = ?`;
|
|
1587
|
+
countParams.push(resourceId);
|
|
1588
|
+
}
|
|
1589
|
+
if (dateRange?.start) {
|
|
1590
|
+
const startDate = dateRange.start instanceof Date ? serializeDate(dateRange.start) : serializeDate(new Date(dateRange.start));
|
|
1591
|
+
const startOp = dateRange.startExclusive ? ">" : ">=";
|
|
1592
|
+
countQuery += ` AND createdAt ${startOp} ?`;
|
|
1593
|
+
countParams.push(startDate);
|
|
1594
|
+
}
|
|
1595
|
+
if (dateRange?.end) {
|
|
1596
|
+
const endDate = dateRange.end instanceof Date ? serializeDate(dateRange.end) : serializeDate(new Date(dateRange.end));
|
|
1597
|
+
const endOp = dateRange.endExclusive ? "<" : "<=";
|
|
1598
|
+
countQuery += ` AND createdAt ${endOp} ?`;
|
|
1599
|
+
countParams.push(endDate);
|
|
1600
|
+
}
|
|
1601
|
+
const countMetadataConditions = [];
|
|
1602
|
+
addSqliteMetadataFilter(countMetadataConditions, countParams, metadataFilter);
|
|
1603
|
+
if (countMetadataConditions.length > 0) countQuery += ` AND ${countMetadataConditions.join(" AND ")}`;
|
|
1604
|
+
const countResult = await this.#db.executeQuery({
|
|
1605
|
+
sql: countQuery,
|
|
1606
|
+
params: countParams
|
|
1607
|
+
});
|
|
1608
|
+
const total = Number(countResult[0]?.count ?? 0);
|
|
1609
|
+
if (total === 0 && paginatedCount === 0 && (!include || include.length === 0)) return {
|
|
1610
|
+
messages: [],
|
|
1611
|
+
total: 0,
|
|
1612
|
+
page,
|
|
1613
|
+
perPage: perPageForResponse,
|
|
1614
|
+
hasMore: false
|
|
1615
|
+
};
|
|
1616
|
+
const messageIds = new Set(paginatedMessages.map((m) => m.id));
|
|
1617
|
+
let includeMessages = [];
|
|
1618
|
+
if (include && include.length > 0) {
|
|
1619
|
+
const includeResult = await this._getIncludedMessages(include);
|
|
1620
|
+
if (Array.isArray(includeResult)) {
|
|
1621
|
+
includeMessages = includeResult;
|
|
1622
|
+
for (const includeMsg of includeMessages) if (!messageIds.has(includeMsg.id)) {
|
|
1623
|
+
paginatedMessages.push(includeMsg);
|
|
1624
|
+
messageIds.add(includeMsg.id);
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
const list = new MessageList().add(paginatedMessages, "memory");
|
|
1629
|
+
const finalMessages = this._sortMessages(list.get.all.db(), field, direction);
|
|
1630
|
+
const threadIdSet = new Set(threadIds);
|
|
1631
|
+
const allThreadMessagesReturned = new Set(finalMessages.filter((m) => m.threadId && threadIdSet.has(m.threadId)).map((m) => m.id)).size >= total;
|
|
1632
|
+
return {
|
|
1633
|
+
messages: finalMessages,
|
|
1634
|
+
total,
|
|
1635
|
+
page,
|
|
1636
|
+
perPage: perPageForResponse,
|
|
1637
|
+
hasMore: metadataFilter ? perPageInput !== false && offset + paginatedCount < total : perPageInput !== false && !allThreadMessagesReturned && offset + perPage < total
|
|
1638
|
+
};
|
|
1639
|
+
} catch (error) {
|
|
1640
|
+
const mastraError = new MastraError({
|
|
1641
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "LIST_MESSAGES", "FAILED"),
|
|
1642
|
+
domain: ErrorDomain.STORAGE,
|
|
1643
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1644
|
+
text: `Failed to list messages for thread ${Array.isArray(threadId) ? threadId.join(",") : threadId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1645
|
+
details: {
|
|
1646
|
+
threadId: Array.isArray(threadId) ? threadId.join(",") : threadId,
|
|
1647
|
+
resourceId: resourceId ?? ""
|
|
1648
|
+
}
|
|
1649
|
+
}, error);
|
|
1650
|
+
this.logger?.error?.(mastraError.toString());
|
|
1651
|
+
this.logger?.trackException?.(mastraError);
|
|
1652
|
+
return {
|
|
1653
|
+
messages: [],
|
|
1654
|
+
total: 0,
|
|
1655
|
+
page,
|
|
1656
|
+
perPage: perPageForResponse,
|
|
1657
|
+
hasMore: false
|
|
1658
|
+
};
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
async updateMessages(args) {
|
|
1662
|
+
const { messages } = args;
|
|
1663
|
+
this.logger.debug("Updating messages", { count: messages.length });
|
|
1664
|
+
if (!messages.length) return [];
|
|
1665
|
+
const messageIds = messages.map((m) => m.id);
|
|
1666
|
+
const fullTableName = this.#db.getTableName(TABLE_MESSAGES);
|
|
1667
|
+
const threadsTableName = this.#db.getTableName(TABLE_THREADS);
|
|
1668
|
+
try {
|
|
1669
|
+
const selectQuery = `SELECT id, content, role, type, createdAt, thread_id AS threadId, resourceId FROM ${fullTableName} WHERE id IN (${messageIds.map(() => "?").join(",")})`;
|
|
1670
|
+
const existingMessages = await this.#db.executeQuery({
|
|
1671
|
+
sql: selectQuery,
|
|
1672
|
+
params: messageIds
|
|
1673
|
+
});
|
|
1674
|
+
if (existingMessages.length === 0) return [];
|
|
1675
|
+
const parsedExistingMessages = existingMessages.map((msg) => {
|
|
1676
|
+
if (typeof msg.content === "string") try {
|
|
1677
|
+
msg.content = JSON.parse(msg.content);
|
|
1678
|
+
} catch {}
|
|
1679
|
+
return msg;
|
|
1680
|
+
});
|
|
1681
|
+
const threadIdsToUpdate = /* @__PURE__ */ new Set();
|
|
1682
|
+
const updateQueries = [];
|
|
1683
|
+
for (const existingMessage of parsedExistingMessages) {
|
|
1684
|
+
const updatePayload = messages.find((m) => m.id === existingMessage.id);
|
|
1685
|
+
if (!updatePayload) continue;
|
|
1686
|
+
const { id, ...fieldsToUpdate } = updatePayload;
|
|
1687
|
+
if (Object.keys(fieldsToUpdate).length === 0) continue;
|
|
1688
|
+
threadIdsToUpdate.add(existingMessage.threadId);
|
|
1689
|
+
if ("threadId" in updatePayload && updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) threadIdsToUpdate.add(updatePayload.threadId);
|
|
1690
|
+
const setClauses = [];
|
|
1691
|
+
const values = [];
|
|
1692
|
+
const updatableFields = { ...fieldsToUpdate };
|
|
1693
|
+
if (updatableFields.content) {
|
|
1694
|
+
const existingContent = existingMessage.content || {};
|
|
1695
|
+
const newContent = {
|
|
1696
|
+
...existingContent,
|
|
1697
|
+
...updatableFields.content,
|
|
1698
|
+
...existingContent?.metadata && updatableFields.content.metadata ? { metadata: {
|
|
1699
|
+
...existingContent.metadata,
|
|
1700
|
+
...updatableFields.content.metadata
|
|
1701
|
+
} } : {}
|
|
1702
|
+
};
|
|
1703
|
+
setClauses.push(`content = ?`);
|
|
1704
|
+
values.push(JSON.stringify(newContent));
|
|
1705
|
+
delete updatableFields.content;
|
|
1706
|
+
}
|
|
1707
|
+
for (const key in updatableFields) if (Object.prototype.hasOwnProperty.call(updatableFields, key)) {
|
|
1708
|
+
const dbColumn = key === "threadId" ? "thread_id" : key;
|
|
1709
|
+
setClauses.push(`${dbColumn} = ?`);
|
|
1710
|
+
values.push(updatableFields[key]);
|
|
1711
|
+
}
|
|
1712
|
+
if (setClauses.length > 0) {
|
|
1713
|
+
values.push(id);
|
|
1714
|
+
const updateQuery = `UPDATE ${fullTableName} SET ${setClauses.join(", ")} WHERE id = ?`;
|
|
1715
|
+
updateQueries.push({
|
|
1716
|
+
sql: updateQuery,
|
|
1717
|
+
params: values
|
|
1718
|
+
});
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
for (const query of updateQueries) await this.#db.executeQuery(query);
|
|
1722
|
+
if (threadIdsToUpdate.size > 0) {
|
|
1723
|
+
const threadUpdateQuery = `UPDATE ${threadsTableName} SET updatedAt = ? WHERE id IN (${Array.from(threadIdsToUpdate).map(() => "?").join(",")})`;
|
|
1724
|
+
const threadUpdateParams = [(/* @__PURE__ */ new Date()).toISOString(), ...Array.from(threadIdsToUpdate)];
|
|
1725
|
+
await this.#db.executeQuery({
|
|
1726
|
+
sql: threadUpdateQuery,
|
|
1727
|
+
params: threadUpdateParams
|
|
1728
|
+
});
|
|
1729
|
+
}
|
|
1730
|
+
return (await this.#db.executeQuery({
|
|
1731
|
+
sql: selectQuery,
|
|
1732
|
+
params: messageIds
|
|
1733
|
+
})).map((message) => {
|
|
1734
|
+
if (typeof message.content === "string") try {
|
|
1735
|
+
message.content = JSON.parse(message.content);
|
|
1736
|
+
} catch {}
|
|
1737
|
+
return message;
|
|
1738
|
+
});
|
|
1739
|
+
} catch (error) {
|
|
1740
|
+
throw new MastraError({
|
|
1741
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "UPDATE_MESSAGES", "FAILED"),
|
|
1742
|
+
domain: ErrorDomain.STORAGE,
|
|
1743
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1744
|
+
details: { count: messages.length }
|
|
1745
|
+
}, error);
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
async deleteMessages(messageIds) {
|
|
1749
|
+
if (messageIds.length === 0) return;
|
|
1750
|
+
const fullTableName = this.#db.getTableName(TABLE_MESSAGES);
|
|
1751
|
+
const threadsTableName = this.#db.getTableName(TABLE_THREADS);
|
|
1752
|
+
try {
|
|
1753
|
+
const placeholders = messageIds.map(() => "?").join(",");
|
|
1754
|
+
const selectQuery = `SELECT DISTINCT thread_id FROM ${fullTableName} WHERE id IN (${placeholders})`;
|
|
1755
|
+
const threadIds = (await this.#db.executeQuery({
|
|
1756
|
+
sql: selectQuery,
|
|
1757
|
+
params: messageIds
|
|
1758
|
+
})).map((r) => r.thread_id).filter(Boolean);
|
|
1759
|
+
const { sql, params } = createSqlBuilder().delete(fullTableName).where(`id IN (${placeholders})`, ...messageIds).build();
|
|
1760
|
+
await this.#db.executeQuery({
|
|
1761
|
+
sql,
|
|
1762
|
+
params
|
|
1763
|
+
});
|
|
1764
|
+
if (threadIds.length > 0) {
|
|
1765
|
+
const threadUpdateQuery = `UPDATE ${threadsTableName} SET updatedAt = ? WHERE id IN (${threadIds.map(() => "?").join(",")})`;
|
|
1766
|
+
const threadUpdateParams = [(/* @__PURE__ */ new Date()).toISOString(), ...threadIds];
|
|
1767
|
+
await this.#db.executeQuery({
|
|
1768
|
+
sql: threadUpdateQuery,
|
|
1769
|
+
params: threadUpdateParams
|
|
1770
|
+
});
|
|
1771
|
+
}
|
|
1772
|
+
} catch (error) {
|
|
1773
|
+
throw new MastraError({
|
|
1774
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "DELETE_MESSAGES", "FAILED"),
|
|
1775
|
+
domain: ErrorDomain.STORAGE,
|
|
1776
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1777
|
+
details: { messageIds: JSON.stringify(messageIds) }
|
|
1778
|
+
}, error);
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1867
1781
|
};
|
|
1782
|
+
//#endregion
|
|
1783
|
+
//#region src/storage/domains/scores/index.ts
|
|
1784
|
+
/** Applies multi-tenant scope conditions to a query builder. */
|
|
1868
1785
|
function applyTenancyFilters(query, filters) {
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
}
|
|
1872
|
-
if (filters?.projectId !== void 0) {
|
|
1873
|
-
query.andWhere("projectId = ?", filters.projectId);
|
|
1874
|
-
}
|
|
1786
|
+
if (filters?.organizationId !== void 0) query.andWhere("organizationId = ?", filters.organizationId);
|
|
1787
|
+
if (filters?.projectId !== void 0) query.andWhere("projectId = ?", filters.projectId);
|
|
1875
1788
|
}
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1789
|
+
/**
|
|
1790
|
+
* Cloudflare D1-specific score row transformation.
|
|
1791
|
+
* Uses Z-suffix timestamps (createdAtZ, updatedAtZ) when available.
|
|
1792
|
+
*/
|
|
1793
|
+
function transformScoreRow$1(row) {
|
|
1794
|
+
return transformScoreRow(row, { preferredTimestampFields: {
|
|
1795
|
+
createdAt: "createdAtZ",
|
|
1796
|
+
updatedAt: "updatedAtZ"
|
|
1797
|
+
} });
|
|
1883
1798
|
}
|
|
1884
1799
|
var ScoresStorageD1 = class extends ScoresStorage {
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
const countResult = await this.#db.executeQuery(countQuery.build());
|
|
2175
|
-
const total = Array.isArray(countResult) ? Number(countResult?.[0]?.count ?? 0) : Number(countResult?.count ?? 0);
|
|
2176
|
-
if (total === 0) {
|
|
2177
|
-
return {
|
|
2178
|
-
pagination: {
|
|
2179
|
-
total: 0,
|
|
2180
|
-
page,
|
|
2181
|
-
perPage: perPageForResponse,
|
|
2182
|
-
hasMore: false
|
|
2183
|
-
},
|
|
2184
|
-
scores: []
|
|
2185
|
-
};
|
|
2186
|
-
}
|
|
2187
|
-
const end = perPageInput === false ? total : start + perPage;
|
|
2188
|
-
const limitValue = perPageInput === false ? total : perPage;
|
|
2189
|
-
const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("traceId = ?", traceId).andWhere("spanId = ?", spanId);
|
|
2190
|
-
applyTenancyFilters(selectQuery, filters);
|
|
2191
|
-
selectQuery.orderBy("createdAt", "DESC").limit(limitValue).offset(start);
|
|
2192
|
-
const { sql, params } = selectQuery.build();
|
|
2193
|
-
const results = await this.#db.executeQuery({ sql, params });
|
|
2194
|
-
const scores = Array.isArray(results) ? results.map(transformScoreRow) : [];
|
|
2195
|
-
return {
|
|
2196
|
-
pagination: {
|
|
2197
|
-
total,
|
|
2198
|
-
page,
|
|
2199
|
-
perPage: perPageForResponse,
|
|
2200
|
-
hasMore: end < total
|
|
2201
|
-
},
|
|
2202
|
-
scores
|
|
2203
|
-
};
|
|
2204
|
-
} catch (error) {
|
|
2205
|
-
throw new MastraError(
|
|
2206
|
-
{
|
|
2207
|
-
id: createStorageErrorId("CLOUDFLARE_D1", "GET_SCORES_BY_SPAN", "FAILED"),
|
|
2208
|
-
domain: ErrorDomain.STORAGE,
|
|
2209
|
-
category: ErrorCategory.THIRD_PARTY
|
|
2210
|
-
},
|
|
2211
|
-
error
|
|
2212
|
-
);
|
|
2213
|
-
}
|
|
2214
|
-
}
|
|
1800
|
+
#db;
|
|
1801
|
+
constructor(config) {
|
|
1802
|
+
super();
|
|
1803
|
+
this.#db = new D1DB(resolveD1Config(config));
|
|
1804
|
+
}
|
|
1805
|
+
async init() {
|
|
1806
|
+
await this.#db.createTable({
|
|
1807
|
+
tableName: TABLE_SCORERS,
|
|
1808
|
+
schema: TABLE_SCHEMAS[TABLE_SCORERS]
|
|
1809
|
+
});
|
|
1810
|
+
await this.#db.alterTable({
|
|
1811
|
+
tableName: TABLE_SCORERS,
|
|
1812
|
+
schema: TABLE_SCHEMAS[TABLE_SCORERS],
|
|
1813
|
+
ifNotExists: [
|
|
1814
|
+
"organizationId",
|
|
1815
|
+
"projectId",
|
|
1816
|
+
"batchId",
|
|
1817
|
+
"datasetId",
|
|
1818
|
+
"datasetItemId"
|
|
1819
|
+
]
|
|
1820
|
+
});
|
|
1821
|
+
}
|
|
1822
|
+
async dangerouslyClearAll() {
|
|
1823
|
+
await this.#db.clearTable({ tableName: TABLE_SCORERS });
|
|
1824
|
+
}
|
|
1825
|
+
async getScoreById({ id }) {
|
|
1826
|
+
try {
|
|
1827
|
+
const fullTableName = this.#db.getTableName(TABLE_SCORERS);
|
|
1828
|
+
const { sql, params } = createSqlBuilder().select("*").from(fullTableName).where("id = ?", id).build();
|
|
1829
|
+
const result = await this.#db.executeQuery({
|
|
1830
|
+
sql,
|
|
1831
|
+
params,
|
|
1832
|
+
first: true
|
|
1833
|
+
});
|
|
1834
|
+
if (!result) return null;
|
|
1835
|
+
return transformScoreRow$1(result);
|
|
1836
|
+
} catch (error) {
|
|
1837
|
+
throw new MastraError({
|
|
1838
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "GET_SCORE_BY_ID", "FAILED"),
|
|
1839
|
+
domain: ErrorDomain.STORAGE,
|
|
1840
|
+
category: ErrorCategory.THIRD_PARTY
|
|
1841
|
+
}, error);
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
async saveScore(score) {
|
|
1845
|
+
let parsedScore;
|
|
1846
|
+
try {
|
|
1847
|
+
parsedScore = saveScorePayloadSchema.parse(score);
|
|
1848
|
+
} catch (error) {
|
|
1849
|
+
throw new MastraError({
|
|
1850
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "SAVE_SCORE", "VALIDATION_FAILED"),
|
|
1851
|
+
domain: ErrorDomain.STORAGE,
|
|
1852
|
+
category: ErrorCategory.USER,
|
|
1853
|
+
details: {
|
|
1854
|
+
scorer: typeof score.scorer?.id === "string" ? score.scorer.id : String(score.scorer?.id ?? "unknown"),
|
|
1855
|
+
entityId: score.entityId ?? "unknown",
|
|
1856
|
+
entityType: score.entityType ?? "unknown",
|
|
1857
|
+
traceId: score.traceId ?? "",
|
|
1858
|
+
spanId: score.spanId ?? ""
|
|
1859
|
+
}
|
|
1860
|
+
}, error);
|
|
1861
|
+
}
|
|
1862
|
+
const id = crypto.randomUUID();
|
|
1863
|
+
try {
|
|
1864
|
+
const fullTableName = this.#db.getTableName(TABLE_SCORERS);
|
|
1865
|
+
const serializedRecord = {};
|
|
1866
|
+
for (const [key, value] of Object.entries(parsedScore)) if (value !== null && value !== void 0) if (typeof value === "object") serializedRecord[key] = JSON.stringify(value);
|
|
1867
|
+
else serializedRecord[key] = value;
|
|
1868
|
+
else serializedRecord[key] = null;
|
|
1869
|
+
const now = /* @__PURE__ */ new Date();
|
|
1870
|
+
serializedRecord.id = id;
|
|
1871
|
+
serializedRecord.createdAt = now.toISOString();
|
|
1872
|
+
serializedRecord.updatedAt = now.toISOString();
|
|
1873
|
+
const columns = Object.keys(serializedRecord);
|
|
1874
|
+
const values = Object.values(serializedRecord);
|
|
1875
|
+
const { sql, params } = createSqlBuilder().insert(fullTableName, columns, values).build();
|
|
1876
|
+
await this.#db.executeQuery({
|
|
1877
|
+
sql,
|
|
1878
|
+
params
|
|
1879
|
+
});
|
|
1880
|
+
return { score: {
|
|
1881
|
+
...parsedScore,
|
|
1882
|
+
id,
|
|
1883
|
+
createdAt: now,
|
|
1884
|
+
updatedAt: now
|
|
1885
|
+
} };
|
|
1886
|
+
} catch (error) {
|
|
1887
|
+
throw new MastraError({
|
|
1888
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "SAVE_SCORE", "FAILED"),
|
|
1889
|
+
domain: ErrorDomain.STORAGE,
|
|
1890
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1891
|
+
details: { id }
|
|
1892
|
+
}, error);
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
async listScoresByScorerId({ scorerId, entityId, entityType, source, pagination, filters }) {
|
|
1896
|
+
try {
|
|
1897
|
+
const { page, perPage: perPageInput } = pagination;
|
|
1898
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
1899
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1900
|
+
const fullTableName = this.#db.getTableName(TABLE_SCORERS);
|
|
1901
|
+
const countQuery = createSqlBuilder().count().from(fullTableName).where("scorerId = ?", scorerId);
|
|
1902
|
+
if (entityId) countQuery.andWhere("entityId = ?", entityId);
|
|
1903
|
+
if (entityType) countQuery.andWhere("entityType = ?", entityType);
|
|
1904
|
+
if (source) countQuery.andWhere("source = ?", source);
|
|
1905
|
+
applyTenancyFilters(countQuery, filters);
|
|
1906
|
+
const countResult = await this.#db.executeQuery(countQuery.build());
|
|
1907
|
+
const total = Array.isArray(countResult) ? Number(countResult?.[0]?.count ?? 0) : Number(countResult?.count ?? 0);
|
|
1908
|
+
if (total === 0) return {
|
|
1909
|
+
pagination: {
|
|
1910
|
+
total: 0,
|
|
1911
|
+
page,
|
|
1912
|
+
perPage: perPageForResponse,
|
|
1913
|
+
hasMore: false
|
|
1914
|
+
},
|
|
1915
|
+
scores: []
|
|
1916
|
+
};
|
|
1917
|
+
const end = perPageInput === false ? total : start + perPage;
|
|
1918
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
1919
|
+
const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("scorerId = ?", scorerId);
|
|
1920
|
+
if (entityId) selectQuery.andWhere("entityId = ?", entityId);
|
|
1921
|
+
if (entityType) selectQuery.andWhere("entityType = ?", entityType);
|
|
1922
|
+
if (source) selectQuery.andWhere("source = ?", source);
|
|
1923
|
+
applyTenancyFilters(selectQuery, filters);
|
|
1924
|
+
selectQuery.orderBy("createdAt", "DESC").limit(limitValue).offset(start);
|
|
1925
|
+
const { sql, params } = selectQuery.build();
|
|
1926
|
+
const results = await this.#db.executeQuery({
|
|
1927
|
+
sql,
|
|
1928
|
+
params
|
|
1929
|
+
});
|
|
1930
|
+
const scores = Array.isArray(results) ? results.map(transformScoreRow$1) : [];
|
|
1931
|
+
return {
|
|
1932
|
+
pagination: {
|
|
1933
|
+
total,
|
|
1934
|
+
page,
|
|
1935
|
+
perPage: perPageForResponse,
|
|
1936
|
+
hasMore: end < total
|
|
1937
|
+
},
|
|
1938
|
+
scores
|
|
1939
|
+
};
|
|
1940
|
+
} catch (error) {
|
|
1941
|
+
throw new MastraError({
|
|
1942
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "GET_SCORES_BY_SCORER_ID", "FAILED"),
|
|
1943
|
+
domain: ErrorDomain.STORAGE,
|
|
1944
|
+
category: ErrorCategory.THIRD_PARTY
|
|
1945
|
+
}, error);
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
async listScoresByRunId({ runId, pagination, filters }) {
|
|
1949
|
+
try {
|
|
1950
|
+
const { page, perPage: perPageInput } = pagination;
|
|
1951
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
1952
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
1953
|
+
const fullTableName = this.#db.getTableName(TABLE_SCORERS);
|
|
1954
|
+
const countQuery = createSqlBuilder().count().from(fullTableName).where("runId = ?", runId);
|
|
1955
|
+
applyTenancyFilters(countQuery, filters);
|
|
1956
|
+
const countResult = await this.#db.executeQuery(countQuery.build());
|
|
1957
|
+
const total = Array.isArray(countResult) ? Number(countResult?.[0]?.count ?? 0) : Number(countResult?.count ?? 0);
|
|
1958
|
+
if (total === 0) return {
|
|
1959
|
+
pagination: {
|
|
1960
|
+
total: 0,
|
|
1961
|
+
page,
|
|
1962
|
+
perPage: perPageForResponse,
|
|
1963
|
+
hasMore: false
|
|
1964
|
+
},
|
|
1965
|
+
scores: []
|
|
1966
|
+
};
|
|
1967
|
+
const end = perPageInput === false ? total : start + perPage;
|
|
1968
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
1969
|
+
const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("runId = ?", runId);
|
|
1970
|
+
applyTenancyFilters(selectQuery, filters);
|
|
1971
|
+
selectQuery.orderBy("createdAt", "DESC").limit(limitValue).offset(start);
|
|
1972
|
+
const { sql, params } = selectQuery.build();
|
|
1973
|
+
const results = await this.#db.executeQuery({
|
|
1974
|
+
sql,
|
|
1975
|
+
params
|
|
1976
|
+
});
|
|
1977
|
+
const scores = Array.isArray(results) ? results.map(transformScoreRow$1) : [];
|
|
1978
|
+
return {
|
|
1979
|
+
pagination: {
|
|
1980
|
+
total,
|
|
1981
|
+
page,
|
|
1982
|
+
perPage: perPageForResponse,
|
|
1983
|
+
hasMore: end < total
|
|
1984
|
+
},
|
|
1985
|
+
scores
|
|
1986
|
+
};
|
|
1987
|
+
} catch (error) {
|
|
1988
|
+
throw new MastraError({
|
|
1989
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "GET_SCORES_BY_RUN_ID", "FAILED"),
|
|
1990
|
+
domain: ErrorDomain.STORAGE,
|
|
1991
|
+
category: ErrorCategory.THIRD_PARTY
|
|
1992
|
+
}, error);
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
async listScoresByEntityId({ entityId, entityType, pagination, filters }) {
|
|
1996
|
+
try {
|
|
1997
|
+
const { page, perPage: perPageInput } = pagination;
|
|
1998
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
1999
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
2000
|
+
const fullTableName = this.#db.getTableName(TABLE_SCORERS);
|
|
2001
|
+
const countQuery = createSqlBuilder().count().from(fullTableName).where("entityId = ?", entityId).andWhere("entityType = ?", entityType);
|
|
2002
|
+
applyTenancyFilters(countQuery, filters);
|
|
2003
|
+
const countResult = await this.#db.executeQuery(countQuery.build());
|
|
2004
|
+
const total = Array.isArray(countResult) ? Number(countResult?.[0]?.count ?? 0) : Number(countResult?.count ?? 0);
|
|
2005
|
+
if (total === 0) return {
|
|
2006
|
+
pagination: {
|
|
2007
|
+
total: 0,
|
|
2008
|
+
page,
|
|
2009
|
+
perPage: perPageForResponse,
|
|
2010
|
+
hasMore: false
|
|
2011
|
+
},
|
|
2012
|
+
scores: []
|
|
2013
|
+
};
|
|
2014
|
+
const end = perPageInput === false ? total : start + perPage;
|
|
2015
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
2016
|
+
const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("entityId = ?", entityId).andWhere("entityType = ?", entityType);
|
|
2017
|
+
applyTenancyFilters(selectQuery, filters);
|
|
2018
|
+
selectQuery.orderBy("createdAt", "DESC").limit(limitValue).offset(start);
|
|
2019
|
+
const { sql, params } = selectQuery.build();
|
|
2020
|
+
const results = await this.#db.executeQuery({
|
|
2021
|
+
sql,
|
|
2022
|
+
params
|
|
2023
|
+
});
|
|
2024
|
+
const scores = Array.isArray(results) ? results.map(transformScoreRow$1) : [];
|
|
2025
|
+
return {
|
|
2026
|
+
pagination: {
|
|
2027
|
+
total,
|
|
2028
|
+
page,
|
|
2029
|
+
perPage: perPageForResponse,
|
|
2030
|
+
hasMore: end < total
|
|
2031
|
+
},
|
|
2032
|
+
scores
|
|
2033
|
+
};
|
|
2034
|
+
} catch (error) {
|
|
2035
|
+
throw new MastraError({
|
|
2036
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "GET_SCORES_BY_ENTITY_ID", "FAILED"),
|
|
2037
|
+
domain: ErrorDomain.STORAGE,
|
|
2038
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2039
|
+
}, error);
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
async listScoresBySpan({ traceId, spanId, pagination, filters }) {
|
|
2043
|
+
try {
|
|
2044
|
+
const { page, perPage: perPageInput } = pagination;
|
|
2045
|
+
const perPage = normalizePerPage(perPageInput, 100);
|
|
2046
|
+
const { offset: start, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
|
|
2047
|
+
const fullTableName = this.#db.getTableName(TABLE_SCORERS);
|
|
2048
|
+
const countQuery = createSqlBuilder().count().from(fullTableName).where("traceId = ?", traceId).andWhere("spanId = ?", spanId);
|
|
2049
|
+
applyTenancyFilters(countQuery, filters);
|
|
2050
|
+
const countResult = await this.#db.executeQuery(countQuery.build());
|
|
2051
|
+
const total = Array.isArray(countResult) ? Number(countResult?.[0]?.count ?? 0) : Number(countResult?.count ?? 0);
|
|
2052
|
+
if (total === 0) return {
|
|
2053
|
+
pagination: {
|
|
2054
|
+
total: 0,
|
|
2055
|
+
page,
|
|
2056
|
+
perPage: perPageForResponse,
|
|
2057
|
+
hasMore: false
|
|
2058
|
+
},
|
|
2059
|
+
scores: []
|
|
2060
|
+
};
|
|
2061
|
+
const end = perPageInput === false ? total : start + perPage;
|
|
2062
|
+
const limitValue = perPageInput === false ? total : perPage;
|
|
2063
|
+
const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("traceId = ?", traceId).andWhere("spanId = ?", spanId);
|
|
2064
|
+
applyTenancyFilters(selectQuery, filters);
|
|
2065
|
+
selectQuery.orderBy("createdAt", "DESC").limit(limitValue).offset(start);
|
|
2066
|
+
const { sql, params } = selectQuery.build();
|
|
2067
|
+
const results = await this.#db.executeQuery({
|
|
2068
|
+
sql,
|
|
2069
|
+
params
|
|
2070
|
+
});
|
|
2071
|
+
const scores = Array.isArray(results) ? results.map(transformScoreRow$1) : [];
|
|
2072
|
+
return {
|
|
2073
|
+
pagination: {
|
|
2074
|
+
total,
|
|
2075
|
+
page,
|
|
2076
|
+
perPage: perPageForResponse,
|
|
2077
|
+
hasMore: end < total
|
|
2078
|
+
},
|
|
2079
|
+
scores
|
|
2080
|
+
};
|
|
2081
|
+
} catch (error) {
|
|
2082
|
+
throw new MastraError({
|
|
2083
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "GET_SCORES_BY_SPAN", "FAILED"),
|
|
2084
|
+
domain: ErrorDomain.STORAGE,
|
|
2085
|
+
category: ErrorCategory.THIRD_PARTY
|
|
2086
|
+
}, error);
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2215
2089
|
};
|
|
2090
|
+
//#endregion
|
|
2091
|
+
//#region src/storage/domains/workflows/index.ts
|
|
2216
2092
|
var WorkflowsStorageD1 = class extends WorkflowsStorage {
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
details: { runId, workflowName }
|
|
2455
|
-
},
|
|
2456
|
-
error
|
|
2457
|
-
);
|
|
2458
|
-
}
|
|
2459
|
-
}
|
|
2093
|
+
#db;
|
|
2094
|
+
constructor(config) {
|
|
2095
|
+
super();
|
|
2096
|
+
this.#db = new D1DB(resolveD1Config(config));
|
|
2097
|
+
}
|
|
2098
|
+
supportsConcurrentUpdates() {
|
|
2099
|
+
return false;
|
|
2100
|
+
}
|
|
2101
|
+
async init() {
|
|
2102
|
+
await this.#db.createTable({
|
|
2103
|
+
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
2104
|
+
schema: TABLE_SCHEMAS[TABLE_WORKFLOW_SNAPSHOT]
|
|
2105
|
+
});
|
|
2106
|
+
}
|
|
2107
|
+
async dangerouslyClearAll() {
|
|
2108
|
+
await this.#db.clearTable({ tableName: TABLE_WORKFLOW_SNAPSHOT });
|
|
2109
|
+
}
|
|
2110
|
+
async updateWorkflowResults(_args) {
|
|
2111
|
+
throw new Error("updateWorkflowResults is not implemented for Cloudflare D1 storage. D1 does not support atomic read-modify-write operations needed for concurrent workflow updates.");
|
|
2112
|
+
}
|
|
2113
|
+
async updateWorkflowState(_args) {
|
|
2114
|
+
throw new Error("updateWorkflowState is not implemented for Cloudflare D1 storage. D1 does not support atomic read-modify-write operations needed for concurrent workflow updates.");
|
|
2115
|
+
}
|
|
2116
|
+
async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt }) {
|
|
2117
|
+
const fullTableName = this.#db.getTableName(TABLE_WORKFLOW_SNAPSHOT);
|
|
2118
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2119
|
+
const currentSnapshot = await this.#db.load({
|
|
2120
|
+
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
2121
|
+
keys: {
|
|
2122
|
+
workflow_name: workflowName,
|
|
2123
|
+
run_id: runId
|
|
2124
|
+
}
|
|
2125
|
+
});
|
|
2126
|
+
const persisting = currentSnapshot ? {
|
|
2127
|
+
...currentSnapshot,
|
|
2128
|
+
resourceId,
|
|
2129
|
+
snapshot: JSON.stringify(snapshot),
|
|
2130
|
+
updatedAt: updatedAt ? updatedAt.toISOString() : now
|
|
2131
|
+
} : {
|
|
2132
|
+
workflow_name: workflowName,
|
|
2133
|
+
run_id: runId,
|
|
2134
|
+
resourceId,
|
|
2135
|
+
snapshot,
|
|
2136
|
+
createdAt: createdAt ? createdAt.toISOString() : now,
|
|
2137
|
+
updatedAt: updatedAt ? updatedAt.toISOString() : now
|
|
2138
|
+
};
|
|
2139
|
+
const processedRecord = await this.#db.processRecord(persisting);
|
|
2140
|
+
const columns = Object.keys(processedRecord);
|
|
2141
|
+
const values = Object.values(processedRecord);
|
|
2142
|
+
const updateMap = {
|
|
2143
|
+
snapshot: "excluded.snapshot",
|
|
2144
|
+
updatedAt: "excluded.updatedAt"
|
|
2145
|
+
};
|
|
2146
|
+
this.logger.debug("Persisting workflow snapshot", {
|
|
2147
|
+
workflowName,
|
|
2148
|
+
runId
|
|
2149
|
+
});
|
|
2150
|
+
const { sql, params } = createSqlBuilder().insert(fullTableName, columns, values, ["workflow_name", "run_id"], updateMap).build();
|
|
2151
|
+
try {
|
|
2152
|
+
await this.#db.executeQuery({
|
|
2153
|
+
sql,
|
|
2154
|
+
params
|
|
2155
|
+
});
|
|
2156
|
+
} catch (error) {
|
|
2157
|
+
throw new MastraError({
|
|
2158
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "PERSIST_WORKFLOW_SNAPSHOT", "FAILED"),
|
|
2159
|
+
domain: ErrorDomain.STORAGE,
|
|
2160
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2161
|
+
text: `Failed to persist workflow snapshot: ${error instanceof Error ? error.message : String(error)}`,
|
|
2162
|
+
details: {
|
|
2163
|
+
workflowName,
|
|
2164
|
+
runId
|
|
2165
|
+
}
|
|
2166
|
+
}, error);
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
async loadWorkflowSnapshot(params) {
|
|
2170
|
+
const { workflowName, runId } = params;
|
|
2171
|
+
this.logger.debug("Loading workflow snapshot", {
|
|
2172
|
+
workflowName,
|
|
2173
|
+
runId
|
|
2174
|
+
});
|
|
2175
|
+
try {
|
|
2176
|
+
const d = await this.#db.load({
|
|
2177
|
+
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
2178
|
+
keys: {
|
|
2179
|
+
workflow_name: workflowName,
|
|
2180
|
+
run_id: runId
|
|
2181
|
+
}
|
|
2182
|
+
});
|
|
2183
|
+
return d ? d.snapshot : null;
|
|
2184
|
+
} catch (error) {
|
|
2185
|
+
throw new MastraError({
|
|
2186
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "LOAD_WORKFLOW_SNAPSHOT", "FAILED"),
|
|
2187
|
+
domain: ErrorDomain.STORAGE,
|
|
2188
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2189
|
+
text: `Failed to load workflow snapshot: ${error instanceof Error ? error.message : String(error)}`,
|
|
2190
|
+
details: {
|
|
2191
|
+
workflowName,
|
|
2192
|
+
runId
|
|
2193
|
+
}
|
|
2194
|
+
}, error);
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
parseWorkflowRun(row) {
|
|
2198
|
+
let parsedSnapshot = row.snapshot;
|
|
2199
|
+
if (typeof parsedSnapshot === "string") try {
|
|
2200
|
+
parsedSnapshot = JSON.parse(row.snapshot);
|
|
2201
|
+
} catch (e) {
|
|
2202
|
+
this.logger.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
|
|
2203
|
+
}
|
|
2204
|
+
return {
|
|
2205
|
+
workflowName: row.workflow_name,
|
|
2206
|
+
runId: row.run_id,
|
|
2207
|
+
snapshot: parsedSnapshot,
|
|
2208
|
+
createdAt: ensureDate(row.createdAt),
|
|
2209
|
+
updatedAt: ensureDate(row.updatedAt),
|
|
2210
|
+
resourceId: row.resourceId
|
|
2211
|
+
};
|
|
2212
|
+
}
|
|
2213
|
+
async listWorkflowRuns({ workflowName, fromDate, toDate, page, perPage, resourceId, status } = {}) {
|
|
2214
|
+
const fullTableName = this.#db.getTableName(TABLE_WORKFLOW_SNAPSHOT);
|
|
2215
|
+
try {
|
|
2216
|
+
const builder = createSqlBuilder().select().from(fullTableName);
|
|
2217
|
+
const countBuilder = createSqlBuilder().count().from(fullTableName);
|
|
2218
|
+
if (workflowName) builder.whereAnd("workflow_name = ?", workflowName);
|
|
2219
|
+
if (status) {
|
|
2220
|
+
builder.whereAnd("json_extract(snapshot, '$.status') = ?", status);
|
|
2221
|
+
countBuilder.whereAnd("json_extract(snapshot, '$.status') = ?", status);
|
|
2222
|
+
}
|
|
2223
|
+
if (resourceId) if (await this.#db.hasColumn(fullTableName, "resourceId")) {
|
|
2224
|
+
builder.whereAnd("resourceId = ?", resourceId);
|
|
2225
|
+
countBuilder.whereAnd("resourceId = ?", resourceId);
|
|
2226
|
+
} else this.logger.warn(`[${fullTableName}] resourceId column not found. Skipping resourceId filter.`);
|
|
2227
|
+
if (fromDate) {
|
|
2228
|
+
builder.whereAnd("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
|
|
2229
|
+
countBuilder.whereAnd("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
|
|
2230
|
+
}
|
|
2231
|
+
if (toDate) {
|
|
2232
|
+
builder.whereAnd("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
|
|
2233
|
+
countBuilder.whereAnd("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
|
|
2234
|
+
}
|
|
2235
|
+
builder.orderBy("createdAt", "DESC");
|
|
2236
|
+
if (typeof perPage === "number" && typeof page === "number") {
|
|
2237
|
+
const offset = page * perPage;
|
|
2238
|
+
builder.limit(perPage);
|
|
2239
|
+
builder.offset(offset);
|
|
2240
|
+
}
|
|
2241
|
+
const { sql, params } = builder.build();
|
|
2242
|
+
let total = 0;
|
|
2243
|
+
if (perPage !== void 0 && page !== void 0) {
|
|
2244
|
+
const { sql: countSql, params: countParams } = countBuilder.build();
|
|
2245
|
+
const countResult = await this.#db.executeQuery({
|
|
2246
|
+
sql: countSql,
|
|
2247
|
+
params: countParams,
|
|
2248
|
+
first: true
|
|
2249
|
+
});
|
|
2250
|
+
total = Number(countResult?.count ?? 0);
|
|
2251
|
+
}
|
|
2252
|
+
const results = await this.#db.executeQuery({
|
|
2253
|
+
sql,
|
|
2254
|
+
params
|
|
2255
|
+
});
|
|
2256
|
+
const runs = (isArrayOfRecords(results) ? results : []).map((row) => this.parseWorkflowRun(row));
|
|
2257
|
+
return {
|
|
2258
|
+
runs,
|
|
2259
|
+
total: total || runs.length
|
|
2260
|
+
};
|
|
2261
|
+
} catch (error) {
|
|
2262
|
+
throw new MastraError({
|
|
2263
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "LIST_WORKFLOW_RUNS", "FAILED"),
|
|
2264
|
+
domain: ErrorDomain.STORAGE,
|
|
2265
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2266
|
+
text: `Failed to retrieve workflow runs: ${error instanceof Error ? error.message : String(error)}`,
|
|
2267
|
+
details: {
|
|
2268
|
+
workflowName: workflowName ?? "",
|
|
2269
|
+
resourceId: resourceId ?? ""
|
|
2270
|
+
}
|
|
2271
|
+
}, error);
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
async getWorkflowRunById({ runId, workflowName }) {
|
|
2275
|
+
const fullTableName = this.#db.getTableName(TABLE_WORKFLOW_SNAPSHOT);
|
|
2276
|
+
try {
|
|
2277
|
+
const conditions = [];
|
|
2278
|
+
const params = [];
|
|
2279
|
+
if (runId) {
|
|
2280
|
+
conditions.push("run_id = ?");
|
|
2281
|
+
params.push(runId);
|
|
2282
|
+
}
|
|
2283
|
+
if (workflowName) {
|
|
2284
|
+
conditions.push("workflow_name = ?");
|
|
2285
|
+
params.push(workflowName);
|
|
2286
|
+
}
|
|
2287
|
+
const sql = `SELECT * FROM ${fullTableName} ${conditions.length > 0 ? "WHERE " + conditions.join(" AND ") : ""} ORDER BY createdAt DESC LIMIT 1`;
|
|
2288
|
+
const result = await this.#db.executeQuery({
|
|
2289
|
+
sql,
|
|
2290
|
+
params,
|
|
2291
|
+
first: true
|
|
2292
|
+
});
|
|
2293
|
+
if (!result) return null;
|
|
2294
|
+
return this.parseWorkflowRun(result);
|
|
2295
|
+
} catch (error) {
|
|
2296
|
+
throw new MastraError({
|
|
2297
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "GET_WORKFLOW_RUN_BY_ID", "FAILED"),
|
|
2298
|
+
domain: ErrorDomain.STORAGE,
|
|
2299
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2300
|
+
text: `Failed to retrieve workflow run by ID: ${error instanceof Error ? error.message : String(error)}`,
|
|
2301
|
+
details: {
|
|
2302
|
+
runId,
|
|
2303
|
+
workflowName: workflowName ?? ""
|
|
2304
|
+
}
|
|
2305
|
+
}, error);
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
async deleteWorkflowRunById({ runId, workflowName }) {
|
|
2309
|
+
const fullTableName = this.#db.getTableName(TABLE_WORKFLOW_SNAPSHOT);
|
|
2310
|
+
try {
|
|
2311
|
+
const sql = `DELETE FROM ${fullTableName} WHERE workflow_name = ? AND run_id = ?`;
|
|
2312
|
+
const params = [workflowName, runId];
|
|
2313
|
+
await this.#db.executeQuery({
|
|
2314
|
+
sql,
|
|
2315
|
+
params
|
|
2316
|
+
});
|
|
2317
|
+
} catch (error) {
|
|
2318
|
+
throw new MastraError({
|
|
2319
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "DELETE_WORKFLOW_RUN_BY_ID", "FAILED"),
|
|
2320
|
+
domain: ErrorDomain.STORAGE,
|
|
2321
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
2322
|
+
text: `Failed to delete workflow run by ID: ${error instanceof Error ? error.message : String(error)}`,
|
|
2323
|
+
details: {
|
|
2324
|
+
runId,
|
|
2325
|
+
workflowName
|
|
2326
|
+
}
|
|
2327
|
+
}, error);
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2460
2330
|
};
|
|
2461
|
-
|
|
2462
|
-
|
|
2331
|
+
//#endregion
|
|
2332
|
+
//#region src/storage/index.ts
|
|
2333
|
+
/**
|
|
2334
|
+
* Cloudflare D1 storage adapter for Mastra.
|
|
2335
|
+
*
|
|
2336
|
+
* Access domain-specific storage via `getStore()`:
|
|
2337
|
+
*
|
|
2338
|
+
* @example
|
|
2339
|
+
* ```typescript
|
|
2340
|
+
* const storage = new D1Store({ id: 'my-store', accountId: '...', apiToken: '...', databaseId: '...' });
|
|
2341
|
+
*
|
|
2342
|
+
* // Access memory domain
|
|
2343
|
+
* const memory = await storage.getStore('memory');
|
|
2344
|
+
* await memory?.saveThread({ thread });
|
|
2345
|
+
*
|
|
2346
|
+
* // Access workflows domain
|
|
2347
|
+
* const workflows = await storage.getStore('workflows');
|
|
2348
|
+
* await workflows?.persistWorkflowSnapshot({ workflowName, runId, snapshot });
|
|
2349
|
+
* ```
|
|
2350
|
+
*/
|
|
2463
2351
|
var D1Store = class extends MastraCompositeStore {
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
* No explicit cleanup needed for D1 in either REST or Workers Binding mode
|
|
2547
|
-
*/
|
|
2548
|
-
async close() {
|
|
2549
|
-
this.logger.debug("Closing D1 connection");
|
|
2550
|
-
}
|
|
2352
|
+
client;
|
|
2353
|
+
binding;
|
|
2354
|
+
tablePrefix;
|
|
2355
|
+
stores;
|
|
2356
|
+
/**
|
|
2357
|
+
* Creates a new D1Store instance
|
|
2358
|
+
* @param config Configuration for D1 access (either REST API or Workers Binding API)
|
|
2359
|
+
*/
|
|
2360
|
+
constructor(config) {
|
|
2361
|
+
try {
|
|
2362
|
+
super({
|
|
2363
|
+
id: config.id,
|
|
2364
|
+
name: "D1",
|
|
2365
|
+
disableInit: config.disableInit
|
|
2366
|
+
});
|
|
2367
|
+
if (config.tablePrefix && !/^[a-zA-Z0-9_]*$/.test(config.tablePrefix)) throw new Error("Invalid tablePrefix: only letters, numbers, and underscores are allowed.");
|
|
2368
|
+
this.tablePrefix = config.tablePrefix || "";
|
|
2369
|
+
if ("binding" in config) {
|
|
2370
|
+
if (!config.binding) throw new Error("D1 binding is required when using Workers Binding API");
|
|
2371
|
+
this.binding = config.binding;
|
|
2372
|
+
this.logger.info("Using D1 Workers Binding API");
|
|
2373
|
+
} else if ("client" in config) {
|
|
2374
|
+
if (!config.client) throw new Error("D1 client is required when using D1ClientConfig");
|
|
2375
|
+
this.client = config.client;
|
|
2376
|
+
this.logger.info("Using D1 Client");
|
|
2377
|
+
} else {
|
|
2378
|
+
if (!config.accountId || !config.databaseId || !config.apiToken) throw new Error("accountId, databaseId, and apiToken are required when using REST API");
|
|
2379
|
+
const cfClient = new Cloudflare({ apiToken: config.apiToken });
|
|
2380
|
+
this.client = { query: ({ sql, params }) => {
|
|
2381
|
+
return cfClient.d1.database.query(config.databaseId, {
|
|
2382
|
+
account_id: config.accountId,
|
|
2383
|
+
sql,
|
|
2384
|
+
params
|
|
2385
|
+
});
|
|
2386
|
+
} };
|
|
2387
|
+
this.logger.info("Using D1 REST API");
|
|
2388
|
+
}
|
|
2389
|
+
} catch (error) {
|
|
2390
|
+
throw new MastraError({
|
|
2391
|
+
id: createStorageErrorId("CLOUDFLARE_D1", "INITIALIZATION", "FAILED"),
|
|
2392
|
+
domain: ErrorDomain.STORAGE,
|
|
2393
|
+
category: ErrorCategory.SYSTEM,
|
|
2394
|
+
text: "Error initializing D1Store"
|
|
2395
|
+
}, error);
|
|
2396
|
+
}
|
|
2397
|
+
let scores;
|
|
2398
|
+
let workflows;
|
|
2399
|
+
let memory;
|
|
2400
|
+
let backgroundTasks;
|
|
2401
|
+
if (this.binding) {
|
|
2402
|
+
const domainConfig = {
|
|
2403
|
+
binding: this.binding,
|
|
2404
|
+
tablePrefix: this.tablePrefix
|
|
2405
|
+
};
|
|
2406
|
+
scores = new ScoresStorageD1(domainConfig);
|
|
2407
|
+
workflows = new WorkflowsStorageD1(domainConfig);
|
|
2408
|
+
memory = new MemoryStorageD1(domainConfig);
|
|
2409
|
+
backgroundTasks = new BackgroundTasksStorageD1(domainConfig);
|
|
2410
|
+
} else {
|
|
2411
|
+
const domainConfig = {
|
|
2412
|
+
client: this.client,
|
|
2413
|
+
tablePrefix: this.tablePrefix
|
|
2414
|
+
};
|
|
2415
|
+
scores = new ScoresStorageD1(domainConfig);
|
|
2416
|
+
workflows = new WorkflowsStorageD1(domainConfig);
|
|
2417
|
+
memory = new MemoryStorageD1(domainConfig);
|
|
2418
|
+
backgroundTasks = new BackgroundTasksStorageD1(domainConfig);
|
|
2419
|
+
}
|
|
2420
|
+
this.stores = {
|
|
2421
|
+
scores,
|
|
2422
|
+
workflows,
|
|
2423
|
+
memory,
|
|
2424
|
+
backgroundTasks
|
|
2425
|
+
};
|
|
2426
|
+
}
|
|
2427
|
+
/**
|
|
2428
|
+
* Close the database connection
|
|
2429
|
+
* No explicit cleanup needed for D1 in either REST or Workers Binding mode
|
|
2430
|
+
*/
|
|
2431
|
+
async close() {
|
|
2432
|
+
this.logger.debug("Closing D1 connection");
|
|
2433
|
+
}
|
|
2551
2434
|
};
|
|
2552
|
-
|
|
2435
|
+
//#endregion
|
|
2553
2436
|
export { BackgroundTasksStorageD1, D1Store, MemoryStorageD1, ScoresStorageD1, WorkflowsStorageD1 };
|
|
2554
|
-
|
|
2437
|
+
|
|
2555
2438
|
//# sourceMappingURL=index.js.map
|