ConstInterrupts the download in progress, and SAYS SO.
🛑 THE EVENT WAS MISSING, AND IT WAS NOT A DEAD LISTENER BUT A BUG. Measured:
geoleaf:cache:cancelled had two listeners and zero emitters. A pre-flight
had read the same figure and concluded "the UI listens for nothing" — true on
the measurement, false on the action to take.
What the listener does (handleCancelled, offline-ui): it resets the progress
bar, re-enables the button and hands control back. Without an emitter, a user
who cancels sees "⏹️ Stopping…" indefinitely, button disabled, with no way
to relaunch anything short of a page reload. The listener was right; the
emitter was what was missing.
⚠️ Emitted here and not in Downloader: the orchestrator owns a profile
download's lifecycle, and Downloader.cancelDownload() is also called by
internal paths. An emitter per intent, not per mechanism.
🛑 It stops the whole gesture, not only its resources. The downloader's controller
lives while the resources download, and no longer: a stop pressed during the entities
pull reached nothing, and the pull ran to its end. The gesture's own controller is
raised here too — the pull ends at its page, and the result of cacheProfile says
cancelled.
Pulls the entities of the selected layers that declare a source, and completes result.
It cannot fail the download: pullDeclaredLayers reports, it does not throw. Losing
tens of megabytes of tiles because one OGC source answered 503 would be a worse outcome
than the partial state it is protecting from.
🛑 The gesture's signal goes with it. Without it, « Stop » reached the resources only: the pull ran to its end, and a complete pull removes what it did not return. Raised before the pull, the signal means no layer is attempted; during it, the pull ends at its page, keeps what it wrote and removes nothing.
Profile being downloaded.
The saved selection, as the download handed it over.
The gesture's signal, raised by cancelDownload().
The download's result, completed in place with pulledLayers and
cancelled.
Keeps the option lists the enumeration found, and hands back what remains to download.
🛑 The lists leave the download. The downloader keeps what it fetches in layers, the
store the cache budget evicts first — and configuration resources are downloaded first,
so a list stored there would be the first thing to go. options/option-lists.ts keeps it
in preferences, which nothing evicts. Extracted from cacheProfile for its ESLint
complexity ceiling (20), seen red at 21.
Everything the enumerator listed.
The resources the downloader must fetch — the option lists removed.
Enforces the configured maxCacheBytes budget over the IndexedDB layer/tile
cache by evicting least-recently-cached records. No-op when the budget is
disabled (0/missing) or IndexedDB is unavailable. Emits
geoleaf:cache:evicted (detail = eviction summary) when records are removed.
Failures are swallowed — eviction must never break a successful download.
Origin-wide storage usage, as the browser reports it.
🛑 navigator.storage.estimate() measures the WHOLE ORIGIN, never one store. Nothing in
the returned numbers is attributable to layer caching, to the tile cache, or to any
single database — everything the page has ever stored is in there, including what other
features wrote. Reading a drop in usage as "the eviction worked" is therefore a
mistake the shape of this value invites.
This is the reference reader — the only one with a caller outside its own file. Three
others exist, and each names the same numbers differently, which is what makes picking
the wrong one silent: a caller that reads used off this object gets undefined.
• offline/db/preferences.ts — getStats() says used where this one says usage,
and keeps percentage as a float where this one rounds.
• offline-ui, cache/download-handler.ts — _checkQuota() reads the estimate raw.
• offline-ui, cache/layer-selector/selection-cache.ts — reads it raw too.
⚠️ The two plugin-side readers are NOT a dedup opportunity, and the reason is in this function's own return: when the browser cannot answer, it returns zeros, which are indistinguishable from "the origin is empty and the quota is nought". Both plugin readers depend on telling those apart — an unreadable quota there means "proceed", while a zero quota means "refuse". Routing them through this reader would turn every unreadable quota into a refusal. The duplication carries a distinction this signature cannot express; removing it without changing the signature would introduce a bug.
Origin usage and quota in bytes, the percentage rounded, and the remainder.
The running download's controller: one per gesture, raised by
cancelDownload().🛑
Downloaderhas its own, and it is not enough: created when the resources start and dropped when they end, it covers neither the enumeration before them nor the entities pull after them.