Skip to main content
@promptjuggler/browser is the client for streaming: it opens the SSE connection, speaks the whole protocol — segments, resets, resume, staleness — and hands you one maintained text per run. Everything in the package is safe to ship to an end user’s browser; it authenticates with a thread-scoped credential and never sees your API key.

Installation

It runs in every modern browser (fetch + ReadableStream) and in Node.js 22+.

The credential flow

Your backend mints the credential with a server SDK and returns it verbatim; the browser hands it to getToken. That one function is the entire auth story:
Your backend (Node example)
Browser

Events

The one flag that matters is gapped: when a terminal event carries gapped: true, the streamed text is (or may be) incomplete — fetch the run through your backend for the authoritative result. When it is false, the text you rendered is the full output and no fetch is needed — with one edge for raw-event consumers: a gap arriving after a clean done retroactively proves that text incomplete (as in the sample above). The React hook folds all of this into runs[runId].gapped, which is why it is the recommended surface. Reconnection is automatic — exponential backoff after failures, immediate when the server hands the connection off during a deploy — and resumes exactly where it left off. disconnect() stops everything.

React

runs[runId] is { text, channel, status: 'streaming' | 'done' | 'failed', gapped?, error? } — render text and you have a chatbot. The options object is read through a ref, so inline literals never cause reconnects; only the deps array does.

Angular

The same surface as signals, for Angular 17+:
stream.connected and stream.runs are signals holding the same shapes as the React hook. Reactivity follows Angular’s grain: any signal read during the factory call — here threadId, hoisted into a local — is tracked, and changing it drops the old subscription (and its runs) and connects a fresh one. Reads deferred into getToken happen after tracking ends, so hoist them as above. Call it in an injection context (a field initializer or constructor); the subscription disconnects when the component is destroyed. Signals-only, so it works in zoneless applications.

Workflows

Every prompt node of a workflow streams into the same connection, keyed by runId and tagged with its channel. Follow only the user-facing lanes with channels: ['support']. See the chatbot guide for the full end-to-end walkthrough.