lucos_search_component 1.0.51 → 1.0.52
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 +15 -3
- package/package.json +1 -1
- package/web-components/lucos-search.js +15 -3
package/dist/index.js
CHANGED
|
@@ -5801,6 +5801,13 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
5801
5801
|
closeAfterSelect: true,
|
|
5802
5802
|
highlight: false, // Will use typesense's hightlight (as it can consider other fields)
|
|
5803
5803
|
load: async function(query, callback) {
|
|
5804
|
+
// Cancel any in-flight search so stale responses don't overwrite newer results
|
|
5805
|
+
if (component._searchAbortController) {
|
|
5806
|
+
component._searchAbortController.abort();
|
|
5807
|
+
}
|
|
5808
|
+
const abortController = new AbortController();
|
|
5809
|
+
component._searchAbortController = abortController;
|
|
5810
|
+
|
|
5804
5811
|
errorMessage.setAttribute('hidden', '');
|
|
5805
5812
|
const queryParams = new URLSearchParams({
|
|
5806
5813
|
q: query,
|
|
@@ -5811,7 +5818,8 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
5811
5818
|
queryParams.set("filter_by",`type:![${component.getAttribute("data-exclude_types")}]`);
|
|
5812
5819
|
}
|
|
5813
5820
|
try {
|
|
5814
|
-
const results = await component.searchRequest(queryParams);
|
|
5821
|
+
const results = await component.searchRequest(queryParams, abortController.signal);
|
|
5822
|
+
if (abortController.signal.aborted) return;
|
|
5815
5823
|
this.clearOptions();
|
|
5816
5824
|
if (component.isLanguageMode) {
|
|
5817
5825
|
results.forEach(r => { if (!r.lang_family) r.lang_family = 'qli'; });
|
|
@@ -5820,6 +5828,7 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
5820
5828
|
if (noLang) results.unshift(noLang);
|
|
5821
5829
|
callback(results);
|
|
5822
5830
|
} catch(err) {
|
|
5831
|
+
if (err.name === 'AbortError') return;
|
|
5823
5832
|
callback([]);
|
|
5824
5833
|
errorMessage.textContent = err.userMessage || 'Search is currently unavailable — please try again later.';
|
|
5825
5834
|
errorMessage.removeAttribute('hidden');
|
|
@@ -5952,7 +5961,7 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
5952
5961
|
}
|
|
5953
5962
|
return this._langFamilies;
|
|
5954
5963
|
}
|
|
5955
|
-
async searchRequest(searchParams) {
|
|
5964
|
+
async searchRequest(searchParams, abortSignal = null) {
|
|
5956
5965
|
const key = this.getAttribute("data-api-key");
|
|
5957
5966
|
if (!key) throw new Error("No `data-api-key` attribute set on `lucos-search` component");
|
|
5958
5967
|
searchParams.set('query_by', "pref_label,labels,description,lyrics");
|
|
@@ -5963,13 +5972,16 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
5963
5972
|
searchParams.set('enable_highlight_v1', false);
|
|
5964
5973
|
searchParams.set('highlight_start_tag', '<span class="highlight">');
|
|
5965
5974
|
searchParams.set('highlight_end_tag', '</span>');
|
|
5975
|
+
const timeoutSignal = AbortSignal.timeout(8000);
|
|
5976
|
+
const signal = abortSignal ? AbortSignal.any([timeoutSignal, abortSignal]) : timeoutSignal;
|
|
5966
5977
|
let response;
|
|
5967
5978
|
try {
|
|
5968
5979
|
response = await fetch("https://arachne.l42.eu/search?"+searchParams.toString(), {
|
|
5969
5980
|
headers: { 'X-TYPESENSE-API-KEY': key },
|
|
5970
|
-
signal
|
|
5981
|
+
signal,
|
|
5971
5982
|
});
|
|
5972
5983
|
} catch(err) {
|
|
5984
|
+
if (err.name === 'AbortError') throw err; // Pass through so caller can detect cancellation
|
|
5973
5985
|
const userMessage = err.name === 'TimeoutError'
|
|
5974
5986
|
? 'Search timed out — please try again later.'
|
|
5975
5987
|
: 'Search is currently unavailable — please try again later.';
|
package/package.json
CHANGED
|
@@ -158,6 +158,13 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
158
158
|
closeAfterSelect: true,
|
|
159
159
|
highlight: false, // Will use typesense's hightlight (as it can consider other fields)
|
|
160
160
|
load: async function(query, callback) {
|
|
161
|
+
// Cancel any in-flight search so stale responses don't overwrite newer results
|
|
162
|
+
if (component._searchAbortController) {
|
|
163
|
+
component._searchAbortController.abort();
|
|
164
|
+
}
|
|
165
|
+
const abortController = new AbortController();
|
|
166
|
+
component._searchAbortController = abortController;
|
|
167
|
+
|
|
161
168
|
errorMessage.setAttribute('hidden', '');
|
|
162
169
|
const queryParams = new URLSearchParams({
|
|
163
170
|
q: query,
|
|
@@ -168,7 +175,8 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
168
175
|
queryParams.set("filter_by",`type:![${component.getAttribute("data-exclude_types")}]`);
|
|
169
176
|
}
|
|
170
177
|
try {
|
|
171
|
-
const results = await component.searchRequest(queryParams);
|
|
178
|
+
const results = await component.searchRequest(queryParams, abortController.signal);
|
|
179
|
+
if (abortController.signal.aborted) return;
|
|
172
180
|
this.clearOptions();
|
|
173
181
|
if (component.isLanguageMode) {
|
|
174
182
|
results.forEach(r => { if (!r.lang_family) r.lang_family = 'qli'; });
|
|
@@ -177,6 +185,7 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
177
185
|
if (noLang) results.unshift(noLang);
|
|
178
186
|
callback(results);
|
|
179
187
|
} catch(err) {
|
|
188
|
+
if (err.name === 'AbortError') return;
|
|
180
189
|
callback([]);
|
|
181
190
|
errorMessage.textContent = err.userMessage || 'Search is currently unavailable — please try again later.';
|
|
182
191
|
errorMessage.removeAttribute('hidden');
|
|
@@ -309,7 +318,7 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
309
318
|
}
|
|
310
319
|
return this._langFamilies;
|
|
311
320
|
}
|
|
312
|
-
async searchRequest(searchParams) {
|
|
321
|
+
async searchRequest(searchParams, abortSignal = null) {
|
|
313
322
|
const key = this.getAttribute("data-api-key");
|
|
314
323
|
if (!key) throw new Error("No `data-api-key` attribute set on `lucos-search` component");
|
|
315
324
|
searchParams.set('query_by', "pref_label,labels,description,lyrics");
|
|
@@ -320,13 +329,16 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
320
329
|
searchParams.set('enable_highlight_v1', false);
|
|
321
330
|
searchParams.set('highlight_start_tag', '<span class="highlight">')
|
|
322
331
|
searchParams.set('highlight_end_tag', '</span>');
|
|
332
|
+
const timeoutSignal = AbortSignal.timeout(8000);
|
|
333
|
+
const signal = abortSignal ? AbortSignal.any([timeoutSignal, abortSignal]) : timeoutSignal;
|
|
323
334
|
let response;
|
|
324
335
|
try {
|
|
325
336
|
response = await fetch("https://arachne.l42.eu/search?"+searchParams.toString(), {
|
|
326
337
|
headers: { 'X-TYPESENSE-API-KEY': key },
|
|
327
|
-
signal
|
|
338
|
+
signal,
|
|
328
339
|
});
|
|
329
340
|
} catch(err) {
|
|
341
|
+
if (err.name === 'AbortError') throw err; // Pass through so caller can detect cancellation
|
|
330
342
|
const userMessage = err.name === 'TimeoutError'
|
|
331
343
|
? 'Search timed out — please try again later.'
|
|
332
344
|
: 'Search is currently unavailable — please try again later.';
|