npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@portalshq/capability-queue-broadcast

v0.1.9

Published

Server-only client for isolated Queue Broadcast Server instances.

Readme

@portalshq/capability-queue-broadcast

Server-only producer API for one isolated Queue Broadcast Server endpoint.

ContentQueuePipeline is the reusable producer preparation boundary. It deduplicates channel/run/content identities, bounds concurrent preparation and measured duration/bytes/count, accounts for shared visuals once, commits results in enqueue order, and exposes staging/release/completion acknowledgments. Applications provide content preparation and persistence callbacks.

The queue is deliberately separate from playback: a trusted application backend uses this package to enqueue and observe jobs; its browser player gets only the public HLS manifest returned by getPlayback(). Use @portalshq/capability-video-delivery's HlsPlaybackSession in a player or another stream-delivery integration.

Backend flow

import { QueueBroadcastClient } from "@portalshq/capability-queue-broadcast";

const broadcast = new QueueBroadcastClient({
  endpoint: process.env.STREAMER_ENDPOINT!, // no token/query/fragment
  token: process.env.STREAMER_API_TOKEN!,   // backend secret only
});

const slotKey = `${generationRunId}:turn:1`;
const image = await broadcast.stageUpload({
  mediaType: "image",
  asset: { data: finishedImageBlob, filename: "scene.jpg", sha256: imageSha256 },
  imageDuration: 18,
  idempotencyKey: `${slotKey}:image`,
  slotKey,
});
await broadcast.stageUpload({
  mediaType: "audio",
  asset: { data: finishedAudioBlob, filename: "narration.wav", sha256: audioSha256 },
  idempotencyKey: `${slotKey}:audio`,
  slotKey,
});
await broadcast.releaseSlot(slotKey);

for await (const update of broadcast.watchJob(image.id)) {
  if (update.status === "failed") throw new Error(update.error ?? "queue job failed");
}

// Pass this descriptor to the application player; do not send `token` to it.
const playback = await broadcast.getPlayback();

enqueueUpload is the preferred distributed ingestion path. A trusted backend sends each completed image, audio, or video asset over authenticated multipart HTTP to its separately running Queue Broadcast Server. The server owns the bytes after receipt, checks the supplied SHA-256, and plays eligible items in FIFO order. Standalone audio is valid for capability consumers; an application may choose to discard it by policy.

For a multi-asset generation turn or scheduled pre-roll, call stageUpload for each successful asset with the same slotKey, then call releaseSlot(slotKey). Release is idempotent and atomically makes all staged items FIFO-eligible, so a partially generated turn cannot air early. Adjacent image/audio items can then be composited by the Streamer; image-only and video items play independently. The QueueUploadAsset SHA-256 field lets the server verify bytes without an extra producer-side copy. Identical idempotency retries return the original job receipt; conflicting reuse maps to QueueBroadcastError status 409.

enqueuePairUpload and stagePair remain supported for legacy producers, but new applications should use individual uploads and slots.

enqueueUrl remains for backwards-compatible server-to-server URL ingestion. It is not the recommended path for applications that already have the finished media bytes.

Availability preflight

Run this immediately before costly generation, archive recovery, or a direct upload. health confirms that the Streamer's media service is ready; getPlayback is authenticated and therefore also detects a bad control URL or queue bearer token. Both accept an AbortSignal, so an operator stop or process shutdown can cancel a pending availability check promptly.

const controller = new AbortController();
const health = await broadcast.health({ signal: controller.signal });
if (!health.ok) throw new Error("Streamer media service is unavailable");

const playback = await broadcast.getPlayback({ signal: controller.signal });
// Generate or upload only after both probes have succeeded.

Do not treat a queue-capacity response from a later upload as a preflight failure: it means the service is reachable but has applied normal backpressure.

massively-social-ebook example

Its generation worker owns a per-channel STREAMER_ENDPOINT and secret. It generates image and narration independently in memory, computes SHA-256, archives canonical media independently, then stages the successful image and optional narration under one slot before releasing it. If narration fails, it releases the image alone; if the image fails, it discards lone narration. The chapter page obtains getPlayback() from its backend and gives the resulting HLS manifest to its existing HLS-capable player. The page never calls queue endpoints or receives the bearer token.

For chat from another broadcast platform, pass normalized provider events to ExternalChatIngress in @portalshq/capability-realtime-fanout with the same endpoint. That produces the chat:<normalized endpoint> topic; platform OAuth, webhooks, storage, and relays remain outside this capability.

Generated API

src/generated/api.ts is generated from the checked-in openapi/streamer.json contract snapshot using Orval:

npm run generate -w @portalshq/capability-queue-broadcast

Refresh openapi/streamer.json from the Streamer repository when updating the client, and commit the snapshot with the generated changes. This makes release tests independent of a sibling checkout. The current snapshot comes from Streamer commit 90b9102 (openapi.json). The hand-written QueueBroadcastClient owns authentication, error mapping, endpoint validation, and polling semantics.

RTMP frame delivery

RTMPStreamer accepts Base64-encoded JPEG frames (a data:image/jpeg;base64, prefix is also accepted) and pipes decoded JPEGs to FFmpeg. Set ffmpegPath when the executable is not available as ffmpeg on PATH. The streamer owns one RTMP destination, bounded buffering, process shutdown, and health status; the consuming application owns generation and starts a separate instance for each destination.

GenerationContext.textOverlay configures FFmpeg's drawtext filter. Pass the same configuration to RTMPStreamer and set audioDurationSeconds from the generated audio: the overlay is visible for the audio duration plus 1.5 seconds at both the head and tail. With no audio it remains visible for five seconds. The deployment FFmpeg build must include the drawtext filter (libfreetype); set fontFile when it has no usable default font.