@mastra/cloudflare-d1 1.0.0-beta.1 → 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,1127 +253,1201 @@ 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);
682
- }
683
- return processedMsg;
684
- });
685
- return processedMessages;
624
+ return processed;
686
625
  }
687
- async listMessagesById({ messageIds }) {
688
- if (messageIds.length === 0) return { messages: [] };
689
- const fullTableName = this.operations.getTableName(TABLE_MESSAGES);
690
- const messages = [];
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);
691
634
  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);
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
+ }
702
659
  }
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() };
660
+ this.logger.debug(
661
+ `Processed batch ${Math.floor(i / batchSize) + 1} of ${Math.ceil(records.length / batchSize)}`
662
+ );
663
+ }
664
+ this.logger.debug(`Successfully batch upserted ${records.length} records into ${tableName}`);
708
665
  } catch (error) {
709
- const mastraError = new MastraError(
666
+ throw new MastraError(
710
667
  {
711
- id: "CLOUDFLARE_D1_STORAGE_LIST_MESSAGES_BY_ID_ERROR",
668
+ id: createStorageErrorId("CLOUDFLARE_D1", "BATCH_UPSERT", "FAILED"),
712
669
  domain: ErrorDomain.STORAGE,
713
670
  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) }
671
+ text: `Failed to batch upsert into ${tableName}: ${error instanceof Error ? error.message : String(error)}`,
672
+ details: { tableName }
716
673
  },
717
674
  error
718
675
  );
719
- this.logger?.error(mastraError.toString());
720
- this.logger?.trackException(mastraError);
721
- throw mastraError;
722
676
  }
723
677
  }
724
- async listMessages(args) {
725
- const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
726
- if (!threadId.trim()) {
727
- throw new MastraError(
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"]
695
+ });
696
+ }
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;
708
+ try {
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
+ };
715
+ } catch (error) {
716
+ const mastraError = new MastraError(
728
717
  {
729
- id: "STORAGE_CLOUDFLARE_D1_LIST_MESSAGES_INVALID_THREAD_ID",
718
+ id: createStorageErrorId("CLOUDFLARE_D1", "GET_RESOURCE_BY_ID", "FAILED"),
730
719
  domain: ErrorDomain.STORAGE,
731
720
  category: ErrorCategory.THIRD_PARTY,
732
- details: { threadId }
733
- },
734
- new Error("threadId must be a non-empty string")
735
- );
736
- }
737
- if (page < 0) {
738
- throw new MastraError(
739
- {
740
- id: "STORAGE_CLOUDFLARE_D1_LIST_MESSAGES_INVALID_PAGE",
741
- domain: ErrorDomain.STORAGE,
742
- category: ErrorCategory.USER,
743
- details: { page }
721
+ text: `Error processing resource ${resourceId}: ${error instanceof Error ? error.message : String(error)}`,
722
+ details: { resourceId }
744
723
  },
745
- new Error("page must be >= 0")
724
+ error
746
725
  );
726
+ this.logger?.error(mastraError.toString());
727
+ this.logger?.trackException(mastraError);
728
+ return null;
747
729
  }
748
- const perPage = normalizePerPage(perPageInput, 40);
749
- const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
730
+ }
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();
750
751
  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;
847
- return {
848
- messages: finalMessages,
849
- total,
850
- page,
851
- perPage: perPageForResponse,
852
- hasMore
853
- };
752
+ await this.#db.executeQuery({ sql, params });
753
+ return resource;
854
754
  } catch (error) {
855
- const mastraError = new MastraError(
755
+ throw new MastraError(
856
756
  {
857
- id: "CLOUDFLARE_D1_STORAGE_LIST_MESSAGES_ERROR",
757
+ id: createStorageErrorId("CLOUDFLARE_D1", "SAVE_RESOURCE", "FAILED"),
858
758
  domain: ErrorDomain.STORAGE,
859
759
  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
- }
760
+ text: `Failed to save resource to ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
761
+ details: { resourceId: resource.id }
865
762
  },
866
763
  error
867
764
  );
868
- this.logger?.error?.(mastraError.toString());
869
- this.logger?.trackException?.(mastraError);
870
- return {
871
- messages: [],
872
- total: 0,
873
- page,
874
- perPage: perPageForResponse,
875
- hasMore: false
876
- };
877
765
  }
878
766
  }
879
- async updateMessages(args) {
880
- const { messages } = args;
881
- this.logger.debug("Updating messages", { count: messages.length });
882
- if (!messages.length) {
883
- return [];
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 });
884
782
  }
885
- const messageIds = messages.map((m) => m.id);
886
- const fullTableName = this.operations.getTableName(TABLE_MESSAGES);
887
- const threadsTableName = this.operations.getTableName(TABLE_THREADS);
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();
888
798
  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
- });
799
+ await this.#db.executeQuery({ sql, params });
800
+ return updatedResource;
967
801
  } catch (error) {
968
802
  throw new MastraError(
969
803
  {
970
- id: "CLOUDFLARE_D1_STORAGE_UPDATE_MESSAGES_FAILED",
804
+ id: createStorageErrorId("CLOUDFLARE_D1", "UPDATE_RESOURCE", "FAILED"),
971
805
  domain: ErrorDomain.STORAGE,
972
806
  category: ErrorCategory.THIRD_PARTY,
973
- details: { count: messages.length }
807
+ text: `Failed to update resource ${resourceId}: ${error instanceof Error ? error.message : String(error)}`,
808
+ details: { resourceId }
974
809
  },
975
810
  error
976
811
  );
977
812
  }
978
813
  }
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
1007
- }) {
1008
- if (!this.binding) {
1009
- throw new Error("Workers binding is not configured");
1010
- }
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;
1011
820
  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
- }
1035
- }
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
+ };
1036
827
  } catch (error) {
1037
- throw new MastraError(
828
+ const mastraError = new MastraError(
1038
829
  {
1039
- id: "CLOUDFLARE_D1_STORE_OPERATIONS_WORKERS_BINDING_QUERY_FAILED",
830
+ id: createStorageErrorId("CLOUDFLARE_D1", "GET_THREAD_BY_ID", "FAILED"),
1040
831
  domain: ErrorDomain.STORAGE,
1041
832
  category: ErrorCategory.THIRD_PARTY,
1042
- details: { sql }
833
+ text: `Error processing thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`,
834
+ details: { threadId }
1043
835
  },
1044
836
  error
1045
837
  );
838
+ this.logger?.error(mastraError.toString());
839
+ this.logger?.trackException(mastraError);
840
+ return null;
1046
841
  }
1047
842
  }
1048
- async executeRestQuery({
1049
- sql,
1050
- params = [],
1051
- first = false
1052
- }) {
1053
- if (!this.client) {
1054
- throw new Error("D1 client is not configured");
843
+ async listThreadsByResourceId(args) {
844
+ const { resourceId, page = 0, perPage: perPageInput, orderBy } = args;
845
+ const perPage = normalizePerPage(perPageInput, 100);
846
+ if (page < 0) {
847
+ throw new MastraError(
848
+ {
849
+ id: createStorageErrorId("CLOUDFLARE_D1", "LIST_THREADS_BY_RESOURCE_ID", "INVALID_PAGE"),
850
+ domain: ErrorDomain.STORAGE,
851
+ category: ErrorCategory.USER,
852
+ details: { page }
853
+ },
854
+ new Error("page must be >= 0")
855
+ );
1055
856
  }
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
+ });
1056
866
  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;
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);
874
+ return {
875
+ threads,
876
+ total,
877
+ page,
878
+ perPage: perPageForResponse,
879
+ hasMore: perPageInput === false ? false : offset + perPage < total
880
+ };
1068
881
  } catch (error) {
1069
- throw new MastraError(
882
+ const mastraError = new MastraError(
1070
883
  {
1071
- id: "CLOUDFLARE_D1_STORE_OPERATIONS_REST_QUERY_FAILED",
884
+ id: createStorageErrorId("CLOUDFLARE_D1", "LIST_THREADS_BY_RESOURCE_ID", "FAILED"),
1072
885
  domain: ErrorDomain.STORAGE,
1073
886
  category: ErrorCategory.THIRD_PARTY,
1074
- details: { sql }
887
+ text: `Error getting threads by resourceId ${resourceId}: ${error instanceof Error ? error.message : String(error)}`,
888
+ details: { resourceId }
1075
889
  },
1076
890
  error
1077
891
  );
892
+ this.logger?.error(mastraError.toString());
893
+ this.logger?.trackException(mastraError);
894
+ return {
895
+ threads: [],
896
+ total: 0,
897
+ page,
898
+ perPage: perPageForResponse,
899
+ hasMore: false
900
+ };
1078
901
  }
1079
902
  }
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) {
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();
1090
925
  try {
1091
- const sql = `PRAGMA table_info(${tableName})`;
1092
- const result = await this.executeQuery({ sql });
1093
- if (!result || !Array.isArray(result)) {
1094
- return [];
1095
- }
1096
- return result.map((row) => ({
1097
- name: row.name,
1098
- type: row.type
1099
- }));
926
+ await this.#db.executeQuery({ sql, params });
927
+ return thread;
1100
928
  } 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);
1114
- }
1115
- return value;
1116
- }
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);
929
+ throw new MastraError(
930
+ {
931
+ id: createStorageErrorId("CLOUDFLARE_D1", "SAVE_THREAD", "FAILED"),
932
+ domain: ErrorDomain.STORAGE,
933
+ category: ErrorCategory.THIRD_PARTY,
934
+ text: `Failed to save thread to ${fullTableName}: ${error instanceof Error ? error.message : String(error)}`,
935
+ details: { threadId: thread.id }
936
+ },
937
+ error
938
+ );
1127
939
  }
1128
940
  }
1129
- async createTable({
1130
- tableName,
1131
- schema
941
+ async updateThread({
942
+ id,
943
+ title,
944
+ metadata
1132
945
  }) {
946
+ const thread = await this.getThreadById({ threadId: id });
1133
947
  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)");
948
+ if (!thread) {
949
+ throw new Error(`Thread ${id} not found`);
1144
950
  }
1145
- const query = createSqlBuilder().createTable(fullTableName, columnDefinitions, tableConstraints);
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);
1146
960
  const { sql, params } = query.build();
1147
- await this.executeQuery({ sql, params });
1148
- this.logger.debug(`Created table ${fullTableName}`);
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
+ };
1149
971
  } catch (error) {
1150
972
  throw new MastraError(
1151
973
  {
1152
- id: "CLOUDFLARE_D1_STORE_OPERATIONS_CREATE_TABLE_FAILED",
974
+ id: createStorageErrorId("CLOUDFLARE_D1", "UPDATE_THREAD", "FAILED"),
1153
975
  domain: ErrorDomain.STORAGE,
1154
976
  category: ErrorCategory.THIRD_PARTY,
1155
- details: { tableName }
977
+ text: `Failed to update thread ${id}: ${error instanceof Error ? error.message : String(error)}`,
978
+ details: { threadId: id }
1156
979
  },
1157
980
  error
1158
981
  );
1159
982
  }
1160
983
  }
1161
- async clearTable({ tableName }) {
984
+ async deleteThread({ threadId }) {
985
+ const fullTableName = this.#db.getTableName(TABLE_THREADS);
1162
986
  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}`);
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 });
1168
994
  } catch (error) {
1169
995
  throw new MastraError(
1170
996
  {
1171
- id: "CLOUDFLARE_D1_STORE_OPERATIONS_CLEAR_TABLE_FAILED",
997
+ id: createStorageErrorId("CLOUDFLARE_D1", "DELETE_THREAD", "FAILED"),
1172
998
  domain: ErrorDomain.STORAGE,
1173
999
  category: ErrorCategory.THIRD_PARTY,
1174
- details: { tableName }
1000
+ text: `Failed to delete thread ${threadId}: ${error instanceof Error ? error.message : String(error)}`,
1001
+ details: { threadId }
1175
1002
  },
1176
1003
  error
1177
1004
  );
1178
1005
  }
1179
1006
  }
1180
- async dropTable({ tableName }) {
1007
+ async saveMessages(args) {
1008
+ const { messages } = args;
1009
+ if (messages.length === 0) return { messages: [] };
1181
1010
  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}`);
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
+ }
1031
+ }
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() };
1186
1058
  } catch (error) {
1187
1059
  throw new MastraError(
1188
1060
  {
1189
- id: "CLOUDFLARE_D1_STORE_OPERATIONS_DROP_TABLE_FAILED",
1061
+ id: createStorageErrorId("CLOUDFLARE_D1", "SAVE_MESSAGES", "FAILED"),
1190
1062
  domain: ErrorDomain.STORAGE,
1191
1063
  category: ErrorCategory.THIRD_PARTY,
1192
- details: { tableName }
1064
+ text: `Failed to save messages: ${error instanceof Error ? error.message : String(error)}`
1193
1065
  },
1194
1066
  error
1195
1067
  );
1196
1068
  }
1197
1069
  }
1198
- async alterTable(args) {
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++;
1113
+ }
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);
1124
+ }
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 = [];
1199
1133
  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}`);
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");
1136
+ const { sql, params } = query.build();
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);
1210
1144
  }
1211
- }
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() };
1212
1150
  } catch (error) {
1213
- throw new MastraError(
1151
+ const mastraError = new MastraError(
1214
1152
  {
1215
- id: "CLOUDFLARE_D1_STORE_OPERATIONS_ALTER_TABLE_FAILED",
1153
+ id: createStorageErrorId("CLOUDFLARE_D1", "LIST_MESSAGES_BY_ID", "FAILED"),
1216
1154
  domain: ErrorDomain.STORAGE,
1217
1155
  category: ErrorCategory.THIRD_PARTY,
1218
- details: { tableName: args.tableName }
1156
+ text: `Failed to retrieve messages by ID: ${error instanceof Error ? error.message : String(error)}`,
1157
+ details: { messageIds: JSON.stringify(messageIds) }
1219
1158
  },
1220
1159
  error
1221
1160
  );
1161
+ this.logger?.error(mastraError.toString());
1162
+ this.logger?.trackException(mastraError);
1163
+ throw mastraError;
1222
1164
  }
1223
1165
  }
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) {
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())) {
1234
1170
  throw new MastraError(
1235
1171
  {
1236
- id: "CLOUDFLARE_D1_STORE_OPERATIONS_INSERT_FAILED",
1172
+ id: createStorageErrorId("CLOUDFLARE_D1", "LIST_MESSAGES", "INVALID_THREAD_ID"),
1237
1173
  domain: ErrorDomain.STORAGE,
1238
1174
  category: ErrorCategory.THIRD_PARTY,
1239
- details: { tableName }
1175
+ details: { threadId: Array.isArray(threadId) ? threadId.join(",") : threadId }
1240
1176
  },
1241
- error
1177
+ new Error("threadId must be a non-empty string or array of non-empty strings")
1242
1178
  );
1243
1179
  }
1244
- }
1245
- async batchInsert({ tableName, records }) {
1180
+ if (page < 0) {
1181
+ throw new MastraError(
1182
+ {
1183
+ id: createStorageErrorId("CLOUDFLARE_D1", "LIST_MESSAGES", "INVALID_PAGE"),
1184
+ domain: ErrorDomain.STORAGE,
1185
+ category: ErrorCategory.USER,
1186
+ details: { page }
1187
+ },
1188
+ new Error("page must be >= 0")
1189
+ );
1190
+ }
1191
+ const perPage = normalizePerPage(perPageInput, 40);
1192
+ const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
1246
1193
  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 });
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
+ }
1275
+ }
1256
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
+ };
1257
1301
  } catch (error) {
1258
- throw new MastraError(
1302
+ const mastraError = new MastraError(
1259
1303
  {
1260
- id: "CLOUDFLARE_D1_STORE_OPERATIONS_BATCH_INSERT_FAILED",
1304
+ id: createStorageErrorId("CLOUDFLARE_D1", "LIST_MESSAGES", "FAILED"),
1261
1305
  domain: ErrorDomain.STORAGE,
1262
1306
  category: ErrorCategory.THIRD_PARTY,
1263
- details: { 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
+ }
1264
1312
  },
1265
1313
  error
1266
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
+ };
1267
1324
  }
1268
1325
  }
1269
- async load({ tableName, keys }) {
1326
+ async updateMessages(args) {
1327
+ const { messages } = args;
1328
+ this.logger.debug("Updating messages", { count: messages.length });
1329
+ if (!messages.length) {
1330
+ return [];
1331
+ }
1332
+ const messageIds = messages.map((m) => m.id);
1333
+ const fullTableName = this.#db.getTableName(TABLE_MESSAGES);
1334
+ const threadsTableName = this.#db.getTableName(TABLE_THREADS);
1270
1335
  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);
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 [];
1341
+ }
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 {
@@ -1801,7 +1906,7 @@ var WorkflowsStorageD1 = class extends WorkflowsStorage {
1801
1906
  resourceId,
1802
1907
  status
1803
1908
  } = {}) {
1804
- const fullTableName = this.operations.getTableName(TABLE_WORKFLOW_SNAPSHOT);
1909
+ const fullTableName = this.#db.getTableName(TABLE_WORKFLOW_SNAPSHOT);
1805
1910
  try {
1806
1911
  const builder = createSqlBuilder().select().from(fullTableName);
1807
1912
  const countBuilder = createSqlBuilder().count().from(fullTableName);
@@ -1811,12 +1916,12 @@ var WorkflowsStorageD1 = class extends WorkflowsStorage {
1811
1916
  countBuilder.whereAnd("json_extract(snapshot, '$.status') = ?", status);
1812
1917
  }
1813
1918
  if (resourceId) {
1814
- const hasResourceId = await this.operations.hasColumn(fullTableName, "resourceId");
1919
+ const hasResourceId = await this.#db.hasColumn(fullTableName, "resourceId");
1815
1920
  if (hasResourceId) {
1816
1921
  builder.whereAnd("resourceId = ?", resourceId);
1817
1922
  countBuilder.whereAnd("resourceId = ?", resourceId);
1818
1923
  } else {
1819
- console.warn(`[${fullTableName}] resourceId column not found. Skipping resourceId filter.`);
1924
+ this.logger.warn(`[${fullTableName}] resourceId column not found. Skipping resourceId filter.`);
1820
1925
  }
1821
1926
  }
1822
1927
  if (fromDate) {
@@ -1837,20 +1942,20 @@ var WorkflowsStorageD1 = class extends WorkflowsStorage {
1837
1942
  let total = 0;
1838
1943
  if (perPage !== void 0 && page !== void 0) {
1839
1944
  const { sql: countSql, params: countParams } = countBuilder.build();
1840
- const countResult = await this.operations.executeQuery({
1945
+ const countResult = await this.#db.executeQuery({
1841
1946
  sql: countSql,
1842
1947
  params: countParams,
1843
1948
  first: true
1844
1949
  });
1845
1950
  total = Number(countResult?.count ?? 0);
1846
1951
  }
1847
- const results = await this.operations.executeQuery({ sql, params });
1952
+ const results = await this.#db.executeQuery({ sql, params });
1848
1953
  const runs = (isArrayOfRecords(results) ? results : []).map((row) => this.parseWorkflowRun(row));
1849
1954
  return { runs, total: total || runs.length };
1850
1955
  } catch (error) {
1851
1956
  throw new MastraError(
1852
1957
  {
1853
- id: "CLOUDFLARE_D1_STORAGE_LIST_WORKFLOW_RUNS_ERROR",
1958
+ id: createStorageErrorId("CLOUDFLARE_D1", "LIST_WORKFLOW_RUNS", "FAILED"),
1854
1959
  domain: ErrorDomain.STORAGE,
1855
1960
  category: ErrorCategory.THIRD_PARTY,
1856
1961
  text: `Failed to retrieve workflow runs: ${error instanceof Error ? error.message : String(error)}`,
@@ -1867,7 +1972,7 @@ var WorkflowsStorageD1 = class extends WorkflowsStorage {
1867
1972
  runId,
1868
1973
  workflowName
1869
1974
  }) {
1870
- const fullTableName = this.operations.getTableName(TABLE_WORKFLOW_SNAPSHOT);
1975
+ const fullTableName = this.#db.getTableName(TABLE_WORKFLOW_SNAPSHOT);
1871
1976
  try {
1872
1977
  const conditions = [];
1873
1978
  const params = [];
@@ -1881,13 +1986,13 @@ var WorkflowsStorageD1 = class extends WorkflowsStorage {
1881
1986
  }
1882
1987
  const whereClause = conditions.length > 0 ? "WHERE " + conditions.join(" AND ") : "";
1883
1988
  const sql = `SELECT * FROM ${fullTableName} ${whereClause} ORDER BY createdAt DESC LIMIT 1`;
1884
- const result = await this.operations.executeQuery({ sql, params, first: true });
1989
+ const result = await this.#db.executeQuery({ sql, params, first: true });
1885
1990
  if (!result) return null;
1886
1991
  return this.parseWorkflowRun(result);
1887
1992
  } catch (error) {
1888
1993
  throw new MastraError(
1889
1994
  {
1890
- id: "CLOUDFLARE_D1_STORAGE_GET_WORKFLOW_RUN_BY_ID_ERROR",
1995
+ id: createStorageErrorId("CLOUDFLARE_D1", "GET_WORKFLOW_RUN_BY_ID", "FAILED"),
1891
1996
  domain: ErrorDomain.STORAGE,
1892
1997
  category: ErrorCategory.THIRD_PARTY,
1893
1998
  text: `Failed to retrieve workflow run by ID: ${error instanceof Error ? error.message : String(error)}`,
@@ -1897,13 +2002,31 @@ var WorkflowsStorageD1 = class extends WorkflowsStorage {
1897
2002
  );
1898
2003
  }
1899
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
+ }
1900
2024
  };
1901
2025
 
1902
2026
  // src/storage/index.ts
1903
2027
  var D1Store = class extends MastraStorage {
1904
2028
  client;
1905
2029
  binding;
1906
- // D1Database binding
1907
2030
  tablePrefix;
1908
2031
  stores;
1909
2032
  /**
@@ -1912,7 +2035,7 @@ var D1Store = class extends MastraStorage {
1912
2035
  */
1913
2036
  constructor(config) {
1914
2037
  try {
1915
- super({ id: config.id, name: "D1" });
2038
+ super({ id: config.id, name: "D1", disableInit: config.disableInit });
1916
2039
  if (config.tablePrefix && !/^[a-zA-Z0-9_]*$/.test(config.tablePrefix)) {
1917
2040
  throw new Error("Invalid tablePrefix: only letters, numbers, and underscores are allowed.");
1918
2041
  }
@@ -1950,7 +2073,7 @@ var D1Store = class extends MastraStorage {
1950
2073
  } catch (error) {
1951
2074
  throw new MastraError(
1952
2075
  {
1953
- id: "CLOUDFLARE_D1_STORAGE_INITIALIZATION_ERROR",
2076
+ id: createStorageErrorId("CLOUDFLARE_D1", "INITIALIZATION", "FAILED"),
1954
2077
  domain: ErrorDomain.STORAGE,
1955
2078
  category: ErrorCategory.SYSTEM,
1956
2079
  text: "Error initializing D1Store"
@@ -1958,189 +2081,26 @@ var D1Store = class extends MastraStorage {
1958
2081
  error
1959
2082
  );
1960
2083
  }
1961
- const operations = new StoreOperationsD1({
1962
- client: this.client,
1963
- binding: this.binding,
1964
- tablePrefix: this.tablePrefix
1965
- });
1966
- const scores = new ScoresStorageD1({
1967
- operations
1968
- });
1969
- const workflows = new WorkflowsStorageD1({
1970
- operations
1971
- });
1972
- const memory = new MemoryStorageD1({
1973
- operations
1974
- });
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
+ }
1975
2098
  this.stores = {
1976
- operations,
1977
2099
  scores,
1978
2100
  workflows,
1979
2101
  memory
1980
2102
  };
1981
2103
  }
1982
- get supports() {
1983
- return {
1984
- selectByIncludeResourceScope: true,
1985
- resourceWorkingMemory: true,
1986
- hasColumn: true,
1987
- createTable: true,
1988
- deleteMessages: false,
1989
- listScoresBySpan: true
1990
- };
1991
- }
1992
- async createTable({
1993
- tableName,
1994
- schema
1995
- }) {
1996
- return this.stores.operations.createTable({ tableName, schema });
1997
- }
1998
- /**
1999
- * Alters table schema to add columns if they don't exist
2000
- * @param tableName Name of the table
2001
- * @param schema Schema of the table
2002
- * @param ifNotExists Array of column names to add if they don't exist
2003
- */
2004
- async alterTable({
2005
- tableName,
2006
- schema,
2007
- ifNotExists
2008
- }) {
2009
- return this.stores.operations.alterTable({ tableName, schema, ifNotExists });
2010
- }
2011
- async clearTable({ tableName }) {
2012
- return this.stores.operations.clearTable({ tableName });
2013
- }
2014
- async dropTable({ tableName }) {
2015
- return this.stores.operations.dropTable({ tableName });
2016
- }
2017
- async hasColumn(table, column) {
2018
- return this.stores.operations.hasColumn(table, column);
2019
- }
2020
- async insert({ tableName, record }) {
2021
- return this.stores.operations.insert({ tableName, record });
2022
- }
2023
- async load({ tableName, keys }) {
2024
- return this.stores.operations.load({ tableName, keys });
2025
- }
2026
- async getThreadById({ threadId }) {
2027
- return this.stores.memory.getThreadById({ threadId });
2028
- }
2029
- async saveThread({ thread }) {
2030
- return this.stores.memory.saveThread({ thread });
2031
- }
2032
- async updateThread({
2033
- id,
2034
- title,
2035
- metadata
2036
- }) {
2037
- return this.stores.memory.updateThread({ id, title, metadata });
2038
- }
2039
- async deleteThread({ threadId }) {
2040
- return this.stores.memory.deleteThread({ threadId });
2041
- }
2042
- async saveMessages(args) {
2043
- return this.stores.memory.saveMessages(args);
2044
- }
2045
- async updateWorkflowResults({
2046
- workflowName,
2047
- runId,
2048
- stepId,
2049
- result,
2050
- requestContext
2051
- }) {
2052
- return this.stores.workflows.updateWorkflowResults({ workflowName, runId, stepId, result, requestContext });
2053
- }
2054
- async updateWorkflowState({
2055
- workflowName,
2056
- runId,
2057
- opts
2058
- }) {
2059
- return this.stores.workflows.updateWorkflowState({ workflowName, runId, opts });
2060
- }
2061
- async persistWorkflowSnapshot({
2062
- workflowName,
2063
- runId,
2064
- resourceId,
2065
- snapshot
2066
- }) {
2067
- return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot });
2068
- }
2069
- async loadWorkflowSnapshot(params) {
2070
- return this.stores.workflows.loadWorkflowSnapshot(params);
2071
- }
2072
- async listWorkflowRuns(args = {}) {
2073
- return this.stores.workflows.listWorkflowRuns(args);
2074
- }
2075
- async getWorkflowRunById({
2076
- runId,
2077
- workflowName
2078
- }) {
2079
- return this.stores.workflows.getWorkflowRunById({ runId, workflowName });
2080
- }
2081
- /**
2082
- * Insert multiple records in a batch operation
2083
- * @param tableName The table to insert into
2084
- * @param records The records to insert
2085
- */
2086
- async batchInsert({ tableName, records }) {
2087
- return this.stores.operations.batchInsert({ tableName, records });
2088
- }
2089
- async updateMessages(_args) {
2090
- return this.stores.memory.updateMessages(_args);
2091
- }
2092
- async getResourceById({ resourceId }) {
2093
- return this.stores.memory.getResourceById({ resourceId });
2094
- }
2095
- async saveResource({ resource }) {
2096
- return this.stores.memory.saveResource({ resource });
2097
- }
2098
- async updateResource({
2099
- resourceId,
2100
- workingMemory,
2101
- metadata
2102
- }) {
2103
- return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
2104
- }
2105
- async getScoreById({ id: _id }) {
2106
- return this.stores.scores.getScoreById({ id: _id });
2107
- }
2108
- async saveScore(_score) {
2109
- return this.stores.scores.saveScore(_score);
2110
- }
2111
- async listScoresByRunId({
2112
- runId: _runId,
2113
- pagination: _pagination
2114
- }) {
2115
- return this.stores.scores.listScoresByRunId({ runId: _runId, pagination: _pagination });
2116
- }
2117
- async listScoresByEntityId({
2118
- entityId: _entityId,
2119
- entityType: _entityType,
2120
- pagination: _pagination
2121
- }) {
2122
- return this.stores.scores.listScoresByEntityId({
2123
- entityId: _entityId,
2124
- entityType: _entityType,
2125
- pagination: _pagination
2126
- });
2127
- }
2128
- async listScoresByScorerId({
2129
- scorerId,
2130
- pagination,
2131
- entityId,
2132
- entityType,
2133
- source
2134
- }) {
2135
- return this.stores.scores.listScoresByScorerId({ scorerId, pagination, entityId, entityType, source });
2136
- }
2137
- async listScoresBySpan({
2138
- traceId,
2139
- spanId,
2140
- pagination
2141
- }) {
2142
- return this.stores.scores.listScoresBySpan({ traceId, spanId, pagination });
2143
- }
2144
2104
  /**
2145
2105
  * Close the database connection
2146
2106
  * No explicit cleanup needed for D1 in either REST or Workers Binding mode
@@ -2150,6 +2110,6 @@ var D1Store = class extends MastraStorage {
2150
2110
  }
2151
2111
  };
2152
2112
 
2153
- export { D1Store };
2113
+ export { D1Store, MemoryStorageD1, ScoresStorageD1, WorkflowsStorageD1 };
2154
2114
  //# sourceMappingURL=index.js.map
2155
2115
  //# sourceMappingURL=index.js.map