@mastra/dynamodb 0.14.3 → 0.14.6-alpha.0
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/CHANGELOG.md +743 -0
- package/dist/index.cjs +34 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +35 -2
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/operations/index.d.ts.map +1 -1
- package/dist/storage/domains/workflows/index.d.ts +19 -1
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/dist/storage/index.d.ts +19 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +16 -7
- package/src/entities/eval.ts +0 -102
- package/src/entities/index.ts +0 -27
- package/src/entities/message.ts +0 -143
- package/src/entities/resource.ts +0 -57
- package/src/entities/score.ts +0 -317
- package/src/entities/thread.ts +0 -66
- package/src/entities/trace.ts +0 -129
- package/src/entities/utils.ts +0 -51
- package/src/entities/workflow-snapshot.ts +0 -56
- package/src/index.ts +0 -1
- package/src/storage/docker-compose.yml +0 -16
- package/src/storage/domains/legacy-evals/index.ts +0 -243
- package/src/storage/domains/memory/index.ts +0 -987
- package/src/storage/domains/operations/index.ts +0 -433
- package/src/storage/domains/score/index.ts +0 -292
- package/src/storage/domains/traces/index.ts +0 -286
- package/src/storage/domains/workflows/index.ts +0 -297
- package/src/storage/index.test.ts +0 -1420
- package/src/storage/index.ts +0 -504
|
@@ -1,433 +0,0 @@
|
|
|
1
|
-
import { DescribeTableCommand } from '@aws-sdk/client-dynamodb';
|
|
2
|
-
import type { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
|
|
3
|
-
import { ErrorCategory, ErrorDomain, MastraError } from '@mastra/core/error';
|
|
4
|
-
import {
|
|
5
|
-
StoreOperations,
|
|
6
|
-
TABLE_EVALS,
|
|
7
|
-
TABLE_MESSAGES,
|
|
8
|
-
TABLE_RESOURCES,
|
|
9
|
-
TABLE_SCORERS,
|
|
10
|
-
TABLE_THREADS,
|
|
11
|
-
TABLE_TRACES,
|
|
12
|
-
TABLE_WORKFLOW_SNAPSHOT,
|
|
13
|
-
} from '@mastra/core/storage';
|
|
14
|
-
import type { StorageColumn, TABLE_NAMES } from '@mastra/core/storage';
|
|
15
|
-
import type { Service } from 'electrodb';
|
|
16
|
-
|
|
17
|
-
export class StoreOperationsDynamoDB extends StoreOperations {
|
|
18
|
-
client: DynamoDBDocumentClient;
|
|
19
|
-
tableName: string;
|
|
20
|
-
service: Service<Record<string, any>>;
|
|
21
|
-
constructor({
|
|
22
|
-
service,
|
|
23
|
-
tableName,
|
|
24
|
-
client,
|
|
25
|
-
}: {
|
|
26
|
-
service: Service<Record<string, any>>;
|
|
27
|
-
tableName: string;
|
|
28
|
-
client: DynamoDBDocumentClient;
|
|
29
|
-
}) {
|
|
30
|
-
super();
|
|
31
|
-
this.service = service;
|
|
32
|
-
this.client = client;
|
|
33
|
-
this.tableName = tableName;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async hasColumn(): Promise<boolean> {
|
|
37
|
-
return true;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async dropTable(): Promise<void> {}
|
|
41
|
-
|
|
42
|
-
// Helper methods for entity/table mapping
|
|
43
|
-
private getEntityNameForTable(tableName: TABLE_NAMES): string | null {
|
|
44
|
-
const mapping: Record<TABLE_NAMES, string> = {
|
|
45
|
-
[TABLE_THREADS]: 'thread',
|
|
46
|
-
[TABLE_MESSAGES]: 'message',
|
|
47
|
-
[TABLE_WORKFLOW_SNAPSHOT]: 'workflow_snapshot',
|
|
48
|
-
[TABLE_EVALS]: 'eval',
|
|
49
|
-
[TABLE_SCORERS]: 'score',
|
|
50
|
-
[TABLE_TRACES]: 'trace',
|
|
51
|
-
[TABLE_RESOURCES]: 'resource',
|
|
52
|
-
};
|
|
53
|
-
return mapping[tableName] || null;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Pre-processes a record to ensure Date objects are converted to ISO strings
|
|
58
|
-
* This is necessary because ElectroDB validation happens before setters are applied
|
|
59
|
-
*/
|
|
60
|
-
private preprocessRecord(record: Record<string, any>): Record<string, any> {
|
|
61
|
-
const processed = { ...record };
|
|
62
|
-
|
|
63
|
-
// Convert Date objects to ISO strings for date fields
|
|
64
|
-
// This prevents ElectroDB validation errors that occur when Date objects are passed
|
|
65
|
-
// to string-typed attributes, even when the attribute has a setter that converts dates
|
|
66
|
-
if (processed.createdAt instanceof Date) {
|
|
67
|
-
processed.createdAt = processed.createdAt.toISOString();
|
|
68
|
-
}
|
|
69
|
-
if (processed.updatedAt instanceof Date) {
|
|
70
|
-
processed.updatedAt = processed.updatedAt.toISOString();
|
|
71
|
-
}
|
|
72
|
-
if (processed.created_at instanceof Date) {
|
|
73
|
-
processed.created_at = processed.created_at.toISOString();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Convert result field to JSON string if it's an object
|
|
77
|
-
if (processed.result && typeof processed.result === 'object') {
|
|
78
|
-
processed.result = JSON.stringify(processed.result);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Convert test_info field to JSON string if it's an object, or remove if undefined/null
|
|
82
|
-
if (processed.test_info && typeof processed.test_info === 'object') {
|
|
83
|
-
processed.test_info = JSON.stringify(processed.test_info);
|
|
84
|
-
} else if (processed.test_info === undefined || processed.test_info === null) {
|
|
85
|
-
delete processed.test_info;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Convert snapshot field to JSON string if it's an object
|
|
89
|
-
if (processed.snapshot && typeof processed.snapshot === 'object') {
|
|
90
|
-
processed.snapshot = JSON.stringify(processed.snapshot);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Convert trace-specific fields to JSON strings if they're objects
|
|
94
|
-
// These fields have set/get functions in the entity but validation happens before set
|
|
95
|
-
if (processed.attributes && typeof processed.attributes === 'object') {
|
|
96
|
-
processed.attributes = JSON.stringify(processed.attributes);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (processed.status && typeof processed.status === 'object') {
|
|
100
|
-
processed.status = JSON.stringify(processed.status);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (processed.events && typeof processed.events === 'object') {
|
|
104
|
-
processed.events = JSON.stringify(processed.events);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (processed.links && typeof processed.links === 'object') {
|
|
108
|
-
processed.links = JSON.stringify(processed.links);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return processed;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Validates that the required DynamoDB table exists and is accessible.
|
|
116
|
-
* This does not check the table structure - it assumes the table
|
|
117
|
-
* was created with the correct structure via CDK/CloudFormation.
|
|
118
|
-
*/
|
|
119
|
-
private async validateTableExists(): Promise<boolean> {
|
|
120
|
-
try {
|
|
121
|
-
const command = new DescribeTableCommand({
|
|
122
|
-
TableName: this.tableName,
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
// If the table exists, this call will succeed
|
|
126
|
-
// If the table doesn't exist, it will throw a ResourceNotFoundException
|
|
127
|
-
await this.client.send(command);
|
|
128
|
-
return true;
|
|
129
|
-
} catch (error: any) {
|
|
130
|
-
// If the table doesn't exist, DynamoDB returns a ResourceNotFoundException
|
|
131
|
-
if (error.name === 'ResourceNotFoundException') {
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// For other errors (like permissions issues), we should throw
|
|
136
|
-
throw new MastraError(
|
|
137
|
-
{
|
|
138
|
-
id: 'STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_EXISTS_FAILED',
|
|
139
|
-
domain: ErrorDomain.STORAGE,
|
|
140
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
141
|
-
details: { tableName: this.tableName },
|
|
142
|
-
},
|
|
143
|
-
error,
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* This method is modified for DynamoDB with ElectroDB single-table design.
|
|
150
|
-
* It assumes the table is created and managed externally via CDK/CloudFormation.
|
|
151
|
-
*
|
|
152
|
-
* This implementation only validates that the required table exists and is accessible.
|
|
153
|
-
* No table creation is attempted - we simply check if we can access the table.
|
|
154
|
-
*/
|
|
155
|
-
async createTable({ tableName }: { tableName: TABLE_NAMES; schema: Record<string, any> }): Promise<void> {
|
|
156
|
-
this.logger.debug('Validating access to externally managed table', { tableName, physicalTable: this.tableName });
|
|
157
|
-
|
|
158
|
-
// For single-table design, we just need to verify the table exists and is accessible
|
|
159
|
-
try {
|
|
160
|
-
const tableExists = await this.validateTableExists();
|
|
161
|
-
|
|
162
|
-
if (!tableExists) {
|
|
163
|
-
this.logger.error(
|
|
164
|
-
`Table ${this.tableName} does not exist or is not accessible. It should be created via CDK/CloudFormation.`,
|
|
165
|
-
);
|
|
166
|
-
throw new Error(
|
|
167
|
-
`Table ${this.tableName} does not exist or is not accessible. Ensure it's created via CDK/CloudFormation before using this store.`,
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
this.logger.debug(`Table ${this.tableName} exists and is accessible`);
|
|
172
|
-
} catch (error) {
|
|
173
|
-
this.logger.error('Error validating table access', { tableName: this.tableName, error });
|
|
174
|
-
throw new MastraError(
|
|
175
|
-
{
|
|
176
|
-
id: 'STORAGE_DYNAMODB_STORE_VALIDATE_TABLE_ACCESS_FAILED',
|
|
177
|
-
domain: ErrorDomain.STORAGE,
|
|
178
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
179
|
-
details: { tableName: this.tableName },
|
|
180
|
-
},
|
|
181
|
-
error,
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
async insert({ tableName, record }: { tableName: TABLE_NAMES; record: Record<string, any> }): Promise<void> {
|
|
187
|
-
this.logger.debug('DynamoDB insert called', { tableName });
|
|
188
|
-
|
|
189
|
-
const entityName = this.getEntityNameForTable(tableName);
|
|
190
|
-
if (!entityName || !this.service.entities[entityName]) {
|
|
191
|
-
throw new MastraError({
|
|
192
|
-
id: 'STORAGE_DYNAMODB_STORE_INSERT_INVALID_ARGS',
|
|
193
|
-
domain: ErrorDomain.STORAGE,
|
|
194
|
-
category: ErrorCategory.USER,
|
|
195
|
-
text: 'No entity defined for tableName',
|
|
196
|
-
details: { tableName },
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
try {
|
|
201
|
-
// Add the entity type to the record and preprocess before creating
|
|
202
|
-
const dataToSave = { entity: entityName, ...this.preprocessRecord(record) };
|
|
203
|
-
await this.service.entities[entityName].create(dataToSave).go();
|
|
204
|
-
} catch (error) {
|
|
205
|
-
throw new MastraError(
|
|
206
|
-
{
|
|
207
|
-
id: 'STORAGE_DYNAMODB_STORE_INSERT_FAILED',
|
|
208
|
-
domain: ErrorDomain.STORAGE,
|
|
209
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
210
|
-
details: { tableName },
|
|
211
|
-
},
|
|
212
|
-
error,
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
async alterTable(_args: {
|
|
218
|
-
tableName: TABLE_NAMES;
|
|
219
|
-
schema: Record<string, StorageColumn>;
|
|
220
|
-
ifNotExists: string[];
|
|
221
|
-
}): Promise<void> {
|
|
222
|
-
// Nothing to do here, DynamoDB has a flexible schema and handles new attributes automatically upon insertion/update.
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Clear all items from a logical "table" (entity type)
|
|
227
|
-
*/
|
|
228
|
-
async clearTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
|
|
229
|
-
this.logger.debug('DynamoDB clearTable called', { tableName });
|
|
230
|
-
|
|
231
|
-
const entityName = this.getEntityNameForTable(tableName)!;
|
|
232
|
-
|
|
233
|
-
if (!entityName || !this.service.entities[entityName]) {
|
|
234
|
-
throw new MastraError({
|
|
235
|
-
id: 'STORAGE_DYNAMODB_STORE_CLEAR_TABLE_INVALID_ARGS',
|
|
236
|
-
domain: ErrorDomain.STORAGE,
|
|
237
|
-
category: ErrorCategory.USER,
|
|
238
|
-
text: 'No entity defined for tableName',
|
|
239
|
-
details: { tableName },
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
try {
|
|
244
|
-
// Scan requires no key, just uses the entity handler
|
|
245
|
-
const result = await this.service.entities[entityName].scan.go({ pages: 'all' }); // Get all pages
|
|
246
|
-
|
|
247
|
-
if (!result.data.length) {
|
|
248
|
-
this.logger.debug(`No records found to clear for ${tableName}`);
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
this.logger.debug(`Found ${result.data.length} records to delete for ${tableName}`);
|
|
253
|
-
|
|
254
|
-
// ElectroDB batch delete expects the key components for each item
|
|
255
|
-
const keysToDelete = result.data.map((item: any) => {
|
|
256
|
-
const key: { entity: string; [key: string]: any } = { entity: entityName };
|
|
257
|
-
|
|
258
|
-
// Construct the key based on the specific entity's primary key structure
|
|
259
|
-
switch (entityName) {
|
|
260
|
-
case 'thread':
|
|
261
|
-
if (!item.id) throw new Error(`Missing required key 'id' for entity 'thread'`);
|
|
262
|
-
key.id = item.id;
|
|
263
|
-
break;
|
|
264
|
-
case 'message':
|
|
265
|
-
if (!item.id) throw new Error(`Missing required key 'id' for entity 'message'`);
|
|
266
|
-
key.id = item.id;
|
|
267
|
-
break;
|
|
268
|
-
case 'workflow_snapshot':
|
|
269
|
-
if (!item.workflow_name)
|
|
270
|
-
throw new Error(`Missing required key 'workflow_name' for entity 'workflow_snapshot'`);
|
|
271
|
-
if (!item.run_id) throw new Error(`Missing required key 'run_id' for entity 'workflow_snapshot'`);
|
|
272
|
-
key.workflow_name = item.workflow_name;
|
|
273
|
-
key.run_id = item.run_id;
|
|
274
|
-
break;
|
|
275
|
-
case 'eval':
|
|
276
|
-
// Assuming 'eval' uses 'run_id' or another unique identifier as part of its PK
|
|
277
|
-
// Adjust based on the actual primary key defined in getElectroDbService
|
|
278
|
-
if (!item.run_id) throw new Error(`Missing required key 'run_id' for entity 'eval'`);
|
|
279
|
-
// Add other key components if necessary for 'eval' PK
|
|
280
|
-
key.run_id = item.run_id;
|
|
281
|
-
// Example: if global_run_id is also part of PK:
|
|
282
|
-
// if (!item.global_run_id) throw new Error(`Missing required key 'global_run_id' for entity 'eval'`);
|
|
283
|
-
// key.global_run_id = item.global_run_id;
|
|
284
|
-
break;
|
|
285
|
-
case 'trace':
|
|
286
|
-
// Assuming 'trace' uses 'id' as its PK
|
|
287
|
-
// Adjust based on the actual primary key defined in getElectroDbService
|
|
288
|
-
if (!item.id) throw new Error(`Missing required key 'id' for entity 'trace'`);
|
|
289
|
-
key.id = item.id;
|
|
290
|
-
break;
|
|
291
|
-
case 'score':
|
|
292
|
-
// Score entity uses 'id' as its PK
|
|
293
|
-
if (!item.id) throw new Error(`Missing required key 'id' for entity 'score'`);
|
|
294
|
-
key.id = item.id;
|
|
295
|
-
break;
|
|
296
|
-
default:
|
|
297
|
-
// Handle unknown entity types - log a warning or throw an error
|
|
298
|
-
this.logger.warn(`Unknown entity type encountered during clearTable: ${entityName}`);
|
|
299
|
-
// Optionally throw an error if strict handling is required
|
|
300
|
-
throw new Error(`Cannot construct delete key for unknown entity type: ${entityName}`);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
return key;
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
const batchSize = 25;
|
|
307
|
-
for (let i = 0; i < keysToDelete.length; i += batchSize) {
|
|
308
|
-
const batchKeys = keysToDelete.slice(i, i + batchSize);
|
|
309
|
-
// Pass the array of key objects to delete
|
|
310
|
-
await this.service.entities[entityName].delete(batchKeys).go();
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
this.logger.debug(`Successfully cleared all records for ${tableName}`);
|
|
314
|
-
} catch (error) {
|
|
315
|
-
throw new MastraError(
|
|
316
|
-
{
|
|
317
|
-
id: 'STORAGE_DYNAMODB_STORE_CLEAR_TABLE_FAILED',
|
|
318
|
-
domain: ErrorDomain.STORAGE,
|
|
319
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
320
|
-
details: { tableName },
|
|
321
|
-
},
|
|
322
|
-
error,
|
|
323
|
-
);
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* Insert multiple records as a batch
|
|
329
|
-
*/
|
|
330
|
-
async batchInsert({ tableName, records }: { tableName: TABLE_NAMES; records: Record<string, any>[] }): Promise<void> {
|
|
331
|
-
this.logger.debug('DynamoDB batchInsert called', { tableName, count: records.length });
|
|
332
|
-
|
|
333
|
-
const entityName = this.getEntityNameForTable(tableName);
|
|
334
|
-
if (!entityName || !this.service.entities[entityName]) {
|
|
335
|
-
throw new MastraError({
|
|
336
|
-
id: 'STORAGE_DYNAMODB_STORE_BATCH_INSERT_INVALID_ARGS',
|
|
337
|
-
domain: ErrorDomain.STORAGE,
|
|
338
|
-
category: ErrorCategory.USER,
|
|
339
|
-
text: 'No entity defined for tableName',
|
|
340
|
-
details: { tableName },
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// Add entity type and preprocess each record
|
|
345
|
-
const recordsToSave = records.map(rec => ({ entity: entityName, ...this.preprocessRecord(rec) }));
|
|
346
|
-
|
|
347
|
-
// ElectroDB has batch limits of 25 items, so we need to chunk
|
|
348
|
-
const batchSize = 25;
|
|
349
|
-
const batches = [];
|
|
350
|
-
for (let i = 0; i < recordsToSave.length; i += batchSize) {
|
|
351
|
-
const batch = recordsToSave.slice(i, i + batchSize);
|
|
352
|
-
batches.push(batch);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
try {
|
|
356
|
-
// Process each batch
|
|
357
|
-
for (const batch of batches) {
|
|
358
|
-
// Create each item individually within the batch
|
|
359
|
-
for (const recordData of batch) {
|
|
360
|
-
if (!recordData.entity) {
|
|
361
|
-
this.logger.error('Missing entity property in record data for batchInsert', { recordData, tableName });
|
|
362
|
-
throw new Error(`Internal error: Missing entity property during batchInsert for ${tableName}`);
|
|
363
|
-
}
|
|
364
|
-
// Log the object just before the create call
|
|
365
|
-
this.logger.debug('Attempting to create record in batchInsert:', { entityName, recordData });
|
|
366
|
-
await this.service.entities[entityName].create(recordData).go();
|
|
367
|
-
}
|
|
368
|
-
// Original batch call: await this.service.entities[entityName].create(batch).go();
|
|
369
|
-
}
|
|
370
|
-
} catch (error) {
|
|
371
|
-
throw new MastraError(
|
|
372
|
-
{
|
|
373
|
-
id: 'STORAGE_DYNAMODB_STORE_BATCH_INSERT_FAILED',
|
|
374
|
-
domain: ErrorDomain.STORAGE,
|
|
375
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
376
|
-
details: { tableName },
|
|
377
|
-
},
|
|
378
|
-
error,
|
|
379
|
-
);
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
/**
|
|
384
|
-
* Load a record by its keys
|
|
385
|
-
*/
|
|
386
|
-
async load<R>({ tableName, keys }: { tableName: TABLE_NAMES; keys: Record<string, string> }): Promise<R | null> {
|
|
387
|
-
this.logger.debug('DynamoDB load called', { tableName, keys });
|
|
388
|
-
|
|
389
|
-
const entityName = this.getEntityNameForTable(tableName);
|
|
390
|
-
if (!entityName || !this.service.entities[entityName]) {
|
|
391
|
-
throw new MastraError({
|
|
392
|
-
id: 'STORAGE_DYNAMODB_STORE_LOAD_INVALID_ARGS',
|
|
393
|
-
domain: ErrorDomain.STORAGE,
|
|
394
|
-
category: ErrorCategory.USER,
|
|
395
|
-
text: 'No entity defined for tableName',
|
|
396
|
-
details: { tableName },
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
try {
|
|
401
|
-
// Add the entity type to the key object for the .get call
|
|
402
|
-
const keyObject = { entity: entityName, ...keys };
|
|
403
|
-
const result = await this.service.entities[entityName].get(keyObject).go();
|
|
404
|
-
|
|
405
|
-
if (!result.data) {
|
|
406
|
-
return null;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
// Add parsing logic if necessary (e.g., for metadata)
|
|
410
|
-
let data = result.data;
|
|
411
|
-
if (data.metadata && typeof data.metadata === 'string') {
|
|
412
|
-
try {
|
|
413
|
-
// data.metadata = JSON.parse(data.metadata); // REMOVED by AI
|
|
414
|
-
} catch {
|
|
415
|
-
/* ignore parse error */
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
// Add similar parsing for other JSON fields if needed based on entity type
|
|
419
|
-
|
|
420
|
-
return data as R;
|
|
421
|
-
} catch (error) {
|
|
422
|
-
throw new MastraError(
|
|
423
|
-
{
|
|
424
|
-
id: 'STORAGE_DYNAMODB_STORE_LOAD_FAILED',
|
|
425
|
-
domain: ErrorDomain.STORAGE,
|
|
426
|
-
category: ErrorCategory.THIRD_PARTY,
|
|
427
|
-
details: { tableName },
|
|
428
|
-
},
|
|
429
|
-
error,
|
|
430
|
-
);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
}
|