Skip to content

Commit 896bed9

Browse files
carlesandresgreptile-apps[bot]raycastbot
authored
fix(models-dev): prevent OOM crash on first launch (#26781)
* Update models-dev extension - chore: ignore local debug files - fix: replace useCachedPromise with direct fetch to prevent OOM on first launch - Pull contributions * Update extensions/models-dev/src/hooks/useModelsData.ts Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Update CHANGELOG.md --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: raycastbot <bot@raycast.com>
1 parent 7ff5a6a commit 896bed9

5 files changed

Lines changed: 75 additions & 44 deletions

File tree

extensions/models-dev/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,7 @@ compiled_raycast_rust
1212

1313
# misc
1414
.DS_Store
15+
16+
# Local files
17+
.claude/
18+
GITHUB_ISSUE.md

extensions/models-dev/CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# models.dev Changelog
22

3+
## [Bug Fix] - 2026-03-31
4+
5+
### Fixed
6+
7+
- Fixed JS heap out of memory crash on first launch caused by `useCachedPromise` memory overhead
8+
- Replaced `useCachedPromise` with direct fetch + Cache API to reduce peak memory usage
9+
10+
### Changed
11+
12+
- Removed `@raycast/utils` dependency (no longer needed)
13+
314
## [Bug Fixes] - 2026-03-16
415

516
### Changed

extensions/models-dev/package-lock.json

Lines changed: 1 addition & 29 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

extensions/models-dev/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,7 @@
120120
}
121121
],
122122
"dependencies": {
123-
"@raycast/api": "^1.104.3",
124-
"@raycast/utils": "^2.2.2"
123+
"@raycast/api": "^1.104.3"
125124
},
126125
"devDependencies": {
127126
"@raycast/eslint-config": "^2.1.1",
Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,66 @@
1-
import { showToast, Toast } from "@raycast/api";
2-
import { useCachedPromise } from "@raycast/utils";
1+
import { Cache, showToast, Toast } from "@raycast/api";
2+
import { useState, useEffect, useRef } from "react";
33
import { fetchModelsData } from "../lib/api";
4+
import type { ModelsData } from "../lib/types";
5+
6+
const cache = new Cache();
7+
const CACHE_KEY = "models-data";
48

59
/**
610
* Hook to fetch models data from models.dev
7-
* useCachedPromise persists the last successful result to disk automatically,
8-
* serving stale-while-revalidate on subsequent opens with no extra heap allocation.
11+
*
12+
* Uses direct fetch + Cache API instead of useCachedPromise to avoid
13+
* Uses direct fetch + Cache API instead of useCachedPromise to avoid
14+
* memory spikes during fresh cache population. See:
15+
* https://github.com/raycast/utils/issues/65
916
*/
1017
export function useModelsData() {
11-
return useCachedPromise(fetchModelsData, [], {
12-
keepPreviousData: true,
13-
onError: (error) => {
14-
showToast({
15-
style: Toast.Style.Failure,
16-
title: "Failed to load models",
17-
message: error instanceof Error ? error.message : "Unknown error",
18-
});
19-
},
18+
const [data, setData] = useState<ModelsData | null>(() => {
19+
const cached = cache.get(CACHE_KEY);
20+
if (cached) {
21+
try {
22+
return JSON.parse(cached) as ModelsData;
23+
} catch {
24+
cache.remove(CACHE_KEY);
25+
}
26+
}
27+
return null;
2028
});
29+
30+
const [isLoading, setIsLoading] = useState(!data);
31+
const fetchedRef = useRef(false);
32+
33+
useEffect(() => {
34+
// Already have cached data or already fetching
35+
if (fetchedRef.current) return;
36+
fetchedRef.current = true;
37+
38+
// If we have cached data, still revalidate in background
39+
const shouldRevalidate = !!data;
40+
41+
if (!shouldRevalidate) {
42+
setIsLoading(true);
43+
}
44+
45+
fetchModelsData()
46+
.then((result) => {
47+
setData(result);
48+
setIsLoading(false);
49+
// Cache write happens after state update to reduce peak memory
50+
cache.set(CACHE_KEY, JSON.stringify(result));
51+
})
52+
.catch((error) => {
53+
setIsLoading(false);
54+
// Only show error if we don't have cached data to fall back on
55+
if (!data) {
56+
showToast({
57+
style: Toast.Style.Failure,
58+
title: "Failed to load models",
59+
message: error instanceof Error ? error.message : "Unknown error",
60+
});
61+
}
62+
});
63+
}, []);
64+
65+
return { data, isLoading };
2166
}

0 commit comments

Comments
 (0)