Source
This app’s code and revision history are public. Download a copy or explore the files below.
Public
The app’s code and revision history are public.
Run it yourself
Take the whole tree and run it on your own machine. The download needs no account. Cloning needs a sign-in.
Download .zip
bounded clone 80ddb8c5df822e290afdd3c7
A copy you download is not bounded. The guarantees this app runs under - declared egress, proven invariants, writes that fail closed - live in the runtime serving this page, not in the files. What you get is auditable source you can read and run, with no runtime guarantees attached.
src/lib/items.ts
// What the sender offers: single files as they are, and folders zipped on this device while they
// are sent (nothing is zipped ahead of time, so a folder of any size starts sending at once).
import { makeZip, predictLength } from "client-zip";
import { sha256 } from "@noble/hashes/sha2";
import { bytesToHex } from "@noble/hashes/utils";
import { CHUNK, type FileInfo } from "./protocol";
/** One thing the person picked: a file, or a folder with the paths of its files inside it. */
export interface Pick {
name: string;
folder: boolean;
files: { path: string; file: File }[];
}
export interface Outgoing {
info: FileInfo;
pick: Pick;
sum?: Promise<string>;
}
/** Files from a file input. A folder input gives paths like "Photos/2026/a.jpg": one pick per top folder. */
export function picksFromInput(list: FileList | File[]): Pick[] {
const picks: Pick[] = [];
const folders = new Map<string, Pick>();
for (const file of Array.from(list)) {
const rel = (file as File & { webkitRelativePath?: string }).webkitRelativePath || "";
const top = rel.includes("/") ? rel.split("/")[0] : "";
if (!top) { picks.push({ name: file.name, folder: false, files: [{ path: file.name, file }] }); continue; }
let p = folders.get(top);
if (!p) { p = { name: top, folder: true, files: [] }; folders.set(top, p); picks.push(p); }
p.files.push({ path: rel, file });
}
return picks;
}
type Entry = { isFile: boolean; isDirectory: boolean; name: string; fullPath: string };
type FileEntry = Entry & { file(ok: (f: File) => void, fail: (e: unknown) => void): void };
type DirEntry = Entry & { createReader(): { readEntries(ok: (e: Entry[]) => void, fail: (e: unknown) => void): void } };
/** Files and folders dropped on the page. Entries must be taken before the first await. */
export function picksFromDrop(dt: DataTransfer): Promise<Pick[]> {
const entries = Array.from(dt.items || [])
.filter((it) => it.kind === "file")
.map((it) => (it as DataTransferItem & { webkitGetAsEntry?: () => Entry | null }).webkitGetAsEntry?.() || null);
const plain = Array.from(dt.files || []);
if (!entries.length || entries.some((e) => !e)) return Promise.resolve(picksFromInput(plain));
return Promise.all((entries as Entry[]).map(async (entry, n): Promise<Pick> => {
if (entry.isFile) {
const file = await fileOf(entry as FileEntry).catch(() => plain[n]);
return { name: file.name, folder: false, files: [{ path: file.name, file }] };
}
const files: Pick["files"] = [];
await walk(entry as DirEntry, files);
return { name: entry.name, folder: true, files };
}));
}
function fileOf(entry: FileEntry): Promise<File> {
return new Promise((ok, fail) => entry.file(ok, fail));
}
async function walk(dir: DirEntry, out: Pick["files"]) {
const reader = dir.createReader();
for (;;) {
const batch = await new Promise<Entry[]>((ok, fail) => reader.readEntries(ok, fail));
if (!batch.length) return;
for (const e of batch) {
if (e.isDirectory) await walk(e as DirEntry, out);
else out.push({ path: e.fullPath.replace(/^\//, ""), file: await fileOf(e as FileEntry) });
}
}
}
/** Turns picks into numbered items, the first one numbered `start`. */
export function toOutgoing(picks: Pick[], start: number): Outgoing[] {
return picks.filter((p) => p.files.length).map((pick, n) => {
const i = start + n;
if (!pick.folder) {
const f = pick.files[0].file;
return { pick, info: { i, name: f.name, size: f.size, type: f.type || "application/octet-stream" } };
}
const size = Number(predictLength(pick.files.map((e) => ({ name: e.path, size: e.file.size }))));
return { pick, info: { i, name: `${pick.name}.zip`, size, type: "application/zip", zip: true } };
});
}
const BLOCK = 1024 * 1024;
/** The item's bytes from offset `from`, in pieces of at most CHUNK bytes. */
export async function* read(item: Outgoing, from: number): AsyncGenerator<Uint8Array> {
if (!item.info.zip) {
const f = item.pick.files[0].file;
for (let pos = from; pos < f.size;) {
const end = Math.min(f.size, pos + BLOCK);
const buf = new Uint8Array(await f.slice(pos, end).arrayBuffer());
for (let o = 0; o < buf.length; o += CHUNK) yield buf.subarray(o, o + CHUNK);
pos = end;
}
return;
}
// The same files in the same order make the same zip, byte for byte, so a dropped connection
// can pick up from an offset and the checksum matches.
const zip = makeZip(item.pick.files.map((e) => ({ name: e.path, lastModified: new Date(e.file.lastModified), input: e.file })), { buffersAreUTF8: true });
const reader = zip.getReader();
let pos = 0;
try {
for (;;) {
const { done, value } = await reader.read();
if (done) return;
let chunk = value;
if (pos + chunk.length <= from) { pos += chunk.length; continue; }
if (pos < from) { chunk = chunk.subarray(from - pos); pos = from; }
for (let o = 0; o < chunk.length; o += CHUNK) yield chunk.subarray(o, o + CHUNK);
pos += chunk.length;
}
} finally {
void reader.cancel().catch(() => null);
}
}
/** SHA-256 of the whole item, worked out once and shared by every receiver. */
export function sumOf(item: Outgoing): Promise<string> {
item.sum ??= (async () => {
const h = sha256.create();
for await (const chunk of read(item, 0)) h.update(chunk);
return bytesToHex(h.digest());
})();
item.sum.catch(() => { item.sum = undefined; });
return item.sum;
}
Change history
Point OpenApps links to openapps.free
Bind agent address in policy
Retry release capture
Use OpenApps service URLs and add contribution strip
Bounded source sync
Bounded source sync
Bounded source sync