@lota-sdk/ui 0.4.8 → 0.4.9

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": "@lota-sdk/ui",
3
- "version": "0.4.8",
3
+ "version": "0.4.9",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -25,8 +25,8 @@
25
25
  "registry": "https://registry.npmjs.org/"
26
26
  },
27
27
  "dependencies": {
28
- "@lota-sdk/shared": "0.4.8",
29
- "ai": "^6.0.145"
28
+ "@lota-sdk/shared": "0.4.9",
29
+ "ai": "^6.0.167"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "@ai-sdk/react": "^3.0.118",
@@ -34,9 +34,9 @@
34
34
  "react": "^19.2.0"
35
35
  },
36
36
  "devDependencies": {
37
- "@ai-sdk/react": "^3.0.147",
38
- "@tanstack/react-query": "^5.96.2",
37
+ "@ai-sdk/react": "^3.0.169",
38
+ "@tanstack/react-query": "^5.99.0",
39
39
  "@types/react": "^19.2.14",
40
- "react": "^19.2.4"
40
+ "react": "^19.2.5"
41
41
  }
42
42
  }
@@ -1,39 +1,71 @@
1
- import { getMessageCreatedAt, withMessageCreatedAt } from '@lota-sdk/shared'
1
+ import {
2
+ baseChatMessageSchema,
3
+ getMessageCreatedAt,
4
+ normalizeMessageBatch,
5
+ normalizeMessageList,
6
+ withMessageCreatedAt,
7
+ } from '@lota-sdk/shared'
8
+ import type { MessageMetadataLike } from '@lota-sdk/shared'
2
9
 
3
10
  interface TimestampedMessage {
4
11
  id: string
5
- metadata?: { createdAt?: unknown }
12
+ metadata?: unknown
6
13
  }
7
14
 
15
+ interface MetadataCompatibleMessage {
16
+ id: string
17
+ metadata?: MessageMetadataLike
18
+ }
19
+
20
+ type ChatMessageGuard<TMessage extends TimestampedMessage> = (message: unknown) => message is TMessage
21
+
8
22
  function normalizeMessage<TMessage extends TimestampedMessage>(message: TMessage): TMessage {
9
- return withMessageCreatedAt(message)
23
+ return withMessageCreatedAt(message as TMessage & MetadataCompatibleMessage)
24
+ }
25
+
26
+ function getComparableCreatedAt(message: TimestampedMessage): number {
27
+ const metadata =
28
+ message.metadata && typeof message.metadata === 'object' ? (message.metadata as MessageMetadataLike) : undefined
29
+
30
+ return getMessageCreatedAt({ metadata }, { fallback: 0 })
10
31
  }
11
32
 
12
33
  function compareChatMessages<TMessage extends TimestampedMessage>(left: TMessage, right: TMessage): number {
13
- const leftTime = getMessageCreatedAt(left, { fallback: 0 })
14
- const rightTime = getMessageCreatedAt(right, { fallback: 0 })
34
+ const leftTime = getComparableCreatedAt(left)
35
+ const rightTime = getComparableCreatedAt(right)
15
36
  if (leftTime !== rightTime) return leftTime - rightTime
16
37
  return left.id.localeCompare(right.id)
17
38
  }
18
39
 
19
- function sortChatMessages<TMessage extends TimestampedMessage>(messages: readonly TMessage[]): TMessage[] {
20
- return [...messages].sort(compareChatMessages)
40
+ export function normalizeChatMessages<TMessage extends TimestampedMessage>(messages: readonly TMessage[]): TMessage[] {
41
+ return normalizeMessageList(messages, {
42
+ getMessageId: (message) => message.id,
43
+ normalizeMessage,
44
+ compareMessages: compareChatMessages,
45
+ })
21
46
  }
22
47
 
23
- export function normalizeChatMessages<TMessage extends TimestampedMessage>(messages: readonly TMessage[]): TMessage[] {
24
- return sortChatMessages(messages.map((message) => normalizeMessage(message)))
48
+ export function normalizeHydratedChatMessages<TMessage extends TimestampedMessage>(
49
+ raw: unknown,
50
+ isMessage: ChatMessageGuard<TMessage>,
51
+ ): TMessage[] {
52
+ return normalizeMessageBatch(raw, {
53
+ parseMessage: (entry) => {
54
+ const parsed = baseChatMessageSchema.safeParse(entry)
55
+ return parsed.success && isMessage(parsed.data) ? parsed.data : null
56
+ },
57
+ getMessageId: (message) => message.id,
58
+ normalizeMessage,
59
+ compareMessages: compareChatMessages,
60
+ })
25
61
  }
26
62
 
27
63
  export function mergeChatMessages<TMessage extends TimestampedMessage>(
28
64
  ...messageLists: ReadonlyArray<readonly TMessage[]>
29
65
  ): TMessage[] {
30
- const messagesById = new Map<string, TMessage>()
31
-
32
- for (const messages of messageLists) {
33
- for (const message of messages) {
34
- messagesById.set(message.id, message)
35
- }
36
- }
37
-
38
- return sortChatMessages(Array.from(messagesById.values()))
66
+ return normalizeMessageList(messageLists.flat(), {
67
+ getMessageId: (message) => message.id,
68
+ normalizeMessage,
69
+ compareMessages: compareChatMessages,
70
+ })
39
71
  }
@@ -2,7 +2,7 @@ import { useInfiniteQuery, useQueryClient } from '@tanstack/react-query'
2
2
  import type { InfiniteData } from '@tanstack/react-query'
3
3
  import { useCallback, useMemo } from 'react'
4
4
 
5
- import { normalizeChatMessages } from '../chat/messages'
5
+ import { mergeChatMessages, normalizeChatMessages } from '../chat/messages'
6
6
 
7
7
  export interface SessionMessagesPageResponse {
8
8
  messages: unknown[]
@@ -35,12 +35,6 @@ export interface UseSessionMessagesReturn<TMessage> {
35
35
  refreshLatestPage: () => Promise<void>
36
36
  }
37
37
 
38
- function deduplicateAndSort<TMessage extends { id: string; metadata?: { createdAt?: unknown } }>(
39
- messages: TMessage[],
40
- ): TMessage[] {
41
- return normalizeChatMessages(Array.from(new Map(messages.map((message) => [message.id, message])).values()))
42
- }
43
-
44
38
  async function normalizePage<TMessage extends { id: string; metadata?: { createdAt?: unknown } }>(params: {
45
39
  response: SessionMessagesPageResponse
46
40
  validateMessages?: (messages: unknown[]) => Promise<TMessage[]>
@@ -85,7 +79,7 @@ export function useSessionMessages<TMessage extends { id: string; metadata?: { c
85
79
 
86
80
  const messages = useMemo(() => {
87
81
  if (!infiniteQuery.data) return []
88
- return deduplicateAndSort(infiniteQuery.data.pages.flatMap((page) => page.messages))
82
+ return mergeChatMessages(...infiniteQuery.data.pages.map((page) => page.messages))
89
83
  }, [infiniteQuery.data])
90
84
 
91
85
  const loadOlderMessages = useCallback(async () => {