diff --git a/bundlesize.config.json b/bundlesize.config.json index a05f853d6..645101608 100644 --- a/bundlesize.config.json +++ b/bundlesize.config.json @@ -2,11 +2,11 @@ "files": [ { "path": "packages/autocomplete-core/dist/umd/index.production.js", - "maxSize": "8.75 kB" + "maxSize": "9 kB" }, { "path": "packages/autocomplete-js/dist/umd/index.production.js", - "maxSize": "20.75 kB" + "maxSize": "21 kB" }, { "path": "packages/autocomplete-preset-algolia/dist/umd/index.production.js", diff --git a/packages/autocomplete-core/src/__tests__/createAutocomplete.test.ts b/packages/autocomplete-core/src/__tests__/createAutocomplete.test.ts index 1ed81526b..0b4aa7b4f 100644 --- a/packages/autocomplete-core/src/__tests__/createAutocomplete.test.ts +++ b/packages/autocomplete-core/src/__tests__/createAutocomplete.test.ts @@ -1,5 +1,13 @@ import { createAlgoliaInsightsPlugin } from '@algolia/autocomplete-plugin-algolia-insights'; +import { getAlgoliaResults } from '@algolia/autocomplete-preset-algolia'; +import userEvent from '@testing-library/user-event'; +import { + createMultiSearchResponse, + createPlayground, + createSearchClient, + runAllMicroTasks, +} from '../../../../test/utils'; import { createAutocomplete } from '../createAutocomplete'; describe('createAutocomplete', () => { @@ -166,5 +174,592 @@ describe('createAutocomplete', () => { 'insights-plugin' ); }); + + describe('from the response', () => { + let insightsClient: ReturnType = undefined; + + beforeEach(() => { + insightsClient = jest.spyOn(window, 'aa'); + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + jest.restoreAllMocks(); + }); + + test('opt-in adds the Insights plugin', async () => { + const onStateChange = jest.fn(); + + const searchClient = createSearchClient({ + search: jest.fn((requests) => { + return Promise.resolve( + createMultiSearchResponse<{ label: string }>( + ...requests.map(({ indexName, query = '' }, index) => ({ + hits: Array.from({ length: 2 }).map((_, i, arr) => ({ + objectID: `${String(index * arr.length + i + 1)}-${query}`, + label: query, + })), + index: indexName, + queryID: `queryID${index}`, + query, + _automaticInsights: true as const, + })) + ) + ); + }), + }); + + const { inputElement } = createPlayground(createAutocomplete, { + onStateChange, + openOnFocus: true, + defaultActiveItemId: 0, + getSources({ query }) { + return [ + { + sourceId: 'items', + getItems() { + return getAlgoliaResults({ + searchClient, + queries: [ + { + indexName: 'indexName', + query, + }, + ], + }); + }, + }, + { + sourceId: 'items2', + getItems() { + return getAlgoliaResults({ + searchClient, + queries: [ + { + indexName: 'indexName2', + query, + }, + ], + }); + }, + }, + ]; + }, + }); + + inputElement.focus(); + await runAllMicroTasks(); + jest.runAllTimers(); + + // The Insights plugin was properly added + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + context: expect.objectContaining({ + algoliaInsightsPlugin: expect.objectContaining({ + insights: expect.objectContaining({ + init: expect.any(Function), + setUserToken: expect.any(Function), + clickedObjectIDsAfterSearch: expect.any(Function), + clickedObjectIDs: expect.any(Function), + clickedFilters: expect.any(Function), + convertedObjectIDsAfterSearch: expect.any(Function), + convertedObjectIDs: expect.any(Function), + convertedFilters: expect.any(Function), + viewedObjectIDs: expect.any(Function), + viewedFilters: expect.any(Function), + }), + }), + }), + }), + }) + ); + + // Typing to change hits and trigger `view` events + userEvent.type(inputElement, 'a'); + await runAllMicroTasks(); + jest.runAllTimers(); + + // Subsequent requests don't send `clickAnalytics` + expect(searchClient.search).toHaveBeenLastCalledWith([ + expect.objectContaining({ + query: 'a', + params: expect.not.objectContaining({ + clickAnalytics: expect.anything(), + }), + }), + expect.objectContaining({ + query: 'a', + params: expect.not.objectContaining({ + clickAnalytics: expect.anything(), + }), + }), + ]); + + // Default `view` events are sent for all + expect(insightsClient).toHaveBeenCalledWith( + 'viewedObjectIDs', + expect.objectContaining({ + eventName: 'Items Viewed', + index: 'indexName', + objectIDs: ['1-a', '2-a'], + algoliaSource: [ + 'autocomplete', + 'autocomplete-internal', + 'autocomplete-automatic', + ], + }) + ); + expect(insightsClient).toHaveBeenCalledWith( + 'viewedObjectIDs', + expect.objectContaining({ + eventName: 'Items Viewed', + index: 'indexName2', + objectIDs: ['3-a', '4-a'], + algoliaSource: [ + 'autocomplete', + 'autocomplete-internal', + 'autocomplete-automatic', + ], + }) + ); + }); + + test('partial opt-in adds the Insights plugin', async () => { + const onStateChange = jest.fn(); + + const searchClient = createSearchClient({ + search: jest.fn((requests) => { + return Promise.resolve( + createMultiSearchResponse<{ label: string }>( + ...requests.map(({ indexName, query = '' }, index) => ({ + hits: Array.from({ length: 2 }).map((_, i, arr) => ({ + objectID: `${String(index * arr.length + i + 1)}-${query}`, + label: query, + })), + index: indexName, + query, + })) + ) + ); + }), + }); + + const searchClient2 = createSearchClient({ + search: jest.fn((requests) => { + return Promise.resolve( + createMultiSearchResponse<{ label: string }>( + ...requests.map(({ indexName, query = '' }, index) => ({ + hits: Array.from({ length: 2 }).map((_, i, arr) => ({ + objectID: `${String( + (index + 1) * arr.length + i + 1 + )}-${query}`, + label: query, + })), + index: indexName, + query, + queryID: `queryID${index}`, + _automaticInsights: true as const, + })) + ) + ); + }), + }); + + const { inputElement } = createPlayground(createAutocomplete, { + onStateChange, + openOnFocus: true, + defaultActiveItemId: 0, + getSources({ query }) { + return [ + { + sourceId: 'items', + getItems() { + return getAlgoliaResults({ + searchClient, + queries: [ + { + indexName: 'indexName', + query, + }, + ], + }); + }, + }, + { + sourceId: 'items2', + getItems() { + return getAlgoliaResults({ + searchClient: searchClient2, + queries: [ + { + indexName: 'indexName2', + query, + }, + ], + }); + }, + }, + ]; + }, + }); + + inputElement.focus(); + await runAllMicroTasks(); + jest.runAllTimers(); + + // The Insights plugin was properly added + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + context: expect.objectContaining({ + algoliaInsightsPlugin: expect.objectContaining({ + insights: expect.objectContaining({ + init: expect.any(Function), + setUserToken: expect.any(Function), + clickedObjectIDsAfterSearch: expect.any(Function), + clickedObjectIDs: expect.any(Function), + clickedFilters: expect.any(Function), + convertedObjectIDsAfterSearch: expect.any(Function), + convertedObjectIDs: expect.any(Function), + convertedFilters: expect.any(Function), + viewedObjectIDs: expect.any(Function), + viewedFilters: expect.any(Function), + }), + }), + }), + }), + }) + ); + + // Typing to change hits and trigger `view` events + userEvent.type(inputElement, 'a'); + await runAllMicroTasks(); + jest.runAllTimers(); + + // Subsequent requests don't send `clickAnalytics` + expect(searchClient.search).toHaveBeenLastCalledWith([ + expect.objectContaining({ + query: 'a', + params: expect.not.objectContaining({ + clickAnalytics: expect.anything(), + }), + }), + ]); + expect(searchClient2.search).toHaveBeenLastCalledWith([ + expect.objectContaining({ + query: 'a', + params: expect.not.objectContaining({ + clickAnalytics: expect.anything(), + }), + }), + ]); + + // Default `view` events are not sent for the first set of items + expect(insightsClient).not.toHaveBeenCalledWith( + 'viewedObjectIDs', + expect.objectContaining({ + eventName: 'Items Viewed', + index: 'indexName', + objectIDs: ['1-a', '2-a'], + algoliaSource: [ + 'autocomplete', + 'autocomplete-internal', + 'autocomplete-automatic', + ], + }) + ); + // Default `view` events are sent for the second set of items + expect(insightsClient).toHaveBeenCalledWith( + 'viewedObjectIDs', + expect.objectContaining({ + eventName: 'Items Viewed', + index: 'indexName2', + objectIDs: ['3-a', '4-a'], + algoliaSource: [ + 'autocomplete', + 'autocomplete-internal', + 'autocomplete-automatic', + ], + }) + ); + }); + + test('opt-out or unset does not add the Insights plugin', async () => { + const onStateChange = jest.fn(); + + const searchClient = createSearchClient({ + search: jest.fn((requests) => { + return Promise.resolve( + createMultiSearchResponse<{ label: string }>( + ...requests.map(({ indexName, query = '' }, index) => ({ + hits: Array.from({ length: 2 }).map((_, i, arr) => ({ + objectID: String(index * arr.length + i + 1), + label: query, + })), + index: indexName, + query, + })) + ) + ); + }), + }); + + const { inputElement } = createPlayground(createAutocomplete, { + onStateChange, + openOnFocus: true, + defaultActiveItemId: 0, + getSources({ query }) { + return [ + { + sourceId: 'items', + getItems() { + return getAlgoliaResults({ + searchClient, + queries: [ + { + indexName: 'indexName', + query, + }, + ], + }); + }, + }, + { + sourceId: 'items2', + getItems() { + return getAlgoliaResults({ + searchClient, + queries: [ + { + indexName: 'indexName2', + query, + }, + ], + }); + }, + }, + ]; + }, + }); + + inputElement.focus(); + await runAllMicroTasks(); + jest.runAllTimers(); + + // The Insights plugin was not added + expect(onStateChange).not.toHaveBeenCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + context: expect.objectContaining({ + algoliaInsightsPlugin: expect.objectContaining({ + insights: expect.objectContaining({ + init: expect.any(Function), + setUserToken: expect.any(Function), + clickedObjectIDsAfterSearch: expect.any(Function), + clickedObjectIDs: expect.any(Function), + clickedFilters: expect.any(Function), + convertedObjectIDsAfterSearch: expect.any(Function), + convertedObjectIDs: expect.any(Function), + convertedFilters: expect.any(Function), + viewedObjectIDs: expect.any(Function), + viewedFilters: expect.any(Function), + }), + }), + }), + }), + }) + ); + + // Typing to change hits and trigger `view` events + userEvent.type(inputElement, 'a'); + await runAllMicroTasks(); + + // No default events are sent + expect(insightsClient).not.toHaveBeenCalled(); + }); + + test('opt-in + `insights: false` does not add the Insights plugin', async () => { + const onStateChange = jest.fn(); + + const searchClient = createSearchClient({ + search: jest.fn((requests) => { + return Promise.resolve( + createMultiSearchResponse<{ label: string }>( + ...requests.map(({ indexName, query = '' }, index) => ({ + hits: Array.from({ length: 2 }).map((_, i, arr) => ({ + objectID: String(index * arr.length + i + 1), + label: query, + })), + index: indexName, + queryID: `queryID${index}`, + query, + _automaticInsights: true as const, + })) + ) + ); + }), + }); + + const { inputElement } = createPlayground(createAutocomplete, { + onStateChange, + openOnFocus: true, + defaultActiveItemId: 0, + insights: false, + getSources({ query }) { + return [ + { + sourceId: 'items', + getItems() { + return getAlgoliaResults({ + searchClient, + queries: [ + { + indexName: 'indexName', + query, + }, + ], + }); + }, + }, + { + sourceId: 'items2', + getItems() { + return getAlgoliaResults({ + searchClient, + queries: [ + { + indexName: 'indexName2', + query, + }, + ], + }); + }, + }, + ]; + }, + }); + + inputElement.focus(); + await runAllMicroTasks(); + jest.runAllTimers(); + + // The Insights plugin was not added + expect(onStateChange).not.toHaveBeenCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + context: expect.objectContaining({ + algoliaInsightsPlugin: expect.objectContaining({ + insights: expect.objectContaining({ + init: expect.any(Function), + setUserToken: expect.any(Function), + clickedObjectIDsAfterSearch: expect.any(Function), + clickedObjectIDs: expect.any(Function), + clickedFilters: expect.any(Function), + convertedObjectIDsAfterSearch: expect.any(Function), + convertedObjectIDs: expect.any(Function), + convertedFilters: expect.any(Function), + viewedObjectIDs: expect.any(Function), + viewedFilters: expect.any(Function), + }), + }), + }), + }), + }) + ); + + // Typing to change hits and trigger `view` events + userEvent.type(inputElement, 'a'); + await runAllMicroTasks(); + + // No default events are sent + expect(insightsClient).not.toHaveBeenCalled(); + }); + + test('opt-in + `insights: true` does not add the Insights plugin a second time', async () => { + const onStateChange = jest.fn(); + + const searchClient = createSearchClient({ + search: jest.fn((requests) => { + return Promise.resolve( + createMultiSearchResponse<{ label: string }>( + ...requests.map(({ indexName, query = '' }, index) => ({ + hits: Array.from({ length: 2 }).map((_, i, arr) => ({ + objectID: String(index * arr.length + i + 1), + label: query, + })), + index: indexName, + queryID: `queryID${index}`, + query, + _automaticInsights: true as const, + })) + ) + ); + }), + }); + + const { inputElement } = createPlayground(createAutocomplete, { + onStateChange, + openOnFocus: true, + defaultActiveItemId: 0, + insights: true, + getSources({ query }) { + return [ + { + sourceId: 'items', + getItems() { + return getAlgoliaResults({ + searchClient, + queries: [ + { + indexName: 'indexName', + query, + }, + ], + }); + }, + }, + { + sourceId: 'items2', + getItems() { + return getAlgoliaResults({ + searchClient, + queries: [ + { + indexName: 'indexName2', + query, + }, + ], + }); + }, + }, + ]; + }, + }); + + inputElement.focus(); + await runAllMicroTasks(); + jest.runAllTimers(); + + // The Insights plugin was added + expect(insightsClient).toHaveBeenCalledWith( + 'addAlgoliaAgent', + 'insights-plugin' + ); + + insightsClient.mockClear(); + + // Typing to change hits and trigger `view` events + userEvent.type(inputElement, 'a'); + await runAllMicroTasks(); + + // The Insights plugin was not added a second time + expect(insightsClient).not.toHaveBeenCalledWith( + 'addAlgoliaAgent', + 'insights-plugin' + ); + }); + }); }); }); diff --git a/packages/autocomplete-core/src/createAutocomplete.ts b/packages/autocomplete-core/src/createAutocomplete.ts index d9c9cf7d5..1bcc903f6 100644 --- a/packages/autocomplete-core/src/createAutocomplete.ts +++ b/packages/autocomplete-core/src/createAutocomplete.ts @@ -53,6 +53,20 @@ export function createAutocomplete< navigator: props.navigator, ...setters, }); + + if ( + !isAlgoliaInsightsPluginEnabled() && + state.context?.algoliaInsightsPlugin?.__automaticInsights && + props.insights !== false + ) { + const plugin = createAlgoliaInsightsPlugin({ + __autocomplete_clickAnalytics: false, + }); + + props.plugins.push(plugin); + + subscribePlugins([plugin]); + } } function refresh() { @@ -68,31 +82,38 @@ export function createAutocomplete< }); } - if ( - props.insights && - !props.plugins.some((plugin) => plugin.name === 'aa.algoliaInsightsPlugin') - ) { + function subscribePlugins(plugins: typeof props.plugins) { + plugins.forEach((plugin) => + plugin.subscribe?.({ + ...setters, + navigator: props.navigator, + refresh, + onSelect(fn) { + subscribers.push({ onSelect: fn }); + }, + onActive(fn) { + subscribers.push({ onActive: fn }); + }, + onResolve(fn) { + subscribers.push({ onResolve: fn }); + }, + }) + ); + } + + function isAlgoliaInsightsPluginEnabled() { + return props.plugins.some( + (plugin) => plugin.name === 'aa.algoliaInsightsPlugin' + ); + } + + if (props.insights && !isAlgoliaInsightsPluginEnabled()) { const insightsParams = typeof props.insights === 'boolean' ? {} : props.insights; props.plugins.push(createAlgoliaInsightsPlugin(insightsParams)); } - props.plugins.forEach((plugin) => - plugin.subscribe?.({ - ...setters, - navigator: props.navigator, - refresh, - onSelect(fn) { - subscribers.push({ onSelect: fn }); - }, - onActive(fn) { - subscribers.push({ onActive: fn }); - }, - onResolve(fn) { - subscribers.push({ onResolve: fn }); - }, - }) - ); + subscribePlugins(props.plugins); injectMetadata({ metadata: getMetadata({ plugins: props.plugins, options }), diff --git a/packages/autocomplete-core/src/onInput.ts b/packages/autocomplete-core/src/onInput.ts index 3e7b26e29..f78452dbb 100644 --- a/packages/autocomplete-core/src/onInput.ts +++ b/packages/autocomplete-core/src/onInput.ts @@ -1,3 +1,6 @@ +import { SearchResponse } from '@algolia/autocomplete-shared'; +import { SearchForFacetValuesResponse } from '@algolia/client-search'; + import { reshape } from './reshape'; import { preResolve, resolve, postResolve } from './resolve'; import { @@ -47,8 +50,14 @@ export function onInput({ props.environment.clearTimeout(lastStalledId); } - const { setCollections, setIsOpen, setQuery, setActiveItemId, setStatus } = - setters; + const { + setCollections, + setIsOpen, + setQuery, + setActiveItemId, + setStatus, + setContext, + } = setters; setQuery(query); setActiveItemId(props.defaultActiveItemId); @@ -117,7 +126,24 @@ export function onInput({ }) ) .then(resolve) - .then((responses) => postResolve(responses, sources, store)) + .then((responses) => { + const __automaticInsights = responses.some(({ items }) => + isSearchResponseWithAutomaticInsightsFlag(items) + ); + + // No need to pollute the context if `__automaticInsights=false` + if (__automaticInsights) { + setContext({ + algoliaInsightsPlugin: { + ...((store.getState().context?.algoliaInsightsPlugin || + {}) as Record), + __automaticInsights, + }, + }); + } + + return postResolve(responses, sources, store); + }) .then((collections) => reshape({ collections, props, state: store.getState() }) ); @@ -168,3 +194,16 @@ export function onInput({ return store.pendingRequests.add(request); } + +function isSearchResponseWithAutomaticInsightsFlag( + items: + | TItem[] + | TItem[][] + | SearchForFacetValuesResponse + | SearchResponse +): items is SearchResponse { + return ( + !Array.isArray(items) && + Boolean((items as SearchResponse)?._automaticInsights) + ); +} diff --git a/packages/autocomplete-core/src/resolve.ts b/packages/autocomplete-core/src/resolve.ts index f0f850bd5..4d643fda3 100644 --- a/packages/autocomplete-core/src/resolve.ts +++ b/packages/autocomplete-core/src/resolve.ts @@ -5,10 +5,10 @@ import type { TransformResponse, } from '@algolia/autocomplete-preset-algolia'; import { decycle, flatten, invariant } from '@algolia/autocomplete-shared'; +import type { SearchResponse } from '@algolia/autocomplete-shared'; import { MultipleQueriesQuery, SearchForFacetValuesResponse, - SearchResponse, } from '@algolia/client-search'; import type { SearchClient } from 'algoliasearch/lite'; diff --git a/packages/autocomplete-core/src/utils/mapToAlgoliaResponse.ts b/packages/autocomplete-core/src/utils/mapToAlgoliaResponse.ts index 41182e3f8..4220f5339 100644 --- a/packages/autocomplete-core/src/utils/mapToAlgoliaResponse.ts +++ b/packages/autocomplete-core/src/utils/mapToAlgoliaResponse.ts @@ -1,7 +1,5 @@ -import type { - SearchForFacetValuesResponse, - SearchResponse, -} from '@algolia/client-search'; +import type { SearchResponse } from '@algolia/autocomplete-shared'; +import type { SearchForFacetValuesResponse } from '@algolia/client-search'; export function mapToAlgoliaResponse( rawResults: Array | SearchForFacetValuesResponse> diff --git a/packages/autocomplete-plugin-algolia-insights/src/createAlgoliaInsightsPlugin.ts b/packages/autocomplete-plugin-algolia-insights/src/createAlgoliaInsightsPlugin.ts index f8ae8f67c..19c6f4e19 100644 --- a/packages/autocomplete-plugin-algolia-insights/src/createAlgoliaInsightsPlugin.ts +++ b/packages/autocomplete-plugin-algolia-insights/src/createAlgoliaInsightsPlugin.ts @@ -7,7 +7,10 @@ import { noop, safelyRunOnBrowser, } from '@algolia/autocomplete-shared'; -import { AutocompleteReshapeSource } from '@algolia/autocomplete-shared/dist/esm/core'; +import { + AutocompleteContext, + AutocompleteReshapeSource, +} from '@algolia/autocomplete-shared/dist/esm/core'; import { createClickedEvent } from './createClickedEvent'; import { createSearchInsightsApi } from './createSearchInsightsApi'; @@ -80,6 +83,10 @@ export type CreateAlgoliaInsightsPluginParams = { * @link https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-plugin-algolia-insights/createAlgoliaInsightsPlugin/#param-onactive */ onActive?(params: OnActiveParams): void; + /** + * @internal + */ + __autocomplete_clickAnalytics?: boolean; }; export function createAlgoliaInsightsPlugin( @@ -90,6 +97,7 @@ export function createAlgoliaInsightsPlugin( onItemsChange, onSelect: onSelectEvent, onActive: onActiveEvent, + __autocomplete_clickAnalytics, } = getOptions(options); let insightsClient = providedInsightsClient as InsightsClient; @@ -160,7 +168,9 @@ export function createAlgoliaInsightsPlugin( setContext({ algoliaInsightsPlugin: { __algoliaSearchParameters: { - clickAnalytics: true, + ...(__autocomplete_clickAnalytics + ? { clickAnalytics: true } + : {}), ...(userToken ? { userToken } : {}), }, insights, @@ -231,31 +241,40 @@ export function createAlgoliaInsightsPlugin( }; } +function getAlgoliaSources( + algoliaSourceBase: string[] = [], + context: AutocompleteContext +) { + return [ + ...algoliaSourceBase, + 'autocomplete-internal', + ...((context.algoliaInsightsPlugin as Record) + ?.__automaticInsights + ? ['autocomplete-automatic'] + : []), + ]; +} + function getOptions(options: CreateAlgoliaInsightsPluginParams) { return { - onItemsChange({ insights, insightsEvents }: OnItemsChangeParams) { + onItemsChange({ insights, insightsEvents, state }: OnItemsChangeParams) { insights.viewedObjectIDs( ...insightsEvents.map((event) => ({ ...event, - algoliaSource: [ - ...(event.algoliaSource || []), - 'autocomplete-internal', - ], + algoliaSource: getAlgoliaSources(event.algoliaSource, state.context), })) ); }, - onSelect({ insights, insightsEvents }: OnSelectParams) { + onSelect({ insights, insightsEvents, state }: OnSelectParams) { insights.clickedObjectIDsAfterSearch( ...insightsEvents.map((event) => ({ ...event, - algoliaSource: [ - ...(event.algoliaSource || []), - 'autocomplete-internal', - ], + algoliaSource: getAlgoliaSources(event.algoliaSource, state.context), })) ); }, onActive: noop, + __autocomplete_clickAnalytics: true, ...options, }; } diff --git a/packages/autocomplete-plugin-redirect-url/src/types/Redirect.ts b/packages/autocomplete-plugin-redirect-url/src/types/Redirect.ts index dca47db2f..125b0fb68 100644 --- a/packages/autocomplete-plugin-redirect-url/src/types/Redirect.ts +++ b/packages/autocomplete-plugin-redirect-url/src/types/Redirect.ts @@ -4,10 +4,8 @@ import { InternalAutocompleteOptions, } from '@algolia/autocomplete-core'; import { SourceTemplates } from '@algolia/autocomplete-js'; -import { - SearchForFacetValuesResponse, - SearchResponse, -} from '@algolia/autocomplete-preset-algolia'; +import { SearchForFacetValuesResponse } from '@algolia/autocomplete-preset-algolia'; +import type { SearchResponse } from '@algolia/autocomplete-shared'; export interface RedirectUrlPlugin { data: RedirectUrlState[]; diff --git a/packages/autocomplete-preset-algolia/src/search/fetchAlgoliaResults.ts b/packages/autocomplete-preset-algolia/src/search/fetchAlgoliaResults.ts index c94ba702f..037d26160 100644 --- a/packages/autocomplete-preset-algolia/src/search/fetchAlgoliaResults.ts +++ b/packages/autocomplete-preset-algolia/src/search/fetchAlgoliaResults.ts @@ -3,13 +3,10 @@ import { UserAgent, invariant, } from '@algolia/autocomplete-shared'; +import type { SearchResponse } from '@algolia/autocomplete-shared'; import { HIGHLIGHT_PRE_TAG, HIGHLIGHT_POST_TAG } from '../constants'; -import type { - SearchForFacetValuesResponse, - SearchResponse, - SearchParams, -} from '../types'; +import type { SearchForFacetValuesResponse, SearchParams } from '../types'; import { getAppIdAndApiKey } from '../utils'; export function fetchAlgoliaResults({ diff --git a/packages/autocomplete-shared/src/SearchResponse.ts b/packages/autocomplete-shared/src/SearchResponse.ts new file mode 100644 index 000000000..fe38db95c --- /dev/null +++ b/packages/autocomplete-shared/src/SearchResponse.ts @@ -0,0 +1,5 @@ +import type { SearchResponse as ClientSearchResponse } from '@algolia/client-search'; + +export type SearchResponse = ClientSearchResponse & { + _automaticInsights?: true; +}; diff --git a/packages/autocomplete-shared/src/core/AutocompleteSource.ts b/packages/autocomplete-shared/src/core/AutocompleteSource.ts index 08014968b..c444efe50 100644 --- a/packages/autocomplete-shared/src/core/AutocompleteSource.ts +++ b/packages/autocomplete-shared/src/core/AutocompleteSource.ts @@ -1,11 +1,9 @@ import type { FacetHit, Hit } from '@algolia/client-search'; import type { MaybePromise } from '../MaybePromise'; -import type { - SearchForFacetValuesResponse, - SearchResponse, -} from '../preset-algolia/algoliasearch'; +import type { SearchForFacetValuesResponse } from '../preset-algolia/algoliasearch'; import type { RequesterDescription } from '../preset-algolia/createRequester'; +import type { SearchResponse } from '../SearchResponse'; import { AutocompleteScopeApi, BaseItem } from './AutocompleteApi'; import { GetSourcesParams } from './AutocompleteOptions'; diff --git a/packages/autocomplete-shared/src/index.ts b/packages/autocomplete-shared/src/index.ts index 22d8f2682..915857d10 100644 --- a/packages/autocomplete-shared/src/index.ts +++ b/packages/autocomplete-shared/src/index.ts @@ -10,6 +10,7 @@ export * from './isEqual'; export * from './MaybePromise'; export * from './noop'; export * from './safelyRunOnBrowser'; +export * from './SearchResponse'; export * from './UserAgent'; export * from './userAgents'; export * from './version'; diff --git a/packages/autocomplete-shared/src/preset-algolia/createRequester.ts b/packages/autocomplete-shared/src/preset-algolia/createRequester.ts index 72519d8b0..0b1c9e643 100644 --- a/packages/autocomplete-shared/src/preset-algolia/createRequester.ts +++ b/packages/autocomplete-shared/src/preset-algolia/createRequester.ts @@ -1,10 +1,10 @@ +import type { SearchResponse } from '../SearchResponse'; import { UserAgent } from '../UserAgent'; import { MultipleQueriesQuery, SearchClient, SearchForFacetValuesResponse, - SearchResponse, } from './algoliasearch'; export interface SearchParams { diff --git a/test/utils/createApiResponse.ts b/test/utils/createApiResponse.ts index 8d35b349b..96d26b5fc 100644 --- a/test/utils/createApiResponse.ts +++ b/test/utils/createApiResponse.ts @@ -1,7 +1,5 @@ -import { - SearchForFacetValuesResponse, - SearchResponse, -} from '@algolia/client-search'; +import type { SearchResponse } from '@algolia/autocomplete-shared'; +import { SearchForFacetValuesResponse } from '@algolia/client-search'; export function createSingleSearchResponse( subset: Partial> = {}