@envive-ai/react-hooks 0.3.18 → 0.3.19-alpha-marlo-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 (206) hide show
  1. package/dist/application/commerce-api.cjs +14 -13
  2. package/dist/application/commerce-api.js +14 -13
  3. package/dist/application/logging/logger.cjs +16 -10
  4. package/dist/application/logging/logger.js +16 -10
  5. package/dist/application/models/guards/api/isApiFormResponse.cjs +9 -8
  6. package/dist/application/models/guards/api/isApiFormResponse.js +9 -8
  7. package/dist/application/models/guards/api/isApiFormSubmittedResponseAttributes.cjs +6 -5
  8. package/dist/application/models/guards/api/isApiFormSubmittedResponseAttributes.js +6 -5
  9. package/dist/application/models/guards/api/isApiOrderResponseAttributes.cjs +19 -18
  10. package/dist/application/models/guards/api/isApiOrderResponseAttributes.js +19 -18
  11. package/dist/application/models/guards/api/isApiOrgConfigResults.cjs +27 -26
  12. package/dist/application/models/guards/api/isApiOrgConfigResults.js +27 -26
  13. package/dist/application/models/guards/api/isApiOrganizationConfig.cjs +23 -22
  14. package/dist/application/models/guards/api/isApiOrganizationConfig.js +23 -22
  15. package/dist/application/models/guards/api/isApiProductResponseAttributes.cjs +12 -11
  16. package/dist/application/models/guards/api/isApiProductResponseAttributes.js +12 -11
  17. package/dist/application/models/guards/api/isApiResponse.cjs +7 -6
  18. package/dist/application/models/guards/api/isApiResponse.js +7 -6
  19. package/dist/application/models/guards/graphQL/isGraphQLColorsConfig.cjs +5 -4
  20. package/dist/application/models/guards/graphQL/isGraphQLColorsConfig.js +5 -4
  21. package/dist/application/models/validators/validateGraphQLFrontendConfig.cjs +15 -14
  22. package/dist/application/models/validators/validateGraphQLFrontendConfig.js +15 -14
  23. package/dist/application/utils/analyticsUtils.cjs +4 -3
  24. package/dist/application/utils/analyticsUtils.js +4 -3
  25. package/dist/application/utils/nextMessageRequestToApiRequest.cjs +3 -1
  26. package/dist/application/utils/nextMessageRequestToApiRequest.js +3 -1
  27. package/dist/atoms/app/index.d.cts +1 -1
  28. package/dist/atoms/app/index.d.ts +7 -7
  29. package/dist/atoms/app/variant.cjs +3 -2
  30. package/dist/atoms/app/variant.d.cts +6 -6
  31. package/dist/atoms/app/variant.js +3 -2
  32. package/dist/atoms/chat/chatState.d.cts +18 -18
  33. package/dist/atoms/chat/chatState.d.ts +18 -18
  34. package/dist/atoms/chat/form.d.cts +3 -3
  35. package/dist/atoms/chat/form.d.ts +2 -2
  36. package/dist/atoms/chat/index.d.cts +3 -3
  37. package/dist/atoms/chat/index.d.ts +3 -3
  38. package/dist/atoms/chat/lastMessage.d.cts +2 -2
  39. package/dist/atoms/chat/lastMessage.d.ts +2 -2
  40. package/dist/atoms/chat/messageQueue.d.cts +7 -7
  41. package/dist/atoms/chat/messageQueue.d.ts +6 -6
  42. package/dist/atoms/chat/performanceMetrics.cjs +3 -2
  43. package/dist/atoms/chat/performanceMetrics.d.cts +6 -6
  44. package/dist/atoms/chat/performanceMetrics.d.ts +6 -6
  45. package/dist/atoms/chat/performanceMetrics.js +3 -2
  46. package/dist/atoms/chat/renderedWidgetRefs.d.cts +3 -3
  47. package/dist/atoms/chat/renderedWidgetRefs.d.ts +2 -2
  48. package/dist/atoms/chat/replies.d.cts +3 -3
  49. package/dist/atoms/chat/replies.d.ts +3 -3
  50. package/dist/atoms/chat/suggestions.d.cts +3 -3
  51. package/dist/atoms/chat/suggestions.d.ts +2 -2
  52. package/dist/atoms/envive/enviveConfig.cjs +5 -4
  53. package/dist/atoms/envive/enviveConfig.d.cts +13 -13
  54. package/dist/atoms/envive/enviveConfig.d.ts +13 -13
  55. package/dist/atoms/envive/enviveConfig.js +5 -4
  56. package/dist/atoms/globalSearch/globalSearch.d.cts +5 -5
  57. package/dist/atoms/globalSearch/globalSearch.d.ts +5 -5
  58. package/dist/atoms/org/customerService.d.cts +6 -6
  59. package/dist/atoms/org/customerService.d.ts +6 -6
  60. package/dist/atoms/org/graphqlConfig.d.cts +4 -4
  61. package/dist/atoms/org/graphqlConfig.d.ts +4 -4
  62. package/dist/atoms/org/newOrgConfigAtom.d.cts +2 -2
  63. package/dist/atoms/org/newOrgConfigAtom.d.ts +2 -2
  64. package/dist/atoms/org/orgAnalyticsConfig.d.cts +5 -5
  65. package/dist/atoms/org/orgAnalyticsConfig.d.ts +5 -5
  66. package/dist/atoms/search/chatSearch.d.cts +17 -17
  67. package/dist/atoms/search/searchAPI.d.cts +13 -13
  68. package/dist/atoms/search/types.d.cts +1 -1
  69. package/dist/atoms/search/utils.d.cts +1 -1
  70. package/dist/atoms/widget/chatPreviewLoading.d.ts +2 -2
  71. package/dist/contexts/amplitudeContext/amplitudeContext.cjs +4 -3
  72. package/dist/contexts/amplitudeContext/amplitudeContext.js +3 -2
  73. package/dist/contexts/enviveConfigContext/enviveConfigContext.cjs +26 -15
  74. package/dist/contexts/enviveConfigContext/enviveConfigContext.d.cts +2 -4
  75. package/dist/contexts/enviveConfigContext/enviveConfigContext.d.ts +2 -4
  76. package/dist/contexts/enviveConfigContext/enviveConfigContext.js +29 -16
  77. package/dist/contexts/enviveConfigContext/index.cjs +2 -1
  78. package/dist/contexts/enviveConfigContext/index.d.cts +2 -1
  79. package/dist/contexts/enviveConfigContext/index.d.ts +2 -1
  80. package/dist/contexts/enviveConfigContext/index.js +2 -1
  81. package/dist/contexts/enviveConfigContext/useEnviveConfig.cjs +12 -0
  82. package/dist/contexts/enviveConfigContext/useEnviveConfig.d.cts +7 -0
  83. package/dist/contexts/enviveConfigContext/useEnviveConfig.d.ts +7 -0
  84. package/dist/contexts/enviveConfigContext/useEnviveConfig.js +11 -0
  85. package/dist/contexts/enviveContext/WindowChatToggleBinder.cjs +7 -5
  86. package/dist/contexts/enviveContext/WindowChatToggleBinder.js +6 -4
  87. package/dist/contexts/enviveContext/enviveContext.cjs +74 -45
  88. package/dist/contexts/enviveContext/enviveContext.d.cts +8 -2
  89. package/dist/contexts/enviveContext/enviveContext.d.ts +8 -2
  90. package/dist/contexts/enviveContext/enviveContext.js +75 -46
  91. package/dist/contexts/enviveContext/index.d.cts +2 -2
  92. package/dist/contexts/enviveContext/index.d.ts +2 -2
  93. package/dist/contexts/enviveContext/types.d.cts +1 -1
  94. package/dist/contexts/enviveContext/types.d.ts +1 -1
  95. package/dist/contexts/featureFlagServiceContext/featureFlagServiceContext.cjs +7 -6
  96. package/dist/contexts/featureFlagServiceContext/featureFlagServiceContext.js +7 -6
  97. package/dist/contexts/graphqlContext/graphqlContext.cjs +8 -7
  98. package/dist/contexts/graphqlContext/graphqlContext.js +8 -7
  99. package/dist/contexts/hardcopyContext/hardcopyContext.cjs +9 -8
  100. package/dist/contexts/hardcopyContext/hardcopyContext.js +9 -8
  101. package/dist/contexts/localStorageContext/localStorageContext.cjs +4 -3
  102. package/dist/contexts/localStorageContext/localStorageContext.js +4 -3
  103. package/dist/contexts/pageContext/pageContext.cjs +23 -4
  104. package/dist/contexts/pageContext/pageContext.d.cts +3 -1
  105. package/dist/contexts/pageContext/pageContext.d.ts +3 -1
  106. package/dist/contexts/pageContext/pageContext.js +24 -5
  107. package/dist/contexts/pageContext/types.d.ts +1 -1
  108. package/dist/contexts/salesAgentContext/chatAPI.cjs +3 -2
  109. package/dist/contexts/salesAgentContext/chatAPI.js +3 -2
  110. package/dist/contexts/salesAgentContext/salesAgentContext.cjs +4 -3
  111. package/dist/contexts/salesAgentContext/salesAgentContext.js +4 -3
  112. package/dist/contexts/salesAgentContext/salesAgentService.cjs +3 -2
  113. package/dist/contexts/salesAgentContext/salesAgentService.js +3 -2
  114. package/dist/contexts/searchContext/searchContext.cjs +8 -6
  115. package/dist/contexts/searchContext/searchContext.js +7 -5
  116. package/dist/contexts/sessionStorageContext/sessionStorageContext.cjs +3 -2
  117. package/dist/contexts/sessionStorageContext/sessionStorageContext.js +3 -2
  118. package/dist/contexts/systemSettingsContext/systemSettingsContext.d.ts +2 -2
  119. package/dist/contexts/types.d.cts +1 -1
  120. package/dist/contexts/types.d.ts +1 -1
  121. package/dist/contexts/typesV3.d.cts +1 -1
  122. package/dist/contexts/uiConfigContext/index.cjs +0 -1
  123. package/dist/contexts/uiConfigContext/index.d.cts +2 -2
  124. package/dist/contexts/uiConfigContext/index.d.ts +2 -2
  125. package/dist/contexts/uiConfigContext/index.js +2 -2
  126. package/dist/contexts/uiConfigContext/uiConfigContext.cjs +13 -30
  127. package/dist/contexts/uiConfigContext/uiConfigContext.d.cts +5 -9
  128. package/dist/contexts/uiConfigContext/uiConfigContext.d.ts +5 -9
  129. package/dist/contexts/uiConfigContext/uiConfigContext.js +14 -29
  130. package/dist/contexts/userIdentityContext/userIdentityContext.cjs +7 -7
  131. package/dist/contexts/userIdentityContext/userIdentityContext.js +7 -7
  132. package/dist/contexts/widgetConfigContext/widgetConfigContext.cjs +4 -3
  133. package/dist/contexts/widgetConfigContext/widgetConfigContext.js +4 -3
  134. package/dist/hooks/CustomerSupportHandoff/useCustomerSupportHandoff.cjs +4 -3
  135. package/dist/hooks/CustomerSupportHandoff/useCustomerSupportHandoff.js +4 -3
  136. package/dist/hooks/GrabAndScroll/useGrabAndScroll.d.cts +2 -2
  137. package/dist/hooks/GrabAndScroll/useGrabAndScroll.d.ts +2 -2
  138. package/dist/hooks/ShopifyUrlOperations/useShopifyUrlOperations.d.cts +2 -2
  139. package/dist/hooks/ShopifyUrlOperations/useShopifyUrlOperations.d.ts +2 -2
  140. package/dist/hooks/SystemSettingsContext/useSystemSettingsContext.d.ts +2 -2
  141. package/dist/hooks/WidgetInteraction/useWidgetInteraction.cjs +2 -2
  142. package/dist/hooks/WidgetInteraction/useWidgetInteraction.js +2 -2
  143. package/dist/hooks/WidgetInteraction/utils.cjs +2 -1
  144. package/dist/hooks/WidgetInteraction/utils.js +2 -1
  145. package/dist/hooks/utils.d.cts +1 -1
  146. package/dist/hooks/utils.d.ts +1 -1
  147. package/dist/services/amplitudeService/amplitudeService.cjs +15 -7
  148. package/dist/services/amplitudeService/amplitudeService.d.cts +2 -1
  149. package/dist/services/amplitudeService/amplitudeService.d.ts +2 -1
  150. package/dist/services/amplitudeService/amplitudeService.js +15 -7
  151. package/dist/services/ga4ProjectionService/ga4ProjectionService.cjs +3 -2
  152. package/dist/services/ga4ProjectionService/ga4ProjectionService.js +3 -2
  153. package/dist/services/userIdentityService/userIdentityService.cjs +8 -7
  154. package/dist/services/userIdentityService/userIdentityService.js +8 -7
  155. package/package.json +2 -2
  156. package/src/application/commerce-api.ts +14 -12
  157. package/src/application/logging/logger.ts +33 -8
  158. package/src/application/models/guards/api/isApiFormResponse.ts +9 -7
  159. package/src/application/models/guards/api/isApiFormSubmittedResponseAttributes.ts +6 -4
  160. package/src/application/models/guards/api/isApiOrderResponseAttributes.ts +19 -17
  161. package/src/application/models/guards/api/isApiOrgConfigResults.ts +40 -48
  162. package/src/application/models/guards/api/isApiOrganizationConfig.ts +25 -38
  163. package/src/application/models/guards/api/isApiProductResponseAttributes.ts +12 -10
  164. package/src/application/models/guards/api/isApiResponse.ts +7 -5
  165. package/src/application/models/guards/graphQL/isGraphQLColorsConfig.ts +5 -3
  166. package/src/application/models/validators/validateGraphQLFrontendConfig.ts +15 -13
  167. package/src/application/utils/analyticsUtils.ts +4 -4
  168. package/src/application/utils/nextMessageRequestToApiRequest.ts +2 -0
  169. package/src/atoms/app/variant.ts +3 -1
  170. package/src/atoms/chat/performanceMetrics.ts +3 -1
  171. package/src/atoms/envive/enviveConfig.ts +5 -3
  172. package/src/contexts/amplitudeContext/amplitudeContext.tsx +1 -1
  173. package/src/contexts/enviveConfigContext/__tests__/enviveConfigContext.test.tsx +4 -3
  174. package/src/contexts/enviveConfigContext/enviveConfigContext.tsx +50 -35
  175. package/src/contexts/enviveConfigContext/index.ts +1 -0
  176. package/src/contexts/enviveConfigContext/useEnviveConfig.ts +9 -0
  177. package/src/contexts/enviveContext/WindowChatToggleBinder.tsx +6 -4
  178. package/src/contexts/enviveContext/enviveContext.tsx +109 -50
  179. package/src/contexts/featureFlagServiceContext/featureFlagServiceContext.tsx +11 -12
  180. package/src/contexts/graphqlContext/__tests__/graphqlContext.test.tsx +4 -4
  181. package/src/contexts/graphqlContext/graphqlContext.tsx +8 -6
  182. package/src/contexts/hardcopyContext/hardcopyContext.tsx +9 -7
  183. package/src/contexts/localStorageContext/__tests__/localStorageContext.test.tsx +6 -6
  184. package/src/contexts/localStorageContext/localStorageContext.tsx +4 -2
  185. package/src/contexts/pageContext/__tests__/pageContext.test.tsx +5 -5
  186. package/src/contexts/pageContext/pageContext.tsx +22 -2
  187. package/src/contexts/salesAgentContext/chatAPI.ts +5 -5
  188. package/src/contexts/salesAgentContext/salesAgentContext.tsx +4 -2
  189. package/src/contexts/salesAgentContext/salesAgentService.ts +4 -2
  190. package/src/contexts/searchContext/__tests__/searchContext.test.tsx +15 -12
  191. package/src/contexts/searchContext/searchContext.tsx +6 -4
  192. package/src/contexts/sessionStorageContext/sessionStorageContext.tsx +3 -1
  193. package/src/contexts/uiConfigContext/__tests__/uiConfigContext.test.tsx +7 -32
  194. package/src/contexts/uiConfigContext/uiConfigContext.tsx +17 -29
  195. package/src/contexts/userIdentityContext/__tests__/userIdentityContext.test.tsx +5 -5
  196. package/src/contexts/userIdentityContext/userIdentityContext.tsx +7 -6
  197. package/src/contexts/widgetConfigContext/__tests__/widgetConfigContext.test.tsx +7 -7
  198. package/src/contexts/widgetConfigContext/widgetConfigContext.tsx +4 -2
  199. package/src/hooks/CustomerSupportHandoff/useCustomerSupportHandoff.ts +4 -2
  200. package/src/hooks/Search/__tests__/useSearch.test.tsx +14 -8
  201. package/src/hooks/WidgetInteraction/useWidgetInteraction.ts +3 -2
  202. package/src/hooks/WidgetInteraction/utils.ts +3 -1
  203. package/src/services/amplitudeService/__tests__/amplitudeService.test.ts +3 -3
  204. package/src/services/amplitudeService/amplitudeService.ts +19 -6
  205. package/src/services/ga4ProjectionService/ga4ProjectionService.ts +3 -1
  206. package/src/services/userIdentityService/userIdentityService.ts +8 -8
@@ -15,9 +15,9 @@ import { mapUrlResolverResponseToVariantInfo } from '../mapping';
15
15
  import { PageProvider, usePage } from '../pageContext';
16
16
 
17
17
  // Mock the Logger to avoid console output in tests
18
- vi.spyOn(Logger, 'logInfo').mockImplementation(() => {});
19
- vi.spyOn(Logger, 'logWarn').mockImplementation(() => {});
20
- vi.spyOn(Logger, 'logError').mockImplementation(() => {});
18
+ vi.spyOn(Logger.prototype, 'logInfo').mockImplementation(() => {});
19
+ vi.spyOn(Logger.prototype, 'logWarn').mockImplementation(() => {});
20
+ vi.spyOn(Logger.prototype, 'logError').mockImplementation(() => {});
21
21
 
22
22
  // Mock CommerceApiClient
23
23
  const mockResolveUrl = vi.fn();
@@ -468,7 +468,7 @@ describe('PageProvider', () => {
468
468
 
469
469
  describe('Error Handling', () => {
470
470
  it('should log error and not crash when resolveUrl fails', async () => {
471
- const errorSpy = vi.spyOn(Logger, 'logError');
471
+ const errorSpy = vi.spyOn(Logger.prototype, 'logError');
472
472
  const error = new Error('Network error');
473
473
  mockResolveUrl.mockRejectedValue(error);
474
474
 
@@ -487,7 +487,7 @@ describe('PageProvider', () => {
487
487
  });
488
488
 
489
489
  it('should handle invalid variant type gracefully', async () => {
490
- const errorSpy = vi.spyOn(Logger, 'logError');
490
+ const errorSpy = vi.spyOn(Logger.prototype, 'logError');
491
491
 
492
492
  const invalidResponse = {
493
493
  variant_type: 'invalid_type',
@@ -5,6 +5,7 @@ import {
5
5
  useContext,
6
6
  useEffect,
7
7
  useMemo,
8
+ useRef,
8
9
  useState,
9
10
  } from 'react';
10
11
  import {
@@ -21,6 +22,8 @@ import { VariantTypeEnum } from 'src/application/models';
21
22
  import { mapApiUserEventToUserEvent, mapUrlResolverResponseToVariantInfo } from './mapping';
22
23
  import { PageDetails } from './types';
23
24
 
25
+ const logger = new Logger('pageContext');
26
+
24
27
  const PageContext = createContext<
25
28
  | (PageDetails & {
26
29
  isLoading: boolean;
@@ -33,13 +36,17 @@ const PageContext = createContext<
33
36
  export const PageProvider: React.FC<{
34
37
  children: ReactNode;
35
38
  previewMode: boolean;
36
- }> = ({ children, previewMode = false }) => {
39
+ /** When URL resolver returns `ready: false`, invoked for extension/injection failure reporting. */
40
+ onUrlResolverNotReady?: (error: Error) => Promise<unknown> | void;
41
+ }> = ({ children, previewMode = false, onUrlResolverNotReady }) => {
37
42
  const [pageUrl, setPageUrl] = useState<string | undefined>(undefined);
38
43
  const [urlResolverResponse, setUrlResolverResponse] = useAtom(urlResolverAtom);
39
44
  const [isLoading, setIsLoading] = useState(false);
40
45
  const [variantInfo, setVariantInfo] = useAtom(pageVariantInfoAtom);
41
46
  const [userEvent, setUserEvent] = useAtom(pageUserEventAtom);
42
47
  const [isSupported, setIsSupported] = useState(false);
48
+ const lastNotReadyUrlRef = useRef<string | null>(null);
49
+
43
50
  useEffect(() => {
44
51
  setPageUrl(window.location.href);
45
52
  }, []);
@@ -70,11 +77,13 @@ export const PageProvider: React.FC<{
70
77
  if (response.ready) {
71
78
  const newVariantInfo = mapUrlResolverResponseToVariantInfo(url, response);
72
79
  const newUserEvent = mapApiUserEventToUserEvent(response.user_event ?? undefined);
80
+ logger.logDebug('setting variant info and user event', { newVariantInfo, newUserEvent });
73
81
  setVariantInfo(newVariantInfo);
74
82
  setUserEvent(newUserEvent);
75
83
  } else {
76
84
  setUserEvent(undefined);
77
85
  setVariantInfo(undefined);
86
+ logger.logDebug('set variant info and user event to undefined', { variantInfo, userEvent });
78
87
  }
79
88
  },
80
89
  [setVariantInfo, setUserEvent],
@@ -102,9 +111,19 @@ export const PageProvider: React.FC<{
102
111
  setVariantFromUrlResolver(cleansedUrl, response);
103
112
  setIsLoading(false);
104
113
  setIsSupported(response.ready);
114
+
115
+ if (response.ready) {
116
+ lastNotReadyUrlRef.current = null;
117
+ } else if (onUrlResolverNotReady && lastNotReadyUrlRef.current !== cleansedUrl) {
118
+ lastNotReadyUrlRef.current = cleansedUrl;
119
+ const err = new Error(`URL resolver returned ready=false for ${cleansedUrl}`);
120
+ Promise.resolve(onUrlResolverNotReady(err)).catch(callbackError => {
121
+ logger.logDebug('onUrlResolverNotReady callback failed', callbackError);
122
+ });
123
+ }
105
124
  } catch (e) {
106
125
  setIsLoading(false);
107
- Logger.logError('Failed to resolve page URL', e, { pageUrl });
126
+ logger.logError('Failed to resolve page URL', e, { pageUrl });
108
127
  }
109
128
  };
110
129
  resolvePageUrl();
@@ -116,6 +135,7 @@ export const PageProvider: React.FC<{
116
135
  userEvent,
117
136
  variantInfo,
118
137
  previewMode,
138
+ onUrlResolverNotReady,
119
139
  ]);
120
140
 
121
141
  const setPageUrlStable = useCallback((url: string) => {
@@ -29,6 +29,8 @@ const TRACKED_USER_EVENTS = [
29
29
  UserEventCategory.PdpVisit,
30
30
  UserEventCategory.PlpVisit,
31
31
  ];
32
+
33
+ const logger = new Logger('chatAPI');
32
34
  export interface SalesAgentChatAPI {
33
35
  logPageVisit: ({ pageVisitCategory }: { pageVisitCategory: PageVisitCategory }) => void;
34
36
  logUserEvent: (event: UserEvent) => void;
@@ -88,11 +90,9 @@ export const useSalesAgentChatAPI = (widget?: WidgetInteractionComponent) => {
88
90
  const logUserEvent = useCallback(
89
91
  (event: UserEvent) => {
90
92
  if (!hasParsedVariantInfo) {
91
- Logger.logWarn(
92
- '[envive-ai] hasParsedVariantInfo is false, not logging user event',
93
- undefined,
94
- { event },
95
- );
93
+ logger.logWarn('hasParsedVariantInfo is false, not logging user event', undefined, {
94
+ event,
95
+ });
96
96
  return;
97
97
  }
98
98
  if (TRACKED_USER_EVENTS.includes(event.category)) {
@@ -47,6 +47,8 @@ export interface MockSalesAgentData {
47
47
 
48
48
  const SalesAgentContext = createContext<SalesAgent | undefined>(undefined);
49
49
 
50
+ const logger = new Logger('salesAgentContext');
51
+
50
52
  export interface SalesAgentProviderProps {
51
53
  children: ReactNode;
52
54
  previewMode?: boolean;
@@ -99,7 +101,7 @@ export const SalesAgentProvider: React.FC<SalesAgentProviderProps> = ({
99
101
  // Remove the pending message from the user events
100
102
  requestPayload.userEvents?.map(userEvent => markUserEventsProcessed([userEvent.eventId]));
101
103
  } catch (error) {
102
- Logger.logError('[envive-ai] error sending message', error);
104
+ logger.logError('error sending message', error);
103
105
  }
104
106
  },
105
107
  [getStreamingResponses, setIsInitialized, markUserEventsProcessed],
@@ -207,7 +209,7 @@ export const SalesAgentProvider: React.FC<SalesAgentProviderProps> = ({
207
209
  try {
208
210
  await hydrateMessages();
209
211
  } catch (error) {
210
- Logger.logError('[envive-ai] error hydrating messages', error);
212
+ logger.logError('error hydrating messages', error);
211
213
  } finally {
212
214
  setIsHydrated(true);
213
215
  }
@@ -12,7 +12,7 @@ import {
12
12
  Response,
13
13
  UserEvent,
14
14
  } from 'src/application/models';
15
- import { messageFromQueryEvent, messageFromResponse } from 'src/application/utils';
15
+ import { messageFromResponse } from 'src/application/utils';
16
16
  import { appDetailsAtom } from 'src/atoms/app';
17
17
  import { analyticsContextAtom } from 'src/atoms/app/variant';
18
18
  import {
@@ -33,6 +33,8 @@ import { v4 as uuid } from 'uuid';
33
33
 
34
34
  import { StatusCodeError } from './statusCodeError';
35
35
 
36
+ const logger = new Logger('salesAgentService');
37
+
36
38
  interface SalesAgentService {
37
39
  createResponsePayload: (payload: {
38
40
  userEvents: UserEvent[];
@@ -223,7 +225,7 @@ const processStreamingResponse = async (
223
225
 
224
226
  lastMessage = updateMessageState(message, lastMessage!, setMessages);
225
227
  } catch (error: unknown) {
226
- Logger.logWarn(`[spiffy-ai] Failed to generate responses from stream`, error, {
228
+ logger.logWarn('Failed to generate responses from stream', error, {
227
229
  lastResponse: lastMessage,
228
230
  response,
229
231
  });
@@ -1,10 +1,7 @@
1
1
  import React from 'react';
2
2
  import { act, render, screen, waitFor } from '@testing-library/react';
3
3
  import { Provider, useAtomValue } from 'jotai';
4
- import {
5
- EnviveConfigProvider,
6
- useEnviveConfig,
7
- } from 'src/contexts/enviveConfigContext/enviveConfigContext';
4
+ import { EnviveConfigProvider, useEnviveConfig } from 'src/contexts/enviveConfigContext';
8
5
  import { LocalStorageProvider } from 'src/contexts/localStorageContext/localStorageContext';
9
6
  import { GraphQLProvider } from 'src/contexts/graphqlContext/graphqlContext';
10
7
  import { UserIdentityProvider } from 'src/contexts/userIdentityContext/userIdentityContext';
@@ -79,14 +76,20 @@ vi.mock('src/contexts/enviveConfigContext/enviveConfigContext', async () => {
79
76
  });
80
77
 
81
78
  // Mock Logger
82
- vi.mock('src/application/logging/logger', () => ({
83
- default: {
84
- logInfo: vi.fn(),
85
- logWarn: vi.fn(),
86
- logError: vi.fn(),
87
- logDebug: vi.fn(),
88
- },
89
- }));
79
+ vi.mock('src/application/logging/logger', () => {
80
+ const MockLogger = vi.fn(function MockLogger() {
81
+ return {
82
+ logInfo: vi.fn(),
83
+ logWarn: vi.fn(),
84
+ logError: vi.fn(),
85
+ logDebug: vi.fn(),
86
+ };
87
+ });
88
+
89
+ return {
90
+ default: MockLogger,
91
+ };
92
+ });
90
93
 
91
94
  // Mock the search service adapter
92
95
  vi.mock('src/atoms/search/searchServiceAdapter', () => ({
@@ -9,7 +9,7 @@ import {
9
9
  import { baseUrlAtom } from 'src/atoms/envive/enviveConfig';
10
10
  import Logger from 'src/application/logging/logger';
11
11
  import { useAppDetails } from 'src/hooks/AppDetails/useAppDetails';
12
- import { useEnviveConfig } from 'src/contexts/enviveConfigContext/enviveConfigContext';
12
+ import { useEnviveConfig } from 'src/contexts/enviveConfigContext';
13
13
  import { SessionRestartRequired } from 'src/types/exceptions/sessionExceptions';
14
14
  import { UnsupportedProductException } from 'src/types/exceptions/unsupportedProductExceptions';
15
15
  import { SearchParams, SearchResult } from 'src/application/models/api/search';
@@ -18,6 +18,8 @@ import {
18
18
  setSearchServiceFunction,
19
19
  } from 'src/atoms/search/searchServiceAdapter';
20
20
 
21
+ const logger = new Logger('searchContext');
22
+
21
23
  const transformProductResponses = (products: SearchResult['products']) =>
22
24
  products.map(data => ({
23
25
  id: data.id,
@@ -49,7 +51,7 @@ async function errorResponseBody(error: ResponseError) {
49
51
 
50
52
  async function throwSessionRestartRequiredIf(errorMsg: string, error: unknown) {
51
53
  if (!(error instanceof ResponseError)) {
52
- Logger.logInfo(errorMsg, error);
54
+ logger.logInfo(errorMsg, error);
53
55
  throw error;
54
56
  }
55
57
 
@@ -63,7 +65,7 @@ async function throwSessionRestartRequiredIf(errorMsg: string, error: unknown) {
63
65
  errorResponse?.app_code?.toUpperCase() === 'RESTART_SESSION' ||
64
66
  errorResponse?.sub_code?.toUpperCase() === 'NOT_FOUND'
65
67
  ) {
66
- Logger.logInfo(
68
+ logger.logInfo(
67
69
  'Session does not exist. Re-start session',
68
70
  error,
69
71
  error.response,
@@ -72,7 +74,7 @@ async function throwSessionRestartRequiredIf(errorMsg: string, error: unknown) {
72
74
  throw new SessionRestartRequired();
73
75
  }
74
76
 
75
- Logger.logInfo(errorMsg, error);
77
+ logger.logInfo(errorMsg, error);
76
78
  throw error;
77
79
  }
78
80
 
@@ -9,6 +9,8 @@ interface SessionStorageContextType {
9
9
 
10
10
  const SessionStorageContext = createContext<SessionStorageContextType | null>(null);
11
11
 
12
+ const logger = new Logger('sessionStorageContext');
13
+
12
14
  export const SessionStorageProvider: React.FC<{
13
15
  children: React.ReactNode;
14
16
  }> = ({ children }) => {
@@ -22,7 +24,7 @@ export const SessionStorageProvider: React.FC<{
22
24
 
23
25
  useEffect(() => {
24
26
  if (!isAvailable) {
25
- Logger.logError('sessionStorage is not available', undefined);
27
+ logger.logError('sessionStorage is not available', undefined);
26
28
  }
27
29
  }, [isAvailable]);
28
30
 
@@ -6,13 +6,13 @@ import { SparkleIconColor } from '@envive-ai/react-toolkit-v3/WelcomeMessage';
6
6
  import { ChatHeaderVariant } from '@envive-ai/react-toolkit-v3/ChatHeader';
7
7
  import { Theme } from '@envive-ai/react-toolkit-v3/Tokens';
8
8
  import { FloatingButtonLocation } from '@envive-ai/react-toolkit-v3/FloatingButton';
9
- import { UiConfigProvider, useUiConfig } from '../uiConfigContext';
9
+ import { useUiConfig } from '../uiConfigContext';
10
10
  import { ConfigVersionEnum, OrgUIConfigV3 } from '../../typesV3';
11
11
 
12
12
  // Mock the Logger to avoid console output in tests
13
- vi.spyOn(Logger, 'logInfo').mockImplementation(() => {});
14
- vi.spyOn(Logger, 'logWarn').mockImplementation(() => {});
15
- vi.spyOn(Logger, 'logError').mockImplementation(() => {});
13
+ vi.spyOn(Logger.prototype, 'logInfo').mockImplementation(() => {});
14
+ vi.spyOn(Logger.prototype, 'logWarn').mockImplementation(() => {});
15
+ vi.spyOn(Logger.prototype, 'logError').mockImplementation(() => {});
16
16
 
17
17
  // Mock the useNewOrgConfig hook
18
18
  vi.mock('src/hooks/NewOrgConfig', () => ({
@@ -30,7 +30,7 @@ const MockUiConfigComponent: React.FC = () => {
30
30
  setIsLoadingState(true);
31
31
  try {
32
32
  const config = await getUiConfig();
33
- setUiConfig(config);
33
+ setUiConfig(config ?? null);
34
34
  setError(null);
35
35
  } catch (err: unknown) {
36
36
  setError(err instanceof Error ? err.message : 'Unknown error');
@@ -74,7 +74,7 @@ const TestWrapper: React.FC<{
74
74
  loading: mockNewOrgConfig.loading ?? false,
75
75
  });
76
76
 
77
- return <UiConfigProvider>{children}</UiConfigProvider>;
77
+ return children;
78
78
  };
79
79
 
80
80
  describe('UiConfigProvider', () => {
@@ -129,7 +129,7 @@ describe('UiConfigProvider', () => {
129
129
  });
130
130
 
131
131
  describe('useUiConfig Hook Integration', () => {
132
- it('should provide context through useUiConfig hook', async () => {
132
+ it('should provide ui config data via useUiConfig hook', async () => {
133
133
  const mockUiConfig = createMockUiConfig();
134
134
 
135
135
  render(
@@ -151,31 +151,6 @@ describe('UiConfigProvider', () => {
151
151
 
152
152
  expect(screen.getByTestId('is-loading')).toHaveTextContent('false');
153
153
  });
154
-
155
- it('should throw error when used outside of UiConfigProvider', () => {
156
- const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
157
-
158
- const TestComponent: React.FC = () => {
159
- try {
160
- useUiConfig();
161
- return <div data-testid="no-error">No Error</div>;
162
- } catch (error: unknown) {
163
- return (
164
- <div data-testid="error">
165
- {error instanceof Error ? error.message : 'Unknown error'}
166
- </div>
167
- );
168
- }
169
- };
170
-
171
- render(<TestComponent />);
172
-
173
- expect(screen.getByTestId('error')).toHaveTextContent(
174
- 'useUiConfig must be used within a UiConfigProvider',
175
- );
176
-
177
- consoleSpy.mockRestore();
178
- });
179
154
  });
180
155
 
181
156
  describe('getUiConfig', () => {
@@ -1,44 +1,32 @@
1
- import { ReactNode, createContext, useCallback, useContext, useMemo, useState } from 'react';
2
1
  import Logger from 'src/application/logging/logger';
3
2
  import { useNewOrgConfig } from 'src/hooks/NewOrgConfig';
4
3
  import { OrgUIConfigV3 } from '../typesV3';
5
4
 
6
- interface UiConfigContextType {
7
- getUiConfig: () => Promise<OrgUIConfigV3>;
5
+ const logger = new Logger('uiConfigContext');
6
+ interface UiConfigHookResult {
7
+ getUiConfig: () => Promise<OrgUIConfigV3 | undefined>;
8
8
  isLoading: boolean;
9
9
  }
10
10
 
11
- const UiConfigContext = createContext<UiConfigContextType | undefined>(undefined);
11
+ export const useUiConfig = (): UiConfigHookResult => {
12
+ const { frontendConfig, loading } = useNewOrgConfig();
12
13
 
13
- export const UiConfigProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
14
- const { frontendConfig, loading: isFrontendConfigLoading } = useNewOrgConfig();
15
- const [isLoadingUiConfig, setIsLoadingUiConfig] = useState(false);
14
+ const getUiConfig = async (): Promise<OrgUIConfigV3 | undefined> => {
15
+ logger.logDebug('uiConfigContext | retrieving ui config');
16
16
 
17
- const getUiConfig = useCallback(async (): Promise<OrgUIConfigV3> => {
18
- setIsLoadingUiConfig(true);
19
- Logger.logInfo('uiConfigContext | retrieving ui config');
20
- if (isFrontendConfigLoading) {
21
- setIsLoadingUiConfig(false);
17
+ if (loading) {
18
+ logger.logDebug('uiConfigContext | frontend config still loading');
22
19
  throw new Error('Frontend config is loading');
23
20
  }
24
21
 
25
- const response = frontendConfig?.uiConfigs as OrgUIConfigV3;
26
- Logger.logInfo('uiConfigContext | returning ui config response', response);
27
- setIsLoadingUiConfig(false);
28
- return response;
29
- }, [frontendConfig, isFrontendConfigLoading]);
30
-
31
- const isLoading = isFrontendConfigLoading || isLoadingUiConfig;
32
-
33
- const value = useMemo(() => ({ getUiConfig, isLoading }), [getUiConfig, isLoading]);
22
+ const response = frontendConfig?.uiConfigs as OrgUIConfigV3 | undefined;
23
+ logger.logDebug('uiConfigContext | returning ui config response', response);
34
24
 
35
- return <UiConfigContext.Provider value={value}>{children}</UiConfigContext.Provider>;
36
- };
25
+ return response;
26
+ };
37
27
 
38
- export const useUiConfig = () => {
39
- const context = useContext(UiConfigContext);
40
- if (!context) {
41
- throw new Error('useUiConfig must be used within a UiConfigProvider');
42
- }
43
- return context;
28
+ return {
29
+ getUiConfig,
30
+ isLoading: loading,
31
+ };
44
32
  };
@@ -8,9 +8,9 @@ import { UserIdentityService } from 'src/services/userIdentityService';
8
8
  import { UserIdentityProvider, useUserIdentity } from '../userIdentityContext';
9
9
 
10
10
  // Mock the Logger to avoid console output in tests
11
- vi.spyOn(Logger, 'logInfo').mockImplementation(() => {});
12
- vi.spyOn(Logger, 'logWarn').mockImplementation(() => {});
13
- vi.spyOn(Logger, 'logError').mockImplementation(() => {});
11
+ vi.spyOn(Logger.prototype, 'logInfo').mockImplementation(() => {});
12
+ vi.spyOn(Logger.prototype, 'logWarn').mockImplementation(() => {});
13
+ vi.spyOn(Logger.prototype, 'logError').mockImplementation(() => {});
14
14
 
15
15
  // Mock uuid to have predictable values in tests
16
16
  vi.mock('uuid', () => ({
@@ -262,7 +262,7 @@ describe('UserIdentityProvider', () => {
262
262
  });
263
263
 
264
264
  it('should log info when setting userId override', async () => {
265
- const logSpy = vi.spyOn(Logger, 'logInfo');
265
+ const logSpy = vi.spyOn(Logger.prototype, 'logInfo');
266
266
 
267
267
  const ComponentWithSet: React.FC = () => {
268
268
  const context = useUserIdentity();
@@ -283,7 +283,7 @@ describe('UserIdentityProvider', () => {
283
283
  });
284
284
 
285
285
  it('should log info when clearing userId override', async () => {
286
- const logSpy = vi.spyOn(Logger, 'logInfo');
286
+ const logSpy = vi.spyOn(Logger.prototype, 'logInfo');
287
287
 
288
288
  const ComponentWithClear: React.FC = () => {
289
289
  const context = useUserIdentity();
@@ -13,6 +13,8 @@ import { useSetAtom } from 'jotai';
13
13
  import { userIdAtom } from 'src/atoms/app';
14
14
  import { UserIdentityService } from 'src/services/userIdentityService';
15
15
 
16
+ const logger = new Logger('userIdentityContext');
17
+
16
18
  export interface UserIdentityContextType {
17
19
  getUserIdOrDefault: () => Promise<string>;
18
20
  getUserIdOverrideFromLocalStorage: () => string | undefined;
@@ -53,7 +55,7 @@ export const UserIdentityProvider: React.FC<UserIdentityProviderProps> = ({
53
55
 
54
56
  const setUserIdOverrideInLocalStorage = useCallback(
55
57
  (userId: string): string => {
56
- Logger.logInfo(`setUserIdOverrideInLocalStorage - Setting user_id=${userId}`);
58
+ logger.logInfo(`setUserIdOverrideInLocalStorage - Setting user_id=${userId}`);
57
59
  setItem(USER_ID_OVERRIDE_KEY, userId);
58
60
  // window.dispatchEvent is handled by useLocalStorage now
59
61
  return userId;
@@ -62,7 +64,7 @@ export const UserIdentityProvider: React.FC<UserIdentityProviderProps> = ({
62
64
  );
63
65
 
64
66
  const clearUserIdOverrideInLocalStorage = useCallback(() => {
65
- Logger.logInfo(`clearUserIdOverrideInLocalStorage - Clearing user_id`);
67
+ logger.logInfo(`clearUserIdOverrideInLocalStorage - Clearing user_id`);
66
68
  // LocalStorageService.getLocalStorage()?.removeItem(USER_ID_OVERRIDE_KEY);
67
69
  // window.dispatchEvent is handled by useLocalStorage now
68
70
  setItem(USER_ID_OVERRIDE_KEY, ''); // Set to empty string to clear
@@ -80,15 +82,15 @@ export const UserIdentityProvider: React.FC<UserIdentityProviderProps> = ({
80
82
 
81
83
  if (!enviveUserId) {
82
84
  throw new Error(
83
- '[spiffy-ai] EnviveAnalytics.resolveEnviveUserId() returned null/undefined - this should never happen',
85
+ 'EnviveAnalytics.resolveEnviveUserId() returned null/undefined - this should never happen',
84
86
  );
85
87
  }
86
88
 
87
89
  return enviveUserId;
88
90
  } catch (error: unknown) {
89
- Logger.logError('[spiffy-ai] CRITICAL: Failed to get user ID from EnviveAnalytics', error);
91
+ logger.logError('CRITICAL: Failed to get user ID from EnviveAnalytics', error);
90
92
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
91
- throw new Error(`[spiffy-ai] CRITICAL: User ID resolution failed: ${errorMessage}`);
93
+ throw new Error(`CRITICAL: User ID resolution failed: ${errorMessage}`);
92
94
  }
93
95
  }, [getUserIdOverrideFromLocalStorage]);
94
96
 
@@ -112,7 +114,6 @@ export const UserIdentityProvider: React.FC<UserIdentityProviderProps> = ({
112
114
  if (isReady && !localUserId) {
113
115
  const setUserIdStates = async () => {
114
116
  const newUserId = await getUserIdOrDefault();
115
- console.log('useUserIdentity useEffect - setting localUserId', newUserId);
116
117
  setLocalUserId(newUserId);
117
118
  setUserId(newUserId);
118
119
  };
@@ -7,9 +7,9 @@ import { WidgetConfigProvider, useWidgetConfig } from '../widgetConfigContext';
7
7
  import { WidgetConfigV3, WidgetTypeV3 } from '../../typesV3';
8
8
 
9
9
  // Mock the Logger to avoid console output in tests
10
- vi.spyOn(Logger, 'logInfo').mockImplementation(() => {});
11
- vi.spyOn(Logger, 'logWarn').mockImplementation(() => {});
12
- vi.spyOn(Logger, 'logError').mockImplementation(() => {});
10
+ vi.spyOn(Logger.prototype, 'logInfo').mockImplementation(() => {});
11
+ vi.spyOn(Logger.prototype, 'logWarn').mockImplementation(() => {});
12
+ vi.spyOn(Logger.prototype, 'logError').mockImplementation(() => {});
13
13
 
14
14
  // Mock the useNewOrgConfig hook
15
15
  vi.mock('src/hooks/NewOrgConfig', () => ({
@@ -394,7 +394,7 @@ describe('WidgetConfigProvider', () => {
394
394
 
395
395
  describe('Logger Integration', () => {
396
396
  it('should log info when retrieving widget config', async () => {
397
- const logInfoSpy = vi.spyOn(Logger, 'logInfo');
397
+ const logDebugSpy = vi.spyOn(Logger.prototype, 'logDebug');
398
398
  const mockWidgetConfig: WidgetConfigV3 = {
399
399
  widgetConfigId: 'test-config-id',
400
400
  type: WidgetTypeV3.PromptCarouselV3,
@@ -425,12 +425,12 @@ describe('WidgetConfigProvider', () => {
425
425
  });
426
426
 
427
427
  await waitFor(() => {
428
- expect(logInfoSpy).toHaveBeenCalledWith('widgetConfigContext | retrieving widget config', {
428
+ expect(logDebugSpy).toHaveBeenCalledWith('retrieving widget config', {
429
429
  widgetConfigId: 'test-config-id',
430
430
  widgetType: WidgetTypeV3.PromptCarouselV3,
431
431
  });
432
- expect(logInfoSpy).toHaveBeenCalledWith(
433
- 'widgetConfigContext | returning widget config response',
432
+ expect(logDebugSpy).toHaveBeenCalledWith(
433
+ 'returning widget config response',
434
434
  mockWidgetConfig,
435
435
  );
436
436
  });
@@ -3,6 +3,8 @@ import Logger from 'src/application/logging/logger';
3
3
  import { useNewOrgConfig } from 'src/hooks/NewOrgConfig';
4
4
  import { WidgetConfigV3, WidgetTypeV3 } from '../typesV3';
5
5
 
6
+ const logger = new Logger('widgetConfigContext');
7
+
6
8
  interface WidgetConfigRequest {
7
9
  widgetType: WidgetTypeV3;
8
10
  widgetConfigId: string;
@@ -22,7 +24,7 @@ export const WidgetConfigProvider: React.FC<{ children: ReactNode }> = ({ childr
22
24
  const getWidgetConfig = useCallback(
23
25
  async (request: WidgetConfigRequest): Promise<WidgetConfigV3> => {
24
26
  setIsLoadingWidgetConfig(true);
25
- Logger.logInfo('widgetConfigContext | retrieving widget config', {
27
+ logger.logDebug('retrieving widget config', {
26
28
  widgetConfigId: request.widgetConfigId,
27
29
  widgetType: request.widgetType,
28
30
  });
@@ -33,7 +35,7 @@ export const WidgetConfigProvider: React.FC<{ children: ReactNode }> = ({ childr
33
35
 
34
36
  const { widgetConfigId, widgetType } = request;
35
37
  const response = frontendConfig?.widgetConfigs?.[widgetConfigId];
36
- Logger.logInfo('widgetConfigContext | returning widget config response', response);
38
+ logger.logDebug('returning widget config response', response);
37
39
 
38
40
  if (widgetConfigId === 'fake-widget-config-id') {
39
41
  return {} as WidgetConfigV3;
@@ -1,6 +1,8 @@
1
1
  import Logger from 'src/application/logging/logger';
2
2
  import { useCallback } from 'react';
3
3
 
4
+ const logger = new Logger('useCustomerSupportHandoff');
5
+
4
6
  /**
5
7
  * Hook to call the `click` method of the merchant's customer support chat widget.
6
8
  *
@@ -17,14 +19,14 @@ export const useCustomerSupportHandoff = (onSwitchToAgent?: () => void) => {
17
19
  const kustomerElement = document.getElementById('kustomer-ui-sdk-iframe');
18
20
 
19
21
  if (kustomerElement == null || !(kustomerElement instanceof HTMLIFrameElement)) {
20
- Logger.logError('[spiffy-ai] Kustomer iFrame element not found', undefined);
22
+ logger.logError('Kustomer iFrame element not found', undefined);
21
23
  return;
22
24
  }
23
25
 
24
26
  const kustomerButton = kustomerElement.contentWindow?.document?.getElementById('rootChatIcon');
25
27
 
26
28
  if (kustomerButton == null) {
27
- Logger.logError('[spiffy-ai] Kustomer button not found', undefined);
29
+ logger.logError('Kustomer button not found', undefined);
28
30
  return;
29
31
  }
30
32
 
@@ -135,14 +135,20 @@ vi.mock('@spiffy-ai/commerce-api-client', () => ({
135
135
  }));
136
136
 
137
137
  // Mock Logger
138
- vi.mock('src/application/logging/logger', () => ({
139
- default: {
140
- logInfo: vi.fn(),
141
- logWarn: vi.fn(),
142
- logError: vi.fn(),
143
- logDebug: vi.fn(),
144
- },
145
- }));
138
+ vi.mock('src/application/logging/logger', () => {
139
+ const MockLogger = vi.fn(function MockLogger() {
140
+ return {
141
+ logInfo: vi.fn(),
142
+ logWarn: vi.fn(),
143
+ logError: vi.fn(),
144
+ logDebug: vi.fn(),
145
+ };
146
+ });
147
+
148
+ return {
149
+ default: MockLogger,
150
+ };
151
+ });
146
152
 
147
153
  // Mock search service adapter
148
154
  vi.mock('src/atoms/search/searchServiceAdapter', () => ({