@seekora-ai/ui-sdk-core 0.2.23 → 0.2.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1394,6 +1394,9 @@ class SearchStateManager {
1394
1394
  constructor(config) {
1395
1395
  this.listeners = [];
1396
1396
  this.debounceTimer = null;
1397
+ this.notifyScheduled = false;
1398
+ this.searchCoalesceTimer = null;
1399
+ this.searchCoalesceResolvers = [];
1397
1400
  this.client = config.client;
1398
1401
  this.autoSearch = config.autoSearch !== false;
1399
1402
  this.debounceMs = config.debounceMs || 300;
@@ -1538,13 +1541,27 @@ class SearchStateManager {
1538
1541
  this.debouncedSearch();
1539
1542
  }
1540
1543
  }
1541
- // Manual search trigger
1544
+ // Manual search trigger — coalesces rapid calls within 10ms into a single API request
1542
1545
  async search(additionalOptions) {
1543
1546
  // Clear debounce timer if exists
1544
1547
  if (this.debounceTimer) {
1545
1548
  clearTimeout(this.debounceTimer);
1546
1549
  this.debounceTimer = null;
1547
1550
  }
1551
+ return new Promise((resolve, reject) => {
1552
+ this.searchCoalesceResolvers.push({ resolve, reject });
1553
+ if (this.searchCoalesceTimer) {
1554
+ clearTimeout(this.searchCoalesceTimer);
1555
+ }
1556
+ this.searchCoalesceTimer = setTimeout(() => {
1557
+ this.searchCoalesceTimer = null;
1558
+ const resolvers = [...this.searchCoalesceResolvers];
1559
+ this.searchCoalesceResolvers = [];
1560
+ this._executeSearch(additionalOptions).then((result) => resolvers.forEach(r => r.resolve(result)), (err) => resolvers.forEach(r => r.reject(err)));
1561
+ }, 10);
1562
+ });
1563
+ }
1564
+ async _executeSearch(additionalOptions) {
1548
1565
  this.setState({ loading: true, error: null });
1549
1566
  try {
1550
1567
  const searchOptions = this.buildSearchOptions(additionalOptions);
@@ -1641,19 +1658,27 @@ class SearchStateManager {
1641
1658
  this.state = { ...this.state, ...updates };
1642
1659
  this.notifyListeners();
1643
1660
  }
1644
- // Notify all listeners of state changes
1661
+ // Notify all listeners of state changes, batched via microtask.
1662
+ // Multiple synchronous mutations (e.g. addRefinement + page reset)
1663
+ // coalesce into a single listener notification.
1645
1664
  notifyListeners() {
1646
- const state = this.getState();
1647
- this.listeners.forEach(listener => {
1648
- try {
1649
- listener(state);
1650
- }
1651
- catch (err) {
1652
- const error = err instanceof Error ? err : new Error(String(err));
1653
- log.error('SearchStateManager: Error in listener', {
1654
- error: error.message,
1655
- });
1656
- }
1665
+ if (this.notifyScheduled)
1666
+ return;
1667
+ this.notifyScheduled = true;
1668
+ queueMicrotask(() => {
1669
+ this.notifyScheduled = false;
1670
+ const state = this.getState();
1671
+ this.listeners.forEach(listener => {
1672
+ try {
1673
+ listener(state);
1674
+ }
1675
+ catch (err) {
1676
+ const error = err instanceof Error ? err : new Error(String(err));
1677
+ log.error('SearchStateManager: Error in listener', {
1678
+ error: error.message,
1679
+ });
1680
+ }
1681
+ });
1657
1682
  });
1658
1683
  }
1659
1684
  /** Explicitly clear results (bypasses keepResultsOnClear) */
@@ -1699,10 +1724,13 @@ class SearchStateManager {
1699
1724
  async fetchFilters(options) {
1700
1725
  log.verbose('SearchStateManager: Fetching filters', { options });
1701
1726
  try {
1702
- const filterString = this.buildFilterString();
1727
+ // Do NOT pass refinement-based filters to the Filters API.
1728
+ // Facets should be generated from the search query only, not narrowed
1729
+ // by active filter selections. This keeps facet options stable when
1730
+ // users toggle filters (same behaviour as performSimplifiedFacetSearch
1731
+ // in the search API).
1703
1732
  const response = await this.client.getFilters({
1704
1733
  q: this.state.query || undefined,
1705
- filter: filterString || undefined,
1706
1734
  ...options,
1707
1735
  });
1708
1736
  log.info('SearchStateManager: Filters fetched', {