@aws-amplify/ui-react-ai 0.3.2 → 1.0.0

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.
Files changed (71) hide show
  1. package/dist/esm/components/AIConversation/AIConversation.mjs +21 -33
  2. package/dist/esm/components/AIConversation/AIConversationProvider.mjs +22 -16
  3. package/dist/esm/components/AIConversation/context/AIContextContext.mjs +8 -0
  4. package/dist/esm/components/AIConversation/context/FallbackComponentContext.mjs +8 -0
  5. package/dist/esm/components/AIConversation/context/MessageRenderContext.mjs +9 -0
  6. package/dist/esm/components/AIConversation/context/ResponseComponentsContext.mjs +6 -2
  7. package/dist/esm/components/AIConversation/context/WelcomeMessageContext.mjs +8 -0
  8. package/dist/esm/components/AIConversation/createAIConversation.mjs +18 -22
  9. package/dist/esm/components/AIConversation/views/Controls/ActionsBarControl.mjs +6 -2
  10. package/dist/esm/components/AIConversation/views/Controls/AttachFileControl.mjs +5 -0
  11. package/dist/esm/components/AIConversation/views/Controls/AttachmentListControl.mjs +5 -0
  12. package/dist/esm/components/AIConversation/views/Controls/AvatarControl.mjs +5 -0
  13. package/dist/esm/components/AIConversation/views/Controls/DefaultMessageControl.mjs +31 -0
  14. package/dist/esm/components/AIConversation/views/Controls/{FieldControl.mjs → FormControl.mjs} +22 -13
  15. package/dist/esm/components/AIConversation/views/Controls/MessagesControl.mjs +42 -42
  16. package/dist/esm/components/AIConversation/views/Controls/PromptControl.mjs +9 -36
  17. package/dist/esm/components/AIConversation/views/default/Form.mjs +4 -5
  18. package/dist/esm/components/AIConversation/views/default/MessageList.mjs +34 -16
  19. package/dist/esm/components/AIConversation/views/default/PromptList.mjs +1 -1
  20. package/dist/esm/hooks/contentFromEvents.mjs +22 -0
  21. package/dist/esm/hooks/createAIHooks.mjs +0 -3
  22. package/dist/esm/hooks/exhaustivelyListMessages.mjs +19 -0
  23. package/dist/esm/hooks/shared.mjs +14 -0
  24. package/dist/esm/hooks/useAIConversation.mjs +246 -106
  25. package/dist/esm/hooks/useAIGeneration.mjs +1 -8
  26. package/dist/esm/index.mjs +0 -1
  27. package/dist/esm/version.mjs +3 -0
  28. package/dist/index.js +569 -444
  29. package/dist/types/components/AIConversation/AIConversation.d.ts +2 -19
  30. package/dist/types/components/AIConversation/AIConversationProvider.d.ts +2 -3
  31. package/dist/types/components/AIConversation/context/AIContextContext.d.ts +6 -0
  32. package/dist/types/components/AIConversation/context/ControlsContext.d.ts +1 -0
  33. package/dist/types/components/AIConversation/context/FallbackComponentContext.d.ts +7 -0
  34. package/dist/types/components/AIConversation/context/MessageRenderContext.d.ts +5 -0
  35. package/dist/types/components/AIConversation/context/ResponseComponentsContext.d.ts +2 -2
  36. package/dist/types/components/AIConversation/context/WelcomeMessageContext.d.ts +8 -0
  37. package/dist/types/components/AIConversation/context/elements/definitions.d.ts +1 -1
  38. package/dist/types/components/AIConversation/context/index.d.ts +5 -0
  39. package/dist/types/components/AIConversation/createAIConversation.d.ts +0 -3
  40. package/dist/types/components/AIConversation/index.d.ts +2 -1
  41. package/dist/types/components/AIConversation/types.d.ts +24 -36
  42. package/dist/types/components/AIConversation/utils.d.ts +2 -2
  43. package/dist/types/components/AIConversation/views/Controls/DefaultMessageControl.d.ts +2 -0
  44. package/dist/types/components/AIConversation/views/Controls/{FieldControl.d.ts → FormControl.d.ts} +2 -2
  45. package/dist/types/components/AIConversation/views/Controls/MessagesControl.d.ts +3 -9
  46. package/dist/types/components/AIConversation/views/Controls/PromptControl.d.ts +0 -3
  47. package/dist/types/components/AIConversation/views/Controls/index.d.ts +3 -4
  48. package/dist/types/components/AIConversation/views/default/Form.d.ts +1 -1
  49. package/dist/types/components/AIConversation/views/default/MessageList.d.ts +1 -1
  50. package/dist/types/components/AIConversation/views/default/PromptList.d.ts +1 -1
  51. package/dist/types/components/AIConversation/views/index.d.ts +2 -3
  52. package/dist/types/hooks/contentFromEvents.d.ts +2 -0
  53. package/dist/types/hooks/createAIHooks.d.ts +0 -3
  54. package/dist/types/hooks/exhaustivelyListMessages.d.ts +8 -0
  55. package/dist/types/hooks/index.d.ts +1 -2
  56. package/dist/types/hooks/shared.d.ts +23 -0
  57. package/dist/types/hooks/useAIConversation.d.ts +6 -4
  58. package/dist/types/hooks/useAIGeneration.d.ts +3 -13
  59. package/dist/types/index.d.ts +1 -1
  60. package/dist/types/types.d.ts +38 -7
  61. package/dist/types/version.d.ts +1 -0
  62. package/package.json +20 -6
  63. package/dist/ai-conversation-styles.css +0 -195
  64. package/dist/ai-conversation-styles.js +0 -2
  65. package/dist/esm/components/AIConversation/views/Controls/HeaderControl.mjs +0 -34
  66. package/dist/esm/components/AIConversation/views/ConversationView.mjs +0 -20
  67. package/dist/esm/hooks/AIContextProvider.mjs +0 -20
  68. package/dist/types/ai-conversation-styles.d.ts +0 -1
  69. package/dist/types/components/AIConversation/views/Controls/HeaderControl.d.ts +0 -9
  70. package/dist/types/components/AIConversation/views/ConversationView.d.ts +0 -2
  71. package/dist/types/hooks/AIContextProvider.d.ts +0 -17
@@ -1,9 +1,10 @@
1
1
  import React__default from 'react';
2
2
  import { withBaseElementProps } from '@aws-amplify/ui-react-core/elements';
3
+ import '../../context/AIContextContext.mjs';
3
4
  import '../../context/ActionsContext.mjs';
4
5
  import '../../context/AvatarsContext.mjs';
5
6
  import { ConversationInputContext } from '../../context/ConversationInputContext.mjs';
6
- import { MessagesContext } from '../../context/MessagesContext.mjs';
7
+ import '../../context/MessagesContext.mjs';
7
8
  import { SuggestedPromptsContext } from '../../context/SuggestedPromptsContext.mjs';
8
9
  import '../../context/MessageVariantContext.mjs';
9
10
  import '../../context/DisplayTextContext.mjs';
@@ -11,10 +12,13 @@ import { ControlsContext } from '../../context/ControlsContext.mjs';
11
12
  import '../../context/LoadingContext.mjs';
12
13
  import '../../context/ResponseComponentsContext.mjs';
13
14
  import '../../context/SendMessageContext.mjs';
15
+ import '../../context/MessageRenderContext.mjs';
16
+ import '../../context/AttachmentContext.mjs';
17
+ import '../../context/WelcomeMessageContext.mjs';
18
+ import '../../context/FallbackComponentContext.mjs';
14
19
  import { AIConversationElements } from '../../context/elements/definitions.mjs';
15
- import { classNames } from '@aws-amplify/ui';
16
20
 
17
- const { View, Button, Text, Heading, Icon } = AIConversationElements;
21
+ const { View, Button } = AIConversationElements;
18
22
  const PROMPT_BLOCK = 'ai-prompts';
19
23
  const PROMPT_CONTROL = `${PROMPT_BLOCK}__prompt`;
20
24
  const PROMPT_CARD = `${PROMPT_CONTROL}__card`;
@@ -22,25 +26,6 @@ const PromptCard = withBaseElementProps(Button, {
22
26
  className: PROMPT_CARD,
23
27
  type: 'button',
24
28
  });
25
- const AIIconProps = () => ({
26
- children: (React__default.createElement(React__default.Fragment, null,
27
- React__default.createElement("path", { d: "M17.5 1.64858C19.047 0.755412 20.953 0.755412 22.5 1.64858L34.6428 8.65923C36.1898 9.55239 37.1428 11.203 37.1428 12.9894V27.0107C37.1428 28.797 36.1898 30.4476 34.6428 31.3408L22.5 38.3514C20.953 39.2446 19.047 39.2446 17.5 38.3514L5.35718 31.3408C3.81017 30.4476 2.85718 28.797 2.85718 27.0107V12.9894C2.85718 11.203 3.81017 9.55239 5.35718 8.65923L17.5 1.64858Z", fill: "white" }),
28
- React__default.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M22.5 1.64851C20.953 0.755347 19.047 0.755347 17.5 1.64851L5.35718 8.65916C3.81017 9.55233 2.85718 11.203 2.85718 12.9893V27.0106C2.85718 28.7969 3.81017 30.4476 5.35718 31.3407L17.5 38.3514C19.047 39.2445 20.953 39.2445 22.5 38.3514L34.6428 31.3407C36.1898 30.4476 37.1428 28.7969 37.1428 27.0106V12.9893C37.1428 11.203 36.1898 9.55233 34.6428 8.65916L22.5 1.64851ZM20.9378 8.01826C20.6156 7.14764 19.3843 7.14764 19.0621 8.01825L16.2388 15.648C16.1375 15.9217 15.9217 16.1375 15.648 16.2388L8.01826 19.0621C7.14765 19.3842 7.14765 20.6156 8.01826 20.9378L15.648 23.7611C15.9217 23.8623 16.1375 24.0782 16.2388 24.3519L19.0621 31.9816C19.3843 32.8522 20.6156 32.8522 20.9378 31.9816L23.7611 24.3519C23.8624 24.0782 24.0782 23.8623 24.3519 23.7611L31.9816 20.9378C32.8523 20.6156 32.8523 19.3842 31.9816 19.0621L24.3519 16.2388C24.0782 16.1375 23.8624 15.9217 23.7611 15.648L20.9378 8.01826Z", fill: "url(#paint0_linear_395_1815)" }),
29
- React__default.createElement("defs", null,
30
- React__default.createElement("linearGradient", { id: "paint0_linear_395_1815", x1: "20", y1: "0.978638", x2: "20", y2: "39.0213", gradientUnits: "userSpaceOnUse" },
31
- React__default.createElement("stop", { stopColor: "#7DD6E8" }),
32
- React__default.createElement("stop", { offset: "1", stopColor: "#BF40BF" }))))),
33
- className: `${PROMPT_CONTROL}__icon`,
34
- width: '40',
35
- height: '40',
36
- viewBox: '0 0 40 40',
37
- fill: 'none',
38
- xmlns: 'http://www.w3.org/2000/svg',
39
- });
40
- const AIIcon = withBaseElementProps(Icon, AIIconProps);
41
- const HeaderText = withBaseElementProps(Heading, {
42
- className: `${PROMPT_CONTROL}__header`,
43
- });
44
29
  const PromptGroupBase = withBaseElementProps(View, {
45
30
  className: `${PROMPT_CONTROL}__buttongroup`,
46
31
  });
@@ -55,9 +40,7 @@ const PromptGroup = React__default.forwardRef(function ButtonGroup(props, ref) {
55
40
  setInput((prevInput) => ({
56
41
  ...prevInput,
57
42
  text: prompt.inputText,
58
- })) },
59
- React__default.createElement(Text, { className: classNames(`${PROMPT_CARD}__header`, `${PROMPT_CARD}__text`) }, prompt.header),
60
- React__default.createElement(Text, { className: `${PROMPT_CARD}__text` }, prompt.inputText)));
43
+ })) }, prompt.component));
61
44
  })));
62
45
  });
63
46
  const Container = withBaseElementProps(View, {
@@ -71,20 +54,10 @@ const PromptControl = () => {
71
54
  return (React__default.createElement(controls.PromptList, { setInput: setInput, suggestedPrompts: suggestedPromptsArray }));
72
55
  }
73
56
  return (React__default.createElement(Container, null,
74
- React__default.createElement(AIIcon, null),
75
- React__default.createElement(HeaderText, null, "How can I help you today?"),
76
57
  React__default.createElement(PromptGroup, null)));
77
58
  };
78
- const AutoHidablePromptControl = () => {
79
- const messages = React__default.useContext(MessagesContext);
80
- if (!messages || messages.length === 0) {
81
- return React__default.createElement(PromptControl, null);
82
- }
83
- };
84
59
  PromptControl.Container = Container;
85
- PromptControl.Header = HeaderText;
86
- PromptControl.Icon = AIIcon;
87
60
  PromptControl.PromptGroup = PromptGroup;
88
61
  PromptControl.PromptCard = PromptCard;
89
62
 
90
- export { AutoHidablePromptControl, PromptControl };
63
+ export { PromptControl };
@@ -3,7 +3,6 @@ import { View, Button, VisuallyHidden, TextAreaField, DropZone } from '@aws-ampl
3
3
  import { useIcons, IconSend, IconAttach } from '@aws-amplify/ui-react/internal';
4
4
  import { ComponentClassName } from '@aws-amplify/ui';
5
5
  import { Attachments } from './Attachments.mjs';
6
- import { LoadingContext } from '../../context/LoadingContext.mjs';
7
6
 
8
7
  function isHTMLFormElement(target) {
9
8
  return 'form' in target;
@@ -25,12 +24,12 @@ const FormWrapper = ({ children, allowAttachments, setInput, }) => {
25
24
  return children;
26
25
  }
27
26
  };
28
- const Form = ({ setInput, input, handleSubmit, allowAttachments, }) => {
27
+ const Form = ({ setInput, input, handleSubmit, allowAttachments, isLoading, }) => {
29
28
  const icons = useIcons('aiConversation');
30
29
  const sendIcon = icons?.send ?? React.createElement(IconSend, null);
31
30
  const attachIcon = icons?.attach ?? React.createElement(IconAttach, null);
32
31
  const hiddenInput = React.useRef(null);
33
- const isLoading = React.useContext(LoadingContext);
32
+ const [composing, setComposing] = React.useState(false);
34
33
  const isInputEmpty = !input?.text?.length && !input?.files?.length;
35
34
  return (React.createElement(FormWrapper, { allowAttachments: allowAttachments, setInput: setInput },
36
35
  React.createElement(View, { as: "form", className: ComponentClassName.AIConversationForm, onSubmit: handleSubmit },
@@ -52,9 +51,9 @@ const Form = ({ setInput, input, handleSubmit, allowAttachments, }) => {
52
51
  files: [...(prevValue?.files ?? []), ...Array.from(files)],
53
52
  }));
54
53
  }, multiple: true, accept: "*", "data-testid": "hidden-file-input" })))) : null,
55
- React.createElement(TextAreaField, { className: ComponentClassName.AIConversationFormField, label: "input", labelHidden: true, autoResize: true, flex: "1", rows: 1, value: input?.text ?? '', testId: "text-input", onKeyDown: (e) => {
54
+ React.createElement(TextAreaField, { className: ComponentClassName.AIConversationFormField, label: "input", labelHidden: true, autoResize: true, flex: "1", rows: 1, value: input?.text ?? '', testId: "text-input", onCompositionStart: () => setComposing(true), onCompositionEnd: () => setComposing(false), onKeyDown: (e) => {
56
55
  // Submit on enter key if shift is not pressed also
57
- const shouldSubmit = !e.shiftKey && e.key === 'Enter';
56
+ const shouldSubmit = !e.shiftKey && e.key === 'Enter' && !composing;
58
57
  if (shouldSubmit && isHTMLFormElement(e.target)) {
59
58
  e.target.form.requestSubmit();
60
59
  e.preventDefault();
@@ -1,7 +1,8 @@
1
1
  import * as React from 'react';
2
- import { View, Avatar, Text } from '@aws-amplify/ui-react';
2
+ import { View, Avatar, Placeholder, Text, Button } from '@aws-amplify/ui-react';
3
3
  import { MessageControl } from '../Controls/MessagesControl.mjs';
4
- import '../../context/ActionsContext.mjs';
4
+ import '../../context/AIContextContext.mjs';
5
+ import { ActionsContext } from '../../context/ActionsContext.mjs';
5
6
  import { AvatarsContext } from '../../context/AvatarsContext.mjs';
6
7
  import '../../context/ConversationInputContext.mjs';
7
8
  import { RoleContext } from '../../context/MessagesContext.mjs';
@@ -12,9 +13,23 @@ import '../../context/ControlsContext.mjs';
12
13
  import { LoadingContext } from '../../context/LoadingContext.mjs';
13
14
  import { RESPONSE_COMPONENT_PREFIX } from '../../context/ResponseComponentsContext.mjs';
14
15
  import '../../context/SendMessageContext.mjs';
16
+ import '../../context/MessageRenderContext.mjs';
17
+ import '../../context/AttachmentContext.mjs';
18
+ import '../../context/WelcomeMessageContext.mjs';
19
+ import '../../context/FallbackComponentContext.mjs';
15
20
  import '../../context/elements/definitions.mjs';
16
21
  import { ComponentClassName, classNames, classNameModifier } from '@aws-amplify/ui';
17
22
 
23
+ const PlaceholderMessage = ({ role }) => {
24
+ const variant = React.useContext(MessageVariantContext);
25
+ return (React.createElement(View, { className: classNames(ComponentClassName.AIConversationMessage, classNameModifier(ComponentClassName.AIConversationMessage, variant), classNameModifier(ComponentClassName.AIConversationMessage, role)) },
26
+ React.createElement(View, { className: ComponentClassName.AIConversationMessageAvatar },
27
+ React.createElement(Avatar, null)),
28
+ React.createElement(View, { className: ComponentClassName.AIConversationMessageBody },
29
+ React.createElement(Placeholder, { width: "25%" }),
30
+ React.createElement(Placeholder, { width: "50%" }),
31
+ React.createElement(Placeholder, { width: "25%" }))));
32
+ };
18
33
  const MessageMeta = ({ message }) => {
19
34
  // need to pass this in as props in order for it to be overridable
20
35
  const avatars = React.useContext(AvatarsContext);
@@ -26,29 +41,30 @@ const MessageMeta = ({ message }) => {
26
41
  React.createElement(Text, { className: ComponentClassName.AIConversationMessageSenderUsername }, avatar?.username),
27
42
  React.createElement(Text, { className: ComponentClassName.AIConversationMessageSenderTimestamp }, getMessageTimestampText(new Date(message.createdAt)))));
28
43
  };
29
- const LoadingMessage = () => {
30
- const avatars = React.useContext(AvatarsContext);
31
- const variant = React.useContext(MessageVariantContext);
32
- const avatar = avatars?.ai;
33
- return (React.createElement(View, { className: classNames(ComponentClassName.AIConversationMessage, classNameModifier(ComponentClassName.AIConversationMessage, variant), classNameModifier(ComponentClassName.AIConversationMessage, 'assistant')) },
34
- React.createElement(View, { className: ComponentClassName.AIConversationMessageAvatar },
35
- React.createElement(Avatar, { isLoading: true }, avatar?.avatar)),
36
- React.createElement(View, { className: ComponentClassName.AIConversationMessageBody },
37
- React.createElement(View, { className: ComponentClassName.AIConversationMessageSender },
38
- React.createElement(Text, { className: ComponentClassName.AIConversationMessageSenderUsername }, avatar?.username)))));
44
+ const MessageActions = ({ message }) => {
45
+ const actions = React.useContext(ActionsContext);
46
+ if (!actions)
47
+ return null;
48
+ return (React.createElement(View, { className: ComponentClassName.AIConversationMessageActions }, actions.map((action, i) => {
49
+ return (React.createElement(Button, { key: i, size: "small", onClick: () => {
50
+ action.handler(message);
51
+ } }, action.component));
52
+ })));
39
53
  };
40
54
  const Message = ({ message }) => {
41
55
  const avatars = React.useContext(AvatarsContext);
42
56
  const variant = React.useContext(MessageVariantContext);
57
+ const { isLoading } = message;
43
58
  const avatar = message.role === 'assistant' ? avatars?.ai : avatars?.user;
44
59
  return (React.createElement(RoleContext.Provider, { value: message.role },
45
60
  React.createElement(View, { className: classNames(ComponentClassName.AIConversationMessage, classNameModifier(ComponentClassName.AIConversationMessage, variant), classNameModifier(ComponentClassName.AIConversationMessage, message.role)) },
46
61
  React.createElement(View, { className: ComponentClassName.AIConversationMessageAvatar },
47
- React.createElement(Avatar, null, avatar?.avatar)),
62
+ React.createElement(Avatar, { isLoading: isLoading }, avatar?.avatar)),
48
63
  React.createElement(View, { className: ComponentClassName.AIConversationMessageBody },
49
64
  React.createElement(MessageMeta, { message: message }),
50
65
  React.createElement(View, { className: ComponentClassName.AIConversationMessageContent },
51
- React.createElement(MessageControl, { message: message }))))));
66
+ React.createElement(MessageControl, { message: message })),
67
+ message.role === 'assistant' ? (React.createElement(MessageActions, { message: message })) : null))));
52
68
  };
53
69
  const MessageList = ({ messages, }) => {
54
70
  const isLoading = React.useContext(LoadingContext);
@@ -56,8 +72,10 @@ const MessageList = ({ messages, }) => {
56
72
  content.text ??
57
73
  content.toolUse?.name.startsWith(RESPONSE_COMPONENT_PREFIX))) ?? [];
58
74
  return (React.createElement(View, { className: ComponentClassName.AIConversationMessageList },
59
- messagesWithRenderableContent.map((message, i) => (React.createElement(Message, { key: `message-${i}`, message: message }))),
60
- isLoading ? React.createElement(LoadingMessage, null) : null));
75
+ isLoading ? (React.createElement(React.Fragment, null,
76
+ React.createElement(PlaceholderMessage, { role: "user" }),
77
+ React.createElement(PlaceholderMessage, { role: "assistant" }))) : null,
78
+ messagesWithRenderableContent.map((message, i) => (React.createElement(Message, { key: `message-${i}`, message: message })))));
61
79
  };
62
80
 
63
81
  export { MessageList };
@@ -9,7 +9,7 @@ const PromptList = ({ setInput, suggestedPrompts = [], }) => {
9
9
  ...prevInput,
10
10
  text: prompt.inputText,
11
11
  }));
12
- } }, prompt.header));
12
+ } }, prompt.component));
13
13
  })));
14
14
  };
15
15
 
@@ -0,0 +1,22 @@
1
+ const contentFromEvents = (contentBlocks) => {
2
+ if (!contentBlocks)
3
+ return [];
4
+ return contentBlocks.map((contentBlock) => {
5
+ const isTextBlock = contentBlock.some((event) => event.text);
6
+ if (isTextBlock) {
7
+ return {
8
+ text: contentBlock
9
+ .map((event) => {
10
+ return event.text;
11
+ })
12
+ .join(''),
13
+ };
14
+ }
15
+ // tool use is never chunked
16
+ if (contentBlock[0].toolUse) {
17
+ return { toolUse: contentBlock[0].toolUse };
18
+ }
19
+ });
20
+ };
21
+
22
+ export { contentFromEvents };
@@ -1,9 +1,6 @@
1
1
  import { createUseAIGeneration } from './useAIGeneration.mjs';
2
2
  import { createUseAIConversation } from './useAIConversation.mjs';
3
3
 
4
- /**
5
- * @experimental
6
- */
7
4
  function createAIHooks(_client) {
8
5
  const useAIConversation = createUseAIConversation(_client);
9
6
  const useAIGeneration = createUseAIGeneration(_client);
@@ -0,0 +1,19 @@
1
+ async function exhaustivelyListMessages({ conversation, messages = [], nextToken, }) {
2
+ const result = await conversation.listMessages({ nextToken });
3
+ if (result.data) {
4
+ messages?.push(...result.data);
5
+ }
6
+ if (result.nextToken) {
7
+ return exhaustivelyListMessages({
8
+ conversation,
9
+ messages,
10
+ nextToken: result.nextToken,
11
+ });
12
+ }
13
+ return {
14
+ ...result,
15
+ data: messages,
16
+ };
17
+ }
18
+
19
+ export { exhaustivelyListMessages };
@@ -0,0 +1,14 @@
1
+ // default state
2
+ const INITIAL_STATE = {
3
+ hasError: false,
4
+ isLoading: false,
5
+ messages: undefined,
6
+ };
7
+ const LOADING_STATE = {
8
+ hasError: false,
9
+ isLoading: true,
10
+ messages: undefined,
11
+ };
12
+ const ERROR_STATE = { hasError: true, isLoading: false };
13
+
14
+ export { ERROR_STATE, INITIAL_STATE, LOADING_STATE };