diff --git a/assets/js/base.js b/assets/js/base.js index 91a8b00..5a4ce3b 100644 --- a/assets/js/base.js +++ b/assets/js/base.js @@ -468,7 +468,15 @@ try { resp = await fetch(url, opts); } catch (err) { throw new Error('Network error: ' + err.message); } let data; try { data = await resp.json(); } catch (_) { data = { success: resp.ok }; } - if (!resp.ok) throw new Error(data.error || data.message || 'HTTP ' + resp.status); + // Resync CSRF token from any response body that carries a fresh one + // (bootstrap rotates on success and returns the current token on rejection). + if (data && data.csrf_token) global.CSRF_TOKEN = data.csrf_token; + if (!resp.ok) { + const err = new Error(data.error || data.message || 'HTTP ' + resp.status); + err.data = data; + err.status = resp.status; + throw err; + } return data; } @@ -2004,6 +2012,7 @@ let _focusedIdx = -1; let _items = []; let _debTimer = null; + let _searchSeq = 0; function _render(items, query) { _items = items.slice(0, maxResults); @@ -2028,16 +2037,21 @@ } async function _search(query) { + // Sequence guard: only the latest query is allowed to render, so a slow + // earlier async source() cannot overwrite a newer query's results. + const seq = ++_searchSeq; dropdown.innerHTML = '