@mastra/cloudflare-d1 0.0.0-trigger-playground-ui-package-20250506151043 → 0.0.0-tsconfig-compile-20250703214351
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_tsup-dts-rollup.d.cts +116 -48
- package/dist/_tsup-dts-rollup.d.ts +116 -48
- package/dist/index.cjs +792 -228
- package/dist/index.d.cts +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +774 -210
- package/package.json +17 -13
package/dist/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { MessageList } from '@mastra/core/agent';
|
|
2
|
+
import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
|
|
1
3
|
import { MastraStorage, TABLE_THREADS, TABLE_MESSAGES, TABLE_WORKFLOW_SNAPSHOT, TABLE_TRACES, TABLE_EVALS } from '@mastra/core/storage';
|
|
2
4
|
import Cloudflare from 'cloudflare';
|
|
5
|
+
import { parseSqlIdentifier } from '@mastra/core/utils';
|
|
3
6
|
|
|
4
7
|
// src/storage/index.ts
|
|
5
|
-
|
|
6
|
-
// src/storage/sql-builder.ts
|
|
7
8
|
var SqlBuilder = class {
|
|
8
9
|
sql = "";
|
|
9
10
|
params = [];
|
|
@@ -13,12 +14,15 @@ var SqlBuilder = class {
|
|
|
13
14
|
if (!columns || Array.isArray(columns) && columns.length === 0) {
|
|
14
15
|
this.sql = "SELECT *";
|
|
15
16
|
} else {
|
|
16
|
-
|
|
17
|
+
const cols = Array.isArray(columns) ? columns : [columns];
|
|
18
|
+
const parsedCols = cols.map((col) => parseSelectIdentifier(col));
|
|
19
|
+
this.sql = `SELECT ${parsedCols.join(", ")}`;
|
|
17
20
|
}
|
|
18
21
|
return this;
|
|
19
22
|
}
|
|
20
23
|
from(table) {
|
|
21
|
-
|
|
24
|
+
const parsedTableName = parseSqlIdentifier(table, "table name");
|
|
25
|
+
this.sql += ` FROM ${parsedTableName}`;
|
|
22
26
|
return this;
|
|
23
27
|
}
|
|
24
28
|
/**
|
|
@@ -55,7 +59,11 @@ var SqlBuilder = class {
|
|
|
55
59
|
return this;
|
|
56
60
|
}
|
|
57
61
|
orderBy(column, direction = "ASC") {
|
|
58
|
-
|
|
62
|
+
const parsedColumn = parseSqlIdentifier(column, "column name");
|
|
63
|
+
if (!["ASC", "DESC"].includes(direction)) {
|
|
64
|
+
throw new Error(`Invalid sort direction: ${direction}`);
|
|
65
|
+
}
|
|
66
|
+
this.sql += ` ORDER BY ${parsedColumn} ${direction}`;
|
|
59
67
|
return this;
|
|
60
68
|
}
|
|
61
69
|
limit(count) {
|
|
@@ -81,27 +89,33 @@ var SqlBuilder = class {
|
|
|
81
89
|
* @param updateMap Object mapping columns to update to their new value (e.g. { name: 'excluded.name' })
|
|
82
90
|
*/
|
|
83
91
|
insert(table, columns, values, conflictColumns, updateMap) {
|
|
84
|
-
const
|
|
92
|
+
const parsedTableName = parseSqlIdentifier(table, "table name");
|
|
93
|
+
const parsedColumns = columns.map((col) => parseSqlIdentifier(col, "column name"));
|
|
94
|
+
const placeholders = parsedColumns.map(() => "?").join(", ");
|
|
85
95
|
if (conflictColumns && updateMap) {
|
|
96
|
+
const parsedConflictColumns = conflictColumns.map((col) => parseSqlIdentifier(col, "column name"));
|
|
86
97
|
const updateClause = Object.entries(updateMap).map(([col, expr]) => `${col} = ${expr}`).join(", ");
|
|
87
|
-
this.sql = `INSERT INTO ${
|
|
98
|
+
this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders}) ON CONFLICT(${parsedConflictColumns.join(", ")}) DO UPDATE SET ${updateClause}`;
|
|
88
99
|
this.params.push(...values);
|
|
89
100
|
return this;
|
|
90
101
|
}
|
|
91
|
-
this.sql = `INSERT INTO ${
|
|
102
|
+
this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders})`;
|
|
92
103
|
this.params.push(...values);
|
|
93
104
|
return this;
|
|
94
105
|
}
|
|
95
106
|
// Update operations
|
|
96
107
|
update(table, columns, values) {
|
|
97
|
-
const
|
|
98
|
-
|
|
108
|
+
const parsedTableName = parseSqlIdentifier(table, "table name");
|
|
109
|
+
const parsedColumns = columns.map((col) => parseSqlIdentifier(col, "column name"));
|
|
110
|
+
const setClause = parsedColumns.map((col) => `${col} = ?`).join(", ");
|
|
111
|
+
this.sql = `UPDATE ${parsedTableName} SET ${setClause}`;
|
|
99
112
|
this.params.push(...values);
|
|
100
113
|
return this;
|
|
101
114
|
}
|
|
102
115
|
// Delete operations
|
|
103
116
|
delete(table) {
|
|
104
|
-
|
|
117
|
+
const parsedTableName = parseSqlIdentifier(table, "table name");
|
|
118
|
+
this.sql = `DELETE FROM ${parsedTableName}`;
|
|
105
119
|
return this;
|
|
106
120
|
}
|
|
107
121
|
/**
|
|
@@ -112,9 +126,16 @@ var SqlBuilder = class {
|
|
|
112
126
|
* @returns The builder instance
|
|
113
127
|
*/
|
|
114
128
|
createTable(table, columnDefinitions, tableConstraints) {
|
|
115
|
-
const
|
|
129
|
+
const parsedTableName = parseSqlIdentifier(table, "table name");
|
|
130
|
+
const parsedColumnDefinitions = columnDefinitions.map((def) => {
|
|
131
|
+
const colName = def.split(/\s+/)[0];
|
|
132
|
+
if (!colName) throw new Error("Empty column name in definition");
|
|
133
|
+
parseSqlIdentifier(colName, "column name");
|
|
134
|
+
return def;
|
|
135
|
+
});
|
|
136
|
+
const columns = parsedColumnDefinitions.join(", ");
|
|
116
137
|
const constraints = tableConstraints && tableConstraints.length > 0 ? ", " + tableConstraints.join(", ") : "";
|
|
117
|
-
this.sql = `CREATE TABLE IF NOT EXISTS ${
|
|
138
|
+
this.sql = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (${columns}${constraints})`;
|
|
118
139
|
return this;
|
|
119
140
|
}
|
|
120
141
|
/**
|
|
@@ -137,13 +158,10 @@ var SqlBuilder = class {
|
|
|
137
158
|
* @returns The builder instance
|
|
138
159
|
*/
|
|
139
160
|
createIndex(indexName, tableName, columnName, indexType = "") {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
raw(sql, ...params) {
|
|
145
|
-
this.sql = sql;
|
|
146
|
-
this.params.push(...params);
|
|
161
|
+
const parsedIndexName = parseSqlIdentifier(indexName, "index name");
|
|
162
|
+
const parsedTableName = parseSqlIdentifier(tableName, "table name");
|
|
163
|
+
const parsedColumnName = parseSqlIdentifier(columnName, "column name");
|
|
164
|
+
this.sql = `CREATE ${indexType ? indexType + " " : ""}INDEX IF NOT EXISTS ${parsedIndexName} ON ${parsedTableName}(${parsedColumnName})`;
|
|
147
165
|
return this;
|
|
148
166
|
}
|
|
149
167
|
/**
|
|
@@ -153,11 +171,12 @@ var SqlBuilder = class {
|
|
|
153
171
|
* @param exact If true, will not add % wildcards
|
|
154
172
|
*/
|
|
155
173
|
like(column, value, exact = false) {
|
|
174
|
+
const parsedColumnName = parseSqlIdentifier(column, "column name");
|
|
156
175
|
const likeValue = exact ? value : `%${value}%`;
|
|
157
176
|
if (this.whereAdded) {
|
|
158
|
-
this.sql += ` AND ${
|
|
177
|
+
this.sql += ` AND ${parsedColumnName} LIKE ?`;
|
|
159
178
|
} else {
|
|
160
|
-
this.sql += ` WHERE ${
|
|
179
|
+
this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
|
|
161
180
|
this.whereAdded = true;
|
|
162
181
|
}
|
|
163
182
|
this.params.push(likeValue);
|
|
@@ -170,11 +189,13 @@ var SqlBuilder = class {
|
|
|
170
189
|
* @param value The value to match
|
|
171
190
|
*/
|
|
172
191
|
jsonLike(column, key, value) {
|
|
173
|
-
const
|
|
192
|
+
const parsedColumnName = parseSqlIdentifier(column, "column name");
|
|
193
|
+
const parsedKey = parseSqlIdentifier(key, "key name");
|
|
194
|
+
const jsonPattern = `%"${parsedKey}":"${value}"%`;
|
|
174
195
|
if (this.whereAdded) {
|
|
175
|
-
this.sql += ` AND ${
|
|
196
|
+
this.sql += ` AND ${parsedColumnName} LIKE ?`;
|
|
176
197
|
} else {
|
|
177
|
-
this.sql += ` WHERE ${
|
|
198
|
+
this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
|
|
178
199
|
this.whereAdded = true;
|
|
179
200
|
}
|
|
180
201
|
this.params.push(jsonPattern);
|
|
@@ -204,6 +225,15 @@ var SqlBuilder = class {
|
|
|
204
225
|
function createSqlBuilder() {
|
|
205
226
|
return new SqlBuilder();
|
|
206
227
|
}
|
|
228
|
+
var SQL_IDENTIFIER_PATTERN = /^[a-zA-Z0-9_]+(\s+AS\s+[a-zA-Z0-9_]+)?$/;
|
|
229
|
+
function parseSelectIdentifier(column) {
|
|
230
|
+
if (column !== "*" && !SQL_IDENTIFIER_PATTERN.test(column)) {
|
|
231
|
+
throw new Error(
|
|
232
|
+
`Invalid column name: "${column}". Must be "*" or a valid identifier (letters, numbers, underscores), optionally with "AS alias".`
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
return column;
|
|
236
|
+
}
|
|
207
237
|
|
|
208
238
|
// src/storage/index.ts
|
|
209
239
|
function isArrayOfRecords(value) {
|
|
@@ -221,24 +251,39 @@ var D1Store = class extends MastraStorage {
|
|
|
221
251
|
* @param config Configuration for D1 access (either REST API or Workers Binding API)
|
|
222
252
|
*/
|
|
223
253
|
constructor(config) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
throw new Error("D1 binding is required when using Workers Binding API");
|
|
254
|
+
try {
|
|
255
|
+
super({ name: "D1" });
|
|
256
|
+
if (config.tablePrefix && !/^[a-zA-Z0-9_]*$/.test(config.tablePrefix)) {
|
|
257
|
+
throw new Error("Invalid tablePrefix: only letters, numbers, and underscores are allowed.");
|
|
229
258
|
}
|
|
230
|
-
this.
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
259
|
+
this.tablePrefix = config.tablePrefix || "";
|
|
260
|
+
if ("binding" in config) {
|
|
261
|
+
if (!config.binding) {
|
|
262
|
+
throw new Error("D1 binding is required when using Workers Binding API");
|
|
263
|
+
}
|
|
264
|
+
this.binding = config.binding;
|
|
265
|
+
this.logger.info("Using D1 Workers Binding API");
|
|
266
|
+
} else {
|
|
267
|
+
if (!config.accountId || !config.databaseId || !config.apiToken) {
|
|
268
|
+
throw new Error("accountId, databaseId, and apiToken are required when using REST API");
|
|
269
|
+
}
|
|
270
|
+
this.accountId = config.accountId;
|
|
271
|
+
this.databaseId = config.databaseId;
|
|
272
|
+
this.client = new Cloudflare({
|
|
273
|
+
apiToken: config.apiToken
|
|
274
|
+
});
|
|
275
|
+
this.logger.info("Using D1 REST API");
|
|
235
276
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
277
|
+
} catch (error) {
|
|
278
|
+
throw new MastraError(
|
|
279
|
+
{
|
|
280
|
+
id: "CLOUDFLARE_D1_STORAGE_INITIALIZATION_ERROR",
|
|
281
|
+
domain: ErrorDomain.STORAGE,
|
|
282
|
+
category: ErrorCategory.SYSTEM,
|
|
283
|
+
text: "Error initializing D1Store"
|
|
284
|
+
},
|
|
285
|
+
error
|
|
286
|
+
);
|
|
242
287
|
}
|
|
243
288
|
}
|
|
244
289
|
// Helper method to get the full table name with prefix
|
|
@@ -248,30 +293,6 @@ var D1Store = class extends MastraStorage {
|
|
|
248
293
|
formatSqlParams(params) {
|
|
249
294
|
return params.map((p) => p === void 0 || p === null ? null : p);
|
|
250
295
|
}
|
|
251
|
-
// Helper method to create SQL indexes for better query performance
|
|
252
|
-
async createIndexIfNotExists(tableName, columnName, indexType = "") {
|
|
253
|
-
const fullTableName = this.getTableName(tableName);
|
|
254
|
-
const indexName = `idx_${tableName}_${columnName}`;
|
|
255
|
-
try {
|
|
256
|
-
const checkQuery = createSqlBuilder().checkIndexExists(indexName, fullTableName);
|
|
257
|
-
const { sql: checkSql, params: checkParams } = checkQuery.build();
|
|
258
|
-
const indexExists = await this.executeQuery({
|
|
259
|
-
sql: checkSql,
|
|
260
|
-
params: checkParams,
|
|
261
|
-
first: true
|
|
262
|
-
});
|
|
263
|
-
if (!indexExists) {
|
|
264
|
-
const createQuery = createSqlBuilder().createIndex(indexName, fullTableName, columnName, indexType);
|
|
265
|
-
const { sql: createSql, params: createParams } = createQuery.build();
|
|
266
|
-
await this.executeQuery({ sql: createSql, params: createParams });
|
|
267
|
-
this.logger.debug(`Created index ${indexName} on ${fullTableName}(${columnName})`);
|
|
268
|
-
}
|
|
269
|
-
} catch (error) {
|
|
270
|
-
this.logger.error(`Error creating index on ${fullTableName}(${columnName}):`, {
|
|
271
|
-
message: error instanceof Error ? error.message : String(error)
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
296
|
async executeWorkersBindingQuery({
|
|
276
297
|
sql,
|
|
277
298
|
params = [],
|
|
@@ -375,34 +396,25 @@ var D1Store = class extends MastraStorage {
|
|
|
375
396
|
throw new Error(`D1 query error: ${error.message}`);
|
|
376
397
|
}
|
|
377
398
|
}
|
|
378
|
-
// Helper to
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
return
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
399
|
+
// Helper to get existing table columns
|
|
400
|
+
async getTableColumns(tableName) {
|
|
401
|
+
try {
|
|
402
|
+
const sql = `PRAGMA table_info(${tableName})`;
|
|
403
|
+
const result = await this.executeQuery({ sql, params: [] });
|
|
404
|
+
if (!result || !Array.isArray(result)) {
|
|
405
|
+
return [];
|
|
406
|
+
}
|
|
407
|
+
return result.map((row) => ({
|
|
408
|
+
name: row.name,
|
|
409
|
+
type: row.type
|
|
410
|
+
}));
|
|
411
|
+
} catch (error) {
|
|
412
|
+
this.logger.error(`Error getting table columns for ${tableName}:`, {
|
|
413
|
+
message: error instanceof Error ? error.message : String(error)
|
|
414
|
+
});
|
|
415
|
+
return [];
|
|
395
416
|
}
|
|
396
417
|
}
|
|
397
|
-
ensureDate(date) {
|
|
398
|
-
if (!date) return void 0;
|
|
399
|
-
return date instanceof Date ? date : new Date(date);
|
|
400
|
-
}
|
|
401
|
-
serializeDate(date) {
|
|
402
|
-
if (!date) return void 0;
|
|
403
|
-
const dateObj = this.ensureDate(date);
|
|
404
|
-
return dateObj?.toISOString();
|
|
405
|
-
}
|
|
406
418
|
// Helper to serialize objects to JSON strings
|
|
407
419
|
serializeValue(value) {
|
|
408
420
|
if (value === null || value === void 0) return null;
|
|
@@ -436,6 +448,18 @@ var D1Store = class extends MastraStorage {
|
|
|
436
448
|
}
|
|
437
449
|
return value;
|
|
438
450
|
}
|
|
451
|
+
getSqlType(type) {
|
|
452
|
+
switch (type) {
|
|
453
|
+
case "bigint":
|
|
454
|
+
return "INTEGER";
|
|
455
|
+
// SQLite uses INTEGER for all integer sizes
|
|
456
|
+
case "jsonb":
|
|
457
|
+
return "TEXT";
|
|
458
|
+
// Store JSON as TEXT in SQLite
|
|
459
|
+
default:
|
|
460
|
+
return super.getSqlType(type);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
439
463
|
async createTable({
|
|
440
464
|
tableName,
|
|
441
465
|
schema
|
|
@@ -451,16 +475,64 @@ var D1Store = class extends MastraStorage {
|
|
|
451
475
|
if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
|
|
452
476
|
tableConstraints.push("UNIQUE (workflow_name, run_id)");
|
|
453
477
|
}
|
|
454
|
-
const query = createSqlBuilder().createTable(fullTableName, columnDefinitions, tableConstraints);
|
|
455
|
-
const { sql, params } = query.build();
|
|
456
478
|
try {
|
|
479
|
+
const query = createSqlBuilder().createTable(fullTableName, columnDefinitions, tableConstraints);
|
|
480
|
+
const { sql, params } = query.build();
|
|
457
481
|
await this.executeQuery({ sql, params });
|
|
458
482
|
this.logger.debug(`Created table ${fullTableName}`);
|
|
459
483
|
} catch (error) {
|
|
460
484
|
this.logger.error(`Error creating table ${fullTableName}:`, {
|
|
461
485
|
message: error instanceof Error ? error.message : String(error)
|
|
462
486
|
});
|
|
463
|
-
throw new
|
|
487
|
+
throw new MastraError(
|
|
488
|
+
{
|
|
489
|
+
id: "CLOUDFLARE_D1_STORAGE_CREATE_TABLE_ERROR",
|
|
490
|
+
domain: ErrorDomain.STORAGE,
|
|
491
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
492
|
+
text: `Failed to create table ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
493
|
+
details: { tableName }
|
|
494
|
+
},
|
|
495
|
+
error
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Alters table schema to add columns if they don't exist
|
|
501
|
+
* @param tableName Name of the table
|
|
502
|
+
* @param schema Schema of the table
|
|
503
|
+
* @param ifNotExists Array of column names to add if they don't exist
|
|
504
|
+
*/
|
|
505
|
+
async alterTable({
|
|
506
|
+
tableName,
|
|
507
|
+
schema,
|
|
508
|
+
ifNotExists
|
|
509
|
+
}) {
|
|
510
|
+
const fullTableName = this.getTableName(tableName);
|
|
511
|
+
try {
|
|
512
|
+
const existingColumns = await this.getTableColumns(fullTableName);
|
|
513
|
+
const existingColumnNames = new Set(existingColumns.map((col) => col.name.toLowerCase()));
|
|
514
|
+
for (const columnName of ifNotExists) {
|
|
515
|
+
if (!existingColumnNames.has(columnName.toLowerCase()) && schema[columnName]) {
|
|
516
|
+
const columnDef = schema[columnName];
|
|
517
|
+
const sqlType = this.getSqlType(columnDef.type);
|
|
518
|
+
const nullable = columnDef.nullable === false ? "NOT NULL" : "";
|
|
519
|
+
const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
|
|
520
|
+
const alterSql = `ALTER TABLE ${fullTableName} ADD COLUMN ${columnName} ${sqlType} ${nullable} ${defaultValue}`.trim();
|
|
521
|
+
await this.executeQuery({ sql: alterSql, params: [] });
|
|
522
|
+
this.logger.debug(`Added column ${columnName} to table ${fullTableName}`);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
} catch (error) {
|
|
526
|
+
throw new MastraError(
|
|
527
|
+
{
|
|
528
|
+
id: "CLOUDFLARE_D1_STORAGE_ALTER_TABLE_ERROR",
|
|
529
|
+
domain: ErrorDomain.STORAGE,
|
|
530
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
531
|
+
text: `Failed to alter table ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
532
|
+
details: { tableName }
|
|
533
|
+
},
|
|
534
|
+
error
|
|
535
|
+
);
|
|
464
536
|
}
|
|
465
537
|
}
|
|
466
538
|
async clearTable({ tableName }) {
|
|
@@ -471,10 +543,16 @@ var D1Store = class extends MastraStorage {
|
|
|
471
543
|
await this.executeQuery({ sql, params });
|
|
472
544
|
this.logger.debug(`Cleared table ${fullTableName}`);
|
|
473
545
|
} catch (error) {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
546
|
+
throw new MastraError(
|
|
547
|
+
{
|
|
548
|
+
id: "CLOUDFLARE_D1_STORAGE_CLEAR_TABLE_ERROR",
|
|
549
|
+
domain: ErrorDomain.STORAGE,
|
|
550
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
551
|
+
text: `Failed to clear table ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
552
|
+
details: { tableName }
|
|
553
|
+
},
|
|
554
|
+
error
|
|
555
|
+
);
|
|
478
556
|
}
|
|
479
557
|
}
|
|
480
558
|
async processRecord(record) {
|
|
@@ -494,9 +572,16 @@ var D1Store = class extends MastraStorage {
|
|
|
494
572
|
try {
|
|
495
573
|
await this.executeQuery({ sql, params });
|
|
496
574
|
} catch (error) {
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
575
|
+
throw new MastraError(
|
|
576
|
+
{
|
|
577
|
+
id: "CLOUDFLARE_D1_STORAGE_INSERT_ERROR",
|
|
578
|
+
domain: ErrorDomain.STORAGE,
|
|
579
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
580
|
+
text: `Failed to insert into ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
581
|
+
details: { tableName }
|
|
582
|
+
},
|
|
583
|
+
error
|
|
584
|
+
);
|
|
500
585
|
}
|
|
501
586
|
}
|
|
502
587
|
async load({ tableName, keys }) {
|
|
@@ -522,10 +607,16 @@ var D1Store = class extends MastraStorage {
|
|
|
522
607
|
}
|
|
523
608
|
return processedResult;
|
|
524
609
|
} catch (error) {
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
610
|
+
throw new MastraError(
|
|
611
|
+
{
|
|
612
|
+
id: "CLOUDFLARE_D1_STORAGE_LOAD_ERROR",
|
|
613
|
+
domain: ErrorDomain.STORAGE,
|
|
614
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
615
|
+
text: `Failed to load from ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
616
|
+
details: { tableName }
|
|
617
|
+
},
|
|
618
|
+
error
|
|
619
|
+
);
|
|
529
620
|
}
|
|
530
621
|
}
|
|
531
622
|
async getThreadById({ threadId }) {
|
|
@@ -542,12 +633,24 @@ var D1Store = class extends MastraStorage {
|
|
|
542
633
|
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata || "{}") : thread.metadata || {}
|
|
543
634
|
};
|
|
544
635
|
} catch (error) {
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
636
|
+
const mastraError = new MastraError(
|
|
637
|
+
{
|
|
638
|
+
id: "CLOUDFLARE_D1_STORAGE_GET_THREAD_BY_ID_ERROR",
|
|
639
|
+
domain: ErrorDomain.STORAGE,
|
|
640
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
641
|
+
text: `Error processing thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
642
|
+
details: { threadId }
|
|
643
|
+
},
|
|
644
|
+
error
|
|
645
|
+
);
|
|
646
|
+
this.logger?.error(mastraError.toString());
|
|
647
|
+
this.logger?.trackException(mastraError);
|
|
548
648
|
return null;
|
|
549
649
|
}
|
|
550
650
|
}
|
|
651
|
+
/**
|
|
652
|
+
* @deprecated use getThreadsByResourceIdPaginated instead
|
|
653
|
+
*/
|
|
551
654
|
async getThreadsByResourceId({ resourceId }) {
|
|
552
655
|
const fullTableName = this.getTableName(TABLE_THREADS);
|
|
553
656
|
try {
|
|
@@ -561,12 +664,66 @@ var D1Store = class extends MastraStorage {
|
|
|
561
664
|
metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata || "{}") : thread.metadata || {}
|
|
562
665
|
}));
|
|
563
666
|
} catch (error) {
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
667
|
+
const mastraError = new MastraError(
|
|
668
|
+
{
|
|
669
|
+
id: "CLOUDFLARE_D1_STORAGE_GET_THREADS_BY_RESOURCE_ID_ERROR",
|
|
670
|
+
domain: ErrorDomain.STORAGE,
|
|
671
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
672
|
+
text: `Error getting threads by resourceId ${resourceId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
673
|
+
details: { resourceId }
|
|
674
|
+
},
|
|
675
|
+
error
|
|
676
|
+
);
|
|
677
|
+
this.logger?.error(mastraError.toString());
|
|
678
|
+
this.logger?.trackException(mastraError);
|
|
567
679
|
return [];
|
|
568
680
|
}
|
|
569
681
|
}
|
|
682
|
+
async getThreadsByResourceIdPaginated(args) {
|
|
683
|
+
const { resourceId, page, perPage } = args;
|
|
684
|
+
const fullTableName = this.getTableName(TABLE_THREADS);
|
|
685
|
+
const mapRowToStorageThreadType = (row) => ({
|
|
686
|
+
...row,
|
|
687
|
+
createdAt: this.ensureDate(row.createdAt),
|
|
688
|
+
updatedAt: this.ensureDate(row.updatedAt),
|
|
689
|
+
metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata || "{}") : row.metadata || {}
|
|
690
|
+
});
|
|
691
|
+
try {
|
|
692
|
+
const countQuery = createSqlBuilder().count().from(fullTableName).where("resourceId = ?", resourceId);
|
|
693
|
+
const countResult = await this.executeQuery(countQuery.build());
|
|
694
|
+
const total = Number(countResult?.[0]?.count ?? 0);
|
|
695
|
+
const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("resourceId = ?", resourceId).orderBy("createdAt", "DESC").limit(perPage).offset(page * perPage);
|
|
696
|
+
const results = await this.executeQuery(selectQuery.build());
|
|
697
|
+
const threads = results.map(mapRowToStorageThreadType);
|
|
698
|
+
return {
|
|
699
|
+
threads,
|
|
700
|
+
total,
|
|
701
|
+
page,
|
|
702
|
+
perPage,
|
|
703
|
+
hasMore: page * perPage + threads.length < total
|
|
704
|
+
};
|
|
705
|
+
} catch (error) {
|
|
706
|
+
const mastraError = new MastraError(
|
|
707
|
+
{
|
|
708
|
+
id: "CLOUDFLARE_D1_STORAGE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_ERROR",
|
|
709
|
+
domain: ErrorDomain.STORAGE,
|
|
710
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
711
|
+
text: `Error getting threads by resourceId ${resourceId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
712
|
+
details: { resourceId }
|
|
713
|
+
},
|
|
714
|
+
error
|
|
715
|
+
);
|
|
716
|
+
this.logger?.error(mastraError.toString());
|
|
717
|
+
this.logger?.trackException(mastraError);
|
|
718
|
+
return {
|
|
719
|
+
threads: [],
|
|
720
|
+
total: 0,
|
|
721
|
+
page,
|
|
722
|
+
perPage,
|
|
723
|
+
hasMore: false
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
}
|
|
570
727
|
async saveThread({ thread }) {
|
|
571
728
|
const fullTableName = this.getTableName(TABLE_THREADS);
|
|
572
729
|
const threadToSave = {
|
|
@@ -593,9 +750,16 @@ var D1Store = class extends MastraStorage {
|
|
|
593
750
|
await this.executeQuery({ sql, params });
|
|
594
751
|
return thread;
|
|
595
752
|
} catch (error) {
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
753
|
+
throw new MastraError(
|
|
754
|
+
{
|
|
755
|
+
id: "CLOUDFLARE_D1_STORAGE_SAVE_THREAD_ERROR",
|
|
756
|
+
domain: ErrorDomain.STORAGE,
|
|
757
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
758
|
+
text: `Failed to save thread to ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
759
|
+
details: { threadId: thread.id }
|
|
760
|
+
},
|
|
761
|
+
error
|
|
762
|
+
);
|
|
599
763
|
}
|
|
600
764
|
}
|
|
601
765
|
async updateThread({
|
|
@@ -604,19 +768,19 @@ var D1Store = class extends MastraStorage {
|
|
|
604
768
|
metadata
|
|
605
769
|
}) {
|
|
606
770
|
const thread = await this.getThreadById({ threadId: id });
|
|
607
|
-
if (!thread) {
|
|
608
|
-
throw new Error(`Thread ${id} not found`);
|
|
609
|
-
}
|
|
610
|
-
const fullTableName = this.getTableName(TABLE_THREADS);
|
|
611
|
-
const mergedMetadata = {
|
|
612
|
-
...typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
613
|
-
...metadata
|
|
614
|
-
};
|
|
615
|
-
const columns = ["title", "metadata", "updatedAt"];
|
|
616
|
-
const values = [title, JSON.stringify(mergedMetadata), (/* @__PURE__ */ new Date()).toISOString()];
|
|
617
|
-
const query = createSqlBuilder().update(fullTableName, columns, values).where("id = ?", id);
|
|
618
|
-
const { sql, params } = query.build();
|
|
619
771
|
try {
|
|
772
|
+
if (!thread) {
|
|
773
|
+
throw new Error(`Thread ${id} not found`);
|
|
774
|
+
}
|
|
775
|
+
const fullTableName = this.getTableName(TABLE_THREADS);
|
|
776
|
+
const mergedMetadata = {
|
|
777
|
+
...typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
|
|
778
|
+
...metadata
|
|
779
|
+
};
|
|
780
|
+
const columns = ["title", "metadata", "updatedAt"];
|
|
781
|
+
const values = [title, JSON.stringify(mergedMetadata), (/* @__PURE__ */ new Date()).toISOString()];
|
|
782
|
+
const query = createSqlBuilder().update(fullTableName, columns, values).where("id = ?", id);
|
|
783
|
+
const { sql, params } = query.build();
|
|
620
784
|
await this.executeQuery({ sql, params });
|
|
621
785
|
return {
|
|
622
786
|
...thread,
|
|
@@ -628,9 +792,16 @@ var D1Store = class extends MastraStorage {
|
|
|
628
792
|
updatedAt: /* @__PURE__ */ new Date()
|
|
629
793
|
};
|
|
630
794
|
} catch (error) {
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
795
|
+
throw new MastraError(
|
|
796
|
+
{
|
|
797
|
+
id: "CLOUDFLARE_D1_STORAGE_UPDATE_THREAD_ERROR",
|
|
798
|
+
domain: ErrorDomain.STORAGE,
|
|
799
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
800
|
+
text: `Failed to update thread ${id}: ${error instanceof Error ? error.message : String(error)}`,
|
|
801
|
+
details: { threadId: id }
|
|
802
|
+
},
|
|
803
|
+
error
|
|
804
|
+
);
|
|
634
805
|
}
|
|
635
806
|
}
|
|
636
807
|
async deleteThread({ threadId }) {
|
|
@@ -644,17 +815,24 @@ var D1Store = class extends MastraStorage {
|
|
|
644
815
|
const { sql: messagesSql, params: messagesParams } = deleteMessagesQuery.build();
|
|
645
816
|
await this.executeQuery({ sql: messagesSql, params: messagesParams });
|
|
646
817
|
} catch (error) {
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
818
|
+
throw new MastraError(
|
|
819
|
+
{
|
|
820
|
+
id: "CLOUDFLARE_D1_STORAGE_DELETE_THREAD_ERROR",
|
|
821
|
+
domain: ErrorDomain.STORAGE,
|
|
822
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
823
|
+
text: `Failed to delete thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
824
|
+
details: { threadId }
|
|
825
|
+
},
|
|
826
|
+
error
|
|
827
|
+
);
|
|
651
828
|
}
|
|
652
829
|
}
|
|
653
|
-
|
|
654
|
-
|
|
830
|
+
async saveMessages(args) {
|
|
831
|
+
const { messages, format = "v1" } = args;
|
|
655
832
|
if (messages.length === 0) return [];
|
|
656
833
|
try {
|
|
657
834
|
const now = /* @__PURE__ */ new Date();
|
|
835
|
+
const threadId = messages[0]?.threadId;
|
|
658
836
|
for (const [i, message] of messages.entries()) {
|
|
659
837
|
if (!message.id) throw new Error(`Message at index ${i} missing id`);
|
|
660
838
|
if (!message.threadId) throw new Error(`Message at index ${i} missing threadId`);
|
|
@@ -673,36 +851,49 @@ var D1Store = class extends MastraStorage {
|
|
|
673
851
|
content: typeof message.content === "string" ? message.content : JSON.stringify(message.content),
|
|
674
852
|
createdAt: createdAt.toISOString(),
|
|
675
853
|
role: message.role,
|
|
676
|
-
type: message.type
|
|
854
|
+
type: message.type || "v2",
|
|
855
|
+
resourceId: message.resourceId
|
|
677
856
|
};
|
|
678
857
|
});
|
|
679
|
-
await
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
858
|
+
await Promise.all([
|
|
859
|
+
this.batchUpsert({
|
|
860
|
+
tableName: TABLE_MESSAGES,
|
|
861
|
+
records: messagesToInsert
|
|
862
|
+
}),
|
|
863
|
+
// Update thread's updatedAt timestamp
|
|
864
|
+
this.executeQuery({
|
|
865
|
+
sql: `UPDATE ${this.getTableName(TABLE_THREADS)} SET updatedAt = ? WHERE id = ?`,
|
|
866
|
+
params: [now.toISOString(), threadId]
|
|
867
|
+
})
|
|
868
|
+
]);
|
|
683
869
|
this.logger.debug(`Saved ${messages.length} messages`);
|
|
684
|
-
|
|
870
|
+
const list = new MessageList().add(messages, "memory");
|
|
871
|
+
if (format === `v2`) return list.get.all.v2();
|
|
872
|
+
return list.get.all.v1();
|
|
685
873
|
} catch (error) {
|
|
686
|
-
|
|
687
|
-
|
|
874
|
+
throw new MastraError(
|
|
875
|
+
{
|
|
876
|
+
id: "CLOUDFLARE_D1_STORAGE_SAVE_MESSAGES_ERROR",
|
|
877
|
+
domain: ErrorDomain.STORAGE,
|
|
878
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
879
|
+
text: `Failed to save messages: ${error instanceof Error ? error.message : String(error)}`
|
|
880
|
+
},
|
|
881
|
+
error
|
|
882
|
+
);
|
|
688
883
|
}
|
|
689
884
|
}
|
|
690
|
-
async
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
const
|
|
694
|
-
const
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
const prevMax = Math.max(...include.map((i) => i.withPreviousMessages || 0));
|
|
698
|
-
const nextMax = Math.max(...include.map((i) => i.withNextMessages || 0));
|
|
699
|
-
const includeIds = include.map((i) => i.id);
|
|
700
|
-
const sql2 = `
|
|
885
|
+
async _getIncludedMessages(threadId, selectBy) {
|
|
886
|
+
const include = selectBy?.include;
|
|
887
|
+
if (!include) return null;
|
|
888
|
+
const prevMax = Math.max(...include.map((i) => i.withPreviousMessages || 0));
|
|
889
|
+
const nextMax = Math.max(...include.map((i) => i.withNextMessages || 0));
|
|
890
|
+
const includeIds = include.map((i) => i.id);
|
|
891
|
+
const sql = `
|
|
701
892
|
WITH ordered_messages AS (
|
|
702
893
|
SELECT
|
|
703
894
|
*,
|
|
704
895
|
ROW_NUMBER() OVER (ORDER BY createdAt DESC) AS row_num
|
|
705
|
-
FROM ${
|
|
896
|
+
FROM ${this.getTableName(TABLE_MESSAGES)}
|
|
706
897
|
WHERE thread_id = ?
|
|
707
898
|
)
|
|
708
899
|
SELECT
|
|
@@ -711,7 +902,7 @@ var D1Store = class extends MastraStorage {
|
|
|
711
902
|
m.role,
|
|
712
903
|
m.type,
|
|
713
904
|
m.createdAt,
|
|
714
|
-
m.thread_id AS
|
|
905
|
+
m.thread_id AS threadId
|
|
715
906
|
FROM ordered_messages m
|
|
716
907
|
WHERE m.id IN (${includeIds.map(() => "?").join(",")})
|
|
717
908
|
OR EXISTS (
|
|
@@ -725,20 +916,38 @@ var D1Store = class extends MastraStorage {
|
|
|
725
916
|
)
|
|
726
917
|
ORDER BY m.createdAt DESC
|
|
727
918
|
`;
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
919
|
+
const params = [
|
|
920
|
+
threadId,
|
|
921
|
+
...includeIds,
|
|
922
|
+
// for m.id IN (...)
|
|
923
|
+
...includeIds,
|
|
924
|
+
// for target.id IN (...)
|
|
925
|
+
prevMax,
|
|
926
|
+
nextMax
|
|
927
|
+
];
|
|
928
|
+
const messages = await this.executeQuery({ sql, params });
|
|
929
|
+
return messages;
|
|
930
|
+
}
|
|
931
|
+
async getMessages({
|
|
932
|
+
threadId,
|
|
933
|
+
selectBy,
|
|
934
|
+
format
|
|
935
|
+
}) {
|
|
936
|
+
const fullTableName = this.getTableName(TABLE_MESSAGES);
|
|
937
|
+
const limit = this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
|
|
938
|
+
const include = selectBy?.include || [];
|
|
939
|
+
const messages = [];
|
|
940
|
+
try {
|
|
941
|
+
if (include.length) {
|
|
942
|
+
const includeResult = await this._getIncludedMessages(threadId, selectBy);
|
|
738
943
|
if (Array.isArray(includeResult)) messages.push(...includeResult);
|
|
739
944
|
}
|
|
740
945
|
const excludeIds = messages.map((m) => m.id);
|
|
741
|
-
|
|
946
|
+
const query = createSqlBuilder().select(["id", "content", "role", "type", "createdAt", "thread_id AS threadId"]).from(fullTableName).where("thread_id = ?", threadId);
|
|
947
|
+
if (excludeIds.length > 0) {
|
|
948
|
+
query.andWhere(`id NOT IN (${excludeIds.map(() => "?").join(",")})`, ...excludeIds);
|
|
949
|
+
}
|
|
950
|
+
query.orderBy("createdAt", "DESC").limit(limit);
|
|
742
951
|
const { sql, params } = query.build();
|
|
743
952
|
const result = await this.executeQuery({ sql, params });
|
|
744
953
|
if (Array.isArray(result)) messages.push(...result);
|
|
@@ -752,18 +961,92 @@ var D1Store = class extends MastraStorage {
|
|
|
752
961
|
const processedMessages = messages.map((message) => {
|
|
753
962
|
const processedMsg = {};
|
|
754
963
|
for (const [key, value] of Object.entries(message)) {
|
|
964
|
+
if (key === `type` && value === `v2`) continue;
|
|
755
965
|
processedMsg[key] = this.deserializeValue(value);
|
|
756
966
|
}
|
|
757
967
|
return processedMsg;
|
|
758
968
|
});
|
|
759
969
|
this.logger.debug(`Retrieved ${messages.length} messages for thread ${threadId}`);
|
|
760
|
-
|
|
970
|
+
const list = new MessageList().add(processedMessages, "memory");
|
|
971
|
+
if (format === `v2`) return list.get.all.v2();
|
|
972
|
+
return list.get.all.v1();
|
|
761
973
|
} catch (error) {
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
974
|
+
const mastraError = new MastraError(
|
|
975
|
+
{
|
|
976
|
+
id: "CLOUDFLARE_D1_STORAGE_GET_MESSAGES_ERROR",
|
|
977
|
+
domain: ErrorDomain.STORAGE,
|
|
978
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
979
|
+
text: `Failed to retrieve messages for thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
980
|
+
details: { threadId }
|
|
981
|
+
},
|
|
982
|
+
error
|
|
983
|
+
);
|
|
984
|
+
this.logger?.error(mastraError.toString());
|
|
985
|
+
this.logger?.trackException(mastraError);
|
|
986
|
+
throw mastraError;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
async getMessagesPaginated({
|
|
990
|
+
threadId,
|
|
991
|
+
selectBy,
|
|
992
|
+
format
|
|
993
|
+
}) {
|
|
994
|
+
const { dateRange, page = 0, perPage = 40 } = selectBy?.pagination || {};
|
|
995
|
+
const { start: fromDate, end: toDate } = dateRange || {};
|
|
996
|
+
const fullTableName = this.getTableName(TABLE_MESSAGES);
|
|
997
|
+
const messages = [];
|
|
998
|
+
try {
|
|
999
|
+
if (selectBy?.include?.length) {
|
|
1000
|
+
const includeResult = await this._getIncludedMessages(threadId, selectBy);
|
|
1001
|
+
if (Array.isArray(includeResult)) messages.push(...includeResult);
|
|
1002
|
+
}
|
|
1003
|
+
const countQuery = createSqlBuilder().count().from(fullTableName).where("thread_id = ?", threadId);
|
|
1004
|
+
if (fromDate) {
|
|
1005
|
+
countQuery.andWhere("createdAt >= ?", this.serializeDate(fromDate));
|
|
1006
|
+
}
|
|
1007
|
+
if (toDate) {
|
|
1008
|
+
countQuery.andWhere("createdAt <= ?", this.serializeDate(toDate));
|
|
1009
|
+
}
|
|
1010
|
+
const countResult = await this.executeQuery(countQuery.build());
|
|
1011
|
+
const total = Number(countResult[0]?.count ?? 0);
|
|
1012
|
+
const query = createSqlBuilder().select(["id", "content", "role", "type", "createdAt", "thread_id AS threadId"]).from(fullTableName).where("thread_id = ?", threadId);
|
|
1013
|
+
if (fromDate) {
|
|
1014
|
+
query.andWhere("createdAt >= ?", this.serializeDate(fromDate));
|
|
1015
|
+
}
|
|
1016
|
+
if (toDate) {
|
|
1017
|
+
query.andWhere("createdAt <= ?", this.serializeDate(toDate));
|
|
1018
|
+
}
|
|
1019
|
+
query.orderBy("createdAt", "DESC").limit(perPage).offset(page * perPage);
|
|
1020
|
+
const results = await this.executeQuery(query.build());
|
|
1021
|
+
const list = new MessageList().add(results, "memory");
|
|
1022
|
+
messages.push(...format === `v2` ? list.get.all.v2() : list.get.all.v1());
|
|
1023
|
+
return {
|
|
1024
|
+
messages,
|
|
1025
|
+
total,
|
|
1026
|
+
page,
|
|
1027
|
+
perPage,
|
|
1028
|
+
hasMore: page * perPage + messages.length < total
|
|
1029
|
+
};
|
|
1030
|
+
} catch (error) {
|
|
1031
|
+
const mastraError = new MastraError(
|
|
1032
|
+
{
|
|
1033
|
+
id: "CLOUDFLARE_D1_STORAGE_GET_MESSAGES_PAGINATED_ERROR",
|
|
1034
|
+
domain: ErrorDomain.STORAGE,
|
|
1035
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1036
|
+
text: `Failed to retrieve messages for thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1037
|
+
details: { threadId }
|
|
1038
|
+
},
|
|
1039
|
+
error
|
|
1040
|
+
);
|
|
1041
|
+
this.logger?.error(mastraError.toString());
|
|
1042
|
+
this.logger?.trackException(mastraError);
|
|
1043
|
+
return {
|
|
1044
|
+
messages: [],
|
|
1045
|
+
total: 0,
|
|
1046
|
+
page,
|
|
1047
|
+
perPage,
|
|
1048
|
+
hasMore: false
|
|
1049
|
+
};
|
|
767
1050
|
}
|
|
768
1051
|
}
|
|
769
1052
|
async persistWorkflowSnapshot({
|
|
@@ -801,23 +1084,42 @@ var D1Store = class extends MastraStorage {
|
|
|
801
1084
|
try {
|
|
802
1085
|
await this.executeQuery({ sql, params });
|
|
803
1086
|
} catch (error) {
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
1087
|
+
throw new MastraError(
|
|
1088
|
+
{
|
|
1089
|
+
id: "CLOUDFLARE_D1_STORAGE_PERSIST_WORKFLOW_SNAPSHOT_ERROR",
|
|
1090
|
+
domain: ErrorDomain.STORAGE,
|
|
1091
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1092
|
+
text: `Failed to persist workflow snapshot: ${error instanceof Error ? error.message : String(error)}`,
|
|
1093
|
+
details: { workflowName, runId }
|
|
1094
|
+
},
|
|
1095
|
+
error
|
|
1096
|
+
);
|
|
808
1097
|
}
|
|
809
1098
|
}
|
|
810
1099
|
async loadWorkflowSnapshot(params) {
|
|
811
1100
|
const { workflowName, runId } = params;
|
|
812
1101
|
this.logger.debug("Loading workflow snapshot", { workflowName, runId });
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
1102
|
+
try {
|
|
1103
|
+
const d = await this.load({
|
|
1104
|
+
tableName: TABLE_WORKFLOW_SNAPSHOT,
|
|
1105
|
+
keys: {
|
|
1106
|
+
workflow_name: workflowName,
|
|
1107
|
+
run_id: runId
|
|
1108
|
+
}
|
|
1109
|
+
});
|
|
1110
|
+
return d ? d.snapshot : null;
|
|
1111
|
+
} catch (error) {
|
|
1112
|
+
throw new MastraError(
|
|
1113
|
+
{
|
|
1114
|
+
id: "CLOUDFLARE_D1_STORAGE_LOAD_WORKFLOW_SNAPSHOT_ERROR",
|
|
1115
|
+
domain: ErrorDomain.STORAGE,
|
|
1116
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1117
|
+
text: `Failed to load workflow snapshot: ${error instanceof Error ? error.message : String(error)}`,
|
|
1118
|
+
details: { workflowName, runId }
|
|
1119
|
+
},
|
|
1120
|
+
error
|
|
1121
|
+
);
|
|
1122
|
+
}
|
|
821
1123
|
}
|
|
822
1124
|
/**
|
|
823
1125
|
* Insert multiple records in a batch operation
|
|
@@ -852,12 +1154,76 @@ var D1Store = class extends MastraStorage {
|
|
|
852
1154
|
}
|
|
853
1155
|
this.logger.debug(`Successfully batch inserted ${records.length} records into ${tableName}`);
|
|
854
1156
|
} catch (error) {
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
1157
|
+
throw new MastraError(
|
|
1158
|
+
{
|
|
1159
|
+
id: "CLOUDFLARE_D1_STORAGE_BATCH_INSERT_ERROR",
|
|
1160
|
+
domain: ErrorDomain.STORAGE,
|
|
1161
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1162
|
+
text: `Failed to batch insert into ${tableName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1163
|
+
details: { tableName }
|
|
1164
|
+
},
|
|
1165
|
+
error
|
|
1166
|
+
);
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
/**
|
|
1170
|
+
* Upsert multiple records in a batch operation
|
|
1171
|
+
* @param tableName The table to insert into
|
|
1172
|
+
* @param records The records to insert
|
|
1173
|
+
*/
|
|
1174
|
+
async batchUpsert({
|
|
1175
|
+
tableName,
|
|
1176
|
+
records
|
|
1177
|
+
}) {
|
|
1178
|
+
if (records.length === 0) return;
|
|
1179
|
+
const fullTableName = this.getTableName(tableName);
|
|
1180
|
+
try {
|
|
1181
|
+
const batchSize = 50;
|
|
1182
|
+
for (let i = 0; i < records.length; i += batchSize) {
|
|
1183
|
+
const batch = records.slice(i, i + batchSize);
|
|
1184
|
+
const recordsToInsert = batch;
|
|
1185
|
+
if (recordsToInsert.length > 0) {
|
|
1186
|
+
const firstRecord = recordsToInsert[0];
|
|
1187
|
+
const columns = Object.keys(firstRecord || {});
|
|
1188
|
+
for (const record of recordsToInsert) {
|
|
1189
|
+
const values = columns.map((col) => {
|
|
1190
|
+
if (!record) return null;
|
|
1191
|
+
const value = typeof col === "string" ? record[col] : null;
|
|
1192
|
+
return this.serializeValue(value);
|
|
1193
|
+
});
|
|
1194
|
+
const recordToUpsert = columns.reduce(
|
|
1195
|
+
(acc, col) => {
|
|
1196
|
+
if (col !== "createdAt") acc[col] = `excluded.${col}`;
|
|
1197
|
+
return acc;
|
|
1198
|
+
},
|
|
1199
|
+
{}
|
|
1200
|
+
);
|
|
1201
|
+
const query = createSqlBuilder().insert(fullTableName, columns, values, ["id"], recordToUpsert);
|
|
1202
|
+
const { sql, params } = query.build();
|
|
1203
|
+
await this.executeQuery({ sql, params });
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
this.logger.debug(
|
|
1207
|
+
`Processed batch ${Math.floor(i / batchSize) + 1} of ${Math.ceil(records.length / batchSize)}`
|
|
1208
|
+
);
|
|
1209
|
+
}
|
|
1210
|
+
this.logger.debug(`Successfully batch upserted ${records.length} records into ${tableName}`);
|
|
1211
|
+
} catch (error) {
|
|
1212
|
+
throw new MastraError(
|
|
1213
|
+
{
|
|
1214
|
+
id: "CLOUDFLARE_D1_STORAGE_BATCH_UPSERT_ERROR",
|
|
1215
|
+
domain: ErrorDomain.STORAGE,
|
|
1216
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1217
|
+
text: `Failed to batch upsert into ${tableName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1218
|
+
details: { tableName }
|
|
1219
|
+
},
|
|
1220
|
+
error
|
|
1221
|
+
);
|
|
859
1222
|
}
|
|
860
1223
|
}
|
|
1224
|
+
/**
|
|
1225
|
+
* @deprecated use getTracesPaginated instead
|
|
1226
|
+
*/
|
|
861
1227
|
async getTraces({
|
|
862
1228
|
name,
|
|
863
1229
|
scope,
|
|
@@ -887,22 +1253,108 @@ var D1Store = class extends MastraStorage {
|
|
|
887
1253
|
if (toDate) {
|
|
888
1254
|
query.andWhere("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
|
|
889
1255
|
}
|
|
890
|
-
query.orderBy("startTime", "DESC").limit(perPage).offset(
|
|
1256
|
+
query.orderBy("startTime", "DESC").limit(perPage).offset(page * perPage);
|
|
891
1257
|
const { sql, params } = query.build();
|
|
892
1258
|
const results = await this.executeQuery({ sql, params });
|
|
893
|
-
return isArrayOfRecords(results) ? results.map(
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
1259
|
+
return isArrayOfRecords(results) ? results.map(
|
|
1260
|
+
(trace) => ({
|
|
1261
|
+
...trace,
|
|
1262
|
+
attributes: this.deserializeValue(trace.attributes, "jsonb"),
|
|
1263
|
+
status: this.deserializeValue(trace.status, "jsonb"),
|
|
1264
|
+
events: this.deserializeValue(trace.events, "jsonb"),
|
|
1265
|
+
links: this.deserializeValue(trace.links, "jsonb"),
|
|
1266
|
+
other: this.deserializeValue(trace.other, "jsonb")
|
|
1267
|
+
})
|
|
1268
|
+
) : [];
|
|
901
1269
|
} catch (error) {
|
|
902
|
-
|
|
1270
|
+
const mastraError = new MastraError(
|
|
1271
|
+
{
|
|
1272
|
+
id: "CLOUDFLARE_D1_STORAGE_GET_TRACES_ERROR",
|
|
1273
|
+
domain: ErrorDomain.STORAGE,
|
|
1274
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1275
|
+
text: `Failed to retrieve traces: ${error instanceof Error ? error.message : String(error)}`,
|
|
1276
|
+
details: {
|
|
1277
|
+
name: name ?? "",
|
|
1278
|
+
scope: scope ?? ""
|
|
1279
|
+
}
|
|
1280
|
+
},
|
|
1281
|
+
error
|
|
1282
|
+
);
|
|
1283
|
+
this.logger?.error(mastraError.toString());
|
|
1284
|
+
this.logger?.trackException(mastraError);
|
|
903
1285
|
return [];
|
|
904
1286
|
}
|
|
905
1287
|
}
|
|
1288
|
+
async getTracesPaginated(args) {
|
|
1289
|
+
const { name, scope, page, perPage, attributes, fromDate, toDate } = args;
|
|
1290
|
+
const fullTableName = this.getTableName(TABLE_TRACES);
|
|
1291
|
+
try {
|
|
1292
|
+
const dataQuery = createSqlBuilder().select("*").from(fullTableName).where("1=1");
|
|
1293
|
+
const countQuery = createSqlBuilder().count().from(fullTableName).where("1=1");
|
|
1294
|
+
if (name) {
|
|
1295
|
+
dataQuery.andWhere("name LIKE ?", `%${name}%`);
|
|
1296
|
+
countQuery.andWhere("name LIKE ?", `%${name}%`);
|
|
1297
|
+
}
|
|
1298
|
+
if (scope) {
|
|
1299
|
+
dataQuery.andWhere("scope = ?", scope);
|
|
1300
|
+
countQuery.andWhere("scope = ?", scope);
|
|
1301
|
+
}
|
|
1302
|
+
if (attributes && Object.keys(attributes).length > 0) {
|
|
1303
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
1304
|
+
dataQuery.jsonLike("attributes", key, value);
|
|
1305
|
+
countQuery.jsonLike("attributes", key, value);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
if (fromDate) {
|
|
1309
|
+
const fromDateStr = fromDate instanceof Date ? fromDate.toISOString() : fromDate;
|
|
1310
|
+
dataQuery.andWhere("createdAt >= ?", fromDateStr);
|
|
1311
|
+
countQuery.andWhere("createdAt >= ?", fromDateStr);
|
|
1312
|
+
}
|
|
1313
|
+
if (toDate) {
|
|
1314
|
+
const toDateStr = toDate instanceof Date ? toDate.toISOString() : toDate;
|
|
1315
|
+
dataQuery.andWhere("createdAt <= ?", toDateStr);
|
|
1316
|
+
countQuery.andWhere("createdAt <= ?", toDateStr);
|
|
1317
|
+
}
|
|
1318
|
+
const countResult = await this.executeQuery(countQuery.build());
|
|
1319
|
+
const total = Number(countResult?.[0]?.count ?? 0);
|
|
1320
|
+
dataQuery.orderBy("startTime", "DESC").limit(perPage).offset(page * perPage);
|
|
1321
|
+
const results = await this.executeQuery(dataQuery.build());
|
|
1322
|
+
const traces = isArrayOfRecords(results) ? results.map(
|
|
1323
|
+
(trace) => ({
|
|
1324
|
+
...trace,
|
|
1325
|
+
attributes: this.deserializeValue(trace.attributes, "jsonb"),
|
|
1326
|
+
status: this.deserializeValue(trace.status, "jsonb"),
|
|
1327
|
+
events: this.deserializeValue(trace.events, "jsonb"),
|
|
1328
|
+
links: this.deserializeValue(trace.links, "jsonb"),
|
|
1329
|
+
other: this.deserializeValue(trace.other, "jsonb")
|
|
1330
|
+
})
|
|
1331
|
+
) : [];
|
|
1332
|
+
return {
|
|
1333
|
+
traces,
|
|
1334
|
+
total,
|
|
1335
|
+
page,
|
|
1336
|
+
perPage,
|
|
1337
|
+
hasMore: page * perPage + traces.length < total
|
|
1338
|
+
};
|
|
1339
|
+
} catch (error) {
|
|
1340
|
+
const mastraError = new MastraError(
|
|
1341
|
+
{
|
|
1342
|
+
id: "CLOUDFLARE_D1_STORAGE_GET_TRACES_ERROR",
|
|
1343
|
+
domain: ErrorDomain.STORAGE,
|
|
1344
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1345
|
+
text: `Failed to retrieve traces: ${error instanceof Error ? error.message : String(error)}`,
|
|
1346
|
+
details: { name: name ?? "", scope: scope ?? "" }
|
|
1347
|
+
},
|
|
1348
|
+
error
|
|
1349
|
+
);
|
|
1350
|
+
this.logger?.error(mastraError.toString());
|
|
1351
|
+
this.logger?.trackException(mastraError);
|
|
1352
|
+
return { traces: [], total: 0, page, perPage, hasMore: false };
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
/**
|
|
1356
|
+
* @deprecated use getEvals instead
|
|
1357
|
+
*/
|
|
906
1358
|
async getEvalsByAgentName(agentName, type) {
|
|
907
1359
|
const fullTableName = this.getTableName(TABLE_EVALS);
|
|
908
1360
|
try {
|
|
@@ -932,12 +1384,108 @@ var D1Store = class extends MastraStorage {
|
|
|
932
1384
|
};
|
|
933
1385
|
}) : [];
|
|
934
1386
|
} catch (error) {
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
1387
|
+
const mastraError = new MastraError(
|
|
1388
|
+
{
|
|
1389
|
+
id: "CLOUDFLARE_D1_STORAGE_GET_EVALS_ERROR",
|
|
1390
|
+
domain: ErrorDomain.STORAGE,
|
|
1391
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1392
|
+
text: `Failed to retrieve evals for agent ${agentName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1393
|
+
details: { agentName }
|
|
1394
|
+
},
|
|
1395
|
+
error
|
|
1396
|
+
);
|
|
1397
|
+
this.logger?.error(mastraError.toString());
|
|
1398
|
+
this.logger?.trackException(mastraError);
|
|
938
1399
|
return [];
|
|
939
1400
|
}
|
|
940
1401
|
}
|
|
1402
|
+
async getEvals(options) {
|
|
1403
|
+
const { agentName, type, page = 0, perPage = 40, fromDate, toDate } = options || {};
|
|
1404
|
+
const fullTableName = this.getTableName(TABLE_EVALS);
|
|
1405
|
+
const conditions = [];
|
|
1406
|
+
const queryParams = [];
|
|
1407
|
+
if (agentName) {
|
|
1408
|
+
conditions.push(`agent_name = ?`);
|
|
1409
|
+
queryParams.push(agentName);
|
|
1410
|
+
}
|
|
1411
|
+
if (type === "test") {
|
|
1412
|
+
conditions.push(`(test_info IS NOT NULL AND json_extract(test_info, '$.testPath') IS NOT NULL)`);
|
|
1413
|
+
} else if (type === "live") {
|
|
1414
|
+
conditions.push(`(test_info IS NULL OR json_extract(test_info, '$.testPath') IS NULL)`);
|
|
1415
|
+
}
|
|
1416
|
+
if (fromDate) {
|
|
1417
|
+
conditions.push(`createdAt >= ?`);
|
|
1418
|
+
queryParams.push(this.serializeDate(fromDate));
|
|
1419
|
+
}
|
|
1420
|
+
if (toDate) {
|
|
1421
|
+
conditions.push(`createdAt <= ?`);
|
|
1422
|
+
queryParams.push(this.serializeDate(toDate));
|
|
1423
|
+
}
|
|
1424
|
+
const countQueryBuilder = createSqlBuilder().count().from(fullTableName);
|
|
1425
|
+
if (conditions.length > 0) {
|
|
1426
|
+
countQueryBuilder.where(conditions.join(" AND "), ...queryParams);
|
|
1427
|
+
}
|
|
1428
|
+
const { sql: countSql, params: countParams } = countQueryBuilder.build();
|
|
1429
|
+
try {
|
|
1430
|
+
const countResult = await this.executeQuery({ sql: countSql, params: countParams, first: true });
|
|
1431
|
+
const total = Number(countResult?.count || 0);
|
|
1432
|
+
const currentOffset = page * perPage;
|
|
1433
|
+
if (total === 0) {
|
|
1434
|
+
return {
|
|
1435
|
+
evals: [],
|
|
1436
|
+
total: 0,
|
|
1437
|
+
page,
|
|
1438
|
+
perPage,
|
|
1439
|
+
hasMore: false
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
const dataQueryBuilder = createSqlBuilder().select("*").from(fullTableName);
|
|
1443
|
+
if (conditions.length > 0) {
|
|
1444
|
+
dataQueryBuilder.where(conditions.join(" AND "), ...queryParams);
|
|
1445
|
+
}
|
|
1446
|
+
dataQueryBuilder.orderBy("createdAt", "DESC").limit(perPage).offset(currentOffset);
|
|
1447
|
+
const { sql: dataSql, params: dataParams } = dataQueryBuilder.build();
|
|
1448
|
+
const rows = await this.executeQuery({ sql: dataSql, params: dataParams });
|
|
1449
|
+
const evals = (isArrayOfRecords(rows) ? rows : []).map((row) => {
|
|
1450
|
+
const result = this.deserializeValue(row.result);
|
|
1451
|
+
const testInfo = row.test_info ? this.deserializeValue(row.test_info) : void 0;
|
|
1452
|
+
if (!result || typeof result !== "object" || !("score" in result)) {
|
|
1453
|
+
throw new Error(`Invalid MetricResult format: ${JSON.stringify(result)}`);
|
|
1454
|
+
}
|
|
1455
|
+
return {
|
|
1456
|
+
input: row.input,
|
|
1457
|
+
output: row.output,
|
|
1458
|
+
result,
|
|
1459
|
+
agentName: row.agent_name,
|
|
1460
|
+
metricName: row.metric_name,
|
|
1461
|
+
instructions: row.instructions,
|
|
1462
|
+
testInfo,
|
|
1463
|
+
globalRunId: row.global_run_id,
|
|
1464
|
+
runId: row.run_id,
|
|
1465
|
+
createdAt: row.createdAt
|
|
1466
|
+
};
|
|
1467
|
+
});
|
|
1468
|
+
const hasMore = currentOffset + evals.length < total;
|
|
1469
|
+
return {
|
|
1470
|
+
evals,
|
|
1471
|
+
total,
|
|
1472
|
+
page,
|
|
1473
|
+
perPage,
|
|
1474
|
+
hasMore
|
|
1475
|
+
};
|
|
1476
|
+
} catch (error) {
|
|
1477
|
+
throw new MastraError(
|
|
1478
|
+
{
|
|
1479
|
+
id: "CLOUDFLARE_D1_STORAGE_GET_EVALS_ERROR",
|
|
1480
|
+
domain: ErrorDomain.STORAGE,
|
|
1481
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1482
|
+
text: `Failed to retrieve evals for agent ${agentName}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1483
|
+
details: { agentName: agentName ?? "", type: type ?? "" }
|
|
1484
|
+
},
|
|
1485
|
+
error
|
|
1486
|
+
);
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
941
1489
|
parseWorkflowRun(row) {
|
|
942
1490
|
let parsedSnapshot = row.snapshot;
|
|
943
1491
|
if (typeof parsedSnapshot === "string") {
|
|
@@ -1006,10 +1554,16 @@ var D1Store = class extends MastraStorage {
|
|
|
1006
1554
|
const runs = (isArrayOfRecords(results) ? results : []).map((row) => this.parseWorkflowRun(row));
|
|
1007
1555
|
return { runs, total: total || runs.length };
|
|
1008
1556
|
} catch (error) {
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1557
|
+
throw new MastraError(
|
|
1558
|
+
{
|
|
1559
|
+
id: "CLOUDFLARE_D1_STORAGE_GET_WORKFLOW_RUNS_ERROR",
|
|
1560
|
+
domain: ErrorDomain.STORAGE,
|
|
1561
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1562
|
+
text: `Failed to retrieve workflow runs: ${error instanceof Error ? error.message : String(error)}`,
|
|
1563
|
+
details: { workflowName: workflowName ?? "", resourceId: resourceId ?? "" }
|
|
1564
|
+
},
|
|
1565
|
+
error
|
|
1566
|
+
);
|
|
1013
1567
|
}
|
|
1014
1568
|
}
|
|
1015
1569
|
async getWorkflowRunById({
|
|
@@ -1034,10 +1588,16 @@ var D1Store = class extends MastraStorage {
|
|
|
1034
1588
|
if (!result) return null;
|
|
1035
1589
|
return this.parseWorkflowRun(result);
|
|
1036
1590
|
} catch (error) {
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1591
|
+
throw new MastraError(
|
|
1592
|
+
{
|
|
1593
|
+
id: "CLOUDFLARE_D1_STORAGE_GET_WORKFLOW_RUN_BY_ID_ERROR",
|
|
1594
|
+
domain: ErrorDomain.STORAGE,
|
|
1595
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
1596
|
+
text: `Failed to retrieve workflow run by ID: ${error instanceof Error ? error.message : String(error)}`,
|
|
1597
|
+
details: { runId, workflowName: workflowName ?? "" }
|
|
1598
|
+
},
|
|
1599
|
+
error
|
|
1600
|
+
);
|
|
1041
1601
|
}
|
|
1042
1602
|
}
|
|
1043
1603
|
/**
|
|
@@ -1047,6 +1607,10 @@ var D1Store = class extends MastraStorage {
|
|
|
1047
1607
|
async close() {
|
|
1048
1608
|
this.logger.debug("Closing D1 connection");
|
|
1049
1609
|
}
|
|
1610
|
+
async updateMessages(_args) {
|
|
1611
|
+
this.logger.error("updateMessages is not yet implemented in CloudflareD1Store");
|
|
1612
|
+
throw new Error("Method not implemented");
|
|
1613
|
+
}
|
|
1050
1614
|
};
|
|
1051
1615
|
|
|
1052
1616
|
export { D1Store };
|