@mastra/cloudflare-d1 1.0.0-beta.1 → 1.0.0-beta.11

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