Unified · Split
| @@ -39,12 +39,17 @@ export function ThemeSearchSection({ · 38 unmodified lines | ||
| 39 | 39 | useEffect(() => { |
| 40 | 40 | requestRef.current?.abort(); |
| 41 | 41 | 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 | + }; | |
| 48 | 53 | }, [open]); |
| 49 | 54 | |
| 50 | 55 | const runSearch = useCallback(async (searchText: string) => { |
| @@ -44,6 +44,7 @@ describe("Open VSX themes", () => { · 43 unmodified lines | ||
| 44 | 44 | extensions: [ |
| 45 | 45 | { namespace: "demo", name: "theme" }, |
| 46 | 46 | { namespace: "icons", name: "theme" }, |
| 47 | + { namespace: "oversized", name: "theme" }, | |
| 47 | 48 | { namespace: "closed", name: "theme" }, |
| 48 | 49 | { namespace: "huge", name: "theme" }, |
| 49 | 50 | ], |
| @@ -65,6 +66,20 @@ describe("Open VSX themes", () => { · 15 unmodified lines | ||
| 65 | 66 | { status: 200 }, |
| 66 | 67 | ); |
| 67 | 68 | } |
| 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 | + } | |
| 68 | 83 | if (url.endsWith("/closed/theme")) { |
| 69 | 84 | return new Response( |
| 70 | 85 | JSON.stringify(extensionDetail({ namespace: "closed", license: "All Rights Reserved" })), |
| @@ -74,6 +89,15 @@ describe("Open VSX themes", () => { · 3 unmodified lines | ||
| 74 | 89 | if (url.endsWith("/huge/theme")) { |
| 75 | 90 | return new Response(JSON.stringify({ padding: "x".repeat(256 * 1024) }), { status: 200 }); |
| 76 | 91 | } |
| 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 | + } | |
| 77 | 101 | if (url.endsWith("/icons/theme/1.0.0/file/package.json")) { |
| 78 | 102 | return new Response(JSON.stringify({ contributes: { iconThemes: [{}] } }), { status: 200 }); |
| 79 | 103 | } |
| @@ -197,8 +197,19 @@ export async function searchOpenVsxThemes( · 196 unmodified lines | ||
| 197 | 197 | try { |
| 198 | 198 | const extension = extensionFromDetail(JSON.parse(new TextDecoder().decode(detailBytes))); |
| 199 | 199 | 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 | + ]); | |
| 201 | 204 | 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 | + } | |
| 202 | 213 | const manifestBytes = await readCappedResponse( |
| 203 | 214 | manifestResponse, |
| 204 | 215 | MAX_MANIFEST_BYTES, |