Skip to content

feat(backend): wire feed renderers into API - #76

Open
kennethnym wants to merge 1 commit into
masterfrom
feat/feed-renderer-api
Open

feat(backend): wire feed renderers into API#76
kennethnym wants to merge 1 commit into
masterfrom
feat/feed-renderer-api

Conversation

@kennethnym

Copy link
Copy Markdown
Owner

What

Wire the FeedItemRenderer functions exported by source packages into the backend and expose them through the /api/feed endpoint via a ?render=json-render query parameter.

Changes

New types:

  • FeedRenderer — holds a sourceId → FeedItemRenderer map. render(items) returns RenderedFeedItem[], silently dropping items with no matching renderer.
  • FeedRendererProvider — interface with feedRendererForUser(userId): FeedRenderer, mirrors FeedSourceProvider.

Backend wiring:

  • tfl/renderer-provider.ts and caldav/renderer-provider.ts export source IDs and renderer functions.
  • UserSession accepts a FeedRenderer via options and exposes it as readonly renderer.
  • UserSessionManager accepts a FeedRendererProvider and passes per-user FeedRenderer instances to sessions.
  • server.ts aggregates renderer entries and wires the provider.

API behavior:

  • GET /api/feed — unchanged.
  • GET /api/feed?render=json-render — renders items via FeedRenderer, converts JrxNodeSpec (json-render format) in the handler, returns items with ui field.
  • Unknown render value → 400.
  • render=json-render with no renderer configured → 500.

Other:

  • UserSession constructor now takes a single options object ({ sources, enhancer?, renderer? }).
  • @nym.sh/jrx added as backend dependency.

Add FeedRenderer and FeedRendererProvider to support server-side
rendering of feed items via ?render=json-render query param.

- FeedRenderer maps sourceId to FeedItemRenderer, renders matching
  items and drops the rest
- FeedRendererProvider mirrors FeedSourceProvider pattern for
  per-user renderer construction
- UserSession exposes renderer, handler converts JrxNode to Spec
- Returns 400 for unknown render format, 500 if renderer missing

Co-authored-by: Ona <no-reply@ona.com>
const renderedItems = session.renderer.render(feed.items).map((item) => ({
...item,
ui: render(item.ui),
}))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both FeedItemRenderer functions and render() from @nym.sh/jrx can throw (e.g. malformed JRX node, duplicate keys, fragment at root). A single bad item would crash the entire request with an unhandled error.

Consider wrapping this in a try/catch to return a structured error, similar to how the enhancement pipeline handles failures gracefully:

Suggested change
}))
let renderedItems
try {
renderedItems = session.renderer.render(feed.items).map((item) => ({
...item,
ui: render(item.ui),
}))
} catch (err) {
const message = err instanceof Error ? err.message : "Unknown rendering error"
return c.json({ error: `Rendering failed: ${message}` }, 500)
}

@ona-patrol

Copy link
Copy Markdown
Collaborator

Reviewed this PR and found 1 area that needs attention — see inline comment on the rendering error handling in http.ts.

Overall the implementation is well-structured: clean separation between FeedRenderer, FeedRendererProvider, and the wiring in server.ts. The UserSession constructor refactor to an options object is a good call. Test coverage is thorough — happy paths, filtering, edge cases, and error responses are all covered.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants