diff --git a/src/lib/rss.ts b/src/lib/rss.ts
new file mode 100644
index 0000000..7c092f5
--- /dev/null
+++ b/src/lib/rss.ts
@@ -0,0 +1,39 @@
+function esc(s: string) {
+ return s.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"')
+}
+
+export type RssItem = {
+ title: string
+ url: string
+ description?: string
+ publishDate: Date
+}
+
+export function buildRss(opts: {
+ title: string
+ description: string
+ siteUrl: string
+ items: RssItem[]
+}) {
+ const items = opts.items
+ .map(
+ (i) => `
+ -
+ ${esc(i.title)}
+ ${esc(opts.siteUrl + i.url)}
+ ${esc(opts.siteUrl + i.url)}
+ ${i.publishDate.toUTCString()}
+ ${i.description ? `${esc(i.description)}` : ''}
+
`,
+ )
+ .join('')
+ return `
+
+
+ ${esc(opts.title)}
+ ${esc(opts.siteUrl)}
+ ${esc(opts.description)}
+ ${items}
+
+`
+}
diff --git a/tests/int/rss.int.spec.ts b/tests/int/rss.int.spec.ts
new file mode 100644
index 0000000..e056cd0
--- /dev/null
+++ b/tests/int/rss.int.spec.ts
@@ -0,0 +1,19 @@
+import { describe, expect, it } from 'vitest'
+import { buildRss } from '@/lib/rss'
+
+describe('buildRss', () => {
+ it('produces a valid-looking RSS 2.0 doc', () => {
+ const xml = buildRss({
+ title: 'T',
+ description: 'D',
+ siteUrl: 'https://x.dev',
+ items: [
+ { title: 'A', url: '/a/', description: 'd', publishDate: new Date('2024-01-01') },
+ ],
+ })
+ expect(xml).toContain('T')
+ expect(xml).toContain('https://x.dev/a/')
+ expect(xml).toContain('Mon, 01 Jan 2024')
+ })
+})