@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.d.ts +4 -0
- package/dist/index.esm.js +43 -15
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +43 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
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
|
-
|
|
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', {
|