@envive-ai/react-hooks 0.3.5 → 0.3.7
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/application/models/api/userEvent.d.cts +4 -2
- package/dist/application/models/api/userEvent.d.ts +4 -2
- package/dist/application/models/graphql/queries/getColorsAndFrontendQuery.cjs +18 -0
- package/dist/application/models/graphql/queries/getColorsAndFrontendQuery.js +18 -0
- package/dist/application/models/guards/api/isApiQueryTypedEventAttributes.cjs +2 -2
- package/dist/application/models/guards/api/isApiQueryTypedEventAttributes.js +2 -2
- package/dist/atoms/app/index.d.ts +1 -1
- package/dist/atoms/app/variant.d.cts +6 -6
- package/dist/atoms/app/variant.d.ts +6 -6
- package/dist/atoms/chat/chatState.d.cts +17 -17
- package/dist/atoms/chat/chatState.d.ts +18 -18
- package/dist/atoms/chat/form.d.cts +2 -2
- package/dist/atoms/chat/form.d.ts +2 -2
- package/dist/atoms/chat/index.cjs +1 -3
- package/dist/atoms/chat/index.d.cts +4 -5
- package/dist/atoms/chat/index.d.ts +4 -5
- package/dist/atoms/chat/index.js +2 -3
- package/dist/atoms/chat/lastMessage.d.cts +2 -2
- package/dist/atoms/chat/lastMessage.d.ts +2 -2
- package/dist/atoms/chat/performanceMetrics.d.cts +6 -6
- package/dist/atoms/chat/performanceMetrics.d.ts +6 -6
- package/dist/atoms/chat/renderedWidgetRefs.d.cts +2 -2
- package/dist/atoms/chat/renderedWidgetRefs.d.ts +2 -2
- package/dist/atoms/chat/suggestions.d.cts +2 -2
- package/dist/atoms/chat/suggestions.d.ts +2 -2
- package/dist/atoms/globalSearch/globalSearch.d.cts +5 -5
- package/dist/atoms/globalSearch/globalSearch.d.ts +5 -5
- package/dist/atoms/org/customerService.d.cts +6 -6
- package/dist/atoms/org/customerService.d.ts +6 -6
- package/dist/atoms/org/graphqlConfig.d.cts +4 -4
- package/dist/atoms/org/graphqlConfig.d.ts +4 -4
- package/dist/atoms/org/newOrgConfigAtom.d.cts +2 -2
- package/dist/atoms/org/newOrgConfigAtom.d.ts +2 -2
- package/dist/atoms/org/orgAnalyticsConfig.d.cts +5 -5
- package/dist/atoms/org/orgAnalyticsConfig.d.ts +5 -5
- package/dist/atoms/search/chatSearch.d.ts +17 -17
- package/dist/atoms/search/searchAPI.d.ts +13 -13
- package/dist/atoms/search/types.d.ts +1 -1
- package/dist/atoms/search/utils.d.ts +1 -1
- package/dist/atoms/widget/chatPreviewLoading.d.cts +2 -2
- package/dist/atoms/widget/chatPreviewLoading.d.ts +2 -2
- package/dist/contexts/enviveContext/enviveContext.cjs +20 -17
- package/dist/contexts/enviveContext/enviveContext.d.cts +3 -1
- package/dist/contexts/enviveContext/enviveContext.d.ts +3 -1
- package/dist/contexts/enviveContext/enviveContext.js +20 -17
- package/dist/contexts/enviveContext/types.d.ts +1 -1
- package/dist/contexts/graphqlContext/graphqlContext.cjs +40 -26
- package/dist/contexts/graphqlContext/graphqlContext.js +41 -26
- package/dist/contexts/graphqlContext/mockV3Config.cjs +47 -45
- package/dist/contexts/graphqlContext/mockV3Config.js +5 -3
- package/dist/contexts/newOrgConfigContext/newOrgConfigContext.cjs +10 -9
- package/dist/contexts/newOrgConfigContext/newOrgConfigContext.d.cts +2 -1
- package/dist/contexts/newOrgConfigContext/newOrgConfigContext.d.ts +2 -1
- package/dist/contexts/newOrgConfigContext/newOrgConfigContext.js +10 -9
- package/dist/contexts/salesAgentContext/chatAPI.cjs +26 -5
- package/dist/contexts/salesAgentContext/chatAPI.d.cts +7 -3
- package/dist/contexts/salesAgentContext/chatAPI.d.ts +7 -3
- package/dist/contexts/salesAgentContext/chatAPI.js +26 -5
- package/dist/contexts/salesAgentContext/salesAgentContext.cjs +4 -7
- package/dist/contexts/salesAgentContext/salesAgentContext.js +4 -7
- package/dist/contexts/salesAgentContext/salesAgentService.cjs +42 -17
- package/dist/contexts/salesAgentContext/salesAgentService.js +42 -17
- package/dist/contexts/types.d.cts +1 -1
- package/dist/contexts/types.d.ts +1 -1
- package/dist/contexts/typesV3.cjs +1 -1
- package/dist/contexts/typesV3.d.cts +4 -4
- package/dist/contexts/typesV3.d.ts +4 -4
- package/dist/contexts/typesV3.js +1 -1
- package/dist/contexts/userIdentityContext/userIdentityContext.cjs +1 -2
- package/dist/contexts/userIdentityContext/userIdentityContext.js +1 -2
- package/dist/hooks/GrabAndScroll/useGrabAndScroll.d.cts +2 -2
- package/dist/hooks/GraphQLConfig/useGraphQLConfig.cjs +1 -2
- package/dist/hooks/GraphQLConfig/useGraphQLConfig.js +1 -2
- package/dist/hooks/Search/useSearch.cjs +1 -1
- package/dist/hooks/Search/useSearch.js +1 -1
- package/dist/hooks/Search/useSearchInput.cjs +1 -1
- package/dist/hooks/Search/useSearchInput.js +1 -1
- package/dist/hooks/utils.d.cts +1 -1
- package/dist/packages/components-v3/dist/ChatHeader/hooks/useGetCloseButtonProperties.cjs +1 -1
- package/dist/packages/components-v3/dist/ChatHeader/hooks/useGetCloseButtonProperties.js +1 -1
- package/dist/packages/components-v3/dist/Container/Container.cjs +1 -1
- package/dist/packages/components-v3/dist/Container/Container.js +1 -1
- package/dist/packages/components-v3/dist/FloatingButton/FloatingButton.cjs +2 -0
- package/dist/packages/components-v3/dist/FloatingButton/FloatingButton.js +4 -0
- package/dist/packages/components-v3/dist/FloatingButton/components/Button.cjs +9 -0
- package/dist/packages/components-v3/dist/FloatingButton/components/Button.js +10 -0
- package/dist/packages/components-v3/dist/FloatingButton/components/Container.cjs +9 -0
- package/dist/packages/components-v3/dist/FloatingButton/components/Container.js +10 -0
- package/dist/packages/components-v3/dist/FloatingButton/components/Icon.cjs +3 -0
- package/dist/packages/components-v3/dist/FloatingButton/components/Icon.js +5 -0
- package/dist/packages/components-v3/dist/FloatingButton/components/Wrapper.cjs +9 -0
- package/dist/packages/components-v3/dist/FloatingButton/components/Wrapper.js +10 -0
- package/dist/packages/components-v3/dist/FloatingButton/components/index.cjs +4 -0
- package/dist/packages/components-v3/dist/FloatingButton/components/index.js +6 -0
- package/dist/packages/components-v3/dist/FloatingButton/index.cjs +2 -0
- package/dist/packages/components-v3/dist/FloatingButton/index.js +4 -0
- package/dist/packages/components-v3/dist/FloatingButton/types/types.cjs +17 -0
- package/dist/packages/components-v3/dist/FloatingButton/types/types.js +16 -0
- package/dist/packages/components-v3/dist/ImageGallery/components/Layout.cjs +3 -3
- package/dist/packages/components-v3/dist/ImageGallery/components/Layout.js +3 -3
- package/dist/packages/components-v3/dist/WelcomeMessage/components/Container.cjs +1 -1
- package/dist/packages/components-v3/dist/WelcomeMessage/components/Container.js +1 -1
- package/dist/packages/components-v3/dist/WelcomeMessage/components/SparkleIcon.cjs +1 -1
- package/dist/packages/components-v3/dist/WelcomeMessage/components/SparkleIcon.js +1 -1
- package/dist/packages/components-v3/dist/utils/CustomIcon.cjs +2 -0
- package/dist/packages/components-v3/dist/utils/CustomIcon.js +4 -0
- package/dist/services/amplitudeService/amplitudeService.cjs +5 -2
- package/dist/services/amplitudeService/amplitudeService.js +5 -2
- package/package.json +1 -5
- package/src/application/models/api/userEvent.ts +3 -1
- package/src/application/models/graphql/queries/getColorsAndFrontendQuery.ts +13 -0
- package/src/application/models/guards/api/isApiQueryTypedEventAttributes.ts +6 -1
- package/src/atoms/chat/index.ts +0 -1
- package/src/contexts/enviveContext/enviveContext.tsx +4 -2
- package/src/contexts/graphqlContext/graphqlContext.tsx +32 -41
- package/src/contexts/graphqlContext/mockV3Config.ts +3 -2
- package/src/contexts/newOrgConfigContext/__tests__/newOrgConfigContext.test.tsx +234 -0
- package/src/contexts/newOrgConfigContext/newOrgConfigContext.tsx +14 -8
- package/src/contexts/salesAgentContext/chatAPI.ts +21 -4
- package/src/contexts/salesAgentContext/salesAgentContext.tsx +3 -7
- package/src/contexts/salesAgentContext/salesAgentService.ts +36 -11
- package/src/contexts/typesV3.ts +3 -4
- package/src/contexts/uiConfigContext/__tests__/uiConfigContext.test.tsx +3 -2
- package/src/contexts/userIdentityContext/userIdentityContext.tsx +0 -1
- package/src/hooks/GraphQLConfig/useGraphQLConfig.ts +0 -1
- package/src/services/amplitudeService/__tests__/amplitudeService.test.ts +0 -15
- package/src/services/amplitudeService/amplitudeService.ts +2 -1
- package/dist/atoms/chat/replies.cjs +0 -46
- package/dist/atoms/chat/replies.d.cts +0 -14
- package/dist/atoms/chat/replies.d.ts +0 -14
- package/dist/atoms/chat/replies.js +0 -45
- package/src/atoms/chat/replies.ts +0 -56
|
@@ -457,4 +457,238 @@ describe('NewOrgConfigProvider', () => {
|
|
|
457
457
|
});
|
|
458
458
|
});
|
|
459
459
|
});
|
|
460
|
+
describe('Override Config', () => {
|
|
461
|
+
it('should use overrideConfig when provided instead of fetched config', async () => {
|
|
462
|
+
const fetchedData = {
|
|
463
|
+
colorsConfig: { accentPrimary: '#FF0000' },
|
|
464
|
+
frontendConfig: { merchantOverrideCss: 'fetched-css' },
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
const overrideData = {
|
|
468
|
+
colorsConfig: { accentPrimary: '#0000FF' },
|
|
469
|
+
frontendConfig: { merchantOverrideCss: 'override-css' },
|
|
470
|
+
orgPageConfig: { pageVariants: [] },
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
// Updated TestWrapper to accept overrideConfig
|
|
474
|
+
const TestWrapperWithOverride: React.FC<{
|
|
475
|
+
children: React.ReactNode;
|
|
476
|
+
orgShortName?: string;
|
|
477
|
+
mockHookData?: any;
|
|
478
|
+
overrideConfig?: any;
|
|
479
|
+
}> = ({ children, orgShortName, mockHookData = {}, overrideConfig }) => {
|
|
480
|
+
const mockUseUIConfig = useUIConfig as any;
|
|
481
|
+
mockUseUIConfig.mockReturnValue({
|
|
482
|
+
data: mockHookData.data || {},
|
|
483
|
+
loading: mockHookData.loading ?? false,
|
|
484
|
+
error: mockHookData.error || null,
|
|
485
|
+
refetch: vi.fn(),
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
return (
|
|
489
|
+
<Provider>
|
|
490
|
+
<EnviveConfigProvider
|
|
491
|
+
identifyingPrefix="test"
|
|
492
|
+
orgShortName={orgShortName}
|
|
493
|
+
baseUrl="https://test-api.example.com"
|
|
494
|
+
orgLevelApiKey="test-key"
|
|
495
|
+
>
|
|
496
|
+
<LocalStorageProvider>
|
|
497
|
+
<GraphQLProvider>
|
|
498
|
+
<NewOrgConfigProvider overrideConfig={overrideConfig}>
|
|
499
|
+
{children}
|
|
500
|
+
</NewOrgConfigProvider>
|
|
501
|
+
</GraphQLProvider>
|
|
502
|
+
</LocalStorageProvider>
|
|
503
|
+
</EnviveConfigProvider>
|
|
504
|
+
</Provider>
|
|
505
|
+
);
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
const ConfigChecker: React.FC = () => {
|
|
509
|
+
const config = useNewOrgConfigContext();
|
|
510
|
+
return (
|
|
511
|
+
<div data-testid="config-checker">
|
|
512
|
+
<div data-testid="accent-color">{config.colorsConfig?.accentPrimary || 'none'}</div>
|
|
513
|
+
<div data-testid="css-value">
|
|
514
|
+
{config.frontendConfig?.merchantOverrideCss || 'none'}
|
|
515
|
+
</div>
|
|
516
|
+
</div>
|
|
517
|
+
);
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
render(
|
|
521
|
+
<TestWrapperWithOverride
|
|
522
|
+
orgShortName="test-org"
|
|
523
|
+
mockHookData={{ data: fetchedData, loading: false }}
|
|
524
|
+
overrideConfig={overrideData}
|
|
525
|
+
>
|
|
526
|
+
<ConfigChecker />
|
|
527
|
+
</TestWrapperWithOverride>,
|
|
528
|
+
);
|
|
529
|
+
|
|
530
|
+
await waitFor(() => {
|
|
531
|
+
expect(screen.getByTestId('accent-color')).toHaveTextContent('#0000FF');
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
// Should use override config, not fetched config
|
|
535
|
+
expect(screen.getByTestId('css-value')).toHaveTextContent('override-css');
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
it('should fall back to fetched config when overrideConfig is not provided', async () => {
|
|
539
|
+
const fetchedData = {
|
|
540
|
+
colorsConfig: { accentPrimary: '#FF0000' },
|
|
541
|
+
frontendConfig: { merchantOverrideCss: 'fetched-css' },
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
const ConfigChecker: React.FC = () => {
|
|
545
|
+
const config = useNewOrgConfigContext();
|
|
546
|
+
return (
|
|
547
|
+
<div data-testid="config-checker">
|
|
548
|
+
<div data-testid="accent-color">{config.colorsConfig?.accentPrimary || 'none'}</div>
|
|
549
|
+
<div data-testid="css-value">
|
|
550
|
+
{config.frontendConfig?.merchantOverrideCss || 'none'}
|
|
551
|
+
</div>
|
|
552
|
+
</div>
|
|
553
|
+
);
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
render(
|
|
557
|
+
<TestWrapper
|
|
558
|
+
orgShortName="test-org"
|
|
559
|
+
mockHookData={{ data: fetchedData, loading: false }}
|
|
560
|
+
>
|
|
561
|
+
<ConfigChecker />
|
|
562
|
+
</TestWrapper>,
|
|
563
|
+
);
|
|
564
|
+
|
|
565
|
+
await waitFor(() => {
|
|
566
|
+
expect(screen.getByTestId('accent-color')).toHaveTextContent('#FF0000');
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
expect(screen.getByTestId('css-value')).toHaveTextContent('fetched-css');
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
it('should set newOrgConfigAtom with overrideConfig when provided', async () => {
|
|
573
|
+
const fetchedData = {
|
|
574
|
+
colorsConfig: { accentPrimary: '#FF0000' },
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
const overrideData = {
|
|
578
|
+
colorsConfig: { accentPrimary: '#00FF00' },
|
|
579
|
+
frontendConfig: { merchantOverrideCss: 'override-css' },
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
const TestWrapperWithOverride: React.FC<{
|
|
583
|
+
children: React.ReactNode;
|
|
584
|
+
orgShortName?: string;
|
|
585
|
+
mockHookData?: any;
|
|
586
|
+
overrideConfig?: any;
|
|
587
|
+
}> = ({ children, orgShortName, mockHookData = {}, overrideConfig }) => {
|
|
588
|
+
const mockUseUIConfig = useUIConfig as any;
|
|
589
|
+
mockUseUIConfig.mockReturnValue({
|
|
590
|
+
data: mockHookData.data || {},
|
|
591
|
+
loading: mockHookData.loading ?? false,
|
|
592
|
+
error: mockHookData.error || null,
|
|
593
|
+
refetch: vi.fn(),
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
return (
|
|
597
|
+
<Provider>
|
|
598
|
+
<EnviveConfigProvider
|
|
599
|
+
identifyingPrefix="test"
|
|
600
|
+
orgShortName={orgShortName}
|
|
601
|
+
baseUrl="https://test-api.example.com"
|
|
602
|
+
orgLevelApiKey="test-key"
|
|
603
|
+
>
|
|
604
|
+
<LocalStorageProvider>
|
|
605
|
+
<GraphQLProvider>
|
|
606
|
+
<NewOrgConfigProvider overrideConfig={overrideConfig}>
|
|
607
|
+
{children}
|
|
608
|
+
</NewOrgConfigProvider>
|
|
609
|
+
</GraphQLProvider>
|
|
610
|
+
</LocalStorageProvider>
|
|
611
|
+
</EnviveConfigProvider>
|
|
612
|
+
</Provider>
|
|
613
|
+
);
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
render(
|
|
617
|
+
<TestWrapperWithOverride
|
|
618
|
+
orgShortName="test-org"
|
|
619
|
+
mockHookData={{ data: fetchedData, loading: false }}
|
|
620
|
+
overrideConfig={overrideData}
|
|
621
|
+
>
|
|
622
|
+
<AtomReaderComponent />
|
|
623
|
+
</TestWrapperWithOverride>,
|
|
624
|
+
);
|
|
625
|
+
|
|
626
|
+
await waitFor(() => {
|
|
627
|
+
expect(screen.getByTestId('atom-has-config')).toHaveTextContent('has-config');
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
// Atom should have the override config values
|
|
631
|
+
expect(screen.getByTestId('atom-has-colors')).toHaveTextContent('true');
|
|
632
|
+
expect(screen.getByTestId('atom-has-frontend')).toHaveTextContent('true');
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
it('should handle undefined overrideConfig gracefully', async () => {
|
|
636
|
+
const fetchedData = {
|
|
637
|
+
colorsConfig: { accentPrimary: '#FF0000' },
|
|
638
|
+
frontendConfig: { merchantOverrideCss: 'fetched-css' },
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
const TestWrapperWithOverride: React.FC<{
|
|
642
|
+
children: React.ReactNode;
|
|
643
|
+
orgShortName?: string;
|
|
644
|
+
mockHookData?: any;
|
|
645
|
+
overrideConfig?: any;
|
|
646
|
+
}> = ({ children, orgShortName, mockHookData = {}, overrideConfig }) => {
|
|
647
|
+
const mockUseUIConfig = useUIConfig as any;
|
|
648
|
+
mockUseUIConfig.mockReturnValue({
|
|
649
|
+
data: mockHookData.data || {},
|
|
650
|
+
loading: mockHookData.loading ?? false,
|
|
651
|
+
error: mockHookData.error || null,
|
|
652
|
+
refetch: vi.fn(),
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
return (
|
|
656
|
+
<Provider>
|
|
657
|
+
<EnviveConfigProvider
|
|
658
|
+
identifyingPrefix="test"
|
|
659
|
+
orgShortName={orgShortName}
|
|
660
|
+
baseUrl="https://test-api.example.com"
|
|
661
|
+
orgLevelApiKey="test-key"
|
|
662
|
+
>
|
|
663
|
+
<LocalStorageProvider>
|
|
664
|
+
<GraphQLProvider>
|
|
665
|
+
<NewOrgConfigProvider overrideConfig={overrideConfig}>
|
|
666
|
+
{children}
|
|
667
|
+
</NewOrgConfigProvider>
|
|
668
|
+
</GraphQLProvider>
|
|
669
|
+
</LocalStorageProvider>
|
|
670
|
+
</EnviveConfigProvider>
|
|
671
|
+
</Provider>
|
|
672
|
+
);
|
|
673
|
+
};
|
|
674
|
+
|
|
675
|
+
render(
|
|
676
|
+
<TestWrapperWithOverride
|
|
677
|
+
orgShortName="test-org"
|
|
678
|
+
mockHookData={{ data: fetchedData, loading: false }}
|
|
679
|
+
overrideConfig={undefined}
|
|
680
|
+
>
|
|
681
|
+
<MockNewOrgConfigComponent />
|
|
682
|
+
</TestWrapperWithOverride>,
|
|
683
|
+
);
|
|
684
|
+
|
|
685
|
+
await waitFor(() => {
|
|
686
|
+
expect(screen.getByTestId('loading')).toHaveTextContent('false');
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
// Should fall back to fetched data
|
|
690
|
+
expect(screen.getByTestId('has-colors-config')).toHaveTextContent('true');
|
|
691
|
+
expect(screen.getByTestId('has-frontend-config')).toHaveTextContent('true');
|
|
692
|
+
});
|
|
693
|
+
});
|
|
460
694
|
});
|
|
@@ -14,26 +14,32 @@ const NewOrgConfigContext = createContext<NewOrgConfigContextType | undefined>(u
|
|
|
14
14
|
|
|
15
15
|
interface NewOrgConfigProviderProps {
|
|
16
16
|
children: ReactNode;
|
|
17
|
+
overrideConfig?: GraphQlConfigValues;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
export const NewOrgConfigProvider: React.FC<NewOrgConfigProviderProps> = ({
|
|
20
|
+
export const NewOrgConfigProvider: React.FC<NewOrgConfigProviderProps> = ({
|
|
21
|
+
children,
|
|
22
|
+
overrideConfig,
|
|
23
|
+
}) => {
|
|
20
24
|
const orgShortName = useAtomValue(orgShortNameAtom);
|
|
21
25
|
const setNewOrgConfig = useSetAtom(newOrgConfigAtom);
|
|
22
26
|
|
|
23
|
-
const { data:
|
|
24
|
-
console.log('NewOrgConfigProvider: newConfig', newConfig, loading, error);
|
|
27
|
+
const { data: fetchedConfig, loading, error } = useUIConfig();
|
|
25
28
|
|
|
26
29
|
const contextValue = useMemo(() => {
|
|
27
30
|
if (!orgShortName || loading) {
|
|
28
|
-
return { ...
|
|
31
|
+
return { ...fetchedConfig, loading: true, error: null };
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
if (error) {
|
|
32
|
-
return { ...
|
|
35
|
+
return { ...fetchedConfig, loading: false, error };
|
|
33
36
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
|
|
38
|
+
const activeConfig = overrideConfig || fetchedConfig;
|
|
39
|
+
|
|
40
|
+
setNewOrgConfig(activeConfig);
|
|
41
|
+
return { ...activeConfig, loading: false, error: null };
|
|
42
|
+
}, [orgShortName, loading, error, setNewOrgConfig, fetchedConfig, overrideConfig]);
|
|
37
43
|
|
|
38
44
|
return (
|
|
39
45
|
<NewOrgConfigContext.Provider value={contextValue}>{children}</NewOrgConfigContext.Provider>
|
|
@@ -6,12 +6,13 @@ import { useSetAtom } from 'jotai';
|
|
|
6
6
|
import { useCallback } from 'react';
|
|
7
7
|
import { v4 as uuid } from 'uuid';
|
|
8
8
|
import { queueUserEventAtom } from 'src/atoms/chat/messageQueue';
|
|
9
|
+
import { SpiffyMetricsEventName, useAmplitude } from '../amplitudeContext';
|
|
9
10
|
|
|
10
11
|
export interface SalesAgentChatAPI {
|
|
11
12
|
logPageVisit: ({ pageVisitCategory }: { pageVisitCategory: PageVisitCategory }) => void;
|
|
12
13
|
logUserEvent: (event: UserEvent) => void;
|
|
13
14
|
onSuggestionClicked: (suggestion: Suggestion) => void;
|
|
14
|
-
onTypedMessageSubmitted: ({ query }: { query: string }) => void;
|
|
15
|
+
onTypedMessageSubmitted: ({ query, userTyped }: { query: string; userTyped: boolean }) => void;
|
|
15
16
|
onFormResponseSubmitted: (formResponse: any) => void; // TODO: Figure out the right type
|
|
16
17
|
}
|
|
17
18
|
|
|
@@ -19,6 +20,7 @@ export const useSalesAgentChatAPI = () => {
|
|
|
19
20
|
// TODO: Each of these functions will trigger both the necessary amplitude events and initiate the
|
|
20
21
|
// necessary actions to trigger the backend API
|
|
21
22
|
const queueUserEvent = useSetAtom(queueUserEventAtom);
|
|
23
|
+
const { trackEvent } = useAmplitude();
|
|
22
24
|
|
|
23
25
|
const logPageVisit = useCallback(
|
|
24
26
|
({ pageVisitCategory }: { pageVisitCategory: PageVisitCategory }) => {
|
|
@@ -44,6 +46,13 @@ export const useSalesAgentChatAPI = () => {
|
|
|
44
46
|
);
|
|
45
47
|
const onSuggestionClicked = useCallback(
|
|
46
48
|
(suggestion: Suggestion) => {
|
|
49
|
+
trackEvent({
|
|
50
|
+
eventName: SpiffyMetricsEventName.ChatSuggestionClicked,
|
|
51
|
+
eventProps: {
|
|
52
|
+
suggestionId: suggestion.id,
|
|
53
|
+
content: suggestion.content,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
47
56
|
const event: UserEvent = {
|
|
48
57
|
eventId: uuid(),
|
|
49
58
|
category: UserEventCategory.SuggestionClicked,
|
|
@@ -55,21 +64,29 @@ export const useSalesAgentChatAPI = () => {
|
|
|
55
64
|
};
|
|
56
65
|
queueUserEvent(event);
|
|
57
66
|
},
|
|
58
|
-
[queueUserEvent],
|
|
67
|
+
[queueUserEvent, trackEvent],
|
|
59
68
|
);
|
|
60
69
|
const onTypedMessageSubmitted = useCallback(
|
|
61
|
-
({ query }: { query: string }) => {
|
|
70
|
+
({ query, userTyped }: { query: string; userTyped: boolean }) => {
|
|
71
|
+
trackEvent({
|
|
72
|
+
eventName: SpiffyMetricsEventName.ChatUserMessageInput,
|
|
73
|
+
eventProps: {
|
|
74
|
+
query,
|
|
75
|
+
userTyped,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
62
78
|
const event: UserEvent = {
|
|
63
79
|
eventId: uuid(),
|
|
64
80
|
category: UserEventCategory.QueryTyped,
|
|
65
81
|
createdAt: new Date().toISOString(),
|
|
66
82
|
attributes: {
|
|
67
83
|
query,
|
|
84
|
+
userTyped,
|
|
68
85
|
},
|
|
69
86
|
};
|
|
70
87
|
queueUserEvent(event);
|
|
71
88
|
},
|
|
72
|
-
[queueUserEvent],
|
|
89
|
+
[queueUserEvent, trackEvent],
|
|
73
90
|
);
|
|
74
91
|
const onFormResponseSubmitted = useCallback(() => {
|
|
75
92
|
// TODO: Implement the form response submitted
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
|
|
2
2
|
import { ReactNode, createContext, useCallback, useEffect, useMemo } from 'react';
|
|
3
3
|
import { UserEventCategory } from '@spiffy-ai/commerce-api-client';
|
|
4
|
+
import Logger from 'src/application/logging/logger';
|
|
4
5
|
import { Message, MessageRole, MessageType } from 'src/application/models/message';
|
|
5
6
|
import { NextMessageRequest, Suggestion, UserEvent } from 'src/application/models';
|
|
6
7
|
import {
|
|
@@ -55,27 +56,22 @@ export const SalesAgentProvider: React.FC<{ children: ReactNode }> = ({ children
|
|
|
55
56
|
const sendMessagesToBackend = useCallback(
|
|
56
57
|
async (requestPayload: NextMessageRequest) => {
|
|
57
58
|
try {
|
|
58
|
-
|
|
59
|
+
await getStreamingResponses(requestPayload);
|
|
59
60
|
setIsInitialized(true);
|
|
60
61
|
|
|
61
|
-
console.log('response', response);
|
|
62
62
|
// Remove the pending message from the user events
|
|
63
63
|
requestPayload.userEvents?.map(userEvent => markUserEventsProcessed([userEvent.eventId]));
|
|
64
64
|
} catch (error) {
|
|
65
|
-
|
|
65
|
+
Logger.logError('[envive-ai] error sending message', error);
|
|
66
66
|
}
|
|
67
67
|
},
|
|
68
68
|
[getStreamingResponses, setIsInitialized, markUserEventsProcessed],
|
|
69
69
|
);
|
|
70
70
|
|
|
71
|
-
console.log('userQueueEventCount', userQueueEventCount);
|
|
72
|
-
console.log('userEvents', userEvents);
|
|
73
|
-
|
|
74
71
|
// This is the primary event loop for communicating with the backend API
|
|
75
72
|
// It will be triggered when there are pending messages to be sent to the backend
|
|
76
73
|
// It will be responsible for sending the messages to the backend and receiving the responses
|
|
77
74
|
useEffect(() => {
|
|
78
|
-
console.log('SalesAgentProvider useEffect', userQueueEventCount, isInitialized, userEvents);
|
|
79
75
|
if (userQueueEventCount > 0) {
|
|
80
76
|
const payloadEvents = userEvents.slice(0, userQueueEventCount);
|
|
81
77
|
|
|
@@ -22,7 +22,10 @@ import {
|
|
|
22
22
|
suggestionsAtom,
|
|
23
23
|
} from 'src/atoms/chat/chatState';
|
|
24
24
|
import { useMessageInterceptor } from 'src/interceptors/useMessageInterceptor';
|
|
25
|
-
import {
|
|
25
|
+
import { SpiffyMetricsEventName, useAmplitude } from 'src/contexts/amplitudeContext';
|
|
26
|
+
import { useFeatureFlagService } from 'src/contexts/featureFlagServiceContext';
|
|
27
|
+
import { UserEventCategory } from '@spiffy-ai/commerce-api-client';
|
|
28
|
+
|
|
26
29
|
import { StatusCodeError } from './statusCodeError';
|
|
27
30
|
|
|
28
31
|
interface SalesAgentService {
|
|
@@ -35,6 +38,25 @@ interface SalesAgentService {
|
|
|
35
38
|
hydrateMessages: () => Promise<void>;
|
|
36
39
|
}
|
|
37
40
|
|
|
41
|
+
const inputPropsToTrackingProps = (
|
|
42
|
+
payload: NextMessageRequest,
|
|
43
|
+
): Record<string, unknown> | undefined => {
|
|
44
|
+
const [userEvent] = payload.userEvents || [];
|
|
45
|
+
if (userEvent.category === UserEventCategory.SuggestionClicked) {
|
|
46
|
+
return {
|
|
47
|
+
user_event_type: 'suggestion_clicked',
|
|
48
|
+
user_query: userEvent.attributes.content,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (userEvent.category === UserEventCategory.QueryTyped) {
|
|
52
|
+
return {
|
|
53
|
+
user_event_type: 'query_typed',
|
|
54
|
+
user_query: userEvent.attributes.query,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return {};
|
|
58
|
+
};
|
|
59
|
+
|
|
38
60
|
export const getQueryParam = (key: string): string | null => {
|
|
39
61
|
const urlObj = new URL(window.location.href);
|
|
40
62
|
return urlObj.searchParams.get(key);
|
|
@@ -115,6 +137,7 @@ const processStreamingResponse = async (
|
|
|
115
137
|
setResponseStreaming: (responseStreaming: boolean) => void,
|
|
116
138
|
): Promise<void> => {
|
|
117
139
|
let lastMessage: Message | undefined;
|
|
140
|
+
setResponseStreaming(true);
|
|
118
141
|
|
|
119
142
|
for await (const response of stream) {
|
|
120
143
|
try {
|
|
@@ -126,8 +149,6 @@ const processStreamingResponse = async (
|
|
|
126
149
|
throw new Error('No response from stream');
|
|
127
150
|
}
|
|
128
151
|
|
|
129
|
-
setResponseStreaming(true);
|
|
130
|
-
|
|
131
152
|
const message = messageFromResponse(response);
|
|
132
153
|
if (!message) {
|
|
133
154
|
throw new Error('Failed to transform API response to client message');
|
|
@@ -148,6 +169,7 @@ const processStreamingResponse = async (
|
|
|
148
169
|
response,
|
|
149
170
|
});
|
|
150
171
|
}
|
|
172
|
+
setResponseStreaming(false);
|
|
151
173
|
}
|
|
152
174
|
};
|
|
153
175
|
|
|
@@ -158,6 +180,7 @@ export const useSalesAgentService: () => SalesAgentService = () => {
|
|
|
158
180
|
const setPendingResponse = useSetAtom(pendingResponseAtom);
|
|
159
181
|
const setResponseStreaming = useSetAtom(responseStreamingAtom);
|
|
160
182
|
const messageInterceptor = useMessageInterceptor();
|
|
183
|
+
const { trackEvent } = useAmplitude();
|
|
161
184
|
|
|
162
185
|
const featureFlagService = useFeatureFlagService();
|
|
163
186
|
const context = useAtomValue(appDetailsAtom);
|
|
@@ -192,10 +215,8 @@ export const useSalesAgentService: () => SalesAgentService = () => {
|
|
|
192
215
|
|
|
193
216
|
const getStreamingResponses = useCallback(
|
|
194
217
|
async (payload: NextMessageRequest): Promise<void> => {
|
|
195
|
-
|
|
196
|
-
// const startTime = Date.now();
|
|
218
|
+
const startTime = Date.now();
|
|
197
219
|
setPendingResponse(true);
|
|
198
|
-
console.log('getStreamingResponses payload', JSON.stringify(payload, null, 2));
|
|
199
220
|
const stream = CommerceApiClient.getNextResponseStreaming(payload);
|
|
200
221
|
|
|
201
222
|
try {
|
|
@@ -208,9 +229,16 @@ export const useSalesAgentService: () => SalesAgentService = () => {
|
|
|
208
229
|
setResponseStreaming,
|
|
209
230
|
);
|
|
210
231
|
|
|
211
|
-
// TODO: Add support for the Chrome Extension communication
|
|
212
232
|
// Log successful next_responses call
|
|
213
|
-
|
|
233
|
+
const responseTime = Date.now() - startTime;
|
|
234
|
+
trackEvent({
|
|
235
|
+
eventName: SpiffyMetricsEventName.ChatAssistantResponse,
|
|
236
|
+
eventProps: {
|
|
237
|
+
responseTimeMs: responseTime.toString(),
|
|
238
|
+
...inputPropsToTrackingProps(payload),
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
// TODO: Add support for the Chrome Extension communication
|
|
214
242
|
// await logBundleEvent({
|
|
215
243
|
// level: 'info',
|
|
216
244
|
// event: 'NEXT_RESPONSE_SUCCESS',
|
|
@@ -264,14 +292,11 @@ export const useSalesAgentService: () => SalesAgentService = () => {
|
|
|
264
292
|
|
|
265
293
|
const hydrateMessages = useCallback(async () => {
|
|
266
294
|
const { orgId, chatId, userId } = context;
|
|
267
|
-
console.log('hydrateMessages', orgId, chatId, userId, context);
|
|
268
295
|
const { messages: existingMessages, userEvents } = await CommerceApiClient.getResponses(
|
|
269
296
|
orgId,
|
|
270
297
|
chatId,
|
|
271
298
|
userId,
|
|
272
299
|
);
|
|
273
|
-
console.log('existingMessages', existingMessages);
|
|
274
|
-
console.log('userEvents', userEvents);
|
|
275
300
|
setMessages([...existingMessages]);
|
|
276
301
|
}, [context, setMessages]);
|
|
277
302
|
|
package/src/contexts/typesV3.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { WidgetWrapperVariant } from '@envive-ai/react-toolkit-v3/WidgetWrapper'
|
|
|
5
5
|
import { Theme } from '@envive-ai/react-toolkit-v3/Tokens';
|
|
6
6
|
import { DynamicLayout } from '@envive-ai/react-toolkit-v3/SocialProof';
|
|
7
7
|
import { SparkleIconColor } from '@envive-ai/react-toolkit-v3/WelcomeMessage';
|
|
8
|
+
import { FloatingButtonLocation } from '@envive-ai/react-toolkit-v3/FloatingButton';
|
|
8
9
|
import { ColorNames } from '../application/models/colorsConfigV3';
|
|
9
10
|
import { CustomerServiceType } from '../types/customerService';
|
|
10
11
|
import type { MerchantVariantSettings, SearchConfig } from './types';
|
|
@@ -170,14 +171,12 @@ type ConfigVersion = ConfigVersionEnum.V3 | ConfigVersionEnum.Deprecated | undef
|
|
|
170
171
|
|
|
171
172
|
type Mode = 'dark' | 'light';
|
|
172
173
|
|
|
173
|
-
type Position = 'bottomLeft' | 'middleLeft' | 'middleRight' | 'bottomRight';
|
|
174
|
-
|
|
175
174
|
type ShowOptions = 'always' | 'postInteraction' | 'none';
|
|
176
175
|
|
|
177
176
|
type Style = 'attached' | 'detached';
|
|
178
177
|
|
|
179
178
|
type FloatingButtonConfig = {
|
|
180
|
-
position:
|
|
179
|
+
position: FloatingButtonLocation;
|
|
181
180
|
backgroundColor?: string;
|
|
182
181
|
mode: Mode;
|
|
183
182
|
showOption: ShowOptions;
|
|
@@ -341,8 +340,8 @@ export type {
|
|
|
341
340
|
PromptButtonCarouselWithImageWidgetV3Config,
|
|
342
341
|
FloatingChatWidgetV3Config,
|
|
343
342
|
FloatingChatConfig,
|
|
343
|
+
FloatingButtonConfig,
|
|
344
344
|
CustomerServiceIntegration,
|
|
345
|
-
Position,
|
|
346
345
|
};
|
|
347
346
|
|
|
348
347
|
export { SocialProofWidgetKind };
|
|
@@ -4,9 +4,10 @@ import Logger from 'src/application/logging/logger';
|
|
|
4
4
|
import { useNewOrgConfig } from 'src/hooks/NewOrgConfig';
|
|
5
5
|
import { SparkleIconColor } from '@envive-ai/react-toolkit-v3/WelcomeMessage';
|
|
6
6
|
import { ChatHeaderVariant } from '@envive-ai/react-toolkit-v3/ChatHeader';
|
|
7
|
+
import { Theme } from '@envive-ai/react-toolkit-v3/Tokens';
|
|
8
|
+
import { FloatingButtonLocation } from '@envive-ai/react-toolkit-v3/FloatingButton';
|
|
7
9
|
import { UiConfigProvider, useUiConfig } from '../uiConfigContext';
|
|
8
10
|
import { ConfigVersionEnum, OrgUIConfigV3 } from '../../typesV3';
|
|
9
|
-
import { Theme } from '@envive-ai/react-toolkit-v3/Tokens';
|
|
10
11
|
|
|
11
12
|
// Mock the Logger to avoid console output in tests
|
|
12
13
|
vi.spyOn(Logger, 'logInfo').mockImplementation(() => {});
|
|
@@ -107,7 +108,7 @@ describe('UiConfigProvider', () => {
|
|
|
107
108
|
},
|
|
108
109
|
},
|
|
109
110
|
floatingButton: {
|
|
110
|
-
position:
|
|
111
|
+
position: FloatingButtonLocation.BOTTOM_RIGHT,
|
|
111
112
|
backgroundColor: '#000000',
|
|
112
113
|
mode: 'light',
|
|
113
114
|
showOption: 'always',
|
|
@@ -156,7 +156,6 @@ export const UserIdentityProvider: React.FC<{ children: React.ReactNode }> = ({
|
|
|
156
156
|
useEffect(() => {
|
|
157
157
|
if (isReady && !localUserId) {
|
|
158
158
|
const newUserId = getUserIdOrDefault();
|
|
159
|
-
console.log('useUserIdentity useEffect - setting localUserId', newUserId);
|
|
160
159
|
setLocalUserId(newUserId);
|
|
161
160
|
setUserId(newUserId);
|
|
162
161
|
}
|
|
@@ -435,21 +435,6 @@ describe('AmplitudeService', () => {
|
|
|
435
435
|
const eventProps = callArgs[1];
|
|
436
436
|
expect(eventProps).toHaveProperty('test', 'value');
|
|
437
437
|
});
|
|
438
|
-
|
|
439
|
-
it('should replace existing supplemental props', async () => {
|
|
440
|
-
const service = createService();
|
|
441
|
-
service.setSupplementalDefaultProps({ prop1: 'value1' });
|
|
442
|
-
service.setSupplementalDefaultProps({ prop2: 'value2' });
|
|
443
|
-
|
|
444
|
-
await service.trackEvent({
|
|
445
|
-
eventName: SpiffyMetricsEventName.ChatUserMessageInput,
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
const callArgs = mockTrack.mock.calls[0];
|
|
449
|
-
const eventProps = callArgs[1];
|
|
450
|
-
expect(eventProps).toHaveProperty('prop2', 'value2');
|
|
451
|
-
expect(eventProps).not.toHaveProperty('prop1');
|
|
452
|
-
});
|
|
453
438
|
});
|
|
454
439
|
|
|
455
440
|
describe('Event enrichment', () => {
|
|
@@ -318,7 +318,8 @@ export class AmplitudeService {
|
|
|
318
318
|
}
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
+
// Ensure that supplemental default props are merged with the existing props
|
|
321
322
|
setSupplementalDefaultProps(props: Record<string, unknown>): void {
|
|
322
|
-
this.supplementalDefaultProps = props;
|
|
323
|
+
this.supplementalDefaultProps = { ...this.supplementalDefaultProps, ...props };
|
|
323
324
|
}
|
|
324
325
|
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
const require_rolldown_runtime = require('../../_virtual/rolldown_runtime.cjs');
|
|
2
|
-
const require_message = require('../../application/models/message.cjs');
|
|
3
|
-
require('../../application/models/index.cjs');
|
|
4
|
-
const require_amplitudeService = require('../../services/amplitudeService/amplitudeService.cjs');
|
|
5
|
-
require('../../contexts/amplitudeContext/amplitudeContext.cjs');
|
|
6
|
-
const require_atoms_chat_chatState = require('./chatState.cjs');
|
|
7
|
-
const require_amplitudeTrackEventAtom = require('../amplitude/amplitudeTrackEventAtom.cjs');
|
|
8
|
-
const require_messageQueue = require('./messageQueue.cjs');
|
|
9
|
-
require('./index.cjs');
|
|
10
|
-
let __spiffy_ai_commerce_api_client = require("@spiffy-ai/commerce-api-client");
|
|
11
|
-
let jotai = require("jotai");
|
|
12
|
-
|
|
13
|
-
//#region src/atoms/chat/replies.ts
|
|
14
|
-
const handleReplyAtom = (0, jotai.atom)(null, (get, set, { message, userTyped }) => {
|
|
15
|
-
if (message.type !== require_message.MessageType.QueryTyped) return;
|
|
16
|
-
const trackEvent = get(require_amplitudeTrackEventAtom.amplitudeTrackEventAtom);
|
|
17
|
-
const queryTyped = message.metadata.content;
|
|
18
|
-
set(require_atoms_chat_chatState.replyEventCategoryAtom, __spiffy_ai_commerce_api_client.UserEventCategory.QueryTyped);
|
|
19
|
-
set(require_atoms_chat_chatState.userQueryAtom, queryTyped);
|
|
20
|
-
set(require_atoms_chat_chatState.messagesAtom, [...get(require_atoms_chat_chatState.messagesAtom), [message]]);
|
|
21
|
-
set(require_atoms_chat_chatState.userHasRepliedAtom, true);
|
|
22
|
-
set(require_messageQueue.queueUserEventAtom, {
|
|
23
|
-
eventId: message.id,
|
|
24
|
-
createdAt: message.createdAt,
|
|
25
|
-
category: __spiffy_ai_commerce_api_client.UserEventCategory.QueryTyped,
|
|
26
|
-
attributes: { query: queryTyped }
|
|
27
|
-
});
|
|
28
|
-
if (trackEvent) trackEvent({
|
|
29
|
-
eventName: require_amplitudeService.SpiffyMetricsEventName.ChatUserMessageInput,
|
|
30
|
-
eventProps: {
|
|
31
|
-
message_id: message.id,
|
|
32
|
-
message_role: message.role,
|
|
33
|
-
message_type: message.type,
|
|
34
|
-
message_metadata: {
|
|
35
|
-
content: message?.metadata?.content,
|
|
36
|
-
created_at: message.createdAt,
|
|
37
|
-
user_typed: userTyped
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
alsoSendToGoogleAnalytics: true
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
//#endregion
|
|
45
|
-
exports.handleReplyAtom = handleReplyAtom;
|
|
46
|
-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVwbGllcy5janMiLCJuYW1lcyI6WyJNZXNzYWdlVHlwZSIsImFtcGxpdHVkZVRyYWNrRXZlbnRBdG9tIiwicmVwbHlFdmVudENhdGVnb3J5QXRvbSIsIlVzZXJFdmVudENhdGVnb3J5IiwidXNlclF1ZXJ5QXRvbSIsIm1lc3NhZ2VzQXRvbSIsInVzZXJIYXNSZXBsaWVkQXRvbSIsInF1ZXVlVXNlckV2ZW50QXRvbSIsIlNwaWZmeU1ldHJpY3NFdmVudE5hbWUiXSwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXRvbXMvY2hhdC9yZXBsaWVzLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGF0b20gfSBmcm9tICdqb3RhaSc7XG5pbXBvcnQgeyBNZXNzYWdlLCBNZXNzYWdlVHlwZSB9IGZyb20gJ3NyYy9hcHBsaWNhdGlvbi9tb2RlbHMnO1xuaW1wb3J0IHtcbiAgbWVzc2FnZXNBdG9tLFxuICByZXBseUV2ZW50Q2F0ZWdvcnlBdG9tLFxuICB1c2VySGFzUmVwbGllZEF0b20sXG4gIHVzZXJRdWVyeUF0b20sXG59IGZyb20gJ3NyYy9hdG9tcy9jaGF0JztcbmltcG9ydCB7IFVzZXJFdmVudENhdGVnb3J5IH0gZnJvbSAnQHNwaWZmeS1haS9jb21tZXJjZS1hcGktY2xpZW50JztcbmltcG9ydCB7IFNwaWZmeU1ldHJpY3NFdmVudE5hbWUgfSBmcm9tICdzcmMvY29udGV4dHMvYW1wbGl0dWRlQ29udGV4dC9hbXBsaXR1ZGVDb250ZXh0JztcbmltcG9ydCB7IGFtcGxpdHVkZVRyYWNrRXZlbnRBdG9tIH0gZnJvbSAnc3JjL2F0b21zL2FtcGxpdHVkZS9hbXBsaXR1ZGVUcmFja0V2ZW50QXRvbSc7XG5pbXBvcnQgeyBxdWV1ZVVzZXJFdmVudEF0b20gfSBmcm9tICcuL21lc3NhZ2VRdWV1ZSc7XG5cbnR5cGUgSGFuZGxlUmVwbHlQYXJhbXMgPSB7XG4gIG1lc3NhZ2U6IE1lc3NhZ2U7XG4gIHVzZXJUeXBlZDogYm9vbGVhbjtcbn07XG5cbmV4cG9ydCBjb25zdCBoYW5kbGVSZXBseUF0b20gPSBhdG9tKG51bGwsIChnZXQsIHNldCwgeyBtZXNzYWdlLCB1c2VyVHlwZWQgfTogSGFuZGxlUmVwbHlQYXJhbXMpID0+IHtcbiAgaWYgKG1lc3NhZ2UudHlwZSAhPT0gTWVzc2FnZVR5cGUuUXVlcnlUeXBlZCkge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbnN0IHRyYWNrRXZlbnQgPSBnZXQoYW1wbGl0dWRlVHJhY2tFdmVudEF0b20pO1xuICBjb25zdCBxdWVyeVR5cGVkID0gbWVzc2FnZS5tZXRhZGF0YS5jb250ZW50O1xuXG4gIHNldChyZXBseUV2ZW50Q2F0ZWdvcnlBdG9tLCBVc2VyRXZlbnRDYXRlZ29yeS5RdWVyeVR5cGVkKTtcbiAgc2V0KHVzZXJRdWVyeUF0b20sIHF1ZXJ5VHlwZWQpO1xuICBzZXQobWVzc2FnZXNBdG9tLCBbLi4uZ2V0KG1lc3NhZ2VzQXRvbSksIFttZXNzYWdlXV0pO1xuICBzZXQodXNlckhhc1JlcGxpZWRBdG9tLCB0cnVlKTtcbiAgc2V0KHF1ZXVlVXNlckV2ZW50QXRvbSwge1xuICAgIGV2ZW50SWQ6IG1lc3NhZ2UuaWQsXG4gICAgY3JlYXRlZEF0OiBtZXNzYWdlLmNyZWF0ZWRBdCxcbiAgICBjYXRlZ29yeTogVXNlckV2ZW50Q2F0ZWdvcnkuUXVlcnlUeXBlZCxcbiAgICBhdHRyaWJ1dGVzOiB7XG4gICAgICBxdWVyeTogcXVlcnlUeXBlZCxcbiAgICB9LFxuICB9KTtcblxuICBpZiAodHJhY2tFdmVudCkge1xuICAgIHRyYWNrRXZlbnQoe1xuICAgICAgZXZlbnROYW1lOiBTcGlmZnlNZXRyaWNzRXZlbnROYW1lLkNoYXRVc2VyTWVzc2FnZUlucHV0LFxuICAgICAgZXZlbnRQcm9wczoge1xuICAgICAgICBtZXNzYWdlX2lkOiBtZXNzYWdlLmlkLFxuICAgICAgICBtZXNzYWdlX3JvbGU6IG1lc3NhZ2Uucm9sZSxcbiAgICAgICAgbWVzc2FnZV90eXBlOiBtZXNzYWdlLnR5cGUsXG4gICAgICAgIG1lc3NhZ2VfbWV0YWRhdGE6IHtcbiAgICAgICAgICBjb250ZW50OiBtZXNzYWdlPy5tZXRhZGF0YT8uY29udGVudCwgLy8gUmVtb3ZlZCBhbXBsaXR1ZGVTYWZlU3RyaW5nXG4gICAgICAgICAgY3JlYXRlZF9hdDogbWVzc2FnZS5jcmVhdGVkQXQsXG4gICAgICAgICAgdXNlcl90eXBlZDogdXNlclR5cGVkLFxuICAgICAgICB9LFxuICAgICAgfSxcbiAgICAgIGFsc29TZW5kVG9Hb29nbGVBbmFseXRpY3M6IHRydWUsXG4gICAgfSk7XG4gIH1cbn0pO1xuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7O0FBa0JBLE1BQWEsa0NBQXVCLE9BQU8sS0FBSyxLQUFLLEVBQUUsU0FBUyxnQkFBbUM7QUFDakcsS0FBSSxRQUFRLFNBQVNBLDRCQUFZLFdBQy9CO0NBR0YsTUFBTSxhQUFhLElBQUlDLHdEQUF3QjtDQUMvQyxNQUFNLGFBQWEsUUFBUSxTQUFTO0FBRXBDLEtBQUlDLHFEQUF3QkMsa0RBQWtCLFdBQVc7QUFDekQsS0FBSUMsNENBQWUsV0FBVztBQUM5QixLQUFJQywyQ0FBYyxDQUFDLEdBQUcsSUFBSUEsMENBQWEsRUFBRSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0FBQ3BELEtBQUlDLGlEQUFvQixLQUFLO0FBQzdCLEtBQUlDLHlDQUFvQjtFQUN0QixTQUFTLFFBQVE7RUFDakIsV0FBVyxRQUFRO0VBQ25CLFVBQVVKLGtEQUFrQjtFQUM1QixZQUFZLEVBQ1YsT0FBTyxZQUNSO0VBQ0YsQ0FBQztBQUVGLEtBQUksV0FDRixZQUFXO0VBQ1QsV0FBV0ssZ0RBQXVCO0VBQ2xDLFlBQVk7R0FDVixZQUFZLFFBQVE7R0FDcEIsY0FBYyxRQUFRO0dBQ3RCLGNBQWMsUUFBUTtHQUN0QixrQkFBa0I7SUFDaEIsU0FBUyxTQUFTLFVBQVU7SUFDNUIsWUFBWSxRQUFRO0lBQ3BCLFlBQVk7SUFDYjtHQUNGO0VBQ0QsMkJBQTJCO0VBQzVCLENBQUM7RUFFSiJ9
|