Skip to content

Sinks

A sink delivers a feed's entries somewhere: a Slack channel, a Discord channel, or any generic endpoint that accepts a JSON Feed. Where the rest of Neurowire reads sources, a sink writes results.

Sinks are an I/O and delivery concern, so they live in the CLI, not the pure library: packages/cli/src/sinks.ts. They pair naturally with the CLI's watch mode (see CLI), which re-fetches on an interval and pushes only newly-seen entries to a sink.

Auto-detection by host

You give a sink a destination URL; the kind is detected from its host (sinkKind(url)):

Host containsSink kind
slack.comslack
discord.com or discordapp.comdiscord
anything elsewebhook

There is no flag to set the kind; the URL decides.

Payload per sink

Slack

Slack incoming-webhook body: a single text field. The text is a header line (<feed title>: <N> new) followed by up to 10 bullet lines, one per entry as • <title> - <link>, with an overflow line (…and N more) when there are more.

json
{ "text": "AI News: 3 new\n• Title one - https://...\n• Title two - https://..." }

The title-to-link separator is a hyphen with spaces, never an em-dash.

Discord

Discord webhook body: a single content field built from the same header-and-bullets text, then capped at Discord's 2000-character limit (truncated if longer).

json
{ "content": "AI News: 3 new\n• Title one - https://..." }

Generic webhook

A generic webhook gets the full JSON Feed 1.1 of the entries as the request body, sent with Content-Type: application/feed+json.

deliver(url, feed)

The one entry point. It picks the kind, builds the right body and content type, and POSTs:

ts
import { deliver } from '@neurowire/cli'

const ok = await deliver('https://hooks.slack.com/services/...', feed)

deliver returns a boolean and never throws: a non-2xx response or a network error writes a one-line warning to stderr ([sink] <host> failed: ...) and returns false. That makes it safe to call from a long-running watch loop without a failed delivery crashing the process.

No built-in retry or dedup

WARNING

A sink fires once per call. It has no built-in retry and no deduplication. A transient failure is logged and dropped; it is not retried.

Deduplication is the watch loop's job, not the sink's. Use the CLI's --state flag with watch mode so each tick delivers only entries not seen on a previous tick. Without it, a watch run would re-deliver the whole feed every interval.

See CLI watch mode for wiring a source, an interval, --state, and one or more sinks together.