import rssParser from "rss-parser"; import jsonata from "jsonata"; function passContext(ctx) { if (ctx?.context != null && typeof ctx.context === "object" && !Array.isArray(ctx.context)) { return { ...ctx.context }; } return {}; } function previewValue(value) { const json = JSON.stringify(value); if (json.length <= 500) { return value; } return { preview: `${json.slice(0, 500)}...`, truncated: true }; } async function fetchRssFeed(ctx) { const url = ctx.config?.url ?? ctx.data?.url; if (typeof url !== "string" || url.length === 0) { throw new Error("fetch-rss-feed: url is required (ctx.config.url or ctx.data.url)"); } const outputVar = ctx.config?.outputVar; const jsonataExpr = ctx.config?.jsonata; if (jsonataExpr && (typeof outputVar !== "string" || outputVar.length === 0)) { throw new Error("fetch-rss-feed: ctx.config.outputVar is required when jsonata is set"); } log.info({ url }, "fetch-rss-feed: fetching feed"); const parser = new rssParser({ customFields: { item: [ ["media:content", "mediaContent"], ["content:encoded", "contentEncoded"], ], }, }); const feed = await parser.parseURL(url); const itemCount = Array.isArray(feed.items) ? feed.items.length : 0; log.info( { title: feed.title, itemCount }, "fetch-rss-feed: fetch complete", ); /** @type {Record} */ const output = { rssFeed: feed }; log.info( { title: feed.title, itemCount }, "fetch-rss-feed: saved rssFeed", ); if (jsonataExpr) { log.info({ outputVar, jsonata: jsonataExpr }, "fetch-rss-feed: evaluating jsonata"); const expression = jsonata(jsonataExpr); const result = await expression.evaluate(feed); output[outputVar] = result; log.info( { outputVar, result: previewValue(result) }, "fetch-rss-feed: saved jsonata result", ); } return { output, context: { ...passContext(ctx), ...output } }; } fetchRssFeed.meta = { description: "Fetch an RSS/Atom feed and optionally transform it with JSONata", previewConfigKey: "url", tags: ["RSS"], config: { url: { type: "string", required: false, description: "Feed URL (or pass data.url)" }, outputVar: { type: "string", required: false, description: "Required when jsonata is set; key on output and context", }, jsonata: { type: "string", required: false, description: "JSONata applied to the parsed feed object", }, }, input: { url: { type: "string", required: false, description: "Feed URL when config.url is omitted" }, }, output: { rssFeed: { type: "object", description: "Parsed RSS/Atom feed from rss-parser" }, }, context: { rssFeed: { type: "object", description: "Same as output.rssFeed (plus outputVar when set)" }, }, example: { data: {}, config: { url: "https://selfh.st/rss/", outputVar: "item", jsonata: "items[0]", }, }, }; export default fetchRssFeed;