feat(hydromancer)!: add l2Book surface and body-driven /info#126
Closed
jaspersagent wants to merge 7 commits into
Closed
feat(hydromancer)!: add l2Book surface and body-driven /info#126jaspersagent wants to merge 7 commits into
jaspersagent wants to merge 7 commits into
Conversation
…hapes
Coins now come from the JSON body (`{type: "assetContext", coins}`),
matching Hydromancer's native /info contract. Non-assetContext bodies
are rejected with 400 in the module, since the proxy never consumes
any other Hydromancer endpoint. The dispatcher no longer sniffs
bodies or synthesizes upstream routes for hydromancer, keeping
handle-proxy-request route-type-agnostic.
BREAKING CHANGE: not backwards compatible with the previous URL-param
shape (`/hydromancer/:coin` + `fetchFromModule`). Routes must now use
`POST /info` with an assetContext body. The transparent passthrough
for other Hydromancer endpoints (userState, meta, etc.) is also gone;
those return 400.
The Bun-to-Node migration's elysia adapter consumes the request stream during the route's parse step, so modules calling request.clone().text() afterward fail with "Failed to read request body". The dispatcher already has the parsed body; thread it into ModuleService.handleRequest as a fourth argument so hydromancer can read its assetContext payload without touching the request stream.
After rebasing onto main, the new lo-tech and pm-insights arms call handleRequest with three args. Mark `body` optional in the interface so only hydromancer (which actually needs it) has to pass it, and default to "" inside hydromancer so an empty body still hits the existing 400 reject for non-assetContext shapes.
Adds a second subscription type to the hydromancer WS multiplex. /info l2Book bodies route through a wait-for-first-frame cache with no REST fallback; coins not seen on the socket within the wait timeout come back null. assetContext-specific config fields gain an assetCtx prefix so the l2Book knobs can sit alongside without ambiguity. The standalone smoketest is removed now that the production path covers the same flow.
The l2Book book handler stranded cache waiters when a request timed out, WS reconnects never detached their event listeners, and the idle-cleanup daemons could not be interrupted. A tryGetOrWait primitive now evicts the waiter on timeout, every WS listener is paired with a removeEventListener in the release path, and the module runs as a scoped layer whose finalizers stop every daemon on teardown. The two near-identical WS channels collapse into one channel-keyed registry and the twin idle-cleanup blocks into one registry-driven loop. The request body parser is now a single valibot variant schema, the asset cache moves to a shared freshness-cache, and the ModuleService body parameter is required so the proxy always threads the parsed body.
…ules dxfeed, lo-tech, and pyth-lazer each hand-rolled the same getOrWaitPrice catch-evict-rethrow block; they now call the shared getOrWaitOrEvict primitive. chainlink-streams and pm-insights re-read the request body with request.clone().text() instead of the body the proxy already parsed; they now take the threaded body parameter. Behavior is unchanged.
Contributor
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
/infonow reads coins from the request body, and the hydromancer module serves order books alongside asset contexts.The route accepts
{"type": "assetContext" | "l2Book", "coins": [...]}and rejects anything else with a 400. The proxy parses the body once and threads it into the module handler.assetContext stays WS-first with a REST fallback. l2Book is WS-first with a short wait for the first frame and no fallback, because books update many times a second and a fallback fetch would be stale before it returned. An unseeded coin comes back
null. Both surfaces multiplex over one WebSocket on one credential.The WS code then went through a review pass. It was leaking three ways: book-cache waiters stranded on timeout, reconnect listeners that were never detached, and idle-cleanup fibers with no way to stop them. All three are closed, and the module now runs as a scoped layer so every background daemon dies with the layer. The two near-identical WS channels became one channel-keyed registry, and the
getOrWaitPricecatch-and-evict block that dxfeed, lo-tech, and pyth-lazer each hand-rolled is now a sharedgetOrWaitOrEvictprimitive.Two things to know before merging:
coinsCleanup*toassetCtx*and friends), so existing operator config files break.ModuleService.handleRequesttakes a requiredbodyparameter now; chainlink-streams and pm-insights use it instead of re-reading the request.