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 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: AbortSignal.timeout(8000),
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lucos_search_component",
3
- "version": "1.0.51",
3
+ "version": "1.0.52",
4
4
  "description": "Web Components for searching lucOS data",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -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: AbortSignal.timeout(8000),
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.';