@mastra/cloudflare-d1 1.0.0-beta.0 → 1.0.0-beta.10

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