react-optimistic-chat 1.1.0 → 1.2.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.d.mts CHANGED
@@ -2,12 +2,13 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import React$1 from 'react';
3
3
 
4
4
  type ChatRole = "AI" | "USER";
5
- type Message = {
5
+ type BaseMessage = {
6
6
  id: number | string;
7
7
  role: ChatRole;
8
8
  content: string;
9
9
  isLoading?: boolean;
10
10
  };
11
+ type Message<T = {}> = BaseMessage & T;
11
12
 
12
13
  type Size$1 = 'xs' | 'sm' | 'md' | 'lg';
13
14
  type Props$5 = {
@@ -34,14 +35,15 @@ type Props$3 = Message & {
34
35
  };
35
36
  declare function ChatMessage({ id, role, content, isLoading, wrapperClassName, icon, aiIconWrapperClassName, aiIconColor, bubbleClassName, aiBubbleClassName, userBubbleClassName, position, loadingRenderer, }: Props$3): react_jsx_runtime.JSX.Element;
36
37
 
37
- type Props$2<T> = {
38
+ type MessagePatch = Partial<BaseMessage> & Record<string, unknown>;
39
+ type Props$2<T extends Message = Message> = {
38
40
  messages: T[];
39
- messageMapper?: (msg: T) => Message;
40
- messageRenderer?: (msg: Message) => React$1.ReactNode;
41
+ messageMapper?: (msg: T) => MessagePatch;
42
+ messageRenderer?: (msg: T) => React$1.ReactNode;
41
43
  className?: string;
42
44
  loadingRenderer?: React$1.ReactNode;
43
45
  };
44
- declare function ChatList<T>({ messages, messageMapper, messageRenderer, className, loadingRenderer, }: Props$2<T>): react_jsx_runtime.JSX.Element;
46
+ declare function ChatList<T extends Message>({ messages, messageMapper, messageRenderer, className, loadingRenderer, }: Props$2<T>): react_jsx_runtime.JSX.Element;
45
47
 
46
48
  type VoiceRecognitionController$1 = {
47
49
  start: () => void;
@@ -70,9 +72,15 @@ type Props$1 = {
70
72
  };
71
73
  declare function ChatInput({ onSend, voice, placeholder, className, inputClassName, micButton, recordingButton, sendButton, sendingButton, maxHeight, value, onChange, isSending, submitOnEnter, }: Props$1): react_jsx_runtime.JSX.Element;
72
74
 
73
- type Props<T> = {
75
+ type MessageProps = {
76
+ messages: Message[];
77
+ messageMapper?: never;
78
+ };
79
+ type RawProps<T> = {
74
80
  messages: T[];
75
- messageMapper?: (msg: T) => Message;
81
+ messageMapper: (msg: T) => Message;
82
+ };
83
+ type CommonProps = {
76
84
  messageRenderer?: (msg: Message) => React.ReactNode;
77
85
  loadingRenderer?: React.ReactNode;
78
86
  listClassName?: string;
@@ -83,20 +91,23 @@ type Props<T> = {
83
91
  inputClassName?: string;
84
92
  className?: string;
85
93
  };
86
- declare function ChatContainer<T>({ messages, messageMapper, messageRenderer, loadingRenderer, listClassName, onSend, isSending, disableVoice, placeholder, inputClassName, className, }: Props<T>): react_jsx_runtime.JSX.Element;
94
+ type Props<T> = CommonProps & (MessageProps | RawProps<T>);
95
+ declare function ChatContainer<T>(props: Props<T>): react_jsx_runtime.JSX.Element;
87
96
 
88
- type MessageMapper$1<TRaw> = (raw: TRaw) => Message;
89
- type Options$2<TQueryRaw, TMutationRaw> = {
97
+ type ExtraFromRaw$1<TRaw> = Omit<TRaw, keyof BaseMessage>;
98
+ type MessageMapperResult$1 = Pick<BaseMessage, "id" | "role" | "content">;
99
+ type MessageMapper$1<TRaw> = Message<ExtraFromRaw$1<TRaw>>;
100
+ type Options$2<TRaw> = {
90
101
  queryKey: readonly unknown[];
91
- queryFn: () => Promise<TQueryRaw[]>;
92
- mutationFn: (content: string) => Promise<TMutationRaw>;
93
- map: MessageMapper$1<TQueryRaw | TMutationRaw>;
102
+ queryFn: () => Promise<TRaw[]>;
103
+ mutationFn: (content: string) => Promise<TRaw>;
104
+ map: (raw: TRaw) => MessageMapperResult$1;
94
105
  onError?: (error: unknown) => void;
95
106
  staleTime?: number;
96
107
  gcTime?: number;
97
108
  };
98
- declare function useOptimisticChat<TQeuryRaw, TMutationRaw>({ queryKey, queryFn, mutationFn, map, onError, staleTime, gcTime, }: Options$2<TQeuryRaw, TMutationRaw>): {
99
- messages: Message[];
109
+ declare function useOptimisticChat<TRaw>({ queryKey, queryFn, mutationFn, map, onError, staleTime, gcTime, }: Options$2<TRaw>): {
110
+ messages: MessageMapper$1<TRaw>[];
100
111
  sendUserMessage: (content: string) => void;
101
112
  isPending: boolean;
102
113
  isInitialLoading: boolean;
@@ -172,25 +183,27 @@ declare function useBrowserSpeechRecognition({ lang, onStart, onEnd, onError, }?
172
183
  onTranscript: (text: string) => void;
173
184
  };
174
185
 
175
- type MessageMapper<TRaw> = (raw: TRaw) => Message;
176
186
  type VoiceRecognitionController = {
177
187
  start: () => void;
178
188
  stop: () => void;
179
189
  isRecording: boolean;
180
190
  onTranscript: (text: string) => void;
181
191
  };
182
- type Options<TQueryRaw, TMutationRaw> = {
192
+ type ExtraFromRaw<TRaw> = Omit<TRaw, keyof BaseMessage>;
193
+ type MessageMapperResult = Pick<BaseMessage, "id" | "role" | "content">;
194
+ type MessageMapper<TRaw> = Message<ExtraFromRaw<TRaw>>;
195
+ type Options<TRaw> = {
183
196
  queryKey: readonly unknown[];
184
- queryFn: () => Promise<TQueryRaw[]>;
185
- mutationFn: (content: string) => Promise<TMutationRaw>;
186
- map: MessageMapper<TQueryRaw | TMutationRaw>;
197
+ queryFn: () => Promise<TRaw[]>;
198
+ mutationFn: (content: string) => Promise<TRaw>;
199
+ map: (raw: TRaw) => MessageMapperResult;
187
200
  voice: VoiceRecognitionController;
188
201
  onError?: (error: unknown) => void;
189
202
  staleTime?: number;
190
203
  gcTime?: number;
191
204
  };
192
- declare function useVoiceOptimisticChat<TQeuryRaw, TMutationRaw>({ queryKey, queryFn, mutationFn, map, voice, onError, staleTime, gcTime, }: Options<TQeuryRaw, TMutationRaw>): {
193
- messages: Message[];
205
+ declare function useVoiceOptimisticChat<TRaw>({ queryKey, queryFn, mutationFn, map, voice, onError, staleTime, gcTime, }: Options<TRaw>): {
206
+ messages: MessageMapper<TRaw>[];
194
207
  isPending: boolean;
195
208
  isInitialLoading: boolean;
196
209
  startRecording: () => Promise<void>;
package/dist/index.d.ts CHANGED
@@ -2,12 +2,13 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import React$1 from 'react';
3
3
 
4
4
  type ChatRole = "AI" | "USER";
5
- type Message = {
5
+ type BaseMessage = {
6
6
  id: number | string;
7
7
  role: ChatRole;
8
8
  content: string;
9
9
  isLoading?: boolean;
10
10
  };
11
+ type Message<T = {}> = BaseMessage & T;
11
12
 
12
13
  type Size$1 = 'xs' | 'sm' | 'md' | 'lg';
13
14
  type Props$5 = {
@@ -34,14 +35,15 @@ type Props$3 = Message & {
34
35
  };
35
36
  declare function ChatMessage({ id, role, content, isLoading, wrapperClassName, icon, aiIconWrapperClassName, aiIconColor, bubbleClassName, aiBubbleClassName, userBubbleClassName, position, loadingRenderer, }: Props$3): react_jsx_runtime.JSX.Element;
36
37
 
37
- type Props$2<T> = {
38
+ type MessagePatch = Partial<BaseMessage> & Record<string, unknown>;
39
+ type Props$2<T extends Message = Message> = {
38
40
  messages: T[];
39
- messageMapper?: (msg: T) => Message;
40
- messageRenderer?: (msg: Message) => React$1.ReactNode;
41
+ messageMapper?: (msg: T) => MessagePatch;
42
+ messageRenderer?: (msg: T) => React$1.ReactNode;
41
43
  className?: string;
42
44
  loadingRenderer?: React$1.ReactNode;
43
45
  };
44
- declare function ChatList<T>({ messages, messageMapper, messageRenderer, className, loadingRenderer, }: Props$2<T>): react_jsx_runtime.JSX.Element;
46
+ declare function ChatList<T extends Message>({ messages, messageMapper, messageRenderer, className, loadingRenderer, }: Props$2<T>): react_jsx_runtime.JSX.Element;
45
47
 
46
48
  type VoiceRecognitionController$1 = {
47
49
  start: () => void;
@@ -70,9 +72,15 @@ type Props$1 = {
70
72
  };
71
73
  declare function ChatInput({ onSend, voice, placeholder, className, inputClassName, micButton, recordingButton, sendButton, sendingButton, maxHeight, value, onChange, isSending, submitOnEnter, }: Props$1): react_jsx_runtime.JSX.Element;
72
74
 
73
- type Props<T> = {
75
+ type MessageProps = {
76
+ messages: Message[];
77
+ messageMapper?: never;
78
+ };
79
+ type RawProps<T> = {
74
80
  messages: T[];
75
- messageMapper?: (msg: T) => Message;
81
+ messageMapper: (msg: T) => Message;
82
+ };
83
+ type CommonProps = {
76
84
  messageRenderer?: (msg: Message) => React.ReactNode;
77
85
  loadingRenderer?: React.ReactNode;
78
86
  listClassName?: string;
@@ -83,20 +91,23 @@ type Props<T> = {
83
91
  inputClassName?: string;
84
92
  className?: string;
85
93
  };
86
- declare function ChatContainer<T>({ messages, messageMapper, messageRenderer, loadingRenderer, listClassName, onSend, isSending, disableVoice, placeholder, inputClassName, className, }: Props<T>): react_jsx_runtime.JSX.Element;
94
+ type Props<T> = CommonProps & (MessageProps | RawProps<T>);
95
+ declare function ChatContainer<T>(props: Props<T>): react_jsx_runtime.JSX.Element;
87
96
 
88
- type MessageMapper$1<TRaw> = (raw: TRaw) => Message;
89
- type Options$2<TQueryRaw, TMutationRaw> = {
97
+ type ExtraFromRaw$1<TRaw> = Omit<TRaw, keyof BaseMessage>;
98
+ type MessageMapperResult$1 = Pick<BaseMessage, "id" | "role" | "content">;
99
+ type MessageMapper$1<TRaw> = Message<ExtraFromRaw$1<TRaw>>;
100
+ type Options$2<TRaw> = {
90
101
  queryKey: readonly unknown[];
91
- queryFn: () => Promise<TQueryRaw[]>;
92
- mutationFn: (content: string) => Promise<TMutationRaw>;
93
- map: MessageMapper$1<TQueryRaw | TMutationRaw>;
102
+ queryFn: () => Promise<TRaw[]>;
103
+ mutationFn: (content: string) => Promise<TRaw>;
104
+ map: (raw: TRaw) => MessageMapperResult$1;
94
105
  onError?: (error: unknown) => void;
95
106
  staleTime?: number;
96
107
  gcTime?: number;
97
108
  };
98
- declare function useOptimisticChat<TQeuryRaw, TMutationRaw>({ queryKey, queryFn, mutationFn, map, onError, staleTime, gcTime, }: Options$2<TQeuryRaw, TMutationRaw>): {
99
- messages: Message[];
109
+ declare function useOptimisticChat<TRaw>({ queryKey, queryFn, mutationFn, map, onError, staleTime, gcTime, }: Options$2<TRaw>): {
110
+ messages: MessageMapper$1<TRaw>[];
100
111
  sendUserMessage: (content: string) => void;
101
112
  isPending: boolean;
102
113
  isInitialLoading: boolean;
@@ -172,25 +183,27 @@ declare function useBrowserSpeechRecognition({ lang, onStart, onEnd, onError, }?
172
183
  onTranscript: (text: string) => void;
173
184
  };
174
185
 
175
- type MessageMapper<TRaw> = (raw: TRaw) => Message;
176
186
  type VoiceRecognitionController = {
177
187
  start: () => void;
178
188
  stop: () => void;
179
189
  isRecording: boolean;
180
190
  onTranscript: (text: string) => void;
181
191
  };
182
- type Options<TQueryRaw, TMutationRaw> = {
192
+ type ExtraFromRaw<TRaw> = Omit<TRaw, keyof BaseMessage>;
193
+ type MessageMapperResult = Pick<BaseMessage, "id" | "role" | "content">;
194
+ type MessageMapper<TRaw> = Message<ExtraFromRaw<TRaw>>;
195
+ type Options<TRaw> = {
183
196
  queryKey: readonly unknown[];
184
- queryFn: () => Promise<TQueryRaw[]>;
185
- mutationFn: (content: string) => Promise<TMutationRaw>;
186
- map: MessageMapper<TQueryRaw | TMutationRaw>;
197
+ queryFn: () => Promise<TRaw[]>;
198
+ mutationFn: (content: string) => Promise<TRaw>;
199
+ map: (raw: TRaw) => MessageMapperResult;
187
200
  voice: VoiceRecognitionController;
188
201
  onError?: (error: unknown) => void;
189
202
  staleTime?: number;
190
203
  gcTime?: number;
191
204
  };
192
- declare function useVoiceOptimisticChat<TQeuryRaw, TMutationRaw>({ queryKey, queryFn, mutationFn, map, voice, onError, staleTime, gcTime, }: Options<TQeuryRaw, TMutationRaw>): {
193
- messages: Message[];
205
+ declare function useVoiceOptimisticChat<TRaw>({ queryKey, queryFn, mutationFn, map, voice, onError, staleTime, gcTime, }: Options<TRaw>): {
206
+ messages: MessageMapper<TRaw>[];
194
207
  isPending: boolean;
195
208
  isInitialLoading: boolean;
196
209
  startRecording: () => Promise<void>;
package/dist/index.js CHANGED
@@ -206,7 +206,7 @@ function ChatList({
206
206
  className,
207
207
  loadingRenderer
208
208
  }) {
209
- const mappedMessages = messageMapper ? messages.map(messageMapper) : messages;
209
+ const mappedMessages = messageMapper ? messages.map((msg) => __spreadValues(__spreadValues({}, msg), messageMapper(msg))) : messages;
210
210
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: `flex flex-col ${className}`, children: mappedMessages.map((msg) => {
211
211
  if (messageRenderer) {
212
212
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react2.default.Fragment, { children: messageRenderer(msg) }, msg.id);
@@ -518,21 +518,22 @@ function ChatInput({
518
518
  // src/components/ChatContainer.tsx
519
519
  var import_react5 = require("react");
520
520
  var import_jsx_runtime6 = require("react/jsx-runtime");
521
- function ChatContainer({
522
- messages,
523
- messageMapper,
524
- messageRenderer,
525
- loadingRenderer,
526
- listClassName,
527
- onSend,
528
- isSending,
529
- disableVoice,
530
- placeholder,
531
- inputClassName,
532
- className
533
- }) {
521
+ function ChatContainer(props) {
534
522
  const [isAtBottom, setIsAtBottom] = (0, import_react5.useState)(true);
535
523
  const scrollRef = (0, import_react5.useRef)(null);
524
+ const {
525
+ messages,
526
+ messageRenderer,
527
+ loadingRenderer,
528
+ listClassName,
529
+ onSend,
530
+ isSending,
531
+ disableVoice,
532
+ placeholder,
533
+ inputClassName,
534
+ className
535
+ } = props;
536
+ const mappedMessages = typeof props.messageMapper === "function" ? props.messages.map(props.messageMapper) : messages;
536
537
  (0, import_react5.useEffect)(() => {
537
538
  const el = scrollRef.current;
538
539
  if (!el) return;
@@ -578,9 +579,9 @@ function ChatContainer({
578
579
  className: `flex-1 overflow-y-auto chatContainer-scroll p-2`,
579
580
  children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
580
581
  ChatList,
581
- __spreadValues(__spreadValues(__spreadValues(__spreadValues({
582
- messages
583
- }, messageMapper && { messageMapper }), messageRenderer && { messageRenderer }), loadingRenderer && { loadingRenderer }), listClassName && { className: listClassName })
582
+ __spreadValues(__spreadValues(__spreadValues({
583
+ messages: mappedMessages
584
+ }, messageRenderer && { messageRenderer }), loadingRenderer && { loadingRenderer }), listClassName && { className: listClassName })
584
585
  )
585
586
  }
586
587
  ),
@@ -644,8 +645,8 @@ function useOptimisticChat({
644
645
  } = (0, import_react_query.useQuery)({
645
646
  queryKey,
646
647
  queryFn: async () => {
647
- const rawList = await queryFn();
648
- return rawList.map(map);
648
+ const raw = await queryFn();
649
+ return raw.map((r) => __spreadValues(__spreadValues({}, map(r)), r));
649
650
  },
650
651
  staleTime,
651
652
  gcTime
@@ -678,10 +679,10 @@ function useOptimisticChat({
678
679
  }
679
680
  ];
680
681
  });
681
- return { prev };
682
+ return prev ? { prev } : {};
682
683
  },
683
684
  onSuccess: (rawAiResponse) => {
684
- const aiMessage = map(rawAiResponse);
685
+ const aiMessage = __spreadValues(__spreadValues({}, map(rawAiResponse)), rawAiResponse);
685
686
  queryClient.setQueryData(queryKey, (old) => {
686
687
  if (!old || old.length === 0) {
687
688
  return [aiMessage];
@@ -701,10 +702,6 @@ function useOptimisticChat({
701
702
  queryClient.setQueryData(queryKey, context.prev);
702
703
  }
703
704
  onError == null ? void 0 : onError(error);
704
- },
705
- // mutation 이후 서버 기준 최신 데이터 재동기화
706
- onSettled: () => {
707
- queryClient.invalidateQueries({ queryKey });
708
705
  }
709
706
  });
710
707
  const sendUserMessage = (content) => {
@@ -746,8 +743,8 @@ function useVoiceOptimisticChat({
746
743
  } = (0, import_react_query2.useQuery)({
747
744
  queryKey,
748
745
  queryFn: async () => {
749
- const rawList = await queryFn();
750
- return rawList.map(map);
746
+ const raw = await queryFn();
747
+ return raw.map((r) => __spreadValues(__spreadValues({}, map(r)), r));
751
748
  },
752
749
  staleTime,
753
750
  gcTime
@@ -774,10 +771,10 @@ function useVoiceOptimisticChat({
774
771
  }
775
772
  ];
776
773
  });
777
- return { prev };
774
+ return prev ? { prev } : {};
778
775
  },
779
776
  onSuccess: (rawAiResponse) => {
780
- const aiMessage = map(rawAiResponse);
777
+ const aiMessage = __spreadValues(__spreadValues({}, map(rawAiResponse)), rawAiResponse);
781
778
  queryClient.setQueryData(queryKey, (old) => {
782
779
  if (!old || old.length === 0) {
783
780
  return [aiMessage];
@@ -797,10 +794,6 @@ function useVoiceOptimisticChat({
797
794
  queryClient.setQueryData(queryKey, context.prev);
798
795
  }
799
796
  onError == null ? void 0 : onError(error);
800
- },
801
- // mutation 이후 서버 기준 최신 데이터 재동기화
802
- onSettled: () => {
803
- queryClient.invalidateQueries({ queryKey });
804
797
  }
805
798
  });
806
799
  const startRecording = async () => {
@@ -850,7 +843,7 @@ function useVoiceOptimisticChat({
850
843
  };
851
844
  return {
852
845
  messages,
853
- // Message[]
846
+ // Message<TExtra>[]
854
847
  isPending,
855
848
  // 사용자가 채팅 전송 후 AI 응답이 올 때까지의 로딩
856
849
  isInitialLoading,
package/dist/index.mjs CHANGED
@@ -165,7 +165,7 @@ function ChatList({
165
165
  className,
166
166
  loadingRenderer
167
167
  }) {
168
- const mappedMessages = messageMapper ? messages.map(messageMapper) : messages;
168
+ const mappedMessages = messageMapper ? messages.map((msg) => __spreadValues(__spreadValues({}, msg), messageMapper(msg))) : messages;
169
169
  return /* @__PURE__ */ jsx4("div", { className: `flex flex-col ${className}`, children: mappedMessages.map((msg) => {
170
170
  if (messageRenderer) {
171
171
  return /* @__PURE__ */ jsx4(React2.Fragment, { children: messageRenderer(msg) }, msg.id);
@@ -477,21 +477,22 @@ function ChatInput({
477
477
  // src/components/ChatContainer.tsx
478
478
  import { useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
479
479
  import { Fragment, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
480
- function ChatContainer({
481
- messages,
482
- messageMapper,
483
- messageRenderer,
484
- loadingRenderer,
485
- listClassName,
486
- onSend,
487
- isSending,
488
- disableVoice,
489
- placeholder,
490
- inputClassName,
491
- className
492
- }) {
480
+ function ChatContainer(props) {
493
481
  const [isAtBottom, setIsAtBottom] = useState3(true);
494
482
  const scrollRef = useRef3(null);
483
+ const {
484
+ messages,
485
+ messageRenderer,
486
+ loadingRenderer,
487
+ listClassName,
488
+ onSend,
489
+ isSending,
490
+ disableVoice,
491
+ placeholder,
492
+ inputClassName,
493
+ className
494
+ } = props;
495
+ const mappedMessages = typeof props.messageMapper === "function" ? props.messages.map(props.messageMapper) : messages;
495
496
  useEffect3(() => {
496
497
  const el = scrollRef.current;
497
498
  if (!el) return;
@@ -537,9 +538,9 @@ function ChatContainer({
537
538
  className: `flex-1 overflow-y-auto chatContainer-scroll p-2`,
538
539
  children: /* @__PURE__ */ jsx6(
539
540
  ChatList,
540
- __spreadValues(__spreadValues(__spreadValues(__spreadValues({
541
- messages
542
- }, messageMapper && { messageMapper }), messageRenderer && { messageRenderer }), loadingRenderer && { loadingRenderer }), listClassName && { className: listClassName })
541
+ __spreadValues(__spreadValues(__spreadValues({
542
+ messages: mappedMessages
543
+ }, messageRenderer && { messageRenderer }), loadingRenderer && { loadingRenderer }), listClassName && { className: listClassName })
543
544
  )
544
545
  }
545
546
  ),
@@ -603,8 +604,8 @@ function useOptimisticChat({
603
604
  } = useQuery({
604
605
  queryKey,
605
606
  queryFn: async () => {
606
- const rawList = await queryFn();
607
- return rawList.map(map);
607
+ const raw = await queryFn();
608
+ return raw.map((r) => __spreadValues(__spreadValues({}, map(r)), r));
608
609
  },
609
610
  staleTime,
610
611
  gcTime
@@ -637,10 +638,10 @@ function useOptimisticChat({
637
638
  }
638
639
  ];
639
640
  });
640
- return { prev };
641
+ return prev ? { prev } : {};
641
642
  },
642
643
  onSuccess: (rawAiResponse) => {
643
- const aiMessage = map(rawAiResponse);
644
+ const aiMessage = __spreadValues(__spreadValues({}, map(rawAiResponse)), rawAiResponse);
644
645
  queryClient.setQueryData(queryKey, (old) => {
645
646
  if (!old || old.length === 0) {
646
647
  return [aiMessage];
@@ -660,10 +661,6 @@ function useOptimisticChat({
660
661
  queryClient.setQueryData(queryKey, context.prev);
661
662
  }
662
663
  onError == null ? void 0 : onError(error);
663
- },
664
- // mutation 이후 서버 기준 최신 데이터 재동기화
665
- onSettled: () => {
666
- queryClient.invalidateQueries({ queryKey });
667
664
  }
668
665
  });
669
666
  const sendUserMessage = (content) => {
@@ -705,8 +702,8 @@ function useVoiceOptimisticChat({
705
702
  } = useQuery2({
706
703
  queryKey,
707
704
  queryFn: async () => {
708
- const rawList = await queryFn();
709
- return rawList.map(map);
705
+ const raw = await queryFn();
706
+ return raw.map((r) => __spreadValues(__spreadValues({}, map(r)), r));
710
707
  },
711
708
  staleTime,
712
709
  gcTime
@@ -733,10 +730,10 @@ function useVoiceOptimisticChat({
733
730
  }
734
731
  ];
735
732
  });
736
- return { prev };
733
+ return prev ? { prev } : {};
737
734
  },
738
735
  onSuccess: (rawAiResponse) => {
739
- const aiMessage = map(rawAiResponse);
736
+ const aiMessage = __spreadValues(__spreadValues({}, map(rawAiResponse)), rawAiResponse);
740
737
  queryClient.setQueryData(queryKey, (old) => {
741
738
  if (!old || old.length === 0) {
742
739
  return [aiMessage];
@@ -756,10 +753,6 @@ function useVoiceOptimisticChat({
756
753
  queryClient.setQueryData(queryKey, context.prev);
757
754
  }
758
755
  onError == null ? void 0 : onError(error);
759
- },
760
- // mutation 이후 서버 기준 최신 데이터 재동기화
761
- onSettled: () => {
762
- queryClient.invalidateQueries({ queryKey });
763
756
  }
764
757
  });
765
758
  const startRecording = async () => {
@@ -809,7 +802,7 @@ function useVoiceOptimisticChat({
809
802
  };
810
803
  return {
811
804
  messages,
812
- // Message[]
805
+ // Message<TExtra>[]
813
806
  isPending,
814
807
  // 사용자가 채팅 전송 후 AI 응답이 올 때까지의 로딩
815
808
  isInitialLoading,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-optimistic-chat",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",