OGC API source configuration.
Called once per page, in order. Awaited.
Optionalsignal: AbortSignal
AbortSignal for cooperative cancellation, checked between pages.
Optionalbbox: [number, number, number, number]
Override bounding box (used by autoRefresh to inject viewport bbox).
Optionaldatetime: string
OGC datetime filter for the first request, e.g. <instant>/... The pages
after it follow the server's next links, which carry the query as the server built them.
OptionalfollowCursor: (url: string) => boolean
Asked before each page AFTER the first, with the cursor the server
rendered. config.url is the caller's own statement; a cursor is the SERVER's, and can
name any host. Returning false stops the walk there, with cursorRefused set — the run
is partial, and must not be read as an exhausted source. Absent, every cursor is followed.
What the run observed — counts, truncation, abort, and the unfollowed cursor.
Walks an OGC API Features collection page by page, handing each one over as it lands.
🛑 This is the ONE authority on pagination, and fetchOgcApiFeatures is a thin accumulator over it. The two used to be one function whose only exit was a fully materialised array, which forced every consumer to hold the whole collection: the offline pull then held it a SECOND time as store records, and wrote all of them in one IndexedDB transaction. Splitting the walk from the accumulation is what lets a consumer bound its own memory — and it is the same code path, so the display path cannot drift away from the pull's.
⚠️
onPageis AWAITED. The loop therefore serialises fetch → consume → fetch, on purpose: a consumer that writes to IndexedDB must be allowed to finish before the next page lands, otherwise the peak memory this function exists to bound comes straight back.⚠️ The cap applies PER PAGE, and
fetchedcounts the raw lengths. The previous shape cut once at the end (slice(0, maxFeatures)) over an array it had fully built; cutting per page yields the same features in the same order while never holding more than one page over the bound.🖐 The break's anchor is NOT the interesting half, and a first draft of this comment claimed it was. It read "a cap that stopped on the delivered count would fetch one page too many" — measured false by mutation:
deliveredismin(fetched, maxFeatures)by construction, so the two conditions are provably the same and nothing can tell them apart. What the raw count is genuinely needed for istruncated.fetched, the half that says by HOW MUCH the source was missed. Anchoring the break on it is a coincidence of having it at hand, not a property.