# Type-Safe Spacedrive Client Usage The TypeScript client now mirrors the Swift client with full type safety via auto-generated types. ## Architecture ``` Auto-Generated Types (Specta) ↓ SpacedriveClient (low-level execution) ↓ React Query Hooks (useCoreQuery, useLibraryQuery) ↓ React Components (fully type-safe!) ``` ## Setup ```typescript import { SpacedriveClient, SpacedriveProvider } from '@sd/ts-client'; import { invoke } from '@tauri-apps/api/core'; import { listen } from '@tauri-apps/api/event'; // Create client const client = SpacedriveClient.fromTauri(invoke, listen); // Wrap your app function App() { return ( ); } ``` ## Usage in Components ### Core Queries (no library required) ```typescript import { useCoreQuery } from '@sd/ts-client'; function LibraryList() { // Fully type-safe! Input and output types are inferred const { data: libraries, isLoading } = useCoreQuery({ type: 'libraries.list', input: {}, // TypeScript validates this matches ListLibrariesInput }); const { data: status } = useCoreQuery({ type: 'core.status', input: {}, // Empty type }); if (isLoading) return
Loading...
; return (

Spacedrive v{status?.version}

); } ``` ### Library Queries (requires library context) ```typescript import { useLibraryQuery, useSpacedriveClient } from '@sd/ts-client'; import { useEffect } from 'react'; function FileExplorer() { const client = useSpacedriveClient(); // Set current library useEffect(() => { client.switchToLibrary('some-library-id'); }, []); // Library-scoped query (auto uses current library) const { data: files } = useLibraryQuery({ type: 'files.directory_listing', input: { path: '/', // TypeScript validates this matches DirectoryListingInput }, }); const { data: jobs } = useLibraryQuery({ type: 'jobs.list', input: {}, }); return (
{files?.entries.map(file => (
{file.name}
// file is fully typed as File! ))}
); } ``` ### Mutations ```typescript import { useCoreMutation, useLibraryMutation } from '@sd/ts-client'; function CreateLibraryButton() { const createLibrary = useCoreMutation('libraries.create'); const handleCreate = () => { createLibrary.mutate({ name: 'My New Library', path: null, // TypeScript validates this matches LibraryCreateInput }, { onSuccess: (result) => { console.log('Created library:', result.id); // result is typed as LibraryCreateOutput! } }); }; return ; } function ApplyTagsButton({ entryIds }: { entryIds: number[] }) { const applyTags = useLibraryMutation('tags.apply'); const handleApply = () => { applyTags.mutate({ entry_ids: entryIds, tag_ids: ['tag-uuid-1', 'tag-uuid-2'], source: null, confidence: null, applied_context: null, instance_attributes: null, // TypeScript validates all fields! }); }; return ; } ``` ## Type Safety Benefits ### Input Validation ```typescript // This works useCoreQuery({ type: 'libraries.list', input: {} }); // TypeScript error: input must match ListLibrariesInput useCoreQuery({ type: 'libraries.list', input: { invalid_field: true } }); ``` ### Output Types ```typescript const { data: files } = useLibraryQuery({ type: 'files.directory_listing', input: { path: '/' } }); // TypeScript knows the exact type! files?.entries // File[] files?.total_count // number files?.cursor // string | null ``` ### Wire Methods (Auto-Generated) ```typescript import { WIRE_METHODS } from '@sd/ts-client'; // All wire methods are in the WIRE_METHODS constant WIRE_METHODS.coreQueries['libraries.list'] // => 'query:libraries.list' WIRE_METHODS.libraryActions['files.copy'] // => 'action:files.copy.input' ``` ## Comparison: Swift vs TypeScript ### Swift (Auto-Generated API) ```swift let client = SpacedriveClient(socketPath: "/tmp/sd.sock") // Auto-generated methods let libraries = try await client.libraries.list() let files = try await client.files.directoryListing(input) ``` ### TypeScript (React Query + Auto-Generated Types) ```typescript const client = SpacedriveClient.fromSocket('/tmp/sd.sock'); // React Query hooks with auto-generated types const { data: libraries } = useCoreQuery({ type: 'libraries.list', input: {} }); const { data: files } = useLibraryQuery({ type: 'files.directory_listing', input: { path: '/' } }); ``` **Both are fully type-safe and auto-generated from the same Rust types!** ## Low-Level API (if needed) ```typescript import { useSpacedriveClient } from '@sd/ts-client'; function CustomComponent() { const client = useSpacedriveClient(); const handleCustomOperation = async () => { // Direct execute method if you need more control const result = await client.execute( 'action:libraries.create.input', { name: 'Test', path: null } ); }; return ; } ``` ## Event Subscription ```typescript import { useSpacedriveClient } from '@sd/ts-client'; import { useEffect } from 'react'; function EventListener() { const client = useSpacedriveClient(); useEffect(() => { const unlisten = client.subscribe((event) => { // event is fully typed as Event union! console.log('Received event:', event); }); return () => unlisten.then(fn => fn()); }, [client]); return null; } ``` ## Next Steps 1. Type generation working (cargo run --bin generate_typescript_types) 2. Client with simple execute method 3. React Query hooks (useCoreQuery, useLibraryQuery, mutations) 4. → Use in interface components 5. → Test with real Tauri app All types are auto-generated - no manual maintenance needed!