@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/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
- this.sql = `SELECT ${Array.isArray(columns) ? columns.join(", ") : columns}`;
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
- this.sql += ` FROM ${table}`;
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
- this.sql += ` ORDER BY ${column} ${direction}`;
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 placeholders = columns.map(() => "?").join(", ");
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 ${table} (${columns.join(", ")}) VALUES (${placeholders}) ON CONFLICT(${conflictColumns.join(", ")}) DO UPDATE SET ${updateClause}`;
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 ${table} (${columns.join(", ")}) VALUES (${placeholders})`;
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 setClause = columns.map((col) => `${col} = ?`).join(", ");
98
- this.sql = `UPDATE ${table} SET ${setClause}`;
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
- this.sql = `DELETE FROM ${table}`;
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 columns = columnDefinitions.join(", ");
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 ${table} (${columns}${constraints})`;
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
- this.sql = `CREATE ${indexType ? indexType + " " : ""}INDEX IF NOT EXISTS ${indexName} ON ${tableName}(${columnName})`;
141
- return this;
142
- }
143
- // Raw SQL with params
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 ${column} LIKE ?`;
177
+ this.sql += ` AND ${parsedColumnName} LIKE ?`;
159
178
  } else {
160
- this.sql += ` WHERE ${column} LIKE ?`;
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 jsonPattern = `%"${key}":"${value}"%`;
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 ${column} LIKE ?`;
196
+ this.sql += ` AND ${parsedColumnName} LIKE ?`;
176
197
  } else {
177
- this.sql += ` WHERE ${column} LIKE ?`;
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
- super({ name: "D1" });
225
- this.tablePrefix = config.tablePrefix || "";
226
- if ("binding" in config) {
227
- if (!config.binding) {
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.binding = config.binding;
231
- this.logger.info("Using D1 Workers Binding API");
232
- } else {
233
- if (!config.accountId || !config.databaseId || !config.apiToken) {
234
- throw new Error("accountId, databaseId, and apiToken are required when using REST API");
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
- this.accountId = config.accountId;
237
- this.databaseId = config.databaseId;
238
- this.client = new Cloudflare({
239
- apiToken: config.apiToken
240
- });
241
- this.logger.info("Using D1 REST API");
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 convert storage type to SQL type
379
- getSqlType(type) {
380
- switch (type) {
381
- case "text":
382
- return "TEXT";
383
- case "timestamp":
384
- return "TIMESTAMP";
385
- case "integer":
386
- return "INTEGER";
387
- case "bigint":
388
- return "INTEGER";
389
- // SQLite doesn't have a separate BIGINT type
390
- case "jsonb":
391
- return "TEXT";
392
- // Store JSON as TEXT in SQLite
393
- default:
394
- return "TEXT";
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 Error(`Failed to create table ${fullTableName}: ${error}`);
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
- this.logger.error(`Error clearing table ${fullTableName}:`, {
475
- message: error instanceof Error ? error.message : String(error)
476
- });
477
- throw new Error(`Failed to clear table ${fullTableName}: ${error}`);
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
- const message = error instanceof Error ? error.message : String(error);
498
- this.logger.error(`Error inserting into ${fullTableName}:`, { message });
499
- throw new Error(`Failed to insert into ${fullTableName}: ${error}`);
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
- this.logger.error(`Error loading from ${fullTableName}:`, {
526
- message: error instanceof Error ? error.message : String(error)
527
- });
528
- return null;
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
- this.logger.error(`Error processing thread ${threadId}:`, {
546
- message: error instanceof Error ? error.message : String(error)
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
- this.logger.error(`Error getting threads by resourceId ${resourceId}:`, {
565
- message: error instanceof Error ? error.message : String(error)
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
- const message = error instanceof Error ? error.message : String(error);
597
- this.logger.error(`Error saving thread to ${fullTableName}:`, { message });
598
- throw error;
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
- const message = error instanceof Error ? error.message : String(error);
632
- this.logger.error("Error updating thread:", { message });
633
- throw error;
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
- this.logger.error(`Error deleting thread ${threadId}:`, {
648
- message: error instanceof Error ? error.message : String(error)
649
- });
650
- throw new Error(`Failed to delete thread ${threadId}: ${error}`);
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
- // Thread and message management methods
654
- async saveMessages({ messages }) {
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 this.batchInsert({
680
- tableName: TABLE_MESSAGES,
681
- records: messagesToInsert
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
- return messages;
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
- this.logger.error("Error saving messages:", { message: error instanceof Error ? error.message : String(error) });
687
- throw error;
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 getMessages({ threadId, selectBy }) {
691
- const fullTableName = this.getTableName(TABLE_MESSAGES);
692
- const limit = typeof selectBy?.last === "number" ? selectBy.last : 40;
693
- const include = selectBy?.include || [];
694
- const messages = [];
695
- try {
696
- if (include.length) {
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 ${fullTableName}
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 "threadId"
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
- const params2 = [
729
- threadId,
730
- ...includeIds,
731
- // for m.id IN (...)
732
- ...includeIds,
733
- // for target.id IN (...)
734
- prevMax,
735
- nextMax
736
- ];
737
- const includeResult = await this.executeQuery({ sql: sql2, params: params2 });
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
- let query = createSqlBuilder().select(["id", "content", "role", "type", '"createdAt"', 'thread_id AS "threadId"']).from(fullTableName).where("thread_id = ?", threadId).andWhere(`id NOT IN (${excludeIds.map(() => "?").join(",")})`, ...excludeIds).orderBy("createdAt", "DESC").limit(limit);
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
- return processedMessages;
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
- this.logger.error("Error retrieving messages for thread", {
763
- threadId,
764
- message: error instanceof Error ? error.message : String(error)
765
- });
766
- return [];
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
- this.logger.error("Error persisting workflow snapshot:", {
805
- message: error instanceof Error ? error.message : String(error)
806
- });
807
- throw error;
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
- const d = await this.load({
814
- tableName: TABLE_WORKFLOW_SNAPSHOT,
815
- keys: {
816
- workflow_name: workflowName,
817
- run_id: runId
818
- }
819
- });
820
- return d ? d.snapshot : null;
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
- this.logger.error(`Error batch inserting into ${tableName}:`, {
856
- message: error instanceof Error ? error.message : String(error)
857
- });
858
- throw new Error(`Failed to batch insert into ${tableName}: ${error}`);
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((page - 1) * perPage);
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((trace) => ({
894
- ...trace,
895
- attributes: this.deserializeValue(trace.attributes, "jsonb"),
896
- status: this.deserializeValue(trace.status, "jsonb"),
897
- events: this.deserializeValue(trace.events, "jsonb"),
898
- links: this.deserializeValue(trace.links, "jsonb"),
899
- other: this.deserializeValue(trace.other, "jsonb")
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
- this.logger.error("Error getting traces:", { message: error instanceof Error ? error.message : String(error) });
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
- this.logger.error(`Error getting evals for agent ${agentName}:`, {
936
- message: error instanceof Error ? error.message : String(error)
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
- this.logger.error("Error getting workflow runs:", {
1010
- message: error instanceof Error ? error.message : String(error)
1011
- });
1012
- throw error;
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
- this.logger.error("Error getting workflow run by ID:", {
1038
- message: error instanceof Error ? error.message : String(error)
1039
- });
1040
- throw error;
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 };