Skip to content

Commit 55cc5e5

Browse files
committed
fix: preview reset race condition, model cache TTL, enhancer auth patterns, JSDoc syntax
1 parent 313b626 commit 55cc5e5

4 files changed

Lines changed: 24 additions & 4 deletions

File tree

app/lib/stores/previews.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export class PreviewsStore {
3030
#REFRESH_DELAY = 300;
3131
#storageChannel?: BroadcastChannel;
3232
#disposePortEvents: Disposer | undefined;
33+
#resetTimer: ReturnType<typeof setTimeout> | undefined;
3334

3435
previews = atom<PreviewInfo[]>([]);
3536

@@ -390,7 +391,8 @@ export class PreviewsStore {
390391

391392
// Defer re-subscription so old runtime teardown completes before we
392393
// start listening to port events again — prevents stale port capture
393-
setTimeout(() => {
394+
clearTimeout(this.#resetTimer);
395+
this.#resetTimer = setTimeout(() => {
394396
this.#broadcastChannel = this.#maybeCreateChannel(PREVIEW_CHANNEL);
395397
this.#storageChannel = this.#maybeCreateChannel('storage-sync-channel');
396398
this.#setupChannelHandlers();

app/routes/api.enhancer.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,20 @@ async function enhancerAction({ context, request }: ActionFunctionArgs) {
113113
} catch (error: unknown) {
114114
logger.error(error);
115115

116-
if (error instanceof Error && error.message?.includes('API key')) {
117-
return errorResponse(new AppError(AppErrorType.UNAUTHORIZED, 'Invalid or missing API key'));
116+
if (error instanceof Error) {
117+
const msg = error.message.toLowerCase();
118+
if (
119+
msg.includes('api key') ||
120+
msg.includes('api_key') ||
121+
msg.includes('unauthorized') ||
122+
msg.includes('authentication') ||
123+
msg.includes('invalid credentials') ||
124+
msg.includes('forbidden') ||
125+
msg.includes('access denied') ||
126+
msg.includes('401')
127+
) {
128+
return errorResponse(new AppError(AppErrorType.UNAUTHORIZED, 'Invalid or missing API key'));
129+
}
118130
}
119131

120132
return errorResponse(error instanceof Error ? error : new AppError(AppErrorType.INTERNAL, 'Internal Server Error'));

app/routes/api.models.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,18 @@ const logger = createScopedLogger('Models');
1414

1515
let cachedProviders: ProviderInfo[] | null = null;
1616
let cachedDefaultProvider: ProviderInfo | null = null;
17+
let cachedProvidersTimestamp = 0;
1718

1819
/** Server-side model list cache with short TTL to avoid repeated fetches during startup */
1920
let cachedModelResponse: { models: ModelInfo[]; timestamp: number } | null = null;
2021
const MODEL_CACHE_TTL_MS = 30_000; // 30 seconds
2122

2223
function getProviderInfo(llmManager: LLMManager) {
24+
if (cachedProviders && Date.now() - cachedProvidersTimestamp > MODEL_CACHE_TTL_MS) {
25+
cachedProviders = null;
26+
cachedDefaultProvider = null;
27+
}
28+
2329
if (!cachedProviders) {
2430
cachedProviders = llmManager.getAllProviders().map((provider) => ({
2531
name: provider.name,
@@ -28,6 +34,7 @@ function getProviderInfo(llmManager: LLMManager) {
2834
labelForGetApiKey: provider.labelForGetApiKey,
2935
icon: provider.icon,
3036
}));
37+
cachedProvidersTimestamp = Date.now();
3138
}
3239

3340
if (!cachedDefaultProvider) {

app/utils/previewErrorHandler.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,6 @@ class PreviewErrorHandler {
188188
logger.info(`Preview error detected [${severity}]: ${title} - ${errorMessage.slice(0, 100)}`);
189189
}
190190

191-
/**
192191
/**
193192
* Reset the handler state
194193
* Call this when user clicks "Ask Devonz" so the same error can be caught again

0 commit comments

Comments
 (0)