@mastra/cloudflare-d1 0.0.0-vnextWorkflows-20250422081019 → 0.0.0-workflow-deno-20250616115451

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,15 @@
1
1
  'use strict';
2
2
 
3
+ var agent = require('@mastra/core/agent');
3
4
  var storage = require('@mastra/core/storage');
4
5
  var Cloudflare = require('cloudflare');
6
+ var utils = require('@mastra/core/utils');
5
7
 
6
8
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
9
 
8
10
  var Cloudflare__default = /*#__PURE__*/_interopDefault(Cloudflare);
9
11
 
10
12
  // src/storage/index.ts
11
-
12
- // src/storage/sql-builder.ts
13
13
  var SqlBuilder = class {
14
14
  sql = "";
15
15
  params = [];
@@ -19,12 +19,15 @@ var SqlBuilder = class {
19
19
  if (!columns || Array.isArray(columns) && columns.length === 0) {
20
20
  this.sql = "SELECT *";
21
21
  } else {
22
- this.sql = `SELECT ${Array.isArray(columns) ? columns.join(", ") : columns}`;
22
+ const cols = Array.isArray(columns) ? columns : [columns];
23
+ const parsedCols = cols.map((col) => parseSelectIdentifier(col));
24
+ this.sql = `SELECT ${parsedCols.join(", ")}`;
23
25
  }
24
26
  return this;
25
27
  }
26
28
  from(table) {
27
- this.sql += ` FROM ${table}`;
29
+ const parsedTableName = utils.parseSqlIdentifier(table, "table name");
30
+ this.sql += ` FROM ${parsedTableName}`;
28
31
  return this;
29
32
  }
30
33
  /**
@@ -61,7 +64,11 @@ var SqlBuilder = class {
61
64
  return this;
62
65
  }
63
66
  orderBy(column, direction = "ASC") {
64
- this.sql += ` ORDER BY ${column} ${direction}`;
67
+ const parsedColumn = utils.parseSqlIdentifier(column, "column name");
68
+ if (!["ASC", "DESC"].includes(direction)) {
69
+ throw new Error(`Invalid sort direction: ${direction}`);
70
+ }
71
+ this.sql += ` ORDER BY ${parsedColumn} ${direction}`;
65
72
  return this;
66
73
  }
67
74
  limit(count) {
@@ -74,6 +81,10 @@ var SqlBuilder = class {
74
81
  this.params.push(count);
75
82
  return this;
76
83
  }
84
+ count() {
85
+ this.sql += "SELECT COUNT(*) AS count";
86
+ return this;
87
+ }
77
88
  /**
78
89
  * Insert a row, or update specific columns on conflict (upsert).
79
90
  * @param table Table name
@@ -83,27 +94,33 @@ var SqlBuilder = class {
83
94
  * @param updateMap Object mapping columns to update to their new value (e.g. { name: 'excluded.name' })
84
95
  */
85
96
  insert(table, columns, values, conflictColumns, updateMap) {
86
- const placeholders = columns.map(() => "?").join(", ");
97
+ const parsedTableName = utils.parseSqlIdentifier(table, "table name");
98
+ const parsedColumns = columns.map((col) => utils.parseSqlIdentifier(col, "column name"));
99
+ const placeholders = parsedColumns.map(() => "?").join(", ");
87
100
  if (conflictColumns && updateMap) {
101
+ const parsedConflictColumns = conflictColumns.map((col) => utils.parseSqlIdentifier(col, "column name"));
88
102
  const updateClause = Object.entries(updateMap).map(([col, expr]) => `${col} = ${expr}`).join(", ");
89
- this.sql = `INSERT INTO ${table} (${columns.join(", ")}) VALUES (${placeholders}) ON CONFLICT(${conflictColumns.join(", ")}) DO UPDATE SET ${updateClause}`;
103
+ this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders}) ON CONFLICT(${parsedConflictColumns.join(", ")}) DO UPDATE SET ${updateClause}`;
90
104
  this.params.push(...values);
91
105
  return this;
92
106
  }
93
- this.sql = `INSERT INTO ${table} (${columns.join(", ")}) VALUES (${placeholders})`;
107
+ this.sql = `INSERT INTO ${parsedTableName} (${parsedColumns.join(", ")}) VALUES (${placeholders})`;
94
108
  this.params.push(...values);
95
109
  return this;
96
110
  }
97
111
  // Update operations
98
112
  update(table, columns, values) {
99
- const setClause = columns.map((col) => `${col} = ?`).join(", ");
100
- this.sql = `UPDATE ${table} SET ${setClause}`;
113
+ const parsedTableName = utils.parseSqlIdentifier(table, "table name");
114
+ const parsedColumns = columns.map((col) => utils.parseSqlIdentifier(col, "column name"));
115
+ const setClause = parsedColumns.map((col) => `${col} = ?`).join(", ");
116
+ this.sql = `UPDATE ${parsedTableName} SET ${setClause}`;
101
117
  this.params.push(...values);
102
118
  return this;
103
119
  }
104
120
  // Delete operations
105
121
  delete(table) {
106
- this.sql = `DELETE FROM ${table}`;
122
+ const parsedTableName = utils.parseSqlIdentifier(table, "table name");
123
+ this.sql = `DELETE FROM ${parsedTableName}`;
107
124
  return this;
108
125
  }
109
126
  /**
@@ -114,9 +131,16 @@ var SqlBuilder = class {
114
131
  * @returns The builder instance
115
132
  */
116
133
  createTable(table, columnDefinitions, tableConstraints) {
117
- const columns = columnDefinitions.join(", ");
134
+ const parsedTableName = utils.parseSqlIdentifier(table, "table name");
135
+ const parsedColumnDefinitions = columnDefinitions.map((def) => {
136
+ const colName = def.split(/\s+/)[0];
137
+ if (!colName) throw new Error("Empty column name in definition");
138
+ utils.parseSqlIdentifier(colName, "column name");
139
+ return def;
140
+ });
141
+ const columns = parsedColumnDefinitions.join(", ");
118
142
  const constraints = tableConstraints && tableConstraints.length > 0 ? ", " + tableConstraints.join(", ") : "";
119
- this.sql = `CREATE TABLE IF NOT EXISTS ${table} (${columns}${constraints})`;
143
+ this.sql = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (${columns}${constraints})`;
120
144
  return this;
121
145
  }
122
146
  /**
@@ -139,13 +163,10 @@ var SqlBuilder = class {
139
163
  * @returns The builder instance
140
164
  */
141
165
  createIndex(indexName, tableName, columnName, indexType = "") {
142
- this.sql = `CREATE ${indexType ? indexType + " " : ""}INDEX IF NOT EXISTS ${indexName} ON ${tableName}(${columnName})`;
143
- return this;
144
- }
145
- // Raw SQL with params
146
- raw(sql, ...params) {
147
- this.sql = sql;
148
- this.params.push(...params);
166
+ const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
167
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
168
+ const parsedColumnName = utils.parseSqlIdentifier(columnName, "column name");
169
+ this.sql = `CREATE ${indexType ? indexType + " " : ""}INDEX IF NOT EXISTS ${parsedIndexName} ON ${parsedTableName}(${parsedColumnName})`;
149
170
  return this;
150
171
  }
151
172
  /**
@@ -155,11 +176,12 @@ var SqlBuilder = class {
155
176
  * @param exact If true, will not add % wildcards
156
177
  */
157
178
  like(column, value, exact = false) {
179
+ const parsedColumnName = utils.parseSqlIdentifier(column, "column name");
158
180
  const likeValue = exact ? value : `%${value}%`;
159
181
  if (this.whereAdded) {
160
- this.sql += ` AND ${column} LIKE ?`;
182
+ this.sql += ` AND ${parsedColumnName} LIKE ?`;
161
183
  } else {
162
- this.sql += ` WHERE ${column} LIKE ?`;
184
+ this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
163
185
  this.whereAdded = true;
164
186
  }
165
187
  this.params.push(likeValue);
@@ -172,11 +194,13 @@ var SqlBuilder = class {
172
194
  * @param value The value to match
173
195
  */
174
196
  jsonLike(column, key, value) {
175
- const jsonPattern = `%"${key}":"${value}"%`;
197
+ const parsedColumnName = utils.parseSqlIdentifier(column, "column name");
198
+ const parsedKey = utils.parseSqlIdentifier(key, "key name");
199
+ const jsonPattern = `%"${parsedKey}":"${value}"%`;
176
200
  if (this.whereAdded) {
177
- this.sql += ` AND ${column} LIKE ?`;
201
+ this.sql += ` AND ${parsedColumnName} LIKE ?`;
178
202
  } else {
179
- this.sql += ` WHERE ${column} LIKE ?`;
203
+ this.sql += ` WHERE ${parsedColumnName} LIKE ?`;
180
204
  this.whereAdded = true;
181
205
  }
182
206
  this.params.push(jsonPattern);
@@ -206,6 +230,15 @@ var SqlBuilder = class {
206
230
  function createSqlBuilder() {
207
231
  return new SqlBuilder();
208
232
  }
233
+ var SQL_IDENTIFIER_PATTERN = /^[a-zA-Z0-9_]+(\s+AS\s+[a-zA-Z0-9_]+)?$/;
234
+ function parseSelectIdentifier(column) {
235
+ if (column !== "*" && !SQL_IDENTIFIER_PATTERN.test(column)) {
236
+ throw new Error(
237
+ `Invalid column name: "${column}". Must be "*" or a valid identifier (letters, numbers, underscores), optionally with "AS alias".`
238
+ );
239
+ }
240
+ return column;
241
+ }
209
242
 
210
243
  // src/storage/index.ts
211
244
  function isArrayOfRecords(value) {
@@ -224,6 +257,9 @@ var D1Store = class extends storage.MastraStorage {
224
257
  */
225
258
  constructor(config) {
226
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.");
262
+ }
227
263
  this.tablePrefix = config.tablePrefix || "";
228
264
  if ("binding" in config) {
229
265
  if (!config.binding) {
@@ -250,30 +286,6 @@ var D1Store = class extends storage.MastraStorage {
250
286
  formatSqlParams(params) {
251
287
  return params.map((p) => p === void 0 || p === null ? null : p);
252
288
  }
253
- // Helper method to create SQL indexes for better query performance
254
- async createIndexIfNotExists(tableName, columnName, indexType = "") {
255
- const fullTableName = this.getTableName(tableName);
256
- const indexName = `idx_${tableName}_${columnName}`;
257
- try {
258
- const checkQuery = createSqlBuilder().checkIndexExists(indexName, fullTableName);
259
- const { sql: checkSql, params: checkParams } = checkQuery.build();
260
- const indexExists = await this.executeQuery({
261
- sql: checkSql,
262
- params: checkParams,
263
- first: true
264
- });
265
- if (!indexExists) {
266
- const createQuery = createSqlBuilder().createIndex(indexName, fullTableName, columnName, indexType);
267
- const { sql: createSql, params: createParams } = createQuery.build();
268
- await this.executeQuery({ sql: createSql, params: createParams });
269
- this.logger.debug(`Created index ${indexName} on ${fullTableName}(${columnName})`);
270
- }
271
- } catch (error) {
272
- this.logger.error(`Error creating index on ${fullTableName}(${columnName}):`, {
273
- message: error instanceof Error ? error.message : String(error)
274
- });
275
- }
276
- }
277
289
  async executeWorkersBindingQuery({
278
290
  sql,
279
291
  params = [],
@@ -377,34 +389,25 @@ var D1Store = class extends storage.MastraStorage {
377
389
  throw new Error(`D1 query error: ${error.message}`);
378
390
  }
379
391
  }
380
- // Helper to convert storage type to SQL type
381
- getSqlType(type) {
382
- switch (type) {
383
- case "text":
384
- return "TEXT";
385
- case "timestamp":
386
- return "TIMESTAMP";
387
- case "integer":
388
- return "INTEGER";
389
- case "bigint":
390
- return "INTEGER";
391
- // SQLite doesn't have a separate BIGINT type
392
- case "jsonb":
393
- return "TEXT";
394
- // Store JSON as TEXT in SQLite
395
- default:
396
- return "TEXT";
392
+ // Helper to get existing table columns
393
+ async getTableColumns(tableName) {
394
+ try {
395
+ const sql = `PRAGMA table_info(${tableName})`;
396
+ const result = await this.executeQuery({ sql, params: [] });
397
+ if (!result || !Array.isArray(result)) {
398
+ return [];
399
+ }
400
+ return result.map((row) => ({
401
+ name: row.name,
402
+ type: row.type
403
+ }));
404
+ } catch (error) {
405
+ this.logger.error(`Error getting table columns for ${tableName}:`, {
406
+ message: error instanceof Error ? error.message : String(error)
407
+ });
408
+ return [];
397
409
  }
398
410
  }
399
- ensureDate(date) {
400
- if (!date) return void 0;
401
- return date instanceof Date ? date : new Date(date);
402
- }
403
- serializeDate(date) {
404
- if (!date) return void 0;
405
- const dateObj = this.ensureDate(date);
406
- return dateObj?.toISOString();
407
- }
408
411
  // Helper to serialize objects to JSON strings
409
412
  serializeValue(value) {
410
413
  if (value === null || value === void 0) return null;
@@ -438,6 +441,18 @@ var D1Store = class extends storage.MastraStorage {
438
441
  }
439
442
  return value;
440
443
  }
444
+ getSqlType(type) {
445
+ switch (type) {
446
+ case "bigint":
447
+ return "INTEGER";
448
+ // SQLite uses INTEGER for all integer sizes
449
+ case "jsonb":
450
+ return "TEXT";
451
+ // Store JSON as TEXT in SQLite
452
+ default:
453
+ return super.getSqlType(type);
454
+ }
455
+ }
441
456
  async createTable({
442
457
  tableName,
443
458
  schema
@@ -465,6 +480,39 @@ var D1Store = class extends storage.MastraStorage {
465
480
  throw new Error(`Failed to create table ${fullTableName}: ${error}`);
466
481
  }
467
482
  }
483
+ /**
484
+ * Alters table schema to add columns if they don't exist
485
+ * @param tableName Name of the table
486
+ * @param schema Schema of the table
487
+ * @param ifNotExists Array of column names to add if they don't exist
488
+ */
489
+ async alterTable({
490
+ tableName,
491
+ schema,
492
+ ifNotExists
493
+ }) {
494
+ const fullTableName = this.getTableName(tableName);
495
+ try {
496
+ const existingColumns = await this.getTableColumns(fullTableName);
497
+ const existingColumnNames = new Set(existingColumns.map((col) => col.name.toLowerCase()));
498
+ for (const columnName of ifNotExists) {
499
+ if (!existingColumnNames.has(columnName.toLowerCase()) && schema[columnName]) {
500
+ const columnDef = schema[columnName];
501
+ const sqlType = this.getSqlType(columnDef.type);
502
+ const nullable = columnDef.nullable === false ? "NOT NULL" : "";
503
+ const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
504
+ const alterSql = `ALTER TABLE ${fullTableName} ADD COLUMN ${columnName} ${sqlType} ${nullable} ${defaultValue}`.trim();
505
+ await this.executeQuery({ sql: alterSql, params: [] });
506
+ this.logger.debug(`Added column ${columnName} to table ${fullTableName}`);
507
+ }
508
+ }
509
+ } catch (error) {
510
+ this.logger.error(`Error altering table ${fullTableName}:`, {
511
+ message: error instanceof Error ? error.message : String(error)
512
+ });
513
+ throw new Error(`Failed to alter table ${fullTableName}: ${error}`);
514
+ }
515
+ }
468
516
  async clearTable({ tableName }) {
469
517
  const fullTableName = this.getTableName(tableName);
470
518
  try {
@@ -496,7 +544,8 @@ var D1Store = class extends storage.MastraStorage {
496
544
  try {
497
545
  await this.executeQuery({ sql, params });
498
546
  } catch (error) {
499
- this.logger.error(`Error inserting into ${fullTableName}:`, { error });
547
+ const message = error instanceof Error ? error.message : String(error);
548
+ this.logger.error(`Error inserting into ${fullTableName}:`, { message });
500
549
  throw new Error(`Failed to insert into ${fullTableName}: ${error}`);
501
550
  }
502
551
  }
@@ -549,6 +598,9 @@ var D1Store = class extends storage.MastraStorage {
549
598
  return null;
550
599
  }
551
600
  }
601
+ /**
602
+ * @deprecated use getThreadsByResourceIdPaginated instead
603
+ */
552
604
  async getThreadsByResourceId({ resourceId }) {
553
605
  const fullTableName = this.getTableName(storage.TABLE_THREADS);
554
606
  try {
@@ -568,6 +620,29 @@ var D1Store = class extends storage.MastraStorage {
568
620
  return [];
569
621
  }
570
622
  }
623
+ async getThreadsByResourceIdPaginated(args) {
624
+ const { resourceId, page, perPage } = args;
625
+ const fullTableName = this.getTableName(storage.TABLE_THREADS);
626
+ const mapRowToStorageThreadType = (row) => ({
627
+ ...row,
628
+ createdAt: this.ensureDate(row.createdAt),
629
+ updatedAt: this.ensureDate(row.updatedAt),
630
+ metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata || "{}") : row.metadata || {}
631
+ });
632
+ const countQuery = createSqlBuilder().count().from(fullTableName).where("resourceId = ?", resourceId);
633
+ const countResult = await this.executeQuery(countQuery.build());
634
+ const total = Number(countResult?.[0]?.count ?? 0);
635
+ const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("resourceId = ?", resourceId).orderBy("createdAt", "DESC").limit(perPage).offset(page * perPage);
636
+ const results = await this.executeQuery(selectQuery.build());
637
+ const threads = results.map(mapRowToStorageThreadType);
638
+ return {
639
+ threads,
640
+ total,
641
+ page,
642
+ perPage,
643
+ hasMore: page * perPage + threads.length < total
644
+ };
645
+ }
571
646
  async saveThread({ thread }) {
572
647
  const fullTableName = this.getTableName(storage.TABLE_THREADS);
573
648
  const threadToSave = {
@@ -594,7 +669,8 @@ var D1Store = class extends storage.MastraStorage {
594
669
  await this.executeQuery({ sql, params });
595
670
  return thread;
596
671
  } catch (error) {
597
- this.logger.error(`Error saving thread to ${fullTableName}:`, { error });
672
+ const message = error instanceof Error ? error.message : String(error);
673
+ this.logger.error(`Error saving thread to ${fullTableName}:`, { message });
598
674
  throw error;
599
675
  }
600
676
  }
@@ -628,7 +704,8 @@ var D1Store = class extends storage.MastraStorage {
628
704
  updatedAt: /* @__PURE__ */ new Date()
629
705
  };
630
706
  } catch (error) {
631
- this.logger.error("Error updating thread:", { error });
707
+ const message = error instanceof Error ? error.message : String(error);
708
+ this.logger.error("Error updating thread:", { message });
632
709
  throw error;
633
710
  }
634
711
  }
@@ -649,11 +726,12 @@ var D1Store = class extends storage.MastraStorage {
649
726
  throw new Error(`Failed to delete thread ${threadId}: ${error}`);
650
727
  }
651
728
  }
652
- // Thread and message management methods
653
- async saveMessages({ messages }) {
729
+ async saveMessages(args) {
730
+ const { messages, format = "v1" } = args;
654
731
  if (messages.length === 0) return [];
655
732
  try {
656
733
  const now = /* @__PURE__ */ new Date();
734
+ const threadId = messages[0]?.threadId;
657
735
  for (const [i, message] of messages.entries()) {
658
736
  if (!message.id) throw new Error(`Message at index ${i} missing id`);
659
737
  if (!message.threadId) throw new Error(`Message at index ${i} missing threadId`);
@@ -672,36 +750,42 @@ var D1Store = class extends storage.MastraStorage {
672
750
  content: typeof message.content === "string" ? message.content : JSON.stringify(message.content),
673
751
  createdAt: createdAt.toISOString(),
674
752
  role: message.role,
675
- type: message.type
753
+ type: message.type || "v2",
754
+ resourceId: message.resourceId
676
755
  };
677
756
  });
678
- await this.batchInsert({
679
- tableName: storage.TABLE_MESSAGES,
680
- records: messagesToInsert
681
- });
757
+ await Promise.all([
758
+ this.batchInsert({
759
+ tableName: storage.TABLE_MESSAGES,
760
+ records: messagesToInsert
761
+ }),
762
+ // Update thread's updatedAt timestamp
763
+ this.executeQuery({
764
+ sql: `UPDATE ${this.getTableName(storage.TABLE_THREADS)} SET updatedAt = ? WHERE id = ?`,
765
+ params: [now.toISOString(), threadId]
766
+ })
767
+ ]);
682
768
  this.logger.debug(`Saved ${messages.length} messages`);
683
- return messages;
769
+ const list = new agent.MessageList().add(messages, "memory");
770
+ if (format === `v2`) return list.get.all.v2();
771
+ return list.get.all.v1();
684
772
  } catch (error) {
685
773
  this.logger.error("Error saving messages:", { message: error instanceof Error ? error.message : String(error) });
686
774
  throw error;
687
775
  }
688
776
  }
689
- async getMessages({ threadId, selectBy }) {
690
- const fullTableName = this.getTableName(storage.TABLE_MESSAGES);
691
- const limit = typeof selectBy?.last === "number" ? selectBy.last : 40;
692
- const include = selectBy?.include || [];
693
- const messages = [];
694
- try {
695
- if (include.length) {
696
- const prevMax = Math.max(...include.map((i) => i.withPreviousMessages || 0));
697
- const nextMax = Math.max(...include.map((i) => i.withNextMessages || 0));
698
- const includeIds = include.map((i) => i.id);
699
- const sql2 = `
777
+ async _getIncludedMessages(threadId, selectBy) {
778
+ const include = selectBy?.include;
779
+ if (!include) return null;
780
+ const prevMax = Math.max(...include.map((i) => i.withPreviousMessages || 0));
781
+ const nextMax = Math.max(...include.map((i) => i.withNextMessages || 0));
782
+ const includeIds = include.map((i) => i.id);
783
+ const sql = `
700
784
  WITH ordered_messages AS (
701
785
  SELECT
702
786
  *,
703
787
  ROW_NUMBER() OVER (ORDER BY createdAt DESC) AS row_num
704
- FROM ${fullTableName}
788
+ FROM ${this.getTableName(storage.TABLE_MESSAGES)}
705
789
  WHERE thread_id = ?
706
790
  )
707
791
  SELECT
@@ -710,7 +794,7 @@ var D1Store = class extends storage.MastraStorage {
710
794
  m.role,
711
795
  m.type,
712
796
  m.createdAt,
713
- m.thread_id AS "threadId"
797
+ m.thread_id AS threadId
714
798
  FROM ordered_messages m
715
799
  WHERE m.id IN (${includeIds.map(() => "?").join(",")})
716
800
  OR EXISTS (
@@ -724,20 +808,38 @@ var D1Store = class extends storage.MastraStorage {
724
808
  )
725
809
  ORDER BY m.createdAt DESC
726
810
  `;
727
- const params2 = [
728
- threadId,
729
- ...includeIds,
730
- // for m.id IN (...)
731
- ...includeIds,
732
- // for target.id IN (...)
733
- prevMax,
734
- nextMax
735
- ];
736
- const includeResult = await this.executeQuery({ sql: sql2, params: params2 });
811
+ const params = [
812
+ threadId,
813
+ ...includeIds,
814
+ // for m.id IN (...)
815
+ ...includeIds,
816
+ // for target.id IN (...)
817
+ prevMax,
818
+ nextMax
819
+ ];
820
+ const messages = await this.executeQuery({ sql, params });
821
+ return messages;
822
+ }
823
+ async getMessages({
824
+ threadId,
825
+ selectBy,
826
+ format
827
+ }) {
828
+ const fullTableName = this.getTableName(storage.TABLE_MESSAGES);
829
+ const limit = typeof selectBy?.last === "number" ? selectBy.last : 40;
830
+ const include = selectBy?.include || [];
831
+ const messages = [];
832
+ try {
833
+ if (include.length) {
834
+ const includeResult = await this._getIncludedMessages(threadId, selectBy);
737
835
  if (Array.isArray(includeResult)) messages.push(...includeResult);
738
836
  }
739
837
  const excludeIds = messages.map((m) => m.id);
740
- 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);
838
+ const query = createSqlBuilder().select(["id", "content", "role", "type", "createdAt", "thread_id AS threadId"]).from(fullTableName).where("thread_id = ?", threadId);
839
+ if (excludeIds.length > 0) {
840
+ query.andWhere(`id NOT IN (${excludeIds.map(() => "?").join(",")})`, ...excludeIds);
841
+ }
842
+ query.orderBy("createdAt", "DESC").limit(limit);
741
843
  const { sql, params } = query.build();
742
844
  const result = await this.executeQuery({ sql, params });
743
845
  if (Array.isArray(result)) messages.push(...result);
@@ -751,12 +853,15 @@ var D1Store = class extends storage.MastraStorage {
751
853
  const processedMessages = messages.map((message) => {
752
854
  const processedMsg = {};
753
855
  for (const [key, value] of Object.entries(message)) {
856
+ if (key === `type` && value === `v2`) continue;
754
857
  processedMsg[key] = this.deserializeValue(value);
755
858
  }
756
859
  return processedMsg;
757
860
  });
758
861
  this.logger.debug(`Retrieved ${messages.length} messages for thread ${threadId}`);
759
- return processedMessages;
862
+ const list = new agent.MessageList().add(processedMessages, "memory");
863
+ if (format === `v2`) return list.get.all.v2();
864
+ return list.get.all.v1();
760
865
  } catch (error) {
761
866
  this.logger.error("Error retrieving messages for thread", {
762
867
  threadId,
@@ -765,6 +870,47 @@ var D1Store = class extends storage.MastraStorage {
765
870
  return [];
766
871
  }
767
872
  }
873
+ async getMessagesPaginated({
874
+ threadId,
875
+ selectBy,
876
+ format
877
+ }) {
878
+ const { dateRange, page = 0, perPage = 40 } = selectBy?.pagination || {};
879
+ const { start: fromDate, end: toDate } = dateRange || {};
880
+ const fullTableName = this.getTableName(storage.TABLE_MESSAGES);
881
+ const messages = [];
882
+ if (selectBy?.include?.length) {
883
+ const includeResult = await this._getIncludedMessages(threadId, selectBy);
884
+ if (Array.isArray(includeResult)) messages.push(...includeResult);
885
+ }
886
+ const countQuery = createSqlBuilder().count().from(fullTableName).where("thread_id = ?", threadId);
887
+ if (fromDate) {
888
+ countQuery.andWhere("createdAt >= ?", this.serializeDate(fromDate));
889
+ }
890
+ if (toDate) {
891
+ countQuery.andWhere("createdAt <= ?", this.serializeDate(toDate));
892
+ }
893
+ const countResult = await this.executeQuery(countQuery.build());
894
+ const total = Number(countResult[0]?.count ?? 0);
895
+ const query = createSqlBuilder().select(["id", "content", "role", "type", "createdAt", "thread_id AS threadId"]).from(fullTableName).where("thread_id = ?", threadId);
896
+ if (fromDate) {
897
+ query.andWhere("createdAt >= ?", this.serializeDate(fromDate));
898
+ }
899
+ if (toDate) {
900
+ query.andWhere("createdAt <= ?", this.serializeDate(toDate));
901
+ }
902
+ query.orderBy("createdAt", "DESC").limit(perPage).offset(page * perPage);
903
+ const results = await this.executeQuery(query.build());
904
+ const list = new agent.MessageList().add(results, "memory");
905
+ messages.push(...format === `v2` ? list.get.all.v2() : list.get.all.v1());
906
+ return {
907
+ messages,
908
+ total,
909
+ page,
910
+ perPage,
911
+ hasMore: page * perPage + messages.length < total
912
+ };
913
+ }
768
914
  async persistWorkflowSnapshot({
769
915
  workflowName,
770
916
  runId,
@@ -857,12 +1003,17 @@ var D1Store = class extends storage.MastraStorage {
857
1003
  throw new Error(`Failed to batch insert into ${tableName}: ${error}`);
858
1004
  }
859
1005
  }
1006
+ /**
1007
+ * @deprecated use getTracesPaginated instead
1008
+ */
860
1009
  async getTraces({
861
1010
  name,
862
1011
  scope,
863
1012
  page,
864
1013
  perPage,
865
- attributes
1014
+ attributes,
1015
+ fromDate,
1016
+ toDate
866
1017
  }) {
867
1018
  const fullTableName = this.getTableName(storage.TABLE_TRACES);
868
1019
  try {
@@ -878,22 +1029,89 @@ var D1Store = class extends storage.MastraStorage {
878
1029
  query.jsonLike("attributes", key, value);
879
1030
  }
880
1031
  }
881
- query.orderBy("startTime", "DESC").limit(perPage).offset((page - 1) * perPage);
1032
+ if (fromDate) {
1033
+ query.andWhere("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
1034
+ }
1035
+ if (toDate) {
1036
+ query.andWhere("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
1037
+ }
1038
+ query.orderBy("startTime", "DESC").limit(perPage).offset(page * perPage);
882
1039
  const { sql, params } = query.build();
883
1040
  const results = await this.executeQuery({ sql, params });
884
- return isArrayOfRecords(results) ? results.map((trace) => ({
885
- ...trace,
886
- attributes: this.deserializeValue(trace.attributes, "jsonb"),
887
- status: this.deserializeValue(trace.status, "jsonb"),
888
- events: this.deserializeValue(trace.events, "jsonb"),
889
- links: this.deserializeValue(trace.links, "jsonb"),
890
- other: this.deserializeValue(trace.other, "jsonb")
891
- })) : [];
1041
+ return isArrayOfRecords(results) ? results.map(
1042
+ (trace) => ({
1043
+ ...trace,
1044
+ attributes: this.deserializeValue(trace.attributes, "jsonb"),
1045
+ status: this.deserializeValue(trace.status, "jsonb"),
1046
+ events: this.deserializeValue(trace.events, "jsonb"),
1047
+ links: this.deserializeValue(trace.links, "jsonb"),
1048
+ other: this.deserializeValue(trace.other, "jsonb")
1049
+ })
1050
+ ) : [];
892
1051
  } catch (error) {
893
1052
  this.logger.error("Error getting traces:", { message: error instanceof Error ? error.message : String(error) });
894
1053
  return [];
895
1054
  }
896
1055
  }
1056
+ async getTracesPaginated(args) {
1057
+ const { name, scope, page, perPage, attributes, fromDate, toDate } = args;
1058
+ const fullTableName = this.getTableName(storage.TABLE_TRACES);
1059
+ try {
1060
+ const dataQuery = createSqlBuilder().select("*").from(fullTableName).where("1=1");
1061
+ const countQuery = createSqlBuilder().count().from(fullTableName).where("1=1");
1062
+ if (name) {
1063
+ dataQuery.andWhere("name LIKE ?", `%${name}%`);
1064
+ countQuery.andWhere("name LIKE ?", `%${name}%`);
1065
+ }
1066
+ if (scope) {
1067
+ dataQuery.andWhere("scope = ?", scope);
1068
+ countQuery.andWhere("scope = ?", scope);
1069
+ }
1070
+ if (attributes && Object.keys(attributes).length > 0) {
1071
+ for (const [key, value] of Object.entries(attributes)) {
1072
+ dataQuery.jsonLike("attributes", key, value);
1073
+ countQuery.jsonLike("attributes", key, value);
1074
+ }
1075
+ }
1076
+ if (fromDate) {
1077
+ const fromDateStr = fromDate instanceof Date ? fromDate.toISOString() : fromDate;
1078
+ dataQuery.andWhere("createdAt >= ?", fromDateStr);
1079
+ countQuery.andWhere("createdAt >= ?", fromDateStr);
1080
+ }
1081
+ if (toDate) {
1082
+ const toDateStr = toDate instanceof Date ? toDate.toISOString() : toDate;
1083
+ dataQuery.andWhere("createdAt <= ?", toDateStr);
1084
+ countQuery.andWhere("createdAt <= ?", toDateStr);
1085
+ }
1086
+ const countResult = await this.executeQuery(countQuery.build());
1087
+ const total = Number(countResult?.[0]?.count ?? 0);
1088
+ dataQuery.orderBy("startTime", "DESC").limit(perPage).offset(page * perPage);
1089
+ const results = await this.executeQuery(dataQuery.build());
1090
+ const traces = isArrayOfRecords(results) ? results.map(
1091
+ (trace) => ({
1092
+ ...trace,
1093
+ attributes: this.deserializeValue(trace.attributes, "jsonb"),
1094
+ status: this.deserializeValue(trace.status, "jsonb"),
1095
+ events: this.deserializeValue(trace.events, "jsonb"),
1096
+ links: this.deserializeValue(trace.links, "jsonb"),
1097
+ other: this.deserializeValue(trace.other, "jsonb")
1098
+ })
1099
+ ) : [];
1100
+ return {
1101
+ traces,
1102
+ total,
1103
+ page,
1104
+ perPage,
1105
+ hasMore: page * perPage + traces.length < total
1106
+ };
1107
+ } catch (error) {
1108
+ this.logger.error("Error getting traces:", { message: error instanceof Error ? error.message : String(error) });
1109
+ return { traces: [], total: 0, page, perPage, hasMore: false };
1110
+ }
1111
+ }
1112
+ /**
1113
+ * @deprecated use getEvals instead
1114
+ */
897
1115
  async getEvalsByAgentName(agentName, type) {
898
1116
  const fullTableName = this.getTableName(storage.TABLE_EVALS);
899
1117
  try {
@@ -929,8 +1147,181 @@ var D1Store = class extends storage.MastraStorage {
929
1147
  return [];
930
1148
  }
931
1149
  }
932
- getWorkflowRuns(_args) {
933
- throw new Error("Method not implemented.");
1150
+ async getEvals(options) {
1151
+ const { agentName, type, page = 0, perPage = 40, fromDate, toDate } = options || {};
1152
+ const fullTableName = this.getTableName(storage.TABLE_EVALS);
1153
+ const conditions = [];
1154
+ const queryParams = [];
1155
+ if (agentName) {
1156
+ conditions.push(`agent_name = ?`);
1157
+ queryParams.push(agentName);
1158
+ }
1159
+ if (type === "test") {
1160
+ conditions.push(`(test_info IS NOT NULL AND json_extract(test_info, '$.testPath') IS NOT NULL)`);
1161
+ } else if (type === "live") {
1162
+ conditions.push(`(test_info IS NULL OR json_extract(test_info, '$.testPath') IS NULL)`);
1163
+ }
1164
+ if (fromDate) {
1165
+ conditions.push(`createdAt >= ?`);
1166
+ queryParams.push(this.serializeDate(fromDate));
1167
+ }
1168
+ if (toDate) {
1169
+ conditions.push(`createdAt <= ?`);
1170
+ queryParams.push(this.serializeDate(toDate));
1171
+ }
1172
+ const countQueryBuilder = createSqlBuilder().count().from(fullTableName);
1173
+ if (conditions.length > 0) {
1174
+ countQueryBuilder.where(conditions.join(" AND "), ...queryParams);
1175
+ }
1176
+ const { sql: countSql, params: countParams } = countQueryBuilder.build();
1177
+ const countResult = await this.executeQuery({ sql: countSql, params: countParams, first: true });
1178
+ const total = Number(countResult?.count || 0);
1179
+ const currentOffset = page * perPage;
1180
+ if (total === 0) {
1181
+ return {
1182
+ evals: [],
1183
+ total: 0,
1184
+ page,
1185
+ perPage,
1186
+ hasMore: false
1187
+ };
1188
+ }
1189
+ const dataQueryBuilder = createSqlBuilder().select("*").from(fullTableName);
1190
+ if (conditions.length > 0) {
1191
+ dataQueryBuilder.where(conditions.join(" AND "), ...queryParams);
1192
+ }
1193
+ dataQueryBuilder.orderBy("createdAt", "DESC").limit(perPage).offset(currentOffset);
1194
+ const { sql: dataSql, params: dataParams } = dataQueryBuilder.build();
1195
+ const rows = await this.executeQuery({ sql: dataSql, params: dataParams });
1196
+ const evals = (isArrayOfRecords(rows) ? rows : []).map((row) => {
1197
+ const result = this.deserializeValue(row.result);
1198
+ const testInfo = row.test_info ? this.deserializeValue(row.test_info) : void 0;
1199
+ if (!result || typeof result !== "object" || !("score" in result)) {
1200
+ throw new Error(`Invalid MetricResult format: ${JSON.stringify(result)}`);
1201
+ }
1202
+ return {
1203
+ input: row.input,
1204
+ output: row.output,
1205
+ result,
1206
+ agentName: row.agent_name,
1207
+ metricName: row.metric_name,
1208
+ instructions: row.instructions,
1209
+ testInfo,
1210
+ globalRunId: row.global_run_id,
1211
+ runId: row.run_id,
1212
+ createdAt: row.createdAt
1213
+ };
1214
+ });
1215
+ const hasMore = currentOffset + evals.length < total;
1216
+ return {
1217
+ evals,
1218
+ total,
1219
+ page,
1220
+ perPage,
1221
+ hasMore
1222
+ };
1223
+ }
1224
+ parseWorkflowRun(row) {
1225
+ let parsedSnapshot = row.snapshot;
1226
+ if (typeof parsedSnapshot === "string") {
1227
+ try {
1228
+ parsedSnapshot = JSON.parse(row.snapshot);
1229
+ } catch (e) {
1230
+ console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
1231
+ }
1232
+ }
1233
+ return {
1234
+ workflowName: row.workflow_name,
1235
+ runId: row.run_id,
1236
+ snapshot: parsedSnapshot,
1237
+ createdAt: this.ensureDate(row.createdAt),
1238
+ updatedAt: this.ensureDate(row.updatedAt),
1239
+ resourceId: row.resourceId
1240
+ };
1241
+ }
1242
+ async hasColumn(table, column) {
1243
+ const sql = `PRAGMA table_info(${table});`;
1244
+ const result = await this.executeQuery({ sql, params: [] });
1245
+ if (!result || !Array.isArray(result)) return false;
1246
+ return result.some((col) => col.name === column || col.name === column.toLowerCase());
1247
+ }
1248
+ async getWorkflowRuns({
1249
+ workflowName,
1250
+ fromDate,
1251
+ toDate,
1252
+ limit,
1253
+ offset,
1254
+ resourceId
1255
+ } = {}) {
1256
+ const fullTableName = this.getTableName(storage.TABLE_WORKFLOW_SNAPSHOT);
1257
+ try {
1258
+ const builder = createSqlBuilder().select().from(fullTableName);
1259
+ const countBuilder = createSqlBuilder().count().from(fullTableName);
1260
+ if (workflowName) builder.whereAnd("workflow_name = ?", workflowName);
1261
+ if (resourceId) {
1262
+ const hasResourceId = await this.hasColumn(fullTableName, "resourceId");
1263
+ if (hasResourceId) {
1264
+ builder.whereAnd("resourceId = ?", resourceId);
1265
+ countBuilder.whereAnd("resourceId = ?", resourceId);
1266
+ } else {
1267
+ console.warn(`[${fullTableName}] resourceId column not found. Skipping resourceId filter.`);
1268
+ }
1269
+ }
1270
+ if (fromDate) {
1271
+ builder.whereAnd("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
1272
+ countBuilder.whereAnd("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
1273
+ }
1274
+ if (toDate) {
1275
+ builder.whereAnd("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
1276
+ countBuilder.whereAnd("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
1277
+ }
1278
+ builder.orderBy("createdAt", "DESC");
1279
+ if (typeof limit === "number") builder.limit(limit);
1280
+ if (typeof offset === "number") builder.offset(offset);
1281
+ const { sql, params } = builder.build();
1282
+ let total = 0;
1283
+ if (limit !== void 0 && offset !== void 0) {
1284
+ const { sql: countSql, params: countParams } = countBuilder.build();
1285
+ const countResult = await this.executeQuery({ sql: countSql, params: countParams, first: true });
1286
+ total = Number(countResult?.count ?? 0);
1287
+ }
1288
+ const results = await this.executeQuery({ sql, params });
1289
+ const runs = (isArrayOfRecords(results) ? results : []).map((row) => this.parseWorkflowRun(row));
1290
+ return { runs, total: total || runs.length };
1291
+ } catch (error) {
1292
+ this.logger.error("Error getting workflow runs:", {
1293
+ message: error instanceof Error ? error.message : String(error)
1294
+ });
1295
+ throw error;
1296
+ }
1297
+ }
1298
+ async getWorkflowRunById({
1299
+ runId,
1300
+ workflowName
1301
+ }) {
1302
+ const fullTableName = this.getTableName(storage.TABLE_WORKFLOW_SNAPSHOT);
1303
+ try {
1304
+ const conditions = [];
1305
+ const params = [];
1306
+ if (runId) {
1307
+ conditions.push("run_id = ?");
1308
+ params.push(runId);
1309
+ }
1310
+ if (workflowName) {
1311
+ conditions.push("workflow_name = ?");
1312
+ params.push(workflowName);
1313
+ }
1314
+ const whereClause = conditions.length > 0 ? "WHERE " + conditions.join(" AND ") : "";
1315
+ const sql = `SELECT * FROM ${fullTableName} ${whereClause} ORDER BY createdAt DESC LIMIT 1`;
1316
+ const result = await this.executeQuery({ sql, params, first: true });
1317
+ if (!result) return null;
1318
+ return this.parseWorkflowRun(result);
1319
+ } catch (error) {
1320
+ this.logger.error("Error getting workflow run by ID:", {
1321
+ message: error instanceof Error ? error.message : String(error)
1322
+ });
1323
+ throw error;
1324
+ }
934
1325
  }
935
1326
  /**
936
1327
  * Close the database connection