Releases: purescript/pursuit
Release list
v0.9.14
-
Fix the version selector getting stuck on "Loading …" after a restart (@thomashoneyman)
The version dropdown on package pages was initialized by a per-page inline script that Yesod bundles into a dynamic
/static/widget/<hash>.jsfile, whose contents live only in the running backend's in-memory store. Any backend restart (such as a deploy) orphaned those widgets: nginx kept serving already-cached HTML that referenced them, the script 404'd, and the selector never populated. The version selector, search box, and search "load more" link are now initialized from the statically embeddedPursuit.js, which is served from disk and survives restarts.
v0.9.13
- Avoid duplicate package decodes for old-version deprecation badges.
- Document the deprecation marker cache in the changelog.
v0.9.12
-
Challenge browser-like scraper traffic to package pages with Anubis
(@thomashoneyman)Distributed scraper fleets (thousands of IPs presenting spoofed browser
user agents, so robots.txt does not apply) crawl the module documentation
pages of large generated packages hard enough to take the server down.
Deploys now install Anubis, a
challenge proxy, and nginx routes cache misses under/packages/through
it: clients with browser-like user agents must pass a JavaScript
proof-of-work challenge once, while non-browser clients (curl, package
uploads, editor tooling, badge fetchers) and known-good search engine
crawlers pass through untouched. Cached package pages,/search, and all
other routes bypass Anubis entirely. -
Serve cached pages to browsers, and treat HEAD like GET (@thomashoneyman)
The nginx Accept-header map had no default, so the composite Accept
strings real browsers send ("text/html,application/xhtml+xml,...") never
matched the page cache, and every browser page view was re-rendered by the
backend. Cache lookups now default to the HTML representation, making
cache hits real for browsers - and confining the Anubis detour above to
genuine cache misses. HEAD requests now take the same cache/challenge path
as GET instead of going straight to the backend, where Yesod runs the full
GET handler for them - previously a full-cost decode that looked free. -
Give large package decodes an aggregate memory budget (@thomashoneyman)
v0.9.11 serialised decodes of package docs JSON files of 5MB or more, but
concurrent decodes of files just below that cutoff could still exhaust the
heap: scrapers crawling the ~1,900 module pages of next-purs-rsc (a 4.4MB
file) repeatedly took the server down by stacking four or five decodes of
it at once. Decodes of files of 1MB or more now share a 16MB in-flight
budget instead: a decode starts once the total size of large files being
decoded fits within the budget (or immediately if it is the only large
decode running, so files bigger than the budget itself are still served).
The queue remains bounded, failing fast with a 503 when full, and the
search index regeneration remains exempt from the bound.Budget admission is first-come-first-served: without an ordering, a file
too large to share the budget (which must wait until nothing else is in
flight) can be overtaken indefinitely by a steady stream of smaller
decodes - load testing showed a crawler burst starving the search index
build behind a react-icons decode for over an hour. -
Type search queries containing type variables now also match more concrete
types:a -> HTMLElementfindsHTMLAnchorElement -> HTMLElementthe same
way_ -> HTMLElementdoes (#395). Unlike a wildcard, instantiating a query
variable charges a small penalty, so results that unify with the query
directly rank first, and repeated variables must be instantiated
consistently (a -> aranksInt -> IntaboveInt -> String). Based on
#396 by @klntsky. (@thomashoneyman) -
The package and module badges on search results are now links to the
package page and module docs page (#424, @joprice). Builtin modules such
as Prim have no package page, so their package badge remains plain text. -
Write cached responses atomically (@thomashoneyman)
Every request that renders a cacheable page writes the response body to
the same cache file, so concurrent requests for one page raced on it: the
losers failed with "resource busy (file is locked)" and were served a 500
(#482 - under a 400-connection homepage flood, a third of responses), and
a half-written file was briefly visible to nginx, which serves the cache
directory directly. Responses are now written to a temporary file and
renamed into place, which is atomic; the same flood now returns no errors.
v0.9.11
What's changed
- Serialise decoding of large package files: rendering a documentation page decodes the entire docs JSON for that package version, and a few generated packages (react-icons, elmish-html, deku, ...) have 10-25MB files whose concurrent decodes could exhaust the heap and restart the server. Decodes of files over 5MB now run one at a time behind a bounded FIFO queue (excess requests fail fast with 503); smaller packages are unaffected. Version pages also no longer decode the same file twice. (#487)
- robots.txt now disallows SEO/bulk crawlers (Semrush, Ahrefs, DotBot, MJ12, Amazonbot, Bytespider, PetalBot).
- The page cache is evicted of files not accessed in 90 days by a weekly cron job installed by the deploy, so it cannot fill the disk.
- Heap limit raised to
-M3400mnow that the server has swap. deploy/SERVER.mddocuments server-only and DigitalOcean account state.
Full Changelog: v0.9.10...v0.9.11
v0.9.10
What's changed
- Fix the OOM crash loop: the search index is now built one package at a time instead of decoding every package into memory at once. The server had been OOM-killed ~56×/day; measured peak live memory for the index rebuild is now ~715MB (was 3.6GB+ and never completed). A package file which cannot be decoded is skipped with a logged error instead of silently aborting the build, and the new index is fully evaluated on the regeneration thread before being published. (#486)
pursuit.service: use both vCPUs and cap the heap below available RAM (-N2 -A64m -M3G).- CI: update deprecated GitHub Actions, pin the runner to
ubuntu-24.04(matching the server, upgraded 2026-07-03) and stack to 3.9.3.
Full Changelog: v0.9.9...v0.9.10
v0.9.9
- Update pursuit.service to 3.5GB max memory (@thomashoneyman)
v0.9.8
- Update PureScript to
0.15.10(@JordanMartinez)
v0.9.7
-
Update Pursuit version in
pursuit.cabal(@JordanMartinez)The previous release still indicates
v0.9.4
v0.9.6
- Update
purescriptto0.15.8(support dark theme) (@JordanMartinez) - Update documentation uploading instructions (@JordanMartinez)
- Bump CI's Ubuntu version to
latest(@JordanMartinez)
v0.9.5
Due to an Ubuntu brownout, CI did not build this release properly.
Please use v0.9.6.