@mastra/cloudflare-d1 0.0.0-separate-trace-data-from-component-20250501141108 → 0.0.0-support-d1-client-20250701191943

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.cjs CHANGED
@@ -1,15 +1,16 @@
1
1
  'use strict';
2
2
 
3
+ var agent = require('@mastra/core/agent');
4
+ var error = require('@mastra/core/error');
3
5
  var storage = require('@mastra/core/storage');
4
6
  var Cloudflare = require('cloudflare');
7
+ var utils = require('@mastra/core/utils');
5
8
 
6
9
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
10
 
8
11
  var Cloudflare__default = /*#__PURE__*/_interopDefault(Cloudflare);
9
12
 
10
13
  // src/storage/index.ts
11
-
12
- // src/storage/sql-builder.ts
13
14
  var SqlBuilder = class {
14
15
  sql = "";
15
16
  params = [];
@@ -19,12 +20,15 @@ var SqlBuilder = class {
19
20
  if (!columns || Array.isArray(columns) && columns.length === 0) {
20
21
  this.sql = "SELECT *";
21
22
  } else {
22
- this.sql = `SELECT ${Array.isArray(columns) ? columns.join(", ") : columns}`;
23
+ const cols = Array.isArray(columns) ? columns : [columns];
24
+ const parsedCols = cols.map((col) => parseSelectIdentifier(col));
25
+ this.sql = `SELECT ${parsedCols.join(", ")}`;
23
26
  }
24
27
  return this;
25
28
  }
26
29
  from(table) {
27
- this.sql += ` FROM ${table}`;
30
+ const parsedTableName = utils.parseSqlIdentifier(table, "table name");
31
+ this.sql += ` FROM ${parsedTableName}`;
28
32
  return this;
29
33
  }
30
34
  /**
@@ -61,7 +65,11 @@ var SqlBuilder = class {
61
65
  return this;
62
66
  }
63
67
  orderBy(column, direction = "ASC") {
64
- this.sql += ` ORDER BY ${column} ${direction}`;
68
+ const parsedColumn = utils.parseSqlIdentifier(column, "column name");
69
+ if (!["ASC", "DESC"].includes(direction)) {
70
+ throw new Error(`Invalid sort direction: ${direction}`);
71
+ }
72
+ this.sql += ` ORDER BY ${parsedColumn} ${direction}`;
65
73
  return this;
66
74
  }
67
75
  limit(count) {
@@ -87,27 +95,33 @@ var SqlBuilder = class {
87
95
  * @param updateMap Object mapping columns to update to their new value (e.g. { name: 'excluded.name' })
88
96
  */
89
97
  insert(table, columns, values, conflictColumns, updateMap) {
90
- const placeholders = columns.map(() => "?").join(", ");
98
+ const parsedTableName = utils.parseSqlIdentifier(table, "table name");
99
+ const parsedColumns = columns.map((col) => utils.parseSqlIdentifier(col, "column name"));
100
+ const placeholders = parsedColumns.map(() => "?").join(", ");
91
101
  if (conflictColumns && updateMap) {
102
+ const parsedConflictColumns = conflictColumns.map((col) => utils.parseSqlIdentifier(col, "column name"));
92
103
  const updateClause = Object.entries(updateMap).map(([col, expr]) => `${col} = ${expr}`).join(", ");
93
- this.sql = `INSERT INTO ${table} (${columns.join(", ")}) VALUES (${placeholders}) ON CONFLICT(${conflictColumns.join(", ")}) DO UPDATE SET ${updateClause}`;
104
+ this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders}) ON CONFLICT(${parsedConflictColumns.join(", ")}) DO UPDATE SET ${updateClause}`;
94
105
  this.params.push(...values);
95
106
  return this;
96
107
  }
97
- this.sql = `INSERT INTO ${table} (${columns.join(", ")}) VALUES (${placeholders})`;
108
+ this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders})`;
98
109
  this.params.push(...values);
99
110
  return this;
100
111
  }
101
112
  // Update operations
102
113
  update(table, columns, values) {
103
- const setClause = columns.map((col) => `${col} = ?`).join(", ");
104
- this.sql = `UPDATE ${table} SET ${setClause}`;
114
+ const parsedTableName = utils.parseSqlIdentifier(table, "table name");
115
+ const parsedColumns = columns.map((col) => utils.parseSqlIdentifier(col, "column name"));
116
+ const setClause = parsedColumns.map((col) => `${col} = ?`).join(", ");
117
+ this.sql = `UPDATE ${parsedTableName} SET ${setClause}`;
105
118
  this.params.push(...values);
106
119
  return this;
107
120
  }
108
121
  // Delete operations
109
122
  delete(table) {
110
- this.sql = `DELETE FROM ${table}`;
123
+ const parsedTableName = utils.parseSqlIdentifier(table, "table name");
124
+ this.sql = `DELETE FROM ${parsedTableName}`;
111
125
  return this;
112
126
  }
113
127
  /**
@@ -118,9 +132,16 @@ var SqlBuilder = class {
118
132
  * @returns The builder instance
119
133
  */
120
134
  createTable(table, columnDefinitions, tableConstraints) {
121
- const columns = columnDefinitions.join(", ");
135
+ const parsedTableName = utils.parseSqlIdentifier(table, "table name");
136
+ const parsedColumnDefinitions = columnDefinitions.map((def) => {
137
+ const colName = def.split(/\s+/)[0];
138
+ if (!colName) throw new Error("Empty column name in definition");
139
+ utils.parseSqlIdentifier(colName, "column name");
140
+ return def;
141
+ });
142
+ const columns = parsedColumnDefinitions.join(", ");
122
143
  const constraints = tableConstraints && tableConstraints.length > 0 ? ", " + tableConstraints.join(", ") : "";
123
- this.sql = `CREATE TABLE IF NOT EXISTS ${table} (${columns}${constraints})`;
144
+ this.sql = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (${columns}${constraints})`;
124
145
  return this;
125
146
  }
126
147
  /**
@@ -143,13 +164,10 @@ var SqlBuilder = class {
143
164
  * @returns The builder instance
144
165
  */
145
166
  createIndex(indexName, tableName, columnName, indexType = "") {
146
- this.sql = `CREATE ${indexType ? indexType + " " : ""}INDEX IF NOT EXISTS ${indexName} ON ${tableName}(${columnName})`;
147
- return this;
148
- }
149
- // Raw SQL with params
150
- raw(sql, ...params) {
151
- this.sql = sql;
152
- this.params.push(...params);
167
+ const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
168
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
169
+ const parsedColumnName = utils.parseSqlIdentifier(columnName, "column name");
170
+ this.sql = `CREATE ${indexType ? indexType + " " : ""}INDEX IF NOT EXISTS ${parsedIndexName} ON ${parsedTableName}(${parsedColumnName})`;
153
171
  return this;
154
172
  }
155
173
  /**
@@ -159,11 +177,12 @@ var SqlBuilder = class {
159
177
  * @param exact If true, will not add % wildcards
160
178
  */
161
179
  like(column, value, exact = false) {
180
+ const parsedColumnName = utils.parseSqlIdentifier(column, "column name");
162
181
  const likeValue = exact ? value : `%${value}%`;
163
182
  if (this.whereAdded) {
164
- this.sql += ` AND ${column} LIKE ?`;
183
+ this.sql += ` AND ${parsedColumnName} LIKE ?`;
165
184
  } else {
166
- this.sql += ` WHERE ${column} LIKE ?`;
185
+ this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
167
186
  this.whereAdded = true;
168
187
  }
169
188
  this.params.push(likeValue);
@@ -176,11 +195,13 @@ var SqlBuilder = class {
176
195
  * @param value The value to match
177
196
  */
178
197
  jsonLike(column, key, value) {
179
- const jsonPattern = `%"${key}":"${value}"%`;
198
+ const parsedColumnName = utils.parseSqlIdentifier(column, "column name");
199
+ const parsedKey = utils.parseSqlIdentifier(key, "key name");
200
+ const jsonPattern = `%"${parsedKey}":"${value}"%`;
180
201
  if (this.whereAdded) {
181
- this.sql += ` AND ${column} LIKE ?`;
202
+ this.sql += ` AND ${parsedColumnName} LIKE ?`;
182
203
  } else {
183
- this.sql += ` WHERE ${column} LIKE ?`;
204
+ this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
184
205
  this.whereAdded = true;
185
206
  }
186
207
  this.params.push(jsonPattern);
@@ -210,6 +231,15 @@ var SqlBuilder = class {
210
231
  function createSqlBuilder() {
211
232
  return new SqlBuilder();
212
233
  }
234
+ var SQL_IDENTIFIER_PATTERN = /^[a-zA-Z0-9_]+(\s+AS\s+[a-zA-Z0-9_]+)?$/;
235
+ function parseSelectIdentifier(column) {
236
+ if (column !== "*" && !SQL_IDENTIFIER_PATTERN.test(column)) {
237
+ throw new Error(
238
+ `Invalid column name: "${column}". Must be "*" or a valid identifier (letters, numbers, underscores), optionally with "AS alias".`
239
+ );
240
+ }
241
+ return column;
242
+ }
213
243
 
214
244
  // src/storage/index.ts
215
245
  function isArrayOfRecords(value) {
@@ -217,8 +247,6 @@ function isArrayOfRecords(value) {
217
247
  }
218
248
  var D1Store = class extends storage.MastraStorage {
219
249
  client;
220
- accountId;
221
- databaseId;
222
250
  binding;
223
251
  // D1Database binding
224
252
  tablePrefix;
@@ -227,24 +255,52 @@ var D1Store = class extends storage.MastraStorage {
227
255
  * @param config Configuration for D1 access (either REST API or Workers Binding API)
228
256
  */
229
257
  constructor(config) {
230
- super({ name: "D1" });
231
- this.tablePrefix = config.tablePrefix || "";
232
- if ("binding" in config) {
233
- if (!config.binding) {
234
- throw new Error("D1 binding is required when using Workers Binding API");
258
+ try {
259
+ super({ name: "D1" });
260
+ if (config.tablePrefix && !/^[a-zA-Z0-9_]*$/.test(config.tablePrefix)) {
261
+ throw new Error("Invalid tablePrefix: only letters, numbers, and underscores are allowed.");
235
262
  }
236
- this.binding = config.binding;
237
- this.logger.info("Using D1 Workers Binding API");
238
- } else {
239
- if (!config.accountId || !config.databaseId || !config.apiToken) {
240
- throw new Error("accountId, databaseId, and apiToken are required when using REST API");
263
+ this.tablePrefix = config.tablePrefix || "";
264
+ if ("binding" in config) {
265
+ if (!config.binding) {
266
+ throw new Error("D1 binding is required when using Workers Binding API");
267
+ }
268
+ this.binding = config.binding;
269
+ this.logger.info("Using D1 Workers Binding API");
270
+ } else if ("client" in config) {
271
+ if (!config.client) {
272
+ throw new Error("D1 client is required when using D1ClientConfig");
273
+ }
274
+ this.client = config.client;
275
+ this.logger.info("Using D1 Client");
276
+ } else {
277
+ if (!config.accountId || !config.databaseId || !config.apiToken) {
278
+ throw new Error("accountId, databaseId, and apiToken are required when using REST API");
279
+ }
280
+ const cfClient = new Cloudflare__default.default({
281
+ apiToken: config.apiToken
282
+ });
283
+ this.client = {
284
+ query: ({ sql, params }) => {
285
+ return cfClient.d1.database.query(config.databaseId, {
286
+ account_id: config.accountId,
287
+ sql,
288
+ params
289
+ });
290
+ }
291
+ };
292
+ this.logger.info("Using D1 REST API");
241
293
  }
242
- this.accountId = config.accountId;
243
- this.databaseId = config.databaseId;
244
- this.client = new Cloudflare__default.default({
245
- apiToken: config.apiToken
246
- });
247
- this.logger.info("Using D1 REST API");
294
+ } catch (error$1) {
295
+ throw new error.MastraError(
296
+ {
297
+ id: "CLOUDFLARE_D1_STORAGE_INITIALIZATION_ERROR",
298
+ domain: error.ErrorDomain.STORAGE,
299
+ category: error.ErrorCategory.SYSTEM,
300
+ text: "Error initializing D1Store"
301
+ },
302
+ error$1
303
+ );
248
304
  }
249
305
  }
250
306
  // Helper method to get the full table name with prefix
@@ -254,30 +310,6 @@ var D1Store = class extends storage.MastraStorage {
254
310
  formatSqlParams(params) {
255
311
  return params.map((p) => p === void 0 || p === null ? null : p);
256
312
  }
257
- // Helper method to create SQL indexes for better query performance
258
- async createIndexIfNotExists(tableName, columnName, indexType = "") {
259
- const fullTableName = this.getTableName(tableName);
260
- const indexName = `idx_${tableName}_${columnName}`;
261
- try {
262
- const checkQuery = createSqlBuilder().checkIndexExists(indexName, fullTableName);
263
- const { sql: checkSql, params: checkParams } = checkQuery.build();
264
- const indexExists = await this.executeQuery({
265
- sql: checkSql,
266
- params: checkParams,
267
- first: true
268
- });
269
- if (!indexExists) {
270
- const createQuery = createSqlBuilder().createIndex(indexName, fullTableName, columnName, indexType);
271
- const { sql: createSql, params: createParams } = createQuery.build();
272
- await this.executeQuery({ sql: createSql, params: createParams });
273
- this.logger.debug(`Created index ${indexName} on ${fullTableName}(${columnName})`);
274
- }
275
- } catch (error) {
276
- this.logger.error(`Error creating index on ${fullTableName}(${columnName}):`, {
277
- message: error instanceof Error ? error.message : String(error)
278
- });
279
- }
280
- }
281
313
  async executeWorkersBindingQuery({
282
314
  sql,
283
315
  params = [],
@@ -330,12 +362,11 @@ var D1Store = class extends storage.MastraStorage {
330
362
  params = [],
331
363
  first = false
332
364
  }) {
333
- if (!this.client || !this.accountId || !this.databaseId) {
365
+ if (!this.client) {
334
366
  throw new Error("Missing required REST API configuration");
335
367
  }
336
368
  try {
337
- const response = await this.client.d1.database.query(this.databaseId, {
338
- account_id: this.accountId,
369
+ const response = await this.client.query({
339
370
  sql,
340
371
  params: this.formatSqlParams(params)
341
372
  });
@@ -366,7 +397,7 @@ var D1Store = class extends storage.MastraStorage {
366
397
  this.logger.debug("Executing SQL query", { sql, params, first });
367
398
  if (this.binding) {
368
399
  return this.executeWorkersBindingQuery({ sql, params, first });
369
- } else if (this.client && this.accountId && this.databaseId) {
400
+ } else if (this.client) {
370
401
  return this.executeRestQuery({ sql, params, first });
371
402
  } else {
372
403
  throw new Error("No valid D1 configuration provided");
@@ -381,34 +412,25 @@ var D1Store = class extends storage.MastraStorage {
381
412
  throw new Error(`D1 query error: ${error.message}`);
382
413
  }
383
414
  }
384
- // Helper to convert storage type to SQL type
385
- getSqlType(type) {
386
- switch (type) {
387
- case "text":
388
- return "TEXT";
389
- case "timestamp":
390
- return "TIMESTAMP";
391
- case "integer":
392
- return "INTEGER";
393
- case "bigint":
394
- return "INTEGER";
395
- // SQLite doesn't have a separate BIGINT type
396
- case "jsonb":
397
- return "TEXT";
398
- // Store JSON as TEXT in SQLite
399
- default:
400
- return "TEXT";
415
+ // Helper to get existing table columns
416
+ async getTableColumns(tableName) {
417
+ try {
418
+ const sql = `PRAGMA table_info(${tableName})`;
419
+ const result = await this.executeQuery({ sql, params: [] });
420
+ if (!result || !Array.isArray(result)) {
421
+ return [];
422
+ }
423
+ return result.map((row) => ({
424
+ name: row.name,
425
+ type: row.type
426
+ }));
427
+ } catch (error) {
428
+ this.logger.error(`Error getting table columns for ${tableName}:`, {
429
+ message: error instanceof Error ? error.message : String(error)
430
+ });
431
+ return [];
401
432
  }
402
433
  }
403
- ensureDate(date) {
404
- if (!date) return void 0;
405
- return date instanceof Date ? date : new Date(date);
406
- }
407
- serializeDate(date) {
408
- if (!date) return void 0;
409
- const dateObj = this.ensureDate(date);
410
- return dateObj?.toISOString();
411
- }
412
434
  // Helper to serialize objects to JSON strings
413
435
  serializeValue(value) {
414
436
  if (value === null || value === void 0) return null;
@@ -442,6 +464,18 @@ var D1Store = class extends storage.MastraStorage {
442
464
  }
443
465
  return value;
444
466
  }
467
+ getSqlType(type) {
468
+ switch (type) {
469
+ case "bigint":
470
+ return "INTEGER";
471
+ // SQLite uses INTEGER for all integer sizes
472
+ case "jsonb":
473
+ return "TEXT";
474
+ // Store JSON as TEXT in SQLite
475
+ default:
476
+ return super.getSqlType(type);
477
+ }
478
+ }
445
479
  async createTable({
446
480
  tableName,
447
481
  schema
@@ -457,16 +491,64 @@ var D1Store = class extends storage.MastraStorage {
457
491
  if (tableName === storage.TABLE_WORKFLOW_SNAPSHOT) {
458
492
  tableConstraints.push("UNIQUE (workflow_name, run_id)");
459
493
  }
460
- const query = createSqlBuilder().createTable(fullTableName, columnDefinitions, tableConstraints);
461
- const { sql, params } = query.build();
462
494
  try {
495
+ const query = createSqlBuilder().createTable(fullTableName, columnDefinitions, tableConstraints);
496
+ const { sql, params } = query.build();
463
497
  await this.executeQuery({ sql, params });
464
498
  this.logger.debug(`Created table ${fullTableName}`);
465
- } catch (error) {
499
+ } catch (error$1) {
466
500
  this.logger.error(`Error creating table ${fullTableName}:`, {
467
- message: error instanceof Error ? error.message : String(error)
501
+ message: error$1 instanceof Error ? error$1.message : String(error$1)
468
502
  });
469
- throw new Error(`Failed to create table ${fullTableName}: ${error}`);
503
+ throw new error.MastraError(
504
+ {
505
+ id: "CLOUDFLARE_D1_STORAGE_CREATE_TABLE_ERROR",
506
+ domain: error.ErrorDomain.STORAGE,
507
+ category: error.ErrorCategory.THIRD_PARTY,
508
+ text: `Failed to create table ${fullTableName}: ${error$1 instanceof Error ? error$1.message : String(error$1)}`,
509
+ details: { tableName }
510
+ },
511
+ error$1
512
+ );
513
+ }
514
+ }
515
+ /**
516
+ * Alters table schema to add columns if they don't exist
517
+ * @param tableName Name of the table
518
+ * @param schema Schema of the table
519
+ * @param ifNotExists Array of column names to add if they don't exist
520
+ */
521
+ async alterTable({
522
+ tableName,
523
+ schema,
524
+ ifNotExists
525
+ }) {
526
+ const fullTableName = this.getTableName(tableName);
527
+ try {
528
+ const existingColumns = await this.getTableColumns(fullTableName);
529
+ const existingColumnNames = new Set(existingColumns.map((col) => col.name.toLowerCase()));
530
+ for (const columnName of ifNotExists) {
531
+ if (!existingColumnNames.has(columnName.toLowerCase()) && schema[columnName]) {
532
+ const columnDef = schema[columnName];
533
+ const sqlType = this.getSqlType(columnDef.type);
534
+ const nullable = columnDef.nullable === false ? "NOT NULL" : "";
535
+ const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
536
+ const alterSql = `ALTER TABLE ${fullTableName} ADD COLUMN ${columnName} ${sqlType} ${nullable} ${defaultValue}`.trim();
537
+ await this.executeQuery({ sql: alterSql, params: [] });
538
+ this.logger.debug(`Added column ${columnName} to table ${fullTableName}`);
539
+ }
540
+ }
541
+ } catch (error$1) {
542
+ throw new error.MastraError(
543
+ {
544
+ id: "CLOUDFLARE_D1_STORAGE_ALTER_TABLE_ERROR",
545
+ domain: error.ErrorDomain.STORAGE,
546
+ category: error.ErrorCategory.THIRD_PARTY,
547
+ text: `Failed to alter table ${fullTableName}: ${error$1 instanceof Error ? error$1.message : String(error$1)}`,
548
+ details: { tableName }
549
+ },
550
+ error$1
551
+ );
470
552
  }
471
553
  }
472
554
  async clearTable({ tableName }) {
@@ -476,11 +558,17 @@ var D1Store = class extends storage.MastraStorage {
476
558
  const { sql, params } = query.build();
477
559
  await this.executeQuery({ sql, params });
478
560
  this.logger.debug(`Cleared table ${fullTableName}`);
479
- } catch (error) {
480
- this.logger.error(`Error clearing table ${fullTableName}:`, {
481
- message: error instanceof Error ? error.message : String(error)
482
- });
483
- throw new Error(`Failed to clear table ${fullTableName}: ${error}`);
561
+ } catch (error$1) {
562
+ throw new error.MastraError(
563
+ {
564
+ id: "CLOUDFLARE_D1_STORAGE_CLEAR_TABLE_ERROR",
565
+ domain: error.ErrorDomain.STORAGE,
566
+ category: error.ErrorCategory.THIRD_PARTY,
567
+ text: `Failed to clear table ${fullTableName}: ${error$1 instanceof Error ? error$1.message : String(error$1)}`,
568
+ details: { tableName }
569
+ },
570
+ error$1
571
+ );
484
572
  }
485
573
  }
486
574
  async processRecord(record) {
@@ -499,10 +587,17 @@ var D1Store = class extends storage.MastraStorage {
499
587
  const { sql, params } = query.build();
500
588
  try {
501
589
  await this.executeQuery({ sql, params });
502
- } catch (error) {
503
- const message = error instanceof Error ? error.message : String(error);
504
- this.logger.error(`Error inserting into ${fullTableName}:`, { message });
505
- throw new Error(`Failed to insert into ${fullTableName}: ${error}`);
590
+ } catch (error$1) {
591
+ throw new error.MastraError(
592
+ {
593
+ id: "CLOUDFLARE_D1_STORAGE_INSERT_ERROR",
594
+ domain: error.ErrorDomain.STORAGE,
595
+ category: error.ErrorCategory.THIRD_PARTY,
596
+ text: `Failed to insert into ${fullTableName}: ${error$1 instanceof Error ? error$1.message : String(error$1)}`,
597
+ details: { tableName }
598
+ },
599
+ error$1
600
+ );
506
601
  }
507
602
  }
508
603
  async load({ tableName, keys }) {
@@ -527,11 +622,17 @@ var D1Store = class extends storage.MastraStorage {
527
622
  processedResult[key] = this.deserializeValue(value);
528
623
  }
529
624
  return processedResult;
530
- } catch (error) {
531
- this.logger.error(`Error loading from ${fullTableName}:`, {
532
- message: error instanceof Error ? error.message : String(error)
533
- });
534
- return null;
625
+ } catch (error$1) {
626
+ throw new error.MastraError(
627
+ {
628
+ id: "CLOUDFLARE_D1_STORAGE_LOAD_ERROR",
629
+ domain: error.ErrorDomain.STORAGE,
630
+ category: error.ErrorCategory.THIRD_PARTY,
631
+ text: `Failed to load from ${fullTableName}: ${error$1 instanceof Error ? error$1.message : String(error$1)}`,
632
+ details: { tableName }
633
+ },
634
+ error$1
635
+ );
535
636
  }
536
637
  }
537
638
  async getThreadById({ threadId }) {
@@ -547,13 +648,25 @@ var D1Store = class extends storage.MastraStorage {
547
648
  updatedAt: this.ensureDate(thread.updatedAt),
548
649
  metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata || "{}") : thread.metadata || {}
549
650
  };
550
- } catch (error) {
551
- this.logger.error(`Error processing thread ${threadId}:`, {
552
- message: error instanceof Error ? error.message : String(error)
553
- });
651
+ } catch (error$1) {
652
+ const mastraError = new error.MastraError(
653
+ {
654
+ id: "CLOUDFLARE_D1_STORAGE_GET_THREAD_BY_ID_ERROR",
655
+ domain: error.ErrorDomain.STORAGE,
656
+ category: error.ErrorCategory.THIRD_PARTY,
657
+ text: `Error processing thread ${threadId}: ${error$1 instanceof Error ? error$1.message : String(error$1)}`,
658
+ details: { threadId }
659
+ },
660
+ error$1
661
+ );
662
+ this.logger?.error(mastraError.toString());
663
+ this.logger?.trackException(mastraError);
554
664
  return null;
555
665
  }
556
666
  }
667
+ /**
668
+ * @deprecated use getThreadsByResourceIdPaginated instead
669
+ */
557
670
  async getThreadsByResourceId({ resourceId }) {
558
671
  const fullTableName = this.getTableName(storage.TABLE_THREADS);
559
672
  try {
@@ -566,13 +679,67 @@ var D1Store = class extends storage.MastraStorage {
566
679
  updatedAt: this.ensureDate(thread.updatedAt),
567
680
  metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata || "{}") : thread.metadata || {}
568
681
  }));
569
- } catch (error) {
570
- this.logger.error(`Error getting threads by resourceId ${resourceId}:`, {
571
- message: error instanceof Error ? error.message : String(error)
572
- });
682
+ } catch (error$1) {
683
+ const mastraError = new error.MastraError(
684
+ {
685
+ id: "CLOUDFLARE_D1_STORAGE_GET_THREADS_BY_RESOURCE_ID_ERROR",
686
+ domain: error.ErrorDomain.STORAGE,
687
+ category: error.ErrorCategory.THIRD_PARTY,
688
+ text: `Error getting threads by resourceId ${resourceId}: ${error$1 instanceof Error ? error$1.message : String(error$1)}`,
689
+ details: { resourceId }
690
+ },
691
+ error$1
692
+ );
693
+ this.logger?.error(mastraError.toString());
694
+ this.logger?.trackException(mastraError);
573
695
  return [];
574
696
  }
575
697
  }
698
+ async getThreadsByResourceIdPaginated(args) {
699
+ const { resourceId, page, perPage } = args;
700
+ const fullTableName = this.getTableName(storage.TABLE_THREADS);
701
+ const mapRowToStorageThreadType = (row) => ({
702
+ ...row,
703
+ createdAt: this.ensureDate(row.createdAt),
704
+ updatedAt: this.ensureDate(row.updatedAt),
705
+ metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata || "{}") : row.metadata || {}
706
+ });
707
+ try {
708
+ const countQuery = createSqlBuilder().count().from(fullTableName).where("resourceId = ?", resourceId);
709
+ const countResult = await this.executeQuery(countQuery.build());
710
+ const total = Number(countResult?.[0]?.count ?? 0);
711
+ const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("resourceId = ?", resourceId).orderBy("createdAt", "DESC").limit(perPage).offset(page * perPage);
712
+ const results = await this.executeQuery(selectQuery.build());
713
+ const threads = results.map(mapRowToStorageThreadType);
714
+ return {
715
+ threads,
716
+ total,
717
+ page,
718
+ perPage,
719
+ hasMore: page * perPage + threads.length < total
720
+ };
721
+ } catch (error$1) {
722
+ const mastraError = new error.MastraError(
723
+ {
724
+ id: "CLOUDFLARE_D1_STORAGE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_ERROR",
725
+ domain: error.ErrorDomain.STORAGE,
726
+ category: error.ErrorCategory.THIRD_PARTY,
727
+ text: `Error getting threads by resourceId ${resourceId}: ${error$1 instanceof Error ? error$1.message : String(error$1)}`,
728
+ details: { resourceId }
729
+ },
730
+ error$1
731
+ );
732
+ this.logger?.error(mastraError.toString());
733
+ this.logger?.trackException(mastraError);
734
+ return {
735
+ threads: [],
736
+ total: 0,
737
+ page,
738
+ perPage,
739
+ hasMore: false
740
+ };
741
+ }
742
+ }
576
743
  async saveThread({ thread }) {
577
744
  const fullTableName = this.getTableName(storage.TABLE_THREADS);
578
745
  const threadToSave = {
@@ -598,10 +765,17 @@ var D1Store = class extends storage.MastraStorage {
598
765
  try {
599
766
  await this.executeQuery({ sql, params });
600
767
  return thread;
601
- } catch (error) {
602
- const message = error instanceof Error ? error.message : String(error);
603
- this.logger.error(`Error saving thread to ${fullTableName}:`, { message });
604
- throw error;
768
+ } catch (error$1) {
769
+ throw new error.MastraError(
770
+ {
771
+ id: "CLOUDFLARE_D1_STORAGE_SAVE_THREAD_ERROR",
772
+ domain: error.ErrorDomain.STORAGE,
773
+ category: error.ErrorCategory.THIRD_PARTY,
774
+ text: `Failed to save thread to ${fullTableName}: ${error$1 instanceof Error ? error$1.message : String(error$1)}`,
775
+ details: { threadId: thread.id }
776
+ },
777
+ error$1
778
+ );
605
779
  }
606
780
  }
607
781
  async updateThread({
@@ -610,19 +784,19 @@ var D1Store = class extends storage.MastraStorage {
610
784
  metadata
611
785
  }) {
612
786
  const thread = await this.getThreadById({ threadId: id });
613
- if (!thread) {
614
- throw new Error(`Thread ${id} not found`);
615
- }
616
- const fullTableName = this.getTableName(storage.TABLE_THREADS);
617
- const mergedMetadata = {
618
- ...typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
619
- ...metadata
620
- };
621
- const columns = ["title", "metadata", "updatedAt"];
622
- const values = [title, JSON.stringify(mergedMetadata), (/* @__PURE__ */ new Date()).toISOString()];
623
- const query = createSqlBuilder().update(fullTableName, columns, values).where("id = ?", id);
624
- const { sql, params } = query.build();
625
787
  try {
788
+ if (!thread) {
789
+ throw new Error(`Thread ${id} not found`);
790
+ }
791
+ const fullTableName = this.getTableName(storage.TABLE_THREADS);
792
+ const mergedMetadata = {
793
+ ...typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
794
+ ...metadata
795
+ };
796
+ const columns = ["title", "metadata", "updatedAt"];
797
+ const values = [title, JSON.stringify(mergedMetadata), (/* @__PURE__ */ new Date()).toISOString()];
798
+ const query = createSqlBuilder().update(fullTableName, columns, values).where("id = ?", id);
799
+ const { sql, params } = query.build();
626
800
  await this.executeQuery({ sql, params });
627
801
  return {
628
802
  ...thread,
@@ -633,10 +807,17 @@ var D1Store = class extends storage.MastraStorage {
633
807
  },
634
808
  updatedAt: /* @__PURE__ */ new Date()
635
809
  };
636
- } catch (error) {
637
- const message = error instanceof Error ? error.message : String(error);
638
- this.logger.error("Error updating thread:", { message });
639
- throw error;
810
+ } catch (error$1) {
811
+ throw new error.MastraError(
812
+ {
813
+ id: "CLOUDFLARE_D1_STORAGE_UPDATE_THREAD_ERROR",
814
+ domain: error.ErrorDomain.STORAGE,
815
+ category: error.ErrorCategory.THIRD_PARTY,
816
+ text: `Failed to update thread ${id}: ${error$1 instanceof Error ? error$1.message : String(error$1)}`,
817
+ details: { threadId: id }
818
+ },
819
+ error$1
820
+ );
640
821
  }
641
822
  }
642
823
  async deleteThread({ threadId }) {
@@ -649,23 +830,36 @@ var D1Store = class extends storage.MastraStorage {
649
830
  const deleteMessagesQuery = createSqlBuilder().delete(messagesTableName).where("thread_id = ?", threadId);
650
831
  const { sql: messagesSql, params: messagesParams } = deleteMessagesQuery.build();
651
832
  await this.executeQuery({ sql: messagesSql, params: messagesParams });
652
- } catch (error) {
653
- this.logger.error(`Error deleting thread ${threadId}:`, {
654
- message: error instanceof Error ? error.message : String(error)
655
- });
656
- throw new Error(`Failed to delete thread ${threadId}: ${error}`);
833
+ } catch (error$1) {
834
+ throw new error.MastraError(
835
+ {
836
+ id: "CLOUDFLARE_D1_STORAGE_DELETE_THREAD_ERROR",
837
+ domain: error.ErrorDomain.STORAGE,
838
+ category: error.ErrorCategory.THIRD_PARTY,
839
+ text: `Failed to delete thread ${threadId}: ${error$1 instanceof Error ? error$1.message : String(error$1)}`,
840
+ details: { threadId }
841
+ },
842
+ error$1
843
+ );
657
844
  }
658
845
  }
659
- // Thread and message management methods
660
- async saveMessages({ messages }) {
846
+ async saveMessages(args) {
847
+ const { messages, format = "v1" } = args;
661
848
  if (messages.length === 0) return [];
662
849
  try {
663
850
  const now = /* @__PURE__ */ new Date();
851
+ const threadId = messages[0]?.threadId;
664
852
  for (const [i, message] of messages.entries()) {
665
853
  if (!message.id) throw new Error(`Message at index ${i} missing id`);
666
- if (!message.threadId) throw new Error(`Message at index ${i} missing threadId`);
667
- if (!message.content) throw new Error(`Message at index ${i} missing content`);
668
- if (!message.role) throw new Error(`Message at index ${i} missing role`);
854
+ if (!message.threadId) {
855
+ throw new Error(`Message at index ${i} missing threadId`);
856
+ }
857
+ if (!message.content) {
858
+ throw new Error(`Message at index ${i} missing content`);
859
+ }
860
+ if (!message.role) {
861
+ throw new Error(`Message at index ${i} missing role`);
862
+ }
669
863
  const thread = await this.getThreadById({ threadId: message.threadId });
670
864
  if (!thread) {
671
865
  throw new Error(`Thread ${message.threadId} not found`);
@@ -679,36 +873,49 @@ var D1Store = class extends storage.MastraStorage {
679
873
  content: typeof message.content === "string" ? message.content : JSON.stringify(message.content),
680
874
  createdAt: createdAt.toISOString(),
681
875
  role: message.role,
682
- type: message.type
876
+ type: message.type || "v2",
877
+ resourceId: message.resourceId
683
878
  };
684
879
  });
685
- await this.batchInsert({
686
- tableName: storage.TABLE_MESSAGES,
687
- records: messagesToInsert
688
- });
880
+ await Promise.all([
881
+ this.batchUpsert({
882
+ tableName: storage.TABLE_MESSAGES,
883
+ records: messagesToInsert
884
+ }),
885
+ // Update thread's updatedAt timestamp
886
+ this.executeQuery({
887
+ sql: `UPDATE ${this.getTableName(storage.TABLE_THREADS)} SET updatedAt = ? WHERE id = ?`,
888
+ params: [now.toISOString(), threadId]
889
+ })
890
+ ]);
689
891
  this.logger.debug(`Saved ${messages.length} messages`);
690
- return messages;
691
- } catch (error) {
692
- this.logger.error("Error saving messages:", { message: error instanceof Error ? error.message : String(error) });
693
- throw error;
892
+ const list = new agent.MessageList().add(messages, "memory");
893
+ if (format === `v2`) return list.get.all.v2();
894
+ return list.get.all.v1();
895
+ } catch (error$1) {
896
+ throw new error.MastraError(
897
+ {
898
+ id: "CLOUDFLARE_D1_STORAGE_SAVE_MESSAGES_ERROR",
899
+ domain: error.ErrorDomain.STORAGE,
900
+ category: error.ErrorCategory.THIRD_PARTY,
901
+ text: `Failed to save messages: ${error$1 instanceof Error ? error$1.message : String(error$1)}`
902
+ },
903
+ error$1
904
+ );
694
905
  }
695
906
  }
696
- async getMessages({ threadId, selectBy }) {
697
- const fullTableName = this.getTableName(storage.TABLE_MESSAGES);
698
- const limit = typeof selectBy?.last === "number" ? selectBy.last : 40;
699
- const include = selectBy?.include || [];
700
- const messages = [];
701
- try {
702
- if (include.length) {
703
- const prevMax = Math.max(...include.map((i) => i.withPreviousMessages || 0));
704
- const nextMax = Math.max(...include.map((i) => i.withNextMessages || 0));
705
- const includeIds = include.map((i) => i.id);
706
- const sql2 = `
907
+ async _getIncludedMessages(threadId, selectBy) {
908
+ const include = selectBy?.include;
909
+ if (!include) return null;
910
+ const prevMax = Math.max(...include.map((i) => i.withPreviousMessages || 0));
911
+ const nextMax = Math.max(...include.map((i) => i.withNextMessages || 0));
912
+ const includeIds = include.map((i) => i.id);
913
+ const sql = `
707
914
  WITH ordered_messages AS (
708
915
  SELECT
709
916
  *,
710
917
  ROW_NUMBER() OVER (ORDER BY createdAt DESC) AS row_num
711
- FROM ${fullTableName}
918
+ FROM ${this.getTableName(storage.TABLE_MESSAGES)}
712
919
  WHERE thread_id = ?
713
920
  )
714
921
  SELECT
@@ -717,7 +924,7 @@ var D1Store = class extends storage.MastraStorage {
717
924
  m.role,
718
925
  m.type,
719
926
  m.createdAt,
720
- m.thread_id AS "threadId"
927
+ m.thread_id AS threadId
721
928
  FROM ordered_messages m
722
929
  WHERE m.id IN (${includeIds.map(() => "?").join(",")})
723
930
  OR EXISTS (
@@ -731,20 +938,41 @@ var D1Store = class extends storage.MastraStorage {
731
938
  )
732
939
  ORDER BY m.createdAt DESC
733
940
  `;
734
- const params2 = [
735
- threadId,
736
- ...includeIds,
737
- // for m.id IN (...)
738
- ...includeIds,
739
- // for target.id IN (...)
740
- prevMax,
741
- nextMax
742
- ];
743
- const includeResult = await this.executeQuery({ sql: sql2, params: params2 });
941
+ const params = [
942
+ threadId,
943
+ ...includeIds,
944
+ // for m.id IN (...)
945
+ ...includeIds,
946
+ // for target.id IN (...)
947
+ prevMax,
948
+ nextMax
949
+ ];
950
+ const messages = await this.executeQuery({ sql, params });
951
+ return messages;
952
+ }
953
+ async getMessages({
954
+ threadId,
955
+ selectBy,
956
+ format
957
+ }) {
958
+ const fullTableName = this.getTableName(storage.TABLE_MESSAGES);
959
+ const limit = this.resolveMessageLimit({
960
+ last: selectBy?.last,
961
+ defaultLimit: 40
962
+ });
963
+ const include = selectBy?.include || [];
964
+ const messages = [];
965
+ try {
966
+ if (include.length) {
967
+ const includeResult = await this._getIncludedMessages(threadId, selectBy);
744
968
  if (Array.isArray(includeResult)) messages.push(...includeResult);
745
969
  }
746
970
  const excludeIds = messages.map((m) => m.id);
747
- 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);
971
+ const query = createSqlBuilder().select(["id", "content", "role", "type", "createdAt", "thread_id AS threadId"]).from(fullTableName).where("thread_id = ?", threadId);
972
+ if (excludeIds.length > 0) {
973
+ query.andWhere(`id NOT IN (${excludeIds.map(() => "?").join(",")})`, ...excludeIds);
974
+ }
975
+ query.orderBy("createdAt", "DESC").limit(limit);
748
976
  const { sql, params } = query.build();
749
977
  const result = await this.executeQuery({ sql, params });
750
978
  if (Array.isArray(result)) messages.push(...result);
@@ -758,18 +986,92 @@ var D1Store = class extends storage.MastraStorage {
758
986
  const processedMessages = messages.map((message) => {
759
987
  const processedMsg = {};
760
988
  for (const [key, value] of Object.entries(message)) {
989
+ if (key === `type` && value === `v2`) continue;
761
990
  processedMsg[key] = this.deserializeValue(value);
762
991
  }
763
992
  return processedMsg;
764
993
  });
765
994
  this.logger.debug(`Retrieved ${messages.length} messages for thread ${threadId}`);
766
- return processedMessages;
767
- } catch (error) {
768
- this.logger.error("Error retrieving messages for thread", {
769
- threadId,
770
- message: error instanceof Error ? error.message : String(error)
771
- });
772
- return [];
995
+ const list = new agent.MessageList().add(processedMessages, "memory");
996
+ if (format === `v2`) return list.get.all.v2();
997
+ return list.get.all.v1();
998
+ } catch (error$1) {
999
+ const mastraError = new error.MastraError(
1000
+ {
1001
+ id: "CLOUDFLARE_D1_STORAGE_GET_MESSAGES_ERROR",
1002
+ domain: error.ErrorDomain.STORAGE,
1003
+ category: error.ErrorCategory.THIRD_PARTY,
1004
+ text: `Failed to retrieve messages for thread ${threadId}: ${error$1 instanceof Error ? error$1.message : String(error$1)}`,
1005
+ details: { threadId }
1006
+ },
1007
+ error$1
1008
+ );
1009
+ this.logger?.error(mastraError.toString());
1010
+ this.logger?.trackException(mastraError);
1011
+ throw mastraError;
1012
+ }
1013
+ }
1014
+ async getMessagesPaginated({
1015
+ threadId,
1016
+ selectBy,
1017
+ format
1018
+ }) {
1019
+ const { dateRange, page = 0, perPage = 40 } = selectBy?.pagination || {};
1020
+ const { start: fromDate, end: toDate } = dateRange || {};
1021
+ const fullTableName = this.getTableName(storage.TABLE_MESSAGES);
1022
+ const messages = [];
1023
+ try {
1024
+ if (selectBy?.include?.length) {
1025
+ const includeResult = await this._getIncludedMessages(threadId, selectBy);
1026
+ if (Array.isArray(includeResult)) messages.push(...includeResult);
1027
+ }
1028
+ const countQuery = createSqlBuilder().count().from(fullTableName).where("thread_id = ?", threadId);
1029
+ if (fromDate) {
1030
+ countQuery.andWhere("createdAt >= ?", this.serializeDate(fromDate));
1031
+ }
1032
+ if (toDate) {
1033
+ countQuery.andWhere("createdAt <= ?", this.serializeDate(toDate));
1034
+ }
1035
+ const countResult = await this.executeQuery(countQuery.build());
1036
+ const total = Number(countResult[0]?.count ?? 0);
1037
+ const query = createSqlBuilder().select(["id", "content", "role", "type", "createdAt", "thread_id AS threadId"]).from(fullTableName).where("thread_id = ?", threadId);
1038
+ if (fromDate) {
1039
+ query.andWhere("createdAt >= ?", this.serializeDate(fromDate));
1040
+ }
1041
+ if (toDate) {
1042
+ query.andWhere("createdAt <= ?", this.serializeDate(toDate));
1043
+ }
1044
+ query.orderBy("createdAt", "DESC").limit(perPage).offset(page * perPage);
1045
+ const results = await this.executeQuery(query.build());
1046
+ const list = new agent.MessageList().add(results, "memory");
1047
+ messages.push(...format === `v2` ? list.get.all.v2() : list.get.all.v1());
1048
+ return {
1049
+ messages,
1050
+ total,
1051
+ page,
1052
+ perPage,
1053
+ hasMore: page * perPage + messages.length < total
1054
+ };
1055
+ } catch (error$1) {
1056
+ const mastraError = new error.MastraError(
1057
+ {
1058
+ id: "CLOUDFLARE_D1_STORAGE_GET_MESSAGES_PAGINATED_ERROR",
1059
+ domain: error.ErrorDomain.STORAGE,
1060
+ category: error.ErrorCategory.THIRD_PARTY,
1061
+ text: `Failed to retrieve messages for thread ${threadId}: ${error$1 instanceof Error ? error$1.message : String(error$1)}`,
1062
+ details: { threadId }
1063
+ },
1064
+ error$1
1065
+ );
1066
+ this.logger?.error(mastraError.toString());
1067
+ this.logger?.trackException(mastraError);
1068
+ return {
1069
+ messages: [],
1070
+ total: 0,
1071
+ page,
1072
+ perPage,
1073
+ hasMore: false
1074
+ };
773
1075
  }
774
1076
  }
775
1077
  async persistWorkflowSnapshot({
@@ -806,24 +1108,43 @@ var D1Store = class extends storage.MastraStorage {
806
1108
  const { sql, params } = query.build();
807
1109
  try {
808
1110
  await this.executeQuery({ sql, params });
809
- } catch (error) {
810
- this.logger.error("Error persisting workflow snapshot:", {
811
- message: error instanceof Error ? error.message : String(error)
812
- });
813
- throw error;
1111
+ } catch (error$1) {
1112
+ throw new error.MastraError(
1113
+ {
1114
+ id: "CLOUDFLARE_D1_STORAGE_PERSIST_WORKFLOW_SNAPSHOT_ERROR",
1115
+ domain: error.ErrorDomain.STORAGE,
1116
+ category: error.ErrorCategory.THIRD_PARTY,
1117
+ text: `Failed to persist workflow snapshot: ${error$1 instanceof Error ? error$1.message : String(error$1)}`,
1118
+ details: { workflowName, runId }
1119
+ },
1120
+ error$1
1121
+ );
814
1122
  }
815
1123
  }
816
1124
  async loadWorkflowSnapshot(params) {
817
1125
  const { workflowName, runId } = params;
818
1126
  this.logger.debug("Loading workflow snapshot", { workflowName, runId });
819
- const d = await this.load({
820
- tableName: storage.TABLE_WORKFLOW_SNAPSHOT,
821
- keys: {
822
- workflow_name: workflowName,
823
- run_id: runId
824
- }
825
- });
826
- return d ? d.snapshot : null;
1127
+ try {
1128
+ const d = await this.load({
1129
+ tableName: storage.TABLE_WORKFLOW_SNAPSHOT,
1130
+ keys: {
1131
+ workflow_name: workflowName,
1132
+ run_id: runId
1133
+ }
1134
+ });
1135
+ return d ? d.snapshot : null;
1136
+ } catch (error$1) {
1137
+ throw new error.MastraError(
1138
+ {
1139
+ id: "CLOUDFLARE_D1_STORAGE_LOAD_WORKFLOW_SNAPSHOT_ERROR",
1140
+ domain: error.ErrorDomain.STORAGE,
1141
+ category: error.ErrorCategory.THIRD_PARTY,
1142
+ text: `Failed to load workflow snapshot: ${error$1 instanceof Error ? error$1.message : String(error$1)}`,
1143
+ details: { workflowName, runId }
1144
+ },
1145
+ error$1
1146
+ );
1147
+ }
827
1148
  }
828
1149
  /**
829
1150
  * Insert multiple records in a batch operation
@@ -857,19 +1178,85 @@ var D1Store = class extends storage.MastraStorage {
857
1178
  );
858
1179
  }
859
1180
  this.logger.debug(`Successfully batch inserted ${records.length} records into ${tableName}`);
860
- } catch (error) {
861
- this.logger.error(`Error batch inserting into ${tableName}:`, {
862
- message: error instanceof Error ? error.message : String(error)
863
- });
864
- throw new Error(`Failed to batch insert into ${tableName}: ${error}`);
1181
+ } catch (error$1) {
1182
+ throw new error.MastraError(
1183
+ {
1184
+ id: "CLOUDFLARE_D1_STORAGE_BATCH_INSERT_ERROR",
1185
+ domain: error.ErrorDomain.STORAGE,
1186
+ category: error.ErrorCategory.THIRD_PARTY,
1187
+ text: `Failed to batch insert into ${tableName}: ${error$1 instanceof Error ? error$1.message : String(error$1)}`,
1188
+ details: { tableName }
1189
+ },
1190
+ error$1
1191
+ );
1192
+ }
1193
+ }
1194
+ /**
1195
+ * Upsert multiple records in a batch operation
1196
+ * @param tableName The table to insert into
1197
+ * @param records The records to insert
1198
+ */
1199
+ async batchUpsert({
1200
+ tableName,
1201
+ records
1202
+ }) {
1203
+ if (records.length === 0) return;
1204
+ const fullTableName = this.getTableName(tableName);
1205
+ try {
1206
+ const batchSize = 50;
1207
+ for (let i = 0; i < records.length; i += batchSize) {
1208
+ const batch = records.slice(i, i + batchSize);
1209
+ const recordsToInsert = batch;
1210
+ if (recordsToInsert.length > 0) {
1211
+ const firstRecord = recordsToInsert[0];
1212
+ const columns = Object.keys(firstRecord || {});
1213
+ for (const record of recordsToInsert) {
1214
+ const values = columns.map((col) => {
1215
+ if (!record) return null;
1216
+ const value = typeof col === "string" ? record[col] : null;
1217
+ return this.serializeValue(value);
1218
+ });
1219
+ const recordToUpsert = columns.reduce(
1220
+ (acc, col) => {
1221
+ if (col !== "createdAt") acc[col] = `excluded.${col}`;
1222
+ return acc;
1223
+ },
1224
+ {}
1225
+ );
1226
+ const query = createSqlBuilder().insert(fullTableName, columns, values, ["id"], recordToUpsert);
1227
+ const { sql, params } = query.build();
1228
+ await this.executeQuery({ sql, params });
1229
+ }
1230
+ }
1231
+ this.logger.debug(
1232
+ `Processed batch ${Math.floor(i / batchSize) + 1} of ${Math.ceil(records.length / batchSize)}`
1233
+ );
1234
+ }
1235
+ this.logger.debug(`Successfully batch upserted ${records.length} records into ${tableName}`);
1236
+ } catch (error$1) {
1237
+ throw new error.MastraError(
1238
+ {
1239
+ id: "CLOUDFLARE_D1_STORAGE_BATCH_UPSERT_ERROR",
1240
+ domain: error.ErrorDomain.STORAGE,
1241
+ category: error.ErrorCategory.THIRD_PARTY,
1242
+ text: `Failed to batch upsert into ${tableName}: ${error$1 instanceof Error ? error$1.message : String(error$1)}`,
1243
+ details: { tableName }
1244
+ },
1245
+ error$1
1246
+ );
865
1247
  }
866
1248
  }
1249
+ /**
1250
+ * @deprecated use getTracesPaginated instead
1251
+ */
867
1252
  async getTraces({
868
1253
  name,
869
1254
  scope,
870
1255
  page,
871
1256
  perPage,
872
- attributes
1257
+ attributes,
1258
+ fromDate,
1259
+ toDate
873
1260
  }) {
874
1261
  const fullTableName = this.getTableName(storage.TABLE_TRACES);
875
1262
  try {
@@ -885,22 +1272,114 @@ var D1Store = class extends storage.MastraStorage {
885
1272
  query.jsonLike("attributes", key, value);
886
1273
  }
887
1274
  }
888
- query.orderBy("startTime", "DESC").limit(perPage).offset((page - 1) * perPage);
1275
+ if (fromDate) {
1276
+ query.andWhere("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
1277
+ }
1278
+ if (toDate) {
1279
+ query.andWhere("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
1280
+ }
1281
+ query.orderBy("startTime", "DESC").limit(perPage).offset(page * perPage);
889
1282
  const { sql, params } = query.build();
890
1283
  const results = await this.executeQuery({ sql, params });
891
- return isArrayOfRecords(results) ? results.map((trace) => ({
892
- ...trace,
893
- attributes: this.deserializeValue(trace.attributes, "jsonb"),
894
- status: this.deserializeValue(trace.status, "jsonb"),
895
- events: this.deserializeValue(trace.events, "jsonb"),
896
- links: this.deserializeValue(trace.links, "jsonb"),
897
- other: this.deserializeValue(trace.other, "jsonb")
898
- })) : [];
899
- } catch (error) {
900
- this.logger.error("Error getting traces:", { message: error instanceof Error ? error.message : String(error) });
1284
+ return isArrayOfRecords(results) ? results.map(
1285
+ (trace) => ({
1286
+ ...trace,
1287
+ attributes: this.deserializeValue(trace.attributes, "jsonb"),
1288
+ status: this.deserializeValue(trace.status, "jsonb"),
1289
+ events: this.deserializeValue(trace.events, "jsonb"),
1290
+ links: this.deserializeValue(trace.links, "jsonb"),
1291
+ other: this.deserializeValue(trace.other, "jsonb")
1292
+ })
1293
+ ) : [];
1294
+ } catch (error$1) {
1295
+ const mastraError = new error.MastraError(
1296
+ {
1297
+ id: "CLOUDFLARE_D1_STORAGE_GET_TRACES_ERROR",
1298
+ domain: error.ErrorDomain.STORAGE,
1299
+ category: error.ErrorCategory.THIRD_PARTY,
1300
+ text: `Failed to retrieve traces: ${error$1 instanceof Error ? error$1.message : String(error$1)}`,
1301
+ details: {
1302
+ name: name ?? "",
1303
+ scope: scope ?? ""
1304
+ }
1305
+ },
1306
+ error$1
1307
+ );
1308
+ this.logger?.error(mastraError.toString());
1309
+ this.logger?.trackException(mastraError);
901
1310
  return [];
902
1311
  }
903
1312
  }
1313
+ async getTracesPaginated(args) {
1314
+ const { name, scope, page, perPage, attributes, fromDate, toDate } = args;
1315
+ const fullTableName = this.getTableName(storage.TABLE_TRACES);
1316
+ try {
1317
+ const dataQuery = createSqlBuilder().select("*").from(fullTableName).where("1=1");
1318
+ const countQuery = createSqlBuilder().count().from(fullTableName).where("1=1");
1319
+ if (name) {
1320
+ dataQuery.andWhere("name LIKE ?", `%${name}%`);
1321
+ countQuery.andWhere("name LIKE ?", `%${name}%`);
1322
+ }
1323
+ if (scope) {
1324
+ dataQuery.andWhere("scope = ?", scope);
1325
+ countQuery.andWhere("scope = ?", scope);
1326
+ }
1327
+ if (attributes && Object.keys(attributes).length > 0) {
1328
+ for (const [key, value] of Object.entries(attributes)) {
1329
+ dataQuery.jsonLike("attributes", key, value);
1330
+ countQuery.jsonLike("attributes", key, value);
1331
+ }
1332
+ }
1333
+ if (fromDate) {
1334
+ const fromDateStr = fromDate instanceof Date ? fromDate.toISOString() : fromDate;
1335
+ dataQuery.andWhere("createdAt >= ?", fromDateStr);
1336
+ countQuery.andWhere("createdAt >= ?", fromDateStr);
1337
+ }
1338
+ if (toDate) {
1339
+ const toDateStr = toDate instanceof Date ? toDate.toISOString() : toDate;
1340
+ dataQuery.andWhere("createdAt <= ?", toDateStr);
1341
+ countQuery.andWhere("createdAt <= ?", toDateStr);
1342
+ }
1343
+ const countResult = await this.executeQuery(countQuery.build());
1344
+ const total = Number(countResult?.[0]?.count ?? 0);
1345
+ dataQuery.orderBy("startTime", "DESC").limit(perPage).offset(page * perPage);
1346
+ const results = await this.executeQuery(dataQuery.build());
1347
+ const traces = isArrayOfRecords(results) ? results.map(
1348
+ (trace) => ({
1349
+ ...trace,
1350
+ attributes: this.deserializeValue(trace.attributes, "jsonb"),
1351
+ status: this.deserializeValue(trace.status, "jsonb"),
1352
+ events: this.deserializeValue(trace.events, "jsonb"),
1353
+ links: this.deserializeValue(trace.links, "jsonb"),
1354
+ other: this.deserializeValue(trace.other, "jsonb")
1355
+ })
1356
+ ) : [];
1357
+ return {
1358
+ traces,
1359
+ total,
1360
+ page,
1361
+ perPage,
1362
+ hasMore: page * perPage + traces.length < total
1363
+ };
1364
+ } catch (error$1) {
1365
+ const mastraError = new error.MastraError(
1366
+ {
1367
+ id: "CLOUDFLARE_D1_STORAGE_GET_TRACES_ERROR",
1368
+ domain: error.ErrorDomain.STORAGE,
1369
+ category: error.ErrorCategory.THIRD_PARTY,
1370
+ text: `Failed to retrieve traces: ${error$1 instanceof Error ? error$1.message : String(error$1)}`,
1371
+ details: { name: name ?? "", scope: scope ?? "" }
1372
+ },
1373
+ error$1
1374
+ );
1375
+ this.logger?.error(mastraError.toString());
1376
+ this.logger?.trackException(mastraError);
1377
+ return { traces: [], total: 0, page, perPage, hasMore: false };
1378
+ }
1379
+ }
1380
+ /**
1381
+ * @deprecated use getEvals instead
1382
+ */
904
1383
  async getEvalsByAgentName(agentName, type) {
905
1384
  const fullTableName = this.getTableName(storage.TABLE_EVALS);
906
1385
  try {
@@ -929,13 +1408,116 @@ var D1Store = class extends storage.MastraStorage {
929
1408
  testInfo
930
1409
  };
931
1410
  }) : [];
932
- } catch (error) {
933
- this.logger.error(`Error getting evals for agent ${agentName}:`, {
934
- message: error instanceof Error ? error.message : String(error)
935
- });
1411
+ } catch (error$1) {
1412
+ const mastraError = new error.MastraError(
1413
+ {
1414
+ id: "CLOUDFLARE_D1_STORAGE_GET_EVALS_ERROR",
1415
+ domain: error.ErrorDomain.STORAGE,
1416
+ category: error.ErrorCategory.THIRD_PARTY,
1417
+ text: `Failed to retrieve evals for agent ${agentName}: ${error$1 instanceof Error ? error$1.message : String(error$1)}`,
1418
+ details: { agentName }
1419
+ },
1420
+ error$1
1421
+ );
1422
+ this.logger?.error(mastraError.toString());
1423
+ this.logger?.trackException(mastraError);
936
1424
  return [];
937
1425
  }
938
1426
  }
1427
+ async getEvals(options) {
1428
+ const { agentName, type, page = 0, perPage = 40, fromDate, toDate } = options || {};
1429
+ const fullTableName = this.getTableName(storage.TABLE_EVALS);
1430
+ const conditions = [];
1431
+ const queryParams = [];
1432
+ if (agentName) {
1433
+ conditions.push(`agent_name = ?`);
1434
+ queryParams.push(agentName);
1435
+ }
1436
+ if (type === "test") {
1437
+ conditions.push(`(test_info IS NOT NULL AND json_extract(test_info, '$.testPath') IS NOT NULL)`);
1438
+ } else if (type === "live") {
1439
+ conditions.push(`(test_info IS NULL OR json_extract(test_info, '$.testPath') IS NULL)`);
1440
+ }
1441
+ if (fromDate) {
1442
+ conditions.push(`createdAt >= ?`);
1443
+ queryParams.push(this.serializeDate(fromDate));
1444
+ }
1445
+ if (toDate) {
1446
+ conditions.push(`createdAt <= ?`);
1447
+ queryParams.push(this.serializeDate(toDate));
1448
+ }
1449
+ const countQueryBuilder = createSqlBuilder().count().from(fullTableName);
1450
+ if (conditions.length > 0) {
1451
+ countQueryBuilder.where(conditions.join(" AND "), ...queryParams);
1452
+ }
1453
+ const { sql: countSql, params: countParams } = countQueryBuilder.build();
1454
+ try {
1455
+ const countResult = await this.executeQuery({
1456
+ sql: countSql,
1457
+ params: countParams,
1458
+ first: true
1459
+ });
1460
+ const total = Number(countResult?.count || 0);
1461
+ const currentOffset = page * perPage;
1462
+ if (total === 0) {
1463
+ return {
1464
+ evals: [],
1465
+ total: 0,
1466
+ page,
1467
+ perPage,
1468
+ hasMore: false
1469
+ };
1470
+ }
1471
+ const dataQueryBuilder = createSqlBuilder().select("*").from(fullTableName);
1472
+ if (conditions.length > 0) {
1473
+ dataQueryBuilder.where(conditions.join(" AND "), ...queryParams);
1474
+ }
1475
+ dataQueryBuilder.orderBy("createdAt", "DESC").limit(perPage).offset(currentOffset);
1476
+ const { sql: dataSql, params: dataParams } = dataQueryBuilder.build();
1477
+ const rows = await this.executeQuery({
1478
+ sql: dataSql,
1479
+ params: dataParams
1480
+ });
1481
+ const evals = (isArrayOfRecords(rows) ? rows : []).map((row) => {
1482
+ const result = this.deserializeValue(row.result);
1483
+ const testInfo = row.test_info ? this.deserializeValue(row.test_info) : void 0;
1484
+ if (!result || typeof result !== "object" || !("score" in result)) {
1485
+ throw new Error(`Invalid MetricResult format: ${JSON.stringify(result)}`);
1486
+ }
1487
+ return {
1488
+ input: row.input,
1489
+ output: row.output,
1490
+ result,
1491
+ agentName: row.agent_name,
1492
+ metricName: row.metric_name,
1493
+ instructions: row.instructions,
1494
+ testInfo,
1495
+ globalRunId: row.global_run_id,
1496
+ runId: row.run_id,
1497
+ createdAt: row.createdAt
1498
+ };
1499
+ });
1500
+ const hasMore = currentOffset + evals.length < total;
1501
+ return {
1502
+ evals,
1503
+ total,
1504
+ page,
1505
+ perPage,
1506
+ hasMore
1507
+ };
1508
+ } catch (error$1) {
1509
+ throw new error.MastraError(
1510
+ {
1511
+ id: "CLOUDFLARE_D1_STORAGE_GET_EVALS_ERROR",
1512
+ domain: error.ErrorDomain.STORAGE,
1513
+ category: error.ErrorCategory.THIRD_PARTY,
1514
+ text: `Failed to retrieve evals for agent ${agentName}: ${error$1 instanceof Error ? error$1.message : String(error$1)}`,
1515
+ details: { agentName: agentName ?? "", type: type ?? "" }
1516
+ },
1517
+ error$1
1518
+ );
1519
+ }
1520
+ }
939
1521
  parseWorkflowRun(row) {
940
1522
  let parsedSnapshot = row.snapshot;
941
1523
  if (typeof parsedSnapshot === "string") {
@@ -997,17 +1579,30 @@ var D1Store = class extends storage.MastraStorage {
997
1579
  let total = 0;
998
1580
  if (limit !== void 0 && offset !== void 0) {
999
1581
  const { sql: countSql, params: countParams } = countBuilder.build();
1000
- const countResult = await this.executeQuery({ sql: countSql, params: countParams, first: true });
1582
+ const countResult = await this.executeQuery({
1583
+ sql: countSql,
1584
+ params: countParams,
1585
+ first: true
1586
+ });
1001
1587
  total = Number(countResult?.count ?? 0);
1002
1588
  }
1003
1589
  const results = await this.executeQuery({ sql, params });
1004
1590
  const runs = (isArrayOfRecords(results) ? results : []).map((row) => this.parseWorkflowRun(row));
1005
1591
  return { runs, total: total || runs.length };
1006
- } catch (error) {
1007
- this.logger.error("Error getting workflow runs:", {
1008
- message: error instanceof Error ? error.message : String(error)
1009
- });
1010
- throw error;
1592
+ } catch (error$1) {
1593
+ throw new error.MastraError(
1594
+ {
1595
+ id: "CLOUDFLARE_D1_STORAGE_GET_WORKFLOW_RUNS_ERROR",
1596
+ domain: error.ErrorDomain.STORAGE,
1597
+ category: error.ErrorCategory.THIRD_PARTY,
1598
+ text: `Failed to retrieve workflow runs: ${error$1 instanceof Error ? error$1.message : String(error$1)}`,
1599
+ details: {
1600
+ workflowName: workflowName ?? "",
1601
+ resourceId: resourceId ?? ""
1602
+ }
1603
+ },
1604
+ error$1
1605
+ );
1011
1606
  }
1012
1607
  }
1013
1608
  async getWorkflowRunById({
@@ -1031,11 +1626,17 @@ var D1Store = class extends storage.MastraStorage {
1031
1626
  const result = await this.executeQuery({ sql, params, first: true });
1032
1627
  if (!result) return null;
1033
1628
  return this.parseWorkflowRun(result);
1034
- } catch (error) {
1035
- this.logger.error("Error getting workflow run by ID:", {
1036
- message: error instanceof Error ? error.message : String(error)
1037
- });
1038
- throw error;
1629
+ } catch (error$1) {
1630
+ throw new error.MastraError(
1631
+ {
1632
+ id: "CLOUDFLARE_D1_STORAGE_GET_WORKFLOW_RUN_BY_ID_ERROR",
1633
+ domain: error.ErrorDomain.STORAGE,
1634
+ category: error.ErrorCategory.THIRD_PARTY,
1635
+ text: `Failed to retrieve workflow run by ID: ${error$1 instanceof Error ? error$1.message : String(error$1)}`,
1636
+ details: { runId, workflowName: workflowName ?? "" }
1637
+ },
1638
+ error$1
1639
+ );
1039
1640
  }
1040
1641
  }
1041
1642
  /**
@@ -1045,6 +1646,10 @@ var D1Store = class extends storage.MastraStorage {
1045
1646
  async close() {
1046
1647
  this.logger.debug("Closing D1 connection");
1047
1648
  }
1649
+ async updateMessages(_args) {
1650
+ this.logger.error("updateMessages is not yet implemented in CloudflareD1Store");
1651
+ throw new Error("Method not implemented");
1652
+ }
1048
1653
  };
1049
1654
 
1050
1655
  exports.D1Store = D1Store;