perf(apps): launch never waits on the credentials RPC — 1.2s budget + memo #119

Merged
lfg2025 merged 1 commits from fix/apps-launch-latency into main 2026-07-24 08:14:48 +00:00
Showing only changes of commit 9fcb68816b - Show all commits

View File

@ -646,37 +646,51 @@ function launchAppNow(id: string) {
useAppLauncherStore().openSession(id)
}
async function maybeShowCredentialsBeforeLaunch(id: string): Promise<boolean> {
try {
const result = await rpcClient.call<AppCredentialsResponse>({
// Per-app credentials memo: the pre-launch RPC could hold an Apps-tab launch
// hostage for its full 5s timeout over the mesh (home-card launches skip this
// gate entirely, which is why they always felt instant). First launch waits at
// most LAUNCH_CRED_BUDGET_MS; the RPC keeps running in the background and its
// answer is memoized, so every later launch of that app resolves instantly.
const LAUNCH_CRED_BUDGET_MS = 1200
const credentialsCache = new Map<string, AppCredentialsResponse | null>()
function fetchCredentials(id: string): Promise<AppCredentialsResponse | null> {
return rpcClient
.call<AppCredentialsResponse>({
method: 'package.credentials',
params: { app_id: id },
timeout: 5000,
})
const credentials = resolveAppCredentials(id, result)
if (!credentials) return false
credentialModal.value = {
show: true,
appId: id,
title: credentials.title || `${packages.value[id]?.manifest.title || id} credentials`,
description: credentials.description || 'Use these credentials when the app asks you to sign in.',
credentials: credentials.credentials,
copied: '',
}
return true
} catch {
const credentials = resolveAppCredentials(id, null)
if (!credentials) return false
credentialModal.value = {
show: true,
appId: id,
title: credentials.title || `${packages.value[id]?.manifest.title || id} credentials`,
description: credentials.description || 'Use these credentials when the app asks you to sign in.',
credentials: credentials.credentials,
copied: '',
}
return true
.then((r) => {
credentialsCache.set(id, r)
return r
})
.catch(() => {
credentialsCache.set(id, null)
return null
})
}
async function maybeShowCredentialsBeforeLaunch(id: string): Promise<boolean> {
const result = credentialsCache.has(id)
? credentialsCache.get(id) ?? null
: await Promise.race([
fetchCredentials(id),
// Budget exceeded launch with the static fallback config; the
// in-flight RPC still lands in the cache for next time.
new Promise<null>((resolve) => setTimeout(() => resolve(null), LAUNCH_CRED_BUDGET_MS)),
])
const credentials = resolveAppCredentials(id, result)
if (!credentials) return false
credentialModal.value = {
show: true,
appId: id,
title: credentials.title || `${packages.value[id]?.manifest.title || id} credentials`,
description: credentials.description || 'Use these credentials when the app asks you to sign in.',
credentials: credentials.credentials,
copied: '',
}
return true
}
function closeCredentialModal() {