react-instantsearch-core 7.45.0 → 7.47.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.
@@ -1,4 +1,4 @@
1
- /*! React InstantSearch Core 7.45.0 | © Algolia, Inc. and contributors; MIT License | https://github.com/algolia/instantsearch */
1
+ /*! React InstantSearch Core 7.47.0 | © Algolia, Inc. and contributors; MIT License | https://github.com/algolia/instantsearch */
2
2
  (function (global, factory) {
3
3
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react')) :
4
4
  typeof define === 'function' && define.amd ? define(['exports', 'react'], factory) :
@@ -24,7 +24,7 @@
24
24
 
25
25
  var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
26
26
 
27
- var version$2 = '7.45.0';
27
+ var version$2 = '7.47.0';
28
28
 
29
29
  function _define_property(obj, key, value) {
30
30
  if (key in obj) {
@@ -6728,23 +6728,33 @@
6728
6728
  }
6729
6729
 
6730
6730
  var nextMicroTask = Promise.resolve();
6731
- function defer(callback) {
6731
+ function defer(callback, // arguments: the first caller of the window decides what the single run
6732
+ // receives. Pass this to fold every later argument into the pending ones
6733
+ // instead.
6734
+ mergeArguments) {
6732
6735
  var progress = null;
6733
6736
  var cancelled = false;
6737
+ var pendingArgs = null;
6734
6738
  var fn = function fn() {
6735
6739
  for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){
6736
6740
  args[_key] = arguments[_key];
6737
6741
  }
6738
6742
  if (progress !== null) {
6743
+ if (mergeArguments && pendingArgs !== null) {
6744
+ pendingArgs = mergeArguments(pendingArgs, args);
6745
+ }
6739
6746
  return;
6740
6747
  }
6748
+ pendingArgs = args;
6741
6749
  progress = nextMicroTask.then(function() {
6742
6750
  progress = null;
6751
+ var runArgs = pendingArgs;
6752
+ pendingArgs = null;
6743
6753
  if (cancelled) {
6744
6754
  cancelled = false;
6745
6755
  return;
6746
6756
  }
6747
- callback.apply(void 0, _to_consumable_array(args));
6757
+ callback.apply(void 0, _to_consumable_array(runArgs));
6748
6758
  });
6749
6759
  };
6750
6760
  fn.wait = function() {
@@ -7642,22 +7652,18 @@
7642
7652
  clearTimeout(cleanupTimerRef.current);
7643
7653
  // Warning: if an unstable function prop is provided, `dequal` is not able
7644
7654
  // to keep its reference and therefore will consider that props did change.
7645
- // This could unsollicitely remove/add the widget, therefore forget its state,
7646
- // and could be a source of confusion.
7655
+ // This unintentionally replaces the widget, which is wasteful (it causes
7656
+ // a search), even though `updateWidget` below keeps its state.
7647
7657
  // If users face this issue, we should advise them to provide stable function
7648
7658
  // references.
7649
7659
  var arePropsEqual = dequal(props, prevPropsRef.current);
7650
- // If props did change, then we execute the cleanup function instantly
7651
- // and then add the widget back. This lets us add the widget without
7660
+ // If props did change, then we replace the widget instantly instead of
7652
7661
  // waiting for the scheduled cleanup function to finish (that we canceled
7653
- // above).
7662
+ // above). `updateWidget` hands the previous widget's `uiState` over to the
7663
+ // new one, so that a parameter change doesn't reset the state the widget
7664
+ // still owns — which would otherwise break routing.
7654
7665
  if (!arePropsEqual) {
7655
- parentIndex.removeWidgets([
7656
- previousWidget
7657
- ]);
7658
- parentIndex.addWidgets([
7659
- widget
7660
- ]);
7666
+ parentIndex.updateWidget(previousWidget, widget);
7661
7667
  }
7662
7668
  }
7663
7669
  return function() {
@@ -8014,6 +8020,11 @@
8014
8020
  return undefined;
8015
8021
  }
8016
8022
 
8023
+ function reduceChildrenUiState(widgets, uiState, widgetUiStateOptions) {
8024
+ return widgets.reduce(function(state, widget) {
8025
+ return widget.getWidgetUiState ? widget.getWidgetUiState(state, widgetUiStateOptions) : state;
8026
+ }, uiState);
8027
+ }
8017
8028
  function createFeedContainer(feedID, parentIndex, instantSearchInstance) {
8018
8029
  var localWidgets = [];
8019
8030
  var initialized = false;
@@ -8215,13 +8226,10 @@
8215
8226
  },
8216
8227
  getWidgetUiState: function getWidgetUiState(uiState) {
8217
8228
  var helper = parentIndex.getHelper();
8218
- var widgetUiStateOptions = {
8229
+ return reduceChildrenUiState(localWidgets, uiState, {
8219
8230
  searchParameters: helper.state,
8220
8231
  helper: helper
8221
- };
8222
- return localWidgets.reduce(function(state, widget) {
8223
- return widget.getWidgetUiState ? widget.getWidgetUiState(state, widgetUiStateOptions) : state;
8224
- }, uiState);
8232
+ });
8225
8233
  },
8226
8234
  getWidgetSearchParameters: function getWidgetSearchParameters(searchParameters, param) {
8227
8235
  var uiState = param.uiState;
@@ -8231,6 +8239,73 @@
8231
8239
  }) : params;
8232
8240
  }, searchParameters);
8233
8241
  },
8242
+ updateWidget: function updateWidget(previousWidget, nextWidget) {
8243
+ var helper = parentIndex.getHelper();
8244
+ // The `uiState` the children own, read before the previous widget is
8245
+ // detached, so that the state it owns can be handed over to the next one.
8246
+ var previousUiState = helper ? reduceChildrenUiState(localWidgets, {}, {
8247
+ searchParameters: helper.state,
8248
+ helper: helper
8249
+ }) : {};
8250
+ previousWidget.parent = undefined;
8251
+ nextWidget.parent = container;
8252
+ // The next widget takes the place of the previous one, so that the order
8253
+ // in which children contribute to the search parameters is unchanged.
8254
+ var nextWidgets = localWidgets.slice();
8255
+ var position = nextWidgets.indexOf(previousWidget);
8256
+ if (position === -1) {
8257
+ nextWidgets.push(nextWidget);
8258
+ } else {
8259
+ nextWidgets[position] = nextWidget;
8260
+ }
8261
+ localWidgets = nextWidgets;
8262
+ if (!helper || !initialized) {
8263
+ return container;
8264
+ }
8265
+ // We still dispose the previous widget, for its side effects and so that
8266
+ // it drops the search parameters it declared on the parent helper.
8267
+ var cleanedState = helper.state;
8268
+ if (previousWidget.dispose) {
8269
+ var next = previousWidget.dispose({
8270
+ helper: helper,
8271
+ state: cleanedState,
8272
+ recommendState: helper.recommendState,
8273
+ parent: container
8274
+ });
8275
+ if (next && !_instanceof(next, algoliasearchHelper.RecommendParameters)) {
8276
+ cleanedState = next;
8277
+ }
8278
+ }
8279
+ // We hand the previous `uiState` over to the children, then read the
8280
+ // `uiState` back from them, so that state no mounted child claims anymore
8281
+ // is dropped. This mirrors the index widget's `updateWidget`.
8282
+ var narrowedUiState = reduceChildrenUiState(localWidgets, {}, {
8283
+ searchParameters: container.getWidgetSearchParameters(cleanedState, {
8284
+ uiState: previousUiState
8285
+ }),
8286
+ helper: helper
8287
+ });
8288
+ // The search parameters are then computed again from that narrowed
8289
+ // `uiState`, so that they can't hold state the `uiState` doesn't describe.
8290
+ var newState = container.getWidgetSearchParameters(cleanedState, {
8291
+ uiState: narrowedUiState
8292
+ });
8293
+ if (nextWidget.getRenderState) {
8294
+ var renderState = nextWidget.getRenderState(instantSearchInstance.renderState[container.getIndexId()] || {}, createInitArgs(instantSearchInstance, container, instantSearchInstance._initialUiState));
8295
+ storeRenderState({
8296
+ renderState: renderState,
8297
+ instantSearchInstance: instantSearchInstance,
8298
+ parent: container
8299
+ });
8300
+ }
8301
+ if (nextWidget.init) {
8302
+ nextWidget.init(createInitArgs(instantSearchInstance, container, instantSearchInstance._initialUiState));
8303
+ }
8304
+ if (newState !== helper.state) {
8305
+ helper.setState(newState);
8306
+ }
8307
+ return container;
8308
+ },
8234
8309
  refreshUiState: function refreshUiState() {
8235
8310
  // no-op: FeedContainer doesn't own UI state
8236
8311
  },
@@ -8592,6 +8667,11 @@
8592
8667
  var helper = null;
8593
8668
  var derivedHelper = null;
8594
8669
  var lastValidSearchParameters = null;
8670
+ // The error this index has already rolled back for. Renders also happen for
8671
+ // reasons unrelated to a search (a chat panel opening, a recommend result,
8672
+ // the stalled timer), and rolling back on every one of them would discard
8673
+ // state written after the failed search.
8674
+ var restoredForError;
8595
8675
  var recomputeLocalRequestDependencies = function recomputeLocalRequestDependencies() {
8596
8676
  if (localInstantSearchInstance) {
8597
8677
  recomputeInstantSearchRequestDependencies(localInstantSearchInstance);
@@ -8731,6 +8811,92 @@
8731
8811
  }
8732
8812
  return this;
8733
8813
  },
8814
+ updateWidget: function updateWidget(previousWidget, nextWidget) {
8815
+ if (typeof previousWidget.dispose !== 'function') {
8816
+ throw new Error(withUsage$u('The widget definition expects a `dispose` method.'));
8817
+ }
8818
+ if (typeof nextWidget.init !== 'function' && typeof nextWidget.render !== 'function') {
8819
+ throw new Error(withUsage$u('The widget definition expects a `render` and/or an `init` method.'));
8820
+ }
8821
+ // The `uiState` as it is before the previous widget is detached, so that
8822
+ // the state it owns can be handed over to the next widget.
8823
+ var previousUiState = localUiState;
8824
+ previousWidget.parent = undefined;
8825
+ nextWidget.parent = this;
8826
+ if (!isIndexWidget(nextWidget)) {
8827
+ addWidgetId(nextWidget);
8828
+ }
8829
+ // The next widget takes the place of the previous one, so that the order
8830
+ // in which widgets contribute to the search parameters is unchanged.
8831
+ var nextWidgets = localWidgets.slice();
8832
+ var position = nextWidgets.indexOf(previousWidget);
8833
+ if (position === -1) {
8834
+ nextWidgets.push(nextWidget);
8835
+ } else {
8836
+ nextWidgets[position] = nextWidget;
8837
+ }
8838
+ localWidgets = nextWidgets;
8839
+ recomputeLocalRequestDependencies();
8840
+ if (!localInstantSearchInstance) {
8841
+ return this;
8842
+ }
8843
+ // We still dispose the previous widget, for its side effects. The search
8844
+ // state it returns is only used as a base when shared state isn't preserved
8845
+ // on unmount, like in `removeWidgets`.
8846
+ var disposedState = previousWidget.dispose({
8847
+ helper: helper,
8848
+ state: helper.state,
8849
+ recommendState: helper.recommendState,
8850
+ parent: this
8851
+ });
8852
+ var cleanedRecommendState = _instanceof(disposedState, algoliasearchHelper.RecommendParameters) ? disposedState : helper.recommendState;
8853
+ var cleanedSearchState = disposedState && !_instanceof(disposedState, algoliasearchHelper.RecommendParameters) ? disposedState : helper.state;
8854
+ var initialSearchParameters = localInstantSearchInstance.future.preserveSharedStateOnUnmount ? new algoliasearchHelper.SearchParameters({
8855
+ index: this.getIndexName()
8856
+ }) : cleanedSearchState;
8857
+ // We hand the previous `uiState` over to the next widgets, then read the
8858
+ // `uiState` back from them. Widgets only pick up the state they claim, so
8859
+ // this drops state that no mounted widget owns anymore (an attribute that
8860
+ // changed, for instance) instead of seeding it with `previousUiState`.
8861
+ localUiState = getLocalWidgetsUiState(localWidgets, {
8862
+ searchParameters: getLocalWidgetsSearchParameters(localWidgets, {
8863
+ uiState: previousUiState,
8864
+ initialSearchParameters: initialSearchParameters
8865
+ }),
8866
+ helper: helper
8867
+ });
8868
+ // The search parameters are then computed again from that narrowed
8869
+ // `uiState`, so that they can't hold state the `uiState` doesn't describe.
8870
+ var newState = getLocalWidgetsSearchParameters(localWidgets, {
8871
+ uiState: localUiState,
8872
+ initialSearchParameters: initialSearchParameters
8873
+ });
8874
+ privateHelperSetState(helper, {
8875
+ state: newState,
8876
+ recommendState: getLocalWidgetsRecommendParameters(localWidgets, {
8877
+ uiState: localUiState,
8878
+ initialRecommendParameters: cleanedRecommendState
8879
+ }),
8880
+ _uiState: localUiState
8881
+ });
8882
+ if (nextWidget.getRenderState) {
8883
+ var renderState = nextWidget.getRenderState(localInstantSearchInstance.renderState[this.getIndexId()] || {}, createInitArgs(localInstantSearchInstance, this, localInstantSearchInstance._initialUiState));
8884
+ storeRenderState({
8885
+ renderState: renderState,
8886
+ instantSearchInstance: localInstantSearchInstance,
8887
+ parent: this
8888
+ });
8889
+ }
8890
+ if (nextWidget.init) {
8891
+ nextWidget.init(createInitArgs(localInstantSearchInstance, this, localInstantSearchInstance._initialUiState));
8892
+ }
8893
+ if (isolated) {
8894
+ this.scheduleLocalSearch();
8895
+ } else {
8896
+ localInstantSearchInstance.scheduleSearch();
8897
+ }
8898
+ return this;
8899
+ },
8734
8900
  removeWidgets: function removeWidgets(widgets) {
8735
8901
  var _this = this;
8736
8902
  if (!Array.isArray(widgets)) {
@@ -8982,7 +9148,10 @@
8982
9148
  var instantSearchInstance = param.instantSearchInstance;
8983
9149
  // we can't attach a listener to the error event of search, as the error
8984
9150
  // then would no longer be thrown for global handlers.
8985
- if (instantSearchInstance.status === 'error' && !instantSearchInstance.mainHelper.hasPendingRequests() && lastValidSearchParameters) {
9151
+ if (instantSearchInstance.status !== 'error') {
9152
+ restoredForError = undefined;
9153
+ } else if (instantSearchInstance.error !== restoredForError && !instantSearchInstance.mainHelper.hasPendingRequests() && lastValidSearchParameters) {
9154
+ restoredForError = instantSearchInstance.error;
8986
9155
  helper.setState(lastValidSearchParameters);
8987
9156
  }
8988
9157
  // We only render index widgets if there are no results.
@@ -9249,7 +9418,7 @@
9249
9418
  });
9250
9419
  }
9251
9420
 
9252
- var version = '4.112.0';
9421
+ var version = '4.114.0';
9253
9422
 
9254
9423
  var ANONYMOUS_TOKEN_COOKIE_KEY = '_ALGOLIA';
9255
9424
  function getCookie(name) {
@@ -11161,6 +11330,14 @@
11161
11330
  instantSearchInstance: _this
11162
11331
  });
11163
11332
  _this.emit('render');
11333
+ }, // status reset accumulates instead of letting the first caller decide: a
11334
+ // render scheduled for a reason unrelated to the search must not cancel the
11335
+ // one a search result asks for, or it would strand the status on `loading`.
11336
+ function(param, param1) {
11337
+ var _param = _sliced_to_array(param, 1), tmp = _param[0], shouldResetStatus = tmp === void 0 ? true : tmp, _param1 = _sliced_to_array(param1, 1), tmp1 = _param1[0], nextShouldResetStatus = tmp1 === void 0 ? true : tmp1;
11338
+ return [
11339
+ shouldResetStatus || nextShouldResetStatus
11340
+ ];
11164
11341
  })), _define_property(_this, "onInternalStateChange", defer(function() {
11165
11342
  var nextUiState = _this.mainIndex.getWidgetUiState({});
11166
11343
  _this.middleware.forEach(function(param) {
@@ -12451,6 +12628,37 @@
12451
12628
  var isPartTool = function isPartTool(part) {
12452
12629
  return startsWith(part.type, 'tool-');
12453
12630
  };
12631
+ var TOOL_PART_PREFIX = 'tool-';
12632
+ /**
12633
+ * Resolves the tool a message part belongs to, from either a part type
12634
+ * (`tool-algolia_search_index`) or a bare tool name.
12635
+ *
12636
+ * An exact registration wins. Otherwise only tools whose `matchesToolName`
12637
+ * claims the name are considered, most specific first, for servers that name a
12638
+ * call after the registered tool: the Algolia MCP Server appends the index name
12639
+ * (`algolia_search_index_products`).
12640
+ *
12641
+ * Generic over the tool shape: the renderer, the loader, the widget and the
12642
+ * connector hold different subsets of the tool contract.
12643
+ */ var findTool = function findTool(partType, tools) {
12644
+ var toolName = startsWith(partType, TOOL_PART_PREFIX) ? partType.slice(TOOL_PART_PREFIX.length) : partType;
12645
+ if (tools[toolName]) {
12646
+ return tools[toolName];
12647
+ }
12648
+ var claimants = Object.keys(tools).filter(function(key) {
12649
+ var _tools_key_matchesToolName, _tools_key;
12650
+ return Boolean((_tools_key = tools[key]) === null || _tools_key === void 0 ? void 0 : (_tools_key_matchesToolName = _tools_key.matchesToolName) === null || _tools_key_matchesToolName === void 0 ? void 0 : _tools_key_matchesToolName.call(_tools_key, toolName));
12651
+ });
12652
+ if (claimants.length === 0) {
12653
+ return undefined;
12654
+ }
12655
+ // Most specific claim wins, ties by name, so the winner doesn't depend on
12656
+ // registration order.
12657
+ claimants.sort(function(a, b) {
12658
+ return b.length - a.length || (a < b ? -1 : 1);
12659
+ });
12660
+ return tools[claimants[0]];
12661
+ };
12454
12662
 
12455
12663
  var createRecords = function createRecords() {
12456
12664
  return Object.create(null);
@@ -14410,6 +14618,13 @@
14410
14618
  }
14411
14619
 
14412
14620
  var SearchIndexToolType = 'algolia_search_index';
14621
+ /**
14622
+ * Whether `toolName` is the search tool as the Algolia MCP Server exposes it:
14623
+ * one tool per index, named after the index it searches
14624
+ * (`algolia_search_index_products`).
14625
+ */ var matchesSearchIndexToolName = function matchesSearchIndexToolName(toolName) {
14626
+ return toolName.startsWith("".concat(SearchIndexToolType, "_"));
14627
+ };
14413
14628
 
14414
14629
  function flat(arr) {
14415
14630
  return arr.reduce(function(acc, array) {
@@ -14611,7 +14826,7 @@
14611
14826
  name: 'chat',
14612
14827
  connector: true
14613
14828
  });
14614
- var OPEN_STATE_CACHE_KEY = 'instantsearch-chat-open-state';
14829
+ var OPEN_STATE_CACHE_KEY$1 = 'instantsearch-chat-open-state';
14615
14830
  function normalizePersistence(persistence, hasCustomChat) {
14616
14831
  if (hasCustomChat) {
14617
14832
  return {
@@ -14637,7 +14852,7 @@
14637
14852
  };
14638
14853
  }
14639
14854
  function getOpenStateCacheKey(type) {
14640
- return "".concat(OPEN_STATE_CACHE_KEY, "-").concat(type);
14855
+ return "".concat(OPEN_STATE_CACHE_KEY$1, "-").concat(type);
14641
14856
  }
14642
14857
  function readPersistedOpen(type) {
14643
14858
  try {
@@ -14669,6 +14884,11 @@
14669
14884
  return refinement.attribute;
14670
14885
  }));
14671
14886
  }
14887
+ /**
14888
+ * One Algolia `numericFilters` entry: `'price <= 1500'`. The operators are
14889
+ * exactly the set `helper.addNumericRefinement` accepts, and exactly the set
14890
+ * the Algolia MCP Server emits.
14891
+ */ var NUMERIC_FILTER = /^(.+?)\s*(<=|>=|!=|=|<|>)\s*(-?\d+(?:\.\d+)?)$/;
14672
14892
  function updateStateFromSearchToolInput(params, helper) {
14673
14893
  // clear all filters first
14674
14894
  var attributesToClear = getAttributesToClear$1({
@@ -14712,6 +14932,16 @@
14712
14932
  helper.toggleFacetRefinement(name, value);
14713
14933
  });
14714
14934
  }
14935
+ if (params.numericFilters) {
14936
+ params.numericFilters.forEach(function(filter) {
14937
+ var match = filter.match(NUMERIC_FILTER);
14938
+ if (!match) {
14939
+ return;
14940
+ }
14941
+ var _match = _sliced_to_array(match, 4), attribute = _match[1], operator = _match[2], value = _match[3];
14942
+ helper.addNumericRefinement(attribute, operator, Number(value));
14943
+ });
14944
+ }
14715
14945
  if (params.query) {
14716
14946
  helper.setQuery(params.query);
14717
14947
  }
@@ -14722,7 +14952,7 @@
14722
14952
  var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
14723
14953
  checkRendering(renderFn, withUsage$q());
14724
14954
  return function(widgetParams) {
14725
- var _ref = widgetParams || {}, _ref_resume = _ref.resume, resume = _ref_resume === void 0 ? false : _ref_resume, _ref_tools = _ref.tools, tools = _ref_tools === void 0 ? {} : _ref_tools, _ref_type = _ref.type, type = _ref_type === void 0 ? 'chat' : _ref_type, persistence = _ref.persistence, context = _ref.context, initialUserMessage = _ref.initialUserMessage, initialMessages = _ref.initialMessages, _ref_disableTriggerValidation = _ref.disableTriggerValidation, disableTriggerValidation = _ref_disableTriggerValidation === void 0 ? false : _ref_disableTriggerValidation, _ref_sendAutomaticallyWhen = _ref.sendAutomaticallyWhen, sendAutomaticallyWhen = _ref_sendAutomaticallyWhen === void 0 ? lastAssistantMessageIsCompleteWithToolCalls : _ref_sendAutomaticallyWhen, _ref_requiresSearch = _ref.requiresSearch, requiresSearch = _ref_requiresSearch === void 0 ? true : _ref_requiresSearch, options = _object_without_properties(_ref, [
14955
+ var _ref = widgetParams || {}, _ref_resume = _ref.resume, resume = _ref_resume === void 0 ? false : _ref_resume, tmp = _ref.tools, tools_ = tmp === void 0 ? {} : tmp, _ref_type = _ref.type, type = _ref_type === void 0 ? 'chat' : _ref_type, persistence = _ref.persistence, context = _ref.context, initialUserMessage = _ref.initialUserMessage, initialMessages = _ref.initialMessages, _ref_disableTriggerValidation = _ref.disableTriggerValidation, disableTriggerValidation = _ref_disableTriggerValidation === void 0 ? false : _ref_disableTriggerValidation, _ref_sendAutomaticallyWhen = _ref.sendAutomaticallyWhen, sendAutomaticallyWhen = _ref_sendAutomaticallyWhen === void 0 ? lastAssistantMessageIsCompleteWithToolCalls : _ref_sendAutomaticallyWhen, _ref_requiresSearch = _ref.requiresSearch, requiresSearch = _ref_requiresSearch === void 0 ? true : _ref_requiresSearch, options = _object_without_properties(_ref, [
14726
14956
  "resume",
14727
14957
  "tools",
14728
14958
  "type",
@@ -14735,11 +14965,12 @@
14735
14965
  "requiresSearch"
14736
14966
  ]);
14737
14967
  var normalizedPersistence = normalizePersistence(persistence, 'chat' in options);
14738
- // Compatibility shim with Algolia MCP Server search tool, which suffixes
14739
- // the tool name with the index name (`searchIndex_products`).
14740
- var resolveTool = function resolveTool(toolName) {
14741
- return tools[toolName] || (toolName.startsWith("".concat(SearchIndexToolType, "_")) ? tools[SearchIndexToolType] : undefined);
14742
- };
14968
+ // The Algolia MCP Server exposes the search tool once per index and names
14969
+ // it after the index (`algolia_search_index_products`). A `matchesToolName`
14970
+ // set by the user wins, as does a tool registered under the derived name.
14971
+ var tools = tools_[SearchIndexToolType] && tools_[SearchIndexToolType].matchesToolName === undefined ? _object_spread_props(_object_spread({}, tools_), _define_property({}, SearchIndexToolType, _object_spread_props(_object_spread({}, tools_[SearchIndexToolType]), {
14972
+ matchesToolName: matchesSearchIndexToolName
14973
+ }))) : tools_;
14743
14974
  var _chatInstance;
14744
14975
  var input = '';
14745
14976
  var open = false;
@@ -14767,21 +14998,44 @@
14767
14998
  return unsubscribe();
14768
14999
  });
14769
15000
  };
14770
- // Extract suggestions from the last assistant message's data-suggestions part
14771
- var getSuggestionsFromMessages = function getSuggestionsFromMessages(messages) {
14772
- // Find the last assistant message (iterate from end)
14773
- var lastAssistantMessage = _to_consumable_array(messages).reverse().find(function(message) {
15001
+ var findSuggestionsPart = function findSuggestionsPart(message) {
15002
+ var _message_parts;
15003
+ return message === null || message === void 0 ? void 0 : (_message_parts = message.parts) === null || _message_parts === void 0 ? void 0 : _message_parts.find(function(part) {
15004
+ var _part_data;
15005
+ return 'type' in part && part.type === 'data-suggestions' && 'data' in part && Array.isArray((_part_data = part.data) === null || _part_data === void 0 ? void 0 : _part_data.suggestions);
15006
+ });
15007
+ };
15008
+ var findLastAssistantMessage = function findLastAssistantMessage(messages) {
15009
+ return _to_consumable_array(messages).reverse().find(function(message) {
14774
15010
  return message.role === 'assistant' && message.parts;
14775
15011
  });
14776
- if (!(lastAssistantMessage === null || lastAssistantMessage === void 0 ? void 0 : lastAssistantMessage.parts)) {
14777
- return undefined;
15012
+ };
15013
+ // Extract suggestions from the last assistant message's data-suggestions part
15014
+ var getSuggestionsFromMessages = function getSuggestionsFromMessages(messages) {
15015
+ var _findSuggestionsPart;
15016
+ return (_findSuggestionsPart = findSuggestionsPart(findLastAssistantMessage(messages))) === null || _findSuggestionsPart === void 0 ? void 0 : _findSuggestionsPart.data.suggestions;
15017
+ };
15018
+ // "Still coming" has to be inferred: the turn is running and has no
15019
+ // `data-suggestions` part yet. Expecting any at all needs evidence, or an
15020
+ // agent that never sends them would sit under a placeholder forever.
15021
+ var getSuggestionsStatus = function getSuggestionsStatus(messages) {
15022
+ var _lastAssistantMessage_metadata;
15023
+ var status = _chatInstance.status;
15024
+ if (status !== 'submitted' && status !== 'streaming') {
15025
+ return 'idle';
14778
15026
  }
14779
- // Find the data-suggestions part
14780
- var suggestionsPart = lastAssistantMessage.parts.find(function(part) {
14781
- var _part_data;
14782
- return 'type' in part && part.type === 'data-suggestions' && 'data' in part && Array.isArray((_part_data = part.data) === null || _part_data === void 0 ? void 0 : _part_data.suggestions);
15027
+ var lastAssistantMessage = findLastAssistantMessage(messages);
15028
+ if (findSuggestionsPart(lastAssistantMessage)) {
15029
+ return 'idle';
15030
+ }
15031
+ var declaresSuggestions = (lastAssistantMessage === null || lastAssistantMessage === void 0 ? void 0 : (_lastAssistantMessage_metadata = lastAssistantMessage.metadata) === null || _lastAssistantMessage_metadata === void 0 ? void 0 : _lastAssistantMessage_metadata.suggestionsEnabled) === true;
15032
+ if (declaresSuggestions) {
15033
+ return 'loading';
15034
+ }
15035
+ var hasSuggestionsHistory = messages.some(function(message) {
15036
+ return message !== lastAssistantMessage && message.role === 'assistant' && Boolean(findSuggestionsPart(message));
14783
15037
  });
14784
- return suggestionsPart === null || suggestionsPart === void 0 ? void 0 : suggestionsPart.data.suggestions;
15038
+ return hasSuggestionsHistory ? 'loading' : 'idle';
14785
15039
  };
14786
15040
  var setMessages = function setMessages(messagesParam) {
14787
15041
  if (typeof messagesParam === 'function') {
@@ -14924,14 +15178,14 @@
14924
15178
  sendAutomaticallyWhen: sendAutomaticallyWhen,
14925
15179
  transport: transport,
14926
15180
  shouldRepairToolInput: function shouldRepairToolInput(toolName) {
14927
- var tool = resolveTool(toolName);
15181
+ var tool = findTool(toolName, tools);
14928
15182
  if (!tool) return true;
14929
15183
  return Boolean(tool.streamInput);
14930
15184
  },
14931
15185
  resolveCancelledToolOutput: function resolveCancelledToolOutput(param) {
14932
15186
  var toolName = param.toolName, toolCallId = param.toolCallId, input = param.input;
14933
- var _resolveTool;
14934
- var cancelOutput = (_resolveTool = resolveTool(toolName)) === null || _resolveTool === void 0 ? void 0 : _resolveTool.cancelOutput;
15187
+ var _findTool;
15188
+ var cancelOutput = (_findTool = findTool(toolName, tools)) === null || _findTool === void 0 ? void 0 : _findTool.cancelOutput;
14935
15189
  if (!cancelOutput) return undefined;
14936
15190
  try {
14937
15191
  var output = cancelOutput({
@@ -14948,7 +15202,7 @@
14948
15202
  },
14949
15203
  onToolCall: function onToolCall(param, submitToolResult) {
14950
15204
  var toolCall = param.toolCall;
14951
- var tool = resolveTool(toolCall.toolName);
15205
+ var tool = findTool(toolCall.toolName, tools);
14952
15206
  if (!tool) {
14953
15207
  return submitToolResult({
14954
15208
  output: 'No tool implemented for "'.concat(toolCall.toolName, '".'),
@@ -14998,7 +15252,8 @@
14998
15252
  // `open` is read by sibling widgets (e.g. `chatTrigger`) via the
14999
15253
  // shared `renderState`. Schedule a full re-render so they pick up
15000
15254
  // the new value instead of staying frozen on their initial state.
15001
- initOptions.instantSearchInstance.scheduleRender();
15255
+ // No search runs here, so it must not settle the main search.
15256
+ initOptions.instantSearchInstance.scheduleRender(false);
15002
15257
  };
15003
15258
  setOpen = function setOpen(nextOpen) {
15004
15259
  updateOpen(nextOpen, nextOpen && !open);
@@ -15051,14 +15306,15 @@
15051
15306
  // disable themselves, so a transition has to escape this widget's own
15052
15307
  // render. Message deltas deliberately don't: they stay local to keep
15053
15308
  // streaming cheap. The `status` setter notifies on every write, hence
15054
- // the comparison.
15309
+ // the comparison. A chat turn is not a search, so the render it
15310
+ // schedules must not settle the main search.
15055
15311
  var lastStatus = _chatInstance.status;
15056
15312
  var renderOnStatusChange = function renderOnStatusChange() {
15057
15313
  var statusChanged = _chatInstance.status !== lastStatus;
15058
15314
  lastStatus = _chatInstance.status;
15059
15315
  render();
15060
15316
  if (statusChanged) {
15061
- initOptions.instantSearchInstance.scheduleRender();
15317
+ initOptions.instantSearchInstance.scheduleRender(false);
15062
15318
  }
15063
15319
  };
15064
15320
  safelyRunOnBrowser(function() {
@@ -15088,8 +15344,10 @@
15088
15344
  renderFn(_object_spread_props(_object_spread({}, this.getWidgetRenderState(initOptions)), {
15089
15345
  instantSearchInstance: instantSearchInstance
15090
15346
  }), true);
15347
+ // A restored open panel is new to the sibling entry points, but it is
15348
+ // not a search result.
15091
15349
  if (open) {
15092
- instantSearchInstance.scheduleRender();
15350
+ instantSearchInstance.scheduleRender(false);
15093
15351
  }
15094
15352
  },
15095
15353
  render: function render(renderOptions) {
@@ -15178,6 +15436,7 @@
15178
15436
  '~isOpenStatePersistenceEnabled': normalizedPersistence.open,
15179
15437
  setMessages: setMessages,
15180
15438
  suggestions: getSuggestionsFromMessages(_chatInstance.messages),
15439
+ suggestionsStatus: getSuggestionsStatus(_chatInstance.messages),
15181
15440
  clearMessages: clearMessages,
15182
15441
  tools: toolsWithAddToolResult,
15183
15442
  records: records,
@@ -15253,9 +15512,48 @@
15253
15512
  * @internal
15254
15513
  */ var useIsHydrated = typeof React__namespace.useSyncExternalStore === 'function' ? useNativeIsHydrated : useLegacyIsHydrated;
15255
15514
 
15515
+ var OPEN_STATE_CACHE_KEY = 'instantsearch-chat-open-state';
15516
+ function isOpenStatePersistenceEnabled(props) {
15517
+ return props.persistence === undefined || props.persistence === true || _type_of(props.persistence) === 'object' && props.persistence.open === true;
15518
+ }
15519
+ function isMessagePersistenceEnabled(props) {
15520
+ return props.persistence === undefined || props.persistence === true || _type_of(props.persistence) === 'object' && props.persistence.messages === true;
15521
+ }
15522
+ function getMessagePersistenceKeySuffix(props) {
15523
+ return props.agentId ? "-".concat(props.agentId) : '';
15524
+ }
15525
+ function hasPersistedOpenState(type) {
15526
+ try {
15527
+ return sessionStorage.getItem("".concat(OPEN_STATE_CACHE_KEY, "-").concat(type)) === 'true';
15528
+ } catch (unused) {
15529
+ return false;
15530
+ }
15531
+ }
15256
15532
  function useChat(props, additionalWidgetProperties) {
15257
15533
  var isHydrated = useIsHydrated();
15534
+ var previousPropsRef = React.useRef(props);
15535
+ var previousChatStateRef = React.useRef(null);
15536
+ useIsomorphicLayoutEffect(function() {
15537
+ {
15538
+ var previousProps = previousPropsRef.current;
15539
+ var previousChatState = previousChatStateRef.current;
15540
+ if (previousChatState && !dequal(previousProps, props) && !('chat' in previousProps)) {
15541
+ var _props_type;
15542
+ var nextType = (_props_type = props.type) !== null && _props_type !== void 0 ? _props_type : 'chat';
15543
+ var losesOpenState = previousChatState.open && (!isOpenStatePersistenceEnabled(props) || !hasPersistedOpenState(nextType));
15544
+ var canRestoreMessages = isMessagePersistenceEnabled(previousProps) && isMessagePersistenceEnabled(props) && getMessagePersistenceKeySuffix(previousProps) === getMessagePersistenceKeySuffix(props);
15545
+ var losesMessages = previousChatState.messages.length > 0 && !canRestoreMessages;
15546
+ { warn(!losesOpenState && !losesMessages, 'Changing the props of the React <Chat> widget replaces its internal Chat instance and clears open state or non-persisted messages. Use stable prop references or provide your own Chat instance to preserve the conversation.'); }
15547
+ }
15548
+ previousPropsRef.current = props;
15549
+ }
15550
+ });
15258
15551
  var chatState = useConnector(connectChat, props, additionalWidgetProperties);
15552
+ useIsomorphicLayoutEffect(function() {
15553
+ {
15554
+ previousChatStateRef.current = chatState;
15555
+ }
15556
+ });
15259
15557
  if (isHydrated) {
15260
15558
  return chatState;
15261
15559
  }
@@ -15281,120 +15579,333 @@
15281
15579
  });
15282
15580
  }
15283
15581
 
15284
- function buildEndpoint(param) {
15285
- var appId = param.appId, agentId = param.agentId;
15286
- return "https://".concat(appId, ".algolia.net/agent-studio/1/agents/").concat(agentId, "/tasks");
15582
+ function isHeaders$1(headers) {
15583
+ return !Array.isArray(headers) && 'entries' in headers && typeof headers.entries === 'function';
15287
15584
  }
15288
- function resolveEndpoint(params) {
15289
- if (params.transport) {
15290
- return {
15291
- endpoint: params.transport.api,
15292
- headers: params.transport.headers || {},
15293
- prepareSendMessagesRequest: params.transport.prepareSendMessagesRequest
15294
- };
15585
+ function headersToRecord$1(headers) {
15586
+ if (!headers) {
15587
+ return {};
15295
15588
  }
15296
- if (!params.appId || !params.apiKey || !params.agentId) {
15297
- throw new Error('[tasks] Either `transport` or `{ appId, apiKey, agentId }` is required.');
15589
+ if (isHeaders$1(headers)) {
15590
+ return Object.fromEntries(headers.entries());
15298
15591
  }
15299
- var headers = {
15300
- 'x-algolia-application-id': params.appId,
15301
- 'x-algolia-api-key': params.apiKey
15302
- };
15303
- if (params.algoliaAgent) {
15304
- headers['x-algolia-agent'] = "".concat(params.algoliaAgent, "; tasks");
15592
+ if (Array.isArray(headers)) {
15593
+ return Object.fromEntries(headers);
15305
15594
  }
15306
- return {
15307
- endpoint: buildEndpoint({
15308
- appId: params.appId,
15309
- agentId: params.agentId
15310
- }),
15311
- headers: headers
15312
- };
15595
+ return headers;
15313
15596
  }
15314
-
15315
- function buildTaskPayload(param) {
15316
- var task = param.task, input = param.input, prepareRequest = param.prepareRequest;
15317
- var payload = {
15318
- task: task,
15319
- input: input
15320
- };
15321
- return prepareRequest ? prepareRequest(payload).body : payload;
15597
+ function withJsonContentType(headers) {
15598
+ var merged = _object_spread({}, headersToRecord$1(headers));
15599
+ Object.keys(merged).forEach(function(name) {
15600
+ if (name.toLowerCase() === 'content-type') {
15601
+ delete merged[name];
15602
+ }
15603
+ });
15604
+ merged['Content-Type'] = 'application/json';
15605
+ return merged;
15322
15606
  }
15323
15607
  function withStreamParam(url) {
15324
15608
  return url.includes('?') ? "".concat(url, "&stream=true") : "".concat(url, "?stream=true");
15325
15609
  }
15326
- function resolveStreamedOutput(data, previous) {
15327
- return typeof data === 'string' ? parsePartialJson(data, previous) : data;
15610
+ function createTaskPreparationContext(context) {
15611
+ function hideProperty(key) {
15612
+ var value = context[key];
15613
+ Object.defineProperty(context, key, {
15614
+ configurable: true,
15615
+ enumerable: false,
15616
+ get: function get() {
15617
+ return value;
15618
+ },
15619
+ set: function set(nextValue) {
15620
+ Reflect.deleteProperty(context, key);
15621
+ Object.defineProperty(context, key, {
15622
+ configurable: true,
15623
+ enumerable: true,
15624
+ value: nextValue,
15625
+ writable: true
15626
+ });
15627
+ }
15628
+ });
15629
+ }
15630
+ // Rich metadata stays out of legacy body spreads until assigned as payload.
15631
+ hideProperty('stream');
15632
+ hideProperty('body');
15633
+ hideProperty('credentials');
15634
+ hideProperty('headers');
15635
+ hideProperty('api');
15636
+ return context;
15637
+ }
15638
+ function unwrap(envelope) {
15639
+ if ((typeof envelope === "undefined" ? "undefined" : _type_of(envelope)) === 'object' && envelope !== null && 'output' in envelope) {
15640
+ return envelope.output;
15641
+ }
15642
+ return undefined;
15328
15643
  }
15329
- function consumeTaskStream(body, onData) {
15644
+ function consumeTaskTextStream(body, onData) {
15330
15645
  return new Promise(function(resolve, reject) {
15331
- var chunkStream = parseJsonEventStream(body);
15646
+ var decoder = new TextDecoder();
15647
+ var reader = body.getReader();
15648
+ var accumulatedText = '';
15332
15649
  var latest;
15333
- processStream(chunkStream, function(chunk) {
15334
- if (!chunk) {
15335
- return;
15650
+ var publish = function publish(output) {
15651
+ if (!isEqual(output, latest)) {
15652
+ latest = output;
15653
+ onData === null || onData === void 0 ? void 0 : onData({
15654
+ output: output
15655
+ });
15336
15656
  }
15337
- // A terminal `error` event aborts the task: reject rather than let the
15338
- // stream close and resolve the last partial snapshot as a success.
15339
- // Throwing here lets `processStream` release the reader and stop
15340
- // consuming; the rejection propagates to the caller's `.catch`.
15341
- if (chunk.type === 'error') {
15342
- throw new Error(chunk.errorText || 'Task stream error');
15657
+ };
15658
+ var read = function read1() {
15659
+ reader.read().then(function(param) {
15660
+ var done = param.done, value = param.value;
15661
+ if (done) {
15662
+ accumulatedText += decoder.decode();
15663
+ reader.releaseLock();
15664
+ try {
15665
+ var output = JSON.parse(accumulatedText);
15666
+ publish(output);
15667
+ resolve({
15668
+ output: output
15669
+ });
15670
+ } catch (error) {
15671
+ reject(error);
15672
+ }
15673
+ return;
15674
+ }
15675
+ try {
15676
+ accumulatedText += decoder.decode(value, {
15677
+ stream: true
15678
+ });
15679
+ var partial = parsePartialJson(accumulatedText, latest);
15680
+ if (partial !== undefined) {
15681
+ publish(partial);
15682
+ }
15683
+ read();
15684
+ } catch (error) {
15685
+ reader.releaseLock();
15686
+ reject(error);
15687
+ }
15688
+ }, function(error) {
15689
+ reader.releaseLock();
15690
+ reject(error);
15691
+ });
15692
+ };
15693
+ read();
15694
+ });
15695
+ }
15696
+ /** Default HTTP transport for named Tasks requests and task-output streams. */ var DefaultTaskTransport = /*#__PURE__*/ function() {
15697
+ function DefaultTaskTransport() {
15698
+ var _ref = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {}, _ref_api = _ref.api, api = _ref_api === void 0 ? '/api/tasks' : _ref_api, credentials = _ref.credentials, headers = _ref.headers, body = _ref.body, customFetch = _ref.fetch, prepareSendMessagesRequest = _ref.prepareSendMessagesRequest;
15699
+ _class_call_check(this, DefaultTaskTransport);
15700
+ _define_property(this, "api", void 0);
15701
+ _define_property(this, "credentials", void 0);
15702
+ _define_property(this, "headers", void 0);
15703
+ _define_property(this, "body", void 0);
15704
+ _define_property(this, "fetch", void 0);
15705
+ _define_property(this, "prepareSendMessagesRequest", void 0);
15706
+ this.api = api;
15707
+ this.credentials = credentials;
15708
+ this.headers = headers;
15709
+ this.body = body;
15710
+ this.fetch = customFetch;
15711
+ this.prepareSendMessagesRequest = prepareSendMessagesRequest;
15712
+ }
15713
+ _create_class(DefaultTaskTransport, [
15714
+ {
15715
+ key: "sendTask",
15716
+ value: function sendTask(param) {
15717
+ var task = param.task, kind = param.kind, input = param.input, stream = param.stream, onData = param.onData;
15718
+ return this.sendTaskRequest({
15719
+ task: task,
15720
+ kind: kind,
15721
+ input: input,
15722
+ stream: stream,
15723
+ onData: onData ? function(data) {
15724
+ return onData(unwrap(data));
15725
+ } : undefined
15726
+ }).then(unwrap);
15343
15727
  }
15344
- if (chunk.type !== 'data-task-output') {
15345
- return;
15728
+ },
15729
+ {
15730
+ /** @internal */ key: "sendTaskRequest",
15731
+ value: function sendTaskRequest(param) {
15732
+ var _this = this;
15733
+ var task = param.task, kind = param.kind, input = param.input, stream = param.stream, onData = param.onData;
15734
+ var _this_fetch;
15735
+ var fetchFn = (_this_fetch = this.fetch) !== null && _this_fetch !== void 0 ? _this_fetch : fetch;
15736
+ return Promise.all([
15737
+ resolveValue(this.credentials),
15738
+ resolveValue(this.headers),
15739
+ resolveValue(this.body)
15740
+ ]).then(function(param) {
15741
+ var _param = _sliced_to_array(param, 3), resolvedCredentials = _param[0], resolvedHeaders = _param[1], resolvedBody = _param[2];
15742
+ var api = _this.api;
15743
+ var credentials = resolvedCredentials;
15744
+ var headers = withJsonContentType(resolvedHeaders);
15745
+ var body = _object_spread(_object_spread_props(_object_spread({}, task === undefined ? {} : {
15746
+ task: task
15747
+ }, kind === undefined ? {} : {
15748
+ kind: kind
15749
+ }), {
15750
+ input: input
15751
+ }), resolvedBody);
15752
+ var preparedBody = resolvedBody ? _object_spread({}, resolvedBody) : undefined;
15753
+ var preparePromise = _this.prepareSendMessagesRequest ? Promise.resolve(_this.prepareSendMessagesRequest(createTaskPreparationContext({
15754
+ task: task,
15755
+ kind: kind,
15756
+ input: input,
15757
+ stream: stream,
15758
+ body: preparedBody,
15759
+ credentials: resolvedCredentials,
15760
+ headers: resolvedHeaders,
15761
+ api: _this.api
15762
+ }))) : Promise.resolve(null);
15763
+ return preparePromise.then(function(prepared) {
15764
+ if (prepared) {
15765
+ body = prepared.body;
15766
+ if (prepared.api) {
15767
+ api = prepared.api;
15768
+ }
15769
+ if (prepared.credentials) {
15770
+ credentials = prepared.credentials;
15771
+ }
15772
+ if (prepared.headers) {
15773
+ headers = withJsonContentType(prepared.headers);
15774
+ }
15775
+ }
15776
+ var request = {
15777
+ method: 'POST',
15778
+ headers: headers,
15779
+ body: JSON.stringify(body)
15780
+ };
15781
+ if (credentials !== undefined) {
15782
+ request.credentials = credentials;
15783
+ }
15784
+ return fetchFn(stream ? withStreamParam(api) : api, request).then(function(response) {
15785
+ var _response_headers_get, _response_headers;
15786
+ if (!response.ok) {
15787
+ throw new Error("HTTP error ".concat(response.status));
15788
+ }
15789
+ var contentType = ((_response_headers = response.headers) === null || _response_headers === void 0 ? void 0 : (_response_headers_get = _response_headers.get) === null || _response_headers_get === void 0 ? void 0 : _response_headers_get.call(_response_headers, 'content-type')) || '';
15790
+ if (stream && contentType.includes('text/plain')) {
15791
+ if (!response.body) {
15792
+ throw new Error('Response body is empty');
15793
+ }
15794
+ return consumeTaskTextStream(response.body, onData);
15795
+ }
15796
+ return response.json();
15797
+ });
15798
+ });
15799
+ });
15346
15800
  }
15347
- latest = resolveStreamedOutput(chunk.data, latest);
15348
- if (onData) {
15349
- onData(latest);
15801
+ }
15802
+ ]);
15803
+ return DefaultTaskTransport;
15804
+ }();
15805
+
15806
+ function buildEndpoint(param) {
15807
+ var appId = param.appId, agentId = param.agentId;
15808
+ return "https://".concat(appId, ".algolia.net/agent-studio/1/agents/").concat(agentId, "/tasks");
15809
+ }
15810
+ function isHeaders(headers) {
15811
+ return !Array.isArray(headers) && 'entries' in headers && typeof headers.entries === 'function';
15812
+ }
15813
+ function headersToRecord(headers) {
15814
+ if (!headers) {
15815
+ return {};
15816
+ }
15817
+ if (isHeaders(headers)) {
15818
+ return Object.fromEntries(headers.entries());
15819
+ }
15820
+ if (Array.isArray(headers)) {
15821
+ return Object.fromEntries(headers);
15822
+ }
15823
+ return _object_spread({}, headers);
15824
+ }
15825
+ function mergeProtectedHeaders(headers, protectedHeaders) {
15826
+ var merged = headersToRecord(headers);
15827
+ Object.entries(protectedHeaders).forEach(function(param) {
15828
+ var _param = _sliced_to_array(param, 2), protectedName = _param[0], value = _param[1];
15829
+ Object.keys(merged).forEach(function(name) {
15830
+ if (name.toLowerCase() === protectedName.toLowerCase()) {
15831
+ delete merged[name];
15350
15832
  }
15351
- }, function() {
15352
- return resolve(latest);
15353
- }, reject);
15833
+ });
15834
+ merged[protectedName] = value;
15354
15835
  });
15836
+ return merged;
15355
15837
  }
15356
- function fetchTask(param) {
15357
- var endpoint = param.endpoint, headers = param.headers, payload = param.payload, onData = param.onData, _param_stream = param.stream, stream = _param_stream === void 0 ? true : _param_stream;
15358
- return fetch(stream ? withStreamParam(endpoint) : endpoint, {
15359
- method: 'POST',
15360
- headers: _object_spread_props(_object_spread({}, headers), {
15361
- 'Content-Type': 'application/json'
15838
+ /** @internal */ function createTaskTransport(param) {
15839
+ var _param_transport = param.transport, transport = _param_transport === void 0 ? {} : _param_transport, appId = param.appId, apiKey = param.apiKey, agentId = param.agentId, algoliaAgent = param.algoliaAgent;
15840
+ var _transport_api;
15841
+ if (!agentId) {
15842
+ return new DefaultTaskTransport(transport);
15843
+ }
15844
+ if (!appId || !apiKey) {
15845
+ throw new Error('[tasks] `appId` and `apiKey` are required when `agentId` is provided.');
15846
+ }
15847
+ var protectedHeaders = {
15848
+ 'x-algolia-application-id': appId,
15849
+ 'x-algolia-api-key': apiKey
15850
+ };
15851
+ if (algoliaAgent) {
15852
+ protectedHeaders['x-algolia-agent'] = "".concat(algoliaAgent, "; tasks");
15853
+ }
15854
+ var originalPrepare = transport.prepareSendMessagesRequest;
15855
+ var prepareSendMessagesRequest = originalPrepare ? function(request) {
15856
+ return Promise.resolve(originalPrepare(request)).then(function(prepared) {
15857
+ return _object_spread_props(_object_spread({}, prepared), {
15858
+ headers: prepared.headers ? mergeProtectedHeaders(prepared.headers, protectedHeaders) : undefined
15859
+ });
15860
+ });
15861
+ } : undefined;
15862
+ return new DefaultTaskTransport(_object_spread_props(_object_spread({}, transport), {
15863
+ api: (_transport_api = transport.api) !== null && _transport_api !== void 0 ? _transport_api : buildEndpoint({
15864
+ appId: appId,
15865
+ agentId: agentId
15362
15866
  }),
15363
- body: JSON.stringify(payload)
15364
- }).then(function(response) {
15365
- var _response_headers_get, _response_headers;
15366
- if (!response.ok) {
15367
- throw new Error("HTTP error ".concat(response.status));
15368
- }
15369
- var contentType = ((_response_headers = response.headers) === null || _response_headers === void 0 ? void 0 : (_response_headers_get = _response_headers.get) === null || _response_headers_get === void 0 ? void 0 : _response_headers_get.call(_response_headers, 'content-type')) || '';
15370
- if (stream && response.body && contentType.includes('text/event-stream')) {
15371
- return consumeTaskStream(response.body, onData);
15372
- }
15373
- return response.json();
15374
- });
15375
- }
15376
- function unwrap(envelope) {
15377
- return envelope === null || envelope === void 0 ? void 0 : envelope.output;
15867
+ headers: function headers() {
15868
+ return Promise.resolve(resolveValue(transport.headers)).then(function(headers) {
15869
+ return mergeProtectedHeaders(headers, protectedHeaders);
15870
+ });
15871
+ },
15872
+ prepareSendMessagesRequest: prepareSendMessagesRequest
15873
+ }));
15378
15874
  }
15379
- function createTaskRunner(param) {
15380
- var endpoint = param.endpoint, headers = param.headers, task = param.task, _param_stream = param.stream, stream = _param_stream === void 0 ? true : _param_stream, prepareRequest = param.prepareRequest;
15875
+
15876
+ function createTaskRunner(options) {
15877
+ var task = options.task, kind = options.kind, _options_stream = options.stream, stream = _options_stream === void 0 ? true : _options_stream;
15878
+ var transport;
15879
+ if (options.transport !== undefined) {
15880
+ transport = options.transport;
15881
+ } else {
15882
+ var prepareRequest = options.prepareRequest;
15883
+ transport = new DefaultTaskTransport({
15884
+ api: options.endpoint,
15885
+ headers: options.headers,
15886
+ fetch: options.fetch,
15887
+ prepareSendMessagesRequest: prepareRequest ? function(param) {
15888
+ var requestTask = param.task, requestKind = param.kind, input = param.input;
15889
+ return prepareRequest(_object_spread_props(_object_spread({}, requestTask === undefined ? {} : {
15890
+ task: requestTask
15891
+ }, requestKind === undefined ? {} : {
15892
+ kind: requestKind
15893
+ }), {
15894
+ input: input
15895
+ }));
15896
+ } : undefined
15897
+ });
15898
+ }
15381
15899
  return {
15382
- submit: function submit(variables) {
15900
+ submit: function submit(input) {
15383
15901
  var onData = (arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}).onData;
15384
- var payload = buildTaskPayload({
15902
+ return transport.sendTask({
15385
15903
  task: task,
15386
- input: variables,
15387
- prepareRequest: prepareRequest
15388
- });
15389
- return fetchTask({
15390
- endpoint: endpoint,
15391
- headers: headers,
15392
- payload: payload,
15904
+ kind: kind,
15905
+ input: input,
15393
15906
  stream: stream,
15394
- onData: onData ? function(partial) {
15395
- return onData(unwrap(partial));
15396
- } : undefined
15397
- }).then(unwrap);
15907
+ onData: onData
15908
+ });
15398
15909
  }
15399
15910
  };
15400
15911
  }
@@ -15407,12 +15918,12 @@
15407
15918
  var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
15408
15919
  checkRendering(renderFn, withUsage$p());
15409
15920
  return function(widgetParams) {
15410
- var agentId = widgetParams.agentId, transport = widgetParams.transport, task = widgetParams.task, _widgetParams_stream = widgetParams.stream, stream = _widgetParams_stream === void 0 ? true : _widgetParams_stream;
15921
+ var agentId = widgetParams.agentId, transport = widgetParams.transport, task = widgetParams.task, kind = widgetParams.kind, _widgetParams_stream = widgetParams.stream, stream = _widgetParams_stream === void 0 ? true : _widgetParams_stream;
15411
15922
  if (!agentId && !transport) {
15412
15923
  throw new Error(withUsage$p('The `agentId` option is required unless a custom `transport` is provided.'));
15413
15924
  }
15414
- if (!task) {
15415
- throw new Error(withUsage$p('The `task` option is required.'));
15925
+ if (!task && !kind) {
15926
+ throw new Error(withUsage$p('Either the `task` or `kind` option is required.'));
15416
15927
  }
15417
15928
  var runner;
15418
15929
  var output;
@@ -15464,6 +15975,8 @@
15464
15975
  // Bump the request id so any in-flight request's callbacks see
15465
15976
  // `isStale()` and are ignored. The fetch itself is left to complete.
15466
15977
  requestId += 1;
15978
+ output = undefined;
15979
+ error = undefined;
15467
15980
  isLoading = false;
15468
15981
  triggerRender();
15469
15982
  };
@@ -15481,32 +15994,31 @@
15481
15994
  $$type: 'ais.tasks',
15482
15995
  init: function init(initOptions) {
15483
15996
  var instantSearchInstance = initOptions.instantSearchInstance;
15484
- if (transport) {
15485
- var resolved = resolveEndpoint({
15486
- transport: transport
15487
- });
15488
- runner = createTaskRunner({
15489
- endpoint: resolved.endpoint,
15490
- headers: resolved.headers,
15491
- task: task,
15492
- stream: stream,
15493
- prepareRequest: resolved.prepareSendMessagesRequest
15494
- });
15495
- } else {
15997
+ if (agentId) {
15496
15998
  var _getAppIdAndApiKey = _sliced_to_array(getAppIdAndApiKey(instantSearchInstance.client), 2), appId = _getAppIdAndApiKey[0], apiKey = _getAppIdAndApiKey[1];
15497
15999
  if (!appId || !apiKey) {
15498
16000
  throw new Error(withUsage$p('Could not extract Algolia credentials from the search client.'));
15499
16001
  }
15500
- var resolved1 = resolveEndpoint({
16002
+ var taskTransport = createTaskTransport({
16003
+ transport: transport,
15501
16004
  appId: appId,
15502
16005
  apiKey: apiKey,
15503
16006
  agentId: agentId,
15504
16007
  algoliaAgent: getAlgoliaAgent(instantSearchInstance.client)
15505
16008
  });
15506
16009
  runner = createTaskRunner({
15507
- endpoint: resolved1.endpoint,
15508
- headers: resolved1.headers,
16010
+ transport: taskTransport,
15509
16011
  task: task,
16012
+ kind: kind,
16013
+ stream: stream
16014
+ });
16015
+ } else {
16016
+ runner = createTaskRunner({
16017
+ transport: createTaskTransport({
16018
+ transport: transport
16019
+ }),
16020
+ task: task,
16021
+ kind: kind,
15510
16022
  stream: stream
15511
16023
  });
15512
16024
  }
@@ -15538,6 +16050,7 @@
15538
16050
  });
15539
16051
  var RENDER_STATE_KEY = 'promptSuggestions';
15540
16052
  var CHAT_RENDER_STATE_KEY = 'chat';
16053
+ var PROMPT_SUGGESTIONS_TASK_KIND = 'prompt_suggestions';
15541
16054
  var DEBOUNCE_MS = 300;
15542
16055
  function parseSuggestions(data) {
15543
16056
  var suggestions = data === null || data === void 0 ? void 0 : data.suggestions;
@@ -15609,12 +16122,10 @@
15609
16122
  if (!agentId && !transport) {
15610
16123
  throw new Error(withUsage$o('The `agentId` option is required unless a custom `transport` is provided.'));
15611
16124
  }
15612
- if (!configurationId) {
15613
- throw new Error(withUsage$o('The `configurationId` option is required.'));
15614
- }
15615
16125
  var tasksState;
15616
16126
  var suggestions = [];
15617
16127
  var isLoading = false;
16128
+ var error;
15618
16129
  var debounceTimer;
15619
16130
  var lastStateSignature = null;
15620
16131
  var latestRenderOptions = null;
@@ -15747,6 +16258,7 @@
15747
16258
  return {
15748
16259
  suggestions: transformed,
15749
16260
  isLoading: isLoading,
16261
+ error: error,
15750
16262
  onSuggestionClick: send,
15751
16263
  sendToChat: send,
15752
16264
  refresh: refresh,
@@ -15759,10 +16271,10 @@
15759
16271
  var handleInnerRender = function handleInnerRender(renderState) {
15760
16272
  tasksState = renderState;
15761
16273
  if (refetchPending) return;
16274
+ error = renderState.error;
15762
16275
  if (renderState.error) {
15763
16276
  // A failed task (including a mid-stream `error` event) must not leave
15764
- // any streamed partial visible. There's no error UI for now, so fall
15765
- // back to a blank suggestions state.
16277
+ // any streamed partial visible.
15766
16278
  suggestions = [];
15767
16279
  } else if (renderState.isLoading || renderState.output !== undefined) {
15768
16280
  // Only adopt the inner output once a request is loading or has
@@ -15773,14 +16285,26 @@
15773
16285
  if (!latestRenderOptions) return;
15774
16286
  renderOutward(latestRenderOptions);
15775
16287
  };
15776
- var tasksWidget = connectTasks(handleInnerRender, noop)(_object_spread_props(_object_spread({}, transport ? {
15777
- transport: transport
15778
- } : {
15779
- agentId: agentId
15780
- }), {
15781
- task: configurationId,
15782
- stream: true
15783
- }));
16288
+ var tasksParams;
16289
+ if (agentId) {
16290
+ tasksParams = {
16291
+ agentId: agentId,
16292
+ transport: transport,
16293
+ task: configurationId,
16294
+ kind: PROMPT_SUGGESTIONS_TASK_KIND,
16295
+ stream: true
16296
+ };
16297
+ } else if (transport) {
16298
+ tasksParams = {
16299
+ transport: transport,
16300
+ task: configurationId,
16301
+ kind: PROMPT_SUGGESTIONS_TASK_KIND,
16302
+ stream: true
16303
+ };
16304
+ } else {
16305
+ throw new Error(withUsage$o('The `agentId` option is required unless a custom `transport` is provided.'));
16306
+ }
16307
+ var tasksWidget = connectTasks(handleInnerRender, noop)(tasksParams);
15784
16308
  return {
15785
16309
  $$type: 'ais.promptSuggestions',
15786
16310
  init: function init(initOptions) {
@@ -15803,6 +16327,7 @@
15803
16327
  if (stateSignature !== lastStateSignature) {
15804
16328
  lastStateSignature = stateSignature;
15805
16329
  refetchPending = true;
16330
+ error = undefined;
15806
16331
  clearTimeout(debounceTimer);
15807
16332
  debounceTimer = setTimeout(function() {
15808
16333
  if (latestRenderOptions === null || latestRenderOptions === void 0 ? void 0 : latestRenderOptions.results) {