@mastra/dynamodb 0.0.0-taofeeqInngest-20250603090617 → 0.0.0-transpile-packages-20250724123433

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.
@@ -1,18 +1,35 @@
1
1
  import { DynamoDBClient, DescribeTableCommand } from '@aws-sdk/client-dynamodb';
2
2
  import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
3
- import type { StorageThreadType, WorkflowRunState, MastraMessageV1, MastraMessageV2 } from '@mastra/core';
4
- import { MessageList } from '@mastra/core/agent';
5
- import {
6
- MastraStorage,
7
- TABLE_THREADS,
8
- TABLE_MESSAGES,
9
- TABLE_WORKFLOW_SNAPSHOT,
10
- TABLE_EVALS,
11
- TABLE_TRACES,
3
+ import type { MastraMessageContentV2 } from '@mastra/core/agent';
4
+ import { ErrorCategory, ErrorDomain, MastraError } from '@mastra/core/error';
5
+ import type { StorageThreadType, MastraMessageV2, MastraMessageV1 } from '@mastra/core/memory';
6
+
7
+ import type { ScoreRowData } from '@mastra/core/scores';
8
+ import { MastraStorage } from '@mastra/core/storage';
9
+ import type {
10
+ EvalRow,
11
+ StorageGetMessagesArg,
12
+ WorkflowRun,
13
+ WorkflowRuns,
14
+ TABLE_NAMES,
15
+ StorageGetTracesArg,
16
+ PaginationInfo,
17
+ StorageColumn,
18
+ StoragePagination,
19
+ StorageDomains,
20
+ PaginationArgs,
21
+ StorageResourceType,
12
22
  } from '@mastra/core/storage';
13
- import type { EvalRow, StorageGetMessagesArg, WorkflowRun, WorkflowRuns, TABLE_NAMES } from '@mastra/core/storage';
23
+ import type { Trace } from '@mastra/core/telemetry';
24
+ import type { WorkflowRunState } from '@mastra/core/workflows';
14
25
  import type { Service } from 'electrodb';
15
26
  import { getElectroDbService } from '../entities';
27
+ import { LegacyEvalsDynamoDB } from './domains/legacy-evals';
28
+ import { MemoryStorageDynamoDB } from './domains/memory';
29
+ import { StoreOperationsDynamoDB } from './domains/operations';
30
+ import { ScoresStorageDynamoDB } from './domains/score';
31
+ import { TracesStorageDynamoDB } from './domains/traces';
32
+ import { WorkflowStorageDynamoDB } from './domains/workflows';
16
33
 
17
34
  export interface DynamoDBStoreConfig {
18
35
  region?: string;
@@ -29,79 +46,82 @@ type MastraService = Service<Record<string, any>> & {
29
46
  [key: string]: any;
30
47
  };
31
48
 
32
- // Define the structure for workflow snapshot items retrieved from DynamoDB
33
- interface WorkflowSnapshotDBItem {
34
- entity: string; // Typically 'workflow_snapshot'
35
- workflow_name: string;
36
- run_id: string;
37
- snapshot: WorkflowRunState; // Should be WorkflowRunState after ElectroDB get attribute processing
38
- createdAt: string; // ISO Date string
39
- updatedAt: string; // ISO Date string
40
- resourceId?: string;
41
- }
42
-
43
49
  export class DynamoDBStore extends MastraStorage {
44
50
  private tableName: string;
45
51
  private client: DynamoDBDocumentClient;
46
52
  private service: MastraService;
47
53
  protected hasInitialized: Promise<boolean> | null = null;
54
+ stores: StorageDomains;
48
55
 
49
56
  constructor({ name, config }: { name: string; config: DynamoDBStoreConfig }) {
50
57
  super({ name });
51
58
 
52
59
  // Validate required config
53
- if (!config.tableName || typeof config.tableName !== 'string' || config.tableName.trim() === '') {
54
- throw new Error('DynamoDBStore: config.tableName must be provided and cannot be empty.');
55
- }
56
- // Validate tableName characters (basic check)
57
- if (!/^[a-zA-Z0-9_.-]{3,255}$/.test(config.tableName)) {
58
- throw new Error(
59
- `DynamoDBStore: config.tableName "${config.tableName}" contains invalid characters or is not between 3 and 255 characters long.`,
60
- );
61
- }
60
+ try {
61
+ if (!config.tableName || typeof config.tableName !== 'string' || config.tableName.trim() === '') {
62
+ throw new Error('DynamoDBStore: config.tableName must be provided and cannot be empty.');
63
+ }
64
+ // Validate tableName characters (basic check)
65
+ if (!/^[a-zA-Z0-9_.-]{3,255}$/.test(config.tableName)) {
66
+ throw new Error(
67
+ `DynamoDBStore: config.tableName "${config.tableName}" contains invalid characters or is not between 3 and 255 characters long.`,
68
+ );
69
+ }
62
70
 
63
- const dynamoClient = new DynamoDBClient({
64
- region: config.region || 'us-east-1',
65
- endpoint: config.endpoint,
66
- credentials: config.credentials,
67
- });
71
+ const dynamoClient = new DynamoDBClient({
72
+ region: config.region || 'us-east-1',
73
+ endpoint: config.endpoint,
74
+ credentials: config.credentials,
75
+ });
68
76
 
69
- this.tableName = config.tableName;
70
- this.client = DynamoDBDocumentClient.from(dynamoClient);
71
- this.service = getElectroDbService(this.client, this.tableName) as MastraService;
77
+ this.tableName = config.tableName;
78
+ this.client = DynamoDBDocumentClient.from(dynamoClient);
79
+ this.service = getElectroDbService(this.client, this.tableName) as MastraService;
72
80
 
73
- // We're using a single table design with ElectroDB,
74
- // so we don't need to create multiple tables
75
- }
81
+ const operations = new StoreOperationsDynamoDB({
82
+ service: this.service,
83
+ tableName: this.tableName,
84
+ client: this.client,
85
+ });
76
86
 
77
- /**
78
- * This method is modified for DynamoDB with ElectroDB single-table design.
79
- * It assumes the table is created and managed externally via CDK/CloudFormation.
80
- *
81
- * This implementation only validates that the required table exists and is accessible.
82
- * No table creation is attempted - we simply check if we can access the table.
83
- */
84
- async createTable({ tableName }: { tableName: TABLE_NAMES; schema: Record<string, any> }): Promise<void> {
85
- this.logger.debug('Validating access to externally managed table', { tableName, physicalTable: this.tableName });
87
+ const traces = new TracesStorageDynamoDB({ service: this.service, operations });
86
88
 
87
- // For single-table design, we just need to verify the table exists and is accessible
88
- try {
89
- const tableExists = await this.validateTableExists();
89
+ const workflows = new WorkflowStorageDynamoDB({ service: this.service });
90
90
 
91
- if (!tableExists) {
92
- this.logger.error(
93
- `Table ${this.tableName} does not exist or is not accessible. It should be created via CDK/CloudFormation.`,
94
- );
95
- throw new Error(
96
- `Table ${this.tableName} does not exist or is not accessible. Ensure it's created via CDK/CloudFormation before using this store.`,
97
- );
98
- }
91
+ const memory = new MemoryStorageDynamoDB({ service: this.service });
92
+
93
+ const scores = new ScoresStorageDynamoDB({ service: this.service });
99
94
 
100
- this.logger.debug(`Table ${this.tableName} exists and is accessible`);
95
+ this.stores = {
96
+ operations,
97
+ legacyEvals: new LegacyEvalsDynamoDB({ service: this.service, tableName: this.tableName }),
98
+ traces,
99
+ workflows,
100
+ memory,
101
+ scores,
102
+ };
101
103
  } catch (error) {
102
- this.logger.error('Error validating table access', { tableName: this.tableName, error });
103
- throw error;
104
+ throw new MastraError(
105
+ {
106
+ id: 'STORAGE_DYNAMODB_STORE_CONSTRUCTOR_FAILED',
107
+ domain: ErrorDomain.STORAGE,
108
+ category: ErrorCategory.USER,
109
+ },
110
+ error,
111
+ );
104
112
  }
113
+
114
+ // We're using a single table design with ElectroDB,
115
+ // so we don't need to create multiple tables
116
+ }
117
+
118
+ get supports() {
119
+ return {
120
+ selectByIncludeResourceScope: true,
121
+ resourceWorkingMemory: true,
122
+ hasColumn: false,
123
+ createTable: false,
124
+ };
105
125
  }
106
126
 
107
127
  /**
@@ -126,7 +146,15 @@ export class DynamoDBStore extends MastraStorage {
126
146
  }
127
147
 
128
148
  // For other errors (like permissions issues), we should throw
129
- throw error;
149
+ throw new MastraError(
150
+ {
151
+ id: 'STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_EXISTS_FAILED',
152
+ domain: ErrorDomain.STORAGE,
153
+ category: ErrorCategory.THIRD_PARTY,
154
+ details: { tableName: this.tableName },
155
+ },
156
+ error,
157
+ );
130
158
  }
131
159
  }
132
160
 
@@ -153,7 +181,15 @@ export class DynamoDBStore extends MastraStorage {
153
181
  // The error has already been handled by _performInitializationAndStore
154
182
  // (i.e., this.hasInitialized was reset). Re-throwing here ensures
155
183
  // the caller of init() is aware of the failure.
156
- throw error;
184
+ throw new MastraError(
185
+ {
186
+ id: 'STORAGE_DYNAMODB_STORE_INIT_FAILED',
187
+ domain: ErrorDomain.STORAGE,
188
+ category: ErrorCategory.THIRD_PARTY,
189
+ details: { tableName: this.tableName },
190
+ },
191
+ error,
192
+ );
157
193
  }
158
194
  }
159
195
 
@@ -180,265 +216,49 @@ export class DynamoDBStore extends MastraStorage {
180
216
  });
181
217
  }
182
218
 
183
- /**
184
- * Clear all items from a logical "table" (entity type)
185
- */
186
- async clearTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
187
- this.logger.debug('DynamoDB clearTable called', { tableName });
188
-
189
- const entityName = this.getEntityNameForTable(tableName);
190
- if (!entityName || !this.service.entities[entityName]) {
191
- throw new Error(`No entity defined for ${tableName}`);
192
- }
193
-
194
- try {
195
- // Scan requires no key, just uses the entity handler
196
- const result = await this.service.entities[entityName].scan.go({ pages: 'all' }); // Get all pages
197
-
198
- if (!result.data.length) {
199
- this.logger.debug(`No records found to clear for ${tableName}`);
200
- return;
201
- }
202
-
203
- this.logger.debug(`Found ${result.data.length} records to delete for ${tableName}`);
204
-
205
- // ElectroDB batch delete expects the key components for each item
206
- const keysToDelete = result.data.map((item: any) => {
207
- const key: { entity: string; [key: string]: any } = { entity: entityName };
208
-
209
- // Construct the key based on the specific entity's primary key structure
210
- switch (entityName) {
211
- case 'thread':
212
- if (!item.id) throw new Error(`Missing required key 'id' for entity 'thread'`);
213
- key.id = item.id;
214
- break;
215
- case 'message':
216
- if (!item.id) throw new Error(`Missing required key 'id' for entity 'message'`);
217
- key.id = item.id;
218
- break;
219
- case 'workflowSnapshot':
220
- if (!item.workflow_name)
221
- throw new Error(`Missing required key 'workflow_name' for entity 'workflowSnapshot'`);
222
- if (!item.run_id) throw new Error(`Missing required key 'run_id' for entity 'workflowSnapshot'`);
223
- key.workflow_name = item.workflow_name;
224
- key.run_id = item.run_id;
225
- break;
226
- case 'eval':
227
- // Assuming 'eval' uses 'run_id' or another unique identifier as part of its PK
228
- // Adjust based on the actual primary key defined in getElectroDbService
229
- if (!item.run_id) throw new Error(`Missing required key 'run_id' for entity 'eval'`);
230
- // Add other key components if necessary for 'eval' PK
231
- key.run_id = item.run_id;
232
- // Example: if global_run_id is also part of PK:
233
- // if (!item.global_run_id) throw new Error(`Missing required key 'global_run_id' for entity 'eval'`);
234
- // key.global_run_id = item.global_run_id;
235
- break;
236
- case 'trace':
237
- // Assuming 'trace' uses 'id' as its PK
238
- // Adjust based on the actual primary key defined in getElectroDbService
239
- if (!item.id) throw new Error(`Missing required key 'id' for entity 'trace'`);
240
- key.id = item.id;
241
- break;
242
- default:
243
- // Handle unknown entity types - log a warning or throw an error
244
- this.logger.warn(`Unknown entity type encountered during clearTable: ${entityName}`);
245
- // Optionally throw an error if strict handling is required
246
- throw new Error(`Cannot construct delete key for unknown entity type: ${entityName}`);
247
- }
219
+ async createTable({ tableName, schema }: { tableName: TABLE_NAMES; schema: Record<string, any> }): Promise<void> {
220
+ return this.stores.operations.createTable({ tableName, schema });
221
+ }
248
222
 
249
- return key;
250
- });
223
+ async alterTable(_args: {
224
+ tableName: TABLE_NAMES;
225
+ schema: Record<string, StorageColumn>;
226
+ ifNotExists: string[];
227
+ }): Promise<void> {
228
+ return this.stores.operations.alterTable(_args);
229
+ }
251
230
 
252
- const batchSize = 25;
253
- for (let i = 0; i < keysToDelete.length; i += batchSize) {
254
- const batchKeys = keysToDelete.slice(i, i + batchSize);
255
- // Pass the array of key objects to delete
256
- await this.service.entities[entityName].delete(batchKeys).go();
257
- }
231
+ async clearTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
232
+ return this.stores.operations.clearTable({ tableName });
233
+ }
258
234
 
259
- this.logger.debug(`Successfully cleared all records for ${tableName}`);
260
- } catch (error) {
261
- this.logger.error('Failed to clear table', { tableName, error });
262
- throw error;
263
- }
235
+ async dropTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
236
+ return this.stores.operations.dropTable({ tableName });
264
237
  }
265
238
 
266
- /**
267
- * Insert a record into the specified "table" (entity)
268
- */
269
239
  async insert({ tableName, record }: { tableName: TABLE_NAMES; record: Record<string, any> }): Promise<void> {
270
- this.logger.debug('DynamoDB insert called', { tableName });
271
-
272
- const entityName = this.getEntityNameForTable(tableName);
273
- if (!entityName || !this.service.entities[entityName]) {
274
- throw new Error(`No entity defined for ${tableName}`);
275
- }
276
-
277
- try {
278
- // Add the entity type to the record before creating
279
- const dataToSave = { entity: entityName, ...record };
280
- await this.service.entities[entityName].create(dataToSave).go();
281
- } catch (error) {
282
- this.logger.error('Failed to insert record', { tableName, error });
283
- throw error;
284
- }
240
+ return this.stores.operations.insert({ tableName, record });
285
241
  }
286
242
 
287
- /**
288
- * Insert multiple records as a batch
289
- */
290
243
  async batchInsert({ tableName, records }: { tableName: TABLE_NAMES; records: Record<string, any>[] }): Promise<void> {
291
- this.logger.debug('DynamoDB batchInsert called', { tableName, count: records.length });
292
-
293
- const entityName = this.getEntityNameForTable(tableName);
294
- if (!entityName || !this.service.entities[entityName]) {
295
- throw new Error(`No entity defined for ${tableName}`);
296
- }
297
-
298
- // Add entity type to each record
299
- const recordsToSave = records.map(rec => ({ entity: entityName, ...rec }));
300
-
301
- // ElectroDB has batch limits of 25 items, so we need to chunk
302
- const batchSize = 25;
303
- const batches = [];
304
- for (let i = 0; i < recordsToSave.length; i += batchSize) {
305
- const batch = recordsToSave.slice(i, i + batchSize);
306
- batches.push(batch);
307
- }
308
-
309
- try {
310
- // Process each batch
311
- for (const batch of batches) {
312
- // Create each item individually within the batch
313
- for (const recordData of batch) {
314
- if (!recordData.entity) {
315
- this.logger.error('Missing entity property in record data for batchInsert', { recordData, tableName });
316
- throw new Error(`Internal error: Missing entity property during batchInsert for ${tableName}`);
317
- }
318
- // Log the object just before the create call
319
- this.logger.debug('Attempting to create record in batchInsert:', { entityName, recordData });
320
- await this.service.entities[entityName].create(recordData).go();
321
- }
322
- // Original batch call: await this.service.entities[entityName].create(batch).go();
323
- }
324
- } catch (error) {
325
- this.logger.error('Failed to batch insert records', { tableName, error });
326
- throw error;
327
- }
244
+ return this.stores.operations.batchInsert({ tableName, records });
328
245
  }
329
246
 
330
- /**
331
- * Load a record by its keys
332
- */
333
247
  async load<R>({ tableName, keys }: { tableName: TABLE_NAMES; keys: Record<string, string> }): Promise<R | null> {
334
- this.logger.debug('DynamoDB load called', { tableName, keys });
335
-
336
- const entityName = this.getEntityNameForTable(tableName);
337
- if (!entityName || !this.service.entities[entityName]) {
338
- throw new Error(`No entity defined for ${tableName}`);
339
- }
340
-
341
- try {
342
- // Add the entity type to the key object for the .get call
343
- const keyObject = { entity: entityName, ...keys };
344
- const result = await this.service.entities[entityName].get(keyObject).go();
345
-
346
- if (!result.data) {
347
- return null;
348
- }
349
-
350
- // Add parsing logic if necessary (e.g., for metadata)
351
- let data = result.data;
352
- if (data.metadata && typeof data.metadata === 'string') {
353
- try {
354
- // data.metadata = JSON.parse(data.metadata); // REMOVED by AI
355
- } catch {
356
- /* ignore parse error */
357
- }
358
- }
359
- // Add similar parsing for other JSON fields if needed based on entity type
360
-
361
- return data as R;
362
- } catch (error) {
363
- this.logger.error('Failed to load record', { tableName, keys, error });
364
- throw error;
365
- }
248
+ return this.stores.operations.load({ tableName, keys });
366
249
  }
367
250
 
368
251
  // Thread operations
369
252
  async getThreadById({ threadId }: { threadId: string }): Promise<StorageThreadType | null> {
370
- this.logger.debug('Getting thread by ID', { threadId });
371
- try {
372
- const result = await this.service.entities.thread.get({ entity: 'thread', id: threadId }).go();
373
-
374
- if (!result.data) {
375
- return null;
376
- }
377
-
378
- // ElectroDB handles the transformation with attribute getters
379
- const data = result.data;
380
- return {
381
- ...data,
382
- // metadata: data.metadata ? JSON.parse(data.metadata) : undefined, // REMOVED by AI
383
- // metadata is already transformed by the entity's getter
384
- } as StorageThreadType;
385
- } catch (error) {
386
- this.logger.error('Failed to get thread by ID', { threadId, error });
387
- throw error;
388
- }
253
+ return this.stores.memory.getThreadById({ threadId });
389
254
  }
390
255
 
391
256
  async getThreadsByResourceId({ resourceId }: { resourceId: string }): Promise<StorageThreadType[]> {
392
- this.logger.debug('Getting threads by resource ID', { resourceId });
393
- try {
394
- const result = await this.service.entities.thread.query.byResource({ entity: 'thread', resourceId }).go();
395
-
396
- if (!result.data.length) {
397
- return [];
398
- }
399
-
400
- // ElectroDB handles the transformation with attribute getters
401
- return result.data.map((data: any) => ({
402
- ...data,
403
- // metadata: data.metadata ? JSON.parse(data.metadata) : undefined, // REMOVED by AI
404
- // metadata is already transformed by the entity's getter
405
- })) as StorageThreadType[];
406
- } catch (error) {
407
- this.logger.error('Failed to get threads by resource ID', { resourceId, error });
408
- throw error;
409
- }
257
+ return this.stores.memory.getThreadsByResourceId({ resourceId });
410
258
  }
411
259
 
412
260
  async saveThread({ thread }: { thread: StorageThreadType }): Promise<StorageThreadType> {
413
- this.logger.debug('Saving thread', { threadId: thread.id });
414
-
415
- const now = new Date();
416
-
417
- const threadData = {
418
- entity: 'thread',
419
- id: thread.id,
420
- resourceId: thread.resourceId,
421
- title: thread.title || `Thread ${thread.id}`,
422
- createdAt: thread.createdAt?.toISOString() || now.toISOString(),
423
- updatedAt: now.toISOString(),
424
- metadata: thread.metadata ? JSON.stringify(thread.metadata) : undefined,
425
- };
426
-
427
- try {
428
- await this.service.entities.thread.create(threadData).go();
429
-
430
- return {
431
- id: thread.id,
432
- resourceId: thread.resourceId,
433
- title: threadData.title,
434
- createdAt: thread.createdAt || now,
435
- updatedAt: now,
436
- metadata: thread.metadata,
437
- };
438
- } catch (error) {
439
- this.logger.error('Failed to save thread', { threadId: thread.id, error });
440
- throw error;
441
- }
261
+ return this.stores.memory.saveThread({ thread });
442
262
  }
443
263
 
444
264
  async updateThread({
@@ -450,68 +270,11 @@ export class DynamoDBStore extends MastraStorage {
450
270
  title: string;
451
271
  metadata: Record<string, unknown>;
452
272
  }): Promise<StorageThreadType> {
453
- this.logger.debug('Updating thread', { threadId: id });
454
-
455
- try {
456
- // First, get the existing thread to merge with updates
457
- const existingThread = await this.getThreadById({ threadId: id });
458
-
459
- if (!existingThread) {
460
- throw new Error(`Thread not found: ${id}`);
461
- }
462
-
463
- const now = new Date();
464
-
465
- // Prepare the update
466
- // Define type for only the fields we are actually updating
467
- type ThreadUpdatePayload = {
468
- updatedAt: string; // ISO String for DDB
469
- title?: string;
470
- metadata?: string; // Stringified JSON for DDB
471
- };
472
- const updateData: ThreadUpdatePayload = {
473
- updatedAt: now.toISOString(),
474
- };
475
-
476
- if (title) {
477
- updateData.title = title;
478
- }
479
-
480
- if (metadata) {
481
- updateData.metadata = JSON.stringify(metadata); // Stringify metadata for update
482
- }
483
-
484
- // Update the thread using the primary key
485
- await this.service.entities.thread.update({ entity: 'thread', id }).set(updateData).go();
486
-
487
- // Return the potentially updated thread object
488
- return {
489
- ...existingThread,
490
- title: title || existingThread.title,
491
- metadata: metadata || existingThread.metadata,
492
- updatedAt: now,
493
- };
494
- } catch (error) {
495
- this.logger.error('Failed to update thread', { threadId: id, error });
496
- throw error;
497
- }
273
+ return this.stores.memory.updateThread({ id, title, metadata });
498
274
  }
499
275
 
500
276
  async deleteThread({ threadId }: { threadId: string }): Promise<void> {
501
- this.logger.debug('Deleting thread', { threadId });
502
-
503
- try {
504
- // Delete the thread using the primary key
505
- await this.service.entities.thread.delete({ entity: 'thread', id: threadId }).go();
506
-
507
- // Note: In a production system, you might want to:
508
- // 1. Delete all messages associated with this thread
509
- // 2. Delete any vector embeddings related to this thread
510
- // These would be additional operations
511
- } catch (error) {
512
- this.logger.error('Failed to delete thread', { threadId, error });
513
- throw error;
514
- }
277
+ return this.stores.memory.deleteThread({ threadId });
515
278
  }
516
279
 
517
280
  // Message operations
@@ -523,119 +286,39 @@ export class DynamoDBStore extends MastraStorage {
523
286
  selectBy,
524
287
  format,
525
288
  }: StorageGetMessagesArg & { format?: 'v1' | 'v2' }): Promise<MastraMessageV1[] | MastraMessageV2[]> {
526
- this.logger.debug('Getting messages', { threadId, selectBy });
527
-
528
- try {
529
- // Query messages by thread ID using the GSI
530
- // Provide *all* composite key components for the 'byThread' index ('entity', 'threadId')
531
- const query = this.service.entities.message.query.byThread({ entity: 'message', threadId });
532
-
533
- // Apply the 'last' limit if provided
534
- if (selectBy?.last && typeof selectBy.last === 'number') {
535
- // Use ElectroDB's limit parameter (descending sort assumed on GSI SK)
536
- // Ensure GSI sk (createdAt) is sorted descending for 'last' to work correctly
537
- // Assuming default sort is ascending on SK, use reverse: true for descending
538
- const results = await query.go({ limit: selectBy.last, reverse: true });
539
- // Use arrow function in map to preserve 'this' context for parseMessageData
540
- const list = new MessageList({ threadId, resourceId }).add(
541
- results.data.map((data: any) => this.parseMessageData(data)),
542
- 'memory',
543
- );
544
- if (format === `v2`) return list.get.all.v2();
545
- return list.get.all.v1();
546
- }
547
-
548
- // If no limit specified, get all messages (potentially paginated by ElectroDB)
549
- // Consider adding default limit or handling pagination if needed
550
- const results = await query.go();
551
- const list = new MessageList({ threadId, resourceId }).add(
552
- results.data.map((data: any) => this.parseMessageData(data)),
553
- 'memory',
554
- );
555
- if (format === `v2`) return list.get.all.v2();
556
- return list.get.all.v1();
557
- } catch (error) {
558
- this.logger.error('Failed to get messages', { threadId, error });
559
- throw error;
560
- }
289
+ return this.stores.memory.getMessages({ threadId, resourceId, selectBy, format });
561
290
  }
291
+
562
292
  async saveMessages(args: { messages: MastraMessageV1[]; format?: undefined | 'v1' }): Promise<MastraMessageV1[]>;
563
293
  async saveMessages(args: { messages: MastraMessageV2[]; format: 'v2' }): Promise<MastraMessageV2[]>;
564
294
  async saveMessages(
565
295
  args: { messages: MastraMessageV1[]; format?: undefined | 'v1' } | { messages: MastraMessageV2[]; format: 'v2' },
566
296
  ): Promise<MastraMessageV2[] | MastraMessageV1[]> {
567
- const { messages, format = 'v1' } = args;
568
- this.logger.debug('Saving messages', { count: messages.length });
569
-
570
- if (!messages.length) {
571
- return [];
572
- }
573
-
574
- // Ensure 'entity' is added and complex fields are handled
575
- const messagesToSave = messages.map(msg => {
576
- const now = new Date().toISOString();
577
- return {
578
- entity: 'message', // Add entity type
579
- id: msg.id,
580
- threadId: msg.threadId,
581
- role: msg.role,
582
- type: msg.type,
583
- resourceId: msg.resourceId,
584
- // Ensure complex fields are stringified if not handled by attribute setters
585
- content: typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content),
586
- toolCallArgs: `toolCallArgs` in msg && msg.toolCallArgs ? JSON.stringify(msg.toolCallArgs) : undefined,
587
- toolCallIds: `toolCallIds` in msg && msg.toolCallIds ? JSON.stringify(msg.toolCallIds) : undefined,
588
- toolNames: `toolNames` in msg && msg.toolNames ? JSON.stringify(msg.toolNames) : undefined,
589
- createdAt: msg.createdAt instanceof Date ? msg.createdAt.toISOString() : msg.createdAt || now,
590
- updatedAt: now, // Add updatedAt
591
- };
592
- });
593
-
594
- try {
595
- // Process messages in batch
596
- const batchSize = 25; // DynamoDB batch limits
597
- const batches = [];
598
-
599
- for (let i = 0; i < messagesToSave.length; i += batchSize) {
600
- const batch = messagesToSave.slice(i, i + batchSize);
601
- batches.push(batch);
602
- }
297
+ return this.stores.memory.saveMessages(args);
298
+ }
603
299
 
604
- // Process each batch
605
- for (const batch of batches) {
606
- // Try creating each item individually instead of passing the whole batch
607
- for (const messageData of batch) {
608
- // Ensure each item has the entity property before sending
609
- if (!messageData.entity) {
610
- this.logger.error('Missing entity property in message data for create', { messageData });
611
- throw new Error('Internal error: Missing entity property during saveMessages');
612
- }
613
- await this.service.entities.message.create(messageData).go();
614
- }
615
- // Original batch call: await this.service.entities.message.create(batch).go();
616
- }
300
+ async getThreadsByResourceIdPaginated(args: {
301
+ resourceId: string;
302
+ page: number;
303
+ perPage: number;
304
+ }): Promise<PaginationInfo & { threads: StorageThreadType[] }> {
305
+ return this.stores.memory.getThreadsByResourceIdPaginated(args);
306
+ }
617
307
 
618
- const list = new MessageList().add(messages, 'memory');
619
- if (format === `v1`) return list.get.all.v1();
620
- return list.get.all.v2();
621
- } catch (error) {
622
- this.logger.error('Failed to save messages', { error });
623
- throw error;
624
- }
308
+ async getMessagesPaginated(
309
+ args: StorageGetMessagesArg & { format?: 'v1' | 'v2' },
310
+ ): Promise<PaginationInfo & { messages: MastraMessageV1[] | MastraMessageV2[] }> {
311
+ return this.stores.memory.getMessagesPaginated(args);
625
312
  }
626
313
 
627
- // Helper function to parse message data (handle JSON fields)
628
- private parseMessageData(data: any): MastraMessageV2 | MastraMessageV1 {
629
- // Removed try/catch and JSON.parse logic - now handled by entity 'get' attributes
630
- // This function now primarily ensures correct typing and Date conversion.
631
- return {
632
- ...data,
633
- // Ensure dates are Date objects if needed (ElectroDB might return strings)
634
- createdAt: data.createdAt ? new Date(data.createdAt) : undefined,
635
- updatedAt: data.updatedAt ? new Date(data.updatedAt) : undefined,
636
- // Other fields like content, toolCallArgs etc. are assumed to be correctly
637
- // transformed by the ElectroDB entity getters.
638
- };
314
+ async updateMessages(_args: {
315
+ messages: Partial<Omit<MastraMessageV2, 'createdAt'>> &
316
+ {
317
+ id: string;
318
+ content?: { metadata?: MastraMessageContentV2['metadata']; content?: MastraMessageContentV2['content'] };
319
+ }[];
320
+ }): Promise<MastraMessageV2[]> {
321
+ return this.stores.memory.updateMessages(_args);
639
322
  }
640
323
 
641
324
  // Trace operations
@@ -647,66 +330,15 @@ export class DynamoDBStore extends MastraStorage {
647
330
  attributes?: Record<string, string>;
648
331
  filters?: Record<string, any>;
649
332
  }): Promise<any[]> {
650
- const { name, scope, page, perPage } = args;
651
- this.logger.debug('Getting traces', { name, scope, page, perPage });
652
-
653
- try {
654
- let query;
655
-
656
- // Determine which index to use based on the provided filters
657
- // Provide *all* composite key components for the relevant index
658
- if (name) {
659
- query = this.service.entities.trace.query.byName({ entity: 'trace', name });
660
- } else if (scope) {
661
- query = this.service.entities.trace.query.byScope({ entity: 'trace', scope });
662
- } else {
663
- this.logger.warn('Performing a scan operation on traces - consider using a more specific query');
664
- query = this.service.entities.trace.scan;
665
- }
666
-
667
- let items: any[] = [];
668
- let cursor = null;
669
- let pagesFetched = 0;
670
- const startPage = page > 0 ? page : 1;
671
-
672
- do {
673
- const results: { data: any[]; cursor: string | null } = await query.go({ cursor, limit: perPage });
674
- pagesFetched++;
675
- if (pagesFetched === startPage) {
676
- items = results.data;
677
- break;
678
- }
679
- cursor = results.cursor;
680
- if (!cursor && results.data.length > 0 && pagesFetched < startPage) {
681
- break;
682
- }
683
- } while (cursor && pagesFetched < startPage);
684
-
685
- return items;
686
- } catch (error) {
687
- this.logger.error('Failed to get traces', { error });
688
- throw error;
689
- }
333
+ return this.stores.traces.getTraces(args);
690
334
  }
691
335
 
692
336
  async batchTraceInsert({ records }: { records: Record<string, any>[] }): Promise<void> {
693
- this.logger.debug('Batch inserting traces', { count: records.length });
694
-
695
- if (!records.length) {
696
- return;
697
- }
337
+ return this.stores.traces.batchTraceInsert({ records });
338
+ }
698
339
 
699
- try {
700
- // Add 'entity' type to each record before passing to generic batchInsert
701
- const recordsToSave = records.map(rec => ({ entity: 'trace', ...rec }));
702
- await this.batchInsert({
703
- tableName: TABLE_TRACES,
704
- records: recordsToSave, // Pass records with 'entity' included
705
- });
706
- } catch (error) {
707
- this.logger.error('Failed to batch insert traces', { error });
708
- throw error;
709
- }
340
+ async getTracesPaginated(_args: StorageGetTracesArg): Promise<PaginationInfo & { traces: Trace[] }> {
341
+ return this.stores.traces.getTracesPaginated(_args);
710
342
  }
711
343
 
712
344
  // Workflow operations
@@ -719,27 +351,7 @@ export class DynamoDBStore extends MastraStorage {
719
351
  runId: string;
720
352
  snapshot: WorkflowRunState;
721
353
  }): Promise<void> {
722
- this.logger.debug('Persisting workflow snapshot', { workflowName, runId });
723
-
724
- try {
725
- const resourceId = 'resourceId' in snapshot ? snapshot.resourceId : undefined;
726
- const now = new Date().toISOString();
727
- // Prepare data including the 'entity' type
728
- const data = {
729
- entity: 'workflow_snapshot', // Add entity type
730
- workflow_name: workflowName,
731
- run_id: runId,
732
- snapshot: JSON.stringify(snapshot), // Stringify the snapshot object
733
- createdAt: now,
734
- updatedAt: now,
735
- resourceId,
736
- };
737
- // Pass the data including 'entity'
738
- await this.service.entities.workflowSnapshot.create(data).go();
739
- } catch (error) {
740
- this.logger.error('Failed to persist workflow snapshot', { workflowName, runId, error });
741
- throw error;
742
- }
354
+ return this.stores.workflows.persistWorkflowSnapshot({ workflowName, runId, snapshot });
743
355
  }
744
356
 
745
357
  async loadWorkflowSnapshot({
@@ -749,29 +361,7 @@ export class DynamoDBStore extends MastraStorage {
749
361
  workflowName: string;
750
362
  runId: string;
751
363
  }): Promise<WorkflowRunState | null> {
752
- this.logger.debug('Loading workflow snapshot', { workflowName, runId });
753
-
754
- try {
755
- // Provide *all* composite key components for the primary index ('entity', 'workflow_name', 'run_id')
756
- const result = await this.service.entities.workflowSnapshot
757
- .get({
758
- entity: 'workflow_snapshot', // Add entity type
759
- workflow_name: workflowName,
760
- run_id: runId,
761
- })
762
- .go();
763
-
764
- if (!result.data?.snapshot) {
765
- // Check snapshot exists
766
- return null;
767
- }
768
-
769
- // Parse the snapshot string
770
- return result.data.snapshot as WorkflowRunState;
771
- } catch (error) {
772
- this.logger.error('Failed to load workflow snapshot', { workflowName, runId, error });
773
- throw error;
774
- }
364
+ return this.stores.workflows.loadWorkflowSnapshot({ workflowName, runId });
775
365
  }
776
366
 
777
367
  async getWorkflowRuns(args?: {
@@ -782,262 +372,45 @@ export class DynamoDBStore extends MastraStorage {
782
372
  offset?: number;
783
373
  resourceId?: string;
784
374
  }): Promise<WorkflowRuns> {
785
- this.logger.debug('Getting workflow runs', { args });
786
-
787
- try {
788
- // Default values
789
- const limit = args?.limit || 10;
790
- const offset = args?.offset || 0;
791
-
792
- let query;
793
-
794
- if (args?.workflowName) {
795
- // Query by workflow name using the primary index
796
- // Provide *all* composite key components for the PK ('entity', 'workflow_name')
797
- query = this.service.entities.workflowSnapshot.query.primary({
798
- entity: 'workflow_snapshot', // Add entity type
799
- workflow_name: args.workflowName,
800
- });
801
- } else {
802
- // If no workflow name, we need to scan
803
- // This is not ideal for production with large datasets
804
- this.logger.warn('Performing a scan operation on workflow snapshots - consider using a more specific query');
805
- query = this.service.entities.workflowSnapshot.scan; // Scan still uses the service entity
806
- }
807
-
808
- const allMatchingSnapshots: WorkflowSnapshotDBItem[] = [];
809
- let cursor: string | null = null;
810
- const DYNAMODB_PAGE_SIZE = 100; // Sensible page size for fetching
811
-
812
- do {
813
- const pageResults: { data: WorkflowSnapshotDBItem[]; cursor: string | null } = await query.go({
814
- limit: DYNAMODB_PAGE_SIZE,
815
- cursor,
816
- });
817
-
818
- if (pageResults.data && pageResults.data.length > 0) {
819
- let pageFilteredData: WorkflowSnapshotDBItem[] = pageResults.data;
820
-
821
- // Apply date filters if specified
822
- if (args?.fromDate || args?.toDate) {
823
- pageFilteredData = pageFilteredData.filter((snapshot: WorkflowSnapshotDBItem) => {
824
- const createdAt = new Date(snapshot.createdAt);
825
- if (args.fromDate && createdAt < args.fromDate) {
826
- return false;
827
- }
828
- if (args.toDate && createdAt > args.toDate) {
829
- return false;
830
- }
831
- return true;
832
- });
833
- }
834
-
835
- // Filter by resourceId if specified
836
- if (args?.resourceId) {
837
- pageFilteredData = pageFilteredData.filter((snapshot: WorkflowSnapshotDBItem) => {
838
- return snapshot.resourceId === args.resourceId;
839
- });
840
- }
841
- allMatchingSnapshots.push(...pageFilteredData);
842
- }
843
-
844
- cursor = pageResults.cursor;
845
- } while (cursor);
846
-
847
- if (!allMatchingSnapshots.length) {
848
- return { runs: [], total: 0 };
849
- }
850
-
851
- // Apply offset and limit to the accumulated filtered results
852
- const total = allMatchingSnapshots.length;
853
- const paginatedData = allMatchingSnapshots.slice(offset, offset + limit);
854
-
855
- // Format and return the results
856
- const runs = paginatedData.map((snapshot: WorkflowSnapshotDBItem) => this.formatWorkflowRun(snapshot));
857
-
858
- return {
859
- runs,
860
- total,
861
- };
862
- } catch (error) {
863
- this.logger.error('Failed to get workflow runs', { error });
864
- throw error;
865
- }
375
+ return this.stores.workflows.getWorkflowRuns(args);
866
376
  }
867
377
 
868
378
  async getWorkflowRunById(args: { runId: string; workflowName?: string }): Promise<WorkflowRun | null> {
869
- const { runId, workflowName } = args;
870
- this.logger.debug('Getting workflow run by ID', { runId, workflowName });
871
-
872
- try {
873
- // If we have a workflowName, we can do a direct get using the primary key
874
- if (workflowName) {
875
- this.logger.debug('WorkflowName provided, using direct GET operation.');
876
- const result = await this.service.entities.workflowSnapshot
877
- .get({
878
- entity: 'workflow_snapshot', // Entity type for PK
879
- workflow_name: workflowName,
880
- run_id: runId,
881
- })
882
- .go();
883
-
884
- if (!result.data) {
885
- return null;
886
- }
887
-
888
- const snapshot = result.data.snapshot;
889
- return {
890
- workflowName: result.data.workflow_name,
891
- runId: result.data.run_id,
892
- snapshot,
893
- createdAt: new Date(result.data.createdAt),
894
- updatedAt: new Date(result.data.updatedAt),
895
- resourceId: result.data.resourceId,
896
- };
897
- }
898
-
899
- // Otherwise, if workflowName is not provided, use the GSI on runId.
900
- // This is more efficient than a full table scan.
901
- this.logger.debug(
902
- 'WorkflowName not provided. Attempting to find workflow run by runId using GSI. Ensure GSI (e.g., "byRunId") is defined on the workflowSnapshot entity with run_id as its key and provisioned in DynamoDB.',
903
- );
904
-
905
- // IMPORTANT: This assumes a GSI (e.g., named 'byRunId') exists on the workflowSnapshot entity
906
- // with 'run_id' as its partition key. This GSI must be:
907
- // 1. Defined in your ElectroDB model (e.g., in stores/dynamodb/src/entities/index.ts).
908
- // 2. Provisioned in the actual DynamoDB table (e.g., via CDK/CloudFormation).
909
- // The query key object includes 'entity' as it's good practice with ElectroDB and single-table design,
910
- // aligning with how other GSIs are queried in this file.
911
- const result = await this.service.entities.workflowSnapshot.query
912
- .gsi2({ entity: 'workflow_snapshot', run_id: runId }) // Replace 'byRunId' with your actual GSI name
913
- .go();
914
-
915
- // If the GSI query returns multiple items (e.g., if run_id is not globally unique across all snapshots),
916
- // this will take the first one. The original scan logic also effectively took the first match found.
917
- // If run_id is guaranteed unique, result.data should contain at most one item.
918
- const matchingRunDbItem: WorkflowSnapshotDBItem | null =
919
- result.data && result.data.length > 0 ? result.data[0] : null;
920
-
921
- if (!matchingRunDbItem) {
922
- return null;
923
- }
379
+ return this.stores.workflows.getWorkflowRunById(args);
380
+ }
924
381
 
925
- const snapshot = matchingRunDbItem.snapshot;
926
- return {
927
- workflowName: matchingRunDbItem.workflow_name,
928
- runId: matchingRunDbItem.run_id,
929
- snapshot,
930
- createdAt: new Date(matchingRunDbItem.createdAt),
931
- updatedAt: new Date(matchingRunDbItem.updatedAt),
932
- resourceId: matchingRunDbItem.resourceId,
933
- };
934
- } catch (error) {
935
- this.logger.error('Failed to get workflow run by ID', { runId, workflowName, error });
936
- throw error;
937
- }
382
+ async getResourceById({ resourceId }: { resourceId: string }): Promise<StorageResourceType | null> {
383
+ return this.stores.memory.getResourceById({ resourceId });
938
384
  }
939
385
 
940
- // Helper function to format workflow run
941
- private formatWorkflowRun(snapshotData: WorkflowSnapshotDBItem): WorkflowRun {
942
- return {
943
- workflowName: snapshotData.workflow_name,
944
- runId: snapshotData.run_id,
945
- snapshot: snapshotData.snapshot as WorkflowRunState,
946
- createdAt: new Date(snapshotData.createdAt),
947
- updatedAt: new Date(snapshotData.updatedAt),
948
- resourceId: snapshotData.resourceId,
949
- };
386
+ async saveResource({ resource }: { resource: StorageResourceType }): Promise<StorageResourceType> {
387
+ return this.stores.memory.saveResource({ resource });
950
388
  }
951
389
 
952
- // Helper methods for entity/table mapping
953
- private getEntityNameForTable(tableName: TABLE_NAMES): string | null {
954
- const mapping: Record<TABLE_NAMES, string> = {
955
- [TABLE_THREADS]: 'thread',
956
- [TABLE_MESSAGES]: 'message',
957
- [TABLE_WORKFLOW_SNAPSHOT]: 'workflowSnapshot',
958
- [TABLE_EVALS]: 'eval',
959
- [TABLE_TRACES]: 'trace',
960
- };
961
- return mapping[tableName] || null;
390
+ async updateResource({
391
+ resourceId,
392
+ workingMemory,
393
+ metadata,
394
+ }: {
395
+ resourceId: string;
396
+ workingMemory?: string;
397
+ metadata?: Record<string, any>;
398
+ }): Promise<StorageResourceType> {
399
+ return this.stores.memory.updateResource({ resourceId, workingMemory, metadata });
962
400
  }
963
401
 
964
402
  // Eval operations
965
403
  async getEvalsByAgentName(agentName: string, type?: 'test' | 'live'): Promise<EvalRow[]> {
966
- this.logger.debug('Getting evals for agent', { agentName, type });
967
-
968
- try {
969
- // Query evals by agent name using the GSI
970
- // Provide *all* composite key components for the 'byAgent' index ('entity', 'agent_name')
971
- const query = this.service.entities.eval.query.byAgent({ entity: 'eval', agent_name: agentName });
972
-
973
- // Fetch potentially all items in descending order, using the correct 'order' option
974
- const results = await query.go({ order: 'desc', limit: 100 }); // Use order: 'desc'
975
-
976
- if (!results.data.length) {
977
- return [];
978
- }
979
-
980
- // Filter by type if specified
981
- let filteredData = results.data;
982
- if (type) {
983
- filteredData = filteredData.filter((evalRecord: Record<string, any>) => {
984
- try {
985
- // Need to handle potential parse errors for test_info
986
- const testInfo =
987
- evalRecord.test_info && typeof evalRecord.test_info === 'string'
988
- ? JSON.parse(evalRecord.test_info)
989
- : undefined;
990
-
991
- if (type === 'test' && !testInfo) {
992
- return false;
993
- }
994
- if (type === 'live' && testInfo) {
995
- return false;
996
- }
997
- } catch (e) {
998
- this.logger.warn('Failed to parse test_info during filtering', { record: evalRecord, error: e });
999
- // Decide how to handle parse errors - exclude or include? Including for now.
1000
- }
1001
- return true;
1002
- });
1003
- }
404
+ return this.stores.legacyEvals.getEvalsByAgentName(agentName, type);
405
+ }
1004
406
 
1005
- // Format the results - ElectroDB transforms most attributes, but we need to map/parse
1006
- return filteredData.map((evalRecord: Record<string, any>) => {
1007
- try {
1008
- return {
1009
- input: evalRecord.input,
1010
- output: evalRecord.output,
1011
- // Safely parse result and test_info
1012
- result:
1013
- evalRecord.result && typeof evalRecord.result === 'string' ? JSON.parse(evalRecord.result) : undefined,
1014
- agentName: evalRecord.agent_name,
1015
- createdAt: evalRecord.created_at, // Keep as string from DDB?
1016
- metricName: evalRecord.metric_name,
1017
- instructions: evalRecord.instructions,
1018
- runId: evalRecord.run_id,
1019
- globalRunId: evalRecord.global_run_id,
1020
- testInfo:
1021
- evalRecord.test_info && typeof evalRecord.test_info === 'string'
1022
- ? JSON.parse(evalRecord.test_info)
1023
- : undefined,
1024
- } as EvalRow;
1025
- } catch (parseError) {
1026
- this.logger.error('Failed to parse eval record', { record: evalRecord, error: parseError });
1027
- // Return a partial record or null/undefined on error?
1028
- // Returning partial for now, might need adjustment based on requirements.
1029
- return {
1030
- agentName: evalRecord.agent_name,
1031
- createdAt: evalRecord.created_at,
1032
- runId: evalRecord.run_id,
1033
- globalRunId: evalRecord.global_run_id,
1034
- } as Partial<EvalRow> as EvalRow; // Cast needed for return type
1035
- }
1036
- });
1037
- } catch (error) {
1038
- this.logger.error('Failed to get evals by agent name', { agentName, type, error });
1039
- throw error;
1040
- }
407
+ async getEvals(
408
+ options: {
409
+ agentName?: string;
410
+ type?: 'test' | 'live';
411
+ } & PaginationArgs,
412
+ ): Promise<PaginationInfo & { evals: EvalRow[] }> {
413
+ return this.stores.legacyEvals.getEvals(options);
1041
414
  }
1042
415
 
1043
416
  /**
@@ -1050,9 +423,60 @@ export class DynamoDBStore extends MastraStorage {
1050
423
  this.client.destroy();
1051
424
  this.logger.debug('DynamoDB client closed successfully for store:', { name: this.name });
1052
425
  } catch (error) {
1053
- this.logger.error('Error closing DynamoDB client for store:', { name: this.name, error });
1054
- // Optionally re-throw or handle as appropriate for your application's error handling strategy
1055
- throw error;
426
+ throw new MastraError(
427
+ {
428
+ id: 'STORAGE_DYNAMODB_STORE_CLOSE_FAILED',
429
+ domain: ErrorDomain.STORAGE,
430
+ category: ErrorCategory.THIRD_PARTY,
431
+ },
432
+ error,
433
+ );
1056
434
  }
1057
435
  }
436
+ /**
437
+ * SCORERS - Not implemented
438
+ */
439
+ async getScoreById({ id: _id }: { id: string }): Promise<ScoreRowData | null> {
440
+ return this.stores.scores.getScoreById({ id: _id });
441
+ }
442
+
443
+ async saveScore(_score: ScoreRowData): Promise<{ score: ScoreRowData }> {
444
+ return this.stores.scores.saveScore(_score);
445
+ }
446
+
447
+ async getScoresByRunId({
448
+ runId: _runId,
449
+ pagination: _pagination,
450
+ }: {
451
+ runId: string;
452
+ pagination: StoragePagination;
453
+ }): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
454
+ return this.stores.scores.getScoresByRunId({ runId: _runId, pagination: _pagination });
455
+ }
456
+
457
+ async getScoresByEntityId({
458
+ entityId: _entityId,
459
+ entityType: _entityType,
460
+ pagination: _pagination,
461
+ }: {
462
+ pagination: StoragePagination;
463
+ entityId: string;
464
+ entityType: string;
465
+ }): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
466
+ return this.stores.scores.getScoresByEntityId({
467
+ entityId: _entityId,
468
+ entityType: _entityType,
469
+ pagination: _pagination,
470
+ });
471
+ }
472
+
473
+ async getScoresByScorerId({
474
+ scorerId: _scorerId,
475
+ pagination: _pagination,
476
+ }: {
477
+ scorerId: string;
478
+ pagination: StoragePagination;
479
+ }): Promise<{ pagination: PaginationInfo; scores: ScoreRowData[] }> {
480
+ return this.stores.scores.getScoresByScorerId({ scorerId: _scorerId, pagination: _pagination });
481
+ }
1058
482
  }