@mastra/mongodb 0.0.0-vnextAgentNetwork-20250602134426 → 0.0.0-workflow-deno-20250616115451

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.
@@ -0,0 +1,23 @@
1
+
2
+ > @mastra/mongodb@0.11.0-alpha.1 build /home/runner/work/mastra/mastra/stores/mongodb
3
+ > tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting
4
+
5
+ CLI Building entry: src/index.ts
6
+ CLI Using tsconfig: tsconfig.json
7
+ CLI tsup v8.5.0
8
+ TSC Build start
9
+ TSC ⚡️ Build success in 9462ms
10
+ DTS Build start
11
+ CLI Target: es2022
12
+ Analysis will use the bundled TypeScript version 5.8.3
13
+ Writing package typings: /home/runner/work/mastra/mastra/stores/mongodb/dist/_tsup-dts-rollup.d.ts
14
+ Analysis will use the bundled TypeScript version 5.8.3
15
+ Writing package typings: /home/runner/work/mastra/mastra/stores/mongodb/dist/_tsup-dts-rollup.d.cts
16
+ DTS ⚡️ Build success in 11575ms
17
+ CLI Cleaning output folder
18
+ ESM Build start
19
+ CJS Build start
20
+ ESM dist/index.js 33.93 KB
21
+ ESM ⚡️ Build success in 1213ms
22
+ CJS dist/index.cjs 34.07 KB
23
+ CJS ⚡️ Build success in 1213ms
package/CHANGELOG.md CHANGED
@@ -1,6 +1,109 @@
1
1
  # @mastra/mongodb
2
2
 
3
- ## 0.0.0-vnextAgentNetwork-20250602134426
3
+ ## 0.0.0-workflow-deno-20250616115451
4
+
5
+ ### Minor Changes
6
+
7
+ - 704d1ca: Thread Timestamp Auto-Update Enhancement
8
+ Added automatic thread updatedAt timestamp updates when messages are saved across all storage providers
9
+ Enhanced user experience: Threads now accurately reflect their latest activity with automatic timestamp updates when new messages are added
10
+ Universal implementation: Consistent behavior across all 7 storage backends (ClickHouse, Cloudflare D1, DynamoDB, MongoDB, PostgreSQL, Upstash, LibSQL)
11
+ Performance optimized: Updates execute in parallel with message saving operations for minimal performance impact
12
+ Backwards compatible: No breaking changes - existing code continues to work unchanged
13
+ Improved conversation ordering: Chat interfaces can now properly sort threads by actual last activity
14
+ This enhancement resolves the issue where active conversations appeared stale due to outdated thread timestamps, providing better conversation management and user experience in chat applications.
15
+
16
+ ### Patch Changes
17
+
18
+ - 63f6b7d: dependencies updates:
19
+ - Updated dependency [`cloudflare@^4.3.0` ↗︎](https://www.npmjs.com/package/cloudflare/v/4.3.0) (from `^4.1.0`, in `dependencies`)
20
+ - Updated dependency [`mongodb@^6.17.0` ↗︎](https://www.npmjs.com/package/mongodb/v/6.17.0) (from `^6.15.0`, in `dependencies`)
21
+ - Updated dependencies [63f6b7d]
22
+ - Updated dependencies [ee9af57]
23
+ - Updated dependencies [36f1c36]
24
+ - Updated dependencies [10d352e]
25
+ - Updated dependencies [3ca9a67]
26
+ - Updated dependencies [53d3c37]
27
+ - Updated dependencies [577ce3a]
28
+ - Updated dependencies [9260b3a]
29
+ - @mastra/core@0.0.0-workflow-deno-20250616115451
30
+
31
+ ## 0.11.0-alpha.1
32
+
33
+ ### Minor Changes
34
+
35
+ - 704d1ca: Thread Timestamp Auto-Update Enhancement
36
+ Added automatic thread updatedAt timestamp updates when messages are saved across all storage providers
37
+ Enhanced user experience: Threads now accurately reflect their latest activity with automatic timestamp updates when new messages are added
38
+ Universal implementation: Consistent behavior across all 7 storage backends (ClickHouse, Cloudflare D1, DynamoDB, MongoDB, PostgreSQL, Upstash, LibSQL)
39
+ Performance optimized: Updates execute in parallel with message saving operations for minimal performance impact
40
+ Backwards compatible: No breaking changes - existing code continues to work unchanged
41
+ Improved conversation ordering: Chat interfaces can now properly sort threads by actual last activity
42
+ This enhancement resolves the issue where active conversations appeared stale due to outdated thread timestamps, providing better conversation management and user experience in chat applications.
43
+
44
+ ## 0.10.4-alpha.0
45
+
46
+ ### Patch Changes
47
+
48
+ - 63f6b7d: dependencies updates:
49
+ - Updated dependency [`cloudflare@^4.3.0` ↗︎](https://www.npmjs.com/package/cloudflare/v/4.3.0) (from `^4.1.0`, in `dependencies`)
50
+ - Updated dependency [`mongodb@^6.17.0` ↗︎](https://www.npmjs.com/package/mongodb/v/6.17.0) (from `^6.15.0`, in `dependencies`)
51
+ - Updated dependencies [63f6b7d]
52
+ - Updated dependencies [36f1c36]
53
+ - Updated dependencies [10d352e]
54
+ - Updated dependencies [53d3c37]
55
+ - @mastra/core@0.10.6-alpha.0
56
+
57
+ ## 0.10.3
58
+
59
+ ### Patch Changes
60
+
61
+ - dffb67b: updated stores to add alter table and change tests
62
+ - 925ab94: added paginated functions to base class and added boilerplate and updated imports
63
+ - e030ea3: Added missing format compatibility to MongoDB getMessages() method
64
+ - 66f4424: Update peerdeps
65
+ - Updated dependencies [d1ed912]
66
+ - Updated dependencies [f6fd25f]
67
+ - Updated dependencies [dffb67b]
68
+ - Updated dependencies [f1f1f1b]
69
+ - Updated dependencies [925ab94]
70
+ - Updated dependencies [f9816ae]
71
+ - Updated dependencies [82090c1]
72
+ - Updated dependencies [1b443fd]
73
+ - Updated dependencies [ce97900]
74
+ - Updated dependencies [f1309d3]
75
+ - Updated dependencies [14a2566]
76
+ - Updated dependencies [f7f8293]
77
+ - Updated dependencies [48eddb9]
78
+ - @mastra/core@0.10.4
79
+
80
+ ## 0.10.3-alpha.2
81
+
82
+ ### Patch Changes
83
+
84
+ - 66f4424: Update peerdeps
85
+
86
+ ## 0.10.3-alpha.1
87
+
88
+ ### Patch Changes
89
+
90
+ - 925ab94: added paginated functions to base class and added boilerplate and updated imports
91
+ - Updated dependencies [925ab94]
92
+ - @mastra/core@0.10.4-alpha.3
93
+
94
+ ## 0.10.3-alpha.0
95
+
96
+ ### Patch Changes
97
+
98
+ - dffb67b: updated stores to add alter table and change tests
99
+ - e030ea3: Added missing format compatibility to MongoDB getMessages() method
100
+ - Updated dependencies [f6fd25f]
101
+ - Updated dependencies [dffb67b]
102
+ - Updated dependencies [f1309d3]
103
+ - Updated dependencies [f7f8293]
104
+ - @mastra/core@0.10.4-alpha.1
105
+
106
+ ## 0.10.2
4
107
 
5
108
  ### Patch Changes
6
109
 
@@ -11,12 +114,19 @@
11
114
  - Updated dependencies [e5dc18d]
12
115
  - Updated dependencies [ab5adbe]
13
116
  - Updated dependencies [1e8bb40]
117
+ - Updated dependencies [1b5fc55]
14
118
  - Updated dependencies [195c428]
15
119
  - Updated dependencies [f73e11b]
120
+ - Updated dependencies [37643b8]
121
+ - Updated dependencies [99fd6cf]
16
122
  - Updated dependencies [c5bf1ce]
123
+ - Updated dependencies [add596e]
124
+ - Updated dependencies [8dc94d8]
125
+ - Updated dependencies [ecebbeb]
126
+ - Updated dependencies [79d5145]
17
127
  - Updated dependencies [12b7002]
18
128
  - Updated dependencies [2901125]
19
- - @mastra/core@0.0.0-vnextAgentNetwork-20250602134426
129
+ - @mastra/core@0.10.2
20
130
 
21
131
  ## 0.10.2-alpha.1
22
132
 
@@ -11,11 +11,15 @@ import { MastraStorage } from '@mastra/core/storage';
11
11
  import { MastraVector } from '@mastra/core/vector';
12
12
  import type { MongoClientOptions } from 'mongodb';
13
13
  import type { OperatorSupport } from '@mastra/core/vector/filter';
14
+ import type { PaginationInfo } from '@mastra/core/storage';
14
15
  import type { QueryResult } from '@mastra/core/vector';
15
16
  import type { QueryVectorParams } from '@mastra/core/vector';
17
+ import type { StorageColumn } from '@mastra/core/storage';
16
18
  import type { StorageGetMessagesArg } from '@mastra/core/storage';
19
+ import type { StorageGetTracesArg } from '@mastra/core/storage';
17
20
  import type { StorageThreadType } from '@mastra/core/memory';
18
21
  import type { TABLE_NAMES } from '@mastra/core/storage';
22
+ import type { Trace } from '@mastra/core/telemetry';
19
23
  import type { UpdateVectorParams } from '@mastra/core/vector';
20
24
  import type { UpsertVectorParams } from '@mastra/core/vector';
21
25
  import type { VectorFilter } from '@mastra/core/vector/filter';
@@ -71,6 +75,17 @@ declare class MongoDBStore extends MastraStorage {
71
75
  private getConnection;
72
76
  private getCollection;
73
77
  createTable(): Promise<void>;
78
+ /**
79
+ * No-op: This backend is schemaless and does not require schema changes.
80
+ * @param tableName Name of the table
81
+ * @param schema Schema of the table
82
+ * @param ifNotExists Array of column names to add if they don't exist
83
+ */
84
+ alterTable(_args: {
85
+ tableName: TABLE_NAMES;
86
+ schema: Record<string, StorageColumn>;
87
+ ifNotExists: string[];
88
+ }): Promise<void>;
74
89
  clearTable({ tableName }: {
75
90
  tableName: TABLE_NAMES;
76
91
  }): Promise<void>;
@@ -103,7 +118,12 @@ declare class MongoDBStore extends MastraStorage {
103
118
  deleteThread({ threadId }: {
104
119
  threadId: string;
105
120
  }): Promise<void>;
106
- getMessages<T = unknown>({ threadId, selectBy }: StorageGetMessagesArg): Promise<T[]>;
121
+ getMessages(args: StorageGetMessagesArg & {
122
+ format?: 'v1';
123
+ }): Promise<MastraMessageV1[]>;
124
+ getMessages(args: StorageGetMessagesArg & {
125
+ format: 'v2';
126
+ }): Promise<MastraMessageV2[]>;
107
127
  saveMessages(args: {
108
128
  messages: MastraMessageV1[];
109
129
  format?: undefined | 'v1';
@@ -153,6 +173,19 @@ declare class MongoDBStore extends MastraStorage {
153
173
  private parseWorkflowRun;
154
174
  private parseRow;
155
175
  private transformEvalRow;
176
+ getTracesPaginated(_args: StorageGetTracesArg): Promise<PaginationInfo & {
177
+ traces: Trace[];
178
+ }>;
179
+ getThreadsByResourceIdPaginated(_args: {
180
+ resourceId: string;
181
+ page?: number;
182
+ perPage?: number;
183
+ }): Promise<PaginationInfo & {
184
+ threads: StorageThreadType[];
185
+ }>;
186
+ getMessagesPaginated(_args: StorageGetMessagesArg): Promise<PaginationInfo & {
187
+ messages: MastraMessageV1[] | MastraMessageV2[];
188
+ }>;
156
189
  close(): Promise<void>;
157
190
  }
158
191
  export { MongoDBStore }
@@ -11,11 +11,15 @@ import { MastraStorage } from '@mastra/core/storage';
11
11
  import { MastraVector } from '@mastra/core/vector';
12
12
  import type { MongoClientOptions } from 'mongodb';
13
13
  import type { OperatorSupport } from '@mastra/core/vector/filter';
14
+ import type { PaginationInfo } from '@mastra/core/storage';
14
15
  import type { QueryResult } from '@mastra/core/vector';
15
16
  import type { QueryVectorParams } from '@mastra/core/vector';
17
+ import type { StorageColumn } from '@mastra/core/storage';
16
18
  import type { StorageGetMessagesArg } from '@mastra/core/storage';
19
+ import type { StorageGetTracesArg } from '@mastra/core/storage';
17
20
  import type { StorageThreadType } from '@mastra/core/memory';
18
21
  import type { TABLE_NAMES } from '@mastra/core/storage';
22
+ import type { Trace } from '@mastra/core/telemetry';
19
23
  import type { UpdateVectorParams } from '@mastra/core/vector';
20
24
  import type { UpsertVectorParams } from '@mastra/core/vector';
21
25
  import type { VectorFilter } from '@mastra/core/vector/filter';
@@ -71,6 +75,17 @@ declare class MongoDBStore extends MastraStorage {
71
75
  private getConnection;
72
76
  private getCollection;
73
77
  createTable(): Promise<void>;
78
+ /**
79
+ * No-op: This backend is schemaless and does not require schema changes.
80
+ * @param tableName Name of the table
81
+ * @param schema Schema of the table
82
+ * @param ifNotExists Array of column names to add if they don't exist
83
+ */
84
+ alterTable(_args: {
85
+ tableName: TABLE_NAMES;
86
+ schema: Record<string, StorageColumn>;
87
+ ifNotExists: string[];
88
+ }): Promise<void>;
74
89
  clearTable({ tableName }: {
75
90
  tableName: TABLE_NAMES;
76
91
  }): Promise<void>;
@@ -103,7 +118,12 @@ declare class MongoDBStore extends MastraStorage {
103
118
  deleteThread({ threadId }: {
104
119
  threadId: string;
105
120
  }): Promise<void>;
106
- getMessages<T = unknown>({ threadId, selectBy }: StorageGetMessagesArg): Promise<T[]>;
121
+ getMessages(args: StorageGetMessagesArg & {
122
+ format?: 'v1';
123
+ }): Promise<MastraMessageV1[]>;
124
+ getMessages(args: StorageGetMessagesArg & {
125
+ format: 'v2';
126
+ }): Promise<MastraMessageV2[]>;
107
127
  saveMessages(args: {
108
128
  messages: MastraMessageV1[];
109
129
  format?: undefined | 'v1';
@@ -153,6 +173,19 @@ declare class MongoDBStore extends MastraStorage {
153
173
  private parseWorkflowRun;
154
174
  private parseRow;
155
175
  private transformEvalRow;
176
+ getTracesPaginated(_args: StorageGetTracesArg): Promise<PaginationInfo & {
177
+ traces: Trace[];
178
+ }>;
179
+ getThreadsByResourceIdPaginated(_args: {
180
+ resourceId: string;
181
+ page?: number;
182
+ perPage?: number;
183
+ }): Promise<PaginationInfo & {
184
+ threads: StorageThreadType[];
185
+ }>;
186
+ getMessagesPaginated(_args: StorageGetMessagesArg): Promise<PaginationInfo & {
187
+ messages: MastraMessageV1[] | MastraMessageV2[];
188
+ }>;
156
189
  close(): Promise<void>;
157
190
  }
158
191
  export { MongoDBStore }
package/dist/index.cjs CHANGED
@@ -447,6 +447,14 @@ var MongoDBStore = class extends storage.MastraStorage {
447
447
  }
448
448
  async createTable() {
449
449
  }
450
+ /**
451
+ * No-op: This backend is schemaless and does not require schema changes.
452
+ * @param tableName Name of the table
453
+ * @param schema Schema of the table
454
+ * @param ifNotExists Array of column names to add if they don't exist
455
+ */
456
+ async alterTable(_args) {
457
+ }
450
458
  async clearTable({ tableName }) {
451
459
  try {
452
460
  const collection = await this.getCollection(tableName);
@@ -584,7 +592,11 @@ var MongoDBStore = class extends storage.MastraStorage {
584
592
  throw error;
585
593
  }
586
594
  }
587
- async getMessages({ threadId, selectBy }) {
595
+ async getMessages({
596
+ threadId,
597
+ selectBy,
598
+ format
599
+ }) {
588
600
  try {
589
601
  const limit = typeof selectBy?.last === "number" ? selectBy.last : 40;
590
602
  const include = selectBy?.include || [];
@@ -623,7 +635,9 @@ var MongoDBStore = class extends storage.MastraStorage {
623
635
  }
624
636
  }
625
637
  messages.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
626
- return messages.slice(0, limit);
638
+ const list = new agent.MessageList().add(messages.slice(0, limit), "memory");
639
+ if (format === `v2`) return list.get.all.v2();
640
+ return list.get.all.v1();
627
641
  } catch (error) {
628
642
  this.logger.error("Error getting messages:", error);
629
643
  throw error;
@@ -655,7 +669,11 @@ var MongoDBStore = class extends storage.MastraStorage {
655
669
  };
656
670
  });
657
671
  const collection = await this.getCollection(storage.TABLE_MESSAGES);
658
- await collection.insertMany(messagesToInsert);
672
+ const threadsCollection = await this.getCollection(storage.TABLE_THREADS);
673
+ await Promise.all([
674
+ collection.insertMany(messagesToInsert),
675
+ threadsCollection.updateOne({ id: threadId }, { $set: { updatedAt: /* @__PURE__ */ new Date() } })
676
+ ]);
659
677
  const list = new agent.MessageList().add(messages, "memory");
660
678
  if (format === `v2`) return list.get.all.v2();
661
679
  return list.get.all.v1();
@@ -898,7 +916,8 @@ var MongoDBStore = class extends storage.MastraStorage {
898
916
  role: row.role,
899
917
  type: row.type,
900
918
  createdAt: new Date(row.createdAt),
901
- threadId: row.thread_id
919
+ threadId: row.thread_id,
920
+ resourceId: row.resourceId
902
921
  };
903
922
  }
904
923
  transformEvalRow(row) {
@@ -923,6 +942,15 @@ var MongoDBStore = class extends storage.MastraStorage {
923
942
  createdAt: row.created_at
924
943
  };
925
944
  }
945
+ async getTracesPaginated(_args) {
946
+ throw new Error("Method not implemented.");
947
+ }
948
+ async getThreadsByResourceIdPaginated(_args) {
949
+ throw new Error("Method not implemented.");
950
+ }
951
+ async getMessagesPaginated(_args) {
952
+ throw new Error("Method not implemented.");
953
+ }
926
954
  async close() {
927
955
  await this.#client.close();
928
956
  }
package/dist/index.js CHANGED
@@ -445,6 +445,14 @@ var MongoDBStore = class extends MastraStorage {
445
445
  }
446
446
  async createTable() {
447
447
  }
448
+ /**
449
+ * No-op: This backend is schemaless and does not require schema changes.
450
+ * @param tableName Name of the table
451
+ * @param schema Schema of the table
452
+ * @param ifNotExists Array of column names to add if they don't exist
453
+ */
454
+ async alterTable(_args) {
455
+ }
448
456
  async clearTable({ tableName }) {
449
457
  try {
450
458
  const collection = await this.getCollection(tableName);
@@ -582,7 +590,11 @@ var MongoDBStore = class extends MastraStorage {
582
590
  throw error;
583
591
  }
584
592
  }
585
- async getMessages({ threadId, selectBy }) {
593
+ async getMessages({
594
+ threadId,
595
+ selectBy,
596
+ format
597
+ }) {
586
598
  try {
587
599
  const limit = typeof selectBy?.last === "number" ? selectBy.last : 40;
588
600
  const include = selectBy?.include || [];
@@ -621,7 +633,9 @@ var MongoDBStore = class extends MastraStorage {
621
633
  }
622
634
  }
623
635
  messages.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
624
- return messages.slice(0, limit);
636
+ const list = new MessageList().add(messages.slice(0, limit), "memory");
637
+ if (format === `v2`) return list.get.all.v2();
638
+ return list.get.all.v1();
625
639
  } catch (error) {
626
640
  this.logger.error("Error getting messages:", error);
627
641
  throw error;
@@ -653,7 +667,11 @@ var MongoDBStore = class extends MastraStorage {
653
667
  };
654
668
  });
655
669
  const collection = await this.getCollection(TABLE_MESSAGES);
656
- await collection.insertMany(messagesToInsert);
670
+ const threadsCollection = await this.getCollection(TABLE_THREADS);
671
+ await Promise.all([
672
+ collection.insertMany(messagesToInsert),
673
+ threadsCollection.updateOne({ id: threadId }, { $set: { updatedAt: /* @__PURE__ */ new Date() } })
674
+ ]);
657
675
  const list = new MessageList().add(messages, "memory");
658
676
  if (format === `v2`) return list.get.all.v2();
659
677
  return list.get.all.v1();
@@ -896,7 +914,8 @@ var MongoDBStore = class extends MastraStorage {
896
914
  role: row.role,
897
915
  type: row.type,
898
916
  createdAt: new Date(row.createdAt),
899
- threadId: row.thread_id
917
+ threadId: row.thread_id,
918
+ resourceId: row.resourceId
900
919
  };
901
920
  }
902
921
  transformEvalRow(row) {
@@ -921,6 +940,15 @@ var MongoDBStore = class extends MastraStorage {
921
940
  createdAt: row.created_at
922
941
  };
923
942
  }
943
+ async getTracesPaginated(_args) {
944
+ throw new Error("Method not implemented.");
945
+ }
946
+ async getThreadsByResourceIdPaginated(_args) {
947
+ throw new Error("Method not implemented.");
948
+ }
949
+ async getMessagesPaginated(_args) {
950
+ throw new Error("Method not implemented.");
951
+ }
924
952
  async close() {
925
953
  await this.#client.close();
926
954
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/mongodb",
3
- "version": "0.0.0-vnextAgentNetwork-20250602134426",
3
+ "version": "0.0.0-workflow-deno-20250616115451",
4
4
  "description": "MongoDB provider for Mastra - includes vector store capabilities",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -20,22 +20,23 @@
20
20
  },
21
21
  "dependencies": {
22
22
  "@types/mongodb": "^4.0.7",
23
- "cloudflare": "^4.1.0",
24
- "mongodb": "^6.15.0",
23
+ "cloudflare": "^4.3.0",
24
+ "mongodb": "^6.17.0",
25
25
  "uuid": "^11.1.0"
26
26
  },
27
27
  "devDependencies": {
28
- "@microsoft/api-extractor": "^7.52.5",
29
- "@types/node": "^20.17.27",
30
- "eslint": "^9.23.0",
31
- "tsup": "^8.4.0",
32
- "typescript": "^5.8.2",
33
- "vitest": "^3.1.2",
34
- "@mastra/core": "0.0.0-vnextAgentNetwork-20250602134426",
35
- "@internal/lint": "0.0.0-vnextAgentNetwork-20250602134426"
28
+ "@microsoft/api-extractor": "^7.52.8",
29
+ "@types/node": "^20.19.0",
30
+ "eslint": "^9.28.0",
31
+ "tsup": "^8.5.0",
32
+ "typescript": "^5.8.3",
33
+ "vitest": "^3.2.3",
34
+ "@internal/lint": "0.0.0-workflow-deno-20250616115451",
35
+ "@internal/storage-test-utils": "0.0.0-workflow-deno-20250616115451",
36
+ "@mastra/core": "0.0.0-workflow-deno-20250616115451"
36
37
  },
37
38
  "peerDependencies": {
38
- "@mastra/core": "^0.10.0-alpha.0"
39
+ "@mastra/core": ">=0.10.4-0 <0.11.0"
39
40
  },
40
41
  "scripts": {
41
42
  "build": "tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting",
@@ -1,7 +1,8 @@
1
1
  import { randomUUID } from 'crypto';
2
- import type { MastraMessageV1, MetricResult, WorkflowRunState } from '@mastra/core';
2
+ import type { MastraMessageV1, MastraMessageV2, MetricResult, WorkflowRunState } from '@mastra/core';
3
+ import type { TABLE_NAMES } from '@mastra/core/storage';
3
4
  import { TABLE_EVALS, TABLE_MESSAGES, TABLE_THREADS, TABLE_WORKFLOW_SNAPSHOT } from '@mastra/core/storage';
4
- import { afterAll, beforeAll, describe, expect, it } from 'vitest';
5
+ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';
5
6
  import type { MongoDBConfig } from './index';
6
7
  import { MongoDBStore } from './index';
7
8
 
@@ -40,15 +41,46 @@ class Test {
40
41
  };
41
42
  }
42
43
 
43
- generateSampleMessage(threadId: string): MastraMessageV1 {
44
+ generateSampleMessageV1({
45
+ threadId,
46
+ resourceId = randomUUID(),
47
+ content = 'Hello',
48
+ }: {
49
+ threadId: string;
50
+ resourceId?: string;
51
+ content?: string;
52
+ }): MastraMessageV1 {
44
53
  return {
45
54
  id: `msg-${randomUUID()}`,
46
55
  role: 'user',
47
56
  type: 'text',
48
57
  threadId,
49
- content: [{ type: 'text', text: 'Hello' }],
58
+ content: [{ type: 'text', text: content }],
50
59
  createdAt: new Date(),
51
- resourceId: randomUUID(),
60
+ resourceId,
61
+ };
62
+ }
63
+
64
+ generateSampleMessageV2({
65
+ threadId,
66
+ resourceId = randomUUID(),
67
+ content = 'Hello',
68
+ }: {
69
+ threadId: string;
70
+ resourceId?: string;
71
+ content?: string;
72
+ }): MastraMessageV2 {
73
+ return {
74
+ id: `msg-${randomUUID()}`,
75
+ role: 'user',
76
+ type: 'text',
77
+ threadId,
78
+ content: {
79
+ format: 2,
80
+ parts: [{ type: 'text', text: content }],
81
+ },
82
+ createdAt: new Date(),
83
+ resourceId,
52
84
  };
53
85
  }
54
86
 
@@ -93,6 +125,7 @@ class Test {
93
125
  suspendedPaths: {},
94
126
  runId,
95
127
  timestamp: timestamp.getTime(),
128
+ status: options.status,
96
129
  } as WorkflowRunState;
97
130
  return { snapshot, runId, stepId };
98
131
  }
@@ -201,7 +234,10 @@ describe('MongoDBStore', () => {
201
234
  await store.saveThread({ thread });
202
235
 
203
236
  // Add some messages
204
- const messages = [test.generateSampleMessage(thread.id), test.generateSampleMessage(thread.id)];
237
+ const messages = [
238
+ test.generateSampleMessageV1({ threadId: thread.id }),
239
+ test.generateSampleMessageV1({ threadId: thread.id }),
240
+ ];
205
241
  await store.saveMessages({ messages });
206
242
 
207
243
  await store.deleteThread({ threadId: thread.id });
@@ -236,6 +272,27 @@ describe('MongoDBStore', () => {
236
272
  expect(retrievedThread?.title).toBe('Updated Title');
237
273
  expect(retrievedThread?.metadata).toEqual({ key: 'newValue' });
238
274
  });
275
+
276
+ it('should update thread updatedAt when a message is saved to it', async () => {
277
+ const test = new Test(store).build();
278
+ await test.clearTables();
279
+
280
+ const thread = test.generateSampleThread();
281
+ await store.saveThread({ thread });
282
+
283
+ const initialThread = await store.getThreadById({ threadId: thread.id });
284
+ expect(initialThread).toBeDefined();
285
+ const originalUpdatedAt = initialThread!.updatedAt;
286
+
287
+ await new Promise(resolve => setTimeout(resolve, 10));
288
+
289
+ const message = test.generateSampleMessageV1({ threadId: thread.id });
290
+ await store.saveMessages({ messages: [message] });
291
+
292
+ const updatedThread = await store.getThreadById({ threadId: thread.id });
293
+ expect(updatedThread).toBeDefined();
294
+ expect(updatedThread!.updatedAt.getTime()).toBeGreaterThan(originalUpdatedAt.getTime());
295
+ });
239
296
  });
240
297
 
241
298
  describe('Message Operations', () => {
@@ -246,8 +303,8 @@ describe('MongoDBStore', () => {
246
303
  await store.saveThread({ thread });
247
304
 
248
305
  const messages = [
249
- test.generateSampleMessage(thread.id),
250
- { ...test.generateSampleMessage(thread.id), role: 'assistant' as const },
306
+ test.generateSampleMessageV1({ threadId: thread.id }),
307
+ { ...test.generateSampleMessageV1({ threadId: thread.id }), role: 'assistant' as const },
251
308
  ];
252
309
 
253
310
  // Save messages
@@ -277,29 +334,129 @@ describe('MongoDBStore', () => {
277
334
 
278
335
  const messages = [
279
336
  {
280
- ...test.generateSampleMessage(thread.id),
281
- content: [{ type: 'text', text: 'First' }] as MastraMessageV1['content'],
337
+ ...test.generateSampleMessageV2({ threadId: thread.id, content: 'First' }),
282
338
  },
283
339
  {
284
- ...test.generateSampleMessage(thread.id),
285
- content: [{ type: 'text', text: 'Second' }] as MastraMessageV1['content'],
340
+ ...test.generateSampleMessageV2({ threadId: thread.id, content: 'Second' }),
286
341
  },
287
342
  {
288
- ...test.generateSampleMessage(thread.id),
289
- content: [{ type: 'text', text: 'Third' }] as MastraMessageV1['content'],
343
+ ...test.generateSampleMessageV2({ threadId: thread.id, content: 'Third' }),
290
344
  },
291
345
  ];
292
346
 
293
- await store.saveMessages({ messages });
347
+ await store.saveMessages({ messages, format: 'v2' });
294
348
 
295
- const retrievedMessages = await store.getMessages({ threadId: thread.id });
349
+ const retrievedMessages = await store.getMessages({ threadId: thread.id, format: 'v2' });
296
350
  expect(retrievedMessages).toHaveLength(3);
297
351
 
298
352
  // Verify order is maintained
299
353
  retrievedMessages.forEach((msg, idx) => {
300
- expect(((msg as any).content[0] as any).text).toBe((messages[idx]!.content[0] as any).text);
354
+ expect((msg as any).content.parts).toEqual(messages[idx]!.content.parts);
301
355
  });
302
356
  });
357
+
358
+ // it('should retrieve messages w/ next/prev messages by message id + resource id', async () => {
359
+ // const test = new Test(store).build();
360
+ // const messages: MastraMessageV2[] = [
361
+ // test.generateSampleMessageV2({ threadId: 'thread-one', content: 'First', resourceId: 'cross-thread-resource' }),
362
+ // test.generateSampleMessageV2({
363
+ // threadId: 'thread-one',
364
+ // content: 'Second',
365
+ // resourceId: 'cross-thread-resource',
366
+ // }),
367
+ // test.generateSampleMessageV2({ threadId: 'thread-one', content: 'Third', resourceId: 'cross-thread-resource' }),
368
+
369
+ // test.generateSampleMessageV2({
370
+ // threadId: 'thread-two',
371
+ // content: 'Fourth',
372
+ // resourceId: 'cross-thread-resource',
373
+ // }),
374
+ // test.generateSampleMessageV2({ threadId: 'thread-two', content: 'Fifth', resourceId: 'cross-thread-resource' }),
375
+ // test.generateSampleMessageV2({ threadId: 'thread-two', content: 'Sixth', resourceId: 'cross-thread-resource' }),
376
+
377
+ // test.generateSampleMessageV2({ threadId: 'thread-three', content: 'Seventh', resourceId: 'other-resource' }),
378
+ // test.generateSampleMessageV2({ threadId: 'thread-three', content: 'Eighth', resourceId: 'other-resource' }),
379
+ // ];
380
+
381
+ // await store.saveMessages({ messages: messages, format: 'v2' });
382
+
383
+ // const retrievedMessages: MastraMessageV2[] = await store.getMessages({ threadId: 'thread-one', format: 'v2' });
384
+ // expect(retrievedMessages).toHaveLength(3);
385
+ // expect(retrievedMessages.map(m => (m.content.parts[0] as any).text)).toEqual(['First', 'Second', 'Third']);
386
+
387
+ // const retrievedMessages2: MastraMessageV2[] = await store.getMessages({ threadId: 'thread-two', format: 'v2' });
388
+ // expect(retrievedMessages2).toHaveLength(3);
389
+ // expect(retrievedMessages2.map(m => (m.content.parts[0] as any).text)).toEqual(['Fourth', 'Fifth', 'Sixth']);
390
+
391
+ // const retrievedMessages3: MastraMessageV2[] = await store.getMessages({ threadId: 'thread-three', format: 'v2' });
392
+ // expect(retrievedMessages3).toHaveLength(2);
393
+ // expect(retrievedMessages3.map(m => (m.content.parts[0] as any).text)).toEqual(['Seventh', 'Eighth']);
394
+
395
+ // const crossThreadMessages: MastraMessageV2[] = await store.getMessages({
396
+ // threadId: 'thread-doesnt-exist',
397
+ // resourceId: 'cross-thread-resource',
398
+ // format: 'v2',
399
+ // selectBy: {
400
+ // last: 0,
401
+ // include: [
402
+ // {
403
+ // id: messages[1].id,
404
+ // withNextMessages: 2,
405
+ // withPreviousMessages: 2,
406
+ // },
407
+ // {
408
+ // id: messages[4].id,
409
+ // withPreviousMessages: 2,
410
+ // withNextMessages: 2,
411
+ // },
412
+ // ],
413
+ // },
414
+ // });
415
+
416
+ // expect(crossThreadMessages).toHaveLength(6);
417
+ // expect(crossThreadMessages.filter(m => m.threadId === `thread-one`)).toHaveLength(3);
418
+ // expect(crossThreadMessages.filter(m => m.threadId === `thread-two`)).toHaveLength(3);
419
+
420
+ // const crossThreadMessages2: MastraMessageV2[] = await store.getMessages({
421
+ // threadId: 'thread-one',
422
+ // resourceId: 'cross-thread-resource',
423
+ // format: 'v2',
424
+ // selectBy: {
425
+ // last: 0,
426
+ // include: [
427
+ // {
428
+ // id: messages[4].id,
429
+ // withPreviousMessages: 1,
430
+ // withNextMessages: 30,
431
+ // },
432
+ // ],
433
+ // },
434
+ // });
435
+
436
+ // expect(crossThreadMessages2).toHaveLength(3);
437
+ // expect(crossThreadMessages2.filter(m => m.threadId === `thread-one`)).toHaveLength(0);
438
+ // expect(crossThreadMessages2.filter(m => m.threadId === `thread-two`)).toHaveLength(3);
439
+
440
+ // const crossThreadMessages3: MastraMessageV2[] = await store.getMessages({
441
+ // threadId: 'thread-two',
442
+ // resourceId: 'cross-thread-resource',
443
+ // format: 'v2',
444
+ // selectBy: {
445
+ // last: 0,
446
+ // include: [
447
+ // {
448
+ // id: messages[1].id,
449
+ // withNextMessages: 1,
450
+ // withPreviousMessages: 1,
451
+ // },
452
+ // ],
453
+ // },
454
+ // });
455
+
456
+ // expect(crossThreadMessages3).toHaveLength(3);
457
+ // expect(crossThreadMessages3.filter(m => m.threadId === `thread-one`)).toHaveLength(3);
458
+ // expect(crossThreadMessages3.filter(m => m.threadId === `thread-two`)).toHaveLength(0);
459
+ // });
303
460
  });
304
461
 
305
462
  describe('Edge Cases and Error Handling', () => {
@@ -485,6 +642,7 @@ describe('MongoDBStore', () => {
485
642
  ],
486
643
  serializedStepGraph: [],
487
644
  runId: runId,
645
+ status: 'running',
488
646
  timestamp: Date.now(),
489
647
  };
490
648
 
@@ -773,6 +931,89 @@ describe('MongoDBStore', () => {
773
931
  });
774
932
  });
775
933
 
934
+ describe('alterTable (no-op/schemaless)', () => {
935
+ const TEST_TABLE = 'test_alter_table'; // Use "table" or "collection" as appropriate
936
+ beforeEach(async () => {
937
+ await store.clearTable({ tableName: TEST_TABLE as TABLE_NAMES });
938
+ });
939
+
940
+ afterEach(async () => {
941
+ await store.clearTable({ tableName: TEST_TABLE as TABLE_NAMES });
942
+ });
943
+
944
+ it('allows inserting records with new fields without alterTable', async () => {
945
+ await store.insert({
946
+ tableName: TEST_TABLE as TABLE_NAMES,
947
+ record: { id: '1', name: 'Alice' },
948
+ });
949
+ await store.insert({
950
+ tableName: TEST_TABLE as TABLE_NAMES,
951
+ record: { id: '2', name: 'Bob', newField: 123 },
952
+ });
953
+
954
+ const row = await store.load<{ id: string; name: string; newField?: number }[]>({
955
+ tableName: TEST_TABLE as TABLE_NAMES,
956
+ keys: { id: '2' },
957
+ });
958
+ expect(row?.[0]?.newField).toBe(123);
959
+ });
960
+
961
+ it('does not throw when calling alterTable (no-op)', async () => {
962
+ await expect(
963
+ store.alterTable({
964
+ tableName: TEST_TABLE as TABLE_NAMES,
965
+ schema: {
966
+ id: { type: 'text', primaryKey: true, nullable: false },
967
+ name: { type: 'text', nullable: true },
968
+ extra: { type: 'integer', nullable: true },
969
+ },
970
+ ifNotExists: ['extra'],
971
+ }),
972
+ ).resolves.not.toThrow();
973
+ });
974
+
975
+ it('can add multiple new fields at write time', async () => {
976
+ await store.insert({
977
+ tableName: TEST_TABLE as TABLE_NAMES,
978
+ record: { id: '3', name: 'Charlie', age: 30, city: 'Paris' },
979
+ });
980
+ const row = await store.load<{ id: string; name: string; age?: number; city?: string }[]>({
981
+ tableName: TEST_TABLE as TABLE_NAMES,
982
+ keys: { id: '3' },
983
+ });
984
+ expect(row?.[0]?.age).toBe(30);
985
+ expect(row?.[0]?.city).toBe('Paris');
986
+ });
987
+
988
+ it('can retrieve all fields, including dynamically added ones', async () => {
989
+ await store.insert({
990
+ tableName: TEST_TABLE as TABLE_NAMES,
991
+ record: { id: '4', name: 'Dana', hobby: 'skiing' },
992
+ });
993
+ const row = await store.load<{ id: string; name: string; hobby?: string }[]>({
994
+ tableName: TEST_TABLE as TABLE_NAMES,
995
+ keys: { id: '4' },
996
+ });
997
+ expect(row?.[0]?.hobby).toBe('skiing');
998
+ });
999
+
1000
+ it('does not restrict or error on arbitrary new fields', async () => {
1001
+ await expect(
1002
+ store.insert({
1003
+ tableName: TEST_TABLE as TABLE_NAMES,
1004
+ record: { id: '5', weirdField: { nested: true }, another: [1, 2, 3] },
1005
+ }),
1006
+ ).resolves.not.toThrow();
1007
+
1008
+ const row = await store.load<{ id: string; weirdField?: any; another?: any }[]>({
1009
+ tableName: TEST_TABLE as TABLE_NAMES,
1010
+ keys: { id: '5' },
1011
+ });
1012
+ expect(row?.[0]?.weirdField).toEqual({ nested: true });
1013
+ expect(row?.[0]?.another).toEqual([1, 2, 3]);
1014
+ });
1015
+ });
1016
+
776
1017
  afterAll(async () => {
777
1018
  try {
778
1019
  await store.close();
@@ -1,7 +1,15 @@
1
1
  import { MessageList } from '@mastra/core/agent';
2
2
  import type { MetricResult, TestInfo } from '@mastra/core/eval';
3
3
  import type { MastraMessageV1, MastraMessageV2, StorageThreadType } from '@mastra/core/memory';
4
- import type { EvalRow, StorageGetMessagesArg, TABLE_NAMES, WorkflowRun } from '@mastra/core/storage';
4
+ import type {
5
+ EvalRow,
6
+ PaginationInfo,
7
+ StorageColumn,
8
+ StorageGetMessagesArg,
9
+ StorageGetTracesArg,
10
+ TABLE_NAMES,
11
+ WorkflowRun,
12
+ } from '@mastra/core/storage';
5
13
  import {
6
14
  MastraStorage,
7
15
  TABLE_EVALS,
@@ -10,6 +18,7 @@ import {
10
18
  TABLE_TRACES,
11
19
  TABLE_WORKFLOW_SNAPSHOT,
12
20
  } from '@mastra/core/storage';
21
+ import type { Trace } from '@mastra/core/telemetry';
13
22
  import type { WorkflowRunState } from '@mastra/core/workflows';
14
23
  import type { Db, MongoClientOptions } from 'mongodb';
15
24
  import { MongoClient } from 'mongodb';
@@ -74,6 +83,20 @@ export class MongoDBStore extends MastraStorage {
74
83
  // Nothing to do here, MongoDB is schemaless
75
84
  }
76
85
 
86
+ /**
87
+ * No-op: This backend is schemaless and does not require schema changes.
88
+ * @param tableName Name of the table
89
+ * @param schema Schema of the table
90
+ * @param ifNotExists Array of column names to add if they don't exist
91
+ */
92
+ async alterTable(_args: {
93
+ tableName: TABLE_NAMES;
94
+ schema: Record<string, StorageColumn>;
95
+ ifNotExists: string[];
96
+ }): Promise<void> {
97
+ // Nothing to do here, MongoDB is schemaless
98
+ }
99
+
77
100
  async clearTable({ tableName }: { tableName: TABLE_NAMES }): Promise<void> {
78
101
  try {
79
102
  const collection = await this.getCollection(tableName);
@@ -232,7 +255,15 @@ export class MongoDBStore extends MastraStorage {
232
255
  }
233
256
  }
234
257
 
235
- async getMessages<T = unknown>({ threadId, selectBy }: StorageGetMessagesArg): Promise<T[]> {
258
+ public async getMessages(args: StorageGetMessagesArg & { format?: 'v1' }): Promise<MastraMessageV1[]>;
259
+ public async getMessages(args: StorageGetMessagesArg & { format: 'v2' }): Promise<MastraMessageV2[]>;
260
+ public async getMessages({
261
+ threadId,
262
+ selectBy,
263
+ format,
264
+ }: StorageGetMessagesArg & {
265
+ format?: 'v1' | 'v2';
266
+ }): Promise<MastraMessageV1[] | MastraMessageV2[]> {
236
267
  try {
237
268
  const limit = typeof selectBy?.last === 'number' ? selectBy.last : 40;
238
269
  const include = selectBy?.include || [];
@@ -287,7 +318,9 @@ export class MongoDBStore extends MastraStorage {
287
318
  // Sort all messages by creation date ascending
288
319
  messages.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
289
320
 
290
- return messages.slice(0, limit) as T[];
321
+ const list = new MessageList().add(messages.slice(0, limit), 'memory');
322
+ if (format === `v2`) return list.get.all.v2();
323
+ return list.get.all.v1();
291
324
  } catch (error) {
292
325
  this.logger.error('Error getting messages:', error as Error);
293
326
  throw error;
@@ -311,6 +344,7 @@ export class MongoDBStore extends MastraStorage {
311
344
  this.logger.error('Thread ID is required to save messages');
312
345
  throw new Error('Thread ID is required');
313
346
  }
347
+
314
348
  try {
315
349
  // Prepare batch statements for all messages
316
350
  const messagesToInsert = messages.map(message => {
@@ -326,9 +360,15 @@ export class MongoDBStore extends MastraStorage {
326
360
  };
327
361
  });
328
362
 
329
- // Execute all inserts in a single batch
363
+ // Execute message inserts and thread update in parallel for better performance
330
364
  const collection = await this.getCollection(TABLE_MESSAGES);
331
- await collection.insertMany(messagesToInsert);
365
+ const threadsCollection = await this.getCollection(TABLE_THREADS);
366
+
367
+ await Promise.all([
368
+ collection.insertMany(messagesToInsert),
369
+ threadsCollection.updateOne({ id: threadId }, { $set: { updatedAt: new Date() } }),
370
+ ]);
371
+
332
372
  const list = new MessageList().add(messages, 'memory');
333
373
  if (format === `v2`) return list.get.all.v2();
334
374
  return list.get.all.v1();
@@ -652,6 +692,7 @@ export class MongoDBStore extends MastraStorage {
652
692
  type: row.type,
653
693
  createdAt: new Date(row.createdAt as string),
654
694
  threadId: row.thread_id,
695
+ resourceId: row.resourceId,
655
696
  } as MastraMessageV2;
656
697
  }
657
698
 
@@ -679,6 +720,24 @@ export class MongoDBStore extends MastraStorage {
679
720
  };
680
721
  }
681
722
 
723
+ async getTracesPaginated(_args: StorageGetTracesArg): Promise<PaginationInfo & { traces: Trace[] }> {
724
+ throw new Error('Method not implemented.');
725
+ }
726
+
727
+ async getThreadsByResourceIdPaginated(_args: {
728
+ resourceId: string;
729
+ page?: number;
730
+ perPage?: number;
731
+ }): Promise<PaginationInfo & { threads: StorageThreadType[] }> {
732
+ throw new Error('Method not implemented.');
733
+ }
734
+
735
+ async getMessagesPaginated(
736
+ _args: StorageGetMessagesArg,
737
+ ): Promise<PaginationInfo & { messages: MastraMessageV1[] | MastraMessageV2[] }> {
738
+ throw new Error('Method not implemented.');
739
+ }
740
+
682
741
  async close(): Promise<void> {
683
742
  await this.#client.close();
684
743
  }