@mastra/convex 1.2.0 → 1.2.1-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { mastraCache, mastraNativeVectorAction, mastraNativeVectorMutation, mastraNativeVectorQuery, mastraStorage } from './chunk-AJFME2ZF.js';
1
+ export { mastraCache, mastraNativeVectorAction, mastraNativeVectorMutation, mastraNativeVectorQuery, mastraStorage } from './chunk-LXPDHMDN.js';
2
2
  export { TABLE_BACKGROUND_TASKS, TABLE_CHANNEL_CONFIG, TABLE_CHANNEL_INSTALLATIONS, TABLE_MESSAGES, TABLE_RESOURCES, TABLE_SCHEDULES, TABLE_SCHEDULE_TRIGGERS, TABLE_SCORERS, TABLE_THREADS, TABLE_WORKFLOW_SNAPSHOT, defineMastraNativeVectorTable, mastraBackgroundTasksTable, mastraCacheListItemsTable, mastraCacheTable, mastraChannelConfigTable, mastraChannelInstallationsTable, mastraDocumentsTable, mastraMessagesTable, mastraResourcesTable, mastraScheduleTriggersTable, mastraSchedulesTable, mastraScoresTable, mastraThreadsTable, mastraVectorIndexesTable, mastraVectorsTable, mastraWorkflowSnapshotsTable } from './chunk-MC75WADX.js';
3
3
  import { MastraServerCache } from '@mastra/core/cache';
4
4
  import { MastraCompositeStore, createVectorErrorId, MemoryStorage, TABLE_THREADS, TABLE_MESSAGES, TABLE_RESOURCES, createStorageErrorId, normalizePerPage, calculatePagination, filterByDateRange, safelyParseJSON, WorkflowsStorage, TABLE_WORKFLOW_SNAPSHOT, ScoresStorage, TABLE_SCORERS, ChannelsStorage, TABLE_CHANNEL_INSTALLATIONS, TABLE_CHANNEL_CONFIG, SchedulesStorage, TABLE_SCHEDULE_TRIGGERS, TABLE_SCHEDULES, BackgroundTasksStorage, TABLE_BACKGROUND_TASKS } from '@mastra/core/storage';
@@ -221,7 +221,8 @@ var ConvexAdminClient = class {
221
221
  }
222
222
  return {
223
223
  result: storageResponse.result,
224
- hasMore: storageResponse.hasMore
224
+ hasMore: storageResponse.hasMore,
225
+ continuationCursor: storageResponse.continuationCursor
225
226
  };
226
227
  }
227
228
  async callStorage(request) {
@@ -349,6 +350,38 @@ var ConvexDB = class extends MastraBase {
349
350
  record: this.normalizePatch(record)
350
351
  });
351
352
  }
353
+ async updateThread({
354
+ id,
355
+ title,
356
+ metadata,
357
+ updatedAt
358
+ }) {
359
+ return this.client.callStorage({
360
+ op: "updateThread",
361
+ tableName: TABLE_THREADS,
362
+ id,
363
+ title,
364
+ metadata,
365
+ updatedAt: updatedAt.toISOString()
366
+ });
367
+ }
368
+ async updateResource({
369
+ resourceId,
370
+ workingMemory,
371
+ metadata,
372
+ createdAt,
373
+ updatedAt
374
+ }) {
375
+ return this.client.callStorage({
376
+ op: "updateResource",
377
+ tableName: TABLE_RESOURCES,
378
+ resourceId,
379
+ ...workingMemory !== void 0 ? { workingMemory } : {},
380
+ ...metadata !== void 0 ? { metadata } : {},
381
+ createdAt: createdAt.toISOString(),
382
+ updatedAt: updatedAt.toISOString()
383
+ });
384
+ }
352
385
  async load({ tableName, keys }) {
353
386
  const result = await this.client.callStorage({
354
387
  op: "load",
@@ -374,6 +407,44 @@ var ConvexDB = class extends MastraBase {
374
407
  ids
375
408
  });
376
409
  }
410
+ async mergeWorkflowStepResult({
411
+ workflowName,
412
+ runId,
413
+ stepId,
414
+ result,
415
+ requestContext
416
+ }) {
417
+ const context = await this.client.callStorage({
418
+ op: "mergeWorkflowStepResult",
419
+ tableName: TABLE_WORKFLOW_SNAPSHOT,
420
+ workflowName,
421
+ runId,
422
+ stepId,
423
+ result: JSON.stringify(result),
424
+ requestContext: JSON.stringify(requestContext)
425
+ });
426
+ if (!context) {
427
+ throw new Error(`Convex workflow step merge returned no context for runId ${runId}`);
428
+ }
429
+ return JSON.parse(context);
430
+ }
431
+ async mergeWorkflowState({
432
+ workflowName,
433
+ runId,
434
+ opts
435
+ }) {
436
+ const snapshot = await this.client.callStorage({
437
+ op: "mergeWorkflowState",
438
+ tableName: TABLE_WORKFLOW_SNAPSHOT,
439
+ workflowName,
440
+ runId,
441
+ opts: JSON.stringify(opts)
442
+ });
443
+ if (!snapshot) {
444
+ throw new Error(`Convex workflow state merge returned no snapshot for runId ${runId}`);
445
+ }
446
+ return JSON.parse(snapshot);
447
+ }
377
448
  async createSchedule(record) {
378
449
  if (!record.id) {
379
450
  throw new Error(`Schedule is missing an id`);
@@ -797,6 +868,23 @@ var ChannelsConvex = class extends ChannelsStorage {
797
868
  await this.#db.deleteMany(TABLE_CHANNEL_CONFIG, [platform]);
798
869
  }
799
870
  };
871
+ function parseStoredThread(row) {
872
+ return {
873
+ ...row,
874
+ metadata: typeof row.metadata === "string" ? safelyParseJSON(row.metadata) : row.metadata,
875
+ createdAt: new Date(row.createdAt),
876
+ updatedAt: new Date(row.updatedAt)
877
+ };
878
+ }
879
+ function parseStoredResource(record) {
880
+ const metadata = typeof record.metadata === "string" ? safelyParseJSON(record.metadata) : record.metadata;
881
+ return {
882
+ ...record,
883
+ metadata: metadata ?? {},
884
+ createdAt: new Date(record.createdAt),
885
+ updatedAt: new Date(record.updatedAt)
886
+ };
887
+ }
800
888
  var MemoryConvex = class extends MemoryStorage {
801
889
  #db;
802
890
  constructor(config) {
@@ -820,12 +908,7 @@ var MemoryConvex = class extends MemoryStorage {
820
908
  keys: { id: threadId }
821
909
  });
822
910
  if (!row || resourceId !== void 0 && row.resourceId !== resourceId) return null;
823
- return {
824
- ...row,
825
- metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata,
826
- createdAt: new Date(row.createdAt),
827
- updatedAt: new Date(row.updatedAt)
828
- };
911
+ return parseStoredThread(row);
829
912
  }
830
913
  async saveThread({ thread }) {
831
914
  await this.#db.insert({
@@ -842,8 +925,13 @@ var MemoryConvex = class extends MemoryStorage {
842
925
  title,
843
926
  metadata
844
927
  }) {
845
- const existing = await this.getThreadById({ threadId: id });
846
- if (!existing) {
928
+ const updated = await this.#db.updateThread({
929
+ id,
930
+ title,
931
+ metadata,
932
+ updatedAt: /* @__PURE__ */ new Date()
933
+ });
934
+ if (!updated) {
847
935
  throw new MastraError({
848
936
  id: createStorageErrorId("CONVEX", "UPDATE_THREAD", "THREAD_NOT_FOUND"),
849
937
  domain: ErrorDomain.STORAGE,
@@ -851,17 +939,7 @@ var MemoryConvex = class extends MemoryStorage {
851
939
  text: `Thread ${id} not found`
852
940
  });
853
941
  }
854
- const updated = {
855
- ...existing,
856
- title,
857
- metadata: {
858
- ...existing.metadata,
859
- ...metadata
860
- },
861
- updatedAt: /* @__PURE__ */ new Date()
862
- };
863
- await this.saveThread({ thread: updated });
864
- return updated;
942
+ return parseStoredThread(updated);
865
943
  }
866
944
  async deleteThread({ threadId }) {
867
945
  const messages = await this.#db.queryTable(TABLE_MESSAGES, [
@@ -889,6 +967,19 @@ var MemoryConvex = class extends MemoryStorage {
889
967
  );
890
968
  }
891
969
  const perPage = normalizePerPage(perPageInput, 100);
970
+ try {
971
+ this.validateMetadataKeys(filter?.metadata);
972
+ } catch (error) {
973
+ throw new MastraError(
974
+ {
975
+ id: createStorageErrorId("CONVEX", "LIST_THREADS", "INVALID_METADATA_KEY"),
976
+ domain: ErrorDomain.STORAGE,
977
+ category: ErrorCategory.USER,
978
+ details: { metadataKeys: filter?.metadata ? Object.keys(filter.metadata).join(", ") : "" }
979
+ },
980
+ error instanceof Error ? error : new Error("Invalid metadata key")
981
+ );
982
+ }
892
983
  const { field, direction } = this.parseOrderBy(orderBy);
893
984
  const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);
894
985
  const queryFilters = [];
@@ -896,15 +987,10 @@ var MemoryConvex = class extends MemoryStorage {
896
987
  queryFilters.push({ field: "resourceId", value: filter.resourceId });
897
988
  }
898
989
  const rows = await this.#db.queryTable(TABLE_THREADS, queryFilters);
899
- let threads = rows.map((row) => ({
900
- ...row,
901
- metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata,
902
- createdAt: new Date(row.createdAt),
903
- updatedAt: new Date(row.updatedAt)
904
- }));
990
+ let threads = rows.map((row) => parseStoredThread(row));
905
991
  if (filter?.metadata && Object.keys(filter.metadata).length > 0) {
906
992
  threads = threads.filter((thread) => {
907
- if (!thread.metadata) return false;
993
+ if (!thread.metadata || typeof thread.metadata !== "object" || Array.isArray(thread.metadata)) return false;
908
994
  return Object.entries(filter.metadata).every(([key, value]) => thread.metadata[key] === value);
909
995
  });
910
996
  }
@@ -1041,19 +1127,11 @@ var MemoryConvex = class extends MemoryStorage {
1041
1127
  const threadIds = [...new Set(messages.map((m) => m.threadId).filter(Boolean))];
1042
1128
  const now = /* @__PURE__ */ new Date();
1043
1129
  for (const threadId of threadIds) {
1044
- const thread = await this.getThreadById({ threadId });
1045
- if (thread) {
1046
- await this.#db.insert({
1047
- tableName: TABLE_THREADS,
1048
- record: {
1049
- ...thread,
1050
- id: thread.id,
1051
- updatedAt: now.toISOString(),
1052
- createdAt: thread.createdAt instanceof Date ? thread.createdAt.toISOString() : thread.createdAt,
1053
- metadata: thread.metadata ?? {}
1054
- }
1055
- });
1056
- }
1130
+ await this.#db.patch({
1131
+ tableName: TABLE_THREADS,
1132
+ id: threadId,
1133
+ record: { updatedAt: now.toISOString() }
1134
+ });
1057
1135
  }
1058
1136
  const list = new MessageList().add(messages, "memory");
1059
1137
  return { messages: list.get.all.db() };
@@ -1099,19 +1177,11 @@ var MemoryConvex = class extends MemoryStorage {
1099
1177
  }
1100
1178
  const now = /* @__PURE__ */ new Date();
1101
1179
  for (const threadId of affectedThreadIds) {
1102
- const thread = await this.getThreadById({ threadId });
1103
- if (thread) {
1104
- await this.#db.insert({
1105
- tableName: TABLE_THREADS,
1106
- record: {
1107
- ...thread,
1108
- id: thread.id,
1109
- updatedAt: now.toISOString(),
1110
- createdAt: thread.createdAt instanceof Date ? thread.createdAt.toISOString() : thread.createdAt,
1111
- metadata: thread.metadata ?? {}
1112
- }
1113
- });
1114
- }
1180
+ await this.#db.patch({
1181
+ tableName: TABLE_THREADS,
1182
+ id: threadId,
1183
+ record: { updatedAt: now.toISOString() }
1184
+ });
1115
1185
  }
1116
1186
  return updated;
1117
1187
  }
@@ -1139,41 +1209,22 @@ var MemoryConvex = class extends MemoryStorage {
1139
1209
  keys: { id: resourceId }
1140
1210
  });
1141
1211
  if (!record) return null;
1142
- return {
1143
- ...record,
1144
- metadata: typeof record.metadata === "string" ? safelyParseJSON(record.metadata) : record.metadata,
1145
- createdAt: new Date(record.createdAt),
1146
- updatedAt: new Date(record.updatedAt)
1147
- };
1212
+ return parseStoredResource(record);
1148
1213
  }
1149
1214
  async updateResource({
1150
1215
  resourceId,
1151
1216
  workingMemory,
1152
1217
  metadata
1153
1218
  }) {
1154
- const existing = await this.getResourceById({ resourceId });
1155
1219
  const now = /* @__PURE__ */ new Date();
1156
- if (!existing) {
1157
- const created = {
1158
- id: resourceId,
1159
- workingMemory,
1160
- metadata: metadata ?? {},
1161
- createdAt: now,
1162
- updatedAt: now
1163
- };
1164
- return this.saveResource({ resource: created });
1165
- }
1166
- const updated = {
1167
- ...existing,
1168
- workingMemory: workingMemory ?? existing.workingMemory,
1169
- metadata: {
1170
- ...existing.metadata,
1171
- ...metadata
1172
- },
1220
+ const updated = await this.#db.updateResource({
1221
+ resourceId,
1222
+ workingMemory,
1223
+ metadata,
1224
+ createdAt: now,
1173
1225
  updatedAt: now
1174
- };
1175
- await this.saveResource({ resource: updated });
1176
- return updated;
1226
+ });
1227
+ return parseStoredResource(updated);
1177
1228
  }
1178
1229
  _sortMessages(messages, field, direction) {
1179
1230
  return messages.sort((a, b) => {
@@ -1634,22 +1685,18 @@ var WorkflowsConvex = class extends WorkflowsStorage {
1634
1685
  this.#db = new ConvexDB(client);
1635
1686
  }
1636
1687
  supportsConcurrentUpdates() {
1637
- return false;
1688
+ return true;
1638
1689
  }
1639
1690
  async init() {
1640
1691
  }
1641
1692
  async dangerouslyClearAll() {
1642
1693
  await this.#db.clearTable({ tableName: TABLE_WORKFLOW_SNAPSHOT });
1643
1694
  }
1644
- async updateWorkflowResults(_args) {
1645
- throw new Error(
1646
- "updateWorkflowResults is not implemented for Convex storage. Convex does not support atomic read-modify-write operations needed for concurrent workflow updates."
1647
- );
1695
+ async updateWorkflowResults(args) {
1696
+ return this.#db.mergeWorkflowStepResult(args);
1648
1697
  }
1649
- async updateWorkflowState(_args) {
1650
- throw new Error(
1651
- "updateWorkflowState is not implemented for Convex storage. Convex does not support atomic read-modify-write operations needed for concurrent workflow updates."
1652
- );
1698
+ async updateWorkflowState(args) {
1699
+ return this.#db.mergeWorkflowState(args);
1653
1700
  }
1654
1701
  async persistWorkflowSnapshot({
1655
1702
  workflowName,
@@ -1810,6 +1857,7 @@ var ConvexStore = class extends MastraCompositeStore {
1810
1857
  }
1811
1858
  };
1812
1859
  var INDEX_METADATA_TABLE = "mastra_vector_indexes";
1860
+ var VECTOR_QUERY_PAGE_SIZE = 256;
1813
1861
  var ConvexVector = class extends MastraVector {
1814
1862
  client;
1815
1863
  constructor(config) {
@@ -1862,10 +1910,7 @@ var ConvexVector = class extends MastraVector {
1862
1910
  if (!index) {
1863
1911
  throw new Error(`Index ${indexName} not found`);
1864
1912
  }
1865
- const vectors = await this.callStorage({
1866
- op: "queryTable",
1867
- tableName: this.vectorTable(indexName)
1868
- });
1913
+ const vectors = await this.queryAllVectors(indexName);
1869
1914
  return {
1870
1915
  dimension: index.dimension,
1871
1916
  count: vectors.length,
@@ -1902,10 +1947,7 @@ var ConvexVector = class extends MastraVector {
1902
1947
  details: { indexName }
1903
1948
  });
1904
1949
  }
1905
- const vectors = await this.callStorage({
1906
- op: "queryTable",
1907
- tableName: this.vectorTable(indexName)
1908
- });
1950
+ const vectors = await this.queryAllVectors(indexName);
1909
1951
  const filtered = filter && !this.isEmptyFilter(filter) ? vectors.filter((record) => this.matchesFilter(record.metadata, filter)) : vectors;
1910
1952
  const scored = filtered.map((record) => ({
1911
1953
  id: record.id,
@@ -1926,10 +1968,7 @@ var ConvexVector = class extends MastraVector {
1926
1968
  if (this.isEmptyFilter(filter)) {
1927
1969
  throw new Error("ConvexVector.updateVector: cannot update with empty filter");
1928
1970
  }
1929
- const vectors = await this.callStorage({
1930
- op: "queryTable",
1931
- tableName: this.vectorTable(params.indexName)
1932
- });
1971
+ const vectors = await this.queryAllVectors(params.indexName);
1933
1972
  const matching = vectors.filter((record) => this.matchesFilter(record.metadata, filter));
1934
1973
  for (const existing2 of matching) {
1935
1974
  const updated2 = {
@@ -1998,10 +2037,7 @@ var ConvexVector = class extends MastraVector {
1998
2037
  if (this.isEmptyFilter(filter)) {
1999
2038
  throw new Error("ConvexVector.deleteVectors: cannot delete with empty filter");
2000
2039
  }
2001
- const vectors = await this.callStorage({
2002
- op: "queryTable",
2003
- tableName: this.vectorTable(indexName)
2004
- });
2040
+ const vectors = await this.queryAllVectors(indexName);
2005
2041
  const matchingIds = vectors.filter((record) => this.matchesFilter(record.metadata, filter)).map((record) => record.id);
2006
2042
  if (matchingIds.length > 0) {
2007
2043
  await this.callStorage({
@@ -2089,6 +2125,27 @@ var ConvexVector = class extends MastraVector {
2089
2125
  async callStorage(request) {
2090
2126
  return this.client.callStorage(request);
2091
2127
  }
2128
+ async queryAllVectors(indexName) {
2129
+ const vectors = [];
2130
+ let cursor = null;
2131
+ let hasMore = true;
2132
+ while (hasMore) {
2133
+ const response = await this.client.callStorageRaw({
2134
+ op: "queryTable",
2135
+ tableName: this.vectorTable(indexName),
2136
+ pageSize: VECTOR_QUERY_PAGE_SIZE,
2137
+ cursor
2138
+ });
2139
+ vectors.push(...response.result);
2140
+ const nextCursor = response.continuationCursor ?? null;
2141
+ hasMore = response.hasMore ?? false;
2142
+ if (hasMore && (!nextCursor || nextCursor === cursor)) {
2143
+ throw new Error("ConvexVector: paginated vector query did not return a valid continuation cursor");
2144
+ }
2145
+ cursor = nextCursor;
2146
+ }
2147
+ return vectors;
2148
+ }
2092
2149
  /**
2093
2150
  * Call storage repeatedly until hasMore is false.
2094
2151
  * Use for bulk operations like clearTable that may need multiple batches.