History

fix(web): filter unavailable theme packages

t3-code[bot] committed 3 months ago

Commit 7bcd7561 parent 4b789603 files · +47 −7Browse files

Unified · Split

3 files
  1. apps/web/src/components/settings/ThemeSearchSection.tsx +11 −6
  2. apps/web/src/openVsxThemes.test.ts +24 −0
  3. apps/web/src/openVsxThemes.ts +12 −1
apps/web/src/components/settings/ThemeSearchSection.tsx+11−6
@@ -39,12 +39,17 @@ export function ThemeSearchSection({ · 38 unmodified lines
3939 useEffect(() => {
4040 requestRef.current?.abort();
4141 requestRef.current = null;
42− if (!open) return;
43− setQuery("");
44− setResults(null);
45− setError(null);
46− setIsSearching(false);
47− setInstallingId(null);
42+ if (open) {
43+ setQuery("");
44+ setResults(null);
45+ setError(null);
46+ setIsSearching(false);
47+ setInstallingId(null);
48+ }
49+ return () => {
50+ requestRef.current?.abort();
51+ requestRef.current = null;
52+ };
4853 }, [open]);
4954
5055 const runSearch = useCallback(async (searchText: string) => {
apps/web/src/openVsxThemes.test.ts+24−0
@@ -44,6 +44,7 @@ describe("Open VSX themes", () => { · 43 unmodified lines
4444 extensions: [
4545 { namespace: "demo", name: "theme" },
4646 { namespace: "icons", name: "theme" },
47+ { namespace: "oversized", name: "theme" },
4748 { namespace: "closed", name: "theme" },
4849 { namespace: "huge", name: "theme" },
4950 ],
@@ -65,6 +66,20 @@ describe("Open VSX themes", () => { · 15 unmodified lines
6566 { status: 200 },
6667 );
6768 }
69+ if (url.endsWith("/oversized/theme")) {
70+ return new Response(
71+ JSON.stringify(
72+ extensionDetail({
73+ namespace: "oversized",
74+ files: {
75+ ...extensionDetail().files,
76+ download: `${ASSET_ROOT.replace("demo/theme", "oversized/theme")}/oversized.vsix`,
77+ },
78+ }),
79+ ),
80+ { status: 200 },
81+ );
82+ }
6883 if (url.endsWith("/closed/theme")) {
6984 return new Response(
7085 JSON.stringify(extensionDetail({ namespace: "closed", license: "All Rights Reserved" })),
@@ -74,6 +89,15 @@ describe("Open VSX themes", () => { · 3 unmodified lines
7489 if (url.endsWith("/huge/theme")) {
7590 return new Response(JSON.stringify({ padding: "x".repeat(256 * 1024) }), { status: 200 });
7691 }
92+ if (url.endsWith("/oversized/theme/1.0.0/file/oversized.vsix")) {
93+ return new Response(null, {
94+ headers: { "content-length": String(20 * 1024 * 1024 + 1) },
95+ status: 200,
96+ });
97+ }
98+ if (url.endsWith(".vsix")) {
99+ return new Response(null, { headers: { "content-length": "1024" }, status: 200 });
100+ }
77101 if (url.endsWith("/icons/theme/1.0.0/file/package.json")) {
78102 return new Response(JSON.stringify({ contributes: { iconThemes: [{}] } }), { status: 200 });
79103 }
apps/web/src/openVsxThemes.ts+12−1
@@ -197,8 +197,19 @@ export async function searchOpenVsxThemes( · 196 unmodified lines
197197 try {
198198 const extension = extensionFromDetail(JSON.parse(new TextDecoder().decode(detailBytes)));
199199 if (!extension) return null;
200− const manifestResponse = await fetch(extension.manifestUrl, signal ? { signal } : {});
200+ const [manifestResponse, packageResponse] = await Promise.all([
201+ fetch(extension.manifestUrl, signal ? { signal } : {}),
202+ fetch(extension.vsixUrl, { method: "HEAD", ...(signal ? { signal } : {}) }),
203+ ]);
201204 if (!manifestResponse.ok) throw new Error("manifest unavailable");
205+ const packageLength = Number(packageResponse.headers.get("content-length"));
206+ if (
207+ packageResponse.ok &&
208+ Number.isFinite(packageLength) &&
209+ packageLength > MAX_VSIX_BYTES
210+ ) {
211+ return null;
212+ }
202213 const manifestBytes = await readCappedResponse(
203214 manifestResponse,
204215 MAX_MANIFEST_BYTES,