IndexedDB, the Cache API and OPFS share one quota per origin, exposed by navigator.storage (StorageManager, Baseline Widely available). estimate() returns approximate usage and quota in bytes, plus usageDetails in Chromium 4,389 . Data is best-effort: under disk pressure the browser evicts whole origins, least recently used first, and Safari 10 also clears script-written data of sites unvisited for seven days. persist() asks for exemption from that eviction; Firefox 555 prompts, while Chrome 1 and Safari decide silently from the user's interaction with the site (a fresh headless profile is refused):
await (await caches.open('big')).put('/zeros', new Response(new Uint8Array(3 * 1024 ** 2)));
const { usage, quota, usageDetails } = await navigator.storage.estimate();
const mib = (bytes) => `${(bytes / 1024 ** 2).toFixed(1)} MiB`;
console.log(mib(usage), 'of', mib(quota), JSON.stringify(usageDetails)); // details: Chromium
console.log(await navigator.storage.persisted(), await navigator.storage.persist());3.0 MiB of 10243.0 MiB {"caches":3145815}
false falseQuotas vary: Chrome allows an origin up to 60% of the disk, Firefox the smaller of 10% of the disk or 10 GiB per site (up to 50% of the disk once persistent), and Safari about 60%. Check estimate() before large downloads, catch QuotaExceededError, and request persistence only for data the user cannot download again.