diff --git a/changelog.md b/changelog.md index 500fe82..66fc500 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,14 @@ # Changelog +## 2025-11-01 - 1.4.0 - feat(feed) +Support custom feedUrl for feeds and use it as the self-link in RSS/Atom/JSON; update docs + +- Add optional feedUrl option to IFeedOptions (ts/classes.feed.ts). +- Use feedUrl as the atom:link rel="self" href in RSS and as the in Atom when provided. +- Expose feedUrl as the JSON Feed "feed_url" value (ts/classes.feed.ts). +- PodcastFeed now uses podcastOptions.feedUrl for its atom self-link (ts/classes.podcast.ts). +- Update README: add a Custom Feed URL section and mention feedUrl in the API docs (readme.md). + ## 2025-10-31 - 1.3.0 - feat(parsing) Replace rss-parser with fast-xml-parser and add native feed parser; update Smartfeed to use parseFeedXML and adjust plugins/tests diff --git a/readme.md b/readme.md index 88a3294..6ab121e 100644 --- a/readme.md +++ b/readme.md @@ -7,6 +7,7 @@ ## Features ✨ - 🎯 **Full TypeScript Support** - Complete type definitions for all feed formats +- 🌐 **Cross-Platform** - Works in Node.js, Bun, Deno, and browsers - 📡 **Multiple Feed Formats** - RSS 2.0, Atom 1.0, JSON Feed 1.0, and Podcast RSS - 🎙️ **Modern Podcast Support** - iTunes tags, Podcast 2.0 namespace (guid, medium, locked, persons, transcripts, funding) - 🔒 **Built-in Validation** - Comprehensive validation for URLs, emails, domains, and timestamps @@ -57,6 +58,30 @@ const atom = feed.exportAtomFeed(); const json = feed.exportJsonFeed(); ``` +### Custom Feed URL + +You can specify a custom URL for your feed's self-reference instead of the default `https://${domain}/feed.xml`: + +```typescript +const feed = smartfeed.createFeed({ + domain: 'example.com', + title: 'My Blog', + description: 'Latest posts', + category: 'Technology', + company: 'Example Inc', + companyEmail: 'hello@example.com', + companyDomain: 'https://example.com', + feedUrl: 'https://cdn.example.com/feeds/main.xml' // Custom feed URL +}); + +// The feedUrl will be used in: +// - RSS: +// - Atom: +// - JSON Feed: "feed_url" field +``` + +This is particularly useful when your feed is hosted on a CDN or different domain than your main site. + ### Creating a Podcast Feed ```typescript @@ -183,6 +208,7 @@ Creates a standard feed (RSS/Atom/JSON). - `company` (string) - Company/organization name - `companyEmail` (string) - Contact email - `companyDomain` (string) - Company website URL (absolute) +- `feedUrl` (string, optional) - Custom URL for the feed's self-reference (defaults to `https://${domain}/feed.xml`) #### `createPodcastFeed(options: IPodcastFeedOptions): PodcastFeed` diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index e0f2e7b..d219c92 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@push.rocks/smartfeed', - version: '1.3.0', + version: '1.4.0', description: 'A library for creating and parsing various feed formats.' } diff --git a/ts/classes.feed.ts b/ts/classes.feed.ts index 819e01e..24f6755 100644 --- a/ts/classes.feed.ts +++ b/ts/classes.feed.ts @@ -19,6 +19,8 @@ export interface IFeedOptions { companyEmail: string; /** The company website URL (must be absolute) */ companyDomain: string; + /** Optional: Custom URL for the feed's atom:link rel="self" (defaults to https://${domain}/feed.xml) */ + feedUrl?: string; } /** @@ -186,7 +188,8 @@ export class Feed { rss += `${this.escapeXml(this.options.category)}\n`; // Atom self link - rss += `\n`; + const selfUrl = this.options.feedUrl || `https://${this.options.domain}/feed.xml`; + rss += `\n`; // Items for (const item of this.items) { @@ -221,7 +224,8 @@ export class Feed { atom += `${this.escapeXml(this.options.title)}\n`; atom += `${this.escapeXml(this.options.description)}\n`; atom += `\n`; - atom += `\n`; + const selfUrl = this.options.feedUrl || `https://${this.options.domain}/feed.xml`; + atom += `\n`; atom += `${this.formatIso8601Date(new Date())}\n`; atom += `@push.rocks/smartfeed\n`; atom += '\n'; @@ -265,7 +269,7 @@ export class Feed { version: 'https://jsonfeed.org/version/1', title: this.options.title, home_page_url: `https://${this.options.domain}`, - feed_url: `https://${this.options.domain}/feed.json`, + feed_url: this.options.feedUrl || `https://${this.options.domain}/feed.json`, description: this.options.description, icon: '', favicon: '', diff --git a/ts/classes.podcast.ts b/ts/classes.podcast.ts index 7cbefe7..e6de743 100644 --- a/ts/classes.podcast.ts +++ b/ts/classes.podcast.ts @@ -369,7 +369,8 @@ export class PodcastFeed extends Feed { rss += `${new Date().toUTCString()}\n`; // Atom self link - rss += `\n`; + const selfUrl = this.podcastOptions.feedUrl || `https://${this.podcastOptions.domain}/feed.xml`; + rss += `\n`; // iTunes channel tags rss += `${this.escapeXml(this.podcastOptions.itunesAuthor)}\n`;