UNPKG

@bliztek/feed-generator

Version:

A simple and lightweight Node.js library for generating RSS 2.0, Atom, and JSON Feed formats.

120 lines (119 loc) 5.15 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.validateFeed = exports.FeedValidationError = void 0; class FeedValidationError extends Error { constructor(field, reason) { super(`Feed validation error [${field}]: ${reason}`); this.name = "FeedValidationError"; this.field = field; this.reason = reason; } } exports.FeedValidationError = FeedValidationError; const isValidUrl = (value) => { try { new URL(value); return true; } catch (_a) { return false; } }; const isValidDate = (value) => { const d = value instanceof Date ? value : new Date(value); return !isNaN(d.getTime()); }; const validateFeed = (feed, format) => { var _a, _b, _c, _d; if (!feed.title) { throw new FeedValidationError("title", "Feed title is required."); } if (!feed.link) { throw new FeedValidationError("link", "Feed link is required."); } if (!isValidUrl(feed.link)) { throw new FeedValidationError("link", `Invalid URL: "${feed.link}".`); } if (!feed.items) { throw new FeedValidationError("items", "Feed items are required."); } if (format === "rss" && !feed.description) { throw new FeedValidationError("description", "Feed description is required for RSS feeds."); } if (feed.updated && !isValidDate(feed.updated)) { throw new FeedValidationError("updated", `Invalid date: "${feed.updated}".`); } if (feed.nextUrl && !isValidUrl(feed.nextUrl)) { throw new FeedValidationError("nextUrl", `Invalid URL: "${feed.nextUrl}".`); } if (feed.skipHours) { for (const h of feed.skipHours) { if (!Number.isInteger(h) || h < 0 || h > 23) { throw new FeedValidationError("skipHours", `Hour must be an integer 0-23, got ${h}.`); } } } if (feed.skipDays) { const validDays = new Set(["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]); for (const d of feed.skipDays) { if (!validDays.has(d)) { throw new FeedValidationError("skipDays", `Day must be a valid day name, got "${d}".`); } } } if (feed.ttl !== undefined && (!Number.isInteger(feed.ttl) || feed.ttl < 0)) { throw new FeedValidationError("ttl", `TTL must be a non-negative integer, got ${feed.ttl}.`); } if (((_a = feed.image) === null || _a === void 0 ? void 0 : _a.url) && !isValidUrl(feed.image.url)) { throw new FeedValidationError("image.url", `Invalid URL: "${feed.image.url}".`); } if (((_b = feed.feedLinks) === null || _b === void 0 ? void 0 : _b.hub) && !isValidUrl(feed.feedLinks.hub)) { throw new FeedValidationError("feedLinks.hub", `Invalid URL: "${feed.feedLinks.hub}".`); } if ((_c = feed.extensions) === null || _c === void 0 ? void 0 : _c.xmlNamespaces) { for (const key of Object.keys(feed.extensions.xmlNamespaces)) { if (!key.startsWith("xmlns:")) { throw new FeedValidationError("extensions.xmlNamespaces", `Namespace key must start with "xmlns:", got "${key}".`); } } } if ((_d = feed.extensions) === null || _d === void 0 ? void 0 : _d.jsonProperties) { for (const key of Object.keys(feed.extensions.jsonProperties)) { if (!key.startsWith("_")) { throw new FeedValidationError("extensions.jsonProperties", `JSON Feed extension keys must start with "_" per spec, got "${key}".`); } } } for (let i = 0; i < feed.items.length; i++) { const item = feed.items[i]; const prefix = `items[${i}]`; if (!item.id) { throw new FeedValidationError(`${prefix}.id`, "Each feed item must have an id."); } if (item.link && !isValidUrl(item.link)) { throw new FeedValidationError(`${prefix}.link`, `Invalid URL: "${item.link}".`); } if (item.date && !isValidDate(item.date)) { throw new FeedValidationError(`${prefix}.date`, `Invalid date: "${item.date}".`); } if (item.updated && !isValidDate(item.updated)) { throw new FeedValidationError(`${prefix}.updated`, `Invalid date: "${item.updated}".`); } if (item.image && !isValidUrl(item.image)) { throw new FeedValidationError(`${prefix}.image`, `Invalid URL: "${item.image}".`); } if (item.enclosures) { for (let j = 0; j < item.enclosures.length; j++) { const enc = item.enclosures[j]; const encPrefix = `${prefix}.enclosures[${j}]`; if (!isValidUrl(enc.url)) { throw new FeedValidationError(`${encPrefix}.url`, `Invalid URL: "${enc.url}".`); } if (format === "rss" && enc.length === undefined) { throw new FeedValidationError(`${encPrefix}.length`, "Enclosure length is required for RSS feeds."); } } } } }; exports.validateFeed = validateFeed;