drop-free.openapps.xyz

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.
Owned app · rev b2e053fd98cb
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.
← All files
src/lib/sink.ts
// Where a file being received is kept until the person saves it. Where the browser allows, it goes
// straight to the site's private storage on disk (so a file can be as big as the free space the
// browser gives the site); otherwise it is kept in memory, which limits very large files.

export interface Sink {
  /** Disk: written to this device's storage. Memory: held in the browser's memory. */
  readonly kind: "disk" | "memory";
  write(part: Blob): Promise<void>;
  finish(): Promise<Blob>;
  discard(): Promise<void>;
}

const DIR = "drop-received";
const HOUR = 3600_000;
let dirHandle: Promise<FileSystemDirectoryHandle | null> | null = null;

function storageDir(): Promise<FileSystemDirectoryHandle | null> {
  dirHandle ??= (async () => {
    try {
      if (typeof FileSystemFileHandle === "undefined" || !("createWritable" in FileSystemFileHandle.prototype)) return null;
      const root = await navigator.storage.getDirectory();
      const dir = await root.getDirectoryHandle(DIR, { create: true });
      // Files kept from an earlier visit are removed once they are an hour old.
      for await (const name of dir.keys()) {
        const t = Number(name.split("-")[0]);
        if (!t || Date.now() - t > HOUR) await dir.removeEntry(name).catch(() => null);
      }
      return dir;
    } catch {
      return null; // private windows and older browsers
    }
  })();
  return dirHandle;
}

/** Free space the browser lets this site use, in bytes, or null when it does not say. */
export async function freeSpace(): Promise<number | null> {
  try {
    const e = await navigator.storage.estimate();
    return typeof e.quota === "number" ? Math.max(0, e.quota - (e.usage || 0)) : null;
  } catch {
    return null;
  }
}

export async function openSink(size: number, label: string): Promise<Sink> {
  const dir = await storageDir();
  const free = dir ? await freeSpace() : null;
  if (dir && (free === null || free > size + 16 * 1024 * 1024)) {
    try {
      const name = `${Date.now()}-${label}`;
      const handle = await dir.getFileHandle(name, { create: true });
      const writable = await handle.createWritable({ keepExistingData: false });
      return new DiskSink(dir, name, handle, writable);
    } catch {
      // fall back to memory
    }
  }
  return new MemorySink();
}

class DiskSink implements Sink {
  readonly kind = "disk";
  constructor(
    private dir: FileSystemDirectoryHandle,
    private name: string,
    private handle: FileSystemFileHandle,
    private writable: FileSystemWritableFileStream,
  ) {}

  write(part: Blob) { return this.writable.write(part); }

  async finish() {
    await this.writable.close();
    return this.handle.getFile();
  }

  async discard() {
    await this.writable.abort().catch(() => null);
    await this.dir.removeEntry(this.name).catch(() => null);
  }
}

class MemorySink implements Sink {
  readonly kind = "memory";
  private parts: Blob[] = [];
  async write(part: Blob) { this.parts.push(part); }
  async finish() { return new Blob(this.parts); }
  async discard() { this.parts = []; }
}

Change history

Point OpenApps links to openapps.free
b2e053fd98 · 17 hours ago
Bind agent address in policy
ca424dd814 · 19 hours ago
Retry release capture
ae07f1a3fd · 19 hours ago
Use OpenApps service URLs and add contribution strip
0452ce136f · 19 hours ago
Bounded source sync
ca1bc00aa4 · 1 day ago
Bounded source sync
d4982911a0 · 1 day ago
Bounded source sync
18c97fe4ca · 1 day ago