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

@sdxc/rss

v2026.10.6

Published

RSS 2.0 feed builder and parser

Readme

@sdxc/rss

RSS 2.0 feed builder and parser.

It builds feeds for publishing and reads feeds published by other sites, covering the whole set of standard channel and item fields plus the namespaced extensions feeds lean on in practice: atom:link, content:encoded, dc:creator, and slash:comments. Anything else namespaced is preserved rather than discarded, so a feed carrying a custom module survives a read and a write unchanged.

Installation

npm add @sdxc/rss

RSS.fromXML takes a document from @sdxc/xml, which installs alongside this package.

Usage

Build A Feed

Describe the channel first, append items, then serialize.

import { RSS } from "@sdxc/rss";

let feed = new RSS({
	title: "My Blog",
	description: "Articles about web development",
	link: "https://example.com",
	language: "en-us",
	atomLink: {
		href: "https://example.com/feed.xml",
		rel: "self",
		type: "application/rss+xml",
	},
});

feed.addItem({
	guid: { value: "tag:example.com,2026:post-1", isPermaLink: false },
	title: "Hello World",
	description: "A short summary",
	link: "https://example.com/posts/hello-world",
	pubDate: new Date().toUTCString(),
	contentEncoded: "<p>Full post content</p>",
});

let xml = feed.toString();

The namespaces the extensions need are declared for you, so the example above writes xmlns:atom and xmlns:content onto the root element.

Read A Feed

import { RSS } from "@sdxc/rss";

let feed = RSS.parse(xml);

console.log(feed.channel.title);
for (let item of feed.items) console.log(item.title, item.link);

Parsing throws on a document that is not RSS, so wrap the call where a malformed feed is an expected outcome rather than a bug:

try {
	return RSS.parse(xml);
} catch (error) {
	return recordBadFeed(error);
}

Fetch A Feed

import { RSS } from "@sdxc/rss";

let feed = await RSS.fetch(new URL("https://example.com/feed.xml"));

RSS.fetch asks the origin not to serve a cached copy, requires the response to be ok and to carry an XML content type, and throws when either fails.

API

new RSS(channel: RSS.Channel)

Creates a feed with the given channel metadata and no items. title, description, and link are required.

rss.channel

The channel data, as a clone. Assigning a new RSS.Channel replaces it and leaves the items in place.

rss.channel = { ...rss.channel, language: "en-us" };

rss.items

The items, as clones, in the order they were added.

rss.addItem(item: RSS.Item)

Appends one item. RSS 2.0 requires at least a title or a description.

rss.removeItem(guid: string)

Removes the first item whose guid value matches.

rss.toJSON()

Returns { channel, items } as plain serializable data.

rss.toString()

Serializes the feed to RSS 2.0 XML.

RSS.parse(source: string): RSS

Parses RSS XML. Throws when the text is not XML, or when its root element is not rss.

RSS.fromXML(xml: XML): RSS

Reads a feed out of an already-parsed document, for a caller that parsed the text for some other purpose first.

RSS.fetch(input: URL | RequestInfo, init?: RequestInit): Promise<RSS>

Retrieves a document and parses it.

Supported Fields

Channel

Beyond the required title, description, and link: language, copyright, managingEditor, webMaster, pubDate, lastBuildDate, category, generator, docs, cloud, ttl, image, rating, textInput, skipHours, and skipDays.

Item

At least one of title or description, plus link, author, category, comments, enclosure, guid, pubDate, and source.

Namespaced Extensions

The four common modules, from the RSS Best Practices Profile, have named fields:

| Field | Element | | ---------------- | ----------------- | | atomLink | atom:link | | contentEncoded | content:encoded | | dcCreator | dc:creator | | slashComments | slash:comments |

Any other namespaced element round-trips through extensions. Declare its namespace on channel.namespaces when you write one, since a prefix with no declaration in scope cannot be serialized.

let feed = new RSS({
	title: "Example",
	description: "Example",
	link: "https://example.com",
	namespaces: { media: "http://search.yahoo.com/mrss/" },
	extensions: [
		{
			name: "media:rating",
			attributes: { scheme: "urn:simple" },
			children: ["adult"],
		},
	],
});

Notes

  1. Dates are the strings the document holds, so write them in RFC 822 style — Tue, 14 Apr 2026 09:00:00 GMT, which is what Date.prototype.toUTCString produces.
  2. description and contentEncoded are written as XML text, so embedded HTML is escaped on the way out and arrives unescaped on the way back in.
  3. guid, category, enclosure, and atomLink each accept a bare string for the common case and an object when attributes matter.
  4. Nothing is sanitized. An item's markup is whatever the publisher wrote, and escaping it is the responsibility of whatever renders it.

Versioning

Releases are dated rather than semantic. A version is the UTC date it was published, written YYYY.M.D, so 2026.9.4 is the release from 4 September 2026. At most one release goes out per day.

Those numbers say when, not what: a later date means a later release and carries no compatibility promise. Any release may change or remove an export.

Depend on one exact date, and move it when you are ready to take the change:

{
	"dependencies": {
		"@sdxc/rss": "2026.9.4"
	}
}

A caret or tilde range reads the date as major, minor and patch, so it accepts every later release in the same year. An exact version keeps the upgrade yours to schedule.

License

MIT

Author

Sergio Xalambrí