Migrate all IPC call sites to typed tauri-specta commands.*
🏗️ Build and Test JellyTau / Run Tests (pull_request) Successful in 4m39s
Traceability Validation / Check Requirement Traces (pull_request) Failing after 36s
🏗️ Build and Test JellyTau / Build Android APK (pull_request) Failing after 1m57s

Replace the remaining ~155 untyped invoke() calls across stores, services,
components, and routes with the generated commands.* wrappers from
$lib/api/bindings, so every IPC call is compile-time-checked against the
command signatures.

- Register repository_get_subtitle_url and repository_get_video_download_url
  in specta_builder() and the invoke_handler; regenerate bindings.ts.
- Source duplicated wire types (AutoplaySettings, CacheConfig, Session,
  ConnectivityStatus, audio/video settings, etc.) from bindings.
- Fix two bugs surfaced by the typed wrappers:
  - VideoDownloadButton passed an un-awaited Promise as the stream URL.
  - setAutoplaySettings omitted the required userId argument.
- Update unit tests asserting the old invoke(name, args) shape.
- Remove the five param-naming guard tests; the compiler and codegen now
  enforce what they checked.

svelte-check: 0 errors. vitest: green. cargo test --lib: green.
This commit is contained in:
2026-06-21 08:47:04 +02:00
parent 14e9d7e03a
commit d01c2aab9f
47 changed files with 456 additions and 2119 deletions
+11 -25
View File
@@ -6,7 +6,7 @@
//
// TRACES: UR-002, UR-017, UR-025 | DR-014
import { invoke } from "@tauri-apps/api/core";
import { commands } from "$lib/api/bindings";
import { auth } from "$lib/stores/auth";
// Types matching Rust structs
@@ -73,12 +73,12 @@ class SyncService {
throw new Error("Not authenticated");
}
const id = await invoke<number>("sync_queue_mutation", {
const id = await commands.syncQueueMutation(
userId,
operation,
itemId,
payload: payload ? JSON.stringify(payload) : null,
});
payload ? JSON.stringify(payload) : null
);
console.log(`[SyncService] Queued ${operation} for item ${itemId}, id: ${id}`);
return id;
@@ -90,11 +90,7 @@ class SyncService {
*/
async queueFavorite(itemId: string, isFavorite: boolean): Promise<number> {
// Update local state first
await invoke("storage_toggle_favorite", {
userId: auth.getUserId(),
itemId,
isFavorite,
});
await commands.storageToggleFavorite(auth.getUserId() ?? "", itemId, isFavorite);
return this.queueMutation(
isFavorite ? "mark_favorite" : "unmark_favorite",
@@ -111,11 +107,7 @@ class SyncService {
positionTicks: number
): Promise<number> {
// Update local state first
await invoke("storage_update_playback_progress", {
userId: auth.getUserId(),
itemId,
positionTicks,
});
await commands.storageUpdatePlaybackProgress(auth.getUserId() ?? "", itemId, positionTicks);
return this.queueMutation("update_progress", itemId, { positionTicks });
}
@@ -126,10 +118,7 @@ class SyncService {
*/
async queueMarkPlayed(itemId: string): Promise<number> {
// Update local state first
await invoke("storage_mark_played", {
userId: auth.getUserId(),
itemId,
});
await commands.storageMarkPlayed(auth.getUserId() ?? "", itemId);
return this.queueMutation("mark_played", itemId);
}
@@ -143,7 +132,7 @@ class SyncService {
return 0;
}
return invoke<number>("sync_get_pending_count", { userId });
return commands.syncGetPendingCount(userId);
}
/**
@@ -155,17 +144,14 @@ class SyncService {
return [];
}
return invoke<SyncQueueItem[]>("sync_get_pending", {
userId,
limit,
});
return commands.syncGetPending(userId, limit ?? null);
}
/**
* Clean up completed operations older than specified days
*/
async cleanup(daysOld: number = 7): Promise<number> {
const deleted = await invoke<number>("sync_cleanup_completed", { daysOld });
const deleted = await commands.syncCleanupCompleted(daysOld);
console.log(`[SyncService] Cleaned up ${deleted} old completed operations`);
return deleted;
}
@@ -204,7 +190,7 @@ class SyncService {
async clearUser(): Promise<void> {
const userId = auth.getUserId();
if (userId) {
await invoke("sync_clear_user", { userId });
await commands.syncClearUser(userId);
console.log("[SyncService] Cleared sync queue for user");
}
}