@mastra/dynamodb 1.0.0-beta.9 → 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 +763 -0
  2. package/README.md +1 -2
  3. package/dist/docs/README.md +1 -1
  4. package/dist/docs/SKILL.md +1 -1
  5. package/dist/docs/SOURCE_MAP.json +1 -1
  6. package/dist/docs/storage/01-reference.md +93 -2
  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 +149 -30
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.js +147 -32
  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 +5 -5
package/dist/index.cjs CHANGED
@@ -60,6 +60,25 @@ var baseAttributes = {
60
60
  }
61
61
  return value;
62
62
  }
63
+ },
64
+ /**
65
+ * TTL attribute for DynamoDB automatic item expiration.
66
+ * This is a Unix timestamp (epoch seconds) that indicates when the item should be deleted.
67
+ *
68
+ * Note: For TTL to work, you must enable TTL on your DynamoDB table
69
+ * specifying this attribute name (default: 'ttl').
70
+ */
71
+ ttl: {
72
+ type: "number",
73
+ required: false
74
+ },
75
+ /**
76
+ * Alternative TTL attribute with configurable name.
77
+ * Use this if you've configured TTL on your DynamoDB table with 'expiresAt' as the attribute name.
78
+ */
79
+ expiresAt: {
80
+ type: "number",
81
+ required: false
63
82
  }
64
83
  };
65
84
 
@@ -568,6 +587,28 @@ var scoreEntity = new electrodb.Entity({
568
587
  return value;
569
588
  }
570
589
  },
590
+ metadata: {
591
+ type: "string",
592
+ required: false,
593
+ set: (value) => {
594
+ if (value && typeof value !== "string") {
595
+ return JSON.stringify(value);
596
+ }
597
+ return value;
598
+ },
599
+ get: (value) => {
600
+ if (value && typeof value === "string") {
601
+ try {
602
+ if (value.startsWith("{") || value.startsWith("[")) {
603
+ return JSON.parse(value);
604
+ }
605
+ } catch {
606
+ return value;
607
+ }
608
+ }
609
+ return value;
610
+ }
611
+ },
571
612
  requestContext: {
572
613
  type: "string",
573
614
  required: false,
@@ -941,7 +982,7 @@ function getElectroDbService(client, tableName) {
941
982
  }
942
983
  function resolveDynamoDBConfig(config) {
943
984
  if ("service" in config) {
944
- return config.service;
985
+ return { service: config.service, ttl: config.ttl };
945
986
  }
946
987
  const dynamoClient = new clientDynamodb.DynamoDBClient({
947
988
  region: config.region || "us-east-1",
@@ -949,7 +990,36 @@ function resolveDynamoDBConfig(config) {
949
990
  credentials: config.credentials
950
991
  });
951
992
  const client = libDynamodb.DynamoDBDocumentClient.from(dynamoClient);
952
- return getElectroDbService(client, config.tableName);
993
+ return {
994
+ service: getElectroDbService(client, config.tableName),
995
+ ttl: config.ttl
996
+ };
997
+ }
998
+
999
+ // src/storage/ttl.ts
1000
+ function calculateTtl(entityName, ttlConfig, customTtlSeconds) {
1001
+ const entityConfig = ttlConfig?.[entityName];
1002
+ if (!entityConfig?.enabled) {
1003
+ return void 0;
1004
+ }
1005
+ const ttlSeconds = customTtlSeconds ?? entityConfig.defaultTtlSeconds;
1006
+ if (ttlSeconds === void 0 || ttlSeconds <= 0) {
1007
+ return void 0;
1008
+ }
1009
+ return Math.floor(Date.now() / 1e3) + ttlSeconds;
1010
+ }
1011
+ function getTtlAttributeName(entityName, ttlConfig) {
1012
+ const entityConfig = ttlConfig?.[entityName];
1013
+ return entityConfig?.attributeName ?? "ttl";
1014
+ }
1015
+ function isTtlEnabled(entityName, ttlConfig) {
1016
+ return ttlConfig?.[entityName]?.enabled === true;
1017
+ }
1018
+ function getTtlProps(entityName, ttlConfig, customTtlSeconds) {
1019
+ const ttlValue = calculateTtl(entityName, ttlConfig, customTtlSeconds);
1020
+ if (ttlValue === void 0) return {};
1021
+ const attributeName = getTtlAttributeName(entityName, ttlConfig);
1022
+ return { [attributeName]: ttlValue };
953
1023
  }
954
1024
  var ENTITY_MAP = {
955
1025
  [storage.TABLE_THREADS]: "thread",
@@ -997,9 +1067,12 @@ async function deleteTableData(service, tableName) {
997
1067
  // src/storage/domains/memory/index.ts
998
1068
  var MemoryStorageDynamoDB = class extends storage.MemoryStorage {
999
1069
  service;
1070
+ ttlConfig;
1000
1071
  constructor(config) {
1001
1072
  super();
1002
- this.service = resolveDynamoDBConfig(config);
1073
+ const resolved = resolveDynamoDBConfig(config);
1074
+ this.service = resolved.service;
1075
+ this.ttlConfig = resolved.ttl;
1003
1076
  }
1004
1077
  async dangerouslyClearAll() {
1005
1078
  await deleteTableData(this.service, storage.TABLE_THREADS);
@@ -1110,7 +1183,8 @@ var MemoryStorageDynamoDB = class extends storage.MemoryStorage {
1110
1183
  title: thread.title || `Thread ${thread.id}`,
1111
1184
  createdAt: thread.createdAt?.toISOString() || now.toISOString(),
1112
1185
  updatedAt: thread.updatedAt?.toISOString() || now.toISOString(),
1113
- metadata: thread.metadata ? JSON.stringify(thread.metadata) : void 0
1186
+ metadata: thread.metadata ? JSON.stringify(thread.metadata) : void 0,
1187
+ ...getTtlProps("thread", this.ttlConfig)
1114
1188
  };
1115
1189
  try {
1116
1190
  await this.service.entities.thread.upsert(threadData).go();
@@ -1377,20 +1451,18 @@ var MemoryStorageDynamoDB = class extends storage.MemoryStorage {
1377
1451
  const now = (/* @__PURE__ */ new Date()).toISOString();
1378
1452
  return {
1379
1453
  entity: "message",
1380
- // Add entity type
1381
1454
  id: msg.id,
1382
1455
  threadId: msg.threadId,
1383
1456
  role: msg.role,
1384
1457
  type: msg.type,
1385
1458
  resourceId: msg.resourceId,
1386
- // Ensure complex fields are stringified if not handled by attribute setters
1387
1459
  content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
1388
1460
  toolCallArgs: `toolCallArgs` in msg && msg.toolCallArgs ? JSON.stringify(msg.toolCallArgs) : void 0,
1389
1461
  toolCallIds: `toolCallIds` in msg && msg.toolCallIds ? JSON.stringify(msg.toolCallIds) : void 0,
1390
1462
  toolNames: `toolNames` in msg && msg.toolNames ? JSON.stringify(msg.toolNames) : void 0,
1391
1463
  createdAt: msg.createdAt instanceof Date ? msg.createdAt.toISOString() : msg.createdAt || now,
1392
- updatedAt: now
1393
- // Add updatedAt
1464
+ updatedAt: now,
1465
+ ...getTtlProps("message", this.ttlConfig)
1394
1466
  };
1395
1467
  });
1396
1468
  try {
@@ -1434,33 +1506,57 @@ var MemoryStorageDynamoDB = class extends storage.MemoryStorage {
1434
1506
  );
1435
1507
  }
1436
1508
  }
1437
- async listThreadsByResourceId(args) {
1438
- const { resourceId, page = 0, perPage: perPageInput, orderBy } = args;
1439
- const perPage = storage.normalizePerPage(perPageInput, 100);
1440
- if (page < 0) {
1509
+ async listThreads(args) {
1510
+ const { page = 0, perPage: perPageInput, orderBy, filter } = args;
1511
+ try {
1512
+ this.validatePaginationInput(page, perPageInput ?? 100);
1513
+ } catch (error$1) {
1441
1514
  throw new error.MastraError(
1442
1515
  {
1443
- id: storage.createStorageErrorId("DYNAMODB", "LIST_THREADS_BY_RESOURCE_ID", "INVALID_PAGE"),
1516
+ id: storage.createStorageErrorId("DYNAMODB", "LIST_THREADS", "INVALID_PAGE"),
1444
1517
  domain: error.ErrorDomain.STORAGE,
1445
1518
  category: error.ErrorCategory.USER,
1446
- details: { page }
1519
+ details: {
1520
+ page,
1521
+ ...perPageInput !== void 0 && { perPage: perPageInput }
1522
+ }
1447
1523
  },
1448
- new Error("page must be >= 0")
1524
+ error$1 instanceof Error ? error$1 : new Error("Invalid pagination parameters")
1449
1525
  );
1450
1526
  }
1527
+ const perPage = storage.normalizePerPage(perPageInput, 100);
1451
1528
  const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
1452
1529
  const { field, direction } = this.parseOrderBy(orderBy);
1453
- this.logger.debug("Getting threads by resource ID with pagination", {
1454
- resourceId,
1530
+ this.logger.debug("Listing threads with filters", {
1531
+ resourceId: filter?.resourceId,
1532
+ metadataKeys: filter?.metadata ? Object.keys(filter.metadata) : [],
1455
1533
  page,
1456
1534
  perPage,
1457
1535
  field,
1458
1536
  direction
1459
1537
  });
1460
1538
  try {
1461
- const query = this.service.entities.thread.query.byResource({ entity: "thread", resourceId });
1462
- const results = await query.go();
1463
- const allThreads = this.transformAndSortThreads(results.data, field, direction);
1539
+ const rawThreads = filter?.resourceId ? (await this.service.entities.thread.query.byResource({
1540
+ entity: "thread",
1541
+ resourceId: filter.resourceId
1542
+ }).go({ pages: "all" })).data : (await this.service.entities.thread.scan.go({ pages: "all" })).data;
1543
+ let allThreads = this.transformAndSortThreads(rawThreads, field, direction);
1544
+ if (filter?.metadata && Object.keys(filter.metadata).length > 0) {
1545
+ allThreads = allThreads.filter((thread) => {
1546
+ let threadMeta = null;
1547
+ if (typeof thread.metadata === "string") {
1548
+ try {
1549
+ threadMeta = JSON.parse(thread.metadata);
1550
+ } catch {
1551
+ return false;
1552
+ }
1553
+ } else if (thread.metadata && typeof thread.metadata === "object") {
1554
+ threadMeta = thread.metadata;
1555
+ }
1556
+ if (!threadMeta) return false;
1557
+ return Object.entries(filter.metadata).every(([key, value]) => threadMeta[key] === value);
1558
+ });
1559
+ }
1464
1560
  const endIndex = offset + perPage;
1465
1561
  const paginatedThreads = allThreads.slice(offset, endIndex);
1466
1562
  const total = allThreads.length;
@@ -1475,10 +1571,15 @@ var MemoryStorageDynamoDB = class extends storage.MemoryStorage {
1475
1571
  } catch (error$1) {
1476
1572
  throw new error.MastraError(
1477
1573
  {
1478
- id: storage.createStorageErrorId("DYNAMODB", "LIST_THREADS_BY_RESOURCE_ID", "FAILED"),
1574
+ id: storage.createStorageErrorId("DYNAMODB", "LIST_THREADS", "FAILED"),
1479
1575
  domain: error.ErrorDomain.STORAGE,
1480
1576
  category: error.ErrorCategory.THIRD_PARTY,
1481
- details: { resourceId, page, perPage }
1577
+ details: {
1578
+ ...filter?.resourceId && { resourceId: filter.resourceId },
1579
+ hasMetadataFilter: !!(filter?.metadata && Object.keys(filter.metadata).length),
1580
+ page,
1581
+ perPage: perPageForResponse
1582
+ }
1482
1583
  },
1483
1584
  error$1
1484
1585
  );
@@ -1656,7 +1757,8 @@ var MemoryStorageDynamoDB = class extends storage.MemoryStorage {
1656
1757
  workingMemory: resource.workingMemory,
1657
1758
  metadata: resource.metadata ? JSON.stringify(resource.metadata) : void 0,
1658
1759
  createdAt: resource.createdAt?.toISOString() || now.toISOString(),
1659
- updatedAt: now.toISOString()
1760
+ updatedAt: now.toISOString(),
1761
+ ...getTtlProps("resource", this.ttlConfig)
1660
1762
  };
1661
1763
  try {
1662
1764
  await this.service.entities.resource.upsert(resourceData).go();
@@ -1731,9 +1833,12 @@ var MemoryStorageDynamoDB = class extends storage.MemoryStorage {
1731
1833
  };
1732
1834
  var ScoresStorageDynamoDB = class extends storage.ScoresStorage {
1733
1835
  service;
1836
+ ttlConfig;
1734
1837
  constructor(config) {
1735
1838
  super();
1736
- this.service = resolveDynamoDBConfig(config);
1839
+ const resolved = resolveDynamoDBConfig(config);
1840
+ this.service = resolved.service;
1841
+ this.ttlConfig = resolved.ttl;
1737
1842
  }
1738
1843
  async dangerouslyClearAll() {
1739
1844
  await deleteTableData(this.service, storage.TABLE_SCORERS);
@@ -1812,6 +1917,8 @@ var ScoresStorageDynamoDB = class extends storage.ScoresStorage {
1812
1917
  const output = typeof validatedScore.output === "string" ? validatedScore.output : JSON.stringify(validatedScore.output);
1813
1918
  const requestContext = typeof validatedScore.requestContext === "string" ? validatedScore.requestContext : JSON.stringify(validatedScore.requestContext);
1814
1919
  const entity = typeof validatedScore.entity === "string" ? validatedScore.entity : JSON.stringify(validatedScore.entity);
1920
+ const metadata = typeof validatedScore.metadata === "string" ? validatedScore.metadata : validatedScore.metadata ? JSON.stringify(validatedScore.metadata) : void 0;
1921
+ const additionalContext = typeof validatedScore.additionalContext === "string" ? validatedScore.additionalContext : validatedScore.additionalContext ? JSON.stringify(validatedScore.additionalContext) : void 0;
1815
1922
  const scoreData = Object.fromEntries(
1816
1923
  Object.entries({
1817
1924
  ...validatedScore,
@@ -1823,13 +1930,16 @@ var ScoresStorageDynamoDB = class extends storage.ScoresStorage {
1823
1930
  input,
1824
1931
  output,
1825
1932
  requestContext,
1933
+ metadata,
1934
+ additionalContext,
1826
1935
  entityData: entity,
1827
1936
  traceId: validatedScore.traceId || "",
1828
1937
  resourceId: validatedScore.resourceId || "",
1829
1938
  threadId: validatedScore.threadId || "",
1830
1939
  spanId: validatedScore.spanId || "",
1831
1940
  createdAt: now.toISOString(),
1832
- updatedAt: now.toISOString()
1941
+ updatedAt: now.toISOString(),
1942
+ ...getTtlProps("score", this.ttlConfig)
1833
1943
  }).filter(([_, value]) => value !== void 0 && value !== null)
1834
1944
  );
1835
1945
  try {
@@ -2036,9 +2146,12 @@ function formatWorkflowRun(snapshotData) {
2036
2146
  }
2037
2147
  var WorkflowStorageDynamoDB = class extends storage.WorkflowsStorage {
2038
2148
  service;
2149
+ ttlConfig;
2039
2150
  constructor(config) {
2040
2151
  super();
2041
- this.service = resolveDynamoDBConfig(config);
2152
+ const resolved = resolveDynamoDBConfig(config);
2153
+ this.service = resolved.service;
2154
+ this.ttlConfig = resolved.ttl;
2042
2155
  }
2043
2156
  async dangerouslyClearAll() {
2044
2157
  await deleteTableData(this.service, storage.TABLE_WORKFLOW_SNAPSHOT);
@@ -2128,13 +2241,13 @@ var WorkflowStorageDynamoDB = class extends storage.WorkflowsStorage {
2128
2241
  const now = /* @__PURE__ */ new Date();
2129
2242
  const data = {
2130
2243
  entity: "workflow_snapshot",
2131
- // Add entity type
2132
2244
  workflow_name: workflowName,
2133
2245
  run_id: runId,
2134
2246
  snapshot: JSON.stringify(snapshot),
2135
2247
  createdAt: (createdAt ?? now).toISOString(),
2136
2248
  updatedAt: (updatedAt ?? now).toISOString(),
2137
- resourceId
2249
+ resourceId,
2250
+ ...getTtlProps("workflow_snapshot", this.ttlConfig)
2138
2251
  };
2139
2252
  await this.service.entities.workflow_snapshot.upsert(data).go();
2140
2253
  } catch (error$1) {
@@ -2344,10 +2457,11 @@ var WorkflowStorageDynamoDB = class extends storage.WorkflowsStorage {
2344
2457
  var isClientConfig = (config) => {
2345
2458
  return "client" in config;
2346
2459
  };
2347
- var DynamoDBStore = class extends storage.MastraStorage {
2460
+ var DynamoDBStore = class extends storage.MastraCompositeStore {
2348
2461
  tableName;
2349
2462
  client;
2350
2463
  service;
2464
+ ttlConfig;
2351
2465
  hasInitialized = null;
2352
2466
  stores;
2353
2467
  constructor({ name, config }) {
@@ -2362,6 +2476,7 @@ var DynamoDBStore = class extends storage.MastraStorage {
2362
2476
  );
2363
2477
  }
2364
2478
  this.tableName = config.tableName;
2479
+ this.ttlConfig = config.ttl;
2365
2480
  if (isClientConfig(config)) {
2366
2481
  this.client = config.client;
2367
2482
  } else {
@@ -2373,7 +2488,7 @@ var DynamoDBStore = class extends storage.MastraStorage {
2373
2488
  this.client = libDynamodb.DynamoDBDocumentClient.from(dynamoClient);
2374
2489
  }
2375
2490
  this.service = getElectroDbService(this.client, this.tableName);
2376
- const domainConfig = { service: this.service };
2491
+ const domainConfig = { service: this.service, ttl: this.ttlConfig };
2377
2492
  const workflows = new WorkflowStorageDynamoDB(domainConfig);
2378
2493
  const memory = new MemoryStorageDynamoDB(domainConfig);
2379
2494
  const scores = new ScoresStorageDynamoDB(domainConfig);
@@ -2487,5 +2602,9 @@ exports.DynamoDBStore = DynamoDBStore;
2487
2602
  exports.MemoryStorageDynamoDB = MemoryStorageDynamoDB;
2488
2603
  exports.ScoresStorageDynamoDB = ScoresStorageDynamoDB;
2489
2604
  exports.WorkflowStorageDynamoDB = WorkflowStorageDynamoDB;
2605
+ exports.calculateTtl = calculateTtl;
2606
+ exports.getTtlAttributeName = getTtlAttributeName;
2607
+ exports.getTtlProps = getTtlProps;
2608
+ exports.isTtlEnabled = isTtlEnabled;
2490
2609
  //# sourceMappingURL=index.cjs.map
2491
2610
  //# sourceMappingURL=index.cjs.map