UNPKG

@yext/search-headless

Version:

A library for powering UI components for Yext Search integrations

553 lines 23.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); const searchUtilities = __importStar(require("./search-utilities")); const transform_filters_1 = require("./utils/transform-filters"); const searchType_1 = require("./models/utils/searchType"); const vertical_1 = require("./slices/vertical"); const universal_1 = require("./slices/universal"); const filters_1 = require("./slices/filters"); const directanswer_1 = require("./slices/directanswer"); const queryrules_1 = require("./slices/queryrules"); const searchstatus_1 = require("./slices/searchstatus"); const generativedirectanswer_1 = require("./slices/generativedirectanswer"); const vertical_2 = require("./models/slices/vertical"); /** * Provides the functionality for interacting with a Search experience. * * @public */ class SearchHeadless { constructor(config, core, stateManager, httpManager, additionalHttpHeaders) { this.config = config; this.core = core; this.stateManager = stateManager; this.httpManager = httpManager; this.additionalHttpHeaders = additionalHttpHeaders; /** * Common utility functions for manipulating Search-related data. */ this.utilities = searchUtilities; this.stateManager.dispatchEvent('meta/setExperienceKey', config.experienceKey); this.stateManager.dispatchEvent('meta/setLocale', config.locale); } /** * Sets {@link QueryState.isPagination} to the specified input. * * @param input - The input to set */ setIsPagination(input) { this.stateManager.dispatchEvent('query/setIsPagination', input); } /** * Sets {@link QueryState.input} to the specified input. * * @param input - The input to set */ setQuery(input) { this.stateManager.dispatchEvent('query/setInput', input); } /** * Sets {@link QueryState.queryTrigger} to the specified trigger. * * @param trigger - The query trigger to set */ setQueryTrigger(trigger) { this.stateManager.dispatchEvent('query/setTrigger', trigger); } /** * Sets {@link QueryState.querySource} to the specified source. * * @param source - The query source to set */ setQuerySource(source) { this.stateManager.dispatchEvent('query/setSource', source); } /** * Sets up Headless to manage the vertical indicated by the verticalKey. * * @param verticalKey - The vertical key to set */ setVertical(verticalKey) { this._resetSearcherStates(); this.stateManager.dispatchEvent('vertical/setVerticalKey', verticalKey); this.stateManager.dispatchEvent('meta/setSearchType', searchType_1.SearchTypeEnum.Vertical); } /** * Sets up Headless to manage universal searches. */ setUniversal() { this._resetSearcherStates(); this.stateManager.dispatchEvent('vertical/setVerticalKey', undefined); this.stateManager.dispatchEvent('meta/setSearchType', searchType_1.SearchTypeEnum.Universal); } /** * Resets the direct answer, filters, query rules, search status, vertical, universal, * and generative direct answer states to their initial values. */ _resetSearcherStates() { this.stateManager.dispatchEvent('set-state', Object.assign(Object.assign({}, this.state), { directAnswer: directanswer_1.initialState, filters: filters_1.initialState, queryRules: queryrules_1.initialState, searchStatus: searchstatus_1.initialState, vertical: vertical_1.initialState, universal: universal_1.initialState, generativeDirectAnswer: generativedirectanswer_1.initialState })); } /** * Sets {@link VerticalSearchState.limit} to the specified limit. * * @param limit - The vertical limit to set */ setVerticalLimit(limit) { this.stateManager.dispatchEvent('vertical/setLimit', limit); } /** * Sets {@link UniversalSearchState.limit} to the specified limit. * * @param limit - The universal limit to set */ setUniversalLimit(limit) { this.stateManager.dispatchEvent('universal/setLimit', limit); } /** * Sets {@link VerticalSearchState.offset} to the specified offset. * * @param offset - The vertical offset to set */ setOffset(offset) { this.stateManager.dispatchEvent('vertical/setOffset', offset); } /** * Sets {@link FiltersState."static"} to the specified filters. * * @param filters - The static filters to set */ setStaticFilters(filters) { this.stateManager.dispatchEvent('filters/setStatic', filters); } /** * Sets {@link FiltersState.facets} to the specified facets. * * @param facets - The facets to set */ setFacets(facets) { this.stateManager.dispatchEvent('filters/setFacets', facets); } /** * Unselects all {@link FiltersState.facets | facets}. */ resetFacets() { this.stateManager.dispatchEvent('filters/resetFacets'); } /** * Sets {@link SpellCheckState.enabled} to the specified boolean value. * * @param enabled - Whether or not spellcheck should be set to enabled */ setSpellCheckEnabled(enabled) { this.stateManager.dispatchEvent('spellCheck/setEnabled', enabled); } /** * Sets {@link SessionTrackingState.enabled} to the specified boolean value. * * @param enabled - Whether or not session tracking should be set to enabled */ setSessionTrackingEnabled(enabled) { this.stateManager.dispatchEvent('sessionTracking/setEnabled', enabled); } /** * Sets {@link SessionTrackingState.sessionId} to the specified ID. * * @param sessionId - The session ID to set */ setSessionId(sessionId) { this.stateManager.dispatchEvent('sessionTracking/setSessionId', sessionId); } /** * Sets the alternativeVerticals for {@link VerticalSearchState.noResults} to the * specified verticals. * * @param alternativeVerticals - The alternative verticals to set */ setAlternativeVerticals(alternativeVerticals) { this.stateManager.dispatchEvent('vertical/setAlternativeVerticals', alternativeVerticals); } /** * Sets {@link VerticalSearchState.sortBys} to the specified sortBys. * * @param sortBys - The sortBys to set */ setSortBys(sortBys) { this.stateManager.dispatchEvent('vertical/setSortBys', sortBys); } /** * Sets {@link MetaState.context} to the specified context. * * @param context - The context to set */ setContext(context) { this.stateManager.dispatchEvent('meta/setContext', context); } /** * Sets {@link MetaState.referrerPageUrl} to the specified URL. * * @param referrerPageUrl - The referring page URL to set */ setReferrerPageUrl(referrerPageUrl) { this.stateManager.dispatchEvent('meta/setReferrerPageUrl', referrerPageUrl); } /** * Sets {@link LocationState.userLocation} to the specified latitude and * longitude. * * @param latLong - The user location to set */ setUserLocation(latLong) { this.stateManager.dispatchEvent('location/setUserLocation', latLong); } /** * Sets the {@link State} to the specified state. * * @param state - The state to set */ setState(state) { this.stateManager.dispatchEvent('set-state', state); } /** * Sets {@link UniversalSearchState.restrictVerticals} to the specified vertical * keys. * * @param restrictVerticals - The new verticals to restrict a universal search */ setRestrictVerticals(restrictVerticals) { this.stateManager.dispatchEvent('universal/setRestrictVerticals', restrictVerticals); } /** * Sets {@link VerticalSearchState.locationRadius} to the specified number of meters. * * @param locationRadius - The radius (in meters) to filter vertical searches by. */ setLocationRadius(locationRadius) { this.stateManager.dispatchEvent('vertical/setLocationRadius', locationRadius); } /** * Gets the current state of the SearchHeadless instance. */ get state() { return this.stateManager.getState(); } /** * Adds a listener for a specific state value of type T. * * @param listener - The listener to add * @returns The function for removing the added listener */ addListener(listener) { return this.stateManager.addListener(listener); } /** * Submits a question to the Search API with the specified request data. * * @param request - The data for the network request * @returns A Promise of a {@link QuestionSubmissionResponse} from the Search API */ async submitQuestion(request) { return this.core.submitQuestion(Object.assign(Object.assign({}, request), { additionalHttpHeaders: this.additionalHttpHeaders })); } /** * Performs a Search across all verticals with relevant parts of the * state used as input to the search. Updates the state with the response data. * * @returns A Promise of a {@link UniversalSearchResponse} from the Search API */ async executeUniversalQuery() { if (this.state.meta.searchType !== searchType_1.SearchTypeEnum.Universal) { console.error('The meta.searchType must be set to \'universal\' for universal search. ' + 'Set the searchType to universal by calling `setUniversal()`'); return; } const thisRequestId = this.httpManager.updateRequestId('universalQuery'); this.stateManager.dispatchEvent('searchStatus/setIsLoading', true); const { input, querySource, queryTrigger } = this.state.query; const skipSpellCheck = !this.state.spellCheck.enabled; const sessionTrackingEnabled = this.state.sessionTracking.enabled; const { limit, restrictVerticals } = this.state.universal; const sessionId = this.state.sessionTracking.sessionId; const { referrerPageUrl, context } = this.state.meta; const { userLocation } = this.state.location; const request = Object.assign(Object.assign({ query: input || '', querySource, queryTrigger, skipSpellCheck }, (sessionTrackingEnabled && { sessionId })), { sessionTrackingEnabled, limit, location: userLocation, context, referrerPageUrl, restrictVerticals, additionalHttpHeaders: this.additionalHttpHeaders }); let response; try { response = await this.core.universalSearch(request); } catch (e) { const isLatestResponse = this.httpManager.processRequestId('universalQuery', thisRequestId); if (isLatestResponse) { this.stateManager.dispatchEvent('searchStatus/setIsLoading', false); } return Promise.reject(e); } const isLatestResponse = this.httpManager.processRequestId('universalQuery', thisRequestId); if (!isLatestResponse) { return response; } this.stateManager.dispatchEvent('universal/setVerticals', response.verticalResults); this.stateManager.dispatchEvent('query/setQueryId', response.queryId); this.stateManager.dispatchEvent('query/setMostRecentSearch', input); this.stateManager.dispatchEvent('spellCheck/setResult', response.spellCheck); this.stateManager.dispatchEvent('query/setSearchIntents', response.searchIntents || []); this.stateManager.dispatchEvent('location/setLocationBias', response.locationBias); this.stateManager.dispatchEvent('searchStatus/setIsLoading', false); this.stateManager.dispatchEvent('meta/setUUID', response.uuid); this.stateManager.dispatchEvent('directAnswer/setResult', response.directAnswer); this.stateManager.dispatchEvent('queryRules/setActions', response.queryRulesActionsData || []); return response; } /** * Performs an autocomplete request across all verticals using the query input * stored in state. * * @returns A Promise of an {@link AutocompleteResponse} from the Search API */ async executeUniversalAutocomplete() { const query = this.state.query.input || ''; return this.core.universalAutocomplete({ input: query, additionalHttpHeaders: this.additionalHttpHeaders }); } /** * Perform a Search for a single vertical with relevant parts of the * state used as input to the search. Updates the state with the response data. * * @returns A Promise of a {@link VerticalSearchResponse} from the Search API or * of undefined if there is no verticalKey defined in state */ async executeVerticalQuery() { var _a; if (this.state.meta.searchType !== searchType_1.SearchTypeEnum.Vertical) { console.error('The meta.searchType must be set to \'vertical\' for vertical search. ' + 'Set the searchType to vertical by calling `setVertical()`'); return; } const thisRequestId = this.httpManager.updateRequestId('verticalQuery'); const verticalKey = this.state.vertical.verticalKey; if (!verticalKey) { console.error('no verticalKey supplied for vertical search'); return; } this.stateManager.dispatchEvent('searchStatus/setIsLoading', true); const { input, isPagination, queryId, querySource, queryTrigger } = this.state.query; const skipSpellCheck = !this.state.spellCheck.enabled; const sessionTrackingEnabled = this.state.sessionTracking.enabled; const sessionId = this.state.sessionTracking.sessionId; const staticFilter = (0, transform_filters_1.transformFiltersToCoreFormat)(this.state.filters.static) || undefined; const facets = (_a = this.state.filters) === null || _a === void 0 ? void 0 : _a.facets; const { limit, offset, sortBys, locationRadius } = this.state.vertical; const { referrerPageUrl, context } = this.state.meta; const { userLocation } = this.state.location; const nextQueryId = isPagination ? queryId : undefined; const facetsToApply = facets === null || facets === void 0 ? void 0 : facets.map(facet => { return { fieldId: facet.fieldId, options: facet.options.filter(o => o.selected) }; }); const request = Object.assign(Object.assign({ query: input || '', querySource, queryTrigger, verticalKey, staticFilter, facets: facetsToApply, retrieveFacets: true, limit, offset, skipSpellCheck }, (sessionTrackingEnabled && { sessionId })), { sessionTrackingEnabled, location: userLocation, sortBys, context, referrerPageUrl, locationRadius, queryId: nextQueryId, additionalHttpHeaders: this.additionalHttpHeaders }); let response; try { response = await this.core.verticalSearch(request); } catch (e) { const isLatestResponse = this.httpManager.processRequestId('verticalQuery', thisRequestId); if (isLatestResponse) { this.stateManager.dispatchEvent('searchStatus/setIsLoading', false); } return Promise.reject(e); } const isLatestResponse = this.httpManager.processRequestId('verticalQuery', thisRequestId); if (!isLatestResponse) { return response; } this.stateManager.dispatchEvent('query/setQueryId', response.queryId); this.stateManager.dispatchEvent('query/setMostRecentSearch', input); this.stateManager.dispatchEvent('query/setIsPagination', false); this.stateManager.dispatchEvent('filters/setFacets', response.facets); this.stateManager.dispatchEvent('spellCheck/setResult', response.spellCheck); this.stateManager.dispatchEvent('query/setSearchIntents', response.searchIntents || []); this.stateManager.dispatchEvent('location/setLocationBias', response.locationBias); this.stateManager.dispatchEvent('directAnswer/setResult', response.directAnswer); this.stateManager.dispatchEvent('meta/setUUID', response.uuid); this.stateManager.dispatchEvent('searchStatus/setIsLoading', false); this.stateManager.dispatchEvent('vertical/handleSearchResponse', response); this.stateManager.dispatchEvent('queryRules/setActions', response.queryRulesActionsData || []); return response; } /** * Performs an autocomplete request for a single vertical using the query input * and vertical key stored in state. * * @returns A Promise of an {@link AutocompleteResponse} from the Search API or * of undefined if there is no verticalKey defined in state */ async executeVerticalAutocomplete() { if (this.state.meta.searchType !== searchType_1.SearchTypeEnum.Vertical) { console.error('The meta.searchType must be set to \'vertical\' for vertical autocomplete. ' + 'Set the searchType to vertical by calling `setVertical()`'); return; } const query = this.state.query.input || ''; const verticalKey = this.state.vertical.verticalKey; if (!verticalKey) { console.error('no verticalKey supplied for vertical autocomplete'); return; } return this.core.verticalAutocomplete({ input: query, verticalKey, additionalHttpHeaders: this.additionalHttpHeaders }); } /** * Performs a filtersearch request against specified fields within a single * vertical using the vertical key stored in state. * * @param query - The query for which to search * @param sectioned - Whether or not the results should be sectioned by field * @param fields - The entity fields to search * @returns A Promise of a {@link FilterSearchResponse} from the Search API or * of undefined if there is no verticalKey defined in state */ async executeFilterSearch(query, sectioned, fields) { if (this.state.meta.searchType !== searchType_1.SearchTypeEnum.Vertical) { console.error('The meta.searchType must be set to \'vertical\' for filter search. ' + 'Set the searchType to vertical by calling `setVertical()`'); return; } const verticalKey = this.state.vertical.verticalKey; if (!verticalKey) { console.error('no verticalKey supplied for filter search'); return; } return this.core.filterSearch({ input: query, verticalKey, sessionTrackingEnabled: this.state.sessionTracking.enabled, sectioned, fields, additionalHttpHeaders: this.additionalHttpHeaders }); } /** * Sets a specified facet option to be selected or unselected. * * @param fieldId - The fieldId for the facet * @param facetOption - The option of the facet to select * @param selected - Whether or not the facet option should be selected */ setFacetOption(fieldId, facetOption, selected) { const payload = { shouldSelect: selected, fieldId, facetOption }; this.stateManager.dispatchEvent('filters/setFacetOption', payload); } /** * Sets a static filter option and whether or not it is selected in state. * * @param filter - The static filter and whether it is selected */ setFilterOption(filter) { this.stateManager.dispatchEvent('filters/setFilterOption', filter); } /** * Perform a generativeDirectAnswer request to the query most recent search stored in state. * * @returns A Promise of a {@link GenerativeDirectAnswerResponse} from the Search API or * of undefined if there is no results defined in state */ async executeGenerativeDirectAnswer() { const thisRequestId = this.httpManager.updateRequestId('generativeDirectAnswer'); const searchId = this.state.meta.uuid; const searchTerm = this.state.query.mostRecentSearch; let results; if (this.state.meta.searchType === searchType_1.SearchTypeEnum.Vertical) { if ((0, vertical_2.isVerticalResults)(this.state.vertical)) { results = [this.state.vertical]; } } else { results = this.state.universal.verticals; } if (!searchId) { console.error('no search id supplied for generative direct answer'); return; } if (!searchTerm) { console.error('no search term supplied for generative direct answer'); return; } if (!results || results.length === 0) { console.error('no results supplied for generative direct answer'); return; } this.stateManager.dispatchEvent('generativeDirectAnswer/setIsLoading', true); let response; try { response = await this.core.generativeDirectAnswer({ searchId, results, searchTerm, additionalHttpHeaders: this.additionalHttpHeaders }); } catch (e) { const isLatestResponse = this.httpManager.processRequestId('generativeDirectAnswer', thisRequestId); if (isLatestResponse) { this.stateManager.dispatchEvent('generativeDirectAnswer/setResponse', undefined); this.stateManager.dispatchEvent('generativeDirectAnswer/setIsLoading', false); } return Promise.reject(e); } const isLatestResponse = this.httpManager.processRequestId('generativeDirectAnswer', thisRequestId); if (!isLatestResponse) { return response; } this.stateManager.dispatchEvent('generativeDirectAnswer/setResponse', response); this.stateManager.dispatchEvent('generativeDirectAnswer/setIsLoading', false); return response; } } exports.default = SearchHeadless; //# sourceMappingURL=search-headless.js.map