@mastra/cloudflare-d1 0.0.0-separate-trace-data-from-component-20250501042644 → 0.0.0-share-agent-metadata-with-cloud-20250718110128

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) {
@@ -211,8 +241,6 @@ function isArrayOfRecords(value) {
211
241
  }
212
242
  var D1Store = class extends MastraStorage {
213
243
  client;
214
- accountId;
215
- databaseId;
216
244
  binding;
217
245
  // D1Database binding
218
246
  tablePrefix;
@@ -221,24 +249,52 @@ var D1Store = class extends MastraStorage {
221
249
  * @param config Configuration for D1 access (either REST API or Workers Binding API)
222
250
  */
223
251
  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");
252
+ try {
253
+ super({ name: "D1" });
254
+ if (config.tablePrefix && !/^[a-zA-Z0-9_]*$/.test(config.tablePrefix)) {
255
+ throw new Error("Invalid tablePrefix: only letters, numbers, and underscores are allowed.");
229
256
  }
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");
257
+ this.tablePrefix = config.tablePrefix || "";
258
+ if ("binding" in config) {
259
+ if (!config.binding) {
260
+ throw new Error("D1 binding is required when using Workers Binding API");
261
+ }
262
+ this.binding = config.binding;
263
+ this.logger.info("Using D1 Workers Binding API");
264
+ } else if ("client" in config) {
265
+ if (!config.client) {
266
+ throw new Error("D1 client is required when using D1ClientConfig");
267
+ }
268
+ this.client = config.client;
269
+ this.logger.info("Using D1 Client");
270
+ } else {
271
+ if (!config.accountId || !config.databaseId || !config.apiToken) {
272
+ throw new Error("accountId, databaseId, and apiToken are required when using REST API");
273
+ }
274
+ const cfClient = new Cloudflare({
275
+ apiToken: config.apiToken
276
+ });
277
+ this.client = {
278
+ query: ({ sql, params }) => {
279
+ return cfClient.d1.database.query(config.databaseId, {
280
+ account_id: config.accountId,
281
+ sql,
282
+ params
283
+ });
284
+ }
285
+ };
286
+ this.logger.info("Using D1 REST API");
235
287
  }
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");
288
+ } catch (error) {
289
+ throw new MastraError(
290
+ {
291
+ id: "CLOUDFLARE_D1_STORAGE_INITIALIZATION_ERROR",
292
+ domain: ErrorDomain.STORAGE,
293
+ category: ErrorCategory.SYSTEM,
294
+ text: "Error initializing D1Store"
295
+ },
296
+ error
297
+ );
242
298
  }
243
299
  }
244
300
  // Helper method to get the full table name with prefix
@@ -248,30 +304,6 @@ var D1Store = class extends MastraStorage {
248
304
  formatSqlParams(params) {
249
305
  return params.map((p) => p === void 0 || p === null ? null : p);
250
306
  }
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
307
  async executeWorkersBindingQuery({
276
308
  sql,
277
309
  params = [],
@@ -324,12 +356,11 @@ var D1Store = class extends MastraStorage {
324
356
  params = [],
325
357
  first = false
326
358
  }) {
327
- if (!this.client || !this.accountId || !this.databaseId) {
359
+ if (!this.client) {
328
360
  throw new Error("Missing required REST API configuration");
329
361
  }
330
362
  try {
331
- const response = await this.client.d1.database.query(this.databaseId, {
332
- account_id: this.accountId,
363
+ const response = await this.client.query({
333
364
  sql,
334
365
  params: this.formatSqlParams(params)
335
366
  });
@@ -360,7 +391,7 @@ var D1Store = class extends MastraStorage {
360
391
  this.logger.debug("Executing SQL query", { sql, params, first });
361
392
  if (this.binding) {
362
393
  return this.executeWorkersBindingQuery({ sql, params, first });
363
- } else if (this.client && this.accountId && this.databaseId) {
394
+ } else if (this.client) {
364
395
  return this.executeRestQuery({ sql, params, first });
365
396
  } else {
366
397
  throw new Error("No valid D1 configuration provided");
@@ -375,34 +406,25 @@ var D1Store = class extends MastraStorage {
375
406
  throw new Error(`D1 query error: ${error.message}`);
376
407
  }
377
408
  }
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";
409
+ // Helper to get existing table columns
410
+ async getTableColumns(tableName) {
411
+ try {
412
+ const sql = `PRAGMA table_info(${tableName})`;
413
+ const result = await this.executeQuery({ sql, params: [] });
414
+ if (!result || !Array.isArray(result)) {
415
+ return [];
416
+ }
417
+ return result.map((row) => ({
418
+ name: row.name,
419
+ type: row.type
420
+ }));
421
+ } catch (error) {
422
+ this.logger.error(`Error getting table columns for ${tableName}:`, {
423
+ message: error instanceof Error ? error.message : String(error)
424
+ });
425
+ return [];
395
426
  }
396
427
  }
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
428
  // Helper to serialize objects to JSON strings
407
429
  serializeValue(value) {
408
430
  if (value === null || value === void 0) return null;
@@ -436,6 +458,18 @@ var D1Store = class extends MastraStorage {
436
458
  }
437
459
  return value;
438
460
  }
461
+ getSqlType(type) {
462
+ switch (type) {
463
+ case "bigint":
464
+ return "INTEGER";
465
+ // SQLite uses INTEGER for all integer sizes
466
+ case "jsonb":
467
+ return "TEXT";
468
+ // Store JSON as TEXT in SQLite
469
+ default:
470
+ return super.getSqlType(type);
471
+ }
472
+ }
439
473
  async createTable({
440
474
  tableName,
441
475
  schema
@@ -451,16 +485,64 @@ var D1Store = class extends MastraStorage {
451
485
  if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
452
486
  tableConstraints.push("UNIQUE (workflow_name, run_id)");
453
487
  }
454
- const query = createSqlBuilder().createTable(fullTableName, columnDefinitions, tableConstraints);
455
- const { sql, params } = query.build();
456
488
  try {
489
+ const query = createSqlBuilder().createTable(fullTableName, columnDefinitions, tableConstraints);
490
+ const { sql, params } = query.build();
457
491
  await this.executeQuery({ sql, params });
458
492
  this.logger.debug(`Created table ${fullTableName}`);
459
493
  } catch (error) {
460
494
  this.logger.error(`Error creating table ${fullTableName}:`, {
461
495
  message: error instanceof Error ? error.message : String(error)
462
496
  });
463
- throw new Error(`Failed to create table ${fullTableName}: ${error}`);
497
+ throw new MastraError(
498
+ {
499
+ id: "CLOUDFLARE_D1_STORAGE_CREATE_TABLE_ERROR",
500
+ domain: ErrorDomain.STORAGE,
501
+ category: ErrorCategory.THIRD_PARTY,
502
+ text: `Failed to create table ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
503
+ details: { tableName }
504
+ },
505
+ error
506
+ );
507
+ }
508
+ }
509
+ /**
510
+ * Alters table schema to add columns if they don't exist
511
+ * @param tableName Name of the table
512
+ * @param schema Schema of the table
513
+ * @param ifNotExists Array of column names to add if they don't exist
514
+ */
515
+ async alterTable({
516
+ tableName,
517
+ schema,
518
+ ifNotExists
519
+ }) {
520
+ const fullTableName = this.getTableName(tableName);
521
+ try {
522
+ const existingColumns = await this.getTableColumns(fullTableName);
523
+ const existingColumnNames = new Set(existingColumns.map((col) => col.name.toLowerCase()));
524
+ for (const columnName of ifNotExists) {
525
+ if (!existingColumnNames.has(columnName.toLowerCase()) && schema[columnName]) {
526
+ const columnDef = schema[columnName];
527
+ const sqlType = this.getSqlType(columnDef.type);
528
+ const nullable = columnDef.nullable === false ? "NOT NULL" : "";
529
+ const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
530
+ const alterSql = `ALTER TABLE ${fullTableName} ADD COLUMN ${columnName} ${sqlType} ${nullable} ${defaultValue}`.trim();
531
+ await this.executeQuery({ sql: alterSql, params: [] });
532
+ this.logger.debug(`Added column ${columnName} to table ${fullTableName}`);
533
+ }
534
+ }
535
+ } catch (error) {
536
+ throw new MastraError(
537
+ {
538
+ id: "CLOUDFLARE_D1_STORAGE_ALTER_TABLE_ERROR",
539
+ domain: ErrorDomain.STORAGE,
540
+ category: ErrorCategory.THIRD_PARTY,
541
+ text: `Failed to alter table ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
542
+ details: { tableName }
543
+ },
544
+ error
545
+ );
464
546
  }
465
547
  }
466
548
  async clearTable({ tableName }) {
@@ -471,10 +553,16 @@ var D1Store = class extends MastraStorage {
471
553
  await this.executeQuery({ sql, params });
472
554
  this.logger.debug(`Cleared table ${fullTableName}`);
473
555
  } 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}`);
556
+ throw new MastraError(
557
+ {
558
+ id: "CLOUDFLARE_D1_STORAGE_CLEAR_TABLE_ERROR",
559
+ domain: ErrorDomain.STORAGE,
560
+ category: ErrorCategory.THIRD_PARTY,
561
+ text: `Failed to clear table ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
562
+ details: { tableName }
563
+ },
564
+ error
565
+ );
478
566
  }
479
567
  }
480
568
  async processRecord(record) {
@@ -494,9 +582,16 @@ var D1Store = class extends MastraStorage {
494
582
  try {
495
583
  await this.executeQuery({ sql, params });
496
584
  } 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}`);
585
+ throw new MastraError(
586
+ {
587
+ id: "CLOUDFLARE_D1_STORAGE_INSERT_ERROR",
588
+ domain: ErrorDomain.STORAGE,
589
+ category: ErrorCategory.THIRD_PARTY,
590
+ text: `Failed to insert into ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
591
+ details: { tableName }
592
+ },
593
+ error
594
+ );
500
595
  }
501
596
  }
502
597
  async load({ tableName, keys }) {
@@ -522,10 +617,16 @@ var D1Store = class extends MastraStorage {
522
617
  }
523
618
  return processedResult;
524
619
  } catch (error) {
525
- this.logger.error(`Error loading from ${fullTableName}:`, {
526
- message: error instanceof Error ? error.message : String(error)
527
- });
528
- return null;
620
+ throw new MastraError(
621
+ {
622
+ id: "CLOUDFLARE_D1_STORAGE_LOAD_ERROR",
623
+ domain: ErrorDomain.STORAGE,
624
+ category: ErrorCategory.THIRD_PARTY,
625
+ text: `Failed to load from ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
626
+ details: { tableName }
627
+ },
628
+ error
629
+ );
529
630
  }
530
631
  }
531
632
  async getThreadById({ threadId }) {
@@ -542,12 +643,24 @@ var D1Store = class extends MastraStorage {
542
643
  metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata || "{}") : thread.metadata || {}
543
644
  };
544
645
  } catch (error) {
545
- this.logger.error(`Error processing thread ${threadId}:`, {
546
- message: error instanceof Error ? error.message : String(error)
547
- });
646
+ const mastraError = new MastraError(
647
+ {
648
+ id: "CLOUDFLARE_D1_STORAGE_GET_THREAD_BY_ID_ERROR",
649
+ domain: ErrorDomain.STORAGE,
650
+ category: ErrorCategory.THIRD_PARTY,
651
+ text: `Error processing thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`,
652
+ details: { threadId }
653
+ },
654
+ error
655
+ );
656
+ this.logger?.error(mastraError.toString());
657
+ this.logger?.trackException(mastraError);
548
658
  return null;
549
659
  }
550
660
  }
661
+ /**
662
+ * @deprecated use getThreadsByResourceIdPaginated instead
663
+ */
551
664
  async getThreadsByResourceId({ resourceId }) {
552
665
  const fullTableName = this.getTableName(TABLE_THREADS);
553
666
  try {
@@ -561,12 +674,66 @@ var D1Store = class extends MastraStorage {
561
674
  metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata || "{}") : thread.metadata || {}
562
675
  }));
563
676
  } catch (error) {
564
- this.logger.error(`Error getting threads by resourceId ${resourceId}:`, {
565
- message: error instanceof Error ? error.message : String(error)
566
- });
677
+ const mastraError = new MastraError(
678
+ {
679
+ id: "CLOUDFLARE_D1_STORAGE_GET_THREADS_BY_RESOURCE_ID_ERROR",
680
+ domain: ErrorDomain.STORAGE,
681
+ category: ErrorCategory.THIRD_PARTY,
682
+ text: `Error getting threads by resourceId ${resourceId}: ${error instanceof Error ? error.message : String(error)}`,
683
+ details: { resourceId }
684
+ },
685
+ error
686
+ );
687
+ this.logger?.error(mastraError.toString());
688
+ this.logger?.trackException(mastraError);
567
689
  return [];
568
690
  }
569
691
  }
692
+ async getThreadsByResourceIdPaginated(args) {
693
+ const { resourceId, page, perPage } = args;
694
+ const fullTableName = this.getTableName(TABLE_THREADS);
695
+ const mapRowToStorageThreadType = (row) => ({
696
+ ...row,
697
+ createdAt: this.ensureDate(row.createdAt),
698
+ updatedAt: this.ensureDate(row.updatedAt),
699
+ metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata || "{}") : row.metadata || {}
700
+ });
701
+ try {
702
+ const countQuery = createSqlBuilder().count().from(fullTableName).where("resourceId = ?", resourceId);
703
+ const countResult = await this.executeQuery(countQuery.build());
704
+ const total = Number(countResult?.[0]?.count ?? 0);
705
+ const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("resourceId = ?", resourceId).orderBy("createdAt", "DESC").limit(perPage).offset(page * perPage);
706
+ const results = await this.executeQuery(selectQuery.build());
707
+ const threads = results.map(mapRowToStorageThreadType);
708
+ return {
709
+ threads,
710
+ total,
711
+ page,
712
+ perPage,
713
+ hasMore: page * perPage + threads.length < total
714
+ };
715
+ } catch (error) {
716
+ const mastraError = new MastraError(
717
+ {
718
+ id: "CLOUDFLARE_D1_STORAGE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_ERROR",
719
+ domain: ErrorDomain.STORAGE,
720
+ category: ErrorCategory.THIRD_PARTY,
721
+ text: `Error getting threads by resourceId ${resourceId}: ${error instanceof Error ? error.message : String(error)}`,
722
+ details: { resourceId }
723
+ },
724
+ error
725
+ );
726
+ this.logger?.error(mastraError.toString());
727
+ this.logger?.trackException(mastraError);
728
+ return {
729
+ threads: [],
730
+ total: 0,
731
+ page,
732
+ perPage,
733
+ hasMore: false
734
+ };
735
+ }
736
+ }
570
737
  async saveThread({ thread }) {
571
738
  const fullTableName = this.getTableName(TABLE_THREADS);
572
739
  const threadToSave = {
@@ -593,9 +760,16 @@ var D1Store = class extends MastraStorage {
593
760
  await this.executeQuery({ sql, params });
594
761
  return thread;
595
762
  } 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;
763
+ throw new MastraError(
764
+ {
765
+ id: "CLOUDFLARE_D1_STORAGE_SAVE_THREAD_ERROR",
766
+ domain: ErrorDomain.STORAGE,
767
+ category: ErrorCategory.THIRD_PARTY,
768
+ text: `Failed to save thread to ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
769
+ details: { threadId: thread.id }
770
+ },
771
+ error
772
+ );
599
773
  }
600
774
  }
601
775
  async updateThread({
@@ -604,19 +778,19 @@ var D1Store = class extends MastraStorage {
604
778
  metadata
605
779
  }) {
606
780
  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
781
  try {
782
+ if (!thread) {
783
+ throw new Error(`Thread ${id} not found`);
784
+ }
785
+ const fullTableName = this.getTableName(TABLE_THREADS);
786
+ const mergedMetadata = {
787
+ ...typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
788
+ ...metadata
789
+ };
790
+ const columns = ["title", "metadata", "updatedAt"];
791
+ const values = [title, JSON.stringify(mergedMetadata), (/* @__PURE__ */ new Date()).toISOString()];
792
+ const query = createSqlBuilder().update(fullTableName, columns, values).where("id = ?", id);
793
+ const { sql, params } = query.build();
620
794
  await this.executeQuery({ sql, params });
621
795
  return {
622
796
  ...thread,
@@ -628,9 +802,16 @@ var D1Store = class extends MastraStorage {
628
802
  updatedAt: /* @__PURE__ */ new Date()
629
803
  };
630
804
  } catch (error) {
631
- const message = error instanceof Error ? error.message : String(error);
632
- this.logger.error("Error updating thread:", { message });
633
- throw error;
805
+ throw new MastraError(
806
+ {
807
+ id: "CLOUDFLARE_D1_STORAGE_UPDATE_THREAD_ERROR",
808
+ domain: ErrorDomain.STORAGE,
809
+ category: ErrorCategory.THIRD_PARTY,
810
+ text: `Failed to update thread ${id}: ${error instanceof Error ? error.message : String(error)}`,
811
+ details: { threadId: id }
812
+ },
813
+ error
814
+ );
634
815
  }
635
816
  }
636
817
  async deleteThread({ threadId }) {
@@ -644,22 +825,35 @@ var D1Store = class extends MastraStorage {
644
825
  const { sql: messagesSql, params: messagesParams } = deleteMessagesQuery.build();
645
826
  await this.executeQuery({ sql: messagesSql, params: messagesParams });
646
827
  } 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}`);
828
+ throw new MastraError(
829
+ {
830
+ id: "CLOUDFLARE_D1_STORAGE_DELETE_THREAD_ERROR",
831
+ domain: ErrorDomain.STORAGE,
832
+ category: ErrorCategory.THIRD_PARTY,
833
+ text: `Failed to delete thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`,
834
+ details: { threadId }
835
+ },
836
+ error
837
+ );
651
838
  }
652
839
  }
653
- // Thread and message management methods
654
- async saveMessages({ messages }) {
840
+ async saveMessages(args) {
841
+ const { messages, format = "v1" } = args;
655
842
  if (messages.length === 0) return [];
656
843
  try {
657
844
  const now = /* @__PURE__ */ new Date();
845
+ const threadId = messages[0]?.threadId;
658
846
  for (const [i, message] of messages.entries()) {
659
847
  if (!message.id) throw new Error(`Message at index ${i} missing id`);
660
- if (!message.threadId) throw new Error(`Message at index ${i} missing threadId`);
661
- if (!message.content) throw new Error(`Message at index ${i} missing content`);
662
- if (!message.role) throw new Error(`Message at index ${i} missing role`);
848
+ if (!message.threadId) {
849
+ throw new Error(`Message at index ${i} missing threadId`);
850
+ }
851
+ if (!message.content) {
852
+ throw new Error(`Message at index ${i} missing content`);
853
+ }
854
+ if (!message.role) {
855
+ throw new Error(`Message at index ${i} missing role`);
856
+ }
663
857
  const thread = await this.getThreadById({ threadId: message.threadId });
664
858
  if (!thread) {
665
859
  throw new Error(`Thread ${message.threadId} not found`);
@@ -673,36 +867,49 @@ var D1Store = class extends MastraStorage {
673
867
  content: typeof message.content === "string" ? message.content : JSON.stringify(message.content),
674
868
  createdAt: createdAt.toISOString(),
675
869
  role: message.role,
676
- type: message.type
870
+ type: message.type || "v2",
871
+ resourceId: message.resourceId
677
872
  };
678
873
  });
679
- await this.batchInsert({
680
- tableName: TABLE_MESSAGES,
681
- records: messagesToInsert
682
- });
874
+ await Promise.all([
875
+ this.batchUpsert({
876
+ tableName: TABLE_MESSAGES,
877
+ records: messagesToInsert
878
+ }),
879
+ // Update thread's updatedAt timestamp
880
+ this.executeQuery({
881
+ sql: `UPDATE ${this.getTableName(TABLE_THREADS)} SET updatedAt = ? WHERE id = ?`,
882
+ params: [now.toISOString(), threadId]
883
+ })
884
+ ]);
683
885
  this.logger.debug(`Saved ${messages.length} messages`);
684
- return messages;
886
+ const list = new MessageList().add(messages, "memory");
887
+ if (format === `v2`) return list.get.all.v2();
888
+ return list.get.all.v1();
685
889
  } catch (error) {
686
- this.logger.error("Error saving messages:", { message: error instanceof Error ? error.message : String(error) });
687
- throw error;
890
+ throw new MastraError(
891
+ {
892
+ id: "CLOUDFLARE_D1_STORAGE_SAVE_MESSAGES_ERROR",
893
+ domain: ErrorDomain.STORAGE,
894
+ category: ErrorCategory.THIRD_PARTY,
895
+ text: `Failed to save messages: ${error instanceof Error ? error.message : String(error)}`
896
+ },
897
+ error
898
+ );
688
899
  }
689
900
  }
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 = `
901
+ async _getIncludedMessages(threadId, selectBy) {
902
+ const include = selectBy?.include;
903
+ if (!include) return null;
904
+ const prevMax = Math.max(...include.map((i) => i.withPreviousMessages || 0));
905
+ const nextMax = Math.max(...include.map((i) => i.withNextMessages || 0));
906
+ const includeIds = include.map((i) => i.id);
907
+ const sql = `
701
908
  WITH ordered_messages AS (
702
909
  SELECT
703
910
  *,
704
911
  ROW_NUMBER() OVER (ORDER BY createdAt DESC) AS row_num
705
- FROM ${fullTableName}
912
+ FROM ${this.getTableName(TABLE_MESSAGES)}
706
913
  WHERE thread_id = ?
707
914
  )
708
915
  SELECT
@@ -711,7 +918,7 @@ var D1Store = class extends MastraStorage {
711
918
  m.role,
712
919
  m.type,
713
920
  m.createdAt,
714
- m.thread_id AS "threadId"
921
+ m.thread_id AS threadId
715
922
  FROM ordered_messages m
716
923
  WHERE m.id IN (${includeIds.map(() => "?").join(",")})
717
924
  OR EXISTS (
@@ -725,20 +932,41 @@ var D1Store = class extends MastraStorage {
725
932
  )
726
933
  ORDER BY m.createdAt DESC
727
934
  `;
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 });
935
+ const params = [
936
+ threadId,
937
+ ...includeIds,
938
+ // for m.id IN (...)
939
+ ...includeIds,
940
+ // for target.id IN (...)
941
+ prevMax,
942
+ nextMax
943
+ ];
944
+ const messages = await this.executeQuery({ sql, params });
945
+ return messages;
946
+ }
947
+ async getMessages({
948
+ threadId,
949
+ selectBy,
950
+ format
951
+ }) {
952
+ const fullTableName = this.getTableName(TABLE_MESSAGES);
953
+ const limit = this.resolveMessageLimit({
954
+ last: selectBy?.last,
955
+ defaultLimit: 40
956
+ });
957
+ const include = selectBy?.include || [];
958
+ const messages = [];
959
+ try {
960
+ if (include.length) {
961
+ const includeResult = await this._getIncludedMessages(threadId, selectBy);
738
962
  if (Array.isArray(includeResult)) messages.push(...includeResult);
739
963
  }
740
964
  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);
965
+ const query = createSqlBuilder().select(["id", "content", "role", "type", "createdAt", "thread_id AS threadId"]).from(fullTableName).where("thread_id = ?", threadId);
966
+ if (excludeIds.length > 0) {
967
+ query.andWhere(`id NOT IN (${excludeIds.map(() => "?").join(",")})`, ...excludeIds);
968
+ }
969
+ query.orderBy("createdAt", "DESC").limit(limit);
742
970
  const { sql, params } = query.build();
743
971
  const result = await this.executeQuery({ sql, params });
744
972
  if (Array.isArray(result)) messages.push(...result);
@@ -752,18 +980,92 @@ var D1Store = class extends MastraStorage {
752
980
  const processedMessages = messages.map((message) => {
753
981
  const processedMsg = {};
754
982
  for (const [key, value] of Object.entries(message)) {
983
+ if (key === `type` && value === `v2`) continue;
755
984
  processedMsg[key] = this.deserializeValue(value);
756
985
  }
757
986
  return processedMsg;
758
987
  });
759
988
  this.logger.debug(`Retrieved ${messages.length} messages for thread ${threadId}`);
760
- return processedMessages;
989
+ const list = new MessageList().add(processedMessages, "memory");
990
+ if (format === `v2`) return list.get.all.v2();
991
+ return list.get.all.v1();
761
992
  } 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 [];
993
+ const mastraError = new MastraError(
994
+ {
995
+ id: "CLOUDFLARE_D1_STORAGE_GET_MESSAGES_ERROR",
996
+ domain: ErrorDomain.STORAGE,
997
+ category: ErrorCategory.THIRD_PARTY,
998
+ text: `Failed to retrieve messages for thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`,
999
+ details: { threadId }
1000
+ },
1001
+ error
1002
+ );
1003
+ this.logger?.error(mastraError.toString());
1004
+ this.logger?.trackException(mastraError);
1005
+ throw mastraError;
1006
+ }
1007
+ }
1008
+ async getMessagesPaginated({
1009
+ threadId,
1010
+ selectBy,
1011
+ format
1012
+ }) {
1013
+ const { dateRange, page = 0, perPage = 40 } = selectBy?.pagination || {};
1014
+ const { start: fromDate, end: toDate } = dateRange || {};
1015
+ const fullTableName = this.getTableName(TABLE_MESSAGES);
1016
+ const messages = [];
1017
+ try {
1018
+ if (selectBy?.include?.length) {
1019
+ const includeResult = await this._getIncludedMessages(threadId, selectBy);
1020
+ if (Array.isArray(includeResult)) messages.push(...includeResult);
1021
+ }
1022
+ const countQuery = createSqlBuilder().count().from(fullTableName).where("thread_id = ?", threadId);
1023
+ if (fromDate) {
1024
+ countQuery.andWhere("createdAt >= ?", this.serializeDate(fromDate));
1025
+ }
1026
+ if (toDate) {
1027
+ countQuery.andWhere("createdAt <= ?", this.serializeDate(toDate));
1028
+ }
1029
+ const countResult = await this.executeQuery(countQuery.build());
1030
+ const total = Number(countResult[0]?.count ?? 0);
1031
+ const query = createSqlBuilder().select(["id", "content", "role", "type", "createdAt", "thread_id AS threadId"]).from(fullTableName).where("thread_id = ?", threadId);
1032
+ if (fromDate) {
1033
+ query.andWhere("createdAt >= ?", this.serializeDate(fromDate));
1034
+ }
1035
+ if (toDate) {
1036
+ query.andWhere("createdAt <= ?", this.serializeDate(toDate));
1037
+ }
1038
+ query.orderBy("createdAt", "DESC").limit(perPage).offset(page * perPage);
1039
+ const results = await this.executeQuery(query.build());
1040
+ const list = new MessageList().add(results, "memory");
1041
+ messages.push(...format === `v2` ? list.get.all.v2() : list.get.all.v1());
1042
+ return {
1043
+ messages,
1044
+ total,
1045
+ page,
1046
+ perPage,
1047
+ hasMore: page * perPage + messages.length < total
1048
+ };
1049
+ } catch (error) {
1050
+ const mastraError = new MastraError(
1051
+ {
1052
+ id: "CLOUDFLARE_D1_STORAGE_GET_MESSAGES_PAGINATED_ERROR",
1053
+ domain: ErrorDomain.STORAGE,
1054
+ category: ErrorCategory.THIRD_PARTY,
1055
+ text: `Failed to retrieve messages for thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`,
1056
+ details: { threadId }
1057
+ },
1058
+ error
1059
+ );
1060
+ this.logger?.error(mastraError.toString());
1061
+ this.logger?.trackException(mastraError);
1062
+ return {
1063
+ messages: [],
1064
+ total: 0,
1065
+ page,
1066
+ perPage,
1067
+ hasMore: false
1068
+ };
767
1069
  }
768
1070
  }
769
1071
  async persistWorkflowSnapshot({
@@ -801,23 +1103,42 @@ var D1Store = class extends MastraStorage {
801
1103
  try {
802
1104
  await this.executeQuery({ sql, params });
803
1105
  } catch (error) {
804
- this.logger.error("Error persisting workflow snapshot:", {
805
- message: error instanceof Error ? error.message : String(error)
806
- });
807
- throw error;
1106
+ throw new MastraError(
1107
+ {
1108
+ id: "CLOUDFLARE_D1_STORAGE_PERSIST_WORKFLOW_SNAPSHOT_ERROR",
1109
+ domain: ErrorDomain.STORAGE,
1110
+ category: ErrorCategory.THIRD_PARTY,
1111
+ text: `Failed to persist workflow snapshot: ${error instanceof Error ? error.message : String(error)}`,
1112
+ details: { workflowName, runId }
1113
+ },
1114
+ error
1115
+ );
808
1116
  }
809
1117
  }
810
1118
  async loadWorkflowSnapshot(params) {
811
1119
  const { workflowName, runId } = params;
812
1120
  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;
1121
+ try {
1122
+ const d = await this.load({
1123
+ tableName: TABLE_WORKFLOW_SNAPSHOT,
1124
+ keys: {
1125
+ workflow_name: workflowName,
1126
+ run_id: runId
1127
+ }
1128
+ });
1129
+ return d ? d.snapshot : null;
1130
+ } catch (error) {
1131
+ throw new MastraError(
1132
+ {
1133
+ id: "CLOUDFLARE_D1_STORAGE_LOAD_WORKFLOW_SNAPSHOT_ERROR",
1134
+ domain: ErrorDomain.STORAGE,
1135
+ category: ErrorCategory.THIRD_PARTY,
1136
+ text: `Failed to load workflow snapshot: ${error instanceof Error ? error.message : String(error)}`,
1137
+ details: { workflowName, runId }
1138
+ },
1139
+ error
1140
+ );
1141
+ }
821
1142
  }
822
1143
  /**
823
1144
  * Insert multiple records in a batch operation
@@ -852,18 +1173,84 @@ var D1Store = class extends MastraStorage {
852
1173
  }
853
1174
  this.logger.debug(`Successfully batch inserted ${records.length} records into ${tableName}`);
854
1175
  } 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}`);
1176
+ throw new MastraError(
1177
+ {
1178
+ id: "CLOUDFLARE_D1_STORAGE_BATCH_INSERT_ERROR",
1179
+ domain: ErrorDomain.STORAGE,
1180
+ category: ErrorCategory.THIRD_PARTY,
1181
+ text: `Failed to batch insert into ${tableName}: ${error instanceof Error ? error.message : String(error)}`,
1182
+ details: { tableName }
1183
+ },
1184
+ error
1185
+ );
1186
+ }
1187
+ }
1188
+ /**
1189
+ * Upsert multiple records in a batch operation
1190
+ * @param tableName The table to insert into
1191
+ * @param records The records to insert
1192
+ */
1193
+ async batchUpsert({
1194
+ tableName,
1195
+ records
1196
+ }) {
1197
+ if (records.length === 0) return;
1198
+ const fullTableName = this.getTableName(tableName);
1199
+ try {
1200
+ const batchSize = 50;
1201
+ for (let i = 0; i < records.length; i += batchSize) {
1202
+ const batch = records.slice(i, i + batchSize);
1203
+ const recordsToInsert = batch;
1204
+ if (recordsToInsert.length > 0) {
1205
+ const firstRecord = recordsToInsert[0];
1206
+ const columns = Object.keys(firstRecord || {});
1207
+ for (const record of recordsToInsert) {
1208
+ const values = columns.map((col) => {
1209
+ if (!record) return null;
1210
+ const value = typeof col === "string" ? record[col] : null;
1211
+ return this.serializeValue(value);
1212
+ });
1213
+ const recordToUpsert = columns.reduce(
1214
+ (acc, col) => {
1215
+ if (col !== "createdAt") acc[col] = `excluded.${col}`;
1216
+ return acc;
1217
+ },
1218
+ {}
1219
+ );
1220
+ const query = createSqlBuilder().insert(fullTableName, columns, values, ["id"], recordToUpsert);
1221
+ const { sql, params } = query.build();
1222
+ await this.executeQuery({ sql, params });
1223
+ }
1224
+ }
1225
+ this.logger.debug(
1226
+ `Processed batch ${Math.floor(i / batchSize) + 1} of ${Math.ceil(records.length / batchSize)}`
1227
+ );
1228
+ }
1229
+ this.logger.debug(`Successfully batch upserted ${records.length} records into ${tableName}`);
1230
+ } catch (error) {
1231
+ throw new MastraError(
1232
+ {
1233
+ id: "CLOUDFLARE_D1_STORAGE_BATCH_UPSERT_ERROR",
1234
+ domain: ErrorDomain.STORAGE,
1235
+ category: ErrorCategory.THIRD_PARTY,
1236
+ text: `Failed to batch upsert into ${tableName}: ${error instanceof Error ? error.message : String(error)}`,
1237
+ details: { tableName }
1238
+ },
1239
+ error
1240
+ );
859
1241
  }
860
1242
  }
1243
+ /**
1244
+ * @deprecated use getTracesPaginated instead
1245
+ */
861
1246
  async getTraces({
862
1247
  name,
863
1248
  scope,
864
1249
  page,
865
1250
  perPage,
866
- attributes
1251
+ attributes,
1252
+ fromDate,
1253
+ toDate
867
1254
  }) {
868
1255
  const fullTableName = this.getTableName(TABLE_TRACES);
869
1256
  try {
@@ -879,22 +1266,114 @@ var D1Store = class extends MastraStorage {
879
1266
  query.jsonLike("attributes", key, value);
880
1267
  }
881
1268
  }
882
- query.orderBy("startTime", "DESC").limit(perPage).offset((page - 1) * perPage);
1269
+ if (fromDate) {
1270
+ query.andWhere("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
1271
+ }
1272
+ if (toDate) {
1273
+ query.andWhere("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
1274
+ }
1275
+ query.orderBy("startTime", "DESC").limit(perPage).offset(page * perPage);
883
1276
  const { sql, params } = query.build();
884
1277
  const results = await this.executeQuery({ sql, params });
885
- return isArrayOfRecords(results) ? results.map((trace) => ({
886
- ...trace,
887
- attributes: this.deserializeValue(trace.attributes, "jsonb"),
888
- status: this.deserializeValue(trace.status, "jsonb"),
889
- events: this.deserializeValue(trace.events, "jsonb"),
890
- links: this.deserializeValue(trace.links, "jsonb"),
891
- other: this.deserializeValue(trace.other, "jsonb")
892
- })) : [];
1278
+ return isArrayOfRecords(results) ? results.map(
1279
+ (trace) => ({
1280
+ ...trace,
1281
+ attributes: this.deserializeValue(trace.attributes, "jsonb"),
1282
+ status: this.deserializeValue(trace.status, "jsonb"),
1283
+ events: this.deserializeValue(trace.events, "jsonb"),
1284
+ links: this.deserializeValue(trace.links, "jsonb"),
1285
+ other: this.deserializeValue(trace.other, "jsonb")
1286
+ })
1287
+ ) : [];
893
1288
  } catch (error) {
894
- this.logger.error("Error getting traces:", { message: error instanceof Error ? error.message : String(error) });
1289
+ const mastraError = new MastraError(
1290
+ {
1291
+ id: "CLOUDFLARE_D1_STORAGE_GET_TRACES_ERROR",
1292
+ domain: ErrorDomain.STORAGE,
1293
+ category: ErrorCategory.THIRD_PARTY,
1294
+ text: `Failed to retrieve traces: ${error instanceof Error ? error.message : String(error)}`,
1295
+ details: {
1296
+ name: name ?? "",
1297
+ scope: scope ?? ""
1298
+ }
1299
+ },
1300
+ error
1301
+ );
1302
+ this.logger?.error(mastraError.toString());
1303
+ this.logger?.trackException(mastraError);
895
1304
  return [];
896
1305
  }
897
1306
  }
1307
+ async getTracesPaginated(args) {
1308
+ const { name, scope, page, perPage, attributes, fromDate, toDate } = args;
1309
+ const fullTableName = this.getTableName(TABLE_TRACES);
1310
+ try {
1311
+ const dataQuery = createSqlBuilder().select("*").from(fullTableName).where("1=1");
1312
+ const countQuery = createSqlBuilder().count().from(fullTableName).where("1=1");
1313
+ if (name) {
1314
+ dataQuery.andWhere("name LIKE ?", `%${name}%`);
1315
+ countQuery.andWhere("name LIKE ?", `%${name}%`);
1316
+ }
1317
+ if (scope) {
1318
+ dataQuery.andWhere("scope = ?", scope);
1319
+ countQuery.andWhere("scope = ?", scope);
1320
+ }
1321
+ if (attributes && Object.keys(attributes).length > 0) {
1322
+ for (const [key, value] of Object.entries(attributes)) {
1323
+ dataQuery.jsonLike("attributes", key, value);
1324
+ countQuery.jsonLike("attributes", key, value);
1325
+ }
1326
+ }
1327
+ if (fromDate) {
1328
+ const fromDateStr = fromDate instanceof Date ? fromDate.toISOString() : fromDate;
1329
+ dataQuery.andWhere("createdAt >= ?", fromDateStr);
1330
+ countQuery.andWhere("createdAt >= ?", fromDateStr);
1331
+ }
1332
+ if (toDate) {
1333
+ const toDateStr = toDate instanceof Date ? toDate.toISOString() : toDate;
1334
+ dataQuery.andWhere("createdAt <= ?", toDateStr);
1335
+ countQuery.andWhere("createdAt <= ?", toDateStr);
1336
+ }
1337
+ const countResult = await this.executeQuery(countQuery.build());
1338
+ const total = Number(countResult?.[0]?.count ?? 0);
1339
+ dataQuery.orderBy("startTime", "DESC").limit(perPage).offset(page * perPage);
1340
+ const results = await this.executeQuery(dataQuery.build());
1341
+ const traces = isArrayOfRecords(results) ? results.map(
1342
+ (trace) => ({
1343
+ ...trace,
1344
+ attributes: this.deserializeValue(trace.attributes, "jsonb"),
1345
+ status: this.deserializeValue(trace.status, "jsonb"),
1346
+ events: this.deserializeValue(trace.events, "jsonb"),
1347
+ links: this.deserializeValue(trace.links, "jsonb"),
1348
+ other: this.deserializeValue(trace.other, "jsonb")
1349
+ })
1350
+ ) : [];
1351
+ return {
1352
+ traces,
1353
+ total,
1354
+ page,
1355
+ perPage,
1356
+ hasMore: page * perPage + traces.length < total
1357
+ };
1358
+ } catch (error) {
1359
+ const mastraError = new MastraError(
1360
+ {
1361
+ id: "CLOUDFLARE_D1_STORAGE_GET_TRACES_ERROR",
1362
+ domain: ErrorDomain.STORAGE,
1363
+ category: ErrorCategory.THIRD_PARTY,
1364
+ text: `Failed to retrieve traces: ${error instanceof Error ? error.message : String(error)}`,
1365
+ details: { name: name ?? "", scope: scope ?? "" }
1366
+ },
1367
+ error
1368
+ );
1369
+ this.logger?.error(mastraError.toString());
1370
+ this.logger?.trackException(mastraError);
1371
+ return { traces: [], total: 0, page, perPage, hasMore: false };
1372
+ }
1373
+ }
1374
+ /**
1375
+ * @deprecated use getEvals instead
1376
+ */
898
1377
  async getEvalsByAgentName(agentName, type) {
899
1378
  const fullTableName = this.getTableName(TABLE_EVALS);
900
1379
  try {
@@ -924,12 +1403,115 @@ var D1Store = class extends MastraStorage {
924
1403
  };
925
1404
  }) : [];
926
1405
  } catch (error) {
927
- this.logger.error(`Error getting evals for agent ${agentName}:`, {
928
- message: error instanceof Error ? error.message : String(error)
929
- });
1406
+ const mastraError = new MastraError(
1407
+ {
1408
+ id: "CLOUDFLARE_D1_STORAGE_GET_EVALS_ERROR",
1409
+ domain: ErrorDomain.STORAGE,
1410
+ category: ErrorCategory.THIRD_PARTY,
1411
+ text: `Failed to retrieve evals for agent ${agentName}: ${error instanceof Error ? error.message : String(error)}`,
1412
+ details: { agentName }
1413
+ },
1414
+ error
1415
+ );
1416
+ this.logger?.error(mastraError.toString());
1417
+ this.logger?.trackException(mastraError);
930
1418
  return [];
931
1419
  }
932
1420
  }
1421
+ async getEvals(options) {
1422
+ const { agentName, type, page = 0, perPage = 40, fromDate, toDate } = options || {};
1423
+ const fullTableName = this.getTableName(TABLE_EVALS);
1424
+ const conditions = [];
1425
+ const queryParams = [];
1426
+ if (agentName) {
1427
+ conditions.push(`agent_name = ?`);
1428
+ queryParams.push(agentName);
1429
+ }
1430
+ if (type === "test") {
1431
+ conditions.push(`(test_info IS NOT NULL AND json_extract(test_info, '$.testPath') IS NOT NULL)`);
1432
+ } else if (type === "live") {
1433
+ conditions.push(`(test_info IS NULL OR json_extract(test_info, '$.testPath') IS NULL)`);
1434
+ }
1435
+ if (fromDate) {
1436
+ conditions.push(`createdAt >= ?`);
1437
+ queryParams.push(this.serializeDate(fromDate));
1438
+ }
1439
+ if (toDate) {
1440
+ conditions.push(`createdAt <= ?`);
1441
+ queryParams.push(this.serializeDate(toDate));
1442
+ }
1443
+ const countQueryBuilder = createSqlBuilder().count().from(fullTableName);
1444
+ if (conditions.length > 0) {
1445
+ countQueryBuilder.where(conditions.join(" AND "), ...queryParams);
1446
+ }
1447
+ const { sql: countSql, params: countParams } = countQueryBuilder.build();
1448
+ try {
1449
+ const countResult = await this.executeQuery({
1450
+ sql: countSql,
1451
+ params: countParams,
1452
+ first: true
1453
+ });
1454
+ const total = Number(countResult?.count || 0);
1455
+ const currentOffset = page * perPage;
1456
+ if (total === 0) {
1457
+ return {
1458
+ evals: [],
1459
+ total: 0,
1460
+ page,
1461
+ perPage,
1462
+ hasMore: false
1463
+ };
1464
+ }
1465
+ const dataQueryBuilder = createSqlBuilder().select("*").from(fullTableName);
1466
+ if (conditions.length > 0) {
1467
+ dataQueryBuilder.where(conditions.join(" AND "), ...queryParams);
1468
+ }
1469
+ dataQueryBuilder.orderBy("createdAt", "DESC").limit(perPage).offset(currentOffset);
1470
+ const { sql: dataSql, params: dataParams } = dataQueryBuilder.build();
1471
+ const rows = await this.executeQuery({
1472
+ sql: dataSql,
1473
+ params: dataParams
1474
+ });
1475
+ const evals = (isArrayOfRecords(rows) ? rows : []).map((row) => {
1476
+ const result = this.deserializeValue(row.result);
1477
+ const testInfo = row.test_info ? this.deserializeValue(row.test_info) : void 0;
1478
+ if (!result || typeof result !== "object" || !("score" in result)) {
1479
+ throw new Error(`Invalid MetricResult format: ${JSON.stringify(result)}`);
1480
+ }
1481
+ return {
1482
+ input: row.input,
1483
+ output: row.output,
1484
+ result,
1485
+ agentName: row.agent_name,
1486
+ metricName: row.metric_name,
1487
+ instructions: row.instructions,
1488
+ testInfo,
1489
+ globalRunId: row.global_run_id,
1490
+ runId: row.run_id,
1491
+ createdAt: row.createdAt
1492
+ };
1493
+ });
1494
+ const hasMore = currentOffset + evals.length < total;
1495
+ return {
1496
+ evals,
1497
+ total,
1498
+ page,
1499
+ perPage,
1500
+ hasMore
1501
+ };
1502
+ } catch (error) {
1503
+ throw new MastraError(
1504
+ {
1505
+ id: "CLOUDFLARE_D1_STORAGE_GET_EVALS_ERROR",
1506
+ domain: ErrorDomain.STORAGE,
1507
+ category: ErrorCategory.THIRD_PARTY,
1508
+ text: `Failed to retrieve evals for agent ${agentName}: ${error instanceof Error ? error.message : String(error)}`,
1509
+ details: { agentName: agentName ?? "", type: type ?? "" }
1510
+ },
1511
+ error
1512
+ );
1513
+ }
1514
+ }
933
1515
  parseWorkflowRun(row) {
934
1516
  let parsedSnapshot = row.snapshot;
935
1517
  if (typeof parsedSnapshot === "string") {
@@ -991,17 +1573,30 @@ var D1Store = class extends MastraStorage {
991
1573
  let total = 0;
992
1574
  if (limit !== void 0 && offset !== void 0) {
993
1575
  const { sql: countSql, params: countParams } = countBuilder.build();
994
- const countResult = await this.executeQuery({ sql: countSql, params: countParams, first: true });
1576
+ const countResult = await this.executeQuery({
1577
+ sql: countSql,
1578
+ params: countParams,
1579
+ first: true
1580
+ });
995
1581
  total = Number(countResult?.count ?? 0);
996
1582
  }
997
1583
  const results = await this.executeQuery({ sql, params });
998
1584
  const runs = (isArrayOfRecords(results) ? results : []).map((row) => this.parseWorkflowRun(row));
999
1585
  return { runs, total: total || runs.length };
1000
1586
  } catch (error) {
1001
- this.logger.error("Error getting workflow runs:", {
1002
- message: error instanceof Error ? error.message : String(error)
1003
- });
1004
- throw error;
1587
+ throw new MastraError(
1588
+ {
1589
+ id: "CLOUDFLARE_D1_STORAGE_GET_WORKFLOW_RUNS_ERROR",
1590
+ domain: ErrorDomain.STORAGE,
1591
+ category: ErrorCategory.THIRD_PARTY,
1592
+ text: `Failed to retrieve workflow runs: ${error instanceof Error ? error.message : String(error)}`,
1593
+ details: {
1594
+ workflowName: workflowName ?? "",
1595
+ resourceId: resourceId ?? ""
1596
+ }
1597
+ },
1598
+ error
1599
+ );
1005
1600
  }
1006
1601
  }
1007
1602
  async getWorkflowRunById({
@@ -1026,10 +1621,16 @@ var D1Store = class extends MastraStorage {
1026
1621
  if (!result) return null;
1027
1622
  return this.parseWorkflowRun(result);
1028
1623
  } catch (error) {
1029
- this.logger.error("Error getting workflow run by ID:", {
1030
- message: error instanceof Error ? error.message : String(error)
1031
- });
1032
- throw error;
1624
+ throw new MastraError(
1625
+ {
1626
+ id: "CLOUDFLARE_D1_STORAGE_GET_WORKFLOW_RUN_BY_ID_ERROR",
1627
+ domain: ErrorDomain.STORAGE,
1628
+ category: ErrorCategory.THIRD_PARTY,
1629
+ text: `Failed to retrieve workflow run by ID: ${error instanceof Error ? error.message : String(error)}`,
1630
+ details: { runId, workflowName: workflowName ?? "" }
1631
+ },
1632
+ error
1633
+ );
1033
1634
  }
1034
1635
  }
1035
1636
  /**
@@ -1039,6 +1640,10 @@ var D1Store = class extends MastraStorage {
1039
1640
  async close() {
1040
1641
  this.logger.debug("Closing D1 connection");
1041
1642
  }
1643
+ async updateMessages(_args) {
1644
+ this.logger.error("updateMessages is not yet implemented in CloudflareD1Store");
1645
+ throw new Error("Method not implemented");
1646
+ }
1042
1647
  };
1043
1648
 
1044
1649
  export { D1Store };