Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 3 additions & 11 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
# Bolt's Journal

## 2024-05-22 - Async Race Conditions

**Learning:** Asynchronous typeahead searches must implement a request ID mechanism. Without it, stale responses can overwrite newer ones, leading to correct search terms displaying incorrect results.
**Action:** Always use a request ID or cancellation token pattern when implementing async search/filter operations.

## 2024-10-25 - Svelte Input Debouncing

**Learning:** Using `on:keyup` for search input debouncing triggers unnecessary API calls on navigation keys (arrows, home, end) and misses changes from paste/cut. Svelte's reactive statements `$: debounce(value)` provide a robust, declarative way to trigger debouncing only when the value actually changes.
**Action:** Replace `on:keyup` handlers with reactive statements for input debouncing to improve performance and correctness.
## 2024-10-24 - Async Search Caching
**Learning:** Adding a simple cache to async search inputs requires handling race conditions carefully. Specifically, manually triggering search (e.g., submit) should invalidate pending debounce timers to prevent double fetches.
**Action:** Always clear pending timers at the start of the async function execution, not just in the debounce wrapper.
26 changes: 24 additions & 2 deletions src/routes/DomainSearch.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
let isLoading: boolean = false;
let debounceTimer: ReturnType<typeof setTimeout>;
let requestId = 0;
// Cache for domain search results to prevent redundant API calls
let cache = new Map<string, DomainModel | null>();

$: errors = invalid ? validator.getErrors() : [];
$: invalid = domainName !== '' && !validator.validate(domainName, { raiseError: false });
Expand All @@ -33,22 +35,42 @@
async function search(submit = false) {
if (invalid) return;

// Clear pending debounce timer to prevent double fetches on manual submit
clearTimeout(debounceTimer);

if (domainName === '') return;
if (submit && domainName === nameSearched) {

const normalizedDomain = domainName.toLocaleLowerCase();

if (submit && normalizedDomain === nameSearched) {
const url = domain ? `/domain/${nameSearched}` : `/register/${nameSearched}`;

return goto(url);
}

// Return cached result if available
if (cache.has(normalizedDomain)) {
domain = cache.get(normalizedDomain);
nameSearched = normalizedDomain;
isLoading = false;

if (submit) {
const url = domain ? `/domain/${nameSearched}` : `/register/${nameSearched}`;
return goto(url);
}
return;
}

const currentRequestId = ++requestId;
nameSearched = domainName.toLocaleLowerCase();
nameSearched = normalizedDomain;
isLoading = true;

const result = await $metaNamesSdk.domainRepository.find(domainName);

if (currentRequestId === requestId) {
domain = result;
isLoading = false;
cache.set(normalizedDomain, result);
}
}

Expand Down