@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
JavaScript
;
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;