From 9ffd8d481084e553fe64a5cda41abea8606f97b5 Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Mon, 29 Jun 2026 10:25:15 -0700 Subject: [PATCH] Flush model writes before sending HTTP requests --- .../hooks/useSendAnyHttpRequest.ts | 42 ++++++++----------- crates-tauri/yaak-app-client/src/lib.rs | 7 ++-- crates/yaak-models/guest-js/store.ts | 29 ++++++++++--- 3 files changed, 44 insertions(+), 34 deletions(-) diff --git a/apps/yaak-client/hooks/useSendAnyHttpRequest.ts b/apps/yaak-client/hooks/useSendAnyHttpRequest.ts index d62edfe8..bd23c444 100644 --- a/apps/yaak-client/hooks/useSendAnyHttpRequest.ts +++ b/apps/yaak-client/hooks/useSendAnyHttpRequest.ts @@ -1,40 +1,32 @@ import type { HttpResponse } from "@yaakapp-internal/models"; -import { getModel } from "@yaakapp-internal/models"; +import { flushAllModelWrites } from "@yaakapp-internal/models"; import { invokeCmd } from "../lib/tauri"; import { getActiveCookieJar } from "./useActiveCookieJar"; import { getActiveEnvironment } from "./useActiveEnvironment"; import { createFastMutation, useFastMutation } from "./useFastMutation"; +async function sendAnyHttpRequestById(id: string | null): Promise { + if (id == null) { + return null; + } + + await flushAllModelWrites(); + + return invokeCmd("cmd_send_http_request", { + requestId: id, + environmentId: getActiveEnvironment()?.id, + cookieJarId: getActiveCookieJar()?.id, + }); +} + export function useSendAnyHttpRequest() { return useFastMutation({ mutationKey: ["send_any_request"], - mutationFn: async (id) => { - const request = getModel("http_request", id ?? "n/a"); - if (request == null) { - return null; - } - - return invokeCmd("cmd_send_http_request", { - request, - environmentId: getActiveEnvironment()?.id, - cookieJarId: getActiveCookieJar()?.id, - }); - }, + mutationFn: sendAnyHttpRequestById, }); } export const sendAnyHttpRequest = createFastMutation({ mutationKey: ["send_any_request"], - mutationFn: async (id) => { - const request = getModel("http_request", id ?? "n/a"); - if (request == null) { - return null; - } - - return invokeCmd("cmd_send_http_request", { - request, - environmentId: getActiveEnvironment()?.id, - cookieJarId: getActiveCookieJar()?.id, - }); - }, + mutationFn: sendAnyHttpRequestById, }); diff --git a/crates-tauri/yaak-app-client/src/lib.rs b/crates-tauri/yaak-app-client/src/lib.rs index 1be81957..1721f963 100644 --- a/crates-tauri/yaak-app-client/src/lib.rs +++ b/crates-tauri/yaak-app-client/src/lib.rs @@ -1425,11 +1425,10 @@ async fn cmd_send_http_request( window: WebviewWindow, environment_id: Option<&str>, cookie_jar_id: Option<&str>, - // NOTE: We receive the entire request because to account for the race - // condition where the user may have just edited a field before sending - // that has not yet been saved in the DB. - request: HttpRequest, + request_id: String, ) -> YaakResult { + let request = app_handle.db().get_http_request(&request_id)?; + let blobs = app_handle.blob_manager(); let response = app_handle.db().upsert_http_response( &HttpResponse { diff --git a/crates/yaak-models/guest-js/store.ts b/crates/yaak-models/guest-js/store.ts index 573d3c84..1b704799 100644 --- a/crates/yaak-models/guest-js/store.ts +++ b/crates/yaak-models/guest-js/store.ts @@ -8,6 +8,8 @@ import { newStoreData } from "./util"; let _store: JotaiStore | null = null; +const pendingModelWrites = new Set>(); + export function initModelStore(store: JotaiStore) { _store = store; @@ -42,6 +44,23 @@ function mustStore(): JotaiStore { return _store; } +function trackModelWrite(write: Promise): Promise { + const tracked = write.finally(() => { + pendingModelWrites.delete(tracked); + }); + + pendingModelWrites.add(tracked); + return tracked; +} + +export async function flushAllModelWrites(): Promise { + const results = await Promise.allSettled([...pendingModelWrites]); + const rejected = results.find((result) => result.status === "rejected"); + if (rejected?.status === "rejected") { + throw rejected.reason; + } +} + let _activeWorkspaceId: string | null = null; export async function changeModelStoreWorkspace(workspaceId: string | null) { @@ -117,7 +136,7 @@ export async function patchModel>( model: T, ): Promise { - return invoke("models_upsert", { model }); + return trackModelWrite(invoke("models_upsert", { model })); } export async function deleteModelById< @@ -134,7 +153,7 @@ export async function deleteModel("models_delete", { model }); + await trackModelWrite(invoke("models_delete", { model })); } export function duplicateModel>( @@ -174,19 +193,19 @@ export function duplicateModel("models_duplicate", { model: { ...model, name } }); + return trackModelWrite(invoke("models_duplicate", { model: { ...model, name } })); } export async function createGlobalModel>( patch: Partial & Pick, ): Promise { - return invoke("models_upsert", { model: patch }); + return trackModelWrite(invoke("models_upsert", { model: patch })); } export async function createWorkspaceModel>( patch: Partial & Pick, ): Promise { - return invoke("models_upsert", { model: patch }); + return trackModelWrite(invoke("models_upsert", { model: patch })); } export function replaceModelsInStore<