From 5f3fd2bfafbfc18179785abf5e2a64dadff684c4 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 21:01:39 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Optimize=20domain=20sea?= =?UTF-8?q?rch=20and=20fix=20race=20conditions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace `on:keyup` with reactive statement to handle all input methods and reduce unnecessary calls. - Implement request ID tracking to fix race conditions in async search. - Clear pending debounce timer on search start. - Add aria-label to CircularProgress for accessibility. Co-authored-by: Yeboster <23556525+Yeboster@users.noreply.github.com> --- .jules/bolt.md | 7 +++++++ src/routes/DomainSearch.svelte | 25 +++++++++++++++++++------ 2 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 .jules/bolt.md diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..e6e2221 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,7 @@ +## 2024-10-24 - [Svelte Search Optimization] +**Learning:** Using `on:keyup` for search inputs misses paste/drag events and triggers on navigation keys. Reactive statements `$: if (val !== null) debounce()` are superior for covering all input methods and reducing unnecessary calls. +**Action:** Prefer reactive statements over `on:keyup` for input-driven side effects. + +## 2024-10-24 - [Async Race Conditions] +**Learning:** Async search functions without request ID tracking can lead to race conditions where a slow, stale request overwrites a newer, faster request. +**Action:** Always implement a `requestId` counter or variable capture in async search handlers to discard stale results. diff --git a/src/routes/DomainSearch.svelte b/src/routes/DomainSearch.svelte index 2cde1b3..d1638e4 100644 --- a/src/routes/DomainSearch.svelte +++ b/src/routes/DomainSearch.svelte @@ -15,11 +15,15 @@ let domainName: string = ''; let nameSearched: string = ''; let isLoading: boolean = false; - let debounceTimer: NodeJS.Timeout; + let debounceTimer: ReturnType; + let lastRequestId = 0; $: errors = invalid ? validator.getErrors() : []; $: invalid = domainName !== '' && !validator.validate(domainName, { raiseError: false }); $: nameSearchedLabel = nameSearched ? `${nameSearched}.${$metaNamesSdk.config.tld}` : null; + // Use a reactive statement instead of on:keyup to handle all input methods (paste, drag/drop) + // and prevent unnecessary searches on navigation keys (arrows, shift). + $: if (domainName !== null) debounce(); function debounce() { clearTimeout(debounceTimer); @@ -27,6 +31,7 @@ } async function search(submit = false) { + clearTimeout(debounceTimer); if (invalid) return; if (domainName === '') return; @@ -36,12 +41,17 @@ return goto(url); } + // Track request ID to handle race conditions where a slow previous request + // overwrites the result of a faster subsequent request. + const requestId = ++lastRequestId; nameSearched = domainName.toLocaleLowerCase(); isLoading = true; - domain = await $metaNamesSdk.domainRepository.find(domainName); - - isLoading = false; + const result = await $metaNamesSdk.domainRepository.find(domainName); + if (requestId === lastRequestId) { + domain = result; + isLoading = false; + } } async function submit() { @@ -55,7 +65,6 @@ class="domain-input" variant="outlined" bind:value={domainName} - on:keyup={() => debounce()} bind:invalid label="Domain name" withTrailingIcon @@ -81,7 +90,11 @@
{nameSearchedLabel} - +
From 40a4b4ebdf4505f523b56486fe4b6c67f335d887 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 13 Feb 2026 14:06:23 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Optimize=20domain=20sea?= =?UTF-8?q?rch=20with=20reactive=20debounce?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rebased on main, adopting existing race condition fixes and accessibility improvements. - Replaced `on:keyup` with reactive statement `$: if (domainName !== null) debounce()` to handle all input methods (paste, drag/drop) and avoid unnecessary searches on navigation keys. Co-authored-by: Yeboster <23556525+Yeboster@users.noreply.github.com> --- .Jules/palette.md | 4 +++ .github/dependabot.yml | 6 ++-- .jules/bolt.md | 11 ++++--- .jules/palette.md | 3 ++ src/components/DomainPayment.svelte | 8 +++-- src/components/Icon.svelte | 2 +- src/components/LoadingButton.svelte | 31 ++++++++++++++++--- src/components/Record.svelte | 14 +++++++-- src/lib/sdk.ts | 2 +- src/lib/server/index.ts | 1 - src/lib/types.ts | 8 ++--- src/lib/url.ts | 9 +++--- src/routes/+layout.svelte | 1 - src/routes/DomainSearch.svelte | 11 +++---- .../register/[name]/fees/[coin]/+server.ts | 19 +++++++----- src/routes/profile/+page.svelte | 1 - src/styles/mixins.scss | 2 +- src/theme/dark/_smui-theme.scss | 2 +- yarn.lock | 20 ++++++------ 19 files changed, 98 insertions(+), 57 deletions(-) create mode 100644 .Jules/palette.md create mode 100644 .jules/palette.md diff --git a/.Jules/palette.md b/.Jules/palette.md new file mode 100644 index 0000000..d6d129b --- /dev/null +++ b/.Jules/palette.md @@ -0,0 +1,4 @@ +## 2024-10-24 - Persisting Error State in Async Buttons + +**Learning:** Async buttons that handle errors often fail to reset their error state on subsequent attempts. This leads to a confusing UX where a successful retry still displays the error icon, making the user believe the action failed again. +**Action:** Always ensure that error flags (e.g., `hasError`) are reset at the _start_ of the async operation, not just set in the `catch` block. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 42adb44..301e978 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,7 +5,7 @@ version: 2 updates: - - package-ecosystem: "npm" - directory: "/" + - package-ecosystem: 'npm' + directory: '/' schedule: - interval: "weekly" + interval: 'weekly' diff --git a/.jules/bolt.md b/.jules/bolt.md index e6e2221..14b815e 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -1,7 +1,10 @@ +# 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-24 - [Svelte Search Optimization] **Learning:** Using `on:keyup` for search inputs misses paste/drag events and triggers on navigation keys. Reactive statements `$: if (val !== null) debounce()` are superior for covering all input methods and reducing unnecessary calls. **Action:** Prefer reactive statements over `on:keyup` for input-driven side effects. - -## 2024-10-24 - [Async Race Conditions] -**Learning:** Async search functions without request ID tracking can lead to race conditions where a slow, stale request overwrites a newer, faster request. -**Action:** Always implement a `requestId` counter or variable capture in async search handlers to discard stale results. diff --git a/.jules/palette.md b/.jules/palette.md new file mode 100644 index 0000000..00f41cd --- /dev/null +++ b/.jules/palette.md @@ -0,0 +1,3 @@ +## 2024-10-24 - Accessible Icon Props and Loading Button State +**Learning:** Svelte wrapper components (like `Icon.svelte`) must spread `$$restProps` to allow passing accessibility attributes (e.g., `aria-label`) from parent components. Without this, icons remain inaccessible to screen readers. Also, persistent "Success" states on buttons can be confusing; auto-resetting them after a timeout improves clarity. +**Action:** Always include `{...$$restProps}` in wrapper components and implement auto-reset logic for temporary success states in interactive elements. diff --git a/src/components/DomainPayment.svelte b/src/components/DomainPayment.svelte index ac131ee..e04970d 100644 --- a/src/components/DomainPayment.svelte +++ b/src/components/DomainPayment.svelte @@ -102,7 +102,11 @@

{domainName}

- addYears(-1)} disabled={years === 1 || feesApproved} aria-label="remove-year"> + addYears(-1)} + disabled={years === 1 || feesApproved} + aria-label="remove-year" + > {years} {yearsLabel} @@ -111,7 +115,7 @@
-
+

Payment token