@mastra/libsql 0.10.3 → 0.10.4-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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/libsql",
3
- "version": "0.10.3",
3
+ "version": "0.10.4-alpha.1",
4
4
  "description": "Libsql provider for Mastra - includes both vector and db storage capabilities",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -29,9 +29,9 @@
29
29
  "tsup": "^8.5.0",
30
30
  "typescript": "^5.8.3",
31
31
  "vitest": "^3.2.3",
32
- "@internal/lint": "0.0.13",
33
- "@mastra/core": "0.10.6",
34
- "@internal/storage-test-utils": "0.0.9"
32
+ "@internal/storage-test-utils": "0.0.9",
33
+ "@mastra/core": "0.10.7-alpha.1",
34
+ "@internal/lint": "0.0.13"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "@mastra/core": ">=0.10.4-0 <0.11.0"
@@ -6,6 +6,7 @@ import {
6
6
  createTestSuite,
7
7
  createSampleMessageV1,
8
8
  resetRole,
9
+ createSampleMessageV2,
9
10
  } from '@internal/storage-test-utils';
10
11
  import type { MastraMessageV1, StorageThreadType } from '@mastra/core';
11
12
  import { Mastra } from '@mastra/core/mastra';
@@ -362,3 +363,167 @@ describe('LibSQLStore Pagination Features', () => {
362
363
  });
363
364
  });
364
365
  });
366
+
367
+ describe('LibSQLStore updateMessages', () => {
368
+ let store: LibSQLStore;
369
+ let thread: StorageThreadType;
370
+
371
+ beforeAll(async () => {
372
+ store = libsql;
373
+ });
374
+
375
+ beforeEach(async () => {
376
+ await store.clearTable({ tableName: TABLE_MESSAGES });
377
+ await store.clearTable({ tableName: TABLE_THREADS });
378
+ const threadData = createSampleThread();
379
+ thread = await store.saveThread({ thread: threadData as StorageThreadType });
380
+ });
381
+
382
+ it('should update a single field of a message (e.g., role)', async () => {
383
+ const originalMessage = createSampleMessageV2({ threadId: thread.id, role: 'user' });
384
+ await store.saveMessages({ messages: [originalMessage], format: 'v2' });
385
+
386
+ const updatedMessages = await store.updateMessages({
387
+ messages: [{ id: originalMessage.id, role: 'assistant' }],
388
+ });
389
+
390
+ expect(updatedMessages).toHaveLength(1);
391
+ expect(updatedMessages[0].role).toBe('assistant');
392
+
393
+ const fromDb = await store.getMessages({ threadId: thread.id, format: 'v2' });
394
+ expect(fromDb[0].role).toBe('assistant');
395
+ });
396
+
397
+ it('should update only the metadata within the content field, preserving other content fields', async () => {
398
+ const originalMessage = createSampleMessageV2({
399
+ threadId: thread.id,
400
+ content: { content: 'hello world', parts: [{ type: 'text', text: 'hello world' }] },
401
+ });
402
+ await store.saveMessages({ messages: [originalMessage], format: 'v2' });
403
+
404
+ const newMetadata = { someKey: 'someValue' };
405
+ await store.updateMessages({
406
+ messages: [{ id: originalMessage.id, content: { metadata: newMetadata } as any }],
407
+ });
408
+
409
+ const fromDb = await store.getMessages({ threadId: thread.id, format: 'v2' });
410
+ expect(fromDb).toHaveLength(1);
411
+ expect(fromDb[0].content.metadata).toEqual(newMetadata);
412
+ expect(fromDb[0].content.content).toBe('hello world');
413
+ expect(fromDb[0].content.parts).toEqual([{ type: 'text', text: 'hello world' }]);
414
+ });
415
+
416
+ it('should update only the content string within the content field, preserving metadata', async () => {
417
+ const originalMessage = createSampleMessageV2({
418
+ threadId: thread.id,
419
+ content: { metadata: { initial: true } },
420
+ });
421
+ await store.saveMessages({ messages: [originalMessage], format: 'v2' });
422
+
423
+ const newContentString = 'This is the new content string';
424
+ await store.updateMessages({
425
+ messages: [{ id: originalMessage.id, content: { content: newContentString } as any }],
426
+ });
427
+
428
+ const fromDb = await store.getMessages({ threadId: thread.id, format: 'v2' });
429
+ expect(fromDb[0].content.content).toBe(newContentString);
430
+ expect(fromDb[0].content.metadata).toEqual({ initial: true });
431
+ });
432
+
433
+ it('should deep merge metadata, not overwrite it', async () => {
434
+ const originalMessage = createSampleMessageV2({
435
+ threadId: thread.id,
436
+ content: { metadata: { initial: true }, content: 'old content' },
437
+ });
438
+ await store.saveMessages({ messages: [originalMessage], format: 'v2' });
439
+
440
+ const newMetadata = { updated: true };
441
+ await store.updateMessages({
442
+ messages: [{ id: originalMessage.id, content: { metadata: newMetadata } as any }],
443
+ });
444
+
445
+ const fromDb = await store.getMessages({ threadId: thread.id, format: 'v2' });
446
+ expect(fromDb[0].content.content).toBe('old content');
447
+ expect(fromDb[0].content.metadata).toEqual({ initial: true, updated: true });
448
+ });
449
+
450
+ it('should update multiple messages at once', async () => {
451
+ const msg1 = createSampleMessageV2({ threadId: thread.id, role: 'user' });
452
+ const msg2 = createSampleMessageV2({ threadId: thread.id, content: { content: 'original' } });
453
+ await store.saveMessages({ messages: [msg1, msg2], format: 'v2' });
454
+
455
+ await store.updateMessages({
456
+ messages: [
457
+ { id: msg1.id, role: 'assistant' },
458
+ { id: msg2.id, content: { content: 'updated' } as any },
459
+ ],
460
+ });
461
+
462
+ const fromDb = await store.getMessages({ threadId: thread.id, format: 'v2' });
463
+ const updatedMsg1 = fromDb.find(m => m.id === msg1.id)!;
464
+ const updatedMsg2 = fromDb.find(m => m.id === msg2.id)!;
465
+
466
+ expect(updatedMsg1.role).toBe('assistant');
467
+ expect(updatedMsg2.content.content).toBe('updated');
468
+ });
469
+
470
+ it('should update the parent thread updatedAt timestamp', async () => {
471
+ const originalMessage = createSampleMessageV2({ threadId: thread.id });
472
+ await store.saveMessages({ messages: [originalMessage], format: 'v2' });
473
+ const initialThread = await store.getThreadById({ threadId: thread.id });
474
+
475
+ await new Promise(r => setTimeout(r, 10));
476
+
477
+ await store.updateMessages({ messages: [{ id: originalMessage.id, role: 'assistant' }] });
478
+
479
+ const updatedThread = await store.getThreadById({ threadId: thread.id });
480
+
481
+ expect(new Date(updatedThread!.updatedAt).getTime()).toBeGreaterThan(new Date(initialThread!.updatedAt).getTime());
482
+ });
483
+
484
+ it('should update timestamps on both threads when moving a message', async () => {
485
+ const thread2 = await store.saveThread({ thread: createSampleThread() });
486
+ const message = createSampleMessageV2({ threadId: thread.id });
487
+ await store.saveMessages({ messages: [message], format: 'v2' });
488
+
489
+ const initialThread1 = await store.getThreadById({ threadId: thread.id });
490
+ const initialThread2 = await store.getThreadById({ threadId: thread2.id });
491
+
492
+ await new Promise(r => setTimeout(r, 10));
493
+
494
+ await store.updateMessages({
495
+ messages: [{ id: message.id, threadId: thread2.id }],
496
+ });
497
+
498
+ const updatedThread1 = await store.getThreadById({ threadId: thread.id });
499
+ const updatedThread2 = await store.getThreadById({ threadId: thread2.id });
500
+
501
+ expect(new Date(updatedThread1!.updatedAt).getTime()).toBeGreaterThan(
502
+ new Date(initialThread1!.updatedAt).getTime(),
503
+ );
504
+ expect(new Date(updatedThread2!.updatedAt).getTime()).toBeGreaterThan(
505
+ new Date(initialThread2!.updatedAt).getTime(),
506
+ );
507
+
508
+ // Verify the message was moved
509
+ const thread1Messages = await store.getMessages({ threadId: thread.id, format: 'v2' });
510
+ const thread2Messages = await store.getMessages({ threadId: thread2.id, format: 'v2' });
511
+ expect(thread1Messages).toHaveLength(0);
512
+ expect(thread2Messages).toHaveLength(1);
513
+ expect(thread2Messages[0].id).toBe(message.id);
514
+ });
515
+
516
+ it('should not fail when trying to update a non-existent message', async () => {
517
+ const originalMessage = createSampleMessageV2({ threadId: thread.id });
518
+ await store.saveMessages({ messages: [originalMessage], format: 'v2' });
519
+
520
+ await expect(
521
+ store.updateMessages({
522
+ messages: [{ id: randomUUID(), role: 'assistant' }],
523
+ }),
524
+ ).resolves.not.toThrow();
525
+
526
+ const fromDb = await store.getMessages({ threadId: thread.id, format: 'v2' });
527
+ expect(fromDb[0].role).toBe(originalMessage.role);
528
+ });
529
+ });