@mastra/mongodb 0.10.2 → 0.10.3-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.
@@ -1,23 +1,23 @@
1
1
 
2
- > @mastra/mongodb@0.10.2-alpha.1 build /home/runner/work/mastra/mastra/stores/mongodb
2
+ > @mastra/mongodb@0.10.3-alpha.1 build /home/runner/work/mastra/mastra/stores/mongodb
3
3
  > tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting
4
4
 
5
5
  CLI Building entry: src/index.ts
6
6
  CLI Using tsconfig: tsconfig.json
7
- CLI tsup v8.4.0
7
+ CLI tsup v8.5.0
8
8
  TSC Build start
9
- TSC ⚡️ Build success in 7878ms
9
+ TSC ⚡️ Build success in 8770ms
10
10
  DTS Build start
11
11
  CLI Target: es2022
12
12
  Analysis will use the bundled TypeScript version 5.8.3
13
13
  Writing package typings: /home/runner/work/mastra/mastra/stores/mongodb/dist/_tsup-dts-rollup.d.ts
14
14
  Analysis will use the bundled TypeScript version 5.8.3
15
15
  Writing package typings: /home/runner/work/mastra/mastra/stores/mongodb/dist/_tsup-dts-rollup.d.cts
16
- DTS ⚡️ Build success in 12007ms
16
+ DTS ⚡️ Build success in 11972ms
17
17
  CLI Cleaning output folder
18
18
  ESM Build start
19
19
  CJS Build start
20
- ESM dist/index.js 33.01 KB
21
- ESM ⚡️ Build success in 1201ms
22
- CJS dist/index.cjs 33.13 KB
23
- CJS ⚡️ Build success in 1192ms
20
+ CJS dist/index.cjs 33.85 KB
21
+ CJS ⚡️ Build success in 1239ms
22
+ ESM dist/index.js 33.73 KB
23
+ ESM ⚡️ Build success in 1239ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @mastra/mongodb
2
2
 
3
+ ## 0.10.3-alpha.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 925ab94: added paginated functions to base class and added boilerplate and updated imports
8
+ - Updated dependencies [925ab94]
9
+ - @mastra/core@0.10.4-alpha.3
10
+
11
+ ## 0.10.3-alpha.0
12
+
13
+ ### Patch Changes
14
+
15
+ - dffb67b: updated stores to add alter table and change tests
16
+ - e030ea3: Added missing format compatibility to MongoDB getMessages() method
17
+ - Updated dependencies [f6fd25f]
18
+ - Updated dependencies [dffb67b]
19
+ - Updated dependencies [f1309d3]
20
+ - Updated dependencies [f7f8293]
21
+ - @mastra/core@0.10.4-alpha.1
22
+
3
23
  ## 0.10.2
4
24
 
5
25
  ### Patch Changes
@@ -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;
@@ -898,7 +912,8 @@ var MongoDBStore = class extends storage.MastraStorage {
898
912
  role: row.role,
899
913
  type: row.type,
900
914
  createdAt: new Date(row.createdAt),
901
- threadId: row.thread_id
915
+ threadId: row.thread_id,
916
+ resourceId: row.resourceId
902
917
  };
903
918
  }
904
919
  transformEvalRow(row) {
@@ -923,6 +938,15 @@ var MongoDBStore = class extends storage.MastraStorage {
923
938
  createdAt: row.created_at
924
939
  };
925
940
  }
941
+ async getTracesPaginated(_args) {
942
+ throw new Error("Method not implemented.");
943
+ }
944
+ async getThreadsByResourceIdPaginated(_args) {
945
+ throw new Error("Method not implemented.");
946
+ }
947
+ async getMessagesPaginated(_args) {
948
+ throw new Error("Method not implemented.");
949
+ }
926
950
  async close() {
927
951
  await this.#client.close();
928
952
  }
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;
@@ -896,7 +910,8 @@ var MongoDBStore = class extends MastraStorage {
896
910
  role: row.role,
897
911
  type: row.type,
898
912
  createdAt: new Date(row.createdAt),
899
- threadId: row.thread_id
913
+ threadId: row.thread_id,
914
+ resourceId: row.resourceId
900
915
  };
901
916
  }
902
917
  transformEvalRow(row) {
@@ -921,6 +936,15 @@ var MongoDBStore = class extends MastraStorage {
921
936
  createdAt: row.created_at
922
937
  };
923
938
  }
939
+ async getTracesPaginated(_args) {
940
+ throw new Error("Method not implemented.");
941
+ }
942
+ async getThreadsByResourceIdPaginated(_args) {
943
+ throw new Error("Method not implemented.");
944
+ }
945
+ async getMessagesPaginated(_args) {
946
+ throw new Error("Method not implemented.");
947
+ }
924
948
  async close() {
925
949
  await this.#client.close();
926
950
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/mongodb",
3
- "version": "0.10.2",
3
+ "version": "0.10.3-alpha.1",
4
4
  "description": "MongoDB provider for Mastra - includes vector store capabilities",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -25,14 +25,15 @@
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",
28
+ "@microsoft/api-extractor": "^7.52.8",
29
+ "@types/node": "^20.17.57",
30
+ "eslint": "^9.28.0",
31
+ "tsup": "^8.5.0",
32
32
  "typescript": "^5.8.2",
33
- "vitest": "^3.1.2",
34
- "@internal/lint": "0.0.8",
35
- "@mastra/core": "0.10.2"
33
+ "vitest": "^3.2.2",
34
+ "@internal/lint": "0.0.10",
35
+ "@mastra/core": "0.10.4-alpha.3",
36
+ "@internal/storage-test-utils": "0.0.6"
36
37
  },
37
38
  "peerDependencies": {
38
39
  "@mastra/core": "^0.10.2-alpha.0"
@@ -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
 
@@ -201,7 +233,10 @@ describe('MongoDBStore', () => {
201
233
  await store.saveThread({ thread });
202
234
 
203
235
  // Add some messages
204
- const messages = [test.generateSampleMessage(thread.id), test.generateSampleMessage(thread.id)];
236
+ const messages = [
237
+ test.generateSampleMessageV1({ threadId: thread.id }),
238
+ test.generateSampleMessageV1({ threadId: thread.id }),
239
+ ];
205
240
  await store.saveMessages({ messages });
206
241
 
207
242
  await store.deleteThread({ threadId: thread.id });
@@ -246,8 +281,8 @@ describe('MongoDBStore', () => {
246
281
  await store.saveThread({ thread });
247
282
 
248
283
  const messages = [
249
- test.generateSampleMessage(thread.id),
250
- { ...test.generateSampleMessage(thread.id), role: 'assistant' as const },
284
+ test.generateSampleMessageV1({ threadId: thread.id }),
285
+ { ...test.generateSampleMessageV1({ threadId: thread.id }), role: 'assistant' as const },
251
286
  ];
252
287
 
253
288
  // Save messages
@@ -277,29 +312,129 @@ describe('MongoDBStore', () => {
277
312
 
278
313
  const messages = [
279
314
  {
280
- ...test.generateSampleMessage(thread.id),
281
- content: [{ type: 'text', text: 'First' }] as MastraMessageV1['content'],
315
+ ...test.generateSampleMessageV2({ threadId: thread.id, content: 'First' }),
282
316
  },
283
317
  {
284
- ...test.generateSampleMessage(thread.id),
285
- content: [{ type: 'text', text: 'Second' }] as MastraMessageV1['content'],
318
+ ...test.generateSampleMessageV2({ threadId: thread.id, content: 'Second' }),
286
319
  },
287
320
  {
288
- ...test.generateSampleMessage(thread.id),
289
- content: [{ type: 'text', text: 'Third' }] as MastraMessageV1['content'],
321
+ ...test.generateSampleMessageV2({ threadId: thread.id, content: 'Third' }),
290
322
  },
291
323
  ];
292
324
 
293
- await store.saveMessages({ messages });
325
+ await store.saveMessages({ messages, format: 'v2' });
294
326
 
295
- const retrievedMessages = await store.getMessages({ threadId: thread.id });
327
+ const retrievedMessages = await store.getMessages({ threadId: thread.id, format: 'v2' });
296
328
  expect(retrievedMessages).toHaveLength(3);
297
329
 
298
330
  // Verify order is maintained
299
331
  retrievedMessages.forEach((msg, idx) => {
300
- expect(((msg as any).content[0] as any).text).toBe((messages[idx]!.content[0] as any).text);
332
+ expect((msg as any).content.parts).toEqual(messages[idx]!.content.parts);
301
333
  });
302
334
  });
335
+
336
+ // it('should retrieve messages w/ next/prev messages by message id + resource id', async () => {
337
+ // const test = new Test(store).build();
338
+ // const messages: MastraMessageV2[] = [
339
+ // test.generateSampleMessageV2({ threadId: 'thread-one', content: 'First', resourceId: 'cross-thread-resource' }),
340
+ // test.generateSampleMessageV2({
341
+ // threadId: 'thread-one',
342
+ // content: 'Second',
343
+ // resourceId: 'cross-thread-resource',
344
+ // }),
345
+ // test.generateSampleMessageV2({ threadId: 'thread-one', content: 'Third', resourceId: 'cross-thread-resource' }),
346
+
347
+ // test.generateSampleMessageV2({
348
+ // threadId: 'thread-two',
349
+ // content: 'Fourth',
350
+ // resourceId: 'cross-thread-resource',
351
+ // }),
352
+ // test.generateSampleMessageV2({ threadId: 'thread-two', content: 'Fifth', resourceId: 'cross-thread-resource' }),
353
+ // test.generateSampleMessageV2({ threadId: 'thread-two', content: 'Sixth', resourceId: 'cross-thread-resource' }),
354
+
355
+ // test.generateSampleMessageV2({ threadId: 'thread-three', content: 'Seventh', resourceId: 'other-resource' }),
356
+ // test.generateSampleMessageV2({ threadId: 'thread-three', content: 'Eighth', resourceId: 'other-resource' }),
357
+ // ];
358
+
359
+ // await store.saveMessages({ messages: messages, format: 'v2' });
360
+
361
+ // const retrievedMessages: MastraMessageV2[] = await store.getMessages({ threadId: 'thread-one', format: 'v2' });
362
+ // expect(retrievedMessages).toHaveLength(3);
363
+ // expect(retrievedMessages.map(m => (m.content.parts[0] as any).text)).toEqual(['First', 'Second', 'Third']);
364
+
365
+ // const retrievedMessages2: MastraMessageV2[] = await store.getMessages({ threadId: 'thread-two', format: 'v2' });
366
+ // expect(retrievedMessages2).toHaveLength(3);
367
+ // expect(retrievedMessages2.map(m => (m.content.parts[0] as any).text)).toEqual(['Fourth', 'Fifth', 'Sixth']);
368
+
369
+ // const retrievedMessages3: MastraMessageV2[] = await store.getMessages({ threadId: 'thread-three', format: 'v2' });
370
+ // expect(retrievedMessages3).toHaveLength(2);
371
+ // expect(retrievedMessages3.map(m => (m.content.parts[0] as any).text)).toEqual(['Seventh', 'Eighth']);
372
+
373
+ // const crossThreadMessages: MastraMessageV2[] = await store.getMessages({
374
+ // threadId: 'thread-doesnt-exist',
375
+ // resourceId: 'cross-thread-resource',
376
+ // format: 'v2',
377
+ // selectBy: {
378
+ // last: 0,
379
+ // include: [
380
+ // {
381
+ // id: messages[1].id,
382
+ // withNextMessages: 2,
383
+ // withPreviousMessages: 2,
384
+ // },
385
+ // {
386
+ // id: messages[4].id,
387
+ // withPreviousMessages: 2,
388
+ // withNextMessages: 2,
389
+ // },
390
+ // ],
391
+ // },
392
+ // });
393
+
394
+ // expect(crossThreadMessages).toHaveLength(6);
395
+ // expect(crossThreadMessages.filter(m => m.threadId === `thread-one`)).toHaveLength(3);
396
+ // expect(crossThreadMessages.filter(m => m.threadId === `thread-two`)).toHaveLength(3);
397
+
398
+ // const crossThreadMessages2: MastraMessageV2[] = await store.getMessages({
399
+ // threadId: 'thread-one',
400
+ // resourceId: 'cross-thread-resource',
401
+ // format: 'v2',
402
+ // selectBy: {
403
+ // last: 0,
404
+ // include: [
405
+ // {
406
+ // id: messages[4].id,
407
+ // withPreviousMessages: 1,
408
+ // withNextMessages: 30,
409
+ // },
410
+ // ],
411
+ // },
412
+ // });
413
+
414
+ // expect(crossThreadMessages2).toHaveLength(3);
415
+ // expect(crossThreadMessages2.filter(m => m.threadId === `thread-one`)).toHaveLength(0);
416
+ // expect(crossThreadMessages2.filter(m => m.threadId === `thread-two`)).toHaveLength(3);
417
+
418
+ // const crossThreadMessages3: MastraMessageV2[] = await store.getMessages({
419
+ // threadId: 'thread-two',
420
+ // resourceId: 'cross-thread-resource',
421
+ // format: 'v2',
422
+ // selectBy: {
423
+ // last: 0,
424
+ // include: [
425
+ // {
426
+ // id: messages[1].id,
427
+ // withNextMessages: 1,
428
+ // withPreviousMessages: 1,
429
+ // },
430
+ // ],
431
+ // },
432
+ // });
433
+
434
+ // expect(crossThreadMessages3).toHaveLength(3);
435
+ // expect(crossThreadMessages3.filter(m => m.threadId === `thread-one`)).toHaveLength(3);
436
+ // expect(crossThreadMessages3.filter(m => m.threadId === `thread-two`)).toHaveLength(0);
437
+ // });
303
438
  });
304
439
 
305
440
  describe('Edge Cases and Error Handling', () => {
@@ -773,6 +908,89 @@ describe('MongoDBStore', () => {
773
908
  });
774
909
  });
775
910
 
911
+ describe('alterTable (no-op/schemaless)', () => {
912
+ const TEST_TABLE = 'test_alter_table'; // Use "table" or "collection" as appropriate
913
+ beforeEach(async () => {
914
+ await store.clearTable({ tableName: TEST_TABLE as TABLE_NAMES });
915
+ });
916
+
917
+ afterEach(async () => {
918
+ await store.clearTable({ tableName: TEST_TABLE as TABLE_NAMES });
919
+ });
920
+
921
+ it('allows inserting records with new fields without alterTable', async () => {
922
+ await store.insert({
923
+ tableName: TEST_TABLE as TABLE_NAMES,
924
+ record: { id: '1', name: 'Alice' },
925
+ });
926
+ await store.insert({
927
+ tableName: TEST_TABLE as TABLE_NAMES,
928
+ record: { id: '2', name: 'Bob', newField: 123 },
929
+ });
930
+
931
+ const row = await store.load<{ id: string; name: string; newField?: number }[]>({
932
+ tableName: TEST_TABLE as TABLE_NAMES,
933
+ keys: { id: '2' },
934
+ });
935
+ expect(row?.[0]?.newField).toBe(123);
936
+ });
937
+
938
+ it('does not throw when calling alterTable (no-op)', async () => {
939
+ await expect(
940
+ store.alterTable({
941
+ tableName: TEST_TABLE as TABLE_NAMES,
942
+ schema: {
943
+ id: { type: 'text', primaryKey: true, nullable: false },
944
+ name: { type: 'text', nullable: true },
945
+ extra: { type: 'integer', nullable: true },
946
+ },
947
+ ifNotExists: ['extra'],
948
+ }),
949
+ ).resolves.not.toThrow();
950
+ });
951
+
952
+ it('can add multiple new fields at write time', async () => {
953
+ await store.insert({
954
+ tableName: TEST_TABLE as TABLE_NAMES,
955
+ record: { id: '3', name: 'Charlie', age: 30, city: 'Paris' },
956
+ });
957
+ const row = await store.load<{ id: string; name: string; age?: number; city?: string }[]>({
958
+ tableName: TEST_TABLE as TABLE_NAMES,
959
+ keys: { id: '3' },
960
+ });
961
+ expect(row?.[0]?.age).toBe(30);
962
+ expect(row?.[0]?.city).toBe('Paris');
963
+ });
964
+
965
+ it('can retrieve all fields, including dynamically added ones', async () => {
966
+ await store.insert({
967
+ tableName: TEST_TABLE as TABLE_NAMES,
968
+ record: { id: '4', name: 'Dana', hobby: 'skiing' },
969
+ });
970
+ const row = await store.load<{ id: string; name: string; hobby?: string }[]>({
971
+ tableName: TEST_TABLE as TABLE_NAMES,
972
+ keys: { id: '4' },
973
+ });
974
+ expect(row?.[0]?.hobby).toBe('skiing');
975
+ });
976
+
977
+ it('does not restrict or error on arbitrary new fields', async () => {
978
+ await expect(
979
+ store.insert({
980
+ tableName: TEST_TABLE as TABLE_NAMES,
981
+ record: { id: '5', weirdField: { nested: true }, another: [1, 2, 3] },
982
+ }),
983
+ ).resolves.not.toThrow();
984
+
985
+ const row = await store.load<{ id: string; weirdField?: any; another?: any }[]>({
986
+ tableName: TEST_TABLE as TABLE_NAMES,
987
+ keys: { id: '5' },
988
+ });
989
+ expect(row?.[0]?.weirdField).toEqual({ nested: true });
990
+ expect(row?.[0]?.another).toEqual([1, 2, 3]);
991
+ });
992
+ });
993
+
776
994
  afterAll(async () => {
777
995
  try {
778
996
  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;
@@ -652,6 +685,7 @@ export class MongoDBStore extends MastraStorage {
652
685
  type: row.type,
653
686
  createdAt: new Date(row.createdAt as string),
654
687
  threadId: row.thread_id,
688
+ resourceId: row.resourceId,
655
689
  } as MastraMessageV2;
656
690
  }
657
691
 
@@ -679,6 +713,24 @@ export class MongoDBStore extends MastraStorage {
679
713
  };
680
714
  }
681
715
 
716
+ async getTracesPaginated(_args: StorageGetTracesArg): Promise<PaginationInfo & { traces: Trace[] }> {
717
+ throw new Error('Method not implemented.');
718
+ }
719
+
720
+ async getThreadsByResourceIdPaginated(_args: {
721
+ resourceId: string;
722
+ page?: number;
723
+ perPage?: number;
724
+ }): Promise<PaginationInfo & { threads: StorageThreadType[] }> {
725
+ throw new Error('Method not implemented.');
726
+ }
727
+
728
+ async getMessagesPaginated(
729
+ _args: StorageGetMessagesArg,
730
+ ): Promise<PaginationInfo & { messages: MastraMessageV1[] | MastraMessageV2[] }> {
731
+ throw new Error('Method not implemented.');
732
+ }
733
+
682
734
  async close(): Promise<void> {
683
735
  await this.#client.close();
684
736
  }