@mastra/cloudflare-d1 0.0.0-working-memory-per-user-20250620163010 → 0.0.0-zod-v4-compat-part-2-20250822105954

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,8 @@
1
- import { MessageList } from '@mastra/core/agent';
2
- import { MastraStorage, TABLE_THREADS, TABLE_MESSAGES, TABLE_WORKFLOW_SNAPSHOT, TABLE_TRACES, TABLE_EVALS } from '@mastra/core/storage';
1
+ import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
2
+ import { MastraStorage, StoreOperations, ScoresStorage, TABLE_SCORERS, LegacyEvalsStorage, TABLE_EVALS, serializeDate, TracesStorage, TABLE_TRACES, WorkflowsStorage, TABLE_WORKFLOW_SNAPSHOT, ensureDate, MemoryStorage, TABLE_RESOURCES, TABLE_THREADS, TABLE_MESSAGES, resolveMessageLimit } from '@mastra/core/storage';
3
3
  import Cloudflare from 'cloudflare';
4
4
  import { parseSqlIdentifier } from '@mastra/core/utils';
5
+ import { MessageList } from '@mastra/core/agent';
5
6
 
6
7
  // src/storage/index.ts
7
8
  var SqlBuilder = class {
@@ -234,361 +235,326 @@ function parseSelectIdentifier(column) {
234
235
  return column;
235
236
  }
236
237
 
237
- // src/storage/index.ts
238
+ // src/storage/domains/utils.ts
238
239
  function isArrayOfRecords(value) {
239
240
  return value && Array.isArray(value) && value.length > 0;
240
241
  }
241
- var D1Store = class extends MastraStorage {
242
- client;
243
- accountId;
244
- databaseId;
245
- binding;
246
- // D1Database binding
247
- tablePrefix;
248
- /**
249
- * Creates a new D1Store instance
250
- * @param config Configuration for D1 access (either REST API or Workers Binding API)
251
- */
252
- constructor(config) {
253
- super({ name: "D1" });
254
- if (config.tablePrefix && !/^[a-zA-Z0-9_]*$/.test(config.tablePrefix)) {
255
- throw new Error("Invalid tablePrefix: only letters, numbers, and underscores are allowed.");
256
- }
257
- this.tablePrefix = config.tablePrefix || "";
258
- if ("binding" in config) {
259
- if (!config.binding) {
260
- throw new Error("D1 binding is required when using Workers Binding API");
261
- }
262
- this.binding = config.binding;
263
- this.logger.info("Using D1 Workers Binding API");
264
- } else {
265
- if (!config.accountId || !config.databaseId || !config.apiToken) {
266
- throw new Error("accountId, databaseId, and apiToken are required when using REST API");
267
- }
268
- this.accountId = config.accountId;
269
- this.databaseId = config.databaseId;
270
- this.client = new Cloudflare({
271
- apiToken: config.apiToken
272
- });
273
- this.logger.info("Using D1 REST API");
274
- }
275
- }
276
- // Helper method to get the full table name with prefix
277
- getTableName(tableName) {
278
- return `${this.tablePrefix}${tableName}`;
279
- }
280
- formatSqlParams(params) {
281
- return params.map((p) => p === void 0 || p === null ? null : p);
282
- }
283
- async executeWorkersBindingQuery({
284
- sql,
285
- params = [],
286
- first = false
287
- }) {
288
- if (!this.binding) {
289
- throw new Error("Workers binding is not configured");
290
- }
291
- try {
292
- const statement = this.binding.prepare(sql);
293
- const formattedParams = this.formatSqlParams(params);
294
- let result;
295
- if (formattedParams.length > 0) {
296
- if (first) {
297
- result = await statement.bind(...formattedParams).first();
298
- if (!result) return null;
299
- return result;
300
- } else {
301
- result = await statement.bind(...formattedParams).all();
302
- const results = result.results || [];
303
- if (result.meta) {
304
- this.logger.debug("Query metadata", { meta: result.meta });
305
- }
306
- return results;
307
- }
308
- } else {
309
- if (first) {
310
- result = await statement.first();
311
- if (!result) return null;
312
- return result;
313
- } else {
314
- result = await statement.all();
315
- const results = result.results || [];
316
- if (result.meta) {
317
- this.logger.debug("Query metadata", { meta: result.meta });
318
- }
319
- return results;
320
- }
321
- }
322
- } catch (workerError) {
323
- this.logger.error("Workers Binding API error", {
324
- message: workerError instanceof Error ? workerError.message : String(workerError),
325
- sql
326
- });
327
- throw new Error(`D1 Workers API error: ${workerError.message}`);
328
- }
242
+ function deserializeValue(value, type) {
243
+ if (value === null || value === void 0) return null;
244
+ if (type === "date" && typeof value === "string") {
245
+ return new Date(value);
329
246
  }
330
- async executeRestQuery({
331
- sql,
332
- params = [],
333
- first = false
334
- }) {
335
- if (!this.client || !this.accountId || !this.databaseId) {
336
- throw new Error("Missing required REST API configuration");
337
- }
338
- try {
339
- const response = await this.client.d1.database.query(this.databaseId, {
340
- account_id: this.accountId,
341
- sql,
342
- params: this.formatSqlParams(params)
343
- });
344
- const result = response.result || [];
345
- const results = result.flatMap((r) => r.results || []);
346
- if (first) {
347
- const firstResult = isArrayOfRecords(results) && results.length > 0 ? results[0] : null;
348
- if (!firstResult) return null;
349
- return firstResult;
350
- }
351
- return results;
352
- } catch (restError) {
353
- this.logger.error("REST API error", {
354
- message: restError instanceof Error ? restError.message : String(restError),
355
- sql
356
- });
357
- throw new Error(`D1 REST API error: ${restError.message}`);
358
- }
359
- }
360
- /**
361
- * Execute a SQL query against the D1 database
362
- * @param options Query options including SQL, parameters, and whether to return only the first result
363
- * @returns Query results as an array or a single object if first=true
364
- */
365
- async executeQuery(options) {
366
- const { sql, params = [], first = false } = options;
247
+ if (type === "jsonb" && typeof value === "string") {
367
248
  try {
368
- this.logger.debug("Executing SQL query", { sql, params, first });
369
- if (this.binding) {
370
- return this.executeWorkersBindingQuery({ sql, params, first });
371
- } else if (this.client && this.accountId && this.databaseId) {
372
- return this.executeRestQuery({ sql, params, first });
373
- } else {
374
- throw new Error("No valid D1 configuration provided");
375
- }
376
- } catch (error) {
377
- this.logger.error("Error executing SQL query", {
378
- message: error instanceof Error ? error.message : String(error),
379
- sql,
380
- params,
381
- first
382
- });
383
- throw new Error(`D1 query error: ${error.message}`);
249
+ return JSON.parse(value);
250
+ } catch {
251
+ return value;
384
252
  }
385
253
  }
386
- // Helper to get existing table columns
387
- async getTableColumns(tableName) {
254
+ if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) {
388
255
  try {
389
- const sql = `PRAGMA table_info(${tableName})`;
390
- const result = await this.executeQuery({ sql, params: [] });
391
- if (!result || !Array.isArray(result)) {
392
- return [];
393
- }
394
- return result.map((row) => ({
395
- name: row.name,
396
- type: row.type
397
- }));
398
- } catch (error) {
399
- this.logger.error(`Error getting table columns for ${tableName}:`, {
400
- message: error instanceof Error ? error.message : String(error)
401
- });
402
- return [];
256
+ return JSON.parse(value);
257
+ } catch {
258
+ return value;
403
259
  }
404
260
  }
405
- // Helper to serialize objects to JSON strings
406
- serializeValue(value) {
407
- if (value === null || value === void 0) return null;
408
- if (value instanceof Date) {
409
- return this.serializeDate(value);
410
- }
411
- if (typeof value === "object") {
412
- return JSON.stringify(value);
413
- }
414
- return value;
261
+ return value;
262
+ }
263
+
264
+ // src/storage/domains/legacy-evals/index.ts
265
+ var LegacyEvalsStorageD1 = class extends LegacyEvalsStorage {
266
+ operations;
267
+ constructor({ operations }) {
268
+ super();
269
+ this.operations = operations;
415
270
  }
416
- // Helper to deserialize JSON strings to objects
417
- deserializeValue(value, type) {
418
- if (value === null || value === void 0) return null;
419
- if (type === "date" && typeof value === "string") {
420
- return new Date(value);
271
+ async getEvals(options) {
272
+ const { agentName, type, page = 0, perPage = 40, dateRange } = options || {};
273
+ const fullTableName = this.operations.getTableName(TABLE_EVALS);
274
+ const conditions = [];
275
+ const queryParams = [];
276
+ if (agentName) {
277
+ conditions.push(`agent_name = ?`);
278
+ queryParams.push(agentName);
421
279
  }
422
- if (type === "jsonb" && typeof value === "string") {
423
- try {
424
- return JSON.parse(value);
425
- } catch {
426
- return value;
427
- }
280
+ if (type === "test") {
281
+ conditions.push(`(test_info IS NOT NULL AND json_extract(test_info, '$.testPath') IS NOT NULL)`);
282
+ } else if (type === "live") {
283
+ conditions.push(`(test_info IS NULL OR json_extract(test_info, '$.testPath') IS NULL)`);
428
284
  }
429
- if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) {
430
- try {
431
- return JSON.parse(value);
432
- } catch {
433
- return value;
434
- }
285
+ if (dateRange?.start) {
286
+ conditions.push(`created_at >= ?`);
287
+ queryParams.push(serializeDate(dateRange.start));
435
288
  }
436
- return value;
437
- }
438
- getSqlType(type) {
439
- switch (type) {
440
- case "bigint":
441
- return "INTEGER";
442
- // SQLite uses INTEGER for all integer sizes
443
- case "jsonb":
444
- return "TEXT";
445
- // Store JSON as TEXT in SQLite
446
- default:
447
- return super.getSqlType(type);
289
+ if (dateRange?.end) {
290
+ conditions.push(`created_at <= ?`);
291
+ queryParams.push(serializeDate(dateRange.end));
448
292
  }
449
- }
450
- async createTable({
451
- tableName,
452
- schema
453
- }) {
454
- const fullTableName = this.getTableName(tableName);
455
- const columnDefinitions = Object.entries(schema).map(([colName, colDef]) => {
456
- const type = this.getSqlType(colDef.type);
457
- const nullable = colDef.nullable === false ? "NOT NULL" : "";
458
- const primaryKey = colDef.primaryKey ? "PRIMARY KEY" : "";
459
- return `${colName} ${type} ${nullable} ${primaryKey}`.trim();
460
- });
461
- const tableConstraints = [];
462
- if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
463
- tableConstraints.push("UNIQUE (workflow_name, run_id)");
293
+ const countQueryBuilder = createSqlBuilder().count().from(fullTableName);
294
+ if (conditions.length > 0) {
295
+ countQueryBuilder.where(conditions.join(" AND "), ...queryParams);
464
296
  }
465
- const query = createSqlBuilder().createTable(fullTableName, columnDefinitions, tableConstraints);
466
- const { sql, params } = query.build();
297
+ const { sql: countSql, params: countParams } = countQueryBuilder.build();
467
298
  try {
468
- await this.executeQuery({ sql, params });
469
- this.logger.debug(`Created table ${fullTableName}`);
470
- } catch (error) {
471
- this.logger.error(`Error creating table ${fullTableName}:`, {
472
- message: error instanceof Error ? error.message : String(error)
299
+ const countResult = await this.operations.executeQuery({
300
+ sql: countSql,
301
+ params: countParams,
302
+ first: true
303
+ });
304
+ const total = Number(countResult?.count || 0);
305
+ const currentOffset = page * perPage;
306
+ if (total === 0) {
307
+ return {
308
+ evals: [],
309
+ total: 0,
310
+ page,
311
+ perPage,
312
+ hasMore: false
313
+ };
314
+ }
315
+ const dataQueryBuilder = createSqlBuilder().select("*").from(fullTableName);
316
+ if (conditions.length > 0) {
317
+ dataQueryBuilder.where(conditions.join(" AND "), ...queryParams);
318
+ }
319
+ dataQueryBuilder.orderBy("created_at", "DESC").limit(perPage).offset(currentOffset);
320
+ const { sql: dataSql, params: dataParams } = dataQueryBuilder.build();
321
+ const rows = await this.operations.executeQuery({
322
+ sql: dataSql,
323
+ params: dataParams
324
+ });
325
+ const evals = (isArrayOfRecords(rows) ? rows : []).map((row) => {
326
+ const result = deserializeValue(row.result);
327
+ const testInfo = row.test_info ? deserializeValue(row.test_info) : void 0;
328
+ if (!result || typeof result !== "object" || !("score" in result)) {
329
+ throw new Error(`Invalid MetricResult format: ${JSON.stringify(result)}`);
330
+ }
331
+ return {
332
+ input: row.input,
333
+ output: row.output,
334
+ result,
335
+ agentName: row.agent_name,
336
+ metricName: row.metric_name,
337
+ instructions: row.instructions,
338
+ testInfo,
339
+ globalRunId: row.global_run_id,
340
+ runId: row.run_id,
341
+ createdAt: row.created_at
342
+ };
473
343
  });
474
- throw new Error(`Failed to create table ${fullTableName}: ${error}`);
344
+ const hasMore = currentOffset + evals.length < total;
345
+ return {
346
+ evals,
347
+ total,
348
+ page,
349
+ perPage,
350
+ hasMore
351
+ };
352
+ } catch (error) {
353
+ throw new MastraError(
354
+ {
355
+ id: "CLOUDFLARE_D1_STORAGE_GET_EVALS_ERROR",
356
+ domain: ErrorDomain.STORAGE,
357
+ category: ErrorCategory.THIRD_PARTY,
358
+ text: `Failed to retrieve evals for agent ${agentName}: ${error instanceof Error ? error.message : String(error)}`,
359
+ details: { agentName: agentName ?? "", type: type ?? "" }
360
+ },
361
+ error
362
+ );
475
363
  }
476
364
  }
477
365
  /**
478
- * Alters table schema to add columns if they don't exist
479
- * @param tableName Name of the table
480
- * @param schema Schema of the table
481
- * @param ifNotExists Array of column names to add if they don't exist
366
+ * @deprecated use getEvals instead
482
367
  */
483
- async alterTable({
484
- tableName,
485
- schema,
486
- ifNotExists
487
- }) {
488
- const fullTableName = this.getTableName(tableName);
368
+ async getEvalsByAgentName(agentName, type) {
369
+ const fullTableName = this.operations.getTableName(TABLE_EVALS);
489
370
  try {
490
- const existingColumns = await this.getTableColumns(fullTableName);
491
- const existingColumnNames = new Set(existingColumns.map((col) => col.name.toLowerCase()));
492
- for (const columnName of ifNotExists) {
493
- if (!existingColumnNames.has(columnName.toLowerCase()) && schema[columnName]) {
494
- const columnDef = schema[columnName];
495
- const sqlType = this.getSqlType(columnDef.type);
496
- const nullable = columnDef.nullable === false ? "NOT NULL" : "";
497
- const defaultValue = columnDef.nullable === false ? this.getDefaultValue(columnDef.type) : "";
498
- const alterSql = `ALTER TABLE ${fullTableName} ADD COLUMN ${columnName} ${sqlType} ${nullable} ${defaultValue}`.trim();
499
- await this.executeQuery({ sql: alterSql, params: [] });
500
- this.logger.debug(`Added column ${columnName} to table ${fullTableName}`);
501
- }
371
+ let query = createSqlBuilder().select("*").from(fullTableName).where("agent_name = ?", agentName);
372
+ if (type === "test") {
373
+ query = query.andWhere("test_info IS NOT NULL AND json_extract(test_info, '$.testPath') IS NOT NULL");
374
+ } else if (type === "live") {
375
+ query = query.andWhere("(test_info IS NULL OR json_extract(test_info, '$.testPath') IS NULL)");
502
376
  }
377
+ query.orderBy("created_at", "DESC");
378
+ const { sql, params } = query.build();
379
+ const results = await this.operations.executeQuery({ sql, params });
380
+ return isArrayOfRecords(results) ? results.map((row) => {
381
+ const result = deserializeValue(row.result);
382
+ const testInfo = row.test_info ? deserializeValue(row.test_info) : void 0;
383
+ return {
384
+ input: row.input || "",
385
+ output: row.output || "",
386
+ result,
387
+ agentName: row.agent_name || "",
388
+ metricName: row.metric_name || "",
389
+ instructions: row.instructions || "",
390
+ runId: row.run_id || "",
391
+ globalRunId: row.global_run_id || "",
392
+ createdAt: row.created_at || "",
393
+ testInfo
394
+ };
395
+ }) : [];
503
396
  } catch (error) {
504
- this.logger.error(`Error altering table ${fullTableName}:`, {
505
- message: error instanceof Error ? error.message : String(error)
506
- });
507
- throw new Error(`Failed to alter table ${fullTableName}: ${error}`);
397
+ const mastraError = new MastraError(
398
+ {
399
+ id: "CLOUDFLARE_D1_STORAGE_GET_EVALS_ERROR",
400
+ domain: ErrorDomain.STORAGE,
401
+ category: ErrorCategory.THIRD_PARTY,
402
+ text: `Failed to retrieve evals for agent ${agentName}: ${error instanceof Error ? error.message : String(error)}`,
403
+ details: { agentName }
404
+ },
405
+ error
406
+ );
407
+ this.logger?.error(mastraError.toString());
408
+ this.logger?.trackException(mastraError);
409
+ return [];
508
410
  }
509
411
  }
510
- async clearTable({ tableName }) {
511
- const fullTableName = this.getTableName(tableName);
412
+ };
413
+ var MemoryStorageD1 = class extends MemoryStorage {
414
+ operations;
415
+ constructor({ operations }) {
416
+ super();
417
+ this.operations = operations;
418
+ }
419
+ async getResourceById({ resourceId }) {
420
+ const resource = await this.operations.load({
421
+ tableName: TABLE_RESOURCES,
422
+ keys: { id: resourceId }
423
+ });
424
+ if (!resource) return null;
512
425
  try {
513
- const query = createSqlBuilder().delete(fullTableName);
514
- const { sql, params } = query.build();
515
- await this.executeQuery({ sql, params });
516
- this.logger.debug(`Cleared table ${fullTableName}`);
426
+ return {
427
+ ...resource,
428
+ createdAt: ensureDate(resource.createdAt),
429
+ updatedAt: ensureDate(resource.updatedAt),
430
+ metadata: typeof resource.metadata === "string" ? JSON.parse(resource.metadata || "{}") : resource.metadata
431
+ };
517
432
  } catch (error) {
518
- this.logger.error(`Error clearing table ${fullTableName}:`, {
519
- message: error instanceof Error ? error.message : String(error)
520
- });
521
- throw new Error(`Failed to clear table ${fullTableName}: ${error}`);
522
- }
523
- }
524
- async processRecord(record) {
525
- const processedRecord = {};
526
- for (const [key, value] of Object.entries(record)) {
527
- processedRecord[key] = this.serializeValue(value);
433
+ const mastraError = new MastraError(
434
+ {
435
+ id: "CLOUDFLARE_D1_STORAGE_GET_RESOURCE_BY_ID_ERROR",
436
+ domain: ErrorDomain.STORAGE,
437
+ category: ErrorCategory.THIRD_PARTY,
438
+ text: `Error processing resource ${resourceId}: ${error instanceof Error ? error.message : String(error)}`,
439
+ details: { resourceId }
440
+ },
441
+ error
442
+ );
443
+ this.logger?.error(mastraError.toString());
444
+ this.logger?.trackException(mastraError);
445
+ return null;
528
446
  }
529
- return processedRecord;
530
447
  }
531
- async insert({ tableName, record }) {
532
- const fullTableName = this.getTableName(tableName);
533
- const processedRecord = await this.processRecord(record);
448
+ async saveResource({ resource }) {
449
+ const fullTableName = this.operations.getTableName(TABLE_RESOURCES);
450
+ const resourceToSave = {
451
+ id: resource.id,
452
+ workingMemory: resource.workingMemory,
453
+ metadata: resource.metadata ? JSON.stringify(resource.metadata) : null,
454
+ createdAt: resource.createdAt,
455
+ updatedAt: resource.updatedAt
456
+ };
457
+ const processedRecord = await this.operations.processRecord(resourceToSave);
534
458
  const columns = Object.keys(processedRecord);
535
459
  const values = Object.values(processedRecord);
536
- const query = createSqlBuilder().insert(fullTableName, columns, values);
460
+ const updateMap = {
461
+ workingMemory: "excluded.workingMemory",
462
+ metadata: "excluded.metadata",
463
+ createdAt: "excluded.createdAt",
464
+ updatedAt: "excluded.updatedAt"
465
+ };
466
+ const query = createSqlBuilder().insert(fullTableName, columns, values, ["id"], updateMap);
537
467
  const { sql, params } = query.build();
538
468
  try {
539
- await this.executeQuery({ sql, params });
469
+ await this.operations.executeQuery({ sql, params });
470
+ return resource;
540
471
  } catch (error) {
541
- const message = error instanceof Error ? error.message : String(error);
542
- this.logger.error(`Error inserting into ${fullTableName}:`, { message });
543
- throw new Error(`Failed to insert into ${fullTableName}: ${error}`);
472
+ throw new MastraError(
473
+ {
474
+ id: "CLOUDFLARE_D1_STORAGE_SAVE_RESOURCE_ERROR",
475
+ domain: ErrorDomain.STORAGE,
476
+ category: ErrorCategory.THIRD_PARTY,
477
+ text: `Failed to save resource to ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
478
+ details: { resourceId: resource.id }
479
+ },
480
+ error
481
+ );
544
482
  }
545
483
  }
546
- async load({ tableName, keys }) {
547
- const fullTableName = this.getTableName(tableName);
548
- const query = createSqlBuilder().select("*").from(fullTableName);
549
- let firstKey = true;
550
- for (const [key, value] of Object.entries(keys)) {
551
- if (firstKey) {
552
- query.where(`${key} = ?`, value);
553
- firstKey = false;
554
- } else {
555
- query.andWhere(`${key} = ?`, value);
556
- }
484
+ async updateResource({
485
+ resourceId,
486
+ workingMemory,
487
+ metadata
488
+ }) {
489
+ const existingResource = await this.getResourceById({ resourceId });
490
+ if (!existingResource) {
491
+ const newResource = {
492
+ id: resourceId,
493
+ workingMemory,
494
+ metadata: metadata || {},
495
+ createdAt: /* @__PURE__ */ new Date(),
496
+ updatedAt: /* @__PURE__ */ new Date()
497
+ };
498
+ return this.saveResource({ resource: newResource });
557
499
  }
558
- query.limit(1);
500
+ const updatedAt = /* @__PURE__ */ new Date();
501
+ const updatedResource = {
502
+ ...existingResource,
503
+ workingMemory: workingMemory !== void 0 ? workingMemory : existingResource.workingMemory,
504
+ metadata: {
505
+ ...existingResource.metadata,
506
+ ...metadata
507
+ },
508
+ updatedAt
509
+ };
510
+ const fullTableName = this.operations.getTableName(TABLE_RESOURCES);
511
+ const columns = ["workingMemory", "metadata", "updatedAt"];
512
+ const values = [updatedResource.workingMemory, JSON.stringify(updatedResource.metadata), updatedAt.toISOString()];
513
+ const query = createSqlBuilder().update(fullTableName, columns, values).where("id = ?", resourceId);
559
514
  const { sql, params } = query.build();
560
515
  try {
561
- const result = await this.executeQuery({ sql, params, first: true });
562
- if (!result) return null;
563
- const processedResult = {};
564
- for (const [key, value] of Object.entries(result)) {
565
- processedResult[key] = this.deserializeValue(value);
566
- }
567
- return processedResult;
516
+ await this.operations.executeQuery({ sql, params });
517
+ return updatedResource;
568
518
  } catch (error) {
569
- this.logger.error(`Error loading from ${fullTableName}:`, {
570
- message: error instanceof Error ? error.message : String(error)
571
- });
572
- return null;
519
+ throw new MastraError(
520
+ {
521
+ id: "CLOUDFLARE_D1_STORAGE_UPDATE_RESOURCE_ERROR",
522
+ domain: ErrorDomain.STORAGE,
523
+ category: ErrorCategory.THIRD_PARTY,
524
+ text: `Failed to update resource ${resourceId}: ${error instanceof Error ? error.message : String(error)}`,
525
+ details: { resourceId }
526
+ },
527
+ error
528
+ );
573
529
  }
574
530
  }
575
531
  async getThreadById({ threadId }) {
576
- const thread = await this.load({
532
+ const thread = await this.operations.load({
577
533
  tableName: TABLE_THREADS,
578
534
  keys: { id: threadId }
579
535
  });
580
536
  if (!thread) return null;
537
+ console.log("thread", thread);
581
538
  try {
582
539
  return {
583
540
  ...thread,
584
- createdAt: this.ensureDate(thread.createdAt),
585
- updatedAt: this.ensureDate(thread.updatedAt),
541
+ createdAt: ensureDate(thread.createdAt),
542
+ updatedAt: ensureDate(thread.updatedAt),
586
543
  metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata || "{}") : thread.metadata || {}
587
544
  };
588
545
  } catch (error) {
589
- this.logger.error(`Error processing thread ${threadId}:`, {
590
- message: error instanceof Error ? error.message : String(error)
591
- });
546
+ const mastraError = new MastraError(
547
+ {
548
+ id: "CLOUDFLARE_D1_STORAGE_GET_THREAD_BY_ID_ERROR",
549
+ domain: ErrorDomain.STORAGE,
550
+ category: ErrorCategory.THIRD_PARTY,
551
+ text: `Error processing thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`,
552
+ details: { threadId }
553
+ },
554
+ error
555
+ );
556
+ this.logger?.error(mastraError.toString());
557
+ this.logger?.trackException(mastraError);
592
558
  return null;
593
559
  }
594
560
  }
@@ -596,58 +562,89 @@ var D1Store = class extends MastraStorage {
596
562
  * @deprecated use getThreadsByResourceIdPaginated instead
597
563
  */
598
564
  async getThreadsByResourceId({ resourceId }) {
599
- const fullTableName = this.getTableName(TABLE_THREADS);
565
+ const fullTableName = this.operations.getTableName(TABLE_THREADS);
600
566
  try {
601
567
  const query = createSqlBuilder().select("*").from(fullTableName).where("resourceId = ?", resourceId);
602
568
  const { sql, params } = query.build();
603
- const results = await this.executeQuery({ sql, params });
569
+ const results = await this.operations.executeQuery({ sql, params });
604
570
  return (isArrayOfRecords(results) ? results : []).map((thread) => ({
605
571
  ...thread,
606
- createdAt: this.ensureDate(thread.createdAt),
607
- updatedAt: this.ensureDate(thread.updatedAt),
572
+ createdAt: ensureDate(thread.createdAt),
573
+ updatedAt: ensureDate(thread.updatedAt),
608
574
  metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata || "{}") : thread.metadata || {}
609
575
  }));
610
576
  } catch (error) {
611
- this.logger.error(`Error getting threads by resourceId ${resourceId}:`, {
612
- message: error instanceof Error ? error.message : String(error)
613
- });
577
+ const mastraError = new MastraError(
578
+ {
579
+ id: "CLOUDFLARE_D1_STORAGE_GET_THREADS_BY_RESOURCE_ID_ERROR",
580
+ domain: ErrorDomain.STORAGE,
581
+ category: ErrorCategory.THIRD_PARTY,
582
+ text: `Error getting threads by resourceId ${resourceId}: ${error instanceof Error ? error.message : String(error)}`,
583
+ details: { resourceId }
584
+ },
585
+ error
586
+ );
587
+ this.logger?.error(mastraError.toString());
588
+ this.logger?.trackException(mastraError);
614
589
  return [];
615
590
  }
616
591
  }
617
592
  async getThreadsByResourceIdPaginated(args) {
618
593
  const { resourceId, page, perPage } = args;
619
- const fullTableName = this.getTableName(TABLE_THREADS);
594
+ const fullTableName = this.operations.getTableName(TABLE_THREADS);
620
595
  const mapRowToStorageThreadType = (row) => ({
621
596
  ...row,
622
- createdAt: this.ensureDate(row.createdAt),
623
- updatedAt: this.ensureDate(row.updatedAt),
597
+ createdAt: ensureDate(row.createdAt),
598
+ updatedAt: ensureDate(row.updatedAt),
624
599
  metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata || "{}") : row.metadata || {}
625
600
  });
626
- const countQuery = createSqlBuilder().count().from(fullTableName).where("resourceId = ?", resourceId);
627
- const countResult = await this.executeQuery(countQuery.build());
628
- const total = Number(countResult?.[0]?.count ?? 0);
629
- const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("resourceId = ?", resourceId).orderBy("createdAt", "DESC").limit(perPage).offset(page * perPage);
630
- const results = await this.executeQuery(selectQuery.build());
631
- const threads = results.map(mapRowToStorageThreadType);
632
- return {
633
- threads,
634
- total,
635
- page,
636
- perPage,
637
- hasMore: page * perPage + threads.length < total
638
- };
601
+ try {
602
+ const countQuery = createSqlBuilder().count().from(fullTableName).where("resourceId = ?", resourceId);
603
+ const countResult = await this.operations.executeQuery(countQuery.build());
604
+ const total = Number(countResult?.[0]?.count ?? 0);
605
+ const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("resourceId = ?", resourceId).orderBy("createdAt", "DESC").limit(perPage).offset(page * perPage);
606
+ const results = await this.operations.executeQuery(selectQuery.build());
607
+ const threads = results.map(mapRowToStorageThreadType);
608
+ return {
609
+ threads,
610
+ total,
611
+ page,
612
+ perPage,
613
+ hasMore: page * perPage + threads.length < total
614
+ };
615
+ } catch (error) {
616
+ const mastraError = new MastraError(
617
+ {
618
+ id: "CLOUDFLARE_D1_STORAGE_GET_THREADS_BY_RESOURCE_ID_PAGINATED_ERROR",
619
+ domain: ErrorDomain.STORAGE,
620
+ category: ErrorCategory.THIRD_PARTY,
621
+ text: `Error getting threads by resourceId ${resourceId}: ${error instanceof Error ? error.message : String(error)}`,
622
+ details: { resourceId }
623
+ },
624
+ error
625
+ );
626
+ this.logger?.error(mastraError.toString());
627
+ this.logger?.trackException(mastraError);
628
+ return {
629
+ threads: [],
630
+ total: 0,
631
+ page,
632
+ perPage,
633
+ hasMore: false
634
+ };
635
+ }
639
636
  }
640
637
  async saveThread({ thread }) {
641
- const fullTableName = this.getTableName(TABLE_THREADS);
638
+ const fullTableName = this.operations.getTableName(TABLE_THREADS);
642
639
  const threadToSave = {
643
640
  id: thread.id,
644
641
  resourceId: thread.resourceId,
645
642
  title: thread.title,
646
643
  metadata: thread.metadata ? JSON.stringify(thread.metadata) : null,
647
- createdAt: thread.createdAt,
648
- updatedAt: thread.updatedAt
644
+ createdAt: thread.createdAt.toISOString(),
645
+ updatedAt: thread.updatedAt.toISOString()
649
646
  };
650
- const processedRecord = await this.processRecord(threadToSave);
647
+ const processedRecord = await this.operations.processRecord(threadToSave);
651
648
  const columns = Object.keys(processedRecord);
652
649
  const values = Object.values(processedRecord);
653
650
  const updateMap = {
@@ -660,12 +657,19 @@ var D1Store = class extends MastraStorage {
660
657
  const query = createSqlBuilder().insert(fullTableName, columns, values, ["id"], updateMap);
661
658
  const { sql, params } = query.build();
662
659
  try {
663
- await this.executeQuery({ sql, params });
660
+ await this.operations.executeQuery({ sql, params });
664
661
  return thread;
665
662
  } catch (error) {
666
- const message = error instanceof Error ? error.message : String(error);
667
- this.logger.error(`Error saving thread to ${fullTableName}:`, { message });
668
- throw error;
663
+ throw new MastraError(
664
+ {
665
+ id: "CLOUDFLARE_D1_STORAGE_SAVE_THREAD_ERROR",
666
+ domain: ErrorDomain.STORAGE,
667
+ category: ErrorCategory.THIRD_PARTY,
668
+ text: `Failed to save thread to ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
669
+ details: { threadId: thread.id }
670
+ },
671
+ error
672
+ );
669
673
  }
670
674
  }
671
675
  async updateThread({
@@ -674,20 +678,21 @@ var D1Store = class extends MastraStorage {
674
678
  metadata
675
679
  }) {
676
680
  const thread = await this.getThreadById({ threadId: id });
677
- if (!thread) {
678
- throw new Error(`Thread ${id} not found`);
679
- }
680
- const fullTableName = this.getTableName(TABLE_THREADS);
681
- const mergedMetadata = {
682
- ...typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
683
- ...metadata
684
- };
685
- const columns = ["title", "metadata", "updatedAt"];
686
- const values = [title, JSON.stringify(mergedMetadata), (/* @__PURE__ */ new Date()).toISOString()];
687
- const query = createSqlBuilder().update(fullTableName, columns, values).where("id = ?", id);
688
- const { sql, params } = query.build();
689
681
  try {
690
- await this.executeQuery({ sql, params });
682
+ if (!thread) {
683
+ throw new Error(`Thread ${id} not found`);
684
+ }
685
+ const fullTableName = this.operations.getTableName(TABLE_THREADS);
686
+ const mergedMetadata = {
687
+ ...typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
688
+ ...metadata
689
+ };
690
+ const updatedAt = /* @__PURE__ */ new Date();
691
+ const columns = ["title", "metadata", "updatedAt"];
692
+ const values = [title, JSON.stringify(mergedMetadata), updatedAt.toISOString()];
693
+ const query = createSqlBuilder().update(fullTableName, columns, values).where("id = ?", id);
694
+ const { sql, params } = query.build();
695
+ await this.operations.executeQuery({ sql, params });
691
696
  return {
692
697
  ...thread,
693
698
  title,
@@ -695,29 +700,42 @@ var D1Store = class extends MastraStorage {
695
700
  ...typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
696
701
  ...metadata
697
702
  },
698
- updatedAt: /* @__PURE__ */ new Date()
703
+ updatedAt
699
704
  };
700
705
  } catch (error) {
701
- const message = error instanceof Error ? error.message : String(error);
702
- this.logger.error("Error updating thread:", { message });
703
- throw error;
706
+ throw new MastraError(
707
+ {
708
+ id: "CLOUDFLARE_D1_STORAGE_UPDATE_THREAD_ERROR",
709
+ domain: ErrorDomain.STORAGE,
710
+ category: ErrorCategory.THIRD_PARTY,
711
+ text: `Failed to update thread ${id}: ${error instanceof Error ? error.message : String(error)}`,
712
+ details: { threadId: id }
713
+ },
714
+ error
715
+ );
704
716
  }
705
717
  }
706
718
  async deleteThread({ threadId }) {
707
- const fullTableName = this.getTableName(TABLE_THREADS);
719
+ const fullTableName = this.operations.getTableName(TABLE_THREADS);
708
720
  try {
709
721
  const deleteThreadQuery = createSqlBuilder().delete(fullTableName).where("id = ?", threadId);
710
722
  const { sql: threadSql, params: threadParams } = deleteThreadQuery.build();
711
- await this.executeQuery({ sql: threadSql, params: threadParams });
712
- const messagesTableName = this.getTableName(TABLE_MESSAGES);
723
+ await this.operations.executeQuery({ sql: threadSql, params: threadParams });
724
+ const messagesTableName = this.operations.getTableName(TABLE_MESSAGES);
713
725
  const deleteMessagesQuery = createSqlBuilder().delete(messagesTableName).where("thread_id = ?", threadId);
714
726
  const { sql: messagesSql, params: messagesParams } = deleteMessagesQuery.build();
715
- await this.executeQuery({ sql: messagesSql, params: messagesParams });
727
+ await this.operations.executeQuery({ sql: messagesSql, params: messagesParams });
716
728
  } catch (error) {
717
- this.logger.error(`Error deleting thread ${threadId}:`, {
718
- message: error instanceof Error ? error.message : String(error)
719
- });
720
- throw new Error(`Failed to delete thread ${threadId}: ${error}`);
729
+ throw new MastraError(
730
+ {
731
+ id: "CLOUDFLARE_D1_STORAGE_DELETE_THREAD_ERROR",
732
+ domain: ErrorDomain.STORAGE,
733
+ category: ErrorCategory.THIRD_PARTY,
734
+ text: `Failed to delete thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`,
735
+ details: { threadId }
736
+ },
737
+ error
738
+ );
721
739
  }
722
740
  }
723
741
  async saveMessages(args) {
@@ -728,9 +746,18 @@ var D1Store = class extends MastraStorage {
728
746
  const threadId = messages[0]?.threadId;
729
747
  for (const [i, message] of messages.entries()) {
730
748
  if (!message.id) throw new Error(`Message at index ${i} missing id`);
731
- if (!message.threadId) throw new Error(`Message at index ${i} missing threadId`);
732
- if (!message.content) throw new Error(`Message at index ${i} missing content`);
733
- if (!message.role) throw new Error(`Message at index ${i} missing role`);
749
+ if (!message.threadId) {
750
+ throw new Error(`Message at index ${i} missing threadId`);
751
+ }
752
+ if (!message.content) {
753
+ throw new Error(`Message at index ${i} missing content`);
754
+ }
755
+ if (!message.role) {
756
+ throw new Error(`Message at index ${i} missing role`);
757
+ }
758
+ if (!message.resourceId) {
759
+ throw new Error(`Message at index ${i} missing resourceId`);
760
+ }
734
761
  const thread = await this.getThreadById({ threadId: message.threadId });
735
762
  if (!thread) {
736
763
  throw new Error(`Thread ${message.threadId} not found`);
@@ -749,13 +776,13 @@ var D1Store = class extends MastraStorage {
749
776
  };
750
777
  });
751
778
  await Promise.all([
752
- this.batchInsert({
779
+ this.operations.batchUpsert({
753
780
  tableName: TABLE_MESSAGES,
754
781
  records: messagesToInsert
755
782
  }),
756
783
  // Update thread's updatedAt timestamp
757
- this.executeQuery({
758
- sql: `UPDATE ${this.getTableName(TABLE_THREADS)} SET updatedAt = ? WHERE id = ?`,
784
+ this.operations.executeQuery({
785
+ sql: `UPDATE ${this.operations.getTableName(TABLE_THREADS)} SET updatedAt = ? WHERE id = ?`,
759
786
  params: [now.toISOString(), threadId]
760
787
  })
761
788
  ]);
@@ -764,63 +791,84 @@ var D1Store = class extends MastraStorage {
764
791
  if (format === `v2`) return list.get.all.v2();
765
792
  return list.get.all.v1();
766
793
  } catch (error) {
767
- this.logger.error("Error saving messages:", { message: error instanceof Error ? error.message : String(error) });
768
- throw error;
794
+ throw new MastraError(
795
+ {
796
+ id: "CLOUDFLARE_D1_STORAGE_SAVE_MESSAGES_ERROR",
797
+ domain: ErrorDomain.STORAGE,
798
+ category: ErrorCategory.THIRD_PARTY,
799
+ text: `Failed to save messages: ${error instanceof Error ? error.message : String(error)}`
800
+ },
801
+ error
802
+ );
769
803
  }
770
804
  }
771
805
  async _getIncludedMessages(threadId, selectBy) {
772
806
  const include = selectBy?.include;
773
807
  if (!include) return null;
774
- const prevMax = Math.max(...include.map((i) => i.withPreviousMessages || 0));
775
- const nextMax = Math.max(...include.map((i) => i.withNextMessages || 0));
776
- const includeIds = include.map((i) => i.id);
777
- const sql = `
778
- WITH ordered_messages AS (
779
- SELECT
780
- *,
781
- ROW_NUMBER() OVER (ORDER BY createdAt DESC) AS row_num
782
- FROM ${this.getTableName(TABLE_MESSAGES)}
783
- WHERE thread_id = ?
784
- )
785
- SELECT
786
- m.id,
787
- m.content,
788
- m.role,
789
- m.type,
790
- m.createdAt,
791
- m.thread_id AS threadId
792
- FROM ordered_messages m
793
- WHERE m.id IN (${includeIds.map(() => "?").join(",")})
794
- OR EXISTS (
795
- SELECT 1 FROM ordered_messages target
796
- WHERE target.id IN (${includeIds.map(() => "?").join(",")})
797
- AND (
798
- (m.row_num <= target.row_num + ? AND m.row_num > target.row_num)
799
- OR
800
- (m.row_num >= target.row_num - ? AND m.row_num < target.row_num)
801
- )
802
- )
803
- ORDER BY m.createdAt DESC
804
- `;
805
- const params = [
806
- threadId,
807
- ...includeIds,
808
- // for m.id IN (...)
809
- ...includeIds,
810
- // for target.id IN (...)
811
- prevMax,
812
- nextMax
813
- ];
814
- const messages = await this.executeQuery({ sql, params });
815
- return messages;
808
+ const unionQueries = [];
809
+ const params = [];
810
+ let paramIdx = 1;
811
+ for (const inc of include) {
812
+ const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
813
+ const searchId = inc.threadId || threadId;
814
+ unionQueries.push(`
815
+ SELECT * FROM (
816
+ WITH ordered_messages AS (
817
+ SELECT
818
+ *,
819
+ ROW_NUMBER() OVER (ORDER BY createdAt ASC) AS row_num
820
+ FROM ${this.operations.getTableName(TABLE_MESSAGES)}
821
+ WHERE thread_id = ?
822
+ )
823
+ SELECT
824
+ m.id,
825
+ m.content,
826
+ m.role,
827
+ m.type,
828
+ m.createdAt,
829
+ m.thread_id AS threadId,
830
+ m.resourceId
831
+ FROM ordered_messages m
832
+ WHERE m.id = ?
833
+ OR EXISTS (
834
+ SELECT 1 FROM ordered_messages target
835
+ WHERE target.id = ?
836
+ AND (
837
+ (m.row_num <= target.row_num + ? AND m.row_num > target.row_num)
838
+ OR
839
+ (m.row_num >= target.row_num - ? AND m.row_num < target.row_num)
840
+ )
841
+ )
842
+ ) AS query_${paramIdx}
843
+ `);
844
+ params.push(searchId, id, id, withNextMessages, withPreviousMessages);
845
+ paramIdx++;
846
+ }
847
+ const finalQuery = unionQueries.join(" UNION ALL ") + " ORDER BY createdAt ASC";
848
+ const messages = await this.operations.executeQuery({ sql: finalQuery, params });
849
+ if (!Array.isArray(messages)) {
850
+ return [];
851
+ }
852
+ const processedMessages = messages.map((message) => {
853
+ const processedMsg = {};
854
+ for (const [key, value] of Object.entries(message)) {
855
+ if (key === `type` && value === `v2`) continue;
856
+ processedMsg[key] = deserializeValue(value);
857
+ }
858
+ return processedMsg;
859
+ });
860
+ return processedMessages;
816
861
  }
817
862
  async getMessages({
818
863
  threadId,
819
864
  selectBy,
820
865
  format
821
866
  }) {
822
- const fullTableName = this.getTableName(TABLE_MESSAGES);
823
- const limit = this.resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
867
+ const fullTableName = this.operations.getTableName(TABLE_MESSAGES);
868
+ const limit = resolveMessageLimit({
869
+ last: selectBy?.last,
870
+ defaultLimit: 40
871
+ });
824
872
  const include = selectBy?.include || [];
825
873
  const messages = [];
826
874
  try {
@@ -835,7 +883,7 @@ var D1Store = class extends MastraStorage {
835
883
  }
836
884
  query.orderBy("createdAt", "DESC").limit(limit);
837
885
  const { sql, params } = query.build();
838
- const result = await this.executeQuery({ sql, params });
886
+ const result = await this.operations.executeQuery({ sql, params });
839
887
  if (Array.isArray(result)) messages.push(...result);
840
888
  messages.sort((a, b) => {
841
889
  const aRecord = a;
@@ -848,7 +896,7 @@ var D1Store = class extends MastraStorage {
848
896
  const processedMsg = {};
849
897
  for (const [key, value] of Object.entries(message)) {
850
898
  if (key === `type` && value === `v2`) continue;
851
- processedMsg[key] = this.deserializeValue(value);
899
+ processedMsg[key] = deserializeValue(value);
852
900
  }
853
901
  return processedMsg;
854
902
  });
@@ -857,11 +905,19 @@ var D1Store = class extends MastraStorage {
857
905
  if (format === `v2`) return list.get.all.v2();
858
906
  return list.get.all.v1();
859
907
  } catch (error) {
860
- this.logger.error("Error retrieving messages for thread", {
861
- threadId,
862
- message: error instanceof Error ? error.message : String(error)
863
- });
864
- return [];
908
+ const mastraError = new MastraError(
909
+ {
910
+ id: "CLOUDFLARE_D1_STORAGE_GET_MESSAGES_ERROR",
911
+ domain: ErrorDomain.STORAGE,
912
+ category: ErrorCategory.THIRD_PARTY,
913
+ text: `Failed to retrieve messages for thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`,
914
+ details: { threadId }
915
+ },
916
+ error
917
+ );
918
+ this.logger?.error(mastraError.toString());
919
+ this.logger?.trackException(mastraError);
920
+ throw mastraError;
865
921
  }
866
922
  }
867
923
  async getMessagesPaginated({
@@ -869,357 +925,1020 @@ var D1Store = class extends MastraStorage {
869
925
  selectBy,
870
926
  format
871
927
  }) {
872
- const { dateRange, page = 0, perPage = 40 } = selectBy?.pagination || {};
928
+ const { dateRange, page = 0, perPage: perPageInput } = selectBy?.pagination || {};
873
929
  const { start: fromDate, end: toDate } = dateRange || {};
874
- const fullTableName = this.getTableName(TABLE_MESSAGES);
930
+ const perPage = perPageInput !== void 0 ? perPageInput : resolveMessageLimit({ last: selectBy?.last, defaultLimit: 40 });
931
+ const fullTableName = this.operations.getTableName(TABLE_MESSAGES);
875
932
  const messages = [];
876
- if (selectBy?.include?.length) {
877
- const includeResult = await this._getIncludedMessages(threadId, selectBy);
878
- if (Array.isArray(includeResult)) messages.push(...includeResult);
879
- }
880
- const countQuery = createSqlBuilder().count().from(fullTableName).where("thread_id = ?", threadId);
881
- if (fromDate) {
882
- countQuery.andWhere("createdAt >= ?", this.serializeDate(fromDate));
883
- }
884
- if (toDate) {
885
- countQuery.andWhere("createdAt <= ?", this.serializeDate(toDate));
886
- }
887
- const countResult = await this.executeQuery(countQuery.build());
888
- const total = Number(countResult[0]?.count ?? 0);
889
- const query = createSqlBuilder().select(["id", "content", "role", "type", "createdAt", "thread_id AS threadId"]).from(fullTableName).where("thread_id = ?", threadId);
890
- if (fromDate) {
891
- query.andWhere("createdAt >= ?", this.serializeDate(fromDate));
892
- }
893
- if (toDate) {
894
- query.andWhere("createdAt <= ?", this.serializeDate(toDate));
895
- }
896
- query.orderBy("createdAt", "DESC").limit(perPage).offset(page * perPage);
897
- const results = await this.executeQuery(query.build());
898
- const list = new MessageList().add(results, "memory");
899
- messages.push(...format === `v2` ? list.get.all.v2() : list.get.all.v1());
900
- return {
901
- messages,
902
- total,
903
- page,
904
- perPage,
905
- hasMore: page * perPage + messages.length < total
906
- };
907
- }
908
- async persistWorkflowSnapshot({
909
- workflowName,
910
- runId,
911
- snapshot
912
- }) {
913
- const fullTableName = this.getTableName(TABLE_WORKFLOW_SNAPSHOT);
914
- const now = (/* @__PURE__ */ new Date()).toISOString();
915
- const currentSnapshot = await this.load({
916
- tableName: TABLE_WORKFLOW_SNAPSHOT,
917
- keys: { workflow_name: workflowName, run_id: runId }
918
- });
919
- const persisting = currentSnapshot ? {
920
- ...currentSnapshot,
921
- snapshot: JSON.stringify(snapshot),
922
- updatedAt: now
923
- } : {
924
- workflow_name: workflowName,
925
- run_id: runId,
926
- snapshot,
927
- createdAt: now,
928
- updatedAt: now
929
- };
930
- const processedRecord = await this.processRecord(persisting);
931
- const columns = Object.keys(processedRecord);
932
- const values = Object.values(processedRecord);
933
- const updateMap = {
934
- snapshot: "excluded.snapshot",
935
- updatedAt: "excluded.updatedAt"
936
- };
937
- this.logger.debug("Persisting workflow snapshot", { workflowName, runId });
938
- const query = createSqlBuilder().insert(fullTableName, columns, values, ["workflow_name", "run_id"], updateMap);
939
- const { sql, params } = query.build();
940
- try {
941
- await this.executeQuery({ sql, params });
942
- } catch (error) {
943
- this.logger.error("Error persisting workflow snapshot:", {
944
- message: error instanceof Error ? error.message : String(error)
945
- });
946
- throw error;
947
- }
948
- }
949
- async loadWorkflowSnapshot(params) {
950
- const { workflowName, runId } = params;
951
- this.logger.debug("Loading workflow snapshot", { workflowName, runId });
952
- const d = await this.load({
953
- tableName: TABLE_WORKFLOW_SNAPSHOT,
954
- keys: {
955
- workflow_name: workflowName,
956
- run_id: runId
957
- }
958
- });
959
- return d ? d.snapshot : null;
960
- }
961
- /**
962
- * Insert multiple records in a batch operation
963
- * @param tableName The table to insert into
964
- * @param records The records to insert
965
- */
966
- async batchInsert({ tableName, records }) {
967
- if (records.length === 0) return;
968
- const fullTableName = this.getTableName(tableName);
969
933
  try {
970
- const batchSize = 50;
971
- for (let i = 0; i < records.length; i += batchSize) {
972
- const batch = records.slice(i, i + batchSize);
973
- const recordsToInsert = batch;
974
- if (recordsToInsert.length > 0) {
975
- const firstRecord = recordsToInsert[0];
976
- const columns = Object.keys(firstRecord || {});
977
- for (const record of recordsToInsert) {
978
- const values = columns.map((col) => {
979
- if (!record) return null;
980
- const value = typeof col === "string" ? record[col] : null;
981
- return this.serializeValue(value);
982
- });
983
- const query = createSqlBuilder().insert(fullTableName, columns, values);
984
- const { sql, params } = query.build();
985
- await this.executeQuery({ sql, params });
986
- }
987
- }
988
- this.logger.debug(
989
- `Processed batch ${Math.floor(i / batchSize) + 1} of ${Math.ceil(records.length / batchSize)}`
990
- );
934
+ if (selectBy?.include?.length) {
935
+ const includeResult = await this._getIncludedMessages(threadId, selectBy);
936
+ if (Array.isArray(includeResult)) messages.push(...includeResult);
991
937
  }
992
- this.logger.debug(`Successfully batch inserted ${records.length} records into ${tableName}`);
993
- } catch (error) {
994
- this.logger.error(`Error batch inserting into ${tableName}:`, {
995
- message: error instanceof Error ? error.message : String(error)
996
- });
997
- throw new Error(`Failed to batch insert into ${tableName}: ${error}`);
998
- }
999
- }
1000
- /**
1001
- * @deprecated use getTracesPaginated instead
1002
- */
1003
- async getTraces({
1004
- name,
1005
- scope,
1006
- page,
1007
- perPage,
1008
- attributes,
1009
- fromDate,
1010
- toDate
1011
- }) {
1012
- const fullTableName = this.getTableName(TABLE_TRACES);
1013
- try {
1014
- const query = createSqlBuilder().select("*").from(fullTableName).where("1=1");
1015
- if (name) {
1016
- query.andWhere("name LIKE ?", `%${name}%`);
938
+ const countQuery = createSqlBuilder().count().from(fullTableName).where("thread_id = ?", threadId);
939
+ if (fromDate) {
940
+ countQuery.andWhere("createdAt >= ?", serializeDate(fromDate));
1017
941
  }
1018
- if (scope) {
1019
- query.andWhere("scope = ?", scope);
942
+ if (toDate) {
943
+ countQuery.andWhere("createdAt <= ?", serializeDate(toDate));
1020
944
  }
1021
- if (attributes && Object.keys(attributes).length > 0) {
1022
- for (const [key, value] of Object.entries(attributes)) {
1023
- query.jsonLike("attributes", key, value);
1024
- }
945
+ const countResult = await this.operations.executeQuery(countQuery.build());
946
+ const total = Number(countResult[0]?.count ?? 0);
947
+ if (total === 0 && messages.length === 0) {
948
+ return {
949
+ messages: [],
950
+ total: 0,
951
+ page,
952
+ perPage,
953
+ hasMore: false
954
+ };
1025
955
  }
956
+ const excludeIds = messages.map((m) => m.id);
957
+ const excludeCondition = excludeIds.length > 0 ? `AND id NOT IN (${excludeIds.map(() => "?").join(",")})` : "";
958
+ let query;
959
+ let queryParams = [threadId];
1026
960
  if (fromDate) {
1027
- query.andWhere("createdAt >= ?", fromDate instanceof Date ? fromDate.toISOString() : fromDate);
961
+ queryParams.push(serializeDate(fromDate));
1028
962
  }
1029
963
  if (toDate) {
1030
- query.andWhere("createdAt <= ?", toDate instanceof Date ? toDate.toISOString() : toDate);
964
+ queryParams.push(serializeDate(toDate));
1031
965
  }
1032
- query.orderBy("startTime", "DESC").limit(perPage).offset(page * perPage);
1033
- const { sql, params } = query.build();
1034
- const results = await this.executeQuery({ sql, params });
1035
- return isArrayOfRecords(results) ? results.map(
1036
- (trace) => ({
1037
- ...trace,
1038
- attributes: this.deserializeValue(trace.attributes, "jsonb"),
1039
- status: this.deserializeValue(trace.status, "jsonb"),
1040
- events: this.deserializeValue(trace.events, "jsonb"),
1041
- links: this.deserializeValue(trace.links, "jsonb"),
1042
- other: this.deserializeValue(trace.other, "jsonb")
1043
- })
1044
- ) : [];
1045
- } catch (error) {
1046
- this.logger.error("Error getting traces:", { message: error instanceof Error ? error.message : String(error) });
1047
- return [];
1048
- }
1049
- }
1050
- async getTracesPaginated(args) {
1051
- const { name, scope, page, perPage, attributes, fromDate, toDate } = args;
1052
- const fullTableName = this.getTableName(TABLE_TRACES);
1053
- try {
1054
- const dataQuery = createSqlBuilder().select("*").from(fullTableName).where("1=1");
1055
- const countQuery = createSqlBuilder().count().from(fullTableName).where("1=1");
1056
- if (name) {
1057
- dataQuery.andWhere("name LIKE ?", `%${name}%`);
1058
- countQuery.andWhere("name LIKE ?", `%${name}%`);
966
+ if (excludeIds.length > 0) {
967
+ queryParams.push(...excludeIds);
1059
968
  }
1060
- if (scope) {
1061
- dataQuery.andWhere("scope = ?", scope);
1062
- countQuery.andWhere("scope = ?", scope);
969
+ if (selectBy?.last && selectBy.last > 0) {
970
+ query = `
971
+ SELECT id, content, role, type, createdAt, thread_id AS threadId, resourceId
972
+ FROM ${fullTableName}
973
+ WHERE thread_id = ?
974
+ ${fromDate ? "AND createdAt >= ?" : ""}
975
+ ${toDate ? "AND createdAt <= ?" : ""}
976
+ ${excludeCondition}
977
+ ORDER BY createdAt DESC
978
+ LIMIT ?
979
+ `;
980
+ queryParams.push(selectBy.last);
981
+ } else {
982
+ query = `
983
+ SELECT id, content, role, type, createdAt, thread_id AS threadId, resourceId
984
+ FROM ${fullTableName}
985
+ WHERE thread_id = ?
986
+ ${fromDate ? "AND createdAt >= ?" : ""}
987
+ ${toDate ? "AND createdAt <= ?" : ""}
988
+ ${excludeCondition}
989
+ ORDER BY createdAt DESC
990
+ LIMIT ? OFFSET ?
991
+ `;
992
+ queryParams.push(perPage, page * perPage);
1063
993
  }
1064
- if (attributes && Object.keys(attributes).length > 0) {
1065
- for (const [key, value] of Object.entries(attributes)) {
1066
- dataQuery.jsonLike("attributes", key, value);
1067
- countQuery.jsonLike("attributes", key, value);
994
+ const results = await this.operations.executeQuery({ sql: query, params: queryParams });
995
+ const processedMessages = results.map((message) => {
996
+ const processedMsg = {};
997
+ for (const [key, value] of Object.entries(message)) {
998
+ if (key === `type` && value === `v2`) continue;
999
+ processedMsg[key] = deserializeValue(value);
1068
1000
  }
1001
+ return processedMsg;
1002
+ });
1003
+ if (selectBy?.last) {
1004
+ processedMessages.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
1069
1005
  }
1070
- if (fromDate) {
1071
- const fromDateStr = fromDate instanceof Date ? fromDate.toISOString() : fromDate;
1072
- dataQuery.andWhere("createdAt >= ?", fromDateStr);
1073
- countQuery.andWhere("createdAt >= ?", fromDateStr);
1074
- }
1075
- if (toDate) {
1076
- const toDateStr = toDate instanceof Date ? toDate.toISOString() : toDate;
1077
- dataQuery.andWhere("createdAt <= ?", toDateStr);
1078
- countQuery.andWhere("createdAt <= ?", toDateStr);
1079
- }
1080
- const countResult = await this.executeQuery(countQuery.build());
1081
- const total = Number(countResult?.[0]?.count ?? 0);
1082
- dataQuery.orderBy("startTime", "DESC").limit(perPage).offset(page * perPage);
1083
- const results = await this.executeQuery(dataQuery.build());
1084
- const traces = isArrayOfRecords(results) ? results.map(
1085
- (trace) => ({
1086
- ...trace,
1087
- attributes: this.deserializeValue(trace.attributes, "jsonb"),
1088
- status: this.deserializeValue(trace.status, "jsonb"),
1089
- events: this.deserializeValue(trace.events, "jsonb"),
1090
- links: this.deserializeValue(trace.links, "jsonb"),
1091
- other: this.deserializeValue(trace.other, "jsonb")
1092
- })
1093
- ) : [];
1006
+ const list = new MessageList().add(processedMessages, "memory");
1007
+ messages.push(...format === `v2` ? list.get.all.v2() : list.get.all.v1());
1094
1008
  return {
1095
- traces,
1009
+ messages,
1096
1010
  total,
1097
1011
  page,
1098
1012
  perPage,
1099
- hasMore: page * perPage + traces.length < total
1013
+ hasMore: selectBy?.last ? false : page * perPage + messages.length < total
1100
1014
  };
1101
1015
  } catch (error) {
1102
- this.logger.error("Error getting traces:", { message: error instanceof Error ? error.message : String(error) });
1103
- return { traces: [], total: 0, page, perPage, hasMore: false };
1104
- }
1105
- }
1106
- /**
1107
- * @deprecated use getEvals instead
1108
- */
1109
- async getEvalsByAgentName(agentName, type) {
1110
- const fullTableName = this.getTableName(TABLE_EVALS);
1111
- try {
1112
- let query = createSqlBuilder().select("*").from(fullTableName).where("agent_name = ?", agentName);
1113
- if (type === "test") {
1114
- query = query.andWhere("test_info IS NOT NULL AND json_extract(test_info, '$.testPath') IS NOT NULL");
1115
- } else if (type === "live") {
1116
- query = query.andWhere("(test_info IS NULL OR json_extract(test_info, '$.testPath') IS NULL)");
1117
- }
1118
- query.orderBy("created_at", "DESC");
1119
- const { sql, params } = query.build();
1120
- const results = await this.executeQuery({ sql, params });
1121
- return isArrayOfRecords(results) ? results.map((row) => {
1122
- const result = this.deserializeValue(row.result);
1123
- const testInfo = row.test_info ? this.deserializeValue(row.test_info) : void 0;
1124
- return {
1125
- input: row.input || "",
1126
- output: row.output || "",
1127
- result,
1128
- agentName: row.agent_name || "",
1129
- metricName: row.metric_name || "",
1130
- instructions: row.instructions || "",
1131
- runId: row.run_id || "",
1132
- globalRunId: row.global_run_id || "",
1133
- createdAt: row.created_at || "",
1134
- testInfo
1135
- };
1136
- }) : [];
1137
- } catch (error) {
1138
- this.logger.error(`Error getting evals for agent ${agentName}:`, {
1139
- message: error instanceof Error ? error.message : String(error)
1140
- });
1141
- return [];
1142
- }
1143
- }
1144
- async getEvals(options) {
1145
- const { agentName, type, page = 0, perPage = 40, fromDate, toDate } = options || {};
1146
- const fullTableName = this.getTableName(TABLE_EVALS);
1147
- const conditions = [];
1148
- const queryParams = [];
1149
- if (agentName) {
1150
- conditions.push(`agent_name = ?`);
1151
- queryParams.push(agentName);
1152
- }
1153
- if (type === "test") {
1154
- conditions.push(`(test_info IS NOT NULL AND json_extract(test_info, '$.testPath') IS NOT NULL)`);
1155
- } else if (type === "live") {
1156
- conditions.push(`(test_info IS NULL OR json_extract(test_info, '$.testPath') IS NULL)`);
1157
- }
1158
- if (fromDate) {
1159
- conditions.push(`createdAt >= ?`);
1160
- queryParams.push(this.serializeDate(fromDate));
1161
- }
1162
- if (toDate) {
1163
- conditions.push(`createdAt <= ?`);
1164
- queryParams.push(this.serializeDate(toDate));
1165
- }
1166
- const countQueryBuilder = createSqlBuilder().count().from(fullTableName);
1167
- if (conditions.length > 0) {
1168
- countQueryBuilder.where(conditions.join(" AND "), ...queryParams);
1169
- }
1170
- const { sql: countSql, params: countParams } = countQueryBuilder.build();
1171
- const countResult = await this.executeQuery({ sql: countSql, params: countParams, first: true });
1172
- const total = Number(countResult?.count || 0);
1173
- const currentOffset = page * perPage;
1174
- if (total === 0) {
1016
+ const mastraError = new MastraError(
1017
+ {
1018
+ id: "CLOUDFLARE_D1_STORAGE_GET_MESSAGES_PAGINATED_ERROR",
1019
+ domain: ErrorDomain.STORAGE,
1020
+ category: ErrorCategory.THIRD_PARTY,
1021
+ text: `Failed to retrieve messages for thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`,
1022
+ details: { threadId }
1023
+ },
1024
+ error
1025
+ );
1026
+ this.logger?.error(mastraError.toString());
1027
+ this.logger?.trackException(mastraError);
1175
1028
  return {
1176
- evals: [],
1029
+ messages: [],
1177
1030
  total: 0,
1178
1031
  page,
1179
1032
  perPage,
1180
1033
  hasMore: false
1181
1034
  };
1182
1035
  }
1183
- const dataQueryBuilder = createSqlBuilder().select("*").from(fullTableName);
1184
- if (conditions.length > 0) {
1185
- dataQueryBuilder.where(conditions.join(" AND "), ...queryParams);
1186
- }
1187
- dataQueryBuilder.orderBy("createdAt", "DESC").limit(perPage).offset(currentOffset);
1188
- const { sql: dataSql, params: dataParams } = dataQueryBuilder.build();
1189
- const rows = await this.executeQuery({ sql: dataSql, params: dataParams });
1190
- const evals = (isArrayOfRecords(rows) ? rows : []).map((row) => {
1191
- const result = this.deserializeValue(row.result);
1192
- const testInfo = row.test_info ? this.deserializeValue(row.test_info) : void 0;
1193
- if (!result || typeof result !== "object" || !("score" in result)) {
1194
- throw new Error(`Invalid MetricResult format: ${JSON.stringify(result)}`);
1195
- }
1196
- return {
1197
- input: row.input,
1198
- output: row.output,
1199
- result,
1200
- agentName: row.agent_name,
1201
- metricName: row.metric_name,
1202
- instructions: row.instructions,
1203
- testInfo,
1204
- globalRunId: row.global_run_id,
1205
- runId: row.run_id,
1206
- createdAt: row.createdAt
1207
- };
1208
- });
1209
- const hasMore = currentOffset + evals.length < total;
1210
- return {
1211
- evals,
1212
- total,
1213
- page,
1214
- perPage,
1215
- hasMore
1216
- };
1217
1036
  }
1218
- parseWorkflowRun(row) {
1219
- let parsedSnapshot = row.snapshot;
1220
- if (typeof parsedSnapshot === "string") {
1221
- try {
1222
- parsedSnapshot = JSON.parse(row.snapshot);
1037
+ async updateMessages(args) {
1038
+ const { messages } = args;
1039
+ this.logger.debug("Updating messages", { count: messages.length });
1040
+ if (!messages.length) {
1041
+ return [];
1042
+ }
1043
+ const messageIds = messages.map((m) => m.id);
1044
+ const fullTableName = this.operations.getTableName(TABLE_MESSAGES);
1045
+ const threadsTableName = this.operations.getTableName(TABLE_THREADS);
1046
+ try {
1047
+ const placeholders = messageIds.map(() => "?").join(",");
1048
+ const selectQuery = `SELECT id, content, role, type, createdAt, thread_id AS threadId, resourceId FROM ${fullTableName} WHERE id IN (${placeholders})`;
1049
+ const existingMessages = await this.operations.executeQuery({ sql: selectQuery, params: messageIds });
1050
+ if (existingMessages.length === 0) {
1051
+ return [];
1052
+ }
1053
+ const parsedExistingMessages = existingMessages.map((msg) => {
1054
+ if (typeof msg.content === "string") {
1055
+ try {
1056
+ msg.content = JSON.parse(msg.content);
1057
+ } catch {
1058
+ }
1059
+ }
1060
+ return msg;
1061
+ });
1062
+ const threadIdsToUpdate = /* @__PURE__ */ new Set();
1063
+ const updateQueries = [];
1064
+ for (const existingMessage of parsedExistingMessages) {
1065
+ const updatePayload = messages.find((m) => m.id === existingMessage.id);
1066
+ if (!updatePayload) continue;
1067
+ const { id, ...fieldsToUpdate } = updatePayload;
1068
+ if (Object.keys(fieldsToUpdate).length === 0) continue;
1069
+ threadIdsToUpdate.add(existingMessage.threadId);
1070
+ if ("threadId" in updatePayload && updatePayload.threadId && updatePayload.threadId !== existingMessage.threadId) {
1071
+ threadIdsToUpdate.add(updatePayload.threadId);
1072
+ }
1073
+ const setClauses = [];
1074
+ const values = [];
1075
+ const updatableFields = { ...fieldsToUpdate };
1076
+ if (updatableFields.content) {
1077
+ const existingContent = existingMessage.content || {};
1078
+ const newContent = {
1079
+ ...existingContent,
1080
+ ...updatableFields.content,
1081
+ // Deep merge metadata if it exists on both
1082
+ ...existingContent?.metadata && updatableFields.content.metadata ? {
1083
+ metadata: {
1084
+ ...existingContent.metadata,
1085
+ ...updatableFields.content.metadata
1086
+ }
1087
+ } : {}
1088
+ };
1089
+ setClauses.push(`content = ?`);
1090
+ values.push(JSON.stringify(newContent));
1091
+ delete updatableFields.content;
1092
+ }
1093
+ for (const key in updatableFields) {
1094
+ if (Object.prototype.hasOwnProperty.call(updatableFields, key)) {
1095
+ const dbColumn = key === "threadId" ? "thread_id" : key;
1096
+ setClauses.push(`${dbColumn} = ?`);
1097
+ values.push(updatableFields[key]);
1098
+ }
1099
+ }
1100
+ if (setClauses.length > 0) {
1101
+ values.push(id);
1102
+ const updateQuery = `UPDATE ${fullTableName} SET ${setClauses.join(", ")} WHERE id = ?`;
1103
+ updateQueries.push({ sql: updateQuery, params: values });
1104
+ }
1105
+ }
1106
+ for (const query of updateQueries) {
1107
+ await this.operations.executeQuery(query);
1108
+ }
1109
+ if (threadIdsToUpdate.size > 0) {
1110
+ const threadPlaceholders = Array.from(threadIdsToUpdate).map(() => "?").join(",");
1111
+ const threadUpdateQuery = `UPDATE ${threadsTableName} SET updatedAt = ? WHERE id IN (${threadPlaceholders})`;
1112
+ const threadUpdateParams = [(/* @__PURE__ */ new Date()).toISOString(), ...Array.from(threadIdsToUpdate)];
1113
+ await this.operations.executeQuery({ sql: threadUpdateQuery, params: threadUpdateParams });
1114
+ }
1115
+ const updatedMessages = await this.operations.executeQuery({ sql: selectQuery, params: messageIds });
1116
+ return updatedMessages.map((message) => {
1117
+ if (typeof message.content === "string") {
1118
+ try {
1119
+ message.content = JSON.parse(message.content);
1120
+ } catch {
1121
+ }
1122
+ }
1123
+ return message;
1124
+ });
1125
+ } catch (error) {
1126
+ throw new MastraError(
1127
+ {
1128
+ id: "CLOUDFLARE_D1_STORAGE_UPDATE_MESSAGES_FAILED",
1129
+ domain: ErrorDomain.STORAGE,
1130
+ category: ErrorCategory.THIRD_PARTY,
1131
+ details: { count: messages.length }
1132
+ },
1133
+ error
1134
+ );
1135
+ }
1136
+ }
1137
+ };
1138
+ var StoreOperationsD1 = class extends StoreOperations {
1139
+ client;
1140
+ binding;
1141
+ tablePrefix;
1142
+ constructor(config) {
1143
+ super();
1144
+ this.client = config.client;
1145
+ this.binding = config.binding;
1146
+ this.tablePrefix = config.tablePrefix || "";
1147
+ }
1148
+ async hasColumn(table, column) {
1149
+ const fullTableName = table.startsWith(this.tablePrefix) ? table : `${this.tablePrefix}${table}`;
1150
+ const sql = `PRAGMA table_info(${fullTableName});`;
1151
+ const result = await this.executeQuery({ sql, params: [] });
1152
+ if (!result || !Array.isArray(result)) return false;
1153
+ return result.some((col) => col.name === column || col.name === column.toLowerCase());
1154
+ }
1155
+ getTableName(tableName) {
1156
+ return `${this.tablePrefix}${tableName}`;
1157
+ }
1158
+ formatSqlParams(params) {
1159
+ return params.map((p) => p === void 0 || p === null ? null : p);
1160
+ }
1161
+ async executeWorkersBindingQuery({
1162
+ sql,
1163
+ params = [],
1164
+ first = false
1165
+ }) {
1166
+ if (!this.binding) {
1167
+ throw new Error("Workers binding is not configured");
1168
+ }
1169
+ try {
1170
+ const statement = this.binding.prepare(sql);
1171
+ const formattedParams = this.formatSqlParams(params);
1172
+ let result;
1173
+ if (formattedParams.length > 0) {
1174
+ if (first) {
1175
+ result = await statement.bind(...formattedParams).first();
1176
+ if (!result) return null;
1177
+ return result;
1178
+ } else {
1179
+ result = await statement.bind(...formattedParams).all();
1180
+ const results = result.results || [];
1181
+ return results;
1182
+ }
1183
+ } else {
1184
+ if (first) {
1185
+ result = await statement.first();
1186
+ if (!result) return null;
1187
+ return result;
1188
+ } else {
1189
+ result = await statement.all();
1190
+ const results = result.results || [];
1191
+ return results;
1192
+ }
1193
+ }
1194
+ } catch (error) {
1195
+ throw new MastraError(
1196
+ {
1197
+ id: "CLOUDFLARE_D1_STORE_OPERATIONS_WORKERS_BINDING_QUERY_FAILED",
1198
+ domain: ErrorDomain.STORAGE,
1199
+ category: ErrorCategory.THIRD_PARTY,
1200
+ details: { sql }
1201
+ },
1202
+ error
1203
+ );
1204
+ }
1205
+ }
1206
+ async executeRestQuery({
1207
+ sql,
1208
+ params = [],
1209
+ first = false
1210
+ }) {
1211
+ if (!this.client) {
1212
+ throw new Error("D1 client is not configured");
1213
+ }
1214
+ try {
1215
+ const formattedParams = this.formatSqlParams(params);
1216
+ const response = await this.client.query({
1217
+ sql,
1218
+ params: formattedParams
1219
+ });
1220
+ const result = response.result || [];
1221
+ const results = result.flatMap((r) => r.results || []);
1222
+ if (first) {
1223
+ return results[0] || null;
1224
+ }
1225
+ return results;
1226
+ } catch (error) {
1227
+ throw new MastraError(
1228
+ {
1229
+ id: "CLOUDFLARE_D1_STORE_OPERATIONS_REST_QUERY_FAILED",
1230
+ domain: ErrorDomain.STORAGE,
1231
+ category: ErrorCategory.THIRD_PARTY,
1232
+ details: { sql }
1233
+ },
1234
+ error
1235
+ );
1236
+ }
1237
+ }
1238
+ async executeQuery(options) {
1239
+ if (this.binding) {
1240
+ return this.executeWorkersBindingQuery(options);
1241
+ } else if (this.client) {
1242
+ return this.executeRestQuery(options);
1243
+ } else {
1244
+ throw new Error("Neither binding nor client is configured");
1245
+ }
1246
+ }
1247
+ async getTableColumns(tableName) {
1248
+ try {
1249
+ const sql = `PRAGMA table_info(${tableName})`;
1250
+ const result = await this.executeQuery({ sql });
1251
+ if (!result || !Array.isArray(result)) {
1252
+ return [];
1253
+ }
1254
+ return result.map((row) => ({
1255
+ name: row.name,
1256
+ type: row.type
1257
+ }));
1258
+ } catch (error) {
1259
+ this.logger.warn(`Failed to get table columns for ${tableName}:`, error);
1260
+ return [];
1261
+ }
1262
+ }
1263
+ serializeValue(value) {
1264
+ if (value === null || value === void 0) {
1265
+ return null;
1266
+ }
1267
+ if (value instanceof Date) {
1268
+ return value.toISOString();
1269
+ }
1270
+ if (typeof value === "object") {
1271
+ return JSON.stringify(value);
1272
+ }
1273
+ return value;
1274
+ }
1275
+ getSqlType(type) {
1276
+ switch (type) {
1277
+ case "bigint":
1278
+ return "INTEGER";
1279
+ // SQLite uses INTEGER for all integer sizes
1280
+ case "jsonb":
1281
+ return "TEXT";
1282
+ // Store JSON as TEXT in SQLite
1283
+ default:
1284
+ return super.getSqlType(type);
1285
+ }
1286
+ }
1287
+ async createTable({
1288
+ tableName,
1289
+ schema
1290
+ }) {
1291
+ try {
1292
+ const fullTableName = this.getTableName(tableName);
1293
+ const columnDefinitions = Object.entries(schema).map(([colName, colDef]) => {
1294
+ const type = this.getSqlType(colDef.type);
1295
+ const nullable = colDef.nullable === false ? "NOT NULL" : "";
1296
+ const primaryKey = colDef.primaryKey ? "PRIMARY KEY" : "";
1297
+ return `${colName} ${type} ${nullable} ${primaryKey}`.trim();
1298
+ });
1299
+ const tableConstraints = [];
1300
+ if (tableName === TABLE_WORKFLOW_SNAPSHOT) {
1301
+ tableConstraints.push("UNIQUE (workflow_name, run_id)");
1302
+ }
1303
+ const query = createSqlBuilder().createTable(fullTableName, columnDefinitions, tableConstraints);
1304
+ const { sql, params } = query.build();
1305
+ await this.executeQuery({ sql, params });
1306
+ this.logger.debug(`Created table ${fullTableName}`);
1307
+ } catch (error) {
1308
+ throw new MastraError(
1309
+ {
1310
+ id: "CLOUDFLARE_D1_STORE_OPERATIONS_CREATE_TABLE_FAILED",
1311
+ domain: ErrorDomain.STORAGE,
1312
+ category: ErrorCategory.THIRD_PARTY,
1313
+ details: { tableName }
1314
+ },
1315
+ error
1316
+ );
1317
+ }
1318
+ }
1319
+ async clearTable({ tableName }) {
1320
+ try {
1321
+ const fullTableName = this.getTableName(tableName);
1322
+ const query = createSqlBuilder().delete(fullTableName);
1323
+ const { sql, params } = query.build();
1324
+ await this.executeQuery({ sql, params });
1325
+ this.logger.debug(`Cleared table ${fullTableName}`);
1326
+ } catch (error) {
1327
+ throw new MastraError(
1328
+ {
1329
+ id: "CLOUDFLARE_D1_STORE_OPERATIONS_CLEAR_TABLE_FAILED",
1330
+ domain: ErrorDomain.STORAGE,
1331
+ category: ErrorCategory.THIRD_PARTY,
1332
+ details: { tableName }
1333
+ },
1334
+ error
1335
+ );
1336
+ }
1337
+ }
1338
+ async dropTable({ tableName }) {
1339
+ try {
1340
+ const fullTableName = this.getTableName(tableName);
1341
+ const sql = `DROP TABLE IF EXISTS ${fullTableName}`;
1342
+ await this.executeQuery({ sql });
1343
+ this.logger.debug(`Dropped table ${fullTableName}`);
1344
+ } catch (error) {
1345
+ throw new MastraError(
1346
+ {
1347
+ id: "CLOUDFLARE_D1_STORE_OPERATIONS_DROP_TABLE_FAILED",
1348
+ domain: ErrorDomain.STORAGE,
1349
+ category: ErrorCategory.THIRD_PARTY,
1350
+ details: { tableName }
1351
+ },
1352
+ error
1353
+ );
1354
+ }
1355
+ }
1356
+ async alterTable(args) {
1357
+ try {
1358
+ const fullTableName = this.getTableName(args.tableName);
1359
+ const existingColumns = await this.getTableColumns(fullTableName);
1360
+ const existingColumnNames = new Set(existingColumns.map((col) => col.name));
1361
+ for (const [columnName, column] of Object.entries(args.schema)) {
1362
+ if (!existingColumnNames.has(columnName) && args.ifNotExists.includes(columnName)) {
1363
+ const sqlType = this.getSqlType(column.type);
1364
+ const defaultValue = this.getDefaultValue(column.type);
1365
+ const sql = `ALTER TABLE ${fullTableName} ADD COLUMN ${columnName} ${sqlType} ${defaultValue}`;
1366
+ await this.executeQuery({ sql });
1367
+ this.logger.debug(`Added column ${columnName} to table ${fullTableName}`);
1368
+ }
1369
+ }
1370
+ } catch (error) {
1371
+ throw new MastraError(
1372
+ {
1373
+ id: "CLOUDFLARE_D1_STORE_OPERATIONS_ALTER_TABLE_FAILED",
1374
+ domain: ErrorDomain.STORAGE,
1375
+ category: ErrorCategory.THIRD_PARTY,
1376
+ details: { tableName: args.tableName }
1377
+ },
1378
+ error
1379
+ );
1380
+ }
1381
+ }
1382
+ async insert({ tableName, record }) {
1383
+ try {
1384
+ const fullTableName = this.getTableName(tableName);
1385
+ const processedRecord = await this.processRecord(record);
1386
+ const columns = Object.keys(processedRecord);
1387
+ const values = Object.values(processedRecord);
1388
+ const query = createSqlBuilder().insert(fullTableName, columns, values);
1389
+ const { sql, params } = query.build();
1390
+ await this.executeQuery({ sql, params });
1391
+ } catch (error) {
1392
+ throw new MastraError(
1393
+ {
1394
+ id: "CLOUDFLARE_D1_STORE_OPERATIONS_INSERT_FAILED",
1395
+ domain: ErrorDomain.STORAGE,
1396
+ category: ErrorCategory.THIRD_PARTY,
1397
+ details: { tableName }
1398
+ },
1399
+ error
1400
+ );
1401
+ }
1402
+ }
1403
+ async batchInsert({ tableName, records }) {
1404
+ try {
1405
+ if (records.length === 0) return;
1406
+ const fullTableName = this.getTableName(tableName);
1407
+ const processedRecords = await Promise.all(records.map((record) => this.processRecord(record)));
1408
+ const columns = Object.keys(processedRecords[0] || {});
1409
+ for (const record of processedRecords) {
1410
+ const values = Object.values(record);
1411
+ const query = createSqlBuilder().insert(fullTableName, columns, values);
1412
+ const { sql, params } = query.build();
1413
+ await this.executeQuery({ sql, params });
1414
+ }
1415
+ } catch (error) {
1416
+ throw new MastraError(
1417
+ {
1418
+ id: "CLOUDFLARE_D1_STORE_OPERATIONS_BATCH_INSERT_FAILED",
1419
+ domain: ErrorDomain.STORAGE,
1420
+ category: ErrorCategory.THIRD_PARTY,
1421
+ details: { tableName }
1422
+ },
1423
+ error
1424
+ );
1425
+ }
1426
+ }
1427
+ async load({ tableName, keys }) {
1428
+ try {
1429
+ const fullTableName = this.getTableName(tableName);
1430
+ const query = createSqlBuilder().select("*").from(fullTableName);
1431
+ let firstKey = true;
1432
+ for (const [key, value] of Object.entries(keys)) {
1433
+ if (firstKey) {
1434
+ query.where(`${key} = ?`, value);
1435
+ firstKey = false;
1436
+ } else {
1437
+ query.andWhere(`${key} = ?`, value);
1438
+ }
1439
+ }
1440
+ query.limit(1);
1441
+ const { sql, params } = query.build();
1442
+ const result = await this.executeQuery({ sql, params, first: true });
1443
+ if (!result) {
1444
+ return null;
1445
+ }
1446
+ const deserializedResult = {};
1447
+ for (const [key, value] of Object.entries(result)) {
1448
+ deserializedResult[key] = deserializeValue(value);
1449
+ }
1450
+ return deserializedResult;
1451
+ } catch (error) {
1452
+ throw new MastraError(
1453
+ {
1454
+ id: "CLOUDFLARE_D1_STORE_OPERATIONS_LOAD_FAILED",
1455
+ domain: ErrorDomain.STORAGE,
1456
+ category: ErrorCategory.THIRD_PARTY,
1457
+ details: { tableName }
1458
+ },
1459
+ error
1460
+ );
1461
+ }
1462
+ }
1463
+ async processRecord(record) {
1464
+ const processed = {};
1465
+ for (const [key, value] of Object.entries(record)) {
1466
+ processed[key] = this.serializeValue(value);
1467
+ }
1468
+ return processed;
1469
+ }
1470
+ /**
1471
+ * Upsert multiple records in a batch operation
1472
+ * @param tableName The table to insert into
1473
+ * @param records The records to insert
1474
+ */
1475
+ async batchUpsert({ tableName, records }) {
1476
+ if (records.length === 0) return;
1477
+ const fullTableName = this.getTableName(tableName);
1478
+ try {
1479
+ const batchSize = 50;
1480
+ for (let i = 0; i < records.length; i += batchSize) {
1481
+ const batch = records.slice(i, i + batchSize);
1482
+ const recordsToInsert = batch;
1483
+ if (recordsToInsert.length > 0) {
1484
+ const firstRecord = recordsToInsert[0];
1485
+ const columns = Object.keys(firstRecord || {});
1486
+ for (const record of recordsToInsert) {
1487
+ const values = columns.map((col) => {
1488
+ if (!record) return null;
1489
+ const value = typeof col === "string" ? record[col] : null;
1490
+ return this.serializeValue(value);
1491
+ });
1492
+ const recordToUpsert = columns.reduce(
1493
+ (acc, col) => {
1494
+ if (col !== "createdAt") acc[col] = `excluded.${col}`;
1495
+ return acc;
1496
+ },
1497
+ {}
1498
+ );
1499
+ const query = createSqlBuilder().insert(fullTableName, columns, values, ["id"], recordToUpsert);
1500
+ const { sql, params } = query.build();
1501
+ await this.executeQuery({ sql, params });
1502
+ }
1503
+ }
1504
+ this.logger.debug(
1505
+ `Processed batch ${Math.floor(i / batchSize) + 1} of ${Math.ceil(records.length / batchSize)}`
1506
+ );
1507
+ }
1508
+ this.logger.debug(`Successfully batch upserted ${records.length} records into ${tableName}`);
1509
+ } catch (error) {
1510
+ throw new MastraError(
1511
+ {
1512
+ id: "CLOUDFLARE_D1_STORAGE_BATCH_UPSERT_ERROR",
1513
+ domain: ErrorDomain.STORAGE,
1514
+ category: ErrorCategory.THIRD_PARTY,
1515
+ text: `Failed to batch upsert into ${tableName}: ${error instanceof Error ? error.message : String(error)}`,
1516
+ details: { tableName }
1517
+ },
1518
+ error
1519
+ );
1520
+ }
1521
+ }
1522
+ };
1523
+ function transformScoreRow(row) {
1524
+ let input = void 0;
1525
+ if (row.input) {
1526
+ try {
1527
+ input = JSON.parse(row.input);
1528
+ } catch {
1529
+ input = row.input;
1530
+ }
1531
+ }
1532
+ return {
1533
+ ...row,
1534
+ input,
1535
+ createdAt: row.createdAtZ || row.createdAt,
1536
+ updatedAt: row.updatedAtZ || row.updatedAt
1537
+ };
1538
+ }
1539
+ var ScoresStorageD1 = class extends ScoresStorage {
1540
+ operations;
1541
+ constructor({ operations }) {
1542
+ super();
1543
+ this.operations = operations;
1544
+ }
1545
+ async getScoreById({ id }) {
1546
+ try {
1547
+ const fullTableName = this.operations.getTableName(TABLE_SCORERS);
1548
+ const query = createSqlBuilder().select("*").from(fullTableName).where("id = ?", id);
1549
+ const { sql, params } = query.build();
1550
+ const result = await this.operations.executeQuery({ sql, params, first: true });
1551
+ if (!result) {
1552
+ return null;
1553
+ }
1554
+ return transformScoreRow(result);
1555
+ } catch (error) {
1556
+ throw new MastraError(
1557
+ {
1558
+ id: "CLOUDFLARE_D1_STORE_SCORES_GET_SCORE_BY_ID_FAILED",
1559
+ domain: ErrorDomain.STORAGE,
1560
+ category: ErrorCategory.THIRD_PARTY
1561
+ },
1562
+ error
1563
+ );
1564
+ }
1565
+ }
1566
+ async saveScore(score) {
1567
+ try {
1568
+ const fullTableName = this.operations.getTableName(TABLE_SCORERS);
1569
+ const { input, ...rest } = score;
1570
+ const serializedRecord = {};
1571
+ for (const [key, value] of Object.entries(rest)) {
1572
+ if (value !== null && value !== void 0) {
1573
+ if (typeof value === "object") {
1574
+ serializedRecord[key] = JSON.stringify(value);
1575
+ } else {
1576
+ serializedRecord[key] = value;
1577
+ }
1578
+ } else {
1579
+ serializedRecord[key] = null;
1580
+ }
1581
+ }
1582
+ serializedRecord.input = JSON.stringify(input);
1583
+ serializedRecord.createdAt = (/* @__PURE__ */ new Date()).toISOString();
1584
+ serializedRecord.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1585
+ const columns = Object.keys(serializedRecord);
1586
+ const values = Object.values(serializedRecord);
1587
+ const query = createSqlBuilder().insert(fullTableName, columns, values);
1588
+ const { sql, params } = query.build();
1589
+ await this.operations.executeQuery({ sql, params });
1590
+ const scoreFromDb = await this.getScoreById({ id: score.id });
1591
+ return { score: scoreFromDb };
1592
+ } catch (error) {
1593
+ throw new MastraError(
1594
+ {
1595
+ id: "CLOUDFLARE_D1_STORE_SCORES_SAVE_SCORE_FAILED",
1596
+ domain: ErrorDomain.STORAGE,
1597
+ category: ErrorCategory.THIRD_PARTY
1598
+ },
1599
+ error
1600
+ );
1601
+ }
1602
+ }
1603
+ async getScoresByScorerId({
1604
+ scorerId,
1605
+ pagination
1606
+ }) {
1607
+ try {
1608
+ const fullTableName = this.operations.getTableName(TABLE_SCORERS);
1609
+ const countQuery = createSqlBuilder().count().from(fullTableName).where("scorerId = ?", scorerId);
1610
+ const countResult = await this.operations.executeQuery(countQuery.build());
1611
+ const total = Array.isArray(countResult) ? Number(countResult?.[0]?.count ?? 0) : Number(countResult?.count ?? 0);
1612
+ if (total === 0) {
1613
+ return {
1614
+ pagination: {
1615
+ total: 0,
1616
+ page: pagination.page,
1617
+ perPage: pagination.perPage,
1618
+ hasMore: false
1619
+ },
1620
+ scores: []
1621
+ };
1622
+ }
1623
+ const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("scorerId = ?", scorerId).limit(pagination.perPage).offset(pagination.page * pagination.perPage);
1624
+ const { sql, params } = selectQuery.build();
1625
+ const results = await this.operations.executeQuery({ sql, params });
1626
+ const scores = Array.isArray(results) ? results.map(transformScoreRow) : [];
1627
+ return {
1628
+ pagination: {
1629
+ total,
1630
+ page: pagination.page,
1631
+ perPage: pagination.perPage,
1632
+ hasMore: total > (pagination.page + 1) * pagination.perPage
1633
+ },
1634
+ scores
1635
+ };
1636
+ } catch (error) {
1637
+ throw new MastraError(
1638
+ {
1639
+ id: "CLOUDFLARE_D1_STORE_SCORES_GET_SCORES_BY_SCORER_ID_FAILED",
1640
+ domain: ErrorDomain.STORAGE,
1641
+ category: ErrorCategory.THIRD_PARTY
1642
+ },
1643
+ error
1644
+ );
1645
+ }
1646
+ }
1647
+ async getScoresByRunId({
1648
+ runId,
1649
+ pagination
1650
+ }) {
1651
+ try {
1652
+ const fullTableName = this.operations.getTableName(TABLE_SCORERS);
1653
+ const countQuery = createSqlBuilder().count().from(fullTableName).where("runId = ?", runId);
1654
+ const countResult = await this.operations.executeQuery(countQuery.build());
1655
+ const total = Array.isArray(countResult) ? Number(countResult?.[0]?.count ?? 0) : Number(countResult?.count ?? 0);
1656
+ if (total === 0) {
1657
+ return {
1658
+ pagination: {
1659
+ total: 0,
1660
+ page: pagination.page,
1661
+ perPage: pagination.perPage,
1662
+ hasMore: false
1663
+ },
1664
+ scores: []
1665
+ };
1666
+ }
1667
+ const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("runId = ?", runId).limit(pagination.perPage).offset(pagination.page * pagination.perPage);
1668
+ const { sql, params } = selectQuery.build();
1669
+ const results = await this.operations.executeQuery({ sql, params });
1670
+ const scores = Array.isArray(results) ? results.map(transformScoreRow) : [];
1671
+ return {
1672
+ pagination: {
1673
+ total,
1674
+ page: pagination.page,
1675
+ perPage: pagination.perPage,
1676
+ hasMore: total > (pagination.page + 1) * pagination.perPage
1677
+ },
1678
+ scores
1679
+ };
1680
+ } catch (error) {
1681
+ throw new MastraError(
1682
+ {
1683
+ id: "CLOUDFLARE_D1_STORE_SCORES_GET_SCORES_BY_RUN_ID_FAILED",
1684
+ domain: ErrorDomain.STORAGE,
1685
+ category: ErrorCategory.THIRD_PARTY
1686
+ },
1687
+ error
1688
+ );
1689
+ }
1690
+ }
1691
+ async getScoresByEntityId({
1692
+ entityId,
1693
+ entityType,
1694
+ pagination
1695
+ }) {
1696
+ try {
1697
+ const fullTableName = this.operations.getTableName(TABLE_SCORERS);
1698
+ const countQuery = createSqlBuilder().count().from(fullTableName).where("entityId = ?", entityId).andWhere("entityType = ?", entityType);
1699
+ const countResult = await this.operations.executeQuery(countQuery.build());
1700
+ const total = Array.isArray(countResult) ? Number(countResult?.[0]?.count ?? 0) : Number(countResult?.count ?? 0);
1701
+ if (total === 0) {
1702
+ return {
1703
+ pagination: {
1704
+ total: 0,
1705
+ page: pagination.page,
1706
+ perPage: pagination.perPage,
1707
+ hasMore: false
1708
+ },
1709
+ scores: []
1710
+ };
1711
+ }
1712
+ const selectQuery = createSqlBuilder().select("*").from(fullTableName).where("entityId = ?", entityId).andWhere("entityType = ?", entityType).limit(pagination.perPage).offset(pagination.page * pagination.perPage);
1713
+ const { sql, params } = selectQuery.build();
1714
+ const results = await this.operations.executeQuery({ sql, params });
1715
+ const scores = Array.isArray(results) ? results.map(transformScoreRow) : [];
1716
+ return {
1717
+ pagination: {
1718
+ total,
1719
+ page: pagination.page,
1720
+ perPage: pagination.perPage,
1721
+ hasMore: total > (pagination.page + 1) * pagination.perPage
1722
+ },
1723
+ scores
1724
+ };
1725
+ } catch (error) {
1726
+ throw new MastraError(
1727
+ {
1728
+ id: "CLOUDFLARE_D1_STORE_SCORES_GET_SCORES_BY_ENTITY_ID_FAILED",
1729
+ domain: ErrorDomain.STORAGE,
1730
+ category: ErrorCategory.THIRD_PARTY
1731
+ },
1732
+ error
1733
+ );
1734
+ }
1735
+ }
1736
+ };
1737
+ function isArrayOfRecords2(value) {
1738
+ return value && Array.isArray(value) && value.length > 0;
1739
+ }
1740
+ var TracesStorageD1 = class extends TracesStorage {
1741
+ operations;
1742
+ constructor({ operations }) {
1743
+ super();
1744
+ this.operations = operations;
1745
+ }
1746
+ async getTraces(args) {
1747
+ const paginatedArgs = {
1748
+ name: args.name,
1749
+ scope: args.scope,
1750
+ page: args.page,
1751
+ perPage: args.perPage,
1752
+ attributes: args.attributes,
1753
+ filters: args.filters,
1754
+ dateRange: args.fromDate || args.toDate ? {
1755
+ start: args.fromDate,
1756
+ end: args.toDate
1757
+ } : void 0
1758
+ };
1759
+ try {
1760
+ const result = await this.getTracesPaginated(paginatedArgs);
1761
+ return result.traces;
1762
+ } catch (error) {
1763
+ throw new MastraError(
1764
+ {
1765
+ id: "CLOUDFLARE_D1_STORAGE_GET_TRACES_ERROR",
1766
+ domain: ErrorDomain.STORAGE,
1767
+ category: ErrorCategory.THIRD_PARTY,
1768
+ text: `Failed to retrieve traces: ${error instanceof Error ? error.message : String(error)}`,
1769
+ details: {
1770
+ name: args.name ?? "",
1771
+ scope: args.scope ?? ""
1772
+ }
1773
+ },
1774
+ error
1775
+ );
1776
+ }
1777
+ }
1778
+ async getTracesPaginated(args) {
1779
+ const { name, scope, page = 0, perPage = 100, attributes, dateRange } = args;
1780
+ const fromDate = dateRange?.start;
1781
+ const toDate = dateRange?.end;
1782
+ const fullTableName = this.operations.getTableName(TABLE_TRACES);
1783
+ try {
1784
+ const dataQuery = createSqlBuilder().select("*").from(fullTableName).where("1=1");
1785
+ const countQuery = createSqlBuilder().count().from(fullTableName).where("1=1");
1786
+ if (name) {
1787
+ dataQuery.andWhere("name LIKE ?", `%${name}%`);
1788
+ countQuery.andWhere("name LIKE ?", `%${name}%`);
1789
+ }
1790
+ if (scope) {
1791
+ dataQuery.andWhere("scope = ?", scope);
1792
+ countQuery.andWhere("scope = ?", scope);
1793
+ }
1794
+ if (attributes && Object.keys(attributes).length > 0) {
1795
+ for (const [key, value] of Object.entries(attributes)) {
1796
+ dataQuery.jsonLike("attributes", key, value);
1797
+ countQuery.jsonLike("attributes", key, value);
1798
+ }
1799
+ }
1800
+ if (fromDate) {
1801
+ const fromDateStr = fromDate instanceof Date ? fromDate.toISOString() : fromDate;
1802
+ dataQuery.andWhere("createdAt >= ?", fromDateStr);
1803
+ countQuery.andWhere("createdAt >= ?", fromDateStr);
1804
+ }
1805
+ if (toDate) {
1806
+ const toDateStr = toDate instanceof Date ? toDate.toISOString() : toDate;
1807
+ dataQuery.andWhere("createdAt <= ?", toDateStr);
1808
+ countQuery.andWhere("createdAt <= ?", toDateStr);
1809
+ }
1810
+ const allDataResult = await this.operations.executeQuery(
1811
+ createSqlBuilder().select("*").from(fullTableName).where("1=1").build()
1812
+ );
1813
+ console.log("allDataResult", allDataResult);
1814
+ const countResult = await this.operations.executeQuery(countQuery.build());
1815
+ const total = Number(countResult?.[0]?.count ?? 0);
1816
+ dataQuery.orderBy("startTime", "DESC").limit(perPage).offset(page * perPage);
1817
+ const results = await this.operations.executeQuery(dataQuery.build());
1818
+ const traces = isArrayOfRecords2(results) ? results.map(
1819
+ (trace) => ({
1820
+ ...trace,
1821
+ attributes: deserializeValue(trace.attributes, "jsonb"),
1822
+ status: deserializeValue(trace.status, "jsonb"),
1823
+ events: deserializeValue(trace.events, "jsonb"),
1824
+ links: deserializeValue(trace.links, "jsonb"),
1825
+ other: deserializeValue(trace.other, "jsonb")
1826
+ })
1827
+ ) : [];
1828
+ return {
1829
+ traces,
1830
+ total,
1831
+ page,
1832
+ perPage,
1833
+ hasMore: page * perPage + traces.length < total
1834
+ };
1835
+ } catch (error) {
1836
+ const mastraError = new MastraError(
1837
+ {
1838
+ id: "CLOUDFLARE_D1_STORAGE_GET_TRACES_PAGINATED_ERROR",
1839
+ domain: ErrorDomain.STORAGE,
1840
+ category: ErrorCategory.THIRD_PARTY,
1841
+ text: `Failed to retrieve traces: ${error instanceof Error ? error.message : String(error)}`,
1842
+ details: { name: name ?? "", scope: scope ?? "" }
1843
+ },
1844
+ error
1845
+ );
1846
+ this.logger?.error(mastraError.toString());
1847
+ this.logger?.trackException(mastraError);
1848
+ return { traces: [], total: 0, page, perPage, hasMore: false };
1849
+ }
1850
+ }
1851
+ async batchTraceInsert({ records }) {
1852
+ this.logger.debug("Batch inserting traces", { count: records.length });
1853
+ await this.operations.batchInsert({
1854
+ tableName: TABLE_TRACES,
1855
+ records
1856
+ });
1857
+ }
1858
+ };
1859
+ var WorkflowsStorageD1 = class extends WorkflowsStorage {
1860
+ operations;
1861
+ constructor({ operations }) {
1862
+ super();
1863
+ this.operations = operations;
1864
+ }
1865
+ async persistWorkflowSnapshot({
1866
+ workflowName,
1867
+ runId,
1868
+ snapshot
1869
+ }) {
1870
+ const fullTableName = this.operations.getTableName(TABLE_WORKFLOW_SNAPSHOT);
1871
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1872
+ const currentSnapshot = await this.operations.load({
1873
+ tableName: TABLE_WORKFLOW_SNAPSHOT,
1874
+ keys: { workflow_name: workflowName, run_id: runId }
1875
+ });
1876
+ const persisting = currentSnapshot ? {
1877
+ ...currentSnapshot,
1878
+ snapshot: JSON.stringify(snapshot),
1879
+ updatedAt: now
1880
+ } : {
1881
+ workflow_name: workflowName,
1882
+ run_id: runId,
1883
+ snapshot,
1884
+ createdAt: now,
1885
+ updatedAt: now
1886
+ };
1887
+ const processedRecord = await this.operations.processRecord(persisting);
1888
+ const columns = Object.keys(processedRecord);
1889
+ const values = Object.values(processedRecord);
1890
+ const updateMap = {
1891
+ snapshot: "excluded.snapshot",
1892
+ updatedAt: "excluded.updatedAt"
1893
+ };
1894
+ this.logger.debug("Persisting workflow snapshot", { workflowName, runId });
1895
+ const query = createSqlBuilder().insert(fullTableName, columns, values, ["workflow_name", "run_id"], updateMap);
1896
+ const { sql, params } = query.build();
1897
+ try {
1898
+ await this.operations.executeQuery({ sql, params });
1899
+ } catch (error) {
1900
+ throw new MastraError(
1901
+ {
1902
+ id: "CLOUDFLARE_D1_STORAGE_PERSIST_WORKFLOW_SNAPSHOT_ERROR",
1903
+ domain: ErrorDomain.STORAGE,
1904
+ category: ErrorCategory.THIRD_PARTY,
1905
+ text: `Failed to persist workflow snapshot: ${error instanceof Error ? error.message : String(error)}`,
1906
+ details: { workflowName, runId }
1907
+ },
1908
+ error
1909
+ );
1910
+ }
1911
+ }
1912
+ async loadWorkflowSnapshot(params) {
1913
+ const { workflowName, runId } = params;
1914
+ this.logger.debug("Loading workflow snapshot", { workflowName, runId });
1915
+ try {
1916
+ const d = await this.operations.load({
1917
+ tableName: TABLE_WORKFLOW_SNAPSHOT,
1918
+ keys: {
1919
+ workflow_name: workflowName,
1920
+ run_id: runId
1921
+ }
1922
+ });
1923
+ return d ? d.snapshot : null;
1924
+ } catch (error) {
1925
+ throw new MastraError(
1926
+ {
1927
+ id: "CLOUDFLARE_D1_STORAGE_LOAD_WORKFLOW_SNAPSHOT_ERROR",
1928
+ domain: ErrorDomain.STORAGE,
1929
+ category: ErrorCategory.THIRD_PARTY,
1930
+ text: `Failed to load workflow snapshot: ${error instanceof Error ? error.message : String(error)}`,
1931
+ details: { workflowName, runId }
1932
+ },
1933
+ error
1934
+ );
1935
+ }
1936
+ }
1937
+ parseWorkflowRun(row) {
1938
+ let parsedSnapshot = row.snapshot;
1939
+ if (typeof parsedSnapshot === "string") {
1940
+ try {
1941
+ parsedSnapshot = JSON.parse(row.snapshot);
1223
1942
  } catch (e) {
1224
1943
  console.warn(`Failed to parse snapshot for workflow ${row.workflow_name}: ${e}`);
1225
1944
  }
@@ -1228,17 +1947,11 @@ var D1Store = class extends MastraStorage {
1228
1947
  workflowName: row.workflow_name,
1229
1948
  runId: row.run_id,
1230
1949
  snapshot: parsedSnapshot,
1231
- createdAt: this.ensureDate(row.createdAt),
1232
- updatedAt: this.ensureDate(row.updatedAt),
1950
+ createdAt: ensureDate(row.createdAt),
1951
+ updatedAt: ensureDate(row.updatedAt),
1233
1952
  resourceId: row.resourceId
1234
1953
  };
1235
1954
  }
1236
- async hasColumn(table, column) {
1237
- const sql = `PRAGMA table_info(${table});`;
1238
- const result = await this.executeQuery({ sql, params: [] });
1239
- if (!result || !Array.isArray(result)) return false;
1240
- return result.some((col) => col.name === column || col.name === column.toLowerCase());
1241
- }
1242
1955
  async getWorkflowRuns({
1243
1956
  workflowName,
1244
1957
  fromDate,
@@ -1247,13 +1960,13 @@ var D1Store = class extends MastraStorage {
1247
1960
  offset,
1248
1961
  resourceId
1249
1962
  } = {}) {
1250
- const fullTableName = this.getTableName(TABLE_WORKFLOW_SNAPSHOT);
1963
+ const fullTableName = this.operations.getTableName(TABLE_WORKFLOW_SNAPSHOT);
1251
1964
  try {
1252
1965
  const builder = createSqlBuilder().select().from(fullTableName);
1253
1966
  const countBuilder = createSqlBuilder().count().from(fullTableName);
1254
1967
  if (workflowName) builder.whereAnd("workflow_name = ?", workflowName);
1255
1968
  if (resourceId) {
1256
- const hasResourceId = await this.hasColumn(fullTableName, "resourceId");
1969
+ const hasResourceId = await this.operations.hasColumn(fullTableName, "resourceId");
1257
1970
  if (hasResourceId) {
1258
1971
  builder.whereAnd("resourceId = ?", resourceId);
1259
1972
  countBuilder.whereAnd("resourceId = ?", resourceId);
@@ -1276,24 +1989,37 @@ var D1Store = class extends MastraStorage {
1276
1989
  let total = 0;
1277
1990
  if (limit !== void 0 && offset !== void 0) {
1278
1991
  const { sql: countSql, params: countParams } = countBuilder.build();
1279
- const countResult = await this.executeQuery({ sql: countSql, params: countParams, first: true });
1992
+ const countResult = await this.operations.executeQuery({
1993
+ sql: countSql,
1994
+ params: countParams,
1995
+ first: true
1996
+ });
1280
1997
  total = Number(countResult?.count ?? 0);
1281
1998
  }
1282
- const results = await this.executeQuery({ sql, params });
1999
+ const results = await this.operations.executeQuery({ sql, params });
1283
2000
  const runs = (isArrayOfRecords(results) ? results : []).map((row) => this.parseWorkflowRun(row));
1284
2001
  return { runs, total: total || runs.length };
1285
2002
  } catch (error) {
1286
- this.logger.error("Error getting workflow runs:", {
1287
- message: error instanceof Error ? error.message : String(error)
1288
- });
1289
- throw error;
2003
+ throw new MastraError(
2004
+ {
2005
+ id: "CLOUDFLARE_D1_STORAGE_GET_WORKFLOW_RUNS_ERROR",
2006
+ domain: ErrorDomain.STORAGE,
2007
+ category: ErrorCategory.THIRD_PARTY,
2008
+ text: `Failed to retrieve workflow runs: ${error instanceof Error ? error.message : String(error)}`,
2009
+ details: {
2010
+ workflowName: workflowName ?? "",
2011
+ resourceId: resourceId ?? ""
2012
+ }
2013
+ },
2014
+ error
2015
+ );
1290
2016
  }
1291
2017
  }
1292
2018
  async getWorkflowRunById({
1293
2019
  runId,
1294
2020
  workflowName
1295
2021
  }) {
1296
- const fullTableName = this.getTableName(TABLE_WORKFLOW_SNAPSHOT);
2022
+ const fullTableName = this.operations.getTableName(TABLE_WORKFLOW_SNAPSHOT);
1297
2023
  try {
1298
2024
  const conditions = [];
1299
2025
  const params = [];
@@ -1307,15 +2033,293 @@ var D1Store = class extends MastraStorage {
1307
2033
  }
1308
2034
  const whereClause = conditions.length > 0 ? "WHERE " + conditions.join(" AND ") : "";
1309
2035
  const sql = `SELECT * FROM ${fullTableName} ${whereClause} ORDER BY createdAt DESC LIMIT 1`;
1310
- const result = await this.executeQuery({ sql, params, first: true });
2036
+ const result = await this.operations.executeQuery({ sql, params, first: true });
1311
2037
  if (!result) return null;
1312
2038
  return this.parseWorkflowRun(result);
1313
2039
  } catch (error) {
1314
- this.logger.error("Error getting workflow run by ID:", {
1315
- message: error instanceof Error ? error.message : String(error)
1316
- });
1317
- throw error;
2040
+ throw new MastraError(
2041
+ {
2042
+ id: "CLOUDFLARE_D1_STORAGE_GET_WORKFLOW_RUN_BY_ID_ERROR",
2043
+ domain: ErrorDomain.STORAGE,
2044
+ category: ErrorCategory.THIRD_PARTY,
2045
+ text: `Failed to retrieve workflow run by ID: ${error instanceof Error ? error.message : String(error)}`,
2046
+ details: { runId, workflowName: workflowName ?? "" }
2047
+ },
2048
+ error
2049
+ );
2050
+ }
2051
+ }
2052
+ };
2053
+
2054
+ // src/storage/index.ts
2055
+ var D1Store = class extends MastraStorage {
2056
+ client;
2057
+ binding;
2058
+ // D1Database binding
2059
+ tablePrefix;
2060
+ stores;
2061
+ /**
2062
+ * Creates a new D1Store instance
2063
+ * @param config Configuration for D1 access (either REST API or Workers Binding API)
2064
+ */
2065
+ constructor(config) {
2066
+ try {
2067
+ super({ name: "D1" });
2068
+ if (config.tablePrefix && !/^[a-zA-Z0-9_]*$/.test(config.tablePrefix)) {
2069
+ throw new Error("Invalid tablePrefix: only letters, numbers, and underscores are allowed.");
2070
+ }
2071
+ this.tablePrefix = config.tablePrefix || "";
2072
+ if ("binding" in config) {
2073
+ if (!config.binding) {
2074
+ throw new Error("D1 binding is required when using Workers Binding API");
2075
+ }
2076
+ this.binding = config.binding;
2077
+ this.logger.info("Using D1 Workers Binding API");
2078
+ } else if ("client" in config) {
2079
+ if (!config.client) {
2080
+ throw new Error("D1 client is required when using D1ClientConfig");
2081
+ }
2082
+ this.client = config.client;
2083
+ this.logger.info("Using D1 Client");
2084
+ } else {
2085
+ if (!config.accountId || !config.databaseId || !config.apiToken) {
2086
+ throw new Error("accountId, databaseId, and apiToken are required when using REST API");
2087
+ }
2088
+ const cfClient = new Cloudflare({
2089
+ apiToken: config.apiToken
2090
+ });
2091
+ this.client = {
2092
+ query: ({ sql, params }) => {
2093
+ return cfClient.d1.database.query(config.databaseId, {
2094
+ account_id: config.accountId,
2095
+ sql,
2096
+ params
2097
+ });
2098
+ }
2099
+ };
2100
+ this.logger.info("Using D1 REST API");
2101
+ }
2102
+ } catch (error) {
2103
+ throw new MastraError(
2104
+ {
2105
+ id: "CLOUDFLARE_D1_STORAGE_INITIALIZATION_ERROR",
2106
+ domain: ErrorDomain.STORAGE,
2107
+ category: ErrorCategory.SYSTEM,
2108
+ text: "Error initializing D1Store"
2109
+ },
2110
+ error
2111
+ );
1318
2112
  }
2113
+ const operations = new StoreOperationsD1({
2114
+ client: this.client,
2115
+ binding: this.binding,
2116
+ tablePrefix: this.tablePrefix
2117
+ });
2118
+ const scores = new ScoresStorageD1({
2119
+ operations
2120
+ });
2121
+ const legacyEvals = new LegacyEvalsStorageD1({
2122
+ operations
2123
+ });
2124
+ const traces = new TracesStorageD1({
2125
+ operations
2126
+ });
2127
+ const workflows = new WorkflowsStorageD1({
2128
+ operations
2129
+ });
2130
+ const memory = new MemoryStorageD1({
2131
+ operations
2132
+ });
2133
+ this.stores = {
2134
+ operations,
2135
+ scores,
2136
+ legacyEvals,
2137
+ traces,
2138
+ workflows,
2139
+ memory
2140
+ };
2141
+ }
2142
+ get supports() {
2143
+ return {
2144
+ selectByIncludeResourceScope: true,
2145
+ resourceWorkingMemory: true,
2146
+ hasColumn: true,
2147
+ createTable: true,
2148
+ deleteMessages: false
2149
+ };
2150
+ }
2151
+ async createTable({
2152
+ tableName,
2153
+ schema
2154
+ }) {
2155
+ return this.stores.operations.createTable({ tableName, schema });
2156
+ }
2157
+ /**
2158
+ * Alters table schema to add columns if they don't exist
2159
+ * @param tableName Name of the table
2160
+ * @param schema Schema of the table
2161
+ * @param ifNotExists Array of column names to add if they don't exist
2162
+ */
2163
+ async alterTable({
2164
+ tableName,
2165
+ schema,
2166
+ ifNotExists
2167
+ }) {
2168
+ return this.stores.operations.alterTable({ tableName, schema, ifNotExists });
2169
+ }
2170
+ async clearTable({ tableName }) {
2171
+ return this.stores.operations.clearTable({ tableName });
2172
+ }
2173
+ async dropTable({ tableName }) {
2174
+ return this.stores.operations.dropTable({ tableName });
2175
+ }
2176
+ async hasColumn(table, column) {
2177
+ return this.stores.operations.hasColumn(table, column);
2178
+ }
2179
+ async insert({ tableName, record }) {
2180
+ return this.stores.operations.insert({ tableName, record });
2181
+ }
2182
+ async load({ tableName, keys }) {
2183
+ return this.stores.operations.load({ tableName, keys });
2184
+ }
2185
+ async getThreadById({ threadId }) {
2186
+ return this.stores.memory.getThreadById({ threadId });
2187
+ }
2188
+ /**
2189
+ * @deprecated use getThreadsByResourceIdPaginated instead
2190
+ */
2191
+ async getThreadsByResourceId({ resourceId }) {
2192
+ return this.stores.memory.getThreadsByResourceId({ resourceId });
2193
+ }
2194
+ async getThreadsByResourceIdPaginated(args) {
2195
+ return this.stores.memory.getThreadsByResourceIdPaginated(args);
2196
+ }
2197
+ async saveThread({ thread }) {
2198
+ return this.stores.memory.saveThread({ thread });
2199
+ }
2200
+ async updateThread({
2201
+ id,
2202
+ title,
2203
+ metadata
2204
+ }) {
2205
+ return this.stores.memory.updateThread({ id, title, metadata });
2206
+ }
2207
+ async deleteThread({ threadId }) {
2208
+ return this.stores.memory.deleteThread({ threadId });
2209
+ }
2210
+ async saveMessages(args) {
2211
+ return this.stores.memory.saveMessages(args);
2212
+ }
2213
+ async getMessages({
2214
+ threadId,
2215
+ selectBy,
2216
+ format
2217
+ }) {
2218
+ return this.stores.memory.getMessages({ threadId, selectBy, format });
2219
+ }
2220
+ async getMessagesPaginated({
2221
+ threadId,
2222
+ selectBy,
2223
+ format
2224
+ }) {
2225
+ return this.stores.memory.getMessagesPaginated({ threadId, selectBy, format });
2226
+ }
2227
+ async persistWorkflowSnapshot({
2228
+ workflowName,
2229
+ runId,
2230
+ snapshot
2231
+ }) {
2232
+ return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, snapshot });
2233
+ }
2234
+ async loadWorkflowSnapshot(params) {
2235
+ return this.stores.workflows.loadWorkflowSnapshot(params);
2236
+ }
2237
+ async getWorkflowRuns({
2238
+ workflowName,
2239
+ fromDate,
2240
+ toDate,
2241
+ limit,
2242
+ offset,
2243
+ resourceId
2244
+ } = {}) {
2245
+ return this.stores.workflows.getWorkflowRuns({ workflowName, fromDate, toDate, limit, offset, resourceId });
2246
+ }
2247
+ async getWorkflowRunById({
2248
+ runId,
2249
+ workflowName
2250
+ }) {
2251
+ return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
2252
+ }
2253
+ /**
2254
+ * Insert multiple records in a batch operation
2255
+ * @param tableName The table to insert into
2256
+ * @param records The records to insert
2257
+ */
2258
+ async batchInsert({ tableName, records }) {
2259
+ return this.stores.operations.batchInsert({ tableName, records });
2260
+ }
2261
+ /**
2262
+ * @deprecated use getTracesPaginated instead
2263
+ */
2264
+ async getTraces(args) {
2265
+ return this.stores.traces.getTraces(args);
2266
+ }
2267
+ async getTracesPaginated(args) {
2268
+ return this.stores.traces.getTracesPaginated(args);
2269
+ }
2270
+ /**
2271
+ * @deprecated use getEvals instead
2272
+ */
2273
+ async getEvalsByAgentName(agentName, type) {
2274
+ return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
2275
+ }
2276
+ async getEvals(options) {
2277
+ return this.stores.legacyEvals.getEvals(options);
2278
+ }
2279
+ async updateMessages(_args) {
2280
+ return this.stores.memory.updateMessages(_args);
2281
+ }
2282
+ async getResourceById({ resourceId }) {
2283
+ return this.stores.memory.getResourceById({ resourceId });
2284
+ }
2285
+ async saveResource({ resource }) {
2286
+ return this.stores.memory.saveResource({ resource });
2287
+ }
2288
+ async updateResource({
2289
+ resourceId,
2290
+ workingMemory,
2291
+ metadata
2292
+ }) {
2293
+ return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
2294
+ }
2295
+ async getScoreById({ id: _id }) {
2296
+ return this.stores.scores.getScoreById({ id: _id });
2297
+ }
2298
+ async saveScore(_score) {
2299
+ return this.stores.scores.saveScore(_score);
2300
+ }
2301
+ async getScoresByRunId({
2302
+ runId: _runId,
2303
+ pagination: _pagination
2304
+ }) {
2305
+ return this.stores.scores.getScoresByRunId({ runId: _runId, pagination: _pagination });
2306
+ }
2307
+ async getScoresByEntityId({
2308
+ entityId: _entityId,
2309
+ entityType: _entityType,
2310
+ pagination: _pagination
2311
+ }) {
2312
+ return this.stores.scores.getScoresByEntityId({
2313
+ entityId: _entityId,
2314
+ entityType: _entityType,
2315
+ pagination: _pagination
2316
+ });
2317
+ }
2318
+ async getScoresByScorerId({
2319
+ scorerId: _scorerId,
2320
+ pagination: _pagination
2321
+ }) {
2322
+ return this.stores.scores.getScoresByScorerId({ scorerId: _scorerId, pagination: _pagination });
1319
2323
  }
1320
2324
  /**
1321
2325
  * Close the database connection
@@ -1324,10 +2328,8 @@ var D1Store = class extends MastraStorage {
1324
2328
  async close() {
1325
2329
  this.logger.debug("Closing D1 connection");
1326
2330
  }
1327
- async updateMessages(_args) {
1328
- this.logger.error("updateMessages is not yet implemented in CloudflareD1Store");
1329
- throw new Error("Method not implemented");
1330
- }
1331
2331
  };
1332
2332
 
1333
2333
  export { D1Store };
2334
+ //# sourceMappingURL=index.js.map
2335
+ //# sourceMappingURL=index.js.map