fix: pass wheel filename through extraction chain to fix micropip install

micropip requires the full PEP 427 wheel filename (name-version-py-abi-plat.whl)
— writing the file as bincio.whl caused InvalidWheelFilename. The wheel URL from
/api/wheel/version now provides the basename; it flows through fetchWheelBase64 →
extractFile → WebView where the file is written with the correct name and
_wheel_path is set as a Pyodide global before PY_INSTALL_WHEEL runs.
This commit is contained in:
Davide Scaini
2026-04-25 09:29:33 +02:00
parent ef45d4f4bb
commit 69571c1306
3 changed files with 23 additions and 13 deletions
+11 -6
View File
@@ -97,12 +97,13 @@ export default function ImportScreen() {
// allows local-network HTTP (NSAllowsLocalNetworking=true in Info.plist).
const instanceUrl = await getInstanceUrl(dbCtx);
setState({ status: 'loading', msg: 'Fetching Bincio engine…' });
const wheelBase64 = await fetchWheelBase64(instanceUrl);
const { base64: wheelBase64, filename: wheelFilename } = await fetchWheelBase64(instanceUrl);
const result = await extractFile(
name,
base64,
wheelBase64,
wheelFilename,
(msg) => setState({ status: 'loading', msg }),
);
@@ -215,21 +216,25 @@ async function getInstanceUrl(db: ReturnType<typeof useSQLiteContext>): Promise<
}
// In-memory cache so repeated imports in one session don't re-download the wheel.
let _cachedWheelBase64: string | null = null;
let _cachedWheel: { base64: string; filename: string } | null = null;
async function fetchWheelBase64(instanceUrl: string): Promise<string> {
if (_cachedWheelBase64) return _cachedWheelBase64;
async function fetchWheelBase64(instanceUrl: string): Promise<{ base64: string; filename: string }> {
if (_cachedWheel) return _cachedWheel;
const base = instanceUrl || 'https://bincio.org';
// Ask the instance for the canonical wheel URL (handles both dev and prod layouts).
let wheelUrl = `${base}/api/wheel/download`;
let wheelFilename = 'bincio-0.1.0-py3-none-any.whl';
try {
const vr = await fetch(`${base}/api/wheel/version`, { signal: AbortSignal.timeout(5000) });
if (vr.ok) {
const d = await vr.json() as { api_url?: string; url?: string };
const path = d.api_url ?? d.url ?? '/api/wheel/download';
wheelUrl = path.startsWith('http') ? path : `${base}${path}`;
// Extract the filename from the URL path (last segment after final /)
const urlBasename = wheelUrl.split('/').pop() ?? '';
if (urlBasename.endsWith('.whl')) wheelFilename = urlBasename;
}
} catch {}
@@ -237,8 +242,8 @@ async function fetchWheelBase64(instanceUrl: string): Promise<string> {
const resp = await fetch(wheelUrl);
if (!resp.ok) throw new Error(`Could not download Bincio engine (${resp.status}). Is the instance running?`);
const buf = await resp.arrayBuffer();
_cachedWheelBase64 = arrayBufferToBase64(buf);
return _cachedWheelBase64;
_cachedWheel = { base64: arrayBufferToBase64(buf), filename: wheelFilename };
return _cachedWheel;
}
function arrayBufferToBase64(buf: ArrayBuffer): string {