react-instantsearch-core 7.41.0 → 7.43.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.41.0 | © Algolia, Inc. and contributors; MIT License | https://github.com/algolia/instantsearch */
1
+ /*! React InstantSearch Core 7.43.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.41.0';
27
+ var version$2 = '7.43.0';
28
28
 
29
29
  function _define_property(obj, key, value) {
30
30
  if (key in obj) {
@@ -3919,7 +3919,7 @@
3919
3919
  function requireVersion() {
3920
3920
  if (hasRequiredVersion) return version$1;
3921
3921
  hasRequiredVersion = 1;
3922
- version$1 = '3.29.2';
3922
+ version$1 = '3.29.3';
3923
3923
  return version$1;
3924
3924
  }
3925
3925
 
@@ -5980,7 +5980,7 @@
5980
5980
  }
5981
5981
  }
5982
5982
 
5983
- var withUsage$v = createDocumentationMessageGenerator({
5983
+ var withUsage$x = createDocumentationMessageGenerator({
5984
5984
  name: 'configure',
5985
5985
  connector: true
5986
5986
  });
@@ -5996,7 +5996,7 @@
5996
5996
  var renderFn = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : noop, unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
5997
5997
  return function(widgetParams) {
5998
5998
  if (!widgetParams || !isPlainObject(widgetParams.searchParameters)) {
5999
- throw new Error(withUsage$v('The `searchParameters` option expects an object.'));
5999
+ throw new Error(withUsage$x('The `searchParameters` option expects an object.'));
6000
6000
  }
6001
6001
  var connectorState = {};
6002
6002
  function refine(helper) {
@@ -6280,6 +6280,45 @@
6280
6280
  return stableValue;
6281
6281
  }
6282
6282
 
6283
+ /**
6284
+ * Forwards the insights-related search parameters to every Recommend query.
6285
+ *
6286
+ * The insights middleware writes `userToken` and `clickAnalytics` on the main
6287
+ * helper's search state, which only feeds search queries: Recommend queries are
6288
+ * built from their own parameters and never read the search state. Without this,
6289
+ * recommendations can't be personalized, and their responses carry no `queryID`
6290
+ * for the connectors to attribute click and conversion events with.
6291
+ *
6292
+ * Parameters set on a widget win, so an explicit `queryParameters.userToken`
6293
+ * keeps overriding the middleware.
6294
+ *
6295
+ * The fallback query is a regular search, so it gets the same treatment — but
6296
+ * only merged into a `fallbackParameters` the widget already set. See below.
6297
+ */ function addInsightsToRecommendParameters(recommendParameters, param) {
6298
+ var userToken = param.userToken, clickAnalytics = param.clickAnalytics;
6299
+ if (userToken === undefined && clickAnalytics === undefined) {
6300
+ return recommendParameters;
6301
+ }
6302
+ var insightsParameters = _object_spread({}, clickAnalytics === undefined ? {} : {
6303
+ clickAnalytics: clickAnalytics
6304
+ }, userToken === undefined ? {} : {
6305
+ userToken: userToken
6306
+ });
6307
+ return new algoliasearchHelper.RecommendParameters({
6308
+ params: recommendParameters.params.map(function(params) {
6309
+ // v4 `TrendingFacetsQuery` doesn't include `queryParameters` or
6310
+ // `fallbackParameters`, but the v5 API and the helper support them, like
6311
+ // `connectTrendingFacets` does.
6312
+ var queryParameters = params.queryParameters, fallbackParameters = params.fallbackParameters;
6313
+ return _object_spread(_object_spread_props(_object_spread({}, params), {
6314
+ queryParameters: _object_spread({}, insightsParameters, queryParameters)
6315
+ }), fallbackParameters && {
6316
+ fallbackParameters: _object_spread({}, insightsParameters, fallbackParameters)
6317
+ });
6318
+ })
6319
+ });
6320
+ }
6321
+
6283
6322
  var id = 0;
6284
6323
  function addWidgetId(widget) {
6285
6324
  if (widget.dependsOn !== 'recommend') {
@@ -7788,13 +7827,13 @@
7788
7827
  return null;
7789
7828
  }
7790
7829
 
7791
- var withUsage$u = createDocumentationMessageGenerator({
7830
+ var withUsage$w = createDocumentationMessageGenerator({
7792
7831
  name: 'dynamic-widgets',
7793
7832
  connector: true
7794
7833
  });
7795
7834
  var connectDynamicWidgets = function connectDynamicWidgets(renderFn) {
7796
7835
  var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
7797
- checkRendering(renderFn, withUsage$u());
7836
+ checkRendering(renderFn, withUsage$w());
7798
7837
  return function(widgetParams) {
7799
7838
  var widgets = widgetParams.widgets, _widgetParams_maxValuesPerFacet = widgetParams.maxValuesPerFacet, maxValuesPerFacet = _widgetParams_maxValuesPerFacet === void 0 ? 20 : _widgetParams_maxValuesPerFacet, _widgetParams_facets = widgetParams.facets, facets = _widgetParams_facets === void 0 ? [
7800
7839
  '*'
@@ -7804,10 +7843,10 @@
7804
7843
  if (!(widgets && Array.isArray(widgets) && widgets.every(function(widget) {
7805
7844
  return (typeof widget === "undefined" ? "undefined" : _type_of(widget)) === 'object';
7806
7845
  }))) {
7807
- throw new Error(withUsage$u('The `widgets` option expects an array of widgets.'));
7846
+ throw new Error(withUsage$w('The `widgets` option expects an array of widgets.'));
7808
7847
  }
7809
7848
  if (!Array.isArray(facets)) {
7810
- throw new Error(withUsage$u("The `facets` option only accepts an array of facets, you passed ".concat(JSON.stringify(facets))));
7849
+ throw new Error(withUsage$w("The `facets` option only accepts an array of facets, you passed ".concat(JSON.stringify(facets))));
7811
7850
  }
7812
7851
  var localWidgets = new Map();
7813
7852
  return {
@@ -7909,7 +7948,7 @@
7909
7948
  results: results
7910
7949
  });
7911
7950
  if (!Array.isArray(attributesToRender)) {
7912
- throw new Error(withUsage$u('The `transformItems` option expects a function that returns an Array.'));
7951
+ throw new Error(withUsage$w('The `transformItems` option expects a function that returns an Array.'));
7913
7952
  }
7914
7953
  return {
7915
7954
  attributesToRender: attributesToRender,
@@ -8231,19 +8270,19 @@
8231
8270
  return toFeedSearchResults(lastResults._state, raw);
8232
8271
  });
8233
8272
  }
8234
- var withUsage$t = createDocumentationMessageGenerator({
8273
+ var withUsage$v = createDocumentationMessageGenerator({
8235
8274
  name: 'feeds',
8236
8275
  connector: true
8237
8276
  });
8238
8277
  var connectFeeds = function connectFeeds(renderFn) {
8239
8278
  var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
8240
- checkRendering(renderFn, withUsage$t());
8279
+ checkRendering(renderFn, withUsage$v());
8241
8280
  return function(widgetParams) {
8242
8281
  var isolated = widgetParams.isolated, _widgetParams_transformFeeds = widgetParams.transformFeeds, transformFeeds = _widgetParams_transformFeeds === void 0 ? function(feeds) {
8243
8282
  return feeds;
8244
8283
  } : _widgetParams_transformFeeds;
8245
8284
  if (isolated !== false) {
8246
- throw new Error(withUsage$t('The `isolated` option currently only supports `false`.'));
8285
+ throw new Error(withUsage$v('The `isolated` option currently only supports `false`.'));
8247
8286
  }
8248
8287
  return {
8249
8288
  $$type: 'ais.feeds',
@@ -8251,7 +8290,7 @@
8251
8290
  init: function init(initOptions) {
8252
8291
  var instantSearchInstance = initOptions.instantSearchInstance;
8253
8292
  if (!instantSearchInstance.compositionID) {
8254
- throw new Error(withUsage$t('The `feeds` widget requires a composition-based InstantSearch instance (compositionID must be set).'));
8293
+ throw new Error(withUsage$v('The `feeds` widget requires a composition-based InstantSearch instance (compositionID must be set).'));
8255
8294
  }
8256
8295
  hydrateFeedsFromInitialResultsIfNeeded(instantSearchInstance, initOptions.parent);
8257
8296
  renderFn(_object_spread_props(_object_spread({}, this.getWidgetRenderState(initOptions)), {
@@ -8297,12 +8336,12 @@
8297
8336
  ];
8298
8337
  feedIDs = transformFeeds(feedIDs);
8299
8338
  if (!Array.isArray(feedIDs)) {
8300
- throw new Error(withUsage$t('The `transformFeeds` option expects a function that returns an Array.'));
8339
+ throw new Error(withUsage$v('The `transformFeeds` option expects a function that returns an Array.'));
8301
8340
  }
8302
8341
  if (!feedIDs.every(function(feedID) {
8303
8342
  return typeof feedID === 'string';
8304
8343
  })) {
8305
- throw new Error(withUsage$t('The `transformFeeds` option expects a function that returns an array of feed IDs (strings).'));
8344
+ throw new Error(withUsage$v('The `transformFeeds` option expects a function that returns an array of feed IDs (strings).'));
8306
8345
  }
8307
8346
  return {
8308
8347
  feedIDs: feedIDs,
@@ -8413,7 +8452,7 @@
8413
8452
  }));
8414
8453
  }
8415
8454
 
8416
- var withUsage$s = createDocumentationMessageGenerator({
8455
+ var withUsage$u = createDocumentationMessageGenerator({
8417
8456
  name: 'index-widget'
8418
8457
  });
8419
8458
  /**
@@ -8541,7 +8580,7 @@
8541
8580
  var index = function index(widgetParams) {
8542
8581
  var _widgetParams_EXPERIMENTAL_isolated;
8543
8582
  if (widgetParams === undefined || widgetParams.indexName === undefined && !widgetParams.isolated && !widgetParams.EXPERIMENTAL_isolated) {
8544
- throw new Error(withUsage$s('The `indexName` option is required.'));
8583
+ throw new Error(withUsage$u('The `indexName` option is required.'));
8545
8584
  }
8546
8585
  // When isolated=true, we use an empty string as the default indexName.
8547
8586
  // This is intentional: isolated indices do not require a real index name.
@@ -8634,7 +8673,7 @@
8634
8673
  addWidgets: function addWidgets(widgets) {
8635
8674
  var _this = this;
8636
8675
  if (!Array.isArray(widgets)) {
8637
- throw new Error(withUsage$s('The `addWidgets` method expects an array of widgets.'));
8676
+ throw new Error(withUsage$u('The `addWidgets` method expects an array of widgets.'));
8638
8677
  }
8639
8678
  var flatWidgets = widgets.reduce(function(acc, w) {
8640
8679
  return acc.concat(Array.isArray(w) ? w : [
@@ -8644,7 +8683,7 @@
8644
8683
  if (flatWidgets.some(function(widget) {
8645
8684
  return typeof widget.init !== 'function' && typeof widget.render !== 'function';
8646
8685
  })) {
8647
- throw new Error(withUsage$s('The widget definition expects a `render` and/or an `init` method.'));
8686
+ throw new Error(withUsage$u('The widget definition expects a `render` and/or an `init` method.'));
8648
8687
  }
8649
8688
  flatWidgets.forEach(function(widget) {
8650
8689
  widget.parent = _this;
@@ -8695,7 +8734,7 @@
8695
8734
  removeWidgets: function removeWidgets(widgets) {
8696
8735
  var _this = this;
8697
8736
  if (!Array.isArray(widgets)) {
8698
- throw new Error(withUsage$s('The `removeWidgets` method expects an array of widgets.'));
8737
+ throw new Error(withUsage$u('The `removeWidgets` method expects an array of widgets.'));
8699
8738
  }
8700
8739
  var flatWidgets = widgets.reduce(function(acc, w) {
8701
8740
  return acc.concat(Array.isArray(w) ? w : [
@@ -8705,7 +8744,7 @@
8705
8744
  if (flatWidgets.some(function(widget) {
8706
8745
  return typeof widget.dispose !== 'function';
8707
8746
  })) {
8708
- throw new Error(withUsage$s('The widget definition expects a `dispose` method.'));
8747
+ throw new Error(withUsage$u('The widget definition expects a `dispose` method.'));
8709
8748
  }
8710
8749
  localWidgets = localWidgets.filter(function(widget) {
8711
8750
  return flatWidgets.indexOf(widget) === -1;
@@ -8833,7 +8872,7 @@
8833
8872
  mainHelper.state
8834
8873
  ].concat(_to_consumable_array(resolveSearchParameters(_this))));
8835
8874
  }, function() {
8836
- return _this.getHelper().recommendState;
8875
+ return addInsightsToRecommendParameters(_this.getHelper().recommendState, mainHelper.state);
8837
8876
  });
8838
8877
  var indexInitialResults = (_instantSearchInstance__initialResults = instantSearchInstance._initialResults) === null || _instantSearchInstance__initialResults === void 0 ? void 0 : _instantSearchInstance__initialResults[this.getIndexId()];
8839
8878
  if (indexInitialResults === null || indexInitialResults === void 0 ? void 0 : indexInitialResults.results) {
@@ -9210,7 +9249,7 @@
9210
9249
  });
9211
9250
  }
9212
9251
 
9213
- var version = '4.108.0';
9252
+ var version = '4.110.0';
9214
9253
 
9215
9254
  var ANONYMOUS_TOKEN_COOKIE_KEY = '_ALGOLIA';
9216
9255
  function getCookie(name) {
@@ -9440,7 +9479,8 @@
9440
9479
  helper.overrideStateWithoutTriggeringChangeEvent(_object_spread_props(_object_spread({}, helper.state), {
9441
9480
  userToken: normalizedUserToken
9442
9481
  }));
9443
- if (existingToken && existingToken !== userToken) {
9482
+ if (existingToken && existingToken !== normalizedUserToken) {
9483
+ helper._recommendCache = {};
9444
9484
  instantSearchInstance.scheduleSearch();
9445
9485
  }
9446
9486
  }
@@ -9529,14 +9569,17 @@
9529
9569
  } else if (event.insightsMethod) {
9530
9570
  if (event.insightsMethod === 'viewedObjectIDs') {
9531
9571
  var payload = event.payload;
9572
+ var getViewEventKey = function getViewEventKey(objectID) {
9573
+ return payload.queryID ? "".concat(payload.queryID, ":").concat(objectID) : objectID;
9574
+ };
9532
9575
  var difference = payload.objectIDs.filter(function(objectID) {
9533
- return !viewedObjectIDs.has(objectID);
9576
+ return !viewedObjectIDs.has(getViewEventKey(objectID));
9534
9577
  });
9535
9578
  if (difference.length === 0) {
9536
9579
  return;
9537
9580
  }
9538
9581
  difference.forEach(function(objectID) {
9539
- return viewedObjectIDs.add(objectID);
9582
+ return viewedObjectIDs.add(getViewEventKey(objectID));
9540
9583
  });
9541
9584
  payload.objectIDs = difference;
9542
9585
  }
@@ -9644,10 +9687,10 @@
9644
9687
  * and the ability to set credentials via extra parameters when sending events.
9645
9688
  */ function isModernInsightsClient(client) {
9646
9689
  var _split_map = _sliced_to_array((client.version || '').split('.').map(Number), 2), major = _split_map[0], minor = _split_map[1];
9647
- /* eslint-disable instantsearch/naming-convention */ var v3 = major >= 3;
9690
+ /* oxlint-disable instantsearch/naming-convention */ var v3 = major >= 3;
9648
9691
  var v2_6 = major === 2 && minor >= 6;
9649
9692
  var v1_10 = major === 1 && minor >= 10;
9650
- /* eslint-enable instantsearch/naming-convention */ return v3 || v2_6 || v1_10;
9693
+ /* oxlint-enable instantsearch/naming-convention */ return v3 || v2_6 || v1_10;
9651
9694
  }
9652
9695
  /**
9653
9696
  * While `search-insights` supports both string and number user tokens,
@@ -11068,7 +11111,7 @@
11068
11111
  };
11069
11112
  }
11070
11113
 
11071
- var withUsage$r = createDocumentationMessageGenerator({
11114
+ var withUsage$t = createDocumentationMessageGenerator({
11072
11115
  name: 'instantsearch'
11073
11116
  });
11074
11117
  function defaultCreateURL() {
@@ -11131,7 +11174,7 @@
11131
11174
  _this.setMaxListeners(100);
11132
11175
  var _options_indexName = options.indexName, indexName = _options_indexName === void 0 ? '' : _options_indexName, compositionID = options.compositionID, numberLocale = options.numberLocale, _options_initialUiState = options.initialUiState, initialUiState = _options_initialUiState === void 0 ? {} : _options_initialUiState, _options_routing = options.routing, routing = _options_routing === void 0 ? null : _options_routing, _options_insights = options.insights, insights = _options_insights === void 0 ? undefined : _options_insights, searchFunction = options.searchFunction, _options_stalledSearchDelay = options.stalledSearchDelay, stalledSearchDelay = _options_stalledSearchDelay === void 0 ? 200 : _options_stalledSearchDelay, _options_searchClient = options.searchClient, searchClient = _options_searchClient === void 0 ? null : _options_searchClient, _options_insightsClient = options.insightsClient, insightsClient = _options_insightsClient === void 0 ? null : _options_insightsClient, _options_onStateChange = options.onStateChange, onStateChange = _options_onStateChange === void 0 ? null : _options_onStateChange, _options_future1 = options.future, future = _options_future1 === void 0 ? _object_spread({}, INSTANTSEARCH_FUTURE_DEFAULTS, options.future || {}) : _options_future1;
11133
11176
  if (searchClient === null) {
11134
- throw new Error(withUsage$r('The `searchClient` option is required.'));
11177
+ throw new Error(withUsage$t('The `searchClient` option is required.'));
11135
11178
  }
11136
11179
  if (typeof searchClient.search !== 'function') {
11137
11180
  throw new Error("The `searchClient` must implement a `search` method.\n\nSee: https://www.algolia.com/doc/guides/building-search-ui/going-further/backend-search/in-depth/backend-instantsearch/js/");
@@ -11140,7 +11183,7 @@
11140
11183
  searchClient.addAlgoliaAgent("instantsearch.js (".concat(version, ")"));
11141
11184
  }
11142
11185
  if (insightsClient && typeof insightsClient !== 'function') {
11143
- throw new Error(withUsage$r('The `insightsClient` option should be a function.'));
11186
+ throw new Error(withUsage$t('The `insightsClient` option should be a function.'));
11144
11187
  }
11145
11188
  _this._initialOptions = options;
11146
11189
  _this.client = searchClient;
@@ -11289,12 +11332,12 @@
11289
11332
  * @param widgets The array of widgets to add to InstantSearch.
11290
11333
  */ function addWidgets(widgets) {
11291
11334
  if (!Array.isArray(widgets)) {
11292
- throw new Error(withUsage$r('The `addWidgets` method expects an array of widgets. Please use `addWidget`.'));
11335
+ throw new Error(withUsage$t('The `addWidgets` method expects an array of widgets. Please use `addWidget`.'));
11293
11336
  }
11294
11337
  if (this.compositionID && widgets.some(function(w) {
11295
11338
  return !Array.isArray(w) && isIndexWidget(w) && !w._isolated;
11296
11339
  })) {
11297
- throw new Error(withUsage$r('The `index` widget cannot be used with a composition-based InstantSearch implementation.'));
11340
+ throw new Error(withUsage$t('The `index` widget cannot be used with a composition-based InstantSearch implementation.'));
11298
11341
  }
11299
11342
  this.mainIndex.addWidgets(widgets);
11300
11343
  return this;
@@ -11323,7 +11366,7 @@
11323
11366
  * The widgets must implement a `dispose()` method to clear their states.
11324
11367
  */ function removeWidgets(widgets) {
11325
11368
  if (!Array.isArray(widgets)) {
11326
- throw new Error(withUsage$r('The `removeWidgets` method expects an array of widgets. Please use `removeWidget`.'));
11369
+ throw new Error(withUsage$t('The `removeWidgets` method expects an array of widgets. Please use `removeWidget`.'));
11327
11370
  }
11328
11371
  this.mainIndex.removeWidgets(widgets);
11329
11372
  return this;
@@ -11337,7 +11380,7 @@
11337
11380
  */ function start() {
11338
11381
  var _this = this;
11339
11382
  if (this.started) {
11340
- throw new Error(withUsage$r('The `start` method has already been called once.'));
11383
+ throw new Error(withUsage$t('The `start` method has already been called once.'));
11341
11384
  }
11342
11385
  // This Helper is used for the queries, we don't care about its state. The
11343
11386
  // states are managed at the `index` level. We use this Helper to create
@@ -11545,7 +11588,7 @@
11545
11588
  var _this = this;
11546
11589
  var callOnStateChange = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : true;
11547
11590
  if (!this.mainHelper) {
11548
- throw new Error(withUsage$r('The `start` method needs to be called before `setUiState`.'));
11591
+ throw new Error(withUsage$t('The `start` method needs to be called before `setUiState`.'));
11549
11592
  }
11550
11593
  // We refresh the index UI state to update the local UI state that the
11551
11594
  // main index passes to the function form of `setUiState`.
@@ -11582,7 +11625,7 @@
11582
11625
  value: function createURL() {
11583
11626
  var nextState = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
11584
11627
  if (!this.started) {
11585
- throw new Error(withUsage$r('The `start` method needs to be called before `createURL`.'));
11628
+ throw new Error(withUsage$t('The `start` method needs to be called before `createURL`.'));
11586
11629
  }
11587
11630
  return this._createURL(nextState);
11588
11631
  }
@@ -11591,7 +11634,7 @@
11591
11634
  key: "refresh",
11592
11635
  value: function refresh() {
11593
11636
  if (!this.mainHelper) {
11594
- throw new Error(withUsage$r('The `start` method needs to be called before `refresh`.'));
11637
+ throw new Error(withUsage$t('The `start` method needs to be called before `refresh`.'));
11595
11638
  }
11596
11639
  this.mainHelper.clearCache().search();
11597
11640
  }
@@ -12102,6 +12145,10 @@
12102
12145
  var ssrSearchRef = React.useRef(null);
12103
12146
  // This is used to re-map the result index to the requesting widget
12104
12147
  var recommendIdx = React.useRef(0);
12148
+ var hydrationCompleteRef = React.useRef(false);
12149
+ React.useEffect(function() {
12150
+ hydrationCompleteRef.current = true;
12151
+ }, []);
12105
12152
  // When <DynamicWidgets> is mounted, a second provider is used above the user-land
12106
12153
  // <InstantSearchSSRProvider> in `getServerState()`.
12107
12154
  // To avoid the user's provider overriding the context value with an empty object,
@@ -12109,21 +12156,23 @@
12109
12156
  if (Object.keys(props).length === 0) {
12110
12157
  return /*#__PURE__*/ React.createElement(React.Fragment, null, children);
12111
12158
  }
12159
+ var contextValue = _object_spread_props(_object_spread({}, props), {
12160
+ ssrSearchRef: ssrSearchRef,
12161
+ recommendIdx: recommendIdx,
12162
+ hydrationCompleteRef: hydrationCompleteRef
12163
+ });
12112
12164
  return /*#__PURE__*/ React.createElement(InstantSearchSSRContext.Provider, {
12113
- value: _object_spread_props(_object_spread({}, props), {
12114
- ssrSearchRef: ssrSearchRef,
12115
- recommendIdx: recommendIdx
12116
- })
12165
+ value: contextValue
12117
12166
  }, children);
12118
12167
  }
12119
12168
 
12120
- var withUsage$q = createDocumentationMessageGenerator({
12169
+ var withUsage$s = createDocumentationMessageGenerator({
12121
12170
  name: 'autocomplete',
12122
12171
  connector: true
12123
12172
  });
12124
12173
  var connectAutocomplete = function connectAutocomplete(renderFn) {
12125
12174
  var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
12126
- checkRendering(renderFn, withUsage$q());
12175
+ checkRendering(renderFn, withUsage$s());
12127
12176
  return function(widgetParams) {
12128
12177
  var _ref = widgetParams || {}, _ref_escapeHTML = _ref.escapeHTML, escapeHTML = _ref_escapeHTML === void 0 ? true : _ref_escapeHTML, _ref_transformItems = _ref.transformItems, transformItems = _ref_transformItems === void 0 ? function(indices) {
12129
12178
  return indices;
@@ -12236,20 +12285,20 @@
12236
12285
  return useConnector(connectAutocomplete, props, additionalWidgetProperties);
12237
12286
  }
12238
12287
 
12239
- var withUsage$p = createDocumentationMessageGenerator({
12288
+ var withUsage$r = createDocumentationMessageGenerator({
12240
12289
  name: 'breadcrumb',
12241
12290
  connector: true
12242
12291
  });
12243
12292
  var connectBreadcrumb = function connectBreadcrumb(renderFn) {
12244
12293
  var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
12245
- checkRendering(renderFn, withUsage$p());
12294
+ checkRendering(renderFn, withUsage$r());
12246
12295
  var connectorState = {};
12247
12296
  return function(widgetParams) {
12248
12297
  var _ref = widgetParams || {}, attributes = _ref.attributes, _ref_separator = _ref.separator, separator = _ref_separator === void 0 ? ' > ' : _ref_separator, _ref_rootPath = _ref.rootPath, rootPath = _ref_rootPath === void 0 ? null : _ref_rootPath, _ref_transformItems = _ref.transformItems, transformItems = _ref_transformItems === void 0 ? function(items) {
12249
12298
  return items;
12250
12299
  } : _ref_transformItems;
12251
12300
  if (!attributes || !Array.isArray(attributes) || attributes.length === 0) {
12252
- throw new Error(withUsage$p('The `attributes` option expects an array of strings.'));
12301
+ throw new Error(withUsage$r('The `attributes` option expects an array of strings.'));
12253
12302
  }
12254
12303
  var _attributes = _sliced_to_array(attributes, 1), hierarchicalFacetName = _attributes[0];
12255
12304
  function getRefinedState(state, facetValue) {
@@ -12395,6 +12444,76 @@
12395
12444
  return useConnector(connectBreadcrumb, props, additionalWidgetProperties);
12396
12445
  }
12397
12446
 
12447
+ var tryParseJson = function tryParseJson(value) {
12448
+ try {
12449
+ return JSON.parse(value);
12450
+ } catch (unused) {
12451
+ return undefined;
12452
+ }
12453
+ };
12454
+ var repairPartialJson = function repairPartialJson(value) {
12455
+ var repaired = value.trim();
12456
+ if (!repaired) {
12457
+ return repaired;
12458
+ }
12459
+ var inString = false;
12460
+ var isEscaped = false;
12461
+ var stack = [];
12462
+ for(var index = 0; index < repaired.length; index++){
12463
+ var char = repaired[index];
12464
+ if (inString) {
12465
+ if (isEscaped) {
12466
+ isEscaped = false;
12467
+ } else if (char === '\\') {
12468
+ isEscaped = true;
12469
+ } else if (char === '"') {
12470
+ inString = false;
12471
+ }
12472
+ continue;
12473
+ }
12474
+ if (char === '"') {
12475
+ inString = true;
12476
+ continue;
12477
+ }
12478
+ if (char === '{' || char === '[') {
12479
+ stack.push(char);
12480
+ continue;
12481
+ }
12482
+ if (char === '}' && stack[stack.length - 1] === '{') {
12483
+ stack.pop();
12484
+ continue;
12485
+ }
12486
+ if (char === ']' && stack[stack.length - 1] === '[') {
12487
+ stack.pop();
12488
+ }
12489
+ }
12490
+ if (inString && !isEscaped) {
12491
+ repaired += '"';
12492
+ }
12493
+ repaired = repaired.replace(RegExp(",\\s*$", "u"), '');
12494
+ if (stack.length > 0) {
12495
+ repaired += stack.reverse().map(function(opening) {
12496
+ return opening === '{' ? '}' : ']';
12497
+ }).join('');
12498
+ }
12499
+ return repaired.replace(RegExp(",\\s*([}\\]])", "gu"), '$1');
12500
+ };
12501
+ var parsePartialJson = function parsePartialJson(accumulatedRawJson, fallbackValue) {
12502
+ var normalized = accumulatedRawJson.trim();
12503
+ if (!normalized) {
12504
+ return fallbackValue;
12505
+ }
12506
+ var directParsed = tryParseJson(normalized);
12507
+ if (directParsed !== undefined) {
12508
+ return directParsed;
12509
+ }
12510
+ var repairedParsed = tryParseJson(repairPartialJson(normalized));
12511
+ if (repairedParsed !== undefined) {
12512
+ return repairedParsed;
12513
+ }
12514
+ return fallbackValue;
12515
+ };
12516
+
12398
12517
  /**
12399
12518
  * Stream parser for parsing SSE (Server-Sent Events) streams.
12400
12519
  * The AI SDK 5 format uses SSE with JSON payloads prefixed by "data: ".
@@ -12676,75 +12795,6 @@
12676
12795
  }
12677
12796
 
12678
12797
  var _computedKey$1;
12679
- var tryParseJson = function tryParseJson(value) {
12680
- try {
12681
- return JSON.parse(value);
12682
- } catch (unused) {
12683
- return undefined;
12684
- }
12685
- };
12686
- var repairPartialJson = function repairPartialJson(value) {
12687
- var repaired = value.trim();
12688
- if (!repaired) {
12689
- return repaired;
12690
- }
12691
- var inString = false;
12692
- var isEscaped = false;
12693
- var stack = [];
12694
- for(var index = 0; index < repaired.length; index++){
12695
- var char = repaired[index];
12696
- if (inString) {
12697
- if (isEscaped) {
12698
- isEscaped = false;
12699
- } else if (char === '\\') {
12700
- isEscaped = true;
12701
- } else if (char === '"') {
12702
- inString = false;
12703
- }
12704
- continue;
12705
- }
12706
- if (char === '"') {
12707
- inString = true;
12708
- continue;
12709
- }
12710
- if (char === '{' || char === '[') {
12711
- stack.push(char);
12712
- continue;
12713
- }
12714
- if (char === '}' && stack[stack.length - 1] === '{') {
12715
- stack.pop();
12716
- continue;
12717
- }
12718
- if (char === ']' && stack[stack.length - 1] === '[') {
12719
- stack.pop();
12720
- }
12721
- }
12722
- if (inString && !isEscaped) {
12723
- repaired += '"';
12724
- }
12725
- repaired = repaired.replace(RegExp(",\\s*$", "u"), '');
12726
- if (stack.length > 0) {
12727
- repaired += stack.reverse().map(function(opening) {
12728
- return opening === '{' ? '}' : ']';
12729
- }).join('');
12730
- }
12731
- return repaired.replace(RegExp(",\\s*([}\\]])", "gu"), '$1');
12732
- };
12733
- var parseToolInputDelta = function parseToolInputDelta(accumulatedRawInput, fallbackInput) {
12734
- var normalized = accumulatedRawInput.trim();
12735
- if (!normalized) {
12736
- return fallbackInput;
12737
- }
12738
- var directParsed = tryParseJson(normalized);
12739
- if (directParsed !== undefined) {
12740
- return directParsed;
12741
- }
12742
- var repairedParsed = tryParseJson(repairPartialJson(normalized));
12743
- if (repairedParsed !== undefined) {
12744
- return repairedParsed;
12745
- }
12746
- return fallbackInput;
12747
- };
12748
12798
  var defaultGuardrailFallbackResponse = 'Sorry, we are not able to generate a response at the moment.';
12749
12799
  _computedKey$1 = /** @internal */ '~addToolResultForMessage';
12750
12800
  var _computedKey1$1 = _computedKey$1;
@@ -13578,7 +13628,7 @@
13578
13628
  toolRawInputByCallId[chunk.toolCallId] = nextRawInput;
13579
13629
  var toolName = (_chunk_toolName = chunk.toolName) !== null && _chunk_toolName !== void 0 ? _chunk_toolName : existingPart === null || existingPart === void 0 ? void 0 : (_existingPart_type = existingPart.type) === null || _existingPart_type === void 0 ? void 0 : _existingPart_type.replace('tool-', '');
13580
13630
  var shouldRepair = toolName ? (_ref2 = (_this_shouldRepairToolInput = (_this1 = _this).shouldRepairToolInput) === null || _this_shouldRepairToolInput === void 0 ? void 0 : _this_shouldRepairToolInput.call(_this1, toolName)) !== null && _ref2 !== void 0 ? _ref2 : true : true;
13581
- var parsedInput = shouldRepair ? parseToolInputDelta(nextRawInput, existingPart === null || existingPart === void 0 ? void 0 : existingPart.input) : existingPart === null || existingPart === void 0 ? void 0 : existingPart.input;
13631
+ var parsedInput = shouldRepair ? parsePartialJson(nextRawInput, existingPart === null || existingPart === void 0 ? void 0 : existingPart.input) : existingPart === null || existingPart === void 0 ? void 0 : existingPart.input;
13582
13632
  var nextToolPart = _object_spread_props(_object_spread({}, existingPart !== null && existingPart !== void 0 ? existingPart : {
13583
13633
  type: "tool-".concat(chunk.toolName),
13584
13634
  toolCallId: chunk.toolCallId
@@ -13681,7 +13731,7 @@
13681
13731
  var previousRawOutput = (_ref3 = (_ref4 = existingPart2 === null || existingPart2 === void 0 ? void 0 : existingPart2.rawOutput) !== null && _ref4 !== void 0 ? _ref4 : toolRawOutputByCallId[toolCallId]) !== null && _ref3 !== void 0 ? _ref3 : '';
13682
13732
  var nextRawOutput = "".concat(previousRawOutput).concat(delta);
13683
13733
  toolRawOutputByCallId[toolCallId] = nextRawOutput;
13684
- var parsedOutput = parseToolInputDelta(nextRawOutput, existingPart2 === null || existingPart2 === void 0 ? void 0 : existingPart2.output);
13734
+ var parsedOutput = parsePartialJson(nextRawOutput, existingPart2 === null || existingPart2 === void 0 ? void 0 : existingPart2.output);
13685
13735
  var nextToolPart1 = _object_spread_props(_object_spread({}, existingPart2 !== null && existingPart2 !== void 0 ? existingPart2 : {
13686
13736
  type: "tool-".concat(toolName1),
13687
13737
  toolCallId: toolCallId,
@@ -13971,9 +14021,25 @@
13971
14021
 
13972
14022
  var _computedKey, _computedKey1, _computedKey2, _computedKey3, _computedKey4, _computedKey5;
13973
14023
  var CACHE_KEY = 'instantsearch-chat-initial-messages';
14024
+ // Message history is a browser concern, and a server render constructs a Chat
14025
+ // too. Reading storage there throws during rendering; the write below only
14026
+ // throws into its own `catch`, so gating it just stops a pointless attempt.
13974
14027
  function getDefaultInitialMessages(id) {
13975
- var initialMessages = sessionStorage.getItem(CACHE_KEY + (id ? "-".concat(id) : ''));
13976
- return initialMessages ? JSON.parse(initialMessages) : [];
14028
+ return safelyRunOnBrowser(function() {
14029
+ try {
14030
+ // `sessionStorage` is not available in every environment with a
14031
+ // `window` (e.g. React Native), and some browsers throw on access
14032
+ // when storage is disabled.
14033
+ var initialMessages = sessionStorage.getItem(CACHE_KEY + (id ? "-".concat(id) : ''));
14034
+ return initialMessages ? JSON.parse(initialMessages) : [];
14035
+ } catch (e) {
14036
+ return [];
14037
+ }
14038
+ }, {
14039
+ fallback: function fallback() {
14040
+ return [];
14041
+ }
14042
+ });
13977
14043
  }
13978
14044
  _computedKey = '~registerMessagesCallback', _computedKey1 = '~registerStatusCallback', _computedKey2 = '~registerErrorCallback';
13979
14045
  var _computedKey6 = _computedKey, _computedKey7 = _computedKey1, _computedKey8 = _computedKey2;
@@ -14052,11 +14118,13 @@
14052
14118
  }
14053
14119
  var saveMessagesInLocalStorage = function saveMessagesInLocalStorage() {
14054
14120
  if (_this.status === 'ready') {
14055
- try {
14056
- sessionStorage.setItem(CACHE_KEY + (id ? "-".concat(id) : ''), JSON.stringify(_this.messages));
14057
- } catch (e) {
14058
- // Do nothing if sessionStorage is not available or full
14059
- }
14121
+ safelyRunOnBrowser(function() {
14122
+ try {
14123
+ sessionStorage.setItem(CACHE_KEY + (id ? "-".concat(id) : ''), JSON.stringify(_this.messages));
14124
+ } catch (e) {
14125
+ // Do nothing if sessionStorage is not available or full
14126
+ }
14127
+ });
14060
14128
  }
14061
14129
  };
14062
14130
  this['~registerMessagesCallback'](saveMessagesInLocalStorage);
@@ -14132,25 +14200,33 @@
14132
14200
  // it is non-empty and the chat is not already processing a message.
14133
14201
  // Returns true when a message was submitted, so callers can clear their input.
14134
14202
  function openChat(chatRenderState) {
14135
- var _ref = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}, message = _ref.message, referer = _ref.referer;
14203
+ var _ref = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}, message = _ref.message, referer = _ref.referer, turnContext = _ref.turnContext;
14136
14204
  var _ref1;
14137
14205
  var _chatRenderState_setOpen;
14138
14206
  if (!chatRenderState) {
14139
14207
  return false;
14140
14208
  }
14141
- (_chatRenderState_setOpen = chatRenderState.setOpen) === null || _chatRenderState_setOpen === void 0 ? void 0 : _chatRenderState_setOpen.call(chatRenderState, true);
14142
14209
  var trimmed = (_ref1 = message === null || message === void 0 ? void 0 : message.trim()) !== null && _ref1 !== void 0 ? _ref1 : '';
14143
14210
  if (!trimmed) {
14144
- var _chatRenderState_focusInput;
14145
- (_chatRenderState_focusInput = chatRenderState.focusInput) === null || _chatRenderState_focusInput === void 0 ? void 0 : _chatRenderState_focusInput.call(chatRenderState);
14211
+ if (chatRenderState.focusInput) {
14212
+ chatRenderState.focusInput();
14213
+ } else {
14214
+ var _chatRenderState_setOpen1;
14215
+ (_chatRenderState_setOpen1 = chatRenderState.setOpen) === null || _chatRenderState_setOpen1 === void 0 ? void 0 : _chatRenderState_setOpen1.call(chatRenderState, true);
14216
+ }
14146
14217
  return false;
14147
14218
  }
14219
+ (_chatRenderState_setOpen = chatRenderState.setOpen) === null || _chatRenderState_setOpen === void 0 ? void 0 : _chatRenderState_setOpen.call(chatRenderState, true);
14148
14220
  if (isChatBusy(chatRenderState) || !chatRenderState.sendMessage) {
14149
14221
  return false;
14150
14222
  }
14151
- chatRenderState.sendMessage({
14223
+ chatRenderState.sendMessage(_object_spread({
14152
14224
  text: trimmed
14153
- }, referer ? {
14225
+ }, turnContext ? {
14226
+ metadata: {
14227
+ turnContext: turnContext
14228
+ }
14229
+ } : {}), referer ? {
14154
14230
  headers: {
14155
14231
  'x-algolia-referer': referer
14156
14232
  }
@@ -14359,10 +14435,62 @@
14359
14435
  return DefaultChatTransport;
14360
14436
  }(HttpChatTransport);
14361
14437
 
14362
- var withUsage$o = createDocumentationMessageGenerator({
14438
+ var withUsage$q = createDocumentationMessageGenerator({
14363
14439
  name: 'chat',
14364
14440
  connector: true
14365
14441
  });
14442
+ var OPEN_STATE_CACHE_KEY = 'instantsearch-chat-open-state';
14443
+ function normalizePersistence(persistence, hasCustomChat) {
14444
+ if (hasCustomChat) {
14445
+ return {
14446
+ messages: false,
14447
+ open: persistence === undefined || (typeof persistence === "undefined" ? "undefined" : _type_of(persistence)) === 'object' && persistence.open === true
14448
+ };
14449
+ }
14450
+ if (persistence === undefined || persistence === true) {
14451
+ return {
14452
+ messages: true,
14453
+ open: true
14454
+ };
14455
+ }
14456
+ if (persistence === false) {
14457
+ return {
14458
+ messages: false,
14459
+ open: false
14460
+ };
14461
+ }
14462
+ return {
14463
+ messages: persistence.messages === true,
14464
+ open: persistence.open === true
14465
+ };
14466
+ }
14467
+ function getOpenStateCacheKey(type) {
14468
+ return "".concat(OPEN_STATE_CACHE_KEY, "-").concat(type);
14469
+ }
14470
+ function readPersistedOpen(type) {
14471
+ try {
14472
+ return safelyRunOnBrowser(function(param) {
14473
+ var browserWindow = param.window;
14474
+ return browserWindow.sessionStorage.getItem(getOpenStateCacheKey(type)) === 'true';
14475
+ }, {
14476
+ fallback: function fallback() {
14477
+ return false;
14478
+ }
14479
+ });
14480
+ } catch (unused) {
14481
+ return false;
14482
+ }
14483
+ }
14484
+ function writePersistedOpen(type, open) {
14485
+ try {
14486
+ safelyRunOnBrowser(function(param) {
14487
+ var browserWindow = param.window;
14488
+ browserWindow.sessionStorage.setItem(getOpenStateCacheKey(type), String(open));
14489
+ });
14490
+ } catch (unused) {
14491
+ // Storage availability must not block the visible state change.
14492
+ }
14493
+ }
14366
14494
  function getAttributesToClear$1(param) {
14367
14495
  var results = param.results, helper = param.helper;
14368
14496
  return uniq(getRefinements(results, helper.state, true).map(function(refinement) {
@@ -14380,26 +14508,36 @@
14380
14508
  attributesToClear: attributesToClear
14381
14509
  }));
14382
14510
  if (params.facetFilters) {
14383
- var attributes = flat(params.facetFilters).map(function(filter) {
14384
- var _filter_split = _sliced_to_array(filter.split(':'), 2), attribute = _filter_split[0], value = _filter_split[1];
14385
- return {
14386
- attribute: attribute,
14387
- value: value
14388
- };
14389
- });
14390
- attributes.forEach(function(param) {
14511
+ var refinements = flat(params.facetFilters).reduce(function(acc, filter) {
14512
+ var separatorIndex = filter.indexOf(':');
14513
+ if (separatorIndex > 0) {
14514
+ acc.push({
14515
+ attribute: filter.slice(0, separatorIndex),
14516
+ value: filter.slice(separatorIndex + 1)
14517
+ });
14518
+ }
14519
+ return acc;
14520
+ }, []);
14521
+ var hierarchicalRefinements = new Map();
14522
+ refinements.forEach(function(param) {
14391
14523
  var attribute = param.attribute, value = param.value;
14392
- if (!helper.state.isConjunctiveFacet(attribute) && !helper.state.isHierarchicalFacet(attribute) && !helper.state.isDisjunctiveFacet(attribute)) {
14393
- var s = helper.state.addDisjunctiveFacet(attribute);
14394
- helper.setState(s);
14395
- helper.toggleFacetRefinement(attribute, value);
14396
- } else {
14397
- var _helper_state_hierarchicalFacets_find;
14398
- var attr = ((_helper_state_hierarchicalFacets_find = helper.state.hierarchicalFacets.find(function(facet) {
14399
- return facet.name === attribute;
14400
- })) === null || _helper_state_hierarchicalFacets_find === void 0 ? void 0 : _helper_state_hierarchicalFacets_find.name) || attribute;
14401
- helper.toggleFacetRefinement(attr, value);
14524
+ var hierarchicalFacet = helper.state.hierarchicalFacets.find(function(facet) {
14525
+ return facet.name === attribute || facet.attributes.includes(attribute);
14526
+ });
14527
+ if (hierarchicalFacet) {
14528
+ var currentValue = hierarchicalRefinements.get(hierarchicalFacet.name);
14529
+ if (currentValue === undefined || value.length > currentValue.length) {
14530
+ hierarchicalRefinements.set(hierarchicalFacet.name, value);
14531
+ }
14532
+ return;
14402
14533
  }
14534
+ if (!helper.state.isConjunctiveFacet(attribute) && !helper.state.isDisjunctiveFacet(attribute)) {
14535
+ helper.setState(helper.state.addDisjunctiveFacet(attribute));
14536
+ }
14537
+ helper.toggleFacetRefinement(attribute, value);
14538
+ });
14539
+ hierarchicalRefinements.forEach(function(value, name) {
14540
+ helper.toggleFacetRefinement(name, value);
14403
14541
  });
14404
14542
  }
14405
14543
  if (params.query) {
@@ -14410,12 +14548,13 @@
14410
14548
  }
14411
14549
  var connectChat = function connectChat(renderFn) {
14412
14550
  var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
14413
- checkRendering(renderFn, withUsage$o());
14551
+ checkRendering(renderFn, withUsage$q());
14414
14552
  return function(widgetParams) {
14415
- 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, 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, [
14553
+ 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, [
14416
14554
  "resume",
14417
14555
  "tools",
14418
14556
  "type",
14557
+ "persistence",
14419
14558
  "context",
14420
14559
  "initialUserMessage",
14421
14560
  "initialMessages",
@@ -14423,6 +14562,7 @@
14423
14562
  "sendAutomaticallyWhen",
14424
14563
  "requiresSearch"
14425
14564
  ]);
14565
+ var normalizedPersistence = normalizePersistence(persistence, 'chat' in options);
14426
14566
  var _chatInstance;
14427
14567
  var input = '';
14428
14568
  var open = false;
@@ -14430,12 +14570,19 @@
14430
14570
  var setInput;
14431
14571
  var setOpen;
14432
14572
  var focusInput;
14573
+ var inputFocusRequested = false;
14433
14574
  var setFeedbackState;
14434
14575
  var hasValidatedEntryPoints = false;
14435
14576
  var agentId = 'agentId' in options ? options.agentId : undefined;
14436
14577
  var feedbackState = {};
14437
14578
  var _sendChatMessageFeedback;
14438
14579
  var feedbackAbortController;
14580
+ var chatSubscriptionUnsubscribers = [];
14581
+ var unsubscribeChatCallbacks = function unsubscribeChatCallbacks() {
14582
+ chatSubscriptionUnsubscribers.splice(0).forEach(function(unsubscribe) {
14583
+ return unsubscribe();
14584
+ });
14585
+ };
14439
14586
  // Extract suggestions from the last assistant message's data-suggestions part
14440
14587
  var getSuggestionsFromMessages = function getSuggestionsFromMessages(messages) {
14441
14588
  // Find the last assistant message (iterate from end)
@@ -14488,6 +14635,11 @@
14488
14635
  hasValidatedEntryPoints = true;
14489
14636
  };
14490
14637
  var makeChatInstance = function makeChatInstance(instantSearchInstance) {
14638
+ // A caller supplied `chat` already owns its transport, so it bypasses the
14639
+ // connector's transport construction and validation below.
14640
+ if ('chat' in options) {
14641
+ return options.chat;
14642
+ }
14491
14643
  var transport;
14492
14644
  var client = instantSearchInstance.client;
14493
14645
  var _getAppIdAndApiKey = _sliced_to_array(getAppIdAndApiKey(client), 2), appId = _getAppIdAndApiKey[0], apiKey = _getAppIdAndApiKey[1];
@@ -14537,7 +14689,7 @@
14537
14689
  if ('agentId' in options && options.agentId) {
14538
14690
  var _options_requestOptions, _options_requestOptions1;
14539
14691
  if (!appId || !apiKey) {
14540
- throw new Error(withUsage$o('Could not extract Algolia credentials from the search client.'));
14692
+ throw new Error(withUsage$q('Could not extract Algolia credentials from the search client.'));
14541
14693
  }
14542
14694
  var createApi = function createApi() {
14543
14695
  var bypassCache = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : false;
@@ -14554,7 +14706,7 @@
14554
14706
  var baseApi = createApi();
14555
14707
  transport = new DefaultChatTransport({
14556
14708
  api: baseApi,
14557
- headers: _object_spread_props(_object_spread({}, _instanceof((_options_requestOptions = options.requestOptions) === null || _options_requestOptions === void 0 ? void 0 : _options_requestOptions.headers, Headers) ? Object.fromEntries(options.requestOptions.headers.entries()) : (_options_requestOptions1 = options.requestOptions) === null || _options_requestOptions1 === void 0 ? void 0 : _options_requestOptions1.headers), {
14709
+ headers: _object_spread_props(_object_spread({}, typeof Headers !== 'undefined' && _instanceof((_options_requestOptions = options.requestOptions) === null || _options_requestOptions === void 0 ? void 0 : _options_requestOptions.headers, Headers) ? Object.fromEntries(options.requestOptions.headers.entries()) : (_options_requestOptions1 = options.requestOptions) === null || _options_requestOptions1 === void 0 ? void 0 : _options_requestOptions1.headers), {
14558
14710
  // Preserve the required Algolia identity headers and chat agent
14559
14711
  // marker, even when requestOptions.headers contains the same keys.
14560
14712
  'x-algolia-application-id': appId,
@@ -14576,12 +14728,10 @@
14576
14728
  });
14577
14729
  }
14578
14730
  if (!transport) {
14579
- throw new Error(withUsage$o('You need to provide either an `agentId` or a `transport`.'));
14580
- }
14581
- if ('chat' in options) {
14582
- return options.chat;
14731
+ throw new Error(withUsage$q('You need to provide either an `agentId` or a `transport`.'));
14583
14732
  }
14584
14733
  return new Chat(_object_spread_props(_object_spread({}, options), {
14734
+ persistence: normalizedPersistence.messages,
14585
14735
  sendAutomaticallyWhen: sendAutomaticallyWhen,
14586
14736
  transport: transport,
14587
14737
  shouldRepairToolInput: function shouldRepairToolInput(toolName) {
@@ -14630,22 +14780,30 @@
14630
14780
  var _this = this;
14631
14781
  var instantSearchInstance = initOptions.instantSearchInstance;
14632
14782
  validateEntryPoints(instantSearchInstance);
14783
+ open = normalizedPersistence.open ? readPersistedOpen(type) : false;
14633
14784
  _chatInstance = makeChatInstance(instantSearchInstance);
14634
14785
  var render = function render() {
14635
14786
  renderFn(_object_spread_props(_object_spread({}, _this.getWidgetRenderState(initOptions)), {
14636
14787
  instantSearchInstance: initOptions.instantSearchInstance
14637
14788
  }), false);
14638
14789
  };
14639
- setOpen = function setOpen(o) {
14640
- open = o;
14790
+ var updateOpen = function updateOpen(nextOpen, requestFocus) {
14791
+ open = nextOpen;
14792
+ inputFocusRequested = nextOpen && (inputFocusRequested || requestFocus);
14793
+ if (normalizedPersistence.open) {
14794
+ writePersistedOpen(type, open);
14795
+ }
14641
14796
  render();
14642
14797
  // `open` is read by sibling widgets (e.g. `chatTrigger`) via the
14643
14798
  // shared `renderState`. Schedule a full re-render so they pick up
14644
14799
  // the new value instead of staying frozen on their initial state.
14645
14800
  initOptions.instantSearchInstance.scheduleRender();
14646
14801
  };
14802
+ setOpen = function setOpen(nextOpen) {
14803
+ updateOpen(nextOpen, nextOpen && !open);
14804
+ };
14647
14805
  focusInput = function focusInput() {
14648
- setOpen(true);
14806
+ updateOpen(true, true);
14649
14807
  };
14650
14808
  setInput = function setInput(i) {
14651
14809
  input = i;
@@ -14659,7 +14817,7 @@
14659
14817
  if (agentId && feedback) {
14660
14818
  var _getAppIdAndApiKey = _sliced_to_array(getAppIdAndApiKey(initOptions.instantSearchInstance.client), 2), appId = _getAppIdAndApiKey[0], apiKey = _getAppIdAndApiKey[1];
14661
14819
  if (!appId || !apiKey) {
14662
- throw new Error(withUsage$o('Could not extract Algolia credentials from the search client.'));
14820
+ throw new Error(withUsage$q('Could not extract Algolia credentials from the search client.'));
14663
14821
  }
14664
14822
  feedbackAbortController = new AbortController();
14665
14823
  _sendChatMessageFeedback = function _sendChatMessageFeedback(messageId, vote) {
@@ -14679,25 +14837,42 @@
14679
14837
  };
14680
14838
  }
14681
14839
  var hasExistingMessages = _chatInstance.messages.length > 0;
14682
- // Set initialMessages before registering callbacks to avoid
14683
- // triggering re-renders during init
14684
- if ((initialMessages === null || initialMessages === void 0 ? void 0 : initialMessages.length) && !resume && !hasExistingMessages) {
14685
- _chatInstance.messages = initialMessages;
14686
- }
14687
- _chatInstance['~registerErrorCallback'](render);
14688
- _chatInstance['~registerMessagesCallback'](render);
14689
- _chatInstance['~registerStatusCallback'](render);
14690
- if (resume) {
14691
- _chatInstance.resumeStream();
14692
- }
14693
- if (initialUserMessage && !resume && !hasExistingMessages) {
14694
- _chatInstance.sendMessage({
14695
- text: initialUserMessage
14696
- });
14697
- }
14840
+ // Unsubscribe previous callbacks before setting initialMessages, then
14841
+ // register the current callbacks after to avoid re-renders during init.
14842
+ // A server render owns no conversation, so it leaves the instance empty.
14843
+ safelyRunOnBrowser(function() {
14844
+ unsubscribeChatCallbacks();
14845
+ if ((initialMessages === null || initialMessages === void 0 ? void 0 : initialMessages.length) && !resume && !hasExistingMessages) {
14846
+ _chatInstance.messages = initialMessages;
14847
+ }
14848
+ });
14849
+ safelyRunOnBrowser(function() {
14850
+ chatSubscriptionUnsubscribers = [
14851
+ _chatInstance['~registerErrorCallback'](render),
14852
+ _chatInstance['~registerMessagesCallback'](render),
14853
+ _chatInstance['~registerStatusCallback'](render)
14854
+ ];
14855
+ });
14856
+ // Resuming and sending reach the network, which a server render must
14857
+ // not: the HTML pass repeats what `getServerState` already rendered, so
14858
+ // each send happens at least twice, and each failure resolves into chat
14859
+ // state well after the render that started it has finished.
14860
+ safelyRunOnBrowser(function() {
14861
+ if (resume) {
14862
+ _chatInstance.resumeStream();
14863
+ }
14864
+ if (initialUserMessage && !resume && !hasExistingMessages) {
14865
+ _chatInstance.sendMessage({
14866
+ text: initialUserMessage
14867
+ });
14868
+ }
14869
+ });
14698
14870
  renderFn(_object_spread_props(_object_spread({}, this.getWidgetRenderState(initOptions)), {
14699
14871
  instantSearchInstance: instantSearchInstance
14700
14872
  }), true);
14873
+ if (open) {
14874
+ instantSearchInstance.scheduleRender();
14875
+ }
14701
14876
  },
14702
14877
  render: function render(renderOptions) {
14703
14878
  validateEntryPoints(renderOptions.instantSearchInstance);
@@ -14726,6 +14901,10 @@
14726
14901
  function applyFilters(params) {
14727
14902
  return updateStateFromSearchToolInput(params, helper);
14728
14903
  }
14904
+ var insightsEventContext = {
14905
+ agentId: agentId,
14906
+ instantSearchStatus: instantSearchInstance.status
14907
+ };
14729
14908
  var toolsWithAddToolResult = {};
14730
14909
  Object.entries(tools).forEach(function(param) {
14731
14910
  var _param = _sliced_to_array(param, 2), key = _param[0], tool = _param[1];
@@ -14733,7 +14912,8 @@
14733
14912
  addToolResult: _chatInstance.addToolResult,
14734
14913
  '~addToolResultForMessage': _chatInstance['~addToolResultForMessage'],
14735
14914
  applyFilters: applyFilters,
14736
- sendEvent: sendEvent
14915
+ sendEvent: sendEvent,
14916
+ insightsEventContext: insightsEventContext
14737
14917
  });
14738
14918
  toolsWithAddToolResult[key] = toolWithAddToolResult;
14739
14919
  });
@@ -14759,7 +14939,7 @@
14759
14939
  })
14760
14940
  ].concat(_to_consumable_array(rest)));
14761
14941
  };
14762
- return {
14942
+ var renderState = {
14763
14943
  indexUiState: instantSearchInstance.getUiState()[parent.getIndexId()],
14764
14944
  input: input,
14765
14945
  open: open,
@@ -14768,6 +14948,12 @@
14768
14948
  setInput: setInput,
14769
14949
  setOpen: setOpen,
14770
14950
  focusInput: focusInput,
14951
+ '~consumeInputFocus': function() {
14952
+ var shouldFocus = inputFocusRequested;
14953
+ inputFocusRequested = false;
14954
+ return shouldFocus;
14955
+ },
14956
+ '~isOpenStatePersistenceEnabled': normalizedPersistence.open,
14771
14957
  setMessages: setMessages,
14772
14958
  suggestions: getSuggestionsFromMessages(_chatInstance.messages),
14773
14959
  clearMessages: clearMessages,
@@ -14787,9 +14973,11 @@
14787
14973
  status: _chatInstance.status,
14788
14974
  stop: _chatInstance.stop
14789
14975
  };
14976
+ return renderState;
14790
14977
  },
14791
14978
  dispose: function dispose() {
14792
14979
  feedbackAbortController === null || feedbackAbortController === void 0 ? void 0 : feedbackAbortController.abort();
14980
+ unsubscribeChatCallbacks();
14793
14981
  unmountFn();
14794
14982
  },
14795
14983
  shouldRender: function shouldRender() {
@@ -14802,8 +14990,624 @@
14802
14990
  };
14803
14991
  };
14804
14992
 
14993
+ var subscribe = function subscribe() {
14994
+ return function() {};
14995
+ };
14996
+ var getClientSnapshot = function getClientSnapshot() {
14997
+ return true;
14998
+ };
14999
+ var getServerSnapshot = function getServerSnapshot() {
15000
+ return false;
15001
+ };
15002
+ function useNativeIsHydrated() {
15003
+ return React__namespace.useSyncExternalStore(subscribe, getClientSnapshot, getServerSnapshot);
15004
+ }
15005
+ // React 16 and 17 have no `useSyncExternalStore`, so the flip waits for an
15006
+ // effect and the render itself cannot tell hydration from a plain mount. These
15007
+ // contexts provide that signal for the supported `getServerState` and
15008
+ // `InstantSearchSSRProvider` flow: the server context covers state collection,
15009
+ // and the SSR context covers HTML rendering and hydration.
15010
+ //
15011
+ // The shim's React 16 and 17 fallback ignores the server snapshot, so using it
15012
+ // here would not distinguish initial hydration from later provider children.
15013
+ function useLegacyIsHydrated() {
15014
+ var _ssrContext_hydrationCompleteRef;
15015
+ var serverContext = useInstantSearchServerContext();
15016
+ var ssrContext = useInstantSearchSSRContext();
15017
+ var isServerRendered = serverContext !== null || ssrContext !== null;
15018
+ var isProviderHydrated = (ssrContext === null || ssrContext === void 0 ? void 0 : (_ssrContext_hydrationCompleteRef = ssrContext.hydrationCompleteRef) === null || _ssrContext_hydrationCompleteRef === void 0 ? void 0 : _ssrContext_hydrationCompleteRef.current) === true;
15019
+ var _React_useState = _sliced_to_array(React__namespace.useState(!isServerRendered || isProviderHydrated), 2), isHydrated = _React_useState[0], setIsHydrated = _React_useState[1];
15020
+ React__namespace.useEffect(function() {
15021
+ setIsHydrated(true);
15022
+ }, []);
15023
+ return isHydrated;
15024
+ }
15025
+ /**
15026
+ * Whether this render can use browser state, or has to reproduce the markup a
15027
+ * server produced without it.
15028
+ *
15029
+ * @internal
15030
+ */ var useIsHydrated = typeof React__namespace.useSyncExternalStore === 'function' ? useNativeIsHydrated : useLegacyIsHydrated;
15031
+
14805
15032
  function useChat(props, additionalWidgetProperties) {
14806
- return useConnector(connectChat, props, additionalWidgetProperties);
15033
+ var isHydrated = useIsHydrated();
15034
+ var chatState = useConnector(connectChat, props, additionalWidgetProperties);
15035
+ if (isHydrated) {
15036
+ return chatState;
15037
+ }
15038
+ // Server rendering only promises the closed Chat shell, so a render that has
15039
+ // to reproduce that markup shows no conversation either. `status` is pinned
15040
+ // with `messages` because it diverges two ways: a server render suppresses
15041
+ // `resumeStream()`, which the browser runs synchronously while initialising,
15042
+ // and a caller-owned chat can already be streaming. `error` is pinned because
15043
+ // a caller-owned chat can already have failed, which a connector-built one
15044
+ // cannot, since its failures arrive in a microtask. `suggestions` is pinned
15045
+ // because the connector derives them from those messages. Only an `id` given
15046
+ // as a connector option passes through, because that value is the same on
15047
+ // both sides. Anything else is withheld: the default is random per Chat, and
15048
+ // an `id` carried by a caller-owned instance is no safer, since the server and
15049
+ // the browser each construct their own.
15050
+ return _object_spread_props(_object_spread({}, chatState), {
15051
+ error: undefined,
15052
+ id: 'id' in props && props.id || '',
15053
+ messages: [],
15054
+ open: false,
15055
+ status: 'ready',
15056
+ suggestions: undefined
15057
+ });
15058
+ }
15059
+
15060
+ function buildEndpoint(param) {
15061
+ var appId = param.appId, agentId = param.agentId;
15062
+ return "https://".concat(appId, ".algolia.net/agent-studio/1/agents/").concat(agentId, "/tasks");
15063
+ }
15064
+ function resolveEndpoint(params) {
15065
+ if (params.transport) {
15066
+ return {
15067
+ endpoint: params.transport.api,
15068
+ headers: params.transport.headers || {},
15069
+ prepareSendMessagesRequest: params.transport.prepareSendMessagesRequest
15070
+ };
15071
+ }
15072
+ if (!params.appId || !params.apiKey || !params.agentId) {
15073
+ throw new Error('[tasks] Either `transport` or `{ appId, apiKey, agentId }` is required.');
15074
+ }
15075
+ var headers = {
15076
+ 'x-algolia-application-id': params.appId,
15077
+ 'x-algolia-api-key': params.apiKey
15078
+ };
15079
+ if (params.algoliaAgent) {
15080
+ headers['x-algolia-agent'] = "".concat(params.algoliaAgent, "; tasks");
15081
+ }
15082
+ return {
15083
+ endpoint: buildEndpoint({
15084
+ appId: params.appId,
15085
+ agentId: params.agentId
15086
+ }),
15087
+ headers: headers
15088
+ };
15089
+ }
15090
+
15091
+ function buildTaskPayload(param) {
15092
+ var task = param.task, input = param.input, prepareRequest = param.prepareRequest;
15093
+ var payload = {
15094
+ task: task,
15095
+ input: input
15096
+ };
15097
+ return prepareRequest ? prepareRequest(payload).body : payload;
15098
+ }
15099
+ function withStreamParam(url) {
15100
+ return url.includes('?') ? "".concat(url, "&stream=true") : "".concat(url, "?stream=true");
15101
+ }
15102
+ function resolveStreamedOutput(data, previous) {
15103
+ return typeof data === 'string' ? parsePartialJson(data, previous) : data;
15104
+ }
15105
+ function consumeTaskStream(body, onData) {
15106
+ return new Promise(function(resolve, reject) {
15107
+ var chunkStream = parseJsonEventStream(body);
15108
+ var latest;
15109
+ processStream(chunkStream, function(chunk) {
15110
+ if (!chunk) {
15111
+ return;
15112
+ }
15113
+ // A terminal `error` event aborts the task: reject rather than let the
15114
+ // stream close and resolve the last partial snapshot as a success.
15115
+ // Throwing here lets `processStream` release the reader and stop
15116
+ // consuming; the rejection propagates to the caller's `.catch`.
15117
+ if (chunk.type === 'error') {
15118
+ throw new Error(chunk.errorText || 'Task stream error');
15119
+ }
15120
+ if (chunk.type !== 'data-task-output') {
15121
+ return;
15122
+ }
15123
+ latest = resolveStreamedOutput(chunk.data, latest);
15124
+ if (onData) {
15125
+ onData(latest);
15126
+ }
15127
+ }, function() {
15128
+ return resolve(latest);
15129
+ }, reject);
15130
+ });
15131
+ }
15132
+ function fetchTask(param) {
15133
+ 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;
15134
+ return fetch(stream ? withStreamParam(endpoint) : endpoint, {
15135
+ method: 'POST',
15136
+ headers: _object_spread_props(_object_spread({}, headers), {
15137
+ 'Content-Type': 'application/json'
15138
+ }),
15139
+ body: JSON.stringify(payload)
15140
+ }).then(function(response) {
15141
+ var _response_headers_get, _response_headers;
15142
+ if (!response.ok) {
15143
+ throw new Error("HTTP error ".concat(response.status));
15144
+ }
15145
+ 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')) || '';
15146
+ if (stream && response.body && contentType.includes('text/event-stream')) {
15147
+ return consumeTaskStream(response.body, onData);
15148
+ }
15149
+ return response.json();
15150
+ });
15151
+ }
15152
+ function unwrap(envelope) {
15153
+ return envelope === null || envelope === void 0 ? void 0 : envelope.output;
15154
+ }
15155
+ function createTaskRunner(param) {
15156
+ 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;
15157
+ return {
15158
+ submit: function submit(variables) {
15159
+ var onData = (arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}).onData;
15160
+ var payload = buildTaskPayload({
15161
+ task: task,
15162
+ input: variables,
15163
+ prepareRequest: prepareRequest
15164
+ });
15165
+ return fetchTask({
15166
+ endpoint: endpoint,
15167
+ headers: headers,
15168
+ payload: payload,
15169
+ stream: stream,
15170
+ onData: onData ? function(partial) {
15171
+ return onData(unwrap(partial));
15172
+ } : undefined
15173
+ }).then(unwrap);
15174
+ }
15175
+ };
15176
+ }
15177
+
15178
+ var withUsage$p = createDocumentationMessageGenerator({
15179
+ name: 'tasks',
15180
+ connector: true
15181
+ });
15182
+ var connectTasks = function connectTasks(renderFn) {
15183
+ var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
15184
+ checkRendering(renderFn, withUsage$p());
15185
+ return function(widgetParams) {
15186
+ var agentId = widgetParams.agentId, transport = widgetParams.transport, task = widgetParams.task, _widgetParams_stream = widgetParams.stream, stream = _widgetParams_stream === void 0 ? true : _widgetParams_stream;
15187
+ if (!agentId && !transport) {
15188
+ throw new Error(withUsage$p('The `agentId` option is required unless a custom `transport` is provided.'));
15189
+ }
15190
+ if (!task) {
15191
+ throw new Error(withUsage$p('The `task` option is required.'));
15192
+ }
15193
+ var runner;
15194
+ var output;
15195
+ var isLoading = false;
15196
+ var error;
15197
+ var disposed = false;
15198
+ var triggerRender = noop;
15199
+ var requestId = 0;
15200
+ var submit = function submit(variables) {
15201
+ if (disposed) return Promise.resolve(undefined);
15202
+ var currentRequestId = requestId += 1;
15203
+ var isStale = function isStale() {
15204
+ return disposed || currentRequestId !== requestId;
15205
+ };
15206
+ // Clear the previous output so consumers can show a loading state
15207
+ // rather than stale data while the new request is in flight.
15208
+ output = undefined;
15209
+ error = undefined;
15210
+ isLoading = true;
15211
+ triggerRender();
15212
+ return Promise.resolve().then(function() {
15213
+ return runner.submit(variables, {
15214
+ onData: stream ? function(partial) {
15215
+ if (isStale()) return;
15216
+ output = partial;
15217
+ triggerRender();
15218
+ } : undefined
15219
+ });
15220
+ }).then(function(next) {
15221
+ var result = next;
15222
+ if (!isStale()) {
15223
+ output = result;
15224
+ }
15225
+ return result;
15226
+ }).catch(function(err) {
15227
+ if (!isStale()) {
15228
+ output = undefined;
15229
+ error = _instanceof(err, Error) ? err : new Error(String(err));
15230
+ }
15231
+ return undefined;
15232
+ }).finally(function() {
15233
+ if (isStale()) return;
15234
+ isLoading = false;
15235
+ triggerRender();
15236
+ });
15237
+ };
15238
+ var invalidate = function invalidate() {
15239
+ if (disposed) return;
15240
+ // Bump the request id so any in-flight request's callbacks see
15241
+ // `isStale()` and are ignored. The fetch itself is left to complete.
15242
+ requestId += 1;
15243
+ isLoading = false;
15244
+ triggerRender();
15245
+ };
15246
+ var getWidgetRenderState = function getWidgetRenderState() {
15247
+ return {
15248
+ output: output,
15249
+ isLoading: isLoading,
15250
+ error: error,
15251
+ submit: submit,
15252
+ invalidate: invalidate,
15253
+ widgetParams: widgetParams
15254
+ };
15255
+ };
15256
+ return {
15257
+ $$type: 'ais.tasks',
15258
+ init: function init(initOptions) {
15259
+ var instantSearchInstance = initOptions.instantSearchInstance;
15260
+ if (transport) {
15261
+ var resolved = resolveEndpoint({
15262
+ transport: transport
15263
+ });
15264
+ runner = createTaskRunner({
15265
+ endpoint: resolved.endpoint,
15266
+ headers: resolved.headers,
15267
+ task: task,
15268
+ stream: stream,
15269
+ prepareRequest: resolved.prepareSendMessagesRequest
15270
+ });
15271
+ } else {
15272
+ var _getAppIdAndApiKey = _sliced_to_array(getAppIdAndApiKey(instantSearchInstance.client), 2), appId = _getAppIdAndApiKey[0], apiKey = _getAppIdAndApiKey[1];
15273
+ if (!appId || !apiKey) {
15274
+ throw new Error(withUsage$p('Could not extract Algolia credentials from the search client.'));
15275
+ }
15276
+ var resolved1 = resolveEndpoint({
15277
+ appId: appId,
15278
+ apiKey: apiKey,
15279
+ agentId: agentId,
15280
+ algoliaAgent: getAlgoliaAgent(instantSearchInstance.client)
15281
+ });
15282
+ runner = createTaskRunner({
15283
+ endpoint: resolved1.endpoint,
15284
+ headers: resolved1.headers,
15285
+ task: task,
15286
+ stream: stream
15287
+ });
15288
+ }
15289
+ triggerRender = function triggerRender() {
15290
+ renderFn(_object_spread_props(_object_spread({}, getWidgetRenderState()), {
15291
+ instantSearchInstance: instantSearchInstance
15292
+ }), false);
15293
+ };
15294
+ renderFn(_object_spread_props(_object_spread({}, getWidgetRenderState()), {
15295
+ instantSearchInstance: instantSearchInstance
15296
+ }), true);
15297
+ },
15298
+ render: function render(renderOptions) {
15299
+ renderFn(_object_spread_props(_object_spread({}, getWidgetRenderState()), {
15300
+ instantSearchInstance: renderOptions.instantSearchInstance
15301
+ }), false);
15302
+ },
15303
+ dispose: function dispose() {
15304
+ disposed = true;
15305
+ unmountFn();
15306
+ }
15307
+ };
15308
+ };
15309
+ };
15310
+
15311
+ var withUsage$o = createDocumentationMessageGenerator({
15312
+ name: 'prompt-suggestions',
15313
+ connector: true
15314
+ });
15315
+ var RENDER_STATE_KEY = 'promptSuggestions';
15316
+ var CHAT_RENDER_STATE_KEY = 'chat';
15317
+ var DEBOUNCE_MS = 300;
15318
+ function parseSuggestions(data) {
15319
+ var suggestions = data === null || data === void 0 ? void 0 : data.suggestions;
15320
+ if (!Array.isArray(suggestions)) {
15321
+ return [];
15322
+ }
15323
+ return suggestions.filter(function(s) {
15324
+ return typeof s === 'string';
15325
+ });
15326
+ }
15327
+ function buildSuggestionMessage(suggestion) {
15328
+ return "The user clicked this on-page suggestion. Use the current page context first, then search only if needed.\n\nSuggestion: ".concat(suggestion);
15329
+ }
15330
+ function stripInternalHitMetadata(hit) {
15331
+ var clean = {};
15332
+ Object.keys(hit).forEach(function(key) {
15333
+ // Strip internal metadata, which is `_`-prefixed
15334
+ // (`_highlightResult`, `_rankingInfo`, `__position`, …).
15335
+ if (!key.startsWith('_')) {
15336
+ clean[key] = hit[key];
15337
+ }
15338
+ });
15339
+ return clean;
15340
+ }
15341
+ var DEFAULT_TRANSFORM_HITS = function DEFAULT_TRANSFORM_HITS(hits) {
15342
+ return hits.slice(0, 5).map(stripInternalHitMetadata);
15343
+ };
15344
+ function buildFilters(results) {
15345
+ var state = results._state;
15346
+ if (!state) {
15347
+ return undefined;
15348
+ }
15349
+ var groups = [];
15350
+ var disjunctiveGroups = {};
15351
+ getRefinements(results, state).forEach(function(refinement) {
15352
+ if (refinement.type === 'numeric') {
15353
+ groups.push([
15354
+ "".concat(refinement.attribute).concat(refinement.operator).concat(refinement.numericValue)
15355
+ ]);
15356
+ return;
15357
+ }
15358
+ var value = refinement.type === 'exclude' ? "".concat(refinement.attribute, ":-").concat(refinement.name) : "".concat(refinement.attribute, ":").concat(refinement.name);
15359
+ if (refinement.type === 'disjunctive') {
15360
+ var group = disjunctiveGroups[refinement.attribute];
15361
+ if (group) {
15362
+ group.push(value);
15363
+ } else {
15364
+ var newGroup = [
15365
+ value
15366
+ ];
15367
+ disjunctiveGroups[refinement.attribute] = newGroup;
15368
+ groups.push(newGroup);
15369
+ }
15370
+ return;
15371
+ }
15372
+ groups.push([
15373
+ value
15374
+ ]);
15375
+ });
15376
+ return groups.length > 0 ? groups : undefined;
15377
+ }
15378
+ var connectPromptSuggestions = function connectPromptSuggestions(renderFn) {
15379
+ var unmountFn = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : noop;
15380
+ checkRendering(renderFn, withUsage$o());
15381
+ return function(widgetParams) {
15382
+ var agentId = widgetParams.agentId, configurationId = widgetParams.configurationId, _widgetParams_transformHits = widgetParams.transformHits, transformHits = _widgetParams_transformHits === void 0 ? DEFAULT_TRANSFORM_HITS : _widgetParams_transformHits, context = widgetParams.context, _widgetParams_transformItems = widgetParams.transformItems, transformItems = _widgetParams_transformItems === void 0 ? function(items) {
15383
+ return items;
15384
+ } : _widgetParams_transformItems, transport = widgetParams.transport;
15385
+ if (!agentId && !transport) {
15386
+ throw new Error(withUsage$o('The `agentId` option is required unless a custom `transport` is provided.'));
15387
+ }
15388
+ if (!configurationId) {
15389
+ throw new Error(withUsage$o('The `configurationId` option is required.'));
15390
+ }
15391
+ var tasksState;
15392
+ var suggestions = [];
15393
+ var isLoading = false;
15394
+ var debounceTimer;
15395
+ var lastStateSignature = null;
15396
+ var latestRenderOptions = null;
15397
+ // Set in `dispose()`. A debounced or in-flight `fetch()` can resolve after
15398
+ // the widget is unmounted; this guard stops those late callbacks from
15399
+ // calling `renderFn` into a torn-down container.
15400
+ var disposed = false;
15401
+ // True between a state-signature change and the debounced refetch that
15402
+ // follows it. While pending, the search state has already moved on, so a
15403
+ // still-in-flight request from the previous state must not paint its
15404
+ // suggestions, its inner render is ignored until the new `submit` starts.
15405
+ var refetchPending = false;
15406
+ var getStateSignature = function getStateSignature(results) {
15407
+ var _buildFilters;
15408
+ if (results.queryID) {
15409
+ return results.queryID;
15410
+ }
15411
+ var query = results.query || '';
15412
+ var filters = JSON.stringify((_buildFilters = buildFilters(results)) !== null && _buildFilters !== void 0 ? _buildFilters : []);
15413
+ var hitIds = (results.hits || []).map(function(hit) {
15414
+ return hit.objectID;
15415
+ }).join(',');
15416
+ return "".concat(query, "|").concat(filters, "|").concat(hitIds);
15417
+ };
15418
+ var getChatRenderState = function getChatRenderState(renderOptions) {
15419
+ var _instantSearchInstance_renderState;
15420
+ var instantSearchInstance = renderOptions.instantSearchInstance, parent = renderOptions.parent;
15421
+ var indexId = parent ? parent.getIndexId() : '';
15422
+ if (!indexId || !((_instantSearchInstance_renderState = instantSearchInstance.renderState) === null || _instantSearchInstance_renderState === void 0 ? void 0 : _instantSearchInstance_renderState[indexId])) {
15423
+ return undefined;
15424
+ }
15425
+ return instantSearchInstance.renderState[indexId][CHAT_RENDER_STATE_KEY];
15426
+ };
15427
+ var sendToChat = function sendToChat(renderOptions) {
15428
+ return function(prompt) {
15429
+ var _ref, _ref1;
15430
+ var chatRenderState = getChatRenderState(renderOptions);
15431
+ if (!chatRenderState || !chatRenderState.sendMessage) {
15432
+ return false;
15433
+ }
15434
+ var results = (_ref = (_ref1 = latestRenderOptions === null || latestRenderOptions === void 0 ? void 0 : latestRenderOptions.results) !== null && _ref1 !== void 0 ? _ref1 : 'results' in renderOptions ? renderOptions.results : null) !== null && _ref !== void 0 ? _ref : null;
15435
+ return openChat(chatRenderState, {
15436
+ message: buildSuggestionMessage(prompt),
15437
+ referer: 'prompt-suggestions-widget',
15438
+ turnContext: buildTurnContext(results)
15439
+ });
15440
+ };
15441
+ };
15442
+ var resolvePageContext = function resolvePageContext(results) {
15443
+ var resolvedContext = typeof context === 'function' ? context() : context;
15444
+ // Explicit context replaces auto-extraction; otherwise derive it from
15445
+ // the current search state. The task's server-owned instructions decide
15446
+ // how to interpret the shape — the client doesn't label it.
15447
+ if (resolvedContext) {
15448
+ return _object_spread({}, resolvedContext);
15449
+ }
15450
+ if (!results) {
15451
+ return undefined;
15452
+ }
15453
+ var filters = buildFilters(results);
15454
+ return _object_spread_props(_object_spread({
15455
+ query: results.query || ''
15456
+ }, filters ? {
15457
+ filters: filters
15458
+ } : {}), {
15459
+ hitsSample: transformHits(results.hits)
15460
+ });
15461
+ };
15462
+ var buildInput = function buildInput(results) {
15463
+ var _resolvePageContext;
15464
+ return (_resolvePageContext = resolvePageContext(results)) !== null && _resolvePageContext !== void 0 ? _resolvePageContext : {};
15465
+ };
15466
+ // The same page context, flattened for the chat handoff: `turnContext` is
15467
+ // a flat `Record<string, string>` per the Agent Studio contract, so
15468
+ // non-string values (e.g. `hitsSample`) are serialized.
15469
+ var buildTurnContext = function buildTurnContext(results) {
15470
+ var pageContext = resolvePageContext(results);
15471
+ if (!pageContext) {
15472
+ return undefined;
15473
+ }
15474
+ var entries = Object.entries(pageContext).map(function(param) {
15475
+ var _param = _sliced_to_array(param, 2), key = _param[0], value = _param[1];
15476
+ return [
15477
+ key,
15478
+ typeof value === 'string' ? value : JSON.stringify(value)
15479
+ ];
15480
+ }).filter(function(param) {
15481
+ var _param = _sliced_to_array(param, 2), value = _param[1];
15482
+ return value.trim() !== '';
15483
+ });
15484
+ return entries.length > 0 ? Object.fromEntries(entries) : undefined;
15485
+ };
15486
+ var renderOutward = function renderOutward(renderOptions) {
15487
+ if (disposed) return;
15488
+ renderFn(_object_spread_props(_object_spread({}, getWidgetRenderState(renderOptions)), {
15489
+ instantSearchInstance: renderOptions.instantSearchInstance
15490
+ }), false);
15491
+ };
15492
+ var fetchAndRender = function fetchAndRender(results, renderOptions) {
15493
+ var _results_hits;
15494
+ if (disposed || !tasksState) return;
15495
+ refetchPending = false;
15496
+ var hasContext = context !== undefined;
15497
+ if (!hasContext && !(results === null || results === void 0 ? void 0 : (_results_hits = results.hits) === null || _results_hits === void 0 ? void 0 : _results_hits.length)) {
15498
+ tasksState.invalidate();
15499
+ suggestions = [];
15500
+ isLoading = false;
15501
+ renderOutward(renderOptions);
15502
+ return;
15503
+ }
15504
+ tasksState.submit(buildInput(results));
15505
+ };
15506
+ var refresh = function refresh() {
15507
+ if (isLoading) return;
15508
+ var results = latestRenderOptions === null || latestRenderOptions === void 0 ? void 0 : latestRenderOptions.results;
15509
+ if (!results || !latestRenderOptions) return;
15510
+ clearTimeout(debounceTimer);
15511
+ lastStateSignature = getStateSignature(results);
15512
+ fetchAndRender(results, latestRenderOptions);
15513
+ };
15514
+ var getWidgetRenderState = function getWidgetRenderState(renderOptions) {
15515
+ var results = 'results' in renderOptions ? renderOptions.results : undefined;
15516
+ var transformed = transformItems(suggestions, {
15517
+ query: (results === null || results === void 0 ? void 0 : results.query) || '',
15518
+ results: results || null
15519
+ });
15520
+ var chatRenderState = getChatRenderState(renderOptions);
15521
+ var isChatBusy$1 = chatRenderState ? !chatRenderState.sendMessage || isChatBusy(chatRenderState) : false;
15522
+ var send = sendToChat(renderOptions);
15523
+ return {
15524
+ suggestions: transformed,
15525
+ isLoading: isLoading,
15526
+ onSuggestionClick: send,
15527
+ sendToChat: send,
15528
+ refresh: refresh,
15529
+ isChatBusy: isChatBusy$1,
15530
+ widgetParams: widgetParams
15531
+ };
15532
+ };
15533
+ // Mirrors each inner render (submit start → skeleton, stream partials,
15534
+ // resolve/error) into this widget's state and re-renders on the client.
15535
+ var handleInnerRender = function handleInnerRender(renderState) {
15536
+ tasksState = renderState;
15537
+ if (refetchPending) return;
15538
+ if (renderState.error) {
15539
+ // A failed task (including a mid-stream `error` event) must not leave
15540
+ // any streamed partial visible. There's no error UI for now, so fall
15541
+ // back to a blank suggestions state.
15542
+ suggestions = [];
15543
+ } else if (renderState.isLoading || renderState.output !== undefined) {
15544
+ // Only adopt the inner output once a request is loading or has
15545
+ // produced one, so the initial no-op render doesn't clobber pills.
15546
+ suggestions = parseSuggestions(renderState.output);
15547
+ }
15548
+ isLoading = renderState.isLoading;
15549
+ if (!latestRenderOptions) return;
15550
+ renderOutward(latestRenderOptions);
15551
+ };
15552
+ var tasksWidget = connectTasks(handleInnerRender, noop)(_object_spread_props(_object_spread({}, transport ? {
15553
+ transport: transport
15554
+ } : {
15555
+ agentId: agentId
15556
+ }), {
15557
+ task: configurationId,
15558
+ stream: true
15559
+ }));
15560
+ return {
15561
+ $$type: 'ais.promptSuggestions',
15562
+ init: function init(initOptions) {
15563
+ var instantSearchInstance = initOptions.instantSearchInstance;
15564
+ tasksWidget.init(initOptions);
15565
+ renderFn(_object_spread_props(_object_spread({}, getWidgetRenderState(initOptions)), {
15566
+ instantSearchInstance: instantSearchInstance
15567
+ }), true);
15568
+ },
15569
+ render: function render(renderOptions) {
15570
+ var results = renderOptions.results, instantSearchInstance = renderOptions.instantSearchInstance;
15571
+ latestRenderOptions = renderOptions;
15572
+ if (!results) {
15573
+ renderFn(_object_spread_props(_object_spread({}, getWidgetRenderState(renderOptions)), {
15574
+ instantSearchInstance: instantSearchInstance
15575
+ }), false);
15576
+ return;
15577
+ }
15578
+ var stateSignature = getStateSignature(results);
15579
+ if (stateSignature !== lastStateSignature) {
15580
+ lastStateSignature = stateSignature;
15581
+ refetchPending = true;
15582
+ clearTimeout(debounceTimer);
15583
+ debounceTimer = setTimeout(function() {
15584
+ if (latestRenderOptions === null || latestRenderOptions === void 0 ? void 0 : latestRenderOptions.results) {
15585
+ fetchAndRender(latestRenderOptions.results, latestRenderOptions);
15586
+ }
15587
+ }, DEBOUNCE_MS);
15588
+ }
15589
+ renderFn(_object_spread_props(_object_spread({}, getWidgetRenderState(renderOptions)), {
15590
+ instantSearchInstance: instantSearchInstance
15591
+ }), false);
15592
+ },
15593
+ dispose: function dispose(disposeOptions) {
15594
+ disposed = true;
15595
+ clearTimeout(debounceTimer);
15596
+ tasksWidget.dispose(disposeOptions);
15597
+ unmountFn();
15598
+ },
15599
+ getRenderState: function getRenderState(renderState, renderOptions) {
15600
+ return _object_spread_props(_object_spread({}, renderState), _define_property({}, RENDER_STATE_KEY, this.getWidgetRenderState(renderOptions)));
15601
+ },
15602
+ getWidgetRenderState: function getWidgetRenderState1(renderOptions) {
15603
+ return getWidgetRenderState(renderOptions);
15604
+ }
15605
+ };
15606
+ };
15607
+ };
15608
+
15609
+ function usePromptSuggestions(props, additionalWidgetProperties) {
15610
+ return useConnector(connectPromptSuggestions, props, additionalWidgetProperties);
14807
15611
  }
14808
15612
 
14809
15613
  var withUsage$n = createDocumentationMessageGenerator({
@@ -19048,6 +19852,7 @@
19048
19852
  exports.useNumericMenu = useNumericMenu;
19049
19853
  exports.usePagination = usePagination;
19050
19854
  exports.usePoweredBy = usePoweredBy;
19855
+ exports.usePromptSuggestions = usePromptSuggestions;
19051
19856
  exports.useQueryRules = useQueryRules;
19052
19857
  exports.useRSCContext = useRSCContext;
19053
19858
  exports.useRange = useRange;