---
title: "@playstack/feeds"
description: "Streaming RSS, Atom, JSON Feed, sitemap, and generate-to-storage primitives for Node and Workers."
tags: ["package","content","feeds","rss","sitemap","pro"]
---

{/* package-access:start */}

> **Pro.** Covered by the Playstack Pro License. Registry access is required; check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/feeds` streams RSS, Atom, JSON Feed, sitemaps, and generate-to-storage documents from application-supplied async iterables in Node and Workers.

{/* package-install:start */}

## Install

After confirming [preview access](/docs/packages#access-policy), install the package at your application's shared Playstack version:

```sh
npm install --save-exact @playstack/feeds@0.1.0-beta.1
```

Check the peer requirements below before choosing a runtime or provider.

{/* package-install:end */}

## Install and generate feeds

```ts
import { generateFeedDocuments } from '@playstack/feeds/feeds'

for await (const document of generateFeedDocuments(
  {
    id: 'https://example.com/',
    title: 'Example',
    description: 'Example posts',
    homePageUrl: 'https://example.com/',
    updatedAt: contentIndexUpdatedAt,
    urls: {
      rss: 'https://example.com/feed.rss.xml',
      atom: 'https://example.com/feed.atom.xml',
      json: 'https://example.com/feed.json',
    },
  },
  () => streamPublishedPosts(),
  { content: 'full', formats: ['rss', 'atom', 'json'] },
)) {
  await write(document)
}
```

The entry source is a factory returning `AsyncIterable<FeedEntry>` and is called once per requested format. Stable entry IDs remain RSS GUID, Atom ID, and JSON Feed ID even when URLs change. `full` and `excerpt` modes fail when their required body is absent.

## Feed properties

Definitions require stable ID, title, description, homepage URL, explicit updated time, and a URL per enabled format. Optional language, authors, icon, and favicon are shared. Entries require ID, URL, title, and published time, with optional updated time, full/excerpt HTML or text, authors, tags, and attachments.

`FeedExtension` adds structured RSS namespaces and channel/entry XML elements; raw XML injection is not accepted.

`FeedEntry.summary` is independent plain text: it becomes RSS description, Atom text summary, and JSON Feed summary even when full content is selected. The generator does not infer a summary by stripping HTML. The `rssContent` option defaults to `'both'` (description and encoded body); `'description'` omits the encoded body and `'encoded'` omits the description.

Entry `tags` serialize as native RSS categories as well as Atom/JSON categories or tags. `FeedDefinition.rights` supplies standard Atom text rights, and `iconUrl` supplies the Atom icon. Use these fields instead of extension overrides: extensions reject attempts to replace standard fields.

## Stream sitemaps

```ts
import { generateSitemapDocuments } from '@playstack/feeds/sitemaps'

const documents = generateSitemapDocuments(streamPublicUrls(), {
  baseUrl: 'https://example.com/',
  maxUrlsPerShard: 50_000,
  gzip: true,
})
```

`SitemapSetOptions` also configures index path, shard prefix, robots path/directives, and uncompressed-byte ceiling. Defaults enforce 50,000 URLs and 50 MiB per deterministic gzip shard, then emit an index and `robots.txt`. Entries support optional last-modified time, alternates, images, videos, and news metadata.

## Generate atomically to storage

```ts
import { R2FeedStorage, generateToStorage } from '@playstack/feeds/storage'

const storage = new R2FeedStorage(env.FEEDS_BUCKET, env.FEEDS_PUBLIC_URL)
await generateToStorage({
  storage,
  namespace: 'marketing',
  documents,
  clock,
  ids,
  cacheControl: 'public, max-age=300',
  onEvent: (event) => operations.record(event),
})
```

Objects write below a versioned generation key. `manifest.json` changes only after every object succeeds, so failed regeneration leaves the prior generation serving.

## Storage and queue bridges

| Contract | Members |
| --- | --- |
| `FeedStorage` | Native `client`, `put`, `get`, `delete`, and public `url`. |
| `FeedGenerationQueue` | Native `client` plus `dispatch(job, { jobId })` returning queued/duplicate. |
| `WebSubPublisher` | Native `client` plus `publish({ hubUrl, topicUrl })`. |

`serveGeneratedDocument` supports `auto`, `proxy`, or `redirect`; auto proxies up to 5 MiB and redirects larger objects. `FeedRegenerator` uses deterministic `feeds:<namespace>:<definitionId>` jobs to coalesce content-change, scheduled, and manual triggers.

## Boundary

The package produces public documents but does not parse feeds, syndicate content, query a filesystem, choose routes, or install scheduler/queue infrastructure. Storage completion events are observational and cannot fail generation; the committed manifest is the durable handoff point.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/feeds@0.1.0-beta.1`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/feeds` | `./dist/index.d.ts` |
| `@playstack/feeds/feeds` | `./dist/feeds.d.ts` |
| `@playstack/feeds/sitemaps` | `./dist/sitemaps.d.ts` |
| `@playstack/feeds/storage` | `./dist/storage.d.ts` |
| `@playstack/feeds/errors` | `./dist/errors.d.ts` |
| `@playstack/feeds/testing` | `./dist/testing.d.ts` |
| `@playstack/feeds/package.json` | No TypeScript declaration (asset or metadata export). |
| `@playstack/feeds/playstack.integration.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}
