HarperFast / HarperFast/studio

Download application feature breaking page in Google Chrome

Open
#1,591 7 comments 0 reactions 0 assignees View on GitHub
blocked
Dominant language
TypeScript
Stars
5
Forks
4
Avg merge
1d 8h
Merged PRs (30d)
40

Description

## Summary

Attempting to download the source code from the applications leads to a "Something went wrong while displaying this webpage." screen and no download occurs. This was reproduced on multiple fabric v5 clusters.

## Repro

1. Sign in to `fabric.harper.fast`.
2. Enter a cluster
3. Applications > exp-engine > Download Application

Packaging loading indicator displays for ~20s
I see the pending `package_component` operation request resolve with a 200 and the bytes begin transferring, but before completion Chrome switches to "Something went wrong while displaying this webpage." and the download is not received.

Image

---

## Update 2026-08-12 — cause, and the split with HarperFast/harper#2150

**Cause: renderer OOM in Studio, not a transport failure.** The bytes really did arrive — the tab died decoding them.

`package_component` hands back the whole tarball as base64 inside JSON, and `DownloadApplicationModal.tsx` then makes four more full-size copies of it in the renderer:

```ts
const bytes = Uint8Array.from(atob(response.payload), c => c.charCodeAt(0));
const file = new Blob([bytes], { type: 'application/gzip' });
```

axios buffers the whole JSON text → `JSON.parse` copies the base64 string → `atob()` copies again → `Uint8Array.from(str, callback)` runs a per-byte JS callback over hundreds of millions of elements on the main thread → `Blob` copies once more. Roughly 5× the tarball inside one renderer, so Chrome kills it. Both the response text and the parsed string are also subject to V8's 512 MiB string cap in the browser.

The server has the mirror-image problem (~4.7× peak, and a hard `ERR_STRING_TOO_LONG` ceiling at ~384 MiB of gzipped output). That half is now tracked as **HarperFast/harper#2150** — stream the tarball as a binary response instead of base64-in-JSON. The machinery already exists there (`streamPackagedDirectory`, and `serverHandlers.js` already pipes stream returns for `get_deployment_payload` / `get_backup`).

### What stays here in Studio

1. **Warn before packaging — no server work needed.** `get_components` already returns per-file `size` and already skips `node_modules`, which matches the modal's default (`skipNodeModules: true`) exactly. Studio can sum the tree and show the size in the modal up front, then warn or require confirmation past a threshold. `APIFileEntry` in `getComponents.ts` doesn't declare `size` yet even though the server sends it.
- Blind spot: with **Include Node Modules** checked the size is unknown, since `get_components` omits that tree. harper#2150 carries an optional `estimate` mode to cover it.
2. **Fail visibly instead of dying.** A guarded decode with a real error toast beats a dead tab, and this is what @jjohnson-hdb actually asked for ("perhaps a warning message in studio would be nice ux in this situation over the generic chrome error").
3. **Consume the stream once harper#2150 lands.** axios (XHR) buffers, so this needs `fetch()`. Best-first:
- `showSaveFilePicker()` + `response.body.pipeTo(writable)` — true streaming to disk, zero JS memory. Chromium only.
- `await response.blob()` — Chrome backs large Blobs with disk, so it sidesteps the string cap, `atob`, and the per-byte callback entirely. Works everywhere; the pragmatic default/fallback.
4. **Handle proxy-mode instances explicitly.** Fabric Connect caps bodies at 2 MB and is a message-passing proxy that can't stream at all, so a large download requires a direct (Bearer operation-token) connection. `resolveInstanceConnection` already reports `mode: 'direct' | 'proxy'` — use it to give a real message rather than letting the request hang.

### And "don't do that" is still partly the answer

Even with streaming working end to end, pulling an ~800 MB customer application through a browser is the wrong tool — `scp` was the right call here. The guardrail above is the deliverable; unlimited in-browser downloads of arbitrarily large applications is not the goal.

Contributor guide

Open the contributing guide

Research direction

Start with DownloadApplicationModal.tsx and getComponents.ts, checking how the download response is decoded and how per-file size is represented. Read resolveInstanceConnection for direct versus proxy behavior, then run the application download flow in Chrome. Done means large downloads no longer kill the tab and the user receives a clear warning or error when the browser cannot handle the operation.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
frontend, web-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.