From 2309021bbde6cf0c5feca412d08fe6b0dc9694d5 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Fri, 6 Feb 2026 01:40:08 +0530 Subject: [PATCH] Enforce static imports and improve error handling for Tauri API calls - Updated the codebase to replace dynamic imports with static imports for Tauri API calls, enhancing performance and compliance with new coding standards. - Implemented try/catch blocks around Tauri API calls to handle errors gracefully in non-Tauri environments. - Refactored components to ensure consistent usage of static imports, improving code clarity and maintainability. - Adjusted the `activeTeamId` property in the User interface to be required, ensuring better type safety. --- CLAUDE.md | 3 ++- src/components/ModelDownloadProgress.tsx | 2 +- src/components/SkillsGrid.tsx | 4 ++-- .../settings/panels/BillingPanel.tsx | 14 +++++--------- .../settings/panels/TeamInvitesPanel.tsx | 4 +--- .../settings/panels/TeamMembersPanel.tsx | 4 +--- src/lib/skills/manager.ts | 4 ++-- src/lib/skills/transport.ts | 18 ++++++++---------- src/main.tsx | 9 ++++----- src/providers/SkillProvider.tsx | 2 +- src/types/api.ts | 2 +- 11 files changed, 28 insertions(+), 38 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 2bf8b6e7d..459ee7209 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -352,6 +352,7 @@ Key updates from recent commits (cd9ebcd to current): ## Key Patterns - **Code Quality**: ESLint and Prettier enforce code standards with Husky hooks. Use type-only imports (`import type`) and consolidate imports from same modules. +- **No dynamic imports**: All imports must be static `import` statements at the top of the file. Do not use `await import()` or `import().then()` inside functions or code blocks. Use try/catch around Tauri API calls for non-Tauri environments instead. - **No localStorage**: Avoid `localStorage` and `sessionStorage`; use Redux (and persist) for app state. Remove any direct usage when working on affected code. - **AI System Integration**: Use `src/lib/ai/` for memory management, constitution loading, entity queries, and session capture. AI providers abstracted through interface pattern. - **V8 Skills Runtime**: Skills execute in V8 JavaScript engine on desktop platforms. Use `SkillProvider` for GitHub sync, `SkillsGrid` for management interface, and Rust runtime commands for lifecycle management. Platform filtering ensures skills only run on supported platforms. @@ -377,7 +378,7 @@ Key updates from recent commits (cd9ebcd to current): - **macOS deep links**: Require `.app` bundle (not `tauri dev`). Clear WebKit caches when debugging stale content: `rm -rf ~/Library/WebKit/com.alphahuman.app ~/Library/Caches/com.alphahuman.app` - **Cargo caching**: May serve stale frontend assets on incremental builds. Run `cargo clean --manifest-path src-tauri/Cargo.toml` if the app shows outdated UI. -- **`window.__TAURI__`**: Not available at module load time. Use dynamic `import()` and try/catch for Tauri plugin calls. +- **`window.__TAURI__`**: Not available at module load time. Use static imports and try/catch around Tauri API calls (not around imports). - **Android background services**: RuntimeService requires notification permissions (API 33+) and foreground service type specification (API 34+). Use Android logging (`android_logger`) for debug output in logcat. - **V8 runtime limitations**: V8 engine is desktop-only. Android skills should use lightweight alternatives or server-side execution patterns. - **Socket connections**: Persistent Socket.io connections via Rust backend work better than WebView-based connections on mobile platforms. diff --git a/src/components/ModelDownloadProgress.tsx b/src/components/ModelDownloadProgress.tsx index cea693b92..4ad68e1fc 100644 --- a/src/components/ModelDownloadProgress.tsx +++ b/src/components/ModelDownloadProgress.tsx @@ -1,3 +1,4 @@ +import { platform } from '@tauri-apps/plugin-os'; import { useEffect, useState } from 'react'; import { useModelStatus } from '../hooks/useModelStatus'; @@ -19,7 +20,6 @@ const ModelDownloadProgress = ({ // Detect mobile platform const detectMobile = async () => { try { - const { platform } = await import('@tauri-apps/plugin-os'); const currentPlatform = await platform(); setIsMobile(currentPlatform === 'android' || currentPlatform === 'ios'); } catch { diff --git a/src/components/SkillsGrid.tsx b/src/components/SkillsGrid.tsx index 222ce669d..1158073b2 100644 --- a/src/components/SkillsGrid.tsx +++ b/src/components/SkillsGrid.tsx @@ -1,3 +1,5 @@ +import { invoke } from '@tauri-apps/api/core'; +import { platform } from '@tauri-apps/plugin-os'; import { useEffect, useMemo, useState } from 'react'; import GoogleIcon from '../assets/icons/GoogleIcon'; @@ -292,7 +294,6 @@ export default function SkillsGrid() { // Detect mobile platform const detectMobile = async () => { try { - const { platform } = await import('@tauri-apps/plugin-os'); const currentPlatform = await platform(); setIsMobile(currentPlatform === 'android' || currentPlatform === 'ios'); } catch { @@ -305,7 +306,6 @@ export default function SkillsGrid() { // Load skills from the V8 runtime engine. const loadSkills = async () => { try { - const { invoke } = await import('@tauri-apps/api/core'); const manifests = await invoke>>('runtime_discover_skills'); console.log('manifests', manifests); diff --git a/src/components/settings/panels/BillingPanel.tsx b/src/components/settings/panels/BillingPanel.tsx index 300825f2e..2bedbae71 100644 --- a/src/components/settings/panels/BillingPanel.tsx +++ b/src/components/settings/panels/BillingPanel.tsx @@ -27,15 +27,11 @@ const BillingPanel = () => { const activeTeam = teams.find(t => t.team._id === activeTeamId); const teamName = activeTeam?.team.name; - // Derive plan from active team when available, fall back to user - const currentTier: PlanTier = - activeTeam?.team.subscription?.plan ?? user?.subscription?.plan ?? 'FREE'; - const hasActive = - activeTeam?.team.subscription?.hasActiveSubscription ?? - user?.subscription?.hasActiveSubscription ?? - false; - const planExpiry = activeTeam?.team.subscription?.planExpiry ?? user?.subscription?.planExpiry; - const usage = activeTeam?.team.usage ?? user?.usage; + // Derive plan from active team (team is source of truth) + const currentTier: PlanTier = activeTeam?.team.subscription?.plan ?? 'FREE'; + const hasActive = activeTeam?.team.subscription?.hasActiveSubscription ?? false; + const planExpiry = activeTeam?.team.subscription?.planExpiry; + const usage = activeTeam?.team.usage; // Local state const [billingInterval, setBillingInterval] = useState<'monthly' | 'annual'>('monthly'); diff --git a/src/components/settings/panels/TeamInvitesPanel.tsx b/src/components/settings/panels/TeamInvitesPanel.tsx index 03008c584..6ba5cf6b7 100644 --- a/src/components/settings/panels/TeamInvitesPanel.tsx +++ b/src/components/settings/panels/TeamInvitesPanel.tsx @@ -22,9 +22,7 @@ const TeamInvitesPanel = () => { const [error, setError] = useState(null); useEffect(() => { - if (activeTeamId) { - dispatch(fetchInvites(activeTeamId)); - } + if (activeTeamId) dispatch(fetchInvites(activeTeamId)); }, [activeTeamId, dispatch]); const handleGenerate = async () => { diff --git a/src/components/settings/panels/TeamMembersPanel.tsx b/src/components/settings/panels/TeamMembersPanel.tsx index 858d0f0f8..ce20e0485 100644 --- a/src/components/settings/panels/TeamMembersPanel.tsx +++ b/src/components/settings/panels/TeamMembersPanel.tsx @@ -24,9 +24,7 @@ const TeamMembersPanel = () => { const [error, setError] = useState(null); useEffect(() => { - if (activeTeamId) { - dispatch(fetchMembers(activeTeamId)); - } + if (activeTeamId) dispatch(fetchMembers(activeTeamId)); }, [activeTeamId, dispatch]); const handleChangeRole = async (member: TeamMember, newRole: TeamRole) => { diff --git a/src/lib/skills/manager.ts b/src/lib/skills/manager.ts index e5935f4f9..fd00fddbe 100644 --- a/src/lib/skills/manager.ts +++ b/src/lib/skills/manager.ts @@ -5,6 +5,8 @@ * and tool invocation. Dispatches status changes to Redux. */ +import { invoke } from "@tauri-apps/api/core"; + import { SkillRuntime } from "./runtime"; import type { SkillManifest, @@ -367,7 +369,6 @@ class SkillManager { case "data/read": { const filename = params.filename as string; try { - const { invoke } = await import("@tauri-apps/api/core"); const content = await invoke("runtime_skill_data_read", { skillId, filename, @@ -382,7 +383,6 @@ class SkillManager { const filename = params.filename as string; const content = params.content as string; try { - const { invoke } = await import("@tauri-apps/api/core"); await invoke("runtime_skill_data_write", { skillId, filename, diff --git a/src/lib/skills/transport.ts b/src/lib/skills/transport.ts index 0cdd7272e..366e48338 100644 --- a/src/lib/skills/transport.ts +++ b/src/lib/skills/transport.ts @@ -10,6 +10,8 @@ * no longer needs to handle reverse RPC from the skill. */ +import { invoke } from '@tauri-apps/api/core'; + export type ReverseRpcHandler = ( method: string, params: Record @@ -58,7 +60,6 @@ export class SkillTransport { hasParams: params !== undefined, }); - const { invoke } = await import("@tauri-apps/api/core"); const result = await invoke("runtime_rpc", { skillId: this.skillId, method, @@ -89,14 +90,12 @@ export class SkillTransport { }); // Fire and forget - import("@tauri-apps/api/core").then(({ invoke }) => { - invoke("runtime_rpc", { - skillId: this.skillId, - method, - params: params ?? {}, - }).catch((err: unknown) => { - console.error("[skill-transport] Notification error:", err); - }); + invoke("runtime_rpc", { + skillId: this.skillId, + method, + params: params ?? {}, + }).catch((err: unknown) => { + console.error("[skill-transport] Notification error:", err); }); } @@ -107,7 +106,6 @@ export class SkillTransport { async kill(): Promise { if (this.skillId && this._started) { try { - const { invoke } = await import("@tauri-apps/api/core"); await invoke("runtime_stop_skill", { skillId: this.skillId }); } catch { // Skill may already be stopped diff --git a/src/main.tsx b/src/main.tsx index 3c5a68649..30ea01b06 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -6,15 +6,14 @@ import App from './App'; import './index.css'; import './polyfills'; import { initSentry } from './services/analytics'; +import { setupDesktopDeepLinkListener } from './utils/desktopDeepLinkListener'; // Initialize Sentry early (before React renders) initSentry(); -// Deep link listener - lazy import to avoid running before Tauri IPC is ready -import('./utils/desktopDeepLinkListener').then(m => { - m.setupDesktopDeepLinkListener().catch(err => { - console.error('[DeepLink] setup error:', err); - }); +// Deep link listener — try/catch handles non-Tauri environments +setupDesktopDeepLinkListener().catch(err => { + console.error('[DeepLink] setup error:', err); }); ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( diff --git a/src/providers/SkillProvider.tsx b/src/providers/SkillProvider.tsx index ee3123423..bc541265a 100644 --- a/src/providers/SkillProvider.tsx +++ b/src/providers/SkillProvider.tsx @@ -7,6 +7,7 @@ * The Rust V8 engine handles skill discovery and auto-start independently. * This provider bridges the Rust engine state with the frontend Redux store. */ +import { invoke } from '@tauri-apps/api/core'; import { type ReactNode, useEffect, useRef } from 'react'; import { skillManager } from '../lib/skills/manager'; @@ -19,7 +20,6 @@ import { DEV_AUTO_LOAD_SKILL } from '../utils/config'; // --------------------------------------------------------------------------- async function discoverSkills(): Promise { - const { invoke } = await import('@tauri-apps/api/core'); const raw = await invoke>>('runtime_discover_skills'); // Map the V8 manifest format to SkillManifest return raw.map(m => ({ diff --git a/src/types/api.ts b/src/types/api.ts index 3b04da755..2fc00557c 100644 --- a/src/types/api.ts +++ b/src/types/api.ts @@ -64,7 +64,7 @@ export interface User { username?: string; languageCode?: string; waitlist?: string; - activeTeamId?: string; + activeTeamId: string; } // Billing types