Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions extensions/models-dev/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@ compiled_raycast_rust

# misc
.DS_Store

# Local files
.claude/
GITHUB_ISSUE.md
11 changes: 11 additions & 0 deletions extensions/models-dev/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# models.dev Changelog

## [Bug Fix] - {PR_MERGE_DATE}

### Fixed

- Fixed JS heap out of memory crash on first launch caused by `useCachedPromise` memory overhead
- Replaced `useCachedPromise` with direct fetch + Cache API to reduce peak memory usage

### Changed

- Removed `@raycast/utils` dependency (no longer needed)

## [Bug Fixes] - 2026-03-16

### Changed
Expand Down
30 changes: 1 addition & 29 deletions extensions/models-dev/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions extensions/models-dev/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,7 @@
}
],
"dependencies": {
"@raycast/api": "^1.104.3",
"@raycast/utils": "^2.2.2"
"@raycast/api": "^1.104.3"
},
"devDependencies": {
"@raycast/eslint-config": "^2.1.1",
Expand Down
70 changes: 57 additions & 13 deletions extensions/models-dev/src/hooks/useModelsData.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,65 @@
import { showToast, Toast } from "@raycast/api";
import { useCachedPromise } from "@raycast/utils";
import { Cache, showToast, Toast } from "@raycast/api";
import { useState, useEffect, useRef } from "react";
import { fetchModelsData } from "../lib/api";
import type { ModelsData } from "../lib/types";

const cache = new Cache();
const CACHE_KEY = "models-data";

/**
* Hook to fetch models data from models.dev
* useCachedPromise persists the last successful result to disk automatically,
* serving stale-while-revalidate on subsequent opens with no extra heap allocation.
*
* Uses direct fetch + Cache API instead of useCachedPromise to avoid
* memory spikes during fresh cache population. See:
* https://github.com/raycast/utils/issues/XXX
*/
export function useModelsData() {
return useCachedPromise(fetchModelsData, [], {
keepPreviousData: true,
onError: (error) => {
showToast({
style: Toast.Style.Failure,
title: "Failed to load models",
message: error instanceof Error ? error.message : "Unknown error",
});
},
const [data, setData] = useState<ModelsData | null>(() => {
const cached = cache.get(CACHE_KEY);
if (cached) {
try {
return JSON.parse(cached) as ModelsData;
} catch {
cache.remove(CACHE_KEY);
}
}
return null;
});

const [isLoading, setIsLoading] = useState(!data);
const fetchedRef = useRef(false);

useEffect(() => {
// Already have cached data or already fetching
if (fetchedRef.current) return;
fetchedRef.current = true;

// If we have cached data, still revalidate in background
const shouldRevalidate = !!data;

if (!shouldRevalidate) {
setIsLoading(true);
}

fetchModelsData()
.then((result) => {
setData(result);
setIsLoading(false);
// Cache write happens after state update to reduce peak memory
cache.set(CACHE_KEY, JSON.stringify(result));
})
.catch((error) => {
setIsLoading(false);
// Only show error if we don't have cached data to fall back on
if (!data) {
showToast({
style: Toast.Style.Failure,
title: "Failed to load models",
message: error instanceof Error ? error.message : "Unknown error",
});
}
});
}, []);

return { data, isLoading };
}
Loading