@mastra/dynamodb 1.0.0-beta.8 → 1.0.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +812 -0
  2. package/README.md +1 -2
  3. package/dist/docs/README.md +31 -0
  4. package/dist/docs/SKILL.md +32 -0
  5. package/dist/docs/SOURCE_MAP.json +6 -0
  6. package/dist/docs/storage/01-reference.md +253 -0
  7. package/dist/entities/eval.d.ts +8 -0
  8. package/dist/entities/eval.d.ts.map +1 -1
  9. package/dist/entities/index.d.ts +61 -4
  10. package/dist/entities/index.d.ts.map +1 -1
  11. package/dist/entities/message.d.ts +8 -0
  12. package/dist/entities/message.d.ts.map +1 -1
  13. package/dist/entities/resource.d.ts +8 -0
  14. package/dist/entities/resource.d.ts.map +1 -1
  15. package/dist/entities/score.d.ts +13 -4
  16. package/dist/entities/score.d.ts.map +1 -1
  17. package/dist/entities/thread.d.ts +8 -0
  18. package/dist/entities/thread.d.ts.map +1 -1
  19. package/dist/entities/trace.d.ts +8 -0
  20. package/dist/entities/trace.d.ts.map +1 -1
  21. package/dist/entities/utils.d.ts +90 -0
  22. package/dist/entities/utils.d.ts.map +1 -1
  23. package/dist/entities/workflow-snapshot.d.ts +8 -0
  24. package/dist/entities/workflow-snapshot.d.ts.map +1 -1
  25. package/dist/index.cjs +154 -45
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.js +152 -47
  28. package/dist/index.js.map +1 -1
  29. package/dist/storage/db/index.d.ts +18 -2
  30. package/dist/storage/db/index.d.ts.map +1 -1
  31. package/dist/storage/domains/memory/index.d.ts +3 -2
  32. package/dist/storage/domains/memory/index.d.ts.map +1 -1
  33. package/dist/storage/domains/scores/index.d.ts +1 -0
  34. package/dist/storage/domains/scores/index.d.ts.map +1 -1
  35. package/dist/storage/domains/workflows/index.d.ts +1 -0
  36. package/dist/storage/domains/workflows/index.d.ts.map +1 -1
  37. package/dist/storage/index.d.ts +90 -2
  38. package/dist/storage/index.d.ts.map +1 -1
  39. package/dist/storage/ttl.d.ts +52 -0
  40. package/dist/storage/ttl.d.ts.map +1 -0
  41. package/package.json +9 -9
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { DynamoDBClient, DescribeTableCommand } from '@aws-sdk/client-dynamodb';
2
2
  import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
3
3
  import { MastraError, ErrorCategory, ErrorDomain } from '@mastra/core/error';
4
- import { MemoryStorage, createStorageErrorId, normalizePerPage, calculatePagination, ScoresStorage, SCORERS_SCHEMA, WorkflowsStorage, MastraStorage, TABLE_SCORERS, TABLE_WORKFLOW_SNAPSHOT, TABLE_RESOURCES, TABLE_MESSAGES, TABLE_THREADS } from '@mastra/core/storage';
4
+ import { MemoryStorage, createStorageErrorId, normalizePerPage, calculatePagination, filterByDateRange, ScoresStorage, SCORERS_SCHEMA, WorkflowsStorage, MastraCompositeStore, TABLE_SCORERS, TABLE_WORKFLOW_SNAPSHOT, TABLE_RESOURCES, TABLE_MESSAGES, TABLE_THREADS } from '@mastra/core/storage';
5
5
  import { Entity, Service } from 'electrodb';
6
6
  import { MessageList } from '@mastra/core/agent';
7
7
  import { saveScorePayloadSchema } from '@mastra/core/evals';
@@ -58,6 +58,25 @@ var baseAttributes = {
58
58
  }
59
59
  return value;
60
60
  }
61
+ },
62
+ /**
63
+ * TTL attribute for DynamoDB automatic item expiration.
64
+ * This is a Unix timestamp (epoch seconds) that indicates when the item should be deleted.
65
+ *
66
+ * Note: For TTL to work, you must enable TTL on your DynamoDB table
67
+ * specifying this attribute name (default: 'ttl').
68
+ */
69
+ ttl: {
70
+ type: "number",
71
+ required: false
72
+ },
73
+ /**
74
+ * Alternative TTL attribute with configurable name.
75
+ * Use this if you've configured TTL on your DynamoDB table with 'expiresAt' as the attribute name.
76
+ */
77
+ expiresAt: {
78
+ type: "number",
79
+ required: false
61
80
  }
62
81
  };
63
82
 
@@ -566,6 +585,28 @@ var scoreEntity = new Entity({
566
585
  return value;
567
586
  }
568
587
  },
588
+ metadata: {
589
+ type: "string",
590
+ required: false,
591
+ set: (value) => {
592
+ if (value && typeof value !== "string") {
593
+ return JSON.stringify(value);
594
+ }
595
+ return value;
596
+ },
597
+ get: (value) => {
598
+ if (value && typeof value === "string") {
599
+ try {
600
+ if (value.startsWith("{") || value.startsWith("[")) {
601
+ return JSON.parse(value);
602
+ }
603
+ } catch {
604
+ return value;
605
+ }
606
+ }
607
+ return value;
608
+ }
609
+ },
569
610
  requestContext: {
570
611
  type: "string",
571
612
  required: false,
@@ -939,7 +980,7 @@ function getElectroDbService(client, tableName) {
939
980
  }
940
981
  function resolveDynamoDBConfig(config) {
941
982
  if ("service" in config) {
942
- return config.service;
983
+ return { service: config.service, ttl: config.ttl };
943
984
  }
944
985
  const dynamoClient = new DynamoDBClient({
945
986
  region: config.region || "us-east-1",
@@ -947,7 +988,36 @@ function resolveDynamoDBConfig(config) {
947
988
  credentials: config.credentials
948
989
  });
949
990
  const client = DynamoDBDocumentClient.from(dynamoClient);
950
- return getElectroDbService(client, config.tableName);
991
+ return {
992
+ service: getElectroDbService(client, config.tableName),
993
+ ttl: config.ttl
994
+ };
995
+ }
996
+
997
+ // src/storage/ttl.ts
998
+ function calculateTtl(entityName, ttlConfig, customTtlSeconds) {
999
+ const entityConfig = ttlConfig?.[entityName];
1000
+ if (!entityConfig?.enabled) {
1001
+ return void 0;
1002
+ }
1003
+ const ttlSeconds = customTtlSeconds ?? entityConfig.defaultTtlSeconds;
1004
+ if (ttlSeconds === void 0 || ttlSeconds <= 0) {
1005
+ return void 0;
1006
+ }
1007
+ return Math.floor(Date.now() / 1e3) + ttlSeconds;
1008
+ }
1009
+ function getTtlAttributeName(entityName, ttlConfig) {
1010
+ const entityConfig = ttlConfig?.[entityName];
1011
+ return entityConfig?.attributeName ?? "ttl";
1012
+ }
1013
+ function isTtlEnabled(entityName, ttlConfig) {
1014
+ return ttlConfig?.[entityName]?.enabled === true;
1015
+ }
1016
+ function getTtlProps(entityName, ttlConfig, customTtlSeconds) {
1017
+ const ttlValue = calculateTtl(entityName, ttlConfig, customTtlSeconds);
1018
+ if (ttlValue === void 0) return {};
1019
+ const attributeName = getTtlAttributeName(entityName, ttlConfig);
1020
+ return { [attributeName]: ttlValue };
951
1021
  }
952
1022
  var ENTITY_MAP = {
953
1023
  [TABLE_THREADS]: "thread",
@@ -995,9 +1065,12 @@ async function deleteTableData(service, tableName) {
995
1065
  // src/storage/domains/memory/index.ts
996
1066
  var MemoryStorageDynamoDB = class extends MemoryStorage {
997
1067
  service;
1068
+ ttlConfig;
998
1069
  constructor(config) {
999
1070
  super();
1000
- this.service = resolveDynamoDBConfig(config);
1071
+ const resolved = resolveDynamoDBConfig(config);
1072
+ this.service = resolved.service;
1073
+ this.ttlConfig = resolved.ttl;
1001
1074
  }
1002
1075
  async dangerouslyClearAll() {
1003
1076
  await deleteTableData(this.service, TABLE_THREADS);
@@ -1108,7 +1181,8 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1108
1181
  title: thread.title || `Thread ${thread.id}`,
1109
1182
  createdAt: thread.createdAt?.toISOString() || now.toISOString(),
1110
1183
  updatedAt: thread.updatedAt?.toISOString() || now.toISOString(),
1111
- metadata: thread.metadata ? JSON.stringify(thread.metadata) : void 0
1184
+ metadata: thread.metadata ? JSON.stringify(thread.metadata) : void 0,
1185
+ ...getTtlProps("thread", this.ttlConfig)
1112
1186
  };
1113
1187
  try {
1114
1188
  await this.service.entities.thread.upsert(threadData).go();
@@ -1277,21 +1351,11 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1277
1351
  if (resourceId) {
1278
1352
  allThreadMessages = allThreadMessages.filter((msg) => msg.resourceId === resourceId);
1279
1353
  }
1280
- if (filter?.dateRange) {
1281
- const dateRange = filter.dateRange;
1282
- allThreadMessages = allThreadMessages.filter((msg) => {
1283
- const createdAt = new Date(msg.createdAt).getTime();
1284
- if (dateRange.start) {
1285
- const startTime = dateRange.start instanceof Date ? dateRange.start.getTime() : new Date(dateRange.start).getTime();
1286
- if (createdAt < startTime) return false;
1287
- }
1288
- if (dateRange.end) {
1289
- const endTime = dateRange.end instanceof Date ? dateRange.end.getTime() : new Date(dateRange.end).getTime();
1290
- if (createdAt > endTime) return false;
1291
- }
1292
- return true;
1293
- });
1294
- }
1354
+ allThreadMessages = filterByDateRange(
1355
+ allThreadMessages,
1356
+ (msg) => new Date(msg.createdAt),
1357
+ filter?.dateRange
1358
+ );
1295
1359
  allThreadMessages.sort((a, b) => {
1296
1360
  const aValue = field === "createdAt" ? new Date(a.createdAt).getTime() : a[field];
1297
1361
  const bValue = field === "createdAt" ? new Date(b.createdAt).getTime() : b[field];
@@ -1385,20 +1449,18 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1385
1449
  const now = (/* @__PURE__ */ new Date()).toISOString();
1386
1450
  return {
1387
1451
  entity: "message",
1388
- // Add entity type
1389
1452
  id: msg.id,
1390
1453
  threadId: msg.threadId,
1391
1454
  role: msg.role,
1392
1455
  type: msg.type,
1393
1456
  resourceId: msg.resourceId,
1394
- // Ensure complex fields are stringified if not handled by attribute setters
1395
1457
  content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
1396
1458
  toolCallArgs: `toolCallArgs` in msg && msg.toolCallArgs ? JSON.stringify(msg.toolCallArgs) : void 0,
1397
1459
  toolCallIds: `toolCallIds` in msg && msg.toolCallIds ? JSON.stringify(msg.toolCallIds) : void 0,
1398
1460
  toolNames: `toolNames` in msg && msg.toolNames ? JSON.stringify(msg.toolNames) : void 0,
1399
1461
  createdAt: msg.createdAt instanceof Date ? msg.createdAt.toISOString() : msg.createdAt || now,
1400
- updatedAt: now
1401
- // Add updatedAt
1462
+ updatedAt: now,
1463
+ ...getTtlProps("message", this.ttlConfig)
1402
1464
  };
1403
1465
  });
1404
1466
  try {
@@ -1442,33 +1504,57 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1442
1504
  );
1443
1505
  }
1444
1506
  }
1445
- async listThreadsByResourceId(args) {
1446
- const { resourceId, page = 0, perPage: perPageInput, orderBy } = args;
1447
- const perPage = normalizePerPage(perPageInput, 100);
1448
- if (page < 0) {
1507
+ async listThreads(args) {
1508
+ const { page = 0, perPage: perPageInput, orderBy, filter } = args;
1509
+ try {
1510
+ this.validatePaginationInput(page, perPageInput ?? 100);
1511
+ } catch (error) {
1449
1512
  throw new MastraError(
1450
1513
  {
1451
- id: createStorageErrorId("DYNAMODB", "LIST_THREADS_BY_RESOURCE_ID", "INVALID_PAGE"),
1514
+ id: createStorageErrorId("DYNAMODB", "LIST_THREADS", "INVALID_PAGE"),
1452
1515
  domain: ErrorDomain.STORAGE,
1453
1516
  category: ErrorCategory.USER,
1454
- details: { page }
1517
+ details: {
1518
+ page,
1519
+ ...perPageInput !== void 0 && { perPage: perPageInput }
1520
+ }
1455
1521
  },
1456
- new Error("page must be >= 0")
1522
+ error instanceof Error ? error : new Error("Invalid pagination parameters")
1457
1523
  );
1458
1524
  }
1525
+ const perPage = normalizePerPage(perPageInput, 100);
1459
1526
  const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
1460
1527
  const { field, direction } = this.parseOrderBy(orderBy);
1461
- this.logger.debug("Getting threads by resource ID with pagination", {
1462
- resourceId,
1528
+ this.logger.debug("Listing threads with filters", {
1529
+ resourceId: filter?.resourceId,
1530
+ metadataKeys: filter?.metadata ? Object.keys(filter.metadata) : [],
1463
1531
  page,
1464
1532
  perPage,
1465
1533
  field,
1466
1534
  direction
1467
1535
  });
1468
1536
  try {
1469
- const query = this.service.entities.thread.query.byResource({ entity: "thread", resourceId });
1470
- const results = await query.go();
1471
- const allThreads = this.transformAndSortThreads(results.data, field, direction);
1537
+ const rawThreads = filter?.resourceId ? (await this.service.entities.thread.query.byResource({
1538
+ entity: "thread",
1539
+ resourceId: filter.resourceId
1540
+ }).go({ pages: "all" })).data : (await this.service.entities.thread.scan.go({ pages: "all" })).data;
1541
+ let allThreads = this.transformAndSortThreads(rawThreads, field, direction);
1542
+ if (filter?.metadata && Object.keys(filter.metadata).length > 0) {
1543
+ allThreads = allThreads.filter((thread) => {
1544
+ let threadMeta = null;
1545
+ if (typeof thread.metadata === "string") {
1546
+ try {
1547
+ threadMeta = JSON.parse(thread.metadata);
1548
+ } catch {
1549
+ return false;
1550
+ }
1551
+ } else if (thread.metadata && typeof thread.metadata === "object") {
1552
+ threadMeta = thread.metadata;
1553
+ }
1554
+ if (!threadMeta) return false;
1555
+ return Object.entries(filter.metadata).every(([key, value]) => threadMeta[key] === value);
1556
+ });
1557
+ }
1472
1558
  const endIndex = offset + perPage;
1473
1559
  const paginatedThreads = allThreads.slice(offset, endIndex);
1474
1560
  const total = allThreads.length;
@@ -1483,10 +1569,15 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1483
1569
  } catch (error) {
1484
1570
  throw new MastraError(
1485
1571
  {
1486
- id: createStorageErrorId("DYNAMODB", "LIST_THREADS_BY_RESOURCE_ID", "FAILED"),
1572
+ id: createStorageErrorId("DYNAMODB", "LIST_THREADS", "FAILED"),
1487
1573
  domain: ErrorDomain.STORAGE,
1488
1574
  category: ErrorCategory.THIRD_PARTY,
1489
- details: { resourceId, page, perPage }
1575
+ details: {
1576
+ ...filter?.resourceId && { resourceId: filter.resourceId },
1577
+ hasMetadataFilter: !!(filter?.metadata && Object.keys(filter.metadata).length),
1578
+ page,
1579
+ perPage: perPageForResponse
1580
+ }
1490
1581
  },
1491
1582
  error
1492
1583
  );
@@ -1664,7 +1755,8 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1664
1755
  workingMemory: resource.workingMemory,
1665
1756
  metadata: resource.metadata ? JSON.stringify(resource.metadata) : void 0,
1666
1757
  createdAt: resource.createdAt?.toISOString() || now.toISOString(),
1667
- updatedAt: now.toISOString()
1758
+ updatedAt: now.toISOString(),
1759
+ ...getTtlProps("resource", this.ttlConfig)
1668
1760
  };
1669
1761
  try {
1670
1762
  await this.service.entities.resource.upsert(resourceData).go();
@@ -1739,9 +1831,12 @@ var MemoryStorageDynamoDB = class extends MemoryStorage {
1739
1831
  };
1740
1832
  var ScoresStorageDynamoDB = class extends ScoresStorage {
1741
1833
  service;
1834
+ ttlConfig;
1742
1835
  constructor(config) {
1743
1836
  super();
1744
- this.service = resolveDynamoDBConfig(config);
1837
+ const resolved = resolveDynamoDBConfig(config);
1838
+ this.service = resolved.service;
1839
+ this.ttlConfig = resolved.ttl;
1745
1840
  }
1746
1841
  async dangerouslyClearAll() {
1747
1842
  await deleteTableData(this.service, TABLE_SCORERS);
@@ -1820,6 +1915,8 @@ var ScoresStorageDynamoDB = class extends ScoresStorage {
1820
1915
  const output = typeof validatedScore.output === "string" ? validatedScore.output : JSON.stringify(validatedScore.output);
1821
1916
  const requestContext = typeof validatedScore.requestContext === "string" ? validatedScore.requestContext : JSON.stringify(validatedScore.requestContext);
1822
1917
  const entity = typeof validatedScore.entity === "string" ? validatedScore.entity : JSON.stringify(validatedScore.entity);
1918
+ const metadata = typeof validatedScore.metadata === "string" ? validatedScore.metadata : validatedScore.metadata ? JSON.stringify(validatedScore.metadata) : void 0;
1919
+ const additionalContext = typeof validatedScore.additionalContext === "string" ? validatedScore.additionalContext : validatedScore.additionalContext ? JSON.stringify(validatedScore.additionalContext) : void 0;
1823
1920
  const scoreData = Object.fromEntries(
1824
1921
  Object.entries({
1825
1922
  ...validatedScore,
@@ -1831,13 +1928,16 @@ var ScoresStorageDynamoDB = class extends ScoresStorage {
1831
1928
  input,
1832
1929
  output,
1833
1930
  requestContext,
1931
+ metadata,
1932
+ additionalContext,
1834
1933
  entityData: entity,
1835
1934
  traceId: validatedScore.traceId || "",
1836
1935
  resourceId: validatedScore.resourceId || "",
1837
1936
  threadId: validatedScore.threadId || "",
1838
1937
  spanId: validatedScore.spanId || "",
1839
1938
  createdAt: now.toISOString(),
1840
- updatedAt: now.toISOString()
1939
+ updatedAt: now.toISOString(),
1940
+ ...getTtlProps("score", this.ttlConfig)
1841
1941
  }).filter(([_, value]) => value !== void 0 && value !== null)
1842
1942
  );
1843
1943
  try {
@@ -2044,9 +2144,12 @@ function formatWorkflowRun(snapshotData) {
2044
2144
  }
2045
2145
  var WorkflowStorageDynamoDB = class extends WorkflowsStorage {
2046
2146
  service;
2147
+ ttlConfig;
2047
2148
  constructor(config) {
2048
2149
  super();
2049
- this.service = resolveDynamoDBConfig(config);
2150
+ const resolved = resolveDynamoDBConfig(config);
2151
+ this.service = resolved.service;
2152
+ this.ttlConfig = resolved.ttl;
2050
2153
  }
2051
2154
  async dangerouslyClearAll() {
2052
2155
  await deleteTableData(this.service, TABLE_WORKFLOW_SNAPSHOT);
@@ -2136,13 +2239,13 @@ var WorkflowStorageDynamoDB = class extends WorkflowsStorage {
2136
2239
  const now = /* @__PURE__ */ new Date();
2137
2240
  const data = {
2138
2241
  entity: "workflow_snapshot",
2139
- // Add entity type
2140
2242
  workflow_name: workflowName,
2141
2243
  run_id: runId,
2142
2244
  snapshot: JSON.stringify(snapshot),
2143
2245
  createdAt: (createdAt ?? now).toISOString(),
2144
2246
  updatedAt: (updatedAt ?? now).toISOString(),
2145
- resourceId
2247
+ resourceId,
2248
+ ...getTtlProps("workflow_snapshot", this.ttlConfig)
2146
2249
  };
2147
2250
  await this.service.entities.workflow_snapshot.upsert(data).go();
2148
2251
  } catch (error) {
@@ -2352,10 +2455,11 @@ var WorkflowStorageDynamoDB = class extends WorkflowsStorage {
2352
2455
  var isClientConfig = (config) => {
2353
2456
  return "client" in config;
2354
2457
  };
2355
- var DynamoDBStore = class extends MastraStorage {
2458
+ var DynamoDBStore = class extends MastraCompositeStore {
2356
2459
  tableName;
2357
2460
  client;
2358
2461
  service;
2462
+ ttlConfig;
2359
2463
  hasInitialized = null;
2360
2464
  stores;
2361
2465
  constructor({ name, config }) {
@@ -2370,6 +2474,7 @@ var DynamoDBStore = class extends MastraStorage {
2370
2474
  );
2371
2475
  }
2372
2476
  this.tableName = config.tableName;
2477
+ this.ttlConfig = config.ttl;
2373
2478
  if (isClientConfig(config)) {
2374
2479
  this.client = config.client;
2375
2480
  } else {
@@ -2381,7 +2486,7 @@ var DynamoDBStore = class extends MastraStorage {
2381
2486
  this.client = DynamoDBDocumentClient.from(dynamoClient);
2382
2487
  }
2383
2488
  this.service = getElectroDbService(this.client, this.tableName);
2384
- const domainConfig = { service: this.service };
2489
+ const domainConfig = { service: this.service, ttl: this.ttlConfig };
2385
2490
  const workflows = new WorkflowStorageDynamoDB(domainConfig);
2386
2491
  const memory = new MemoryStorageDynamoDB(domainConfig);
2387
2492
  const scores = new ScoresStorageDynamoDB(domainConfig);
@@ -2491,6 +2596,6 @@ var DynamoDBStore = class extends MastraStorage {
2491
2596
  }
2492
2597
  };
2493
2598
 
2494
- export { DynamoDBStore, MemoryStorageDynamoDB, ScoresStorageDynamoDB, WorkflowStorageDynamoDB };
2599
+ export { DynamoDBStore, MemoryStorageDynamoDB, ScoresStorageDynamoDB, WorkflowStorageDynamoDB, calculateTtl, getTtlAttributeName, getTtlProps, isTtlEnabled };
2495
2600
  //# sourceMappingURL=index.js.map
2496
2601
  //# sourceMappingURL=index.js.map