@mastra/cloudflare-d1 0.0.0-remove-unused-model-providers-api-20251030210744 → 0.0.0-remove-ai-peer-dep-from-evals-20260105220639

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