Skip to content

Commit e4ebac9

Browse files
committed
feat: add npm registry fallback and improve error logging
- Add npm registry fallback when GitHub API is rate-limited or unavailable (fetches version from registry.npmjs.com/vite-plus, constructs download URL) - For specific ?tag= requests, construct URL directly from tag as fallback - Log GitHub releases list response for troubleshooting - Log npm registry errors
1 parent 33edf95 commit e4ebac9

2 files changed

Lines changed: 75 additions & 12 deletions

File tree

routes/download.test.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, it } from "vite-plus/test";
2-
import { detectArch, parseRelease } from "./download";
2+
import { buildReleaseFromTag, detectArch, parseRelease } from "./download";
33

44
describe("detectArch", () => {
55
it("defaults to x64 when no query param or user-agent", () => {
@@ -119,3 +119,17 @@ describe("parseRelease", () => {
119119
expect(result!.assets.arm64).toBeUndefined();
120120
});
121121
});
122+
123+
describe("buildReleaseFromTag", () => {
124+
it("constructs download URLs from a tag", () => {
125+
const result = buildReleaseFromTag("v0.1.17-alpha.0");
126+
expect(result).toEqual({
127+
tag: "v0.1.17-alpha.0",
128+
assets: {
129+
x64: "https://github.com/voidzero-dev/vite-plus/releases/download/v0.1.17-alpha.0/vp-setup-x86_64-pc-windows-msvc.exe",
130+
arm64:
131+
"https://github.com/voidzero-dev/vite-plus/releases/download/v0.1.17-alpha.0/vp-setup-aarch64-pc-windows-msvc.exe",
132+
},
133+
});
134+
});
135+
});

routes/download.ts

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,51 @@ async function fetchRelease(
8181
return null;
8282
}
8383
const releases = (await res.json()) as GitHubRelease[];
84+
console.log(
85+
"GitHub releases:",
86+
releases.map((r) => ({ tag: r.tag_name, assets: r.assets.map((a) => a.name) })),
87+
);
8488
return (
8589
releases.find((r) =>
8690
r.assets.some((a) => a.name === ASSET_NAMES.x64 || a.name === ASSET_NAMES.arm64),
8791
) ?? null
8892
);
8993
}
9094

95+
export function buildReleaseFromTag(tag: string): CachedRelease {
96+
const base = `https://github.com/${GITHUB_OWNER}/${GITHUB_REPO}/releases/download/${tag}`;
97+
return {
98+
tag,
99+
assets: {
100+
x64: `${base}/${ASSET_NAMES.x64}`,
101+
arm64: `${base}/${ASSET_NAMES.arm64}`,
102+
},
103+
};
104+
}
105+
106+
interface NpmDistTags {
107+
alpha?: string;
108+
latest?: string;
109+
}
110+
111+
async function fetchLatestVersionFromNpm(): Promise<string | null> {
112+
try {
113+
const res = await fetch("https://registry.npmjs.com/vite-plus", {
114+
headers: { Accept: "application/vnd.npm.install-v1+json" },
115+
});
116+
if (!res.ok) {
117+
console.error(`npm registry error: ${res.status} ${res.statusText}`);
118+
return null;
119+
}
120+
const data = (await res.json()) as { "dist-tags"?: NpmDistTags };
121+
// Windows installers are currently published under the alpha dist-tag
122+
return data["dist-tags"]?.alpha ?? data["dist-tags"]?.latest ?? null;
123+
} catch (err) {
124+
console.error("Failed to fetch version from npm registry:", err);
125+
return null;
126+
}
127+
}
128+
91129
function cacheKey(tag: string | undefined): string {
92130
return tag ? `release:tag:${tag}` : "release:latest";
93131
}
@@ -106,21 +144,32 @@ async function getRelease(
106144

107145
try {
108146
const release = await fetchRelease(tag, githubToken);
109-
if (!release) return null;
110-
const parsed = parseRelease(release);
111-
if (parsed) {
112-
const ttl = tag ? TAGGED_CACHE_TTL : LATEST_CACHE_TTL;
113-
const staleTtl = ttl + 3600;
114-
await Promise.all([
115-
kv.put(key, parsed, { ttl }),
116-
kv.put(staleCacheKey(tag), parsed, { ttl: staleTtl }),
117-
]);
147+
if (release) {
148+
const parsed = parseRelease(release);
149+
if (parsed) {
150+
const ttl = tag ? TAGGED_CACHE_TTL : LATEST_CACHE_TTL;
151+
const staleTtl = ttl + 3600;
152+
await Promise.all([
153+
kv.put(key, parsed, { ttl }),
154+
kv.put(staleCacheKey(tag), parsed, { ttl: staleTtl }),
155+
]);
156+
return parsed;
157+
}
118158
}
119-
return parsed;
120159
} catch (err) {
121160
console.error("Failed to fetch release from GitHub:", err);
122-
return await kv.get<CachedRelease>(staleCacheKey(tag));
123161
}
162+
163+
// Fallback 1: stale KV cache
164+
const stale = await kv.get<CachedRelease>(staleCacheKey(tag));
165+
if (stale) return stale;
166+
167+
// Fallback 2: construct download URLs from tag or npm registry version
168+
if (tag) return buildReleaseFromTag(tag);
169+
const version = await fetchLatestVersionFromNpm();
170+
if (version) return buildReleaseFromTag(`v${version}`);
171+
172+
return null;
124173
}
125174

126175
export const GET = defineHandler(async (c) => {

0 commit comments

Comments
 (0)