diff --git a/README.md b/README.md
index 18ecf3da..00bd2d0a 100644
--- a/README.md
+++ b/README.md
@@ -18,13 +18,6 @@ Feedsmith offers universal and format‑specific parsers that maintain the origi
---
-> [!IMPORTANT]
-> **Feedsmith 3.x is in final stages of development.** Check out the [v3.x guide](https://v3.feedsmith.dev/migration/v2-to-v3) to explore new features and learn how to upgrade. Install it with:
->
-> `npm install feedsmith@next`
-
----
-
## Features
### Core
@@ -53,7 +46,7 @@ Feedsmith offers universal and format‑specific parsers that maintain the origi
## Supported Formats
-Feedsmith aims to fully support all major feed formats and namespaces in complete alignment with their specifications.
+Feedsmith aims to fully support all major feed formats and namespaces in complete alignment with their specs.
✅ Available
·
@@ -112,6 +105,7 @@ Feedsmith aims to fully support all major feed formats and namespaces in complet
| [W3C Basic Geo](https://feedsmith.dev/reference/namespaces/geo) | `` | RSS, Atom | ✅ | ✅ |
| [GeoRSS Simple](https://feedsmith.dev/reference/namespaces/georss) | `` | RSS, Atom, RDF | ✅ | ✅ |
| [RDF](https://feedsmith.dev/reference/namespaces/rdf) | `` | RDF | ✅ | ✅ |
+| [XML](https://feedsmith.dev/reference/namespaces/xml) | `` | RSS, Atom, RDF | ✅ | ✅ |
## Quick Start
@@ -231,7 +225,7 @@ try {
Feedsmith provides comprehensive TypeScript types for all feed formats:
```typescript
-import type { Rss, Atom, Json, Opml } from 'feedsmith/types'
+import type { Rss, Atom, Json, Opml } from 'feedsmith'
// Access all types for a format
type Feed = Rss.Feed
diff --git a/benchmarks/javascript/bun.lock b/benchmarks/javascript/bun.lock
index a7504fcd..c82cddf7 100644
--- a/benchmarks/javascript/bun.lock
+++ b/benchmarks/javascript/bun.lock
@@ -11,7 +11,7 @@
"benchmark": "^2.1.4",
"feedme": "^2.0.2",
"feedparser": "^2.3.1",
- "feedsmith": "2.9.3",
+ "feedsmith": "2.9.4",
"node-opml-parser": "^1.0.0",
"opml": "^0.5.8",
"opml-generator": "^1.1.1",
@@ -19,7 +19,7 @@
"opmlparser": "^0.8.0",
"podcast-feed-parser": "^1.0.4",
"rss-parser": "^3.13.0",
- "tinybench": "^6.0.0",
+ "tinybench": "^6.0.1",
},
"devDependencies": {
"@types/benchmark": "^2.1.5",
@@ -122,7 +122,7 @@
"feedparser": ["feedparser@2.3.1", "", { "dependencies": { "addressparser": "^1.0.1", "array-indexofobject": "~0.0.1", "lodash.assign": "^4.2.0", "lodash.get": "^4.4.2", "lodash.has": "^4.5.2", "lodash.uniq": "^4.5.0", "mri": "^1.1.5", "readable-stream": "^2.3.7", "sax": ">=1.2.4 <1.4.4" }, "bin": { "feedparser": "bin/feedparser.js" } }, "sha512-YTeYlA9+7UVj1tHMs4L27JypeghmA3wjE3ZUWuv/N9L3vDxuYe+5fOM14MxkCAyNzMtCHaQ+1KoQAHJfJoy0Vw=="],
- "feedsmith": ["feedsmith@2.9.3", "", { "dependencies": { "entities": "^7.0.1", "fast-xml-parser": "~5.7.1" } }, "sha512-H2Dj/gax2p61HszgUdhORg4Wtpfz9wu6w6fhloEWovcx2xF9+QzzNJvHisn4Vr2yoozjQpH75pq2OLf9/JY8Gg=="],
+ "feedsmith": ["feedsmith@2.9.4", "", { "dependencies": { "entities": "^7.0.1", "fast-xml-parser": "~5.7.2" } }, "sha512-mjBxjSQ52mZQQZmSwSwB4e+Ig6KHadjUG9rwNOb6t6s6F1mO/qFrijBi5TKjtjExfi6/eJ6v30TsaPgcYlQeNA=="],
"follow-redirects": ["follow-redirects@1.15.9", "", {}, "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="],
@@ -246,7 +246,7 @@
"strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="],
- "tinybench": ["tinybench@6.0.0", "", {}, "sha512-BWlWpVbbZXaYjRV0twGLNQO00Zj4HA/sjLOQP2IvzQqGwRGp+2kh7UU3ijyJ3ywFRogYDRbiHDMrUOfaMnN56g=="],
+ "tinybench": ["tinybench@6.0.1", "", {}, "sha512-cMdWsxmysdg8mNWf1pujiWl3TW0cU6m8QuNw55QlnP3I6N96Grb0wnu5N0syHIu3LbiVZCNqlfWzWDq84HZphA=="],
"tough-cookie": ["tough-cookie@2.5.0", "", { "dependencies": { "psl": "^1.1.28", "punycode": "^2.1.1" } }, "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g=="],
@@ -286,7 +286,7 @@
"@ulisesgascon/rss-feed-parser/fast-xml-parser": ["fast-xml-parser@4.5.3", "", { "dependencies": { "strnum": "^1.1.1" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig=="],
- "feedsmith/fast-xml-parser": ["fast-xml-parser@5.7.1", "", { "dependencies": { "@nodable/entities": "^2.1.0", "fast-xml-builder": "^1.1.5", "path-expression-matcher": "^1.5.0", "strnum": "^2.2.3" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-8Cc3f8GUGUULg34pBch/KGyPLglS+OFs05deyOlY7fL2MTagYPKrVQNmR1fLF/yJ9PH5ZSTd3YDF6pnmeZU+zA=="],
+ "feedsmith/fast-xml-parser": ["fast-xml-parser@5.7.2", "", { "dependencies": { "@nodable/entities": "^2.1.0", "fast-xml-builder": "^1.1.5", "path-expression-matcher": "^1.5.0", "strnum": "^2.2.3" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-P7oW7tLbYnhOLQk/Gv7cZgzgMPP/XN03K02/Jy6Y/NHzyIAIpxuZIM/YqAkfiXFPxA2CTm7NtCijK9EDu09u2w=="],
"node-opml-parser/sax": ["sax@1.1.5", "", {}, "sha512-z19WXQiOz8RBu3zDpOE9541RgB7Q5NecZ7SAgU3yUvqhMNvG4hTChbrutzpDyDSHmeHouR5Rqk4eup1o6C6dxQ=="],
diff --git a/benchmarks/javascript/package.json b/benchmarks/javascript/package.json
index 7dec5417..dc97b43c 100644
--- a/benchmarks/javascript/package.json
+++ b/benchmarks/javascript/package.json
@@ -9,7 +9,7 @@
"benchmark": "^2.1.4",
"feedme": "^2.0.2",
"feedparser": "^2.3.1",
- "feedsmith": "2.9.3",
+ "feedsmith": "2.9.4",
"node-opml-parser": "^1.0.0",
"opml": "^0.5.8",
"opml-generator": "^1.1.1",
@@ -17,7 +17,7 @@
"opmlparser": "^0.8.0",
"podcast-feed-parser": "^1.0.4",
"rss-parser": "^3.13.0",
- "tinybench": "^6.0.0"
+ "tinybench": "^6.0.1"
},
"devDependencies": {
"@types/benchmark": "^2.1.5",
diff --git a/compatibility/bundler/esm/index.ts b/compatibility/bundler/esm/index.ts
index c600c46b..ed0f94dc 100644
--- a/compatibility/bundler/esm/index.ts
+++ b/compatibility/bundler/esm/index.ts
@@ -1,6 +1,5 @@
-import type { RssFeed } from 'feedsmith'
+import type { Rss } from 'feedsmith'
import { generateRssFeed, parseFeed } from 'feedsmith'
-import type { Rss } from 'feedsmith/types'
const rssXml = `
@@ -20,11 +19,4 @@ const feedData: Rss.Feed = {
items: [],
}
-const _legacyFeedData: RssFeed = {
- title: 'Legacy Type',
- link: 'https://example.com',
- description: 'Legacy description',
- items: [],
-}
-
const _generatedRss = generateRssFeed(feedData)
diff --git a/compatibility/explicit-modules/cjs-package/index.cts b/compatibility/explicit-modules/cjs-package/index.cts
index c600c46b..ed0f94dc 100644
--- a/compatibility/explicit-modules/cjs-package/index.cts
+++ b/compatibility/explicit-modules/cjs-package/index.cts
@@ -1,6 +1,5 @@
-import type { RssFeed } from 'feedsmith'
+import type { Rss } from 'feedsmith'
import { generateRssFeed, parseFeed } from 'feedsmith'
-import type { Rss } from 'feedsmith/types'
const rssXml = `
@@ -20,11 +19,4 @@ const feedData: Rss.Feed = {
items: [],
}
-const _legacyFeedData: RssFeed = {
- title: 'Legacy Type',
- link: 'https://example.com',
- description: 'Legacy description',
- items: [],
-}
-
const _generatedRss = generateRssFeed(feedData)
diff --git a/compatibility/explicit-modules/cjs-package/index.mts b/compatibility/explicit-modules/cjs-package/index.mts
index c600c46b..ed0f94dc 100644
--- a/compatibility/explicit-modules/cjs-package/index.mts
+++ b/compatibility/explicit-modules/cjs-package/index.mts
@@ -1,6 +1,5 @@
-import type { RssFeed } from 'feedsmith'
+import type { Rss } from 'feedsmith'
import { generateRssFeed, parseFeed } from 'feedsmith'
-import type { Rss } from 'feedsmith/types'
const rssXml = `
@@ -20,11 +19,4 @@ const feedData: Rss.Feed = {
items: [],
}
-const _legacyFeedData: RssFeed = {
- title: 'Legacy Type',
- link: 'https://example.com',
- description: 'Legacy description',
- items: [],
-}
-
const _generatedRss = generateRssFeed(feedData)
diff --git a/compatibility/explicit-modules/esm-package/index.cts b/compatibility/explicit-modules/esm-package/index.cts
index c600c46b..ed0f94dc 100644
--- a/compatibility/explicit-modules/esm-package/index.cts
+++ b/compatibility/explicit-modules/esm-package/index.cts
@@ -1,6 +1,5 @@
-import type { RssFeed } from 'feedsmith'
+import type { Rss } from 'feedsmith'
import { generateRssFeed, parseFeed } from 'feedsmith'
-import type { Rss } from 'feedsmith/types'
const rssXml = `
@@ -20,11 +19,4 @@ const feedData: Rss.Feed = {
items: [],
}
-const _legacyFeedData: RssFeed = {
- title: 'Legacy Type',
- link: 'https://example.com',
- description: 'Legacy description',
- items: [],
-}
-
const _generatedRss = generateRssFeed(feedData)
diff --git a/compatibility/explicit-modules/esm-package/index.mts b/compatibility/explicit-modules/esm-package/index.mts
index c600c46b..ed0f94dc 100644
--- a/compatibility/explicit-modules/esm-package/index.mts
+++ b/compatibility/explicit-modules/esm-package/index.mts
@@ -1,6 +1,5 @@
-import type { RssFeed } from 'feedsmith'
+import type { Rss } from 'feedsmith'
import { generateRssFeed, parseFeed } from 'feedsmith'
-import type { Rss } from 'feedsmith/types'
const rssXml = `
@@ -20,11 +19,4 @@ const feedData: Rss.Feed = {
items: [],
}
-const _legacyFeedData: RssFeed = {
- title: 'Legacy Type',
- link: 'https://example.com',
- description: 'Legacy description',
- items: [],
-}
-
const _generatedRss = generateRssFeed(feedData)
diff --git a/compatibility/explicit-modules/mixed-package/index.cts b/compatibility/explicit-modules/mixed-package/index.cts
index c600c46b..ed0f94dc 100644
--- a/compatibility/explicit-modules/mixed-package/index.cts
+++ b/compatibility/explicit-modules/mixed-package/index.cts
@@ -1,6 +1,5 @@
-import type { RssFeed } from 'feedsmith'
+import type { Rss } from 'feedsmith'
import { generateRssFeed, parseFeed } from 'feedsmith'
-import type { Rss } from 'feedsmith/types'
const rssXml = `
@@ -20,11 +19,4 @@ const feedData: Rss.Feed = {
items: [],
}
-const _legacyFeedData: RssFeed = {
- title: 'Legacy Type',
- link: 'https://example.com',
- description: 'Legacy description',
- items: [],
-}
-
const _generatedRss = generateRssFeed(feedData)
diff --git a/compatibility/explicit-modules/mixed-package/index.mts b/compatibility/explicit-modules/mixed-package/index.mts
index c600c46b..ed0f94dc 100644
--- a/compatibility/explicit-modules/mixed-package/index.mts
+++ b/compatibility/explicit-modules/mixed-package/index.mts
@@ -1,6 +1,5 @@
-import type { RssFeed } from 'feedsmith'
+import type { Rss } from 'feedsmith'
import { generateRssFeed, parseFeed } from 'feedsmith'
-import type { Rss } from 'feedsmith/types'
const rssXml = `
@@ -20,11 +19,4 @@ const feedData: Rss.Feed = {
items: [],
}
-const _legacyFeedData: RssFeed = {
- title: 'Legacy Type',
- link: 'https://example.com',
- description: 'Legacy description',
- items: [],
-}
-
const _generatedRss = generateRssFeed(feedData)
diff --git a/compatibility/explicit-modules/mixed-package/index.ts b/compatibility/explicit-modules/mixed-package/index.ts
index c600c46b..ed0f94dc 100644
--- a/compatibility/explicit-modules/mixed-package/index.ts
+++ b/compatibility/explicit-modules/mixed-package/index.ts
@@ -1,6 +1,5 @@
-import type { RssFeed } from 'feedsmith'
+import type { Rss } from 'feedsmith'
import { generateRssFeed, parseFeed } from 'feedsmith'
-import type { Rss } from 'feedsmith/types'
const rssXml = `
@@ -20,11 +19,4 @@ const feedData: Rss.Feed = {
items: [],
}
-const _legacyFeedData: RssFeed = {
- title: 'Legacy Type',
- link: 'https://example.com',
- description: 'Legacy description',
- items: [],
-}
-
const _generatedRss = generateRssFeed(feedData)
diff --git a/compatibility/typescript/legacy-cjs/index.ts b/compatibility/typescript/legacy-cjs/index.ts
index d092e738..12836654 100644
--- a/compatibility/typescript/legacy-cjs/index.ts
+++ b/compatibility/typescript/legacy-cjs/index.ts
@@ -1,8 +1,7 @@
// biome-ignore lint/style/noCommonJs: This file tests CJS compatibility.
const { generateRssFeed, parseFeed } = require('feedsmith')
-import type { RssFeed } from 'feedsmith'
-import type { Rss } from 'feedsmith/types'
+import type { Rss } from 'feedsmith'
const rssXml = `
@@ -22,11 +21,4 @@ const feedData: Rss.Feed = {
items: [],
}
-const _legacyFeedData: RssFeed = {
- title: 'Legacy Type',
- link: 'https://example.com',
- description: 'Legacy description',
- items: [],
-}
-
const _generatedRss = generateRssFeed(feedData)
diff --git a/compatibility/typescript/modern-cjs/index.ts b/compatibility/typescript/modern-cjs/index.ts
index c600c46b..ed0f94dc 100644
--- a/compatibility/typescript/modern-cjs/index.ts
+++ b/compatibility/typescript/modern-cjs/index.ts
@@ -1,6 +1,5 @@
-import type { RssFeed } from 'feedsmith'
+import type { Rss } from 'feedsmith'
import { generateRssFeed, parseFeed } from 'feedsmith'
-import type { Rss } from 'feedsmith/types'
const rssXml = `
@@ -20,11 +19,4 @@ const feedData: Rss.Feed = {
items: [],
}
-const _legacyFeedData: RssFeed = {
- title: 'Legacy Type',
- link: 'https://example.com',
- description: 'Legacy description',
- items: [],
-}
-
const _generatedRss = generateRssFeed(feedData)
diff --git a/compatibility/typescript/modern-esm/index.ts b/compatibility/typescript/modern-esm/index.ts
index c600c46b..ed0f94dc 100644
--- a/compatibility/typescript/modern-esm/index.ts
+++ b/compatibility/typescript/modern-esm/index.ts
@@ -1,6 +1,5 @@
-import type { RssFeed } from 'feedsmith'
+import type { Rss } from 'feedsmith'
import { generateRssFeed, parseFeed } from 'feedsmith'
-import type { Rss } from 'feedsmith/types'
const rssXml = `
@@ -20,11 +19,4 @@ const feedData: Rss.Feed = {
items: [],
}
-const _legacyFeedData: RssFeed = {
- title: 'Legacy Type',
- link: 'https://example.com',
- description: 'Legacy description',
- items: [],
-}
-
const _generatedRss = generateRssFeed(feedData)
diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts
index 8b7aeba7..730c8eae 100644
--- a/docs/.vitepress/config.ts
+++ b/docs/.vitepress/config.ts
@@ -55,8 +55,8 @@ export default defineConfig({
{ text: 'Parsing', link: '/parsing' },
{ text: 'Generating', link: '/generating' },
{
- text: 'v2.x',
- items: [{ text: 'v3.x (Next)', link: 'https://v3.feedsmith.dev' }],
+ text: 'v3.0 (Next)',
+ items: [{ text: 'v2.0', link: 'https://feedsmith.dev' }],
},
],
sidebar: [
@@ -75,6 +75,7 @@ export default defineConfig({
{ text: 'Namespaces', link: '/parsing/namespaces' },
{ text: 'Dates', link: '/parsing/dates' },
{ text: 'Detecting', link: '/parsing/detecting' },
+ { text: 'Errors', link: '/parsing/errors' },
{ text: 'Examples', link: '/parsing/examples' },
],
},
@@ -83,7 +84,8 @@ export default defineConfig({
items: [
{ text: 'Overview', link: '/generating' },
{ text: 'Styling', link: '/generating/styling' },
- { text: 'Lenient Mode', link: '/generating/lenient-mode' },
+ { text: 'Strict Mode', link: '/generating/strict-mode' },
+ { text: 'Errors', link: '/generating/errors' },
{ text: 'Examples', link: '/generating/examples' },
],
},
@@ -140,6 +142,7 @@ export default defineConfig({
{ text: 'W3C Basic Geo', link: '/reference/namespaces/geo' },
{ text: 'GeoRSS Simple', link: '/reference/namespaces/georss' },
{ text: 'RDF', link: '/reference/namespaces/rdf' },
+ { text: 'XML', link: '/reference/namespaces/xml' },
],
},
{
@@ -154,7 +157,10 @@ export default defineConfig({
},
{
text: 'Migration',
- items: [{ text: 'From 1.x to 2.x', link: '/migration/v1-to-v2' }],
+ items: [
+ { text: 'From 2.x to 3.x', link: '/migration/v2-to-v3' },
+ { text: 'From 1.x to 2.x', link: '/migration/v1-to-v2' },
+ ],
},
],
search: {
diff --git a/docs/generating.md b/docs/generating.md
index 89b47843..2ce9dff6 100644
--- a/docs/generating.md
+++ b/docs/generating.md
@@ -4,11 +4,7 @@ title: Generating Feeds
# Generating Feeds
-Create RSS, Atom, JSON Feed, and OPML files with full namespace support.
-
-## Overview
-
-Feed generation is straightforward - provide the feed data and get back a properly formatted string:
+Create RSS, Atom, JSON Feed, and OPML files with full namespace support. Just provide the feed data and get back a properly formatted string:
```typescript
import {
diff --git a/docs/generating/errors.md b/docs/generating/errors.md
new file mode 100644
index 00000000..b7326495
--- /dev/null
+++ b/docs/generating/errors.md
@@ -0,0 +1,37 @@
+# Error Handling
+
+Feedsmith provides a dedicated error type for generation failures.
+
+## GenerateError
+
+Thrown when feed generation fails due to invalid input. Applies to all generators: `generateRssFeed`, `generateAtomFeed`, `generateJsonFeed`, and `generateOpml`.
+
+```typescript
+import { generateRssFeed, GenerateError } from 'feedsmith'
+
+try {
+ generateRssFeed({})
+} catch (error) {
+ if (error instanceof GenerateError) {
+ console.log(error.message) // "Invalid input RSS"
+ }
+}
+```
+
+## Error Hierarchy
+
+All error classes extend the built-in `Error`, so `instanceof Error` checks work as expected. This can be useful for catching any generation error alongside other errors in a single handler:
+
+```typescript
+import { generateJsonFeed, GenerateError } from 'feedsmith'
+
+try {
+ generateJsonFeed(data)
+} catch (error) {
+ if (error instanceof GenerateError) {
+ // Feedsmith-specific generation failure.
+ } else if (error instanceof Error) {
+ // Any other error.
+ }
+}
+```
diff --git a/docs/generating/examples.md b/docs/generating/examples.md
index 18c15519..0f48c478 100644
--- a/docs/generating/examples.md
+++ b/docs/generating/examples.md
@@ -32,7 +32,7 @@ const rssFeed = generateRssFeed({
description: 'Learn the basics of TypeScript and why you should use it',
pubDate: new Date('2024-01-15T10:00:00Z'),
guid: 'https://myblog.com/posts/intro-to-typescript',
- authors: ['john@myblog.com (John Doe)'],
+ authors: [{ email: 'john@myblog.com', name: 'John Doe' }],
categories: [{ name: 'TypeScript' }, { name: 'Programming' }]
}
]
@@ -97,11 +97,10 @@ Generates (showing first lines):
Build type-safe RSS feeds using the exported types:
```typescript
-import type { Rss } from 'feedsmith/types'
-import { generateRssFeed } from 'feedsmith'
+import { type RssFeed, generateRssFeed } from 'feedsmith'
// Define items with full type safety
-const items: Array> = [{
+const items: Array> = [{
title: 'New Episode',
description: 'Episode description',
enclosures: [{
@@ -112,7 +111,7 @@ const items: Array> = [{
}]
// Build the feed
-const feed: Rss.Feed = {
+const feed: RssFeed.Feed = {
title: 'My Podcast',
link: 'https://example.com',
description: 'A podcast',
@@ -131,7 +130,7 @@ import { generateAtomFeed } from 'feedsmith'
const atomFeed = generateAtomFeed({
id: 'https://myblog.com/feed',
- title: 'My Tech Blog',
+ title: { value: 'My Tech Blog' },
updated: new Date('2024-01-15T12:00:00Z'),
links: [
{ href: 'https://myblog.com/feed.xml', rel: 'self' },
@@ -140,9 +139,9 @@ const atomFeed = generateAtomFeed({
entries: [
{
id: 'https://myblog.com/posts/1',
- title: 'Introduction to TypeScript',
+ title: { value: 'Introduction to TypeScript' },
updated: new Date('2024-01-15T10:00:00Z'),
- content: '
Learn the basics of TypeScript and why you should use it
',
+ content: { value: '
Learn the basics of TypeScript and why you should use it
', type: 'html' },
links: [{ href: 'https://myblog.com/posts/intro-to-typescript' }],
categories: [{ term: 'typescript', label: 'TypeScript' }]
}
@@ -165,7 +164,9 @@ Generates:
Introduction to TypeScript2024-01-15T10:00:00.000Z
- Learn the basics of TypeScript and why you should use it
+
+ Learn the basics of TypeScript and why you should use it
]]>
+
diff --git a/docs/generating/lenient-mode.md b/docs/generating/lenient-mode.md
deleted file mode 100644
index d537199b..00000000
--- a/docs/generating/lenient-mode.md
+++ /dev/null
@@ -1,107 +0,0 @@
----
-title: "Generating Feeds: Lenient Mode"
----
-
-# Lenient Mode
-
-By default, the generate functions enforce all fields that are required by the feed specifications and expect `Date` objects for date fields. However, when working with parsed feeds or building feeds incrementally, you may need more flexibility. This is where **lenient mode** comes in.
-
-## What is Lenient Mode?
-
-Lenient mode allows you to:
-- Generate feeds **without spec-required fields** (all fields become optional)
-- Use **string dates** instead of Date objects
-- Pass through **invalid date strings** as-is
-
-This is particularly useful when working with the _parse → modify → generate_ workflow, as the parse functions return objects where all fields are optional and dates are strings.
-
-## Basic Usage
-
-Add `{ lenient: true }` as the second parameter to any generate function:
-
-```typescript
-import { generateRssFeed, parseRssFeed } from 'feedsmith'
-
-// Parse returns an object with all optional fields and string dates
-const parsedFeed = parseRssFeed(xmlString)
-
-// Generate with lenient mode accepts the parsed output directly
-const regeneratedXml = generateRssFeed(parsedFeed, { lenient: true })
-```
-
-## Strict vs Lenient Mode
-
-### Strict Mode (default)
-
-```typescript
-// Requires spec-mandated fields with Date objects for dates
-const feed = {
- title: 'My Blog', // Required by RSS spec
- link: 'https://example.com', // Required by RSS spec
- description: 'A blog about things', // Required by RSS spec
- pubDate: new Date('2024-01-01'), // Optional, but must be Date if provided
- items: [
- {
- title: 'Post 1', // At least title or description required
- link: 'https://example.com/post1',
- description: 'First post',
- pubDate: new Date('2024-01-02') // Optional, but must be Date if provided
- }
- ]
-}
-
-const xml = generateRssFeed(feed)
-```
-
-### Lenient Mode
-
-```typescript
-// All fields become optional, accepts string dates
-const partialFeed = {
- title: 'My Blog',
- // link and description not required in lenient mode
- pubDate: '2024-01-01T00:00:00Z', // String date accepted
- items: [
- {
- title: 'Post 1',
- // No other fields required
- pubDate: 'Mon, 01 Jan 2024 12:00:00 GMT' // RFC822 string accepted
- }
- ]
-}
-
-const xml = generateRssFeed(partialFeed, { lenient: true })
-```
-
-## Invalid Date Handling
-
-In lenient mode, invalid date strings are preserved as-is instead of stripping them from the output:
-
-```typescript
-const feedWithInvalidDates = {
- title: 'Feed with Custom Dates',
- pubDate: 'Yesterday at 3pm', // Invalid but preserved
- items: [
- {
- title: 'Post',
- pubDate: 'Coming soon' // Invalid but preserved
- }
- ]
-}
-
-const xml = generateRssFeed(feedWithInvalidDates, { lenient: true })
-// Output will contain: Yesterday at 3pm
-```
-
-## When to Use Lenient Mode
-
-### Use lenient mode
-- Processing feeds from external sources (_parse → modify → generate_ workflow)
-- Building feeds incrementally where not all data is available initially
-- Working with legacy feeds that don't strictly follow specifications
-- Migrating or transforming feeds between different systems
-
-### Use strict mode (default)
-- Creating new feeds from scratch with complete data
-- You want TypeScript to enforce all spec-required fields
-- Date formatting consistency is critical
diff --git a/docs/generating/strict-mode.md b/docs/generating/strict-mode.md
new file mode 100644
index 00000000..3483aa41
--- /dev/null
+++ b/docs/generating/strict-mode.md
@@ -0,0 +1,120 @@
+---
+title: "Generating Feeds: Strict Mode"
+---
+
+# Strict Mode
+
+By default, Feedsmith is lenient — all fields are optional to accommodate real-world feeds that may not follow specifications exactly. When strict mode is enabled, TypeScript will enforce fields that are required by the specification. This validation happens at compile time only, not at runtime.
+
+```typescript
+import { generateRssFeed } from 'feedsmith'
+
+// Lenient mode (default) - compiles fine even with missing required fields
+const rss = generateRssFeed({ title: 'My Feed' })
+
+// Strict mode - TypeScript error if required fields are missing
+const rss = generateRssFeed(
+ {
+ title: 'My Feed',
+ link: 'https://example.com',
+ description: 'A complete feed'
+ },
+ { strict: true }
+)
+```
+
+## Enabling Strict Mode
+
+Pass `strict: true` in the options to enable compile-time validation:
+
+```typescript
+import {
+ generateRssFeed,
+ generateAtomFeed,
+ generateJsonFeed,
+ generateOpml
+} from 'feedsmith'
+
+// RSS with strict mode
+const rss = generateRssFeed(feedData, { strict: true })
+
+// Atom with strict mode
+const atom = generateAtomFeed(feedData, { strict: true })
+
+// JSON Feed with strict mode
+const json = generateJsonFeed(feedData, { strict: true })
+
+// OPML with strict mode
+const opml = generateOpml(opmlData, { strict: true })
+```
+
+## Date Objects Required
+
+In strict mode, date fields must be JavaScript `Date` objects, not strings:
+
+```typescript
+// Lenient mode - strings work fine
+generateRssFeed({
+ title: 'Feed',
+ link: 'https://example.com',
+ description: 'Description',
+ pubDate: '2024-01-01T00:00:00Z' // String dates allowed
+})
+
+// Strict mode - must use Date objects
+generateRssFeed(
+ {
+ title: 'Feed',
+ link: 'https://example.com',
+ description: 'Description',
+ pubDate: new Date('2024-01-01') // Date object required
+ },
+ { strict: true }
+)
+```
+
+## Required Fields
+
+See the reference documentation for required fields in strict mode:
+
+- [RSS Reference](/reference/feeds/rss#type-definitions)
+- [Atom Reference](/reference/feeds/atom#type-definitions)
+- [JSON Feed Reference](/reference/feeds/json-feed#type-definitions)
+- [OPML Reference](/reference/opml#type-definitions)
+
+Some namespaces also have required fields for their nested types. Look for `Requirable<...>` in type definitions to identify fields that become required in strict mode.
+
+## Combining with Other Options
+
+Strict mode can be combined with other generation options:
+
+```typescript
+generateRssFeed(
+ {
+ title: 'My Feed',
+ link: 'https://example.com',
+ description: 'A complete feed',
+ items: []
+ },
+ {
+ strict: true,
+ stylesheets: [{ type: 'text/xsl', href: '/feed.xsl' }]
+ }
+)
+```
+
+## When to Use Strict Mode
+
+**Use strict mode when:**
+- Building new feeds from scratch where you control all data
+- You want TypeScript to catch missing required fields
+- Following specifications precisely matters for your use case
+
+**Use lenient mode (default) when:**
+- Processing feeds from external sources
+- Migrating existing feeds that may be incomplete
+
+## Related
+
+- **[Generating Feeds](/generating)** - Overview of feed generation
+- **[TypeScript Guide](/reference/typescript)** - Working with Feedsmith types
diff --git a/docs/generating/styling.md b/docs/generating/styling.md
index 0a86022b..948d8789 100644
--- a/docs/generating/styling.md
+++ b/docs/generating/styling.md
@@ -2,12 +2,10 @@
title: "Generating Feeds: Styling"
---
-# Styling
+# Styling Feeds
XML-based feeds (RSS, Atom) and OPML files support stylesheets to provide custom styling and transformations in browsers and feed readers.
-## Overview
-
Stylesheets allow you to:
- **Transform feed appearance** in browsers and feed readers
diff --git a/docs/index.md b/docs/index.md
index ae4db6a0..296e741c 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -8,11 +8,6 @@ Fast, all‑in‑one JavaScript feed parser and generator for RSS, Atom, RDF, an
Feedsmith offers universal and format‑specific parsers that maintain the original feed structure in a clean, object-oriented format while intelligently normalizing legacy elements. Access all feed data without compromising simplicity.
-> [!IMPORTANT]
-> **Feedsmith 3.x is in final stages of development.** Check out the [v3.x guide](https://v3.feedsmith.dev/migration/v2-to-v3) to explore new features and learn how to upgrade. Install it with:
->
-> `npm install feedsmith@next`
-
## Features
### Core
diff --git a/docs/migration/v2-to-v3.md b/docs/migration/v2-to-v3.md
new file mode 100644
index 00000000..345335e7
--- /dev/null
+++ b/docs/migration/v2-to-v3.md
@@ -0,0 +1,559 @@
+---
+title: Migrating from 2.x to 3.x
+---
+
+# Migrating from 2.x to 3.x
+
+This guide covers all breaking changes when upgrading from Feedsmith 2.x to 3.x. Each breaking change is detailed with specific upgrade steps and examples.
+
+> [!IMPORTANT]
+> Version 3.x inverts the default behavior: feeds are now lenient by default (all fields optional), with strict mode available as an opt-in via `{ strict: true }`.
+
+## Installation
+
+Update your package to the latest 3.x version:
+
+```bash
+npm install feedsmith@latest
+```
+
+## Migration Checklist
+
+Use this checklist to ensure a complete migration:
+
+- Remove `{ lenient: true }` from all generate function calls
+- Add `{ strict: true }` where you need compile-time validation of required fields
+- Update type parameters if using strict types directly (add `true` as last parameter)
+- Remove `DeepPartial` from imports
+- Change `feedsmith/types` imports to `feedsmith`
+- Update Atom text fields (`title`, `subtitle`, `rights`, `summary`): read `.value` instead of plain string, generate with `{ value: '...' }` instead of plain string
+- Update Atom `entry.content` usage: read `content?.value` instead of `content`, generate with `{ value: '...' }` instead of plain string
+- Update RSS person fields (`managingEditor`, `webMaster`, `authors`): read `.email`/`.name` instead of plain string, generate with `{ email: '...', name: '...' }` instead of plain string
+- Replace Media namespace deprecated field (`group` → `groups`)
+- Replace Podcast namespace deprecated fields (`location` → `locations`, `value` → `values`, `chats` → `chat`)
+- Replace Dublin Core singular fields with plural arrays (e.g., `title` → `titles`)
+- Replace Dublin Core Terms singular fields with plural arrays (e.g., `title` → `titles`)
+- Rename type imports: `Rss` → `RssFeed`, `Atom` → `AtomFeed`, `Json` → `JsonFeed`, `Rdf` → `RdfFeed`
+- Test feed generation to ensure output is correct
+- Update error handling to use `DetectError`, `MalformedError`, `ParseError`, and `GenerateError` instead of generic `Error`
+
+## Breaking Changes
+
+### Strict Mode Now Opt-In
+
+In version 2.x, generate functions enforced spec-required fields by default and to make all fields optional, it required passing `{ lenient: true }`. In 3.x, this is inverted: all fields are optional by default and `{ strict: true }` enables compile-time validation of spec-required fields.
+
+#### Before (2.x)
+```typescript
+import { generateRssFeed } from 'feedsmith'
+
+// Strict mode (default) - required fields and Date objects
+const xml = generateRssFeed({
+ title: 'My Blog',
+ description: 'A blog about things',
+ pubDate: new Date('2024-01-01'),
+})
+
+// Lenient mode - all optional, string dates accepted
+const xml = generateRssFeed({
+ title: 'My Blog',
+ pubDate: '2024-01-01T00:00:00Z',
+}, { lenient: true })
+```
+
+#### After (3.x)
+```typescript
+import { generateRssFeed } from 'feedsmith'
+
+// Lenient mode (default) - all optional, string dates accepted
+const xml = generateRssFeed({
+ title: 'My Blog',
+ pubDate: '2024-01-01T00:00:00Z',
+})
+
+// Strict mode - required fields and Date objects
+const xml = generateRssFeed({
+ title: 'My Blog',
+ description: 'A blog about things',
+ pubDate: new Date('2024-01-01'),
+}, { strict: true })
+```
+
+#### Migration Steps
+1. Remove `{ lenient: true }` from all generate function calls (it's now the default)
+2. Add `{ strict: true }` if you want to preserve v2's default strict behavior
+
+### All Type Fields Now Optional by Default
+
+Related to the above, previously required fields in type definitions are now optional by default. Pass `true` as the strict type parameter if you need compile-time enforcement.
+
+#### Before (2.x)
+```typescript
+import type { Atom } from 'feedsmith/types'
+
+// TypeScript enforced required fields
+const entry: Atom.Entry = {
+ id: 'https://example.com/post/1',
+ title: 'Post Title',
+ updated: new Date('2024-01-01'),
+}
+```
+
+#### After (3.x)
+```typescript
+import type { Atom } from 'feedsmith'
+
+// All fields optional by default
+const entry: Atom.Entry = {
+ title: 'Post Title',
+}
+
+// Pass `true` for compile-time enforcement
+const strictEntry: Atom.Entry = {
+ id: 'https://example.com/post/1',
+ title: 'Post Title',
+ updated: new Date('2024-01-01'),
+}
+```
+
+#### Migration Steps
+1. If you relied on TypeScript to enforce required fields, add `true` as the last type parameter
+2. Alternatively, add runtime validation for required fields
+
+### `DeepPartial` Type Removed
+
+The `DeepPartial` utility type has been removed. Since all type fields are now optional by default, this type is no longer needed.
+
+#### Before (2.x)
+```typescript
+import type { DeepPartial, Rss } from 'feedsmith/types'
+
+const processFeed = (feed: DeepPartial>) => {
+ console.log(feed.title)
+}
+```
+
+#### After (3.x)
+```typescript
+import type { Rss } from 'feedsmith'
+
+// All fields already optional - DeepPartial not needed
+const processFeed = (feed: Rss.Feed) => {
+ console.log(feed.title)
+}
+```
+
+#### Migration Steps
+1. Remove `DeepPartial` from your imports
+2. Use base types directly (`Rss.Feed`, `Atom.Feed`, etc.)
+
+### Types Entry Point Removed
+
+The `feedsmith/types` entry point has been removed. All types are now exported from the main `feedsmith` entry point. Additionally, deprecated type aliases (`RssFeed`, `AtomFeed`, `JsonFeed`, `RdfFeed`, `Opml`) have been removed.
+
+#### Before (2.x)
+```typescript
+import type { Rss } from 'feedsmith/types'
+import { parseRssFeed } from 'feedsmith'
+```
+
+#### After (3.x)
+```typescript
+import { type Rss, parseRssFeed } from 'feedsmith'
+```
+
+#### Migration Steps
+1. Change `feedsmith/types` imports to `feedsmith`
+2. Replace deprecated type aliases: `RssFeed` → `Rss.Feed`, `AtomFeed` → `Atom.Feed`, etc.
+
+### Atom text fields changed from string to object
+
+The `title`, `subtitle`, `rights`, and `summary` fields on Atom feeds and entries were previously flattened to strings. This meant any additional attributes like `type` (indicating whether the text is plain text, HTML, or XHTML) and XML namespace declarations were lost during parsing. In the new version, they use the `Atom.Text` object that preserves these attributes, properly representing the [Atom text construct](https://www.rfc-editor.org/rfc/rfc4287#section-3.1).
+
+The affected fields are:
+- **Feed**: `title`, `subtitle`, `rights`
+- **Entry**: `title`, `summary`, `rights`
+- **Source** (in entry): `title`, `subtitle`, `rights`
+
+```xml
+My <em>Blog</em>
+```
+
+#### Before (2.x)
+```typescript
+// Parsing
+const feed = parseAtomFeed(xml)
+const title = feed.title // string
+const subtitle = feed.subtitle // string
+const rights = feed.entries?.[0]?.rights // string
+
+// Generating
+const xml = generateAtomFeed({
+ title: 'My Blog',
+ subtitle: 'A blog about things',
+})
+```
+
+#### After (3.x)
+```typescript
+// Parsing
+const feed = parseAtomFeed(xml)
+const title = feed.title?.value // string (text content)
+const titleType = feed.title?.type // e.g. 'html', 'xhtml', 'text'
+const subtitle = feed.subtitle?.value // string
+const rights = feed.entries?.[0]?.rights?.value // string
+
+// Generating
+const xml = generateAtomFeed({
+ title: { value: 'My Blog' },
+ subtitle: { value: 'A blog about things', type: 'text' },
+})
+```
+
+#### Migration Steps
+1. Replace reads with `.value` (e.g., `feed.title` → `feed.title?.value`)
+2. Update generate calls: `title: 'text'` → `title: { value: 'text' }`
+3. Optionally use `type` for richer text metadata (`'text'`, `'html'`, `'xhtml'`)
+
+### Atom Entry `content` changed from string to object
+
+The `content` field on Atom entries was previously flattened to a string. This meant, any additional attributes like `type` (indicating content type), `src` (remote content URI), and XML namespace declarations were lost during parsing. In the new version, it is replaced with the `Atom.Content` object that preserves these attributes, properly representing the [Atom content construct](https://www.rfc-editor.org/rfc/rfc4287#section-4.1.3).
+
+```xml
+
+ Text
+
+```
+
+#### Before (2.x)
+```typescript
+// Parsing
+const feed = parseAtomFeed(xml)
+const content = feed.entries?.[0]?.content // string
+
+// Generating
+const xml = generateAtomFeed({
+ entries: [{ content: '
', type: 'html' } }],
+})
+```
+
+#### Migration Steps
+1. Replace `entry.content` reads with `entry.content?.value`
+2. Update generate calls: `content: 'text'` → `content: { value: 'text' }`
+3. Optionally use `type` and `src` for richer content metadata
+
+### RSS Person Fields Changed from Strings to Objects
+
+The `managingEditor`, `webMaster`, and `authors` fields on RSS feeds and items were previously plain strings (e.g., `'editor@example.com (Editor Name)'`). In the new version, they use the `Rss.Person` object that preserves structured data, properly representing the [RSS person construct](https://www.rssboard.org/rss-specification#ltauthorgtSubelementOfLtitemgt).
+
+The affected fields are:
+- **Feed**: `managingEditor`, `webMaster`
+- **Item**: `authors`
+
+```xml
+editor@example.com (Editor Name)
+john@example.com (John Doe)
+```
+
+#### Before (2.x)
+```typescript
+// Parsing
+const feed = parseRssFeed(xml)
+const editor = feed.managingEditor // 'editor@example.com (Editor Name)'
+const author = feed.items?.[0]?.authors?.[0] // 'john@example.com (John Doe)'
+
+// Generating
+const xml = generateRssFeed({
+ managingEditor: 'editor@example.com (Editor Name)',
+ items: [{ authors: ['john@example.com (John Doe)'] }],
+})
+```
+
+#### After (3.x)
+```typescript
+// Parsing
+const feed = parseRssFeed(xml)
+const editor = feed.managingEditor // { email: 'editor@example.com', name: 'Editor Name' }
+const author = feed.items?.[0]?.authors?.[0] // { email: 'john@example.com', name: 'John Doe' }
+
+// Generating
+const xml = generateRssFeed({
+ managingEditor: { email: 'editor@example.com', name: 'Editor Name' },
+ items: [{ authors: [{ email: 'john@example.com', name: 'John Doe' }] }],
+})
+```
+
+> [!NOTE]
+> The `link` property on `Rss.Person` is parse-only — it is extracted from URLs found in the person string but is not included in generated XML output, as RSS spec does not define a standard way to encode links in person fields.
+
+#### Migration Steps
+1. Replace string reads with object property access (e.g., `feed.managingEditor` → `feed.managingEditor?.email`)
+2. Update generate calls: `'email (Name)'` → `{ email: 'email', name: 'Name' }`
+
+### Media Namespace: Deprecated Field Removed
+
+The deprecated `group` field has been removed to align with the [Media RSS specification](https://www.rssboard.org/media-rss):
+- `group` → `groups` (spec allows multiple `media:group` elements)
+
+#### Before (2.x)
+```typescript
+const feed = parseRssFeed(xml)
+const group = feed.media?.group
+```
+
+#### After (3.x)
+```typescript
+const feed = parseRssFeed(xml)
+const group = feed.media?.groups?.[0]
+```
+
+#### Migration Steps
+1. Replace `group` with `groups`
+2. Access the first element: `media.group` → `media.groups?.[0]`
+
+### Podcast Namespace: Deprecated Fields Removed
+
+Deprecated fields have been removed to align with the [Podcasting 2.0 specification](https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md):
+
+- `location` → `locations` (spec allows multiple `podcast:location` elements)
+- `value` → `values` (spec allows multiple `podcast:value` elements)
+- `chats` → `chat` (spec allows only one `podcast:chat` element)
+
+#### Before (2.x)
+```typescript
+const feed = parseRssFeed(xml)
+const location = feed.items?.[0]?.podcast?.location
+const value = feed.items?.[0]?.podcast?.value
+const chat = feed.items?.[0]?.podcast?.chats?.[0]
+```
+
+#### After (3.x)
+```typescript
+const feed = parseRssFeed(xml)
+const location = feed.items?.[0]?.podcast?.locations?.[0]
+const value = feed.items?.[0]?.podcast?.values?.[0]
+const chat = feed.items?.[0]?.podcast?.chat
+```
+
+#### Migration Steps
+1. Replace `location` with `locations` (wrap in array)
+2. Replace `value` with `values` (wrap in array)
+3. Replace `chats` with `chat` (use `chats[0]` if you had multiple)
+
+### Dublin Core Namespace: Singular Fields Removed
+
+Deprecated singular fields have been removed to align with the [Dublin Core specification](https://www.dublincore.org/specifications/dublin-core/dces/) where all elements are repeatable:
+
+- `title` → `titles`
+- `creator` → `creators`
+- `subject` → `subjects`
+- `description` → `descriptions`
+- `publisher` → `publishers`
+- `contributor` → `contributors`
+- `date` → `dates`
+- `type` → `types`
+- `format` → `formats`
+- `identifier` → `identifiers`
+- `source` → `sources`
+- `language` → `languages`
+- `relation` → `relations`
+- `coverage` (now array)
+- `rights` (now array)
+
+#### Before (2.x)
+```typescript
+const feed = parseRssFeed(xml)
+const title = feed.dc?.title
+const creator = feed.dc?.creator
+const coverage = feed.dc?.coverage
+```
+
+#### After (3.x)
+```typescript
+const feed = parseRssFeed(xml)
+const title = feed.dc?.titles?.[0]
+const creator = feed.dc?.creators?.[0]
+const coverage = feed.dc?.coverage?.[0]
+```
+
+#### Migration Steps
+1. Replace singular fields with plural equivalents (e.g., `dc.title` → `dc.titles?.[0]`)
+2. Fields that kept their name (`coverage`, `rights`) are now arrays: `dc.coverage` → `dc.coverage?.[0]`
+
+### Dublin Core Terms Namespace: Singular Fields Removed
+
+Deprecated singular fields have been removed to align with the [Dublin Core Terms specification](https://www.dublincore.org/specifications/dublin-core/dcmi-terms/) where all elements are repeatable:
+
+- `title` → `titles`
+- `creator` → `creators`
+- `subject` → `subjects`
+- `description` → `descriptions`
+- `publisher` → `publishers`
+- `contributor` → `contributors`
+- `date` → `dates`
+- `type` → `types`
+- `format` → `formats`
+- `identifier` → `identifiers`
+- `source` → `sources`
+- `language` → `languages`
+- `relation` → `relations`
+- `abstract` → `abstracts`
+- `audience` → `audiences`
+- `alternative` → `alternatives`
+- `educationLevel` → `educationLevels`
+- `extent` → `extents`
+- `hasFormat` → `hasFormats`
+- `hasPart` → `hasParts`
+- `hasVersion` → `hasVersions`
+- `instructionalMethod` → `instructionalMethods`
+- `license` → `licenses`
+- `mediator` → `mediators`
+- `medium` → `mediums`
+- `provenance` → `provenances`
+- `rightsHolder` → `rightsHolders`
+- `spatial` → `spatials`
+- `temporal` → `temporals`
+- `accrualMethod` → `accrualMethods`
+- `accrualPeriodicity` → `accrualPeriodicities`
+- `accrualPolicy` → `accrualPolicies`
+- `bibliographicCitation` → `bibliographicCitations`
+- Plus 21 fields that kept their name but are now arrays (e.g., `created`, `modified`, `issued`, `valid`)
+
+#### Before (2.x)
+```typescript
+const feed = parseRssFeed(xml)
+const title = feed.dcterms?.title
+const creator = feed.dcterms?.creator
+const created = feed.dcterms?.created
+```
+
+#### After (3.x)
+```typescript
+const feed = parseRssFeed(xml)
+const title = feed.dcterms?.titles?.[0]
+const creator = feed.dcterms?.creators?.[0]
+const created = feed.dcterms?.created?.[0]
+```
+
+#### Migration Steps
+1. Replace singular fields with plural equivalents (e.g., `dcterms.title` → `dcterms.titles?.[0]`)
+2. Fields that kept their name (e.g., `created`, `modified`) are now arrays: `dcterms.created` → `dcterms.created?.[0]`
+
+## New Features
+
+### Improved Error Handling
+
+Feedsmith now throws dedicated error types for different failure scenarios:
+
+- `DetectError` — thrown when input doesn't match the expected feed format
+- `MalformedError` — thrown when content is malformed (e.g., invalid XML)
+- `ParseError` — thrown when content parsed but produced an invalid result
+- `GenerateError` — thrown when feed generation fails due to invalid input
+
+See [Parsing Errors](/parsing/errors) and [Generating Errors](/generating/errors) for more details.
+
+### Namespace Type Exports
+
+All namespace types are now exported directly from the main package:
+
+```typescript
+import type { ItunesNs, DcNs, MediaNs, PodcastNs } from 'feedsmith'
+
+const category: ItunesNs.Category = {
+ text: 'Technology'
+}
+
+const transcript: PodcastNs.Transcript = {
+ url: 'https://example.com/transcript.srt',
+ type: 'application/srt'
+}
+```
+
+See [Working with TypeScript](/reference/typescript#importing-namespace-types) for the more information and usage examples.
+
+### Utility Type Exports
+
+Common utility types `DateLike` and `XmlStylesheet` are now exported from the main package:
+
+```typescript
+import type { RssFeed, DateLike, XmlStylesheet } from 'feedsmith'
+
+type RssMetadata = Omit, 'items'>
+
+const stylesheet: XmlStylesheet = {
+ type: 'text/xsl',
+ href: '/feed.xsl',
+}
+```
+
+### Format Type Namespaces Renamed
+
+The four format-level type namespaces have been renamed to align with their parse function names:
+
+| Before (2.x) | After (3.x) |
+|---|---|
+| `Rss` | `RssFeed` |
+| `Atom` | `AtomFeed` |
+| `Json` | `JsonFeed` |
+| `Rdf` | `RdfFeed` |
+
+```typescript
+// Before (2.x)
+import type { Rss, Atom } from 'feedsmith/types'
+const feed: Rss.Feed = parseRssFeed(xml)
+const entry: Atom.Entry = entries[0]
+
+// After (3.x)
+import type { RssFeed, AtomFeed } from 'feedsmith'
+const feed: RssFeed.Feed = parseRssFeed(xml)
+const entry: AtomFeed.Entry = entries[0]
+```
+
+Nested type access works identically: `RssFeed.Item`, `AtomFeed.Link`, `JsonFeed.Author`, `RdfFeed.Image`, etc. The shape of each namespace is unchanged — only the outer name is different.
+
+The previous names (`Rss`, `Atom`, `Json`, `Rdf`) remain available as deprecated aliases and will be removed in 4.x.
+
+### `parseFeed` Return Type Now Exported as `AnyFeed`
+
+The universal `parseFeed` function now has a publicly exported return type, `AnyFeed`, so you can annotate the result directly:
+
+```typescript
+import { parseFeed, type AnyFeed } from 'feedsmith'
+
+const result: AnyFeed = parseFeed(content)
+const { format, feed } = result
+```
+
+The function itself, its options, and its return shape are unchanged.
+
+### Custom Date Parsing
+
+Parse functions now accept a `parseDateFn` option to convert date strings into any format. All date fields across feeds, items, and namespaces are passed through the provided function. See [Parsing Dates](/parsing/dates) for details and examples.
+
+```typescript
+import { parseRssFeed } from 'feedsmith'
+
+const feed = parseRssFeed(xml, {
+ parseDateFn: (raw) => new Date(raw),
+})
+
+feed.pubDate // Date
+```
+
+### XML Namespace Support
+
+RSS, Atom, and RDF feeds now support the [XML namespace](/reference/namespaces/xml) (`xml:*` attributes). The `xml` property is available on both feed and item levels, providing access to `xml:lang`, `xml:base`, `xml:space`, and `xml:id` attributes.
diff --git a/docs/parsing.md b/docs/parsing.md
index b5690f73..35c2aab4 100644
--- a/docs/parsing.md
+++ b/docs/parsing.md
@@ -41,7 +41,7 @@ The universal parser:
- Automatically detects the feed format using format detection functions
- Returns an object with `format` and `feed` properties
- Supports RSS, Atom, RDF, and JSON Feed formats
-- Throws an error for unrecognized or invalid feeds
+- Throws `DetectError`, `MalformedError`, or `ParseError` for invalid feeds
> [!IMPORTANT]
> The universal parser uses detection functions to identify the feed format. While these work well for most feeds, they might not perfectly detect all valid feeds, especially those with non-standard structures. If you know the feed format in advance, using a dedicated parser is more reliable.
@@ -89,26 +89,8 @@ opml.body?.outlines
## Error Handling
-If the feed is unrecognized or invalid, an `Error` will be thrown with a descriptive message.
-
-```typescript
-import { parseFeed, parseJsonFeed } from 'feedsmith'
-
-try {
- const universalFeed = parseFeed('')
-} catch (error) {
- // Error: Unrecognized feed format
-}
-
-try {
- const jsonFeed = parseJsonFeed('{}')
-} catch (error) {
- // Error: Invalid feed format
-}
-```
+Parsing functions throw `DetectError` when the input doesn't match the expected format, `MalformedError` when the content is malformed, or `ParseError` when the parsed result is invalid. See [Error Handling](/parsing/errors) for details.
## Returned Values
-The parsing functions return JavaScript objects representing the feed in its original structure.
-
-For detailed examples of input and output for each feed format, see the [Parsing Examples](/parsing/examples) page.
+The parsing functions return JavaScript objects representing the feed in its original structure. See [Parsing Examples](/parsing/examples) page for detailed examples of input and output for each feed format.
diff --git a/docs/parsing/dates.md b/docs/parsing/dates.md
index c110a810..cb01dddc 100644
--- a/docs/parsing/dates.md
+++ b/docs/parsing/dates.md
@@ -1,21 +1,60 @@
---
-title: "Parsing Feeds: Handling Dates"
+title: "Parsing Feeds: Parsing Dates"
---
-# Handling Dates
+# Parsing Dates
-Dates in feeds do not always follow a format defined in the specifications, or even any consistent format. Instead of attempting to parse all of them and risking errors, Feedsmith returns dates in their original string form. This method allows for the use of a preferred date parsing library, custom function, or the `Date` object directly.
+Dates in feeds are notoriously unreliable — wrong formats, missing timezones, localized strings, inconsistencies within the same feed. Rather than shipping a built-in parser that handles some cases and silently fails on others, Feedsmith returns date strings as-is and lets you bring your own parsing logic via the `parseDateFn` option. This way you can use whichever date library (or the native `Date` constructor) fits your needs and handle edge cases on your terms.
-### Common Issues
+The function receives the raw (trimmed) date string and its return value replaces it in the result.
-- **RSS**: Should use RFC 2822 format, but many feeds use incorrect formats
-- **Atom**: ISO 8601/RFC 3339 format, generally more consistent but still varies
-- **Real-world problems**:
- - Missing timezone information
- - Invalid day/month combinations
- - Inconsistent formatting within the same feed
- - Localized date strings
- - Custom date formats
+### Using `Date` constructor
-> [!NOTE]
-> Automatic date parsing may be implemented in a future version of Feedsmith, with an option to preserve string behavior for backward compatibility.
+```typescript
+import { parseFeed } from 'feedsmith'
+
+const { feed } = parseFeed(xml, {
+ parseDateFn: (raw) => new Date(raw),
+})
+
+feed.pubDate // Date
+```
+
+### Using a date library
+
+```typescript
+import { parseRssFeed } from 'feedsmith'
+import { parse } from 'date-fns'
+
+const feed = parseRssFeed(xml, {
+ parseDateFn: (raw) => {
+ return parse(raw, 'EEE, dd MMM yyyy HH:mm:ss xx', new Date())
+ },
+})
+
+feed.pubDate // Date
+```
+
+### Type safety with `TDate`
+
+All parse functions accept a generic `TDate` parameter that defaults to `string`. When `parseDateFn` is provided, `TDate` is inferred from its return type, so all date fields in the result are typed accordingly:
+
+```typescript
+import { parseAtomFeed } from 'feedsmith'
+
+// Without parseDateFn — dates are strings.
+const feed = parseAtomFeed(xml)
+feed.updated // string | undefined
+
+// With parseDateFn — dates match the return type.
+const feed = parseAtomFeed(xml, {
+ parseDateFn: (raw) => new Date(raw),
+})
+feed.updated // Date | undefined
+```
+
+### Error handling
+
+Errors thrown by `parseDateFn` are **not caught** — they propagate to the caller. You should wrap your logic in a try/catch if it might throw. This is intentional — silently swallowing errors would hide invalid dates, and you're better off deciding how to handle them yourself.
+
+If a date string is empty or whitespace-only, `parseDateFn` is not called and the field is omitted from the result.
diff --git a/docs/parsing/detecting.md b/docs/parsing/detecting.md
index b38ba3df..175152c3 100644
--- a/docs/parsing/detecting.md
+++ b/docs/parsing/detecting.md
@@ -2,7 +2,7 @@
title: "Parsing Feeds: Detecting Format"
---
-# Detecting Format
+# Detecting Feed Format
You can quickly detect the feed format without parsing it.
diff --git a/docs/parsing/errors.md b/docs/parsing/errors.md
new file mode 100644
index 00000000..6dc7ff5a
--- /dev/null
+++ b/docs/parsing/errors.md
@@ -0,0 +1,95 @@
+# Error Handling
+
+Feedsmith provides dedicated error types for different failure scenarios during parsing.
+
+## Error Types
+
+### DetectError
+
+Thrown when the input doesn't match the expected feed format. This happens before any parsing is attempted.
+
+```typescript
+import { parseRssFeed, DetectError } from 'feedsmith'
+
+try {
+ parseRssFeed('not rss')
+} catch (error) {
+ if (error instanceof DetectError) {
+ console.log(error.message) // "Invalid feed format"
+ }
+}
+```
+
+### MalformedError
+
+Thrown when the content is malformed and the underlying parser fails (e.g., invalid XML).
+
+```typescript
+import { parseRssFeed, MalformedError } from 'feedsmith'
+
+try {
+ parseRssFeed('Test')
+} catch (error) {
+ if (error instanceof MalformedError) {
+ console.log(error.message) // "Invalid feed format"
+ }
+}
+```
+
+### ParseError
+
+Thrown when the content is syntactically valid but produces an empty or invalid result.
+
+```typescript
+import { parseRssFeed, ParseError } from 'feedsmith'
+
+try {
+ parseRssFeed('')
+} catch (error) {
+ if (error instanceof ParseError) {
+ console.log(error.message) // "Invalid feed format"
+ }
+}
+```
+
+## Universal Parser
+
+The universal `parseFeed` function throws the same error types:
+
+```typescript
+import { parseFeed, DetectError, MalformedError, ParseError } from 'feedsmith'
+
+try {
+ parseFeed('')
+} catch (error) {
+ if (error instanceof DetectError) {
+ // Unrecognized feed format.
+ } else if (error instanceof MalformedError) {
+ // Malformed XML.
+ } else if (error instanceof ParseError) {
+ // Valid XML but invalid feed structure.
+ }
+}
+```
+
+## Error Hierarchy
+
+All error classes extend the built-in `Error`, so `instanceof Error` checks work as expected. This can be useful for catching any parsing error alongside other errors in a single handler:
+
+```typescript
+import { parseFeed, DetectError, MalformedError, ParseError } from 'feedsmith'
+
+try {
+ parseFeed(input)
+} catch (error) {
+ if (error instanceof DetectError) {
+ // Unrecognized feed format.
+ } else if (error instanceof MalformedError) {
+ // Malformed XML.
+ } else if (error instanceof ParseError) {
+ // Valid XML but invalid feed structure.
+ } else if (error instanceof Error) {
+ // Any other error.
+ }
+}
+```
diff --git a/docs/parsing/examples.md b/docs/parsing/examples.md
index d32c8eff..ae04a862 100644
--- a/docs/parsing/examples.md
+++ b/docs/parsing/examples.md
@@ -125,6 +125,7 @@ const rssFeed = parseRssFeed(`
First item title
http://example.org/item/1
Some description of the first item.
+ john@example.org (John Doe)http://example.org/comments/1http://example.org/guid/1
@@ -144,7 +145,7 @@ Returns:
"link": "http://example.org/",
"description": "For documentation only",
"language": "en",
- "webMaster": "webmaster@example.org",
+ "webMaster": { "email": "webmaster@example.org" },
"pubDate": "Sat, 19 Mar 1988 07:15:00 GMT",
"lastBuildDate": "Sat, 19 Mar 1988 07:15:00 GMT",
"categories": [{ "name": "Examples2", "domain": "http://www.example.com/cusips" }],
@@ -179,6 +180,7 @@ Returns:
"title": "First item title",
"link": "http://example.org/item/1",
"description": "Some description of the first item.",
+ "authors": [{ "email": "john@example.org", "name": "John Doe" }],
"comments": "http://example.org/comments/1",
"enclosures": [
{
diff --git a/docs/quick-start.md b/docs/quick-start.md
index 66f0275f..ee537f75 100644
--- a/docs/quick-start.md
+++ b/docs/quick-start.md
@@ -127,36 +127,43 @@ console.log(rss) // Complete RSS XML
## Error Handling
-If the feed is unrecognized or invalid, an `Error` will be thrown with a descriptive message.
+Feedsmith throws dedicated error types for different failure scenarios:
-```typescript
-import { parseFeed, parseJsonFeed } from 'feedsmith'
+- `DetectError` — input doesn't match the expected feed format
+- `MalformedError` — content is malformed (e.g., invalid XML)
+- `ParseError` — content parsed but produced an invalid result
+- `GenerateError` — feed generation failed due to invalid input
-try {
- const universalFeed = parseFeed('')
-} catch (error) {
- // Error: Unrecognized feed format
-}
+```typescript
+import { parseRssFeed, DetectError, MalformedError, ParseError } from 'feedsmith'
try {
- const jsonFeed = parseJsonFeed('{}')
+ parseRssFeed(input)
} catch (error) {
- // Error: Invalid feed format
+ if (error instanceof DetectError) {
+ // Not RSS format
+ } else if (error instanceof MalformedError) {
+ // Malformed XML
+ } else if (error instanceof ParseError) {
+ // Valid XML but invalid feed structure
+ }
}
```
+See [Parsing Errors](/parsing/errors) and [Generating Errors](/generating/errors) for more details.
+
## TypeScript Types
Feedsmith provides comprehensive TypeScript types for all feed formats:
```typescript
-import type { Rss, Atom, Json, Opml } from 'feedsmith/types'
+import type { RssFeed, AtomFeed, JsonFeed, Opml } from 'feedsmith'
// Access all types for a format
-type Feed = Rss.Feed
-type Item = Rss.Item
-type Category = Rss.Category
-type Enclosure = Rss.Enclosure
+type Feed = RssFeed.Feed
+type Item = RssFeed.Item
+type Category = RssFeed.Category
+type Enclosure = RssFeed.Enclosure
```
Each format exports its complete type system, including nested types and namespace types. See the [TypeScript guide](/reference/typescript) for details.
diff --git a/docs/reference/feeds/atom.md b/docs/reference/feeds/atom.md
index c43ac3fc..56f902fb 100644
--- a/docs/reference/feeds/atom.md
+++ b/docs/reference/feeds/atom.md
@@ -2,7 +2,7 @@
title: "Reference: Atom Feed"
---
-# Atom Feed
+# Atom Feed Reference
Atom is a syndication format based on XML that provides a robust framework for web feeds. Feedsmith provides comprehensive parsing and generation capabilities.
@@ -39,7 +39,8 @@ Atom is a syndication format based on XML that provides a robust framework for w
Trackback,
YouTube,
W3C Basic Geo,
- GeoRSS Simple
+ GeoRSS Simple,
+ XML
@@ -85,7 +86,6 @@ Generates Atom XML from feed data.
import { generateAtomFeed } from 'feedsmith'
const xml = generateAtomFeed(feedData, {
- lenient: true,
stylesheets: [{ type: 'text/xsl', href: '/feed.xsl' }]
})
```
@@ -101,7 +101,7 @@ const xml = generateAtomFeed(feedData, {
| Option | Type | Default | Description |
|--------|------|---------|-------------|
-| `lenient` | `boolean` | `false` | Enable lenient mode for relaxed validation, see [Lenient Mode](/generating/lenient-mode) |
+| `strict` | `boolean` | `false` | Enable strict mode for spec-required field validation, see [Strict Mode](/generating/strict-mode) |
| `stylesheets` | `Stylesheet[]` | - | Add stylesheets for visual formatting, see [Feed Styling](/generating/styling) |
#### Returns
@@ -129,16 +129,16 @@ const isAtom = detectAtomFeed(xmlContent)
## Types
-All Atom types are available under the `Atom` namespace:
+All Atom types are available under the `AtomFeed` namespace:
```typescript
-import type { Atom } from 'feedsmith/types'
+import type { AtomFeed } from 'feedsmith'
// Access any type from the definitions below
-type Feed = Atom.Feed
-type Entry = Atom.Entry
-type Link = Atom.Link
-type Person = Atom.Person
+type Feed = AtomFeed.Feed
+type Entry = AtomFeed.Entry
+type Link = AtomFeed.Link
+type Person = AtomFeed.Person
// … see type definitions below for all available types
```
@@ -147,7 +147,7 @@ See the [TypeScript guide](/reference/typescript) for usage examples.
### Type Definitions
> [!INFO]
-> `TDate` represents date fields in the type definitions. When **parsing**, dates are returned as strings in their original format (see [Parsing › Handling Dates](/parsing/dates) for more details). When **generating**, dates should be provided as JavaScript `Date` objects.
+> For details on type parameters (`TDate`, `TStrict`) and `Requirable` markers, see [TypeScript Reference](/reference/typescript#tdate).
<<< @/../src/feeds/atom/common/types.ts#reference
diff --git a/docs/reference/feeds/json-feed.md b/docs/reference/feeds/json-feed.md
index 641efcfd..3f6def0c 100644
--- a/docs/reference/feeds/json-feed.md
+++ b/docs/reference/feeds/json-feed.md
@@ -2,7 +2,7 @@
title: "Reference: JSON Feed"
---
-# JSON Feed
+# JSON Feed Reference
JSON Feed is a syndication format based on JSON that provides a simple, straightforward way to publish feeds. Feedsmith provides full parsing and generation capabilities.
@@ -62,9 +62,7 @@ Generates JSON Feed from feed data.
```typescript
import { generateJsonFeed } from 'feedsmith'
-const json = generateJsonFeed(feedData, {
- lenient: true
-})
+const json = generateJsonFeed(feedData)
```
#### Parameters
@@ -78,7 +76,7 @@ const json = generateJsonFeed(feedData, {
| Option | Type | Default | Description |
|--------|------|---------|-------------|
-| `lenient` | `boolean` | `false` | Enable lenient mode for relaxed validation, see [Lenient Mode](/generating/lenient-mode) |
+| `strict` | `boolean` | `false` | Enable strict mode for spec-required field validation, see [Strict Mode](/generating/strict-mode) |
#### Returns
`object` - Generated JSON Feed
@@ -105,16 +103,16 @@ const isJsonFeed = detectJsonFeed(jsonContent)
## Types
-All JSON Feed types are available under the `Json` namespace:
+All JSON Feed types are available under the `JsonFeed` namespace:
```typescript
-import type { Json } from 'feedsmith/types'
+import type { JsonFeed } from 'feedsmith'
// Access any type from the definitions below
-type Feed = Json.Feed
-type Item = Json.Item
-type Author = Json.Author
-type Attachment = Json.Attachment
+type Feed = JsonFeed.Feed
+type Item = JsonFeed.Item
+type Author = JsonFeed.Author
+type Attachment = JsonFeed.Attachment
// … see type definitions below for all available types
```
@@ -123,7 +121,7 @@ See the [TypeScript guide](/reference/typescript) for usage examples.
### Type Definitions
> [!INFO]
-> `TDate` represents date fields in the type definitions. When **parsing**, dates are returned as strings in their original format (see [Parsing › Handling Dates](/parsing/dates) for more details). When **generating**, dates should be provided as JavaScript `Date` objects.
+> For details on type parameters (`TDate`, `TStrict`) and `Requirable` markers, see [TypeScript Reference](/reference/typescript#tdate).
<<< @/../src/feeds/json/common/types.ts#reference
diff --git a/docs/reference/feeds/rdf.md b/docs/reference/feeds/rdf.md
index 9fef3cab..9ad617f7 100644
--- a/docs/reference/feeds/rdf.md
+++ b/docs/reference/feeds/rdf.md
@@ -2,7 +2,7 @@
title: "Reference: RDF Feed"
---
-# RDF Feed
+# RDF Feed Reference
RDF (Resource Description Framework) Site Summary is an early XML-based syndication format that uses RDF metadata. Feedsmith provides full parsing capabilities.
@@ -19,17 +19,18 @@ RDF (Resource Description Framework) Site Summary is an early XML-based syndicat
@@ -94,16 +95,16 @@ const isRdf = detectRdfFeed(xmlContent)
## Types
-All RDF types are available under the `Rdf` namespace:
+All RDF types are available under the `RdfFeed` namespace:
```typescript
-import type { Rdf } from 'feedsmith/types'
+import type { RdfFeed } from 'feedsmith'
// Access any type from the definitions below
-type Feed = Rdf.Feed
-type Item = Rdf.Item
-type Image = Rdf.Image
-type TextInput = Rdf.TextInput
+type Feed = RdfFeed.Feed
+type Item = RdfFeed.Item
+type Image = RdfFeed.Image
+type TextInput = RdfFeed.TextInput
// … see type definitions below for all available types
```
diff --git a/docs/reference/feeds/rss.md b/docs/reference/feeds/rss.md
index 955d587a..22290ec1 100644
--- a/docs/reference/feeds/rss.md
+++ b/docs/reference/feeds/rss.md
@@ -2,7 +2,7 @@
title: "Reference: RSS Feed"
---
-# RSS Feed
+# RSS Feed Reference
RSS (Really Simple Syndication) is one of the most widely used web feed formats. Feedsmith automatically normalizes legacy elements to their modern equivalents.
@@ -46,7 +46,8 @@ RSS (Really Simple Syndication) is one of the most widely used web feed formats.
Source,
blogChannel,
W3C Basic Geo,
- GeoRSS Simple
+ GeoRSS Simple,
+ XML
@@ -92,7 +93,6 @@ Generates RSS XML from feed data.
import { generateRssFeed } from 'feedsmith'
const xml = generateRssFeed(feedData, {
- lenient: true,
stylesheets: [{ type: 'text/xsl', href: '/feed.xsl' }]
})
```
@@ -108,7 +108,7 @@ const xml = generateRssFeed(feedData, {
| Option | Type | Default | Description |
|--------|------|---------|-------------|
-| `lenient` | `boolean` | `false` | Enable lenient mode for relaxed validation, see [Lenient Mode](/generating/lenient-mode) |
+| `strict` | `boolean` | `false` | Enable strict mode for spec-required field validation, see [Strict Mode](/generating/strict-mode) |
| `stylesheets` | `Stylesheet[]` | - | Add stylesheets for visual formatting, see [Feed Styling](/generating/styling) |
#### Returns
@@ -136,16 +136,16 @@ const isRss = detectRssFeed(xmlContent)
## Types
-All RSS types are available under the `Rss` namespace:
+All RSS types are available under the `RssFeed` namespace:
```typescript
-import type { Rss } from 'feedsmith/types'
+import type { RssFeed } from 'feedsmith'
// Access any type from the definitions below
-type Feed = Rss.Feed
-type Item = Rss.Item
-type Category = Rss.Category
-type Enclosure = Rss.Enclosure
+type Feed = RssFeed.Feed
+type Item = RssFeed.Item
+type Category = RssFeed.Category
+type Enclosure = RssFeed.Enclosure
// … see type definitions below for all available types
```
@@ -154,7 +154,7 @@ See the [TypeScript guide](/reference/typescript) for usage examples.
### Type Definitions
> [!INFO]
-> `TDate` represents date fields in the type definitions. When **parsing**, dates are returned as strings in their original format (see [Parsing › Handling Dates](/parsing/dates) for more details). When **generating**, dates should be provided as JavaScript `Date` objects.
+> For details on type parameters (`TDate`, `TStrict`) and `Requirable` markers, see [TypeScript Reference](/reference/typescript#tdate).
<<< @/../src/feeds/rss/common/types.ts#reference
diff --git a/docs/reference/namespaces/acast.md b/docs/reference/namespaces/acast.md
index 020ff48b..99ac0b97 100644
--- a/docs/reference/namespaces/acast.md
+++ b/docs/reference/namespaces/acast.md
@@ -2,7 +2,7 @@
title: "Reference: Acast Namespace"
---
-# Acast Namespace
+# Acast Namespace Reference
The Acast namespace provides podcast-specific metadata for Acast's podcast hosting platform, including show and episode identifiers, encrypted settings, and network information.
diff --git a/docs/reference/namespaces/admin.md b/docs/reference/namespaces/admin.md
index fbe80c13..b1943d14 100644
--- a/docs/reference/namespaces/admin.md
+++ b/docs/reference/namespaces/admin.md
@@ -2,7 +2,7 @@
title: "Reference: Administrative Namespace"
---
-# Administrative Namespace
+# Administrative Namespace Reference
The Administrative namespace (MVCB - Meta Vocabulary for Community Building) provides administrative metadata about RSS/RDF feeds, enabling better identification of feed generators and error reporting contacts.
diff --git a/docs/reference/namespaces/app.md b/docs/reference/namespaces/app.md
index 07f0c546..32b167aa 100644
--- a/docs/reference/namespaces/app.md
+++ b/docs/reference/namespaces/app.md
@@ -2,7 +2,7 @@
title: "Reference: Atom Publishing Protocol Namespace"
---
-# Atom Publishing Protocol Namespace
+# Atom Publishing Protocol Namespace Reference
Extends Atom feeds with elements for content management workflows.
@@ -33,6 +33,9 @@ Extends Atom feeds with elements for content management workflows.
## Types
+> [!INFO]
+> For details on type parameters (`TDate`), see [TypeScript Reference](/reference/typescript#tdate).
+
<<< @/../src/namespaces/app/common/types.ts#reference
## Related
diff --git a/docs/reference/namespaces/arxiv.md b/docs/reference/namespaces/arxiv.md
index b056e5ed..d053b040 100644
--- a/docs/reference/namespaces/arxiv.md
+++ b/docs/reference/namespaces/arxiv.md
@@ -2,7 +2,7 @@
title: "Reference: arXiv Namespace"
---
-# arXiv Namespace
+# arXiv Namespace Reference
arXiv is an extension namespace for the arXiv preprint repository API, providing metadata specific to scholarly papers in physics, mathematics, computer science, and related fields.
diff --git a/docs/reference/namespaces/atom.md b/docs/reference/namespaces/atom.md
index bbca23b2..718c299a 100644
--- a/docs/reference/namespaces/atom.md
+++ b/docs/reference/namespaces/atom.md
@@ -2,7 +2,7 @@
title: "Reference: Atom Namespace"
---
-# Atom Namespace
+# Atom Namespace Reference
The Atom namespace allows RSS and RDF feeds to include Atom-specific elements, providing richer metadata and linking capabilities. This namespace provides partial Atom elements that can be embedded within other feed formats.
@@ -37,7 +37,7 @@ The Atom namespace allows RSS and RDF feeds to include Atom-specific elements, p
## Types
> [!INFO]
-> `TDate` represents date fields in the type definitions. When **parsing**, dates are returned as strings in their original format (see [Parsing › Handling Dates](/parsing/dates) for more details). When **generating**, dates should be provided as JavaScript `Date` objects.
+> For details on type parameters (`TDate`, `TStrict`) and `Requirable` markers, see [TypeScript Reference](/reference/typescript#tdate).
<<< @/../src/feeds/atom/common/types.ts#reference
diff --git a/docs/reference/namespaces/blogchannel.md b/docs/reference/namespaces/blogchannel.md
index 34a9e523..aa3122e9 100644
--- a/docs/reference/namespaces/blogchannel.md
+++ b/docs/reference/namespaces/blogchannel.md
@@ -2,7 +2,7 @@
title: "Reference: blogChannel Namespace"
---
-# blogChannel Namespace
+# blogChannel Namespace Reference
The blogChannel namespace is an RSS 2.0 module for weblogging applications, providing metadata about blog-related content such as blogrolls, recommended links, and subscription lists.
diff --git a/docs/reference/namespaces/cc.md b/docs/reference/namespaces/cc.md
index 30616a7e..0e96fa0d 100644
--- a/docs/reference/namespaces/cc.md
+++ b/docs/reference/namespaces/cc.md
@@ -2,7 +2,7 @@
title: "Reference: ccREL Namespace"
---
-# ccREL Namespace
+# ccREL Namespace Reference
The Creative Commons Rights Expression Language (ccREL) enables RSS and Atom feeds to declare copyright licenses and additional permissions for feed content.
diff --git a/docs/reference/namespaces/content.md b/docs/reference/namespaces/content.md
index 0ed03e44..453d1c42 100644
--- a/docs/reference/namespaces/content.md
+++ b/docs/reference/namespaces/content.md
@@ -2,7 +2,7 @@
title: "Reference: Content Namespace"
---
-# Content Namespace
+# Content Namespace Reference
The Content namespace allows RSS and RDF feeds to include full content alongside or instead of summaries. It provides a way to embed complete articles or posts within feed items.
diff --git a/docs/reference/namespaces/creativecommons.md b/docs/reference/namespaces/creativecommons.md
index 7f3fdc95..d2ccbddb 100644
--- a/docs/reference/namespaces/creativecommons.md
+++ b/docs/reference/namespaces/creativecommons.md
@@ -2,7 +2,7 @@
title: "Reference: Creative Commons Namespace"
---
-# Creative Commons Namespace
+# Creative Commons Namespace Reference
The Creative Commons namespace provides elements for specifying the license under which the feed content is distributed. This allows content creators to clearly indicate their licensing terms using Creative Commons or other license URLs.
diff --git a/docs/reference/namespaces/dc.md b/docs/reference/namespaces/dc.md
index db49a3ef..65ea4a06 100644
--- a/docs/reference/namespaces/dc.md
+++ b/docs/reference/namespaces/dc.md
@@ -2,7 +2,7 @@
title: "Reference: Dublin Core Namespace"
---
-# Dublin Core Namespace
+# Dublin Core Namespace Reference
The Dublin Core namespace provides standardized metadata elements for describing digital resources. It offers a simple and effective way to add bibliographic information to feeds and items.
@@ -38,7 +38,7 @@ The Dublin Core namespace provides standardized metadata elements for describing
## Types
> [!INFO]
-> `TDate` represents date fields in the type definitions. When **parsing**, dates are returned as strings in their original format (see [Parsing › Handling Dates](/parsing/dates) for more details). When **generating**, dates should be provided as JavaScript `Date` objects.
+> For details on type parameters (`TDate`), see [TypeScript Reference](/reference/typescript#tdate).
<<< @/../src/namespaces/dc/common/types.ts#reference
diff --git a/docs/reference/namespaces/dcterms.md b/docs/reference/namespaces/dcterms.md
index 58852ed2..dca90665 100644
--- a/docs/reference/namespaces/dcterms.md
+++ b/docs/reference/namespaces/dcterms.md
@@ -2,7 +2,7 @@
title: "Reference: Dublin Core Terms Namespace"
---
-# Dublin Core Terms Namespace
+# Dublin Core Terms Namespace Reference
The Dublin Core Terms namespace provides extended metadata elements based on the Dublin Core Metadata Initiative, offering comprehensive resource description capabilities.
@@ -38,7 +38,7 @@ The Dublin Core Terms namespace provides extended metadata elements based on the
## Types
> [!INFO]
-> `TDate` represents date fields in the type definitions. When **parsing**, dates are returned as strings in their original format (see [Parsing › Handling Dates](/parsing/dates) for more details). When **generating**, dates should be provided as JavaScript `Date` objects.
+> For details on type parameters (`TDate`), see [TypeScript Reference](/reference/typescript#tdate).
<<< @/../src/namespaces/dcterms/common/types.ts#reference
diff --git a/docs/reference/namespaces/feedpress.md b/docs/reference/namespaces/feedpress.md
index b1955b4b..19eace02 100644
--- a/docs/reference/namespaces/feedpress.md
+++ b/docs/reference/namespaces/feedpress.md
@@ -2,7 +2,7 @@
title: "Reference: FeedPress Namespace"
---
-# FeedPress Namespace
+# FeedPress Namespace Reference
The FeedPress namespace provides elements for FeedPress-specific feed metadata, including podcast identifiers, newsletter identifiers, locale information, and custom CSS file references.
diff --git a/docs/reference/namespaces/geo.md b/docs/reference/namespaces/geo.md
index 8c33e029..7f6ae060 100644
--- a/docs/reference/namespaces/geo.md
+++ b/docs/reference/namespaces/geo.md
@@ -2,7 +2,7 @@
title: "Reference: W3C Basic Geo Namespace"
---
-# W3C Basic Geo Namespace
+# W3C Basic Geo Namespace Reference
The W3C Basic Geo (WGS84 lat/long) Vocabulary provides a simple way to represent geographic coordinates in RSS and Atom feeds using the WGS84 geodetic reference datum.
diff --git a/docs/reference/namespaces/georss.md b/docs/reference/namespaces/georss.md
index a31c8332..ad0b6b35 100644
--- a/docs/reference/namespaces/georss.md
+++ b/docs/reference/namespaces/georss.md
@@ -2,7 +2,7 @@
title: "Reference: GeoRSS Simple Namespace"
---
-# GeoRSS Simple Namespace
+# GeoRSS Simple Namespace Reference
The GeoRSS Simple namespace enables geographic tagging of RSS feeds and items, allowing publishers to associate location information with their content.
@@ -37,6 +37,9 @@ The GeoRSS Simple namespace enables geographic tagging of RSS feeds and items, a
## Types
+> [!INFO]
+> For details on type parameters (`TStrict`) and `Requirable` markers, see [TypeScript Reference](/reference/typescript#tstrict).
+
<<< @/../src/namespaces/georss/common/types.ts#reference
## Related
diff --git a/docs/reference/namespaces/googleplay.md b/docs/reference/namespaces/googleplay.md
index 83401254..023c3f0a 100644
--- a/docs/reference/namespaces/googleplay.md
+++ b/docs/reference/namespaces/googleplay.md
@@ -2,7 +2,7 @@
title: "Reference: Google Play Podcast Namespace"
---
-# Google Play Podcast Namespace
+# Google Play Podcast Namespace Reference
The Google Play Podcast namespace provides podcast-specific metadata for feed and episode information optimized for Google Play's podcast platform, including author details, content descriptions, and content policies.
@@ -36,6 +36,9 @@ The Google Play Podcast namespace provides podcast-specific metadata for feed an
## Structure
+> [!INFO]
+> For details on type parameters (`TStrict`) and `Requirable` markers, see [TypeScript Reference](/reference/typescript#tstrict).
+
<<< @/../src/namespaces/googleplay/common/types.ts#reference
## Related
diff --git a/docs/reference/namespaces/itunes.md b/docs/reference/namespaces/itunes.md
index 4e0cebd2..00d9de40 100644
--- a/docs/reference/namespaces/itunes.md
+++ b/docs/reference/namespaces/itunes.md
@@ -2,7 +2,7 @@
title: "Reference: iTunes Namespace"
---
-# iTunes Namespace
+# iTunes Namespace Reference
The iTunes namespace provides podcast-specific metadata for RSS and Atom feeds. This namespace is essential for podcast distribution through Apple Podcasts and other podcast platforms.
@@ -36,6 +36,9 @@ The iTunes namespace provides podcast-specific metadata for RSS and Atom feeds.
## Types
+> [!INFO]
+> For details on type parameters (`TStrict`) and `Requirable` markers, see [TypeScript Reference](/reference/typescript#tstrict).
+
<<< @/../src/namespaces/itunes/common/types.ts#reference
## Related
diff --git a/docs/reference/namespaces/media.md b/docs/reference/namespaces/media.md
index cfc55f67..2d03c48d 100644
--- a/docs/reference/namespaces/media.md
+++ b/docs/reference/namespaces/media.md
@@ -2,7 +2,7 @@
title: "Reference: Media RSS Namespace"
---
-# Media RSS Namespace
+# Media RSS Namespace Reference
The Media RSS namespace provides rich media metadata for RSS feeds, enabling comprehensive description of multimedia content including videos, images, and audio files.
@@ -37,6 +37,9 @@ The Media RSS namespace provides rich media metadata for RSS feeds, enabling com
## Types
+> [!INFO]
+> For details on type parameters (`TStrict`) and `Requirable` markers, see [TypeScript Reference](/reference/typescript#tstrict).
+
<<< @/../src/namespaces/media/common/types.ts#reference
## Related
diff --git a/docs/reference/namespaces/opensearch.md b/docs/reference/namespaces/opensearch.md
index 8deeeb1b..896aad99 100644
--- a/docs/reference/namespaces/opensearch.md
+++ b/docs/reference/namespaces/opensearch.md
@@ -2,7 +2,7 @@
title: "Reference: OpenSearch Namespace"
---
-# OpenSearch Namespace
+# OpenSearch Namespace Reference
The OpenSearch namespace provides elements for communicating search metadata and pagination information in RSS and Atom feeds. It enables search engines and APIs to publish search results in standard syndication formats.
@@ -33,6 +33,9 @@ The OpenSearch namespace provides elements for communicating search metadata and
## Types
+> [!INFO]
+> For details on type parameters (`TStrict`) and `Requirable` markers, see [TypeScript Reference](/reference/typescript#tstrict).
+
<<< @/../src/namespaces/opensearch/common/types.ts#reference
## Related
diff --git a/docs/reference/namespaces/pingback.md b/docs/reference/namespaces/pingback.md
index d05326f3..b9bc298b 100644
--- a/docs/reference/namespaces/pingback.md
+++ b/docs/reference/namespaces/pingback.md
@@ -2,7 +2,7 @@
title: "Reference: Pingback Namespace"
---
-# Pingback Namespace
+# Pingback Namespace Reference
The Pingback namespace provides a mechanism for notifying websites when content references or links to them, enabling automatic trackback of linkages between web resources.
diff --git a/docs/reference/namespaces/podcast.md b/docs/reference/namespaces/podcast.md
index 969fd95e..9997292d 100644
--- a/docs/reference/namespaces/podcast.md
+++ b/docs/reference/namespaces/podcast.md
@@ -2,7 +2,7 @@
title: "Reference: Podcast Index Namespace"
---
-# Podcast Index Namespace
+# Podcast Index Namespace Reference
The Podcast Index namespace implements the Podcasting 2.0 specification, providing advanced features for modern podcasting including transcripts, chapters, value streaming, and enhanced metadata.
@@ -34,7 +34,7 @@ The Podcast Index namespace implements the Podcasting 2.0 specification, providi
## Types
> [!INFO]
-> `TDate` represents date fields in the type definitions. When **parsing**, dates are returned as strings in their original format (see [Parsing › Handling Dates](/parsing/dates) for more details). When **generating**, dates should be provided as JavaScript `Date` objects.
+> For details on type parameters (`TDate`, `TStrict`) and `Requirable` markers, see [TypeScript Reference](/reference/typescript#tdate).
<<< @/../src/namespaces/podcast/common/types.ts#reference
diff --git a/docs/reference/namespaces/prism.md b/docs/reference/namespaces/prism.md
index 0d0387e6..132969d2 100644
--- a/docs/reference/namespaces/prism.md
+++ b/docs/reference/namespaces/prism.md
@@ -2,7 +2,7 @@
title: "Reference: PRISM Namespace"
---
-# PRISM Namespace
+# PRISM Namespace Reference
The PRISM (Publishing Requirements for Industry Standard Metadata) namespace provides comprehensive metadata elements for scholarly and academic publishing, including bibliographic information, page ranges, DOIs, and publication details.
@@ -36,7 +36,7 @@ The PRISM (Publishing Requirements for Industry Standard Metadata) namespace pro
## Types
> [!INFO]
-> `TDate` represents date fields in the type definitions. When **parsing**, dates are returned as strings in their original format (see [Parsing › Handling Dates](/parsing/dates) for more details). When **generating**, dates should be provided as JavaScript `Date` objects.
+> For details on type parameters (`TDate`), see [TypeScript Reference](/reference/typescript#tdate).
<<< @/../src/namespaces/prism/common/types.ts#reference
diff --git a/docs/reference/namespaces/psc.md b/docs/reference/namespaces/psc.md
index a64f250f..86cf0064 100644
--- a/docs/reference/namespaces/psc.md
+++ b/docs/reference/namespaces/psc.md
@@ -2,7 +2,7 @@
title: "Reference: Podlove Simple Chapters Namespace"
---
-# Podlove Simple Chapters Namespace
+# Podlove Simple Chapters Namespace Reference
The Podlove Simple Chapters (PSC) namespace provides structured chapter information for podcasts and other media, allowing creators to define timed segments with titles, links, and images.
@@ -36,6 +36,9 @@ The Podlove Simple Chapters (PSC) namespace provides structured chapter informat
## Structure
+> [!INFO]
+> For details on type parameters (`TStrict`) and `Requirable` markers, see [TypeScript Reference](/reference/typescript#tstrict).
+
<<< @/../src/namespaces/psc/common/types.ts#reference
## Related
diff --git a/docs/reference/namespaces/rawvoice.md b/docs/reference/namespaces/rawvoice.md
index 34e5fc93..7675d356 100644
--- a/docs/reference/namespaces/rawvoice.md
+++ b/docs/reference/namespaces/rawvoice.md
@@ -2,7 +2,7 @@
title: "Reference: RawVoice Namespace"
---
-# RawVoice Namespace
+# RawVoice Namespace Reference
The RawVoice namespace provides elements for enhanced podcast and video content delivery, including live streaming, video formats, and episode metadata.
@@ -35,6 +35,9 @@ The RawVoice namespace provides elements for enhanced podcast and video content
## Types
+> [!INFO]
+> For details on type parameters (`TDate`, `TStrict`) and `Requirable` markers, see [TypeScript Reference](/reference/typescript#tdate).
+
<<< @/../src/namespaces/rawvoice/common/types.ts#reference
## Related
diff --git a/docs/reference/namespaces/rdf.md b/docs/reference/namespaces/rdf.md
index 7450f812..b3eba209 100644
--- a/docs/reference/namespaces/rdf.md
+++ b/docs/reference/namespaces/rdf.md
@@ -2,7 +2,7 @@
title: "Reference: RDF Namespace"
---
-# RDF Namespace
+# RDF Namespace Reference
Built-in namespace for RDF feeds exposing standard RDF metadata.
diff --git a/docs/reference/namespaces/slash.md b/docs/reference/namespaces/slash.md
index 5481c63f..49ad8165 100644
--- a/docs/reference/namespaces/slash.md
+++ b/docs/reference/namespaces/slash.md
@@ -2,7 +2,7 @@
title: "Reference: Slash Namespace"
---
-# Slash Namespace
+# Slash Namespace Reference
The Slash namespace provides metadata about user engagement, particularly comment counts. Originally created by Slashdot, it's now widely used to indicate discussion activity on feed items.
diff --git a/docs/reference/namespaces/source.md b/docs/reference/namespaces/source.md
index 487512c9..9121c9a3 100644
--- a/docs/reference/namespaces/source.md
+++ b/docs/reference/namespaces/source.md
@@ -2,7 +2,7 @@
title: "Reference: Source Namespace"
---
-# Source Namespace
+# Source Namespace Reference
The Source namespace provides elements for enhanced feed metadata, including social media accounts, subscription lists, blogrolls, and source content in various formats like Markdown and OPML outlines.
@@ -33,6 +33,9 @@ The Source namespace provides elements for enhanced feed metadata, including soc
## Types
+> [!INFO]
+> For details on type parameters (`TStrict`) and `Requirable` markers, see [TypeScript Reference](/reference/typescript#tstrict).
+
<<< @/../src/namespaces/source/common/types.ts#reference
## Related
diff --git a/docs/reference/namespaces/spotify.md b/docs/reference/namespaces/spotify.md
index 5f4d207d..5bb31999 100644
--- a/docs/reference/namespaces/spotify.md
+++ b/docs/reference/namespaces/spotify.md
@@ -2,7 +2,7 @@
title: "Reference: Spotify Namespace"
---
-# Spotify Namespace
+# Spotify Namespace Reference
The Spotify namespace provides podcast-specific metadata for Spotify's podcast platform, including episode limits and country targeting information.
@@ -33,6 +33,9 @@ The Spotify namespace provides podcast-specific metadata for Spotify's podcast p
## Types
+> [!INFO]
+> For details on type parameters (`TStrict`) and `Requirable` markers, see [TypeScript Reference](/reference/typescript#tstrict).
+
<<< @/../src/namespaces/spotify/common/types.ts#reference
## Related
diff --git a/docs/reference/namespaces/sy.md b/docs/reference/namespaces/sy.md
index c6bf0e30..b4e39d7c 100644
--- a/docs/reference/namespaces/sy.md
+++ b/docs/reference/namespaces/sy.md
@@ -2,7 +2,7 @@
title: "Reference: Syndication Namespace"
---
-# Syndication Namespace
+# Syndication Namespace Reference
The Syndication namespace provides information about the frequency and timing of feed updates. It helps aggregators understand how often to check for new content.
@@ -38,7 +38,7 @@ The Syndication namespace provides information about the frequency and timing of
## Types
> [!INFO]
-> `TDate` represents date fields in the type definitions. When **parsing**, dates are returned as strings in their original format (see [Parsing › Handling Dates](/parsing/dates) for more details). When **generating**, dates should be provided as JavaScript `Date` objects.
+> For details on type parameters (`TDate`), see [TypeScript Reference](/reference/typescript#tdate).
<<< @/../src/namespaces/sy/common/types.ts#reference
diff --git a/docs/reference/namespaces/thr.md b/docs/reference/namespaces/thr.md
index 4a60afaa..02db4eaa 100644
--- a/docs/reference/namespaces/thr.md
+++ b/docs/reference/namespaces/thr.md
@@ -2,7 +2,7 @@
title: "Reference: Atom Threading Namespace"
---
-# Atom Threading Namespace
+# Atom Threading Namespace Reference
The Atom Threading namespace provides elements for representing threaded discussions and comment relationships in Atom feeds, enabling proper conversation threading.
@@ -37,7 +37,7 @@ The Atom Threading namespace provides elements for representing threaded discuss
## Types
> [!INFO]
-> `TDate` represents date fields in the type definitions. When **parsing**, dates are returned as strings in their original format (see [Parsing › Handling Dates](/parsing/dates) for more details). When **generating**, dates should be provided as JavaScript `Date` objects.
+> For details on type parameters (`TDate`, `TStrict`) and `Requirable` markers, see [TypeScript Reference](/reference/typescript#tdate).
<<< @/../src/namespaces/thr/common/types.ts#reference
diff --git a/docs/reference/namespaces/trackback.md b/docs/reference/namespaces/trackback.md
index 62328c3b..3b9e3f77 100644
--- a/docs/reference/namespaces/trackback.md
+++ b/docs/reference/namespaces/trackback.md
@@ -2,7 +2,7 @@
title: "Reference: Trackback Namespace"
---
-# Trackback Namespace
+# Trackback Namespace Reference
The Trackback namespace enables peer-to-peer communication between web sites that publish related content. In its simplest form, trackback is a means of sending a message that lets a site know you've published a link to one of its pages.
diff --git a/docs/reference/namespaces/wfw.md b/docs/reference/namespaces/wfw.md
index e8cdee06..355bb929 100644
--- a/docs/reference/namespaces/wfw.md
+++ b/docs/reference/namespaces/wfw.md
@@ -2,7 +2,7 @@
title: "Reference: Comment API Namespace"
---
-# Comment API Namespace
+# Comment API Namespace Reference
The Comment API namespace provides elements for linking to comment feeds and comment posting interfaces, enabling better integration between feeds and commenting systems.
diff --git a/docs/reference/namespaces/xml.md b/docs/reference/namespaces/xml.md
new file mode 100644
index 00000000..82b98f8b
--- /dev/null
+++ b/docs/reference/namespaces/xml.md
@@ -0,0 +1,40 @@
+# XML Namespace Reference
+
+Supports `xml:lang`, `xml:base`, `xml:space`, and `xml:id` attributes at both feed and item/entry level in RSS, Atom, and RDF feeds.
+
+