@seedprotocol/feed 0.4.9 → 0.4.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/config.ts","../src/getFeedItems.ts","../src/rss/generateRssXml.ts","../src/utils/etag.ts","../src/cache/MemoryCache.ts","../src/cache/FileCache.ts","../src/cache/CacheManager.ts","../src/cache/config.ts","../src/services/arweaveImageService.ts","../src/index.ts"],"sourcesContent":["/**\n * Load feed configuration from environment variables\n *\n * Environment variables:\n * - FEED_ITEM_URL_BASE: Base URL for attestation links (e.g. https://optimism-sepolia.easscan.org or https://easscan.org).\n * When set, item links use {base}/attestation/view/{uid}. Default: 'https://easscan.org'. Set to override.\n * - FEED_ITEM_URL_PATH: Path segment for attestation links (default: 'attestation/view').\n * Only used when FEED_ITEM_URL_BASE is set.\n * - FEED_SITE_URL: Site URL for fallback when FEED_ITEM_URL_BASE is unset (default: 'https://seedprotocol.io').\n */\nexport function loadFeedConfig(): {\n itemUrlBase: string;\n itemUrlPath: string;\n siteUrl: string;\n} {\n const itemUrlBase = process.env.FEED_ITEM_URL_BASE?.trim() || 'https://optimism-sepolia.easscan.org';\n const itemUrlPath =\n process.env.FEED_ITEM_URL_PATH?.trim() || 'attestation/view';\n const siteUrl =\n process.env.FEED_SITE_URL?.trim() || 'https://seedprotocol.io';\n\n return { itemUrlBase, itemUrlPath, siteUrl };\n}\n","import {\n getSeedsBySchemaName,\n getItemVersionsFromEas,\n getItemPropertiesFromEas,\n EasClient,\n setSchemaUidForSchemaDefinition,\n getArweaveUrlForTransaction,\n} from '@seedprotocol/sdk';\nimport { gql } from 'graphql-request';\nimport { loadFeedConfig } from './config';\n\nconst relationValuesToExclude = [\n '0x0000000000000000000000000000000000000000000000000000000000000020',\n];\n\nconst GET_SEEDS = gql`\n query GetSeeds($where: AttestationWhereInput!, $take: Int) {\n itemSeeds: attestations(where: $where, orderBy: [{ timeCreated: desc }], take: $take) {\n id\n decodedDataJson\n attester\n schema {\n schemaNames {\n name\n }\n }\n refUID\n revoked\n schemaId\n timeCreated\n isOffchain\n }\n }\n`;\n\ninterface AttestationLike {\n id: string;\n decodedDataJson: string;\n refUID: string;\n schemaId: string;\n timeCreated: number;\n attester?: string;\n schema?: { schemaNames?: Array<{ name: string }> };\n}\n\ntype SetFeedItemDefaultsOptions = {\n itemUrlBase?: string;\n itemUrlPath: string;\n siteUrl: string;\n};\n\n// Helper to convert snake_case to camelCase\nconst toCamelCase = (str: string): string => {\n return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\n};\n\n// Helper to format timestamp as RFC 822 date string for RSS pubDate\nconst formatRfc822Date = (timestamp: number): string => {\n const date = new Date(timestamp * 1000);\n const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];\n const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n\n const day = days[date.getUTCDay()];\n const month = months[date.getUTCMonth()];\n const year = date.getUTCFullYear();\n const dayNum = date.getUTCDate().toString().padStart(2, '0');\n const hours = date.getUTCHours().toString().padStart(2, '0');\n const minutes = date.getUTCMinutes().toString().padStart(2, '0');\n const seconds = date.getUTCSeconds().toString().padStart(2, '0');\n\n return `${day}, ${dayNum} ${month} ${year} ${hours}:${minutes}:${seconds} GMT`;\n};\n\nconst getCollectionPath = (schemaName: string): string => {\n return schemaName === 'image' ? 'images' : schemaName === 'post' ? 'posts' : schemaName.toLowerCase() + 's';\n};\n\n// Inlined from SDK - parses relation property names like \"author_id\" -> { propertyName: \"authors\", modelName: \"author\", isList: false }\nconst parseEasRelationPropertyName = (easPropertyName: string): { propertyName: string; modelName: string; isList: boolean } | null => {\n const [singularProperty, modelName, idSegment] = easPropertyName.split('_');\n if (!singularProperty || !modelName) return null;\n const isList = idSegment === 'ids';\n const propertyName = singularProperty.endsWith('s') ? singularProperty : singularProperty + 's';\n return { propertyName, modelName, isList };\n};\n\n// Helper to set default values for feed items\nconst setFeedItemDefaults = (\n item: Record<string, unknown>,\n seedUid: string,\n schemaName: string,\n options: SetFeedItemDefaultsOptions\n): void => {\n const { itemUrlBase, itemUrlPath, siteUrl } = options;\n\n // Set default title if not present (check both snake_case and camelCase)\n if (!item.title && !item.Title) {\n item.title = seedUid;\n item.Title = seedUid;\n } else if (item.title && !item.Title) {\n item.Title = item.title;\n } else if (item.Title && !item.title) {\n item.title = item.Title;\n }\n\n const storageTransactionIdSnake =\n item.storage_transaction_id &&\n typeof item.storage_transaction_id === 'string' &&\n item.storage_transaction_id.trim() !== '' &&\n item.storage_transaction_id !== 'undefined' &&\n item.storage_transaction_id !== seedUid\n ? (item.storage_transaction_id as string).trim()\n : null;\n const storageTransactionIdCamel =\n item.storageTransactionId &&\n typeof item.storageTransactionId === 'string' &&\n item.storageTransactionId.trim() !== '' &&\n item.storageTransactionId !== 'undefined' &&\n item.storageTransactionId !== seedUid\n ? (item.storageTransactionId as string).trim()\n : null;\n const storageTransactionId = storageTransactionIdSnake || storageTransactionIdCamel;\n\n const collectionPath = getCollectionPath(schemaName);\n const validSeedUid =\n seedUid && typeof seedUid === 'string' && seedUid.trim() !== '' ? seedUid : 'unknown';\n\n let defaultLink: string;\n if (storageTransactionId && storageTransactionId !== seedUid && storageTransactionId.length > 0) {\n try {\n defaultLink = getArweaveUrlForTransaction(storageTransactionId);\n } catch (error) {\n console.error('[feed] [setFeedItemDefaults] Error generating Arweave URL:', error);\n if (itemUrlBase != null) {\n defaultLink = `${itemUrlBase.replace(/\\/$/, '')}/${(itemUrlPath ?? 'attestation/view').replace(/^\\//, '')}/${validSeedUid}`;\n } else {\n defaultLink = `${siteUrl.replace(/\\/$/, '')}/${collectionPath}/${validSeedUid}`;\n }\n }\n } else {\n if (itemUrlBase != null) {\n defaultLink = `${itemUrlBase.replace(/\\/$/, '')}/${(itemUrlPath ?? 'attestation/view').replace(/^\\//, '')}/${validSeedUid}`;\n } else {\n defaultLink = `${siteUrl.replace(/\\/$/, '')}/${collectionPath}/${validSeedUid}`;\n }\n }\n\n const currentLink = (item.link || item.Link) as string | undefined;\n if (!currentLink || currentLink === 'undefined' || (typeof currentLink === 'string' && currentLink.trim() === '')) {\n item.link = defaultLink;\n item.Link = defaultLink;\n } else {\n if (!item.link || item.link === 'undefined') {\n item.link = currentLink;\n }\n if (!item.Link || item.Link === 'undefined') {\n item.Link = currentLink;\n }\n }\n\n const currentGuid = (item.guid || item.Guid) as string | undefined;\n if (!currentGuid || currentGuid === 'undefined' || (typeof currentGuid === 'string' && currentGuid.trim() === '')) {\n item.guid = item.link || defaultLink;\n item.Guid = item.guid;\n } else {\n if (!item.guid || item.guid === 'undefined') {\n item.guid = currentGuid;\n }\n if (!item.Guid || item.Guid === 'undefined') {\n item.Guid = currentGuid;\n }\n }\n\n if (item.timeCreated && !item.pubDate && !item.PubDate) {\n const pubDate = formatRfc822Date(item.timeCreated as number);\n item.pubDate = pubDate;\n item.PubDate = pubDate;\n } else if (item.pubDate && !item.PubDate) {\n item.PubDate = item.pubDate;\n } else if (item.PubDate && !item.pubDate) {\n item.pubDate = item.PubDate;\n }\n\n if (!item.seedUid && !item.SeedUid) {\n item.seedUid = seedUid;\n item.SeedUid = seedUid;\n } else if (item.seedUid && !item.SeedUid) {\n item.SeedUid = item.seedUid;\n } else if (item.SeedUid && !item.seedUid) {\n item.seedUid = item.SeedUid;\n }\n\n if (item.attester && !item.Attester) {\n item.Attester = item.attester;\n } else if (item.Attester && !item.attester) {\n item.attester = item.Attester;\n }\n};\n\nconst seedUidToModelType = new Map<string, string>();\nconst relatedSeedUids = new Set<string>();\nconst versionUidToSeedUid = new Map<string, string>();\nconst assembledFeedItems = new Map<string, Record<string, unknown>>();\nconst versionsBySeedUid = new Map<string, AttestationLike[]>();\nconst latestVersionUidsBySeedUid = new Map<string, string>();\n\nconst processItemProperty = async (\n property: AttestationLike,\n itemSeeds: AttestationLike[]\n): Promise<void> => {\n let metadata: { name: string; value: string | string[]; type?: string };\n try {\n metadata = JSON.parse(property.decodedDataJson)[0]?.value ?? {};\n } catch (error) {\n console.error('[feed] [processItemProperty] Error parsing metadata:', error);\n return;\n }\n\n let propertyNameSnake = metadata.name;\n if (!propertyNameSnake) {\n return;\n }\n\n const schemaUid = property.schemaId;\n setSchemaUidForSchemaDefinition({\n text: propertyNameSnake,\n schemaUid,\n });\n\n let isRelation = false;\n let isList = false;\n const easType = metadata.type;\n const isBytes32Relation =\n (easType === 'bytes32' || easType === 'bytes32[]') &&\n propertyNameSnake !== 'storage_transaction_id' &&\n propertyNameSnake !== 'storage_provider_transaction_id';\n const isNamingConventionRelation =\n !isBytes32Relation &&\n (propertyNameSnake.endsWith('_id') || propertyNameSnake.endsWith('_ids')) &&\n propertyNameSnake !== 'storage_transaction_id' &&\n propertyNameSnake !== 'storage_provider_transaction_id';\n\n if (isBytes32Relation || isNamingConventionRelation) {\n isRelation = true;\n if (Array.isArray(metadata.value)) {\n isList = true;\n if (isNamingConventionRelation) {\n const result = parseEasRelationPropertyName(propertyNameSnake);\n if (result) {\n propertyNameSnake = result.propertyName;\n }\n }\n metadata.value.forEach((value: string) => {\n if (!relationValuesToExclude.includes(value)) relatedSeedUids.add(value);\n });\n } else if (!relationValuesToExclude.includes(metadata.value as string)) {\n relatedSeedUids.add(metadata.value as string);\n }\n }\n\n let propertyValue = metadata.value;\n if (typeof propertyValue !== 'string') {\n propertyValue = JSON.stringify(propertyValue);\n }\n\n if (isRelation && !isList) {\n const relatedSeed = itemSeeds.find((seed) => seed.id === metadata.value);\n if (relatedSeed?.schema?.schemaNames) {\n // refSeedType and refSchemaUid used for type resolution - not needed for feed assembly\n }\n }\n\n if (isRelation && isList) {\n const relatedSeeds = itemSeeds.filter((seed) =>\n Array.isArray(metadata.value) && metadata.value.includes(seed.id)\n );\n if (relatedSeeds.length > 0) {\n // refSeedType and refSchemaUid used for type resolution - not needed for feed assembly\n }\n }\n\n const seedUidForProperty = versionUidToSeedUid.get(property.refUID);\n if (!seedUidForProperty) {\n return;\n }\n\n const existingFeedItem = assembledFeedItems.get(seedUidForProperty) || {};\n existingFeedItem[propertyNameSnake] = propertyValue;\n const propertyNameCamel = toCamelCase(propertyNameSnake);\n if (propertyNameCamel !== propertyNameSnake) {\n existingFeedItem[propertyNameCamel] = propertyValue;\n }\n assembledFeedItems.set(seedUidForProperty, existingFeedItem);\n};\n\nconst processSeeds = async (seeds: AttestationLike[]): Promise<void> => {\n const seedUids = seeds.map((seed) => seed.id);\n\n for (let i = 0; i < seeds.length; i++) {\n const seed = seeds[i];\n if (!seed) continue;\n seedUids.push(seed.id);\n const modelType = seed.schema?.schemaNames?.[0]?.name ?? 'unknown';\n seedUidToModelType.set(seed.id, modelType);\n\n if (!assembledFeedItems.has(seed.id)) {\n assembledFeedItems.set(seed.id, {\n seedUid: seed.id,\n timeCreated: seed.timeCreated,\n attester: seed.attester,\n });\n }\n }\n\n const itemVersions = await getItemVersionsFromEas({ seedUids });\n\n for (let i = 0; i < itemVersions.length; i++) {\n const itemVersion = itemVersions[i] as AttestationLike;\n const seedUid = itemVersion.refUID;\n versionUidToSeedUid.set(itemVersion.id, seedUid);\n const existingVersions = versionsBySeedUid.get(seedUid) || [];\n versionsBySeedUid.set(seedUid, [...existingVersions, itemVersion]);\n }\n\n const latestVersionUids: string[] = [];\n for (const [seedUid, versions] of versionsBySeedUid.entries()) {\n const sortedVersions = [...versions].sort((a, b) => b.timeCreated - a.timeCreated);\n const latestVersion = sortedVersions[0];\n if (latestVersion) {\n latestVersionUids.push(latestVersion.id);\n latestVersionUidsBySeedUid.set(seedUid, latestVersion.id);\n }\n }\n\n const itemProperties = await getItemPropertiesFromEas({ versionUids: latestVersionUids });\n\n for (let i = 0; i < itemProperties.length; i++) {\n await processItemProperty(itemProperties[i] as AttestationLike, seeds);\n }\n};\n\nconst RESERVED_KEYS = new Set([\n 'seedUid',\n 'SeedUid',\n 'timeCreated',\n 'attester',\n 'Attester',\n 'storage_transaction_id',\n 'storage_provider_transaction_id',\n 'storageTransactionId',\n 'storageProviderTransactionId',\n]);\n\n/**\n * Resolves relation properties to Arweave URLs when the related item has a\n * storageTransactionId. Detects relations by value-in-assembledFeedItems\n * (value is an attestation UID we fetched). Supports optional _id/_ids key\n * normalization for backward compatibility.\n */\nfunction resolveRelationPropertiesToUrls(schemaName: string): void {\n const itemsToProcess = Array.from(assembledFeedItems.entries()).filter(\n ([seedUid]) => seedUidToModelType.get(seedUid) === schemaName\n );\n\n for (const [, item] of itemsToProcess) {\n const keysToProcess = Object.keys(item).filter(\n (k) => !k.startsWith('_') && !RESERVED_KEYS.has(k)\n );\n\n for (const key of keysToProcess) {\n const value = item[key];\n const isList = Array.isArray(value);\n\n if (isList) {\n const uids = value as unknown[];\n const hasRelationUid = uids.some(\n (v) =>\n typeof v === 'string' &&\n !relationValuesToExclude.includes(v) &&\n assembledFeedItems.has(v)\n );\n if (!hasRelationUid) continue;\n\n const urls: string[] = [];\n let resolved = false;\n for (const uid of uids) {\n if (typeof uid !== 'string' || relationValuesToExclude.includes(uid)) {\n urls.push(String(uid));\n continue;\n }\n const related = assembledFeedItems.get(uid);\n const txId =\n (related?.storageTransactionId ?? related?.storage_transaction_id) as string | undefined;\n if (txId && typeof txId === 'string' && txId.trim()) {\n try {\n urls.push(getArweaveUrlForTransaction(txId));\n resolved = true;\n } catch {\n urls.push(uid);\n }\n } else {\n urls.push(uid);\n }\n }\n if (resolved) {\n const outputKey = key.endsWith('_ids') ? key.replace(/_ids$/, '') : key;\n item[outputKey] = urls;\n if (outputKey !== key) {\n delete item[key];\n const camelKey = toCamelCase(key);\n if (camelKey !== key) delete item[camelKey];\n }\n }\n } else if (typeof value === 'string') {\n if (relationValuesToExclude.includes(value)) continue;\n if (!assembledFeedItems.has(value)) continue;\n\n const related = assembledFeedItems.get(value);\n const txId =\n (related?.storageTransactionId ?? related?.storage_transaction_id) as string | undefined;\n if (txId && typeof txId === 'string' && txId.trim()) {\n try {\n const outputKey = key.endsWith('_id') ? key.replace(/_id$/, '') : key;\n item[outputKey] = getArweaveUrlForTransaction(txId);\n if (outputKey !== key) {\n delete item[key];\n const camelKey = toCamelCase(key);\n if (camelKey !== key) delete item[camelKey];\n }\n } catch {\n // keep original on error\n }\n }\n }\n }\n }\n}\n\nexport const getFeedItemsBySchemaName = async (schemaName: string): Promise<Record<string, unknown>[]> => {\n const feedConfig = loadFeedConfig();\n const easClient = EasClient.getEasClient();\n\n const seeds = (await getSeedsBySchemaName(schemaName)) as AttestationLike[];\n await processSeeds(seeds);\n\n const relatedSeedUidsArray = Array.from(relatedSeedUids);\n const { itemSeeds: relatedSeeds } = await easClient.request(GET_SEEDS, {\n where: {\n id: {\n in: relatedSeedUidsArray,\n },\n },\n });\n\n await processSeeds((relatedSeeds ?? []) as AttestationLike[]);\n\n resolveRelationPropertiesToUrls(schemaName);\n\n const setFeedItemDefaultsOptions: SetFeedItemDefaultsOptions = {\n itemUrlBase: feedConfig.itemUrlBase,\n itemUrlPath: feedConfig.itemUrlPath,\n siteUrl: feedConfig.siteUrl,\n };\n\n const feedItems = Array.from(assembledFeedItems.entries())\n .filter(([seedUid]) => seedUidToModelType.get(seedUid) === schemaName)\n .map(([, item]) => {\n const seedUid = (item.seedUid || item.SeedUid) as string;\n setFeedItemDefaults(item, seedUid, schemaName, setFeedItemDefaultsOptions);\n return item;\n });\n\n return feedItems;\n};\n","import { create } from 'xmlbuilder2'\nimport type { XMLBuilder } from 'xmlbuilder2/lib/interfaces'\n\nconst DC_NS = 'http://purl.org/dc/elements/1.1/'\nconst CONTENT_NS = 'http://purl.org/rss/1.0/modules/content/'\nconst MEDIA_NS = 'http://search.yahoo.com/mrss/'\n\nconst CAPITALIZED_DUPLICATES = new Set(['Title', 'Link', 'Guid', 'PubDate', 'SeedUid'])\n\nfunction isRedundantCapitalizedKey(key: string, item: Record<string, unknown>): boolean {\n if (!CAPITALIZED_DUPLICATES.has(key)) return false\n const lowerKey = key.charAt(0).toLowerCase() + key.slice(1)\n return lowerKey in item\n}\n\nconst RSS_ITEM_ORDER = [\n 'title',\n 'link',\n 'description',\n 'guid',\n 'pubDate',\n 'authors',\n 'categories',\n 'comments',\n 'enclosures',\n 'source',\n 'dc',\n 'content:encoded',\n 'media:content',\n 'media:thumbnail',\n 'media:title',\n 'media:description',\n 'media:keywords',\n 'media:credit',\n 'media:copyright',\n 'media:category',\n 'media:group',\n]\n\nfunction formatRfc822Date(date: Date | string): string {\n if (date instanceof Date) {\n return date.toUTCString()\n }\n if (typeof date === 'string') {\n const d = new Date(date)\n return !Number.isNaN(d.getTime()) ? d.toUTCString() : date\n }\n return String(date)\n}\n\nfunction needsCdata(value: string): boolean {\n return /[<>&]|]]>/.test(value)\n}\n\nfunction addTextOrCdata(parent: XMLBuilder, value: string): void {\n const str = String(value)\n if (str === '') return\n\n if (needsCdata(str)) {\n if (str.includes(']]>')) {\n const parts = str.split(']]>')\n for (let i = 0; i < parts.length; i++) {\n parent.dat(parts[i]!)\n if (i < parts.length - 1) {\n parent.txt(']]>')\n }\n }\n } else {\n parent.dat(str)\n }\n } else {\n parent.txt(str)\n }\n}\n\nfunction serializeItemProperty(\n itemParent: XMLBuilder,\n key: string,\n value: unknown,\n item: Record<string, unknown>\n): void {\n if (value === undefined || value === null || key.startsWith('_')) return\n if (key === 'items') return\n\n if (key === 'guid') {\n const guid = value as { value?: string; isPermaLink?: boolean }\n const val = guid?.value ?? String(value)\n if (!val) return\n const attrs: Record<string, string> = {}\n if (typeof guid?.isPermaLink === 'boolean') {\n attrs.isPermaLink = guid.isPermaLink ? 'true' : 'false'\n }\n const guidEle = itemParent.ele('guid', attrs)\n addTextOrCdata(guidEle, val)\n return\n }\n\n if (key === 'source' && value !== null && typeof value === 'object') {\n const src = value as { title?: string; url?: string }\n const title = src.title ?? String(value)\n if (!title) return\n const attrs = src.url ? { url: src.url } : {}\n itemParent.ele('source', attrs).txt(title)\n return\n }\n\n if (key === 'enclosures' && Array.isArray(value)) {\n for (const enc of value) {\n if (enc && typeof enc === 'object' && 'url' in enc) {\n const attrs: Record<string, string> = {\n url: String(enc.url),\n type: String(enc.type ?? 'application/octet-stream'),\n }\n if (enc.length != null) attrs.length = String(enc.length)\n itemParent.ele('enclosure', attrs)\n }\n }\n return\n }\n\n if (key === 'dc' && value !== null && typeof value === 'object') {\n const dc = value as Record<string, unknown>\n for (const [dcKey, dcVal] of Object.entries(dc)) {\n if (dcVal === undefined || dcVal === null) continue\n if (Array.isArray(dcVal)) {\n for (const v of dcVal) {\n if (v !== undefined && v !== null) {\n const ele = itemParent.ele(DC_NS, dcKey)\n if (v instanceof Date) {\n ele.txt(v.toISOString())\n } else {\n addTextOrCdata(ele, String(v))\n }\n }\n }\n } else {\n const ele = itemParent.ele(DC_NS, dcKey)\n if (dcVal instanceof Date) {\n ele.txt(dcVal.toISOString())\n } else {\n addTextOrCdata(ele, String(dcVal))\n }\n }\n }\n return\n }\n\n if (key === 'content:encoded') {\n const ele = itemParent.ele(CONTENT_NS, 'encoded')\n addTextOrCdata(ele, String(value))\n return\n }\n if (key === 'content' && typeof value === 'string' && !('content:encoded' in item)) {\n const ele = itemParent.ele(CONTENT_NS, 'encoded')\n addTextOrCdata(ele, String(value))\n return\n }\n if (key === 'content') return\n\n if (key.startsWith('media:') && Array.isArray(value)) {\n const localName = key.slice(6)\n for (const item of value) {\n if (item === null || typeof item !== 'object') continue\n const attrs: Record<string, string> = {}\n let textContent: string | null = null\n for (const [attr, attrVal] of Object.entries(item)) {\n if (attrVal === undefined || attrVal === null) continue\n if (attr === 'value') {\n textContent = String(attrVal)\n } else {\n attrs[attr] = String(attrVal)\n }\n }\n const mediaEle = itemParent.ele(MEDIA_NS, localName, attrs)\n if (textContent !== null) {\n addTextOrCdata(mediaEle, textContent)\n }\n }\n return\n }\n\n if (key.startsWith('media:') && typeof value === 'string') {\n const localName = key.slice(6)\n const ele = itemParent.ele(MEDIA_NS, localName)\n addTextOrCdata(ele, value)\n return\n }\n\n if (key === 'media:group' && Array.isArray(value)) {\n for (const group of value) {\n if (group && typeof group === 'object') {\n const groupEle = itemParent.ele(MEDIA_NS, 'group')\n for (const [gk, gv] of Object.entries(group)) {\n if (gv === undefined || gv === null) continue\n if (gk.startsWith('media:')) {\n const ln = gk.slice(6)\n if (Array.isArray(gv)) {\n for (const v of gv) {\n if (v && typeof v === 'object') {\n const child = groupEle.ele(MEDIA_NS, ln)\n for (const [ck, cv] of Object.entries(v)) {\n if (cv !== undefined && cv !== null) {\n if (ck === 'url' || ck === 'value') {\n addTextOrCdata(child, String(cv))\n } else {\n child.att(ck, String(cv))\n }\n }\n }\n }\n }\n } else if (typeof gv === 'string') {\n addTextOrCdata(groupEle.ele(MEDIA_NS, ln), gv)\n }\n }\n }\n }\n }\n return\n }\n\n if (value instanceof Date) {\n const ele = itemParent.ele(key)\n ele.txt(formatRfc822Date(value))\n return\n }\n\n if (key === 'authors' && Array.isArray(value)) {\n for (const a of value) {\n const authorStr =\n typeof a === 'string'\n ? a\n : a && typeof a === 'object' && 'name' in a\n ? `${(a as { email?: string }).email ?? ''} (${(a as { name?: string }).name ?? ''})`.trim() ||\n (a as { name?: string }).name\n : String(a)\n if (authorStr) itemParent.ele('author').txt(authorStr)\n }\n return\n }\n if (key === 'categories' && Array.isArray(value)) {\n for (const c of value) {\n const cat =\n c && typeof c === 'object' && 'name' in c ? (c as { name: string }).name : String(c)\n if (cat) {\n const attrs =\n c && typeof c === 'object' && 'domain' in c && (c as { domain?: string }).domain\n ? { domain: (c as { domain: string }).domain }\n : {}\n itemParent.ele('category', attrs).txt(cat)\n }\n }\n return\n }\n\n // Array of strings (e.g. resolved relation URLs for list relations like images)\n if (Array.isArray(value) && value.every((v) => typeof v === 'string')) {\n const tagName = key.endsWith('s') ? key.slice(0, -1) : key\n for (const v of value) {\n if (v) {\n const ele = itemParent.ele(tagName)\n addTextOrCdata(ele, v)\n }\n }\n return\n }\n\n const ele = itemParent.ele(key)\n addTextOrCdata(ele, String(value))\n}\n\nexport interface RssFeedInput {\n title: string\n link?: string\n description: string\n language?: string\n copyright?: string\n webMaster?: string\n pubDate?: Date\n lastBuildDate?: Date\n items: Record<string, unknown>[]\n}\n\nexport function generateRssXml(rssFeed: RssFeedInput): string {\n const doc = create({ version: '1.0', encoding: 'UTF-8' })\n const rss = doc.ele('rss', { version: '2.0' })\n const channel = rss.ele('channel', {\n 'xmlns:dc': DC_NS,\n 'xmlns:content': CONTENT_NS,\n 'xmlns:media': MEDIA_NS,\n })\n\n channel.ele('title').txt(rssFeed.title)\n if (rssFeed.link) channel.ele('link').txt(rssFeed.link)\n channel.ele('description').txt(rssFeed.description)\n if (rssFeed.language) channel.ele('language').txt(rssFeed.language)\n if (rssFeed.copyright) channel.ele('copyright').txt(rssFeed.copyright)\n if (rssFeed.webMaster) channel.ele('webMaster').txt(rssFeed.webMaster)\n if (rssFeed.pubDate) channel.ele('pubDate').txt(formatRfc822Date(rssFeed.pubDate))\n if (rssFeed.lastBuildDate) channel.ele('lastBuildDate').txt(formatRfc822Date(rssFeed.lastBuildDate))\n\n const orderedSet = new Set(RSS_ITEM_ORDER)\n\n for (const item of rssFeed.items) {\n const itemParent = channel.ele('item')\n const processedKeys = new Set<string>()\n\n for (const key of RSS_ITEM_ORDER) {\n if (key in item) {\n serializeItemProperty(itemParent, key, item[key], item)\n processedKeys.add(key)\n }\n }\n const otherKeys = Object.keys(item)\n .filter(\n (k) =>\n !processedKeys.has(k) &&\n !orderedSet.has(k) &&\n !k.startsWith('_') &&\n !isRedundantCapitalizedKey(k, item)\n )\n .sort()\n for (const key of otherKeys) {\n serializeItemProperty(itemParent, key, item[key], item)\n }\n }\n\n return doc.end({ prettyPrint: true })\n}\n","import { createHash } from 'crypto';\n\n/**\n * Generate an ETag from a string value\n * Uses MD5 hash for consistency and performance\n */\nexport function generateETag(value: string): string {\n const hash = createHash('md5').update(value).digest('hex');\n return `\"${hash.substring(0, 16)}\"`;\n}\n\n/**\n * Generate an ETag for feed data based on schema, format, timestamp, and item count\n */\nexport function generateFeedETag(\n schemaName: string,\n format: string,\n lastProcessedTimestamp: number,\n itemCount: number\n): string {\n const etagValue = `${schemaName}-${format}-${lastProcessedTimestamp}-${itemCount}`;\n return generateETag(etagValue);\n}\n\n/**\n * Generate an ETag for cached feed content\n */\nexport function generateContentETag(\n schemaName: string,\n format: string,\n lastModified: number,\n contentLength: number\n): string {\n const etagValue = `${schemaName}-${format}-${lastModified}-${contentLength}`;\n return generateETag(etagValue);\n}\n\n/**\n * Check if an ETag matches the provided value\n * Handles both quoted and unquoted ETags\n */\nexport function etagMatches(etag1: string, etag2: string): boolean {\n // Remove quotes if present\n const normalize = (etag: string) => etag.replace(/^\"|\"$/g, '');\n return normalize(etag1) === normalize(etag2);\n}\n\n/**\n * Parse If-None-Match header value\n * Returns array of ETags (can be comma-separated)\n */\nexport function parseIfNoneMatch(headerValue: string | null | undefined): string[] {\n if (!headerValue) {\n return [];\n }\n return headerValue\n .split(',')\n .map(etag => etag.trim())\n .filter(etag => etag.length > 0);\n}\n\n/**\n * Check if any of the provided ETags match the current ETag\n */\nexport function checkIfNoneMatch(\n ifNoneMatchHeader: string | null | undefined,\n currentETag: string\n): boolean {\n const etags = parseIfNoneMatch(ifNoneMatchHeader);\n if (etags.length === 0) {\n return false;\n }\n \n // Check for wildcard\n if (etags.includes('*')) {\n return true;\n }\n \n // Check if any ETag matches\n return etags.some(etag => etagMatches(etag, currentETag));\n}\n","import type { CachedFeedData, CachedFeedContent, CacheConfig, CachedImageMetadata } from './types';\nimport type { GraphQLItem, ImageMetadata } from '../types';\nimport { generateFeedETag, generateContentETag } from '../utils/etag';\n\n/**\n * In-memory cache implementation with TTL support\n */\nexport class MemoryCache {\n private feedDataCache: Map<string, CachedFeedData> = new Map();\n private feedContentCache: Map<string, CachedFeedContent> = new Map();\n private imageMetadataCache: Map<string, CachedImageMetadata> = new Map();\n private config: CacheConfig;\n private refreshLocks: Map<string, Promise<void>> = new Map();\n\n constructor(config: CacheConfig) {\n this.config = config;\n }\n\n /**\n * Get cached feed data for a schema\n */\n getFeedData(schemaName: string): CachedFeedData | null {\n const cached = this.feedDataCache.get(schemaName);\n if (!cached) {\n return null;\n }\n\n // Check if cache is expired\n const now = Math.floor(Date.now() / 1000);\n const age = now - cached.lastUpdated;\n if (age > this.config.ttl) {\n this.feedDataCache.delete(schemaName);\n // Also clear related content cache\n this.clearContentCache(schemaName);\n return null;\n }\n\n return cached;\n }\n\n /**\n * Set cached feed data for a schema\n */\n setFeedData(schemaName: string, items: GraphQLItem[]): void {\n const now = Math.floor(Date.now() / 1000);\n \n // Find the newest item by timeCreated\n let lastProcessedTimestamp = 0;\n let lastProcessedItemId = '';\n \n for (const item of items) {\n const timeCreated = (item as any).timeCreated;\n if (timeCreated && timeCreated > lastProcessedTimestamp) {\n lastProcessedTimestamp = timeCreated;\n lastProcessedItemId = item.id || (item as any).seedUid || (item as any).SeedUid || '';\n }\n }\n\n // If no timeCreated found, use current time\n if (lastProcessedTimestamp === 0) {\n lastProcessedTimestamp = now;\n }\n\n const etag = generateFeedETag(schemaName, 'data', lastProcessedTimestamp, items.length);\n\n const cachedData: CachedFeedData = {\n items: [...items], // Create a copy\n lastProcessedTimestamp,\n lastProcessedItemId,\n lastUpdated: now,\n etag,\n };\n\n this.feedDataCache.set(schemaName, cachedData);\n }\n\n /**\n * Get cached feed content for a schema and format\n */\n getFeedContent(schemaName: string, format: string): CachedFeedContent | null {\n const key = `${schemaName}:${format}`;\n const cached = this.feedContentCache.get(key);\n if (!cached) {\n return null;\n }\n\n // Check if cache is expired\n const now = Math.floor(Date.now() / 1000);\n if (now > cached.expiresAt) {\n this.feedContentCache.delete(key);\n return null;\n }\n\n return cached;\n }\n\n /**\n * Set cached feed content for a schema and format\n */\n setFeedContent(\n schemaName: string,\n format: string,\n content: string,\n contentType: string\n ): void {\n const key = `${schemaName}:${format}`;\n const now = Math.floor(Date.now() / 1000);\n const expiresAt = now + this.config.ttl;\n\n const etag = generateContentETag(schemaName, format, now, content.length);\n\n const cachedContent: CachedFeedContent = {\n content,\n contentType,\n etag,\n lastModified: now,\n expiresAt,\n };\n\n this.feedContentCache.set(key, cachedContent);\n }\n\n /**\n * Clear feed data cache for a schema\n */\n clearFeedData(schemaName: string): void {\n this.feedDataCache.delete(schemaName);\n }\n\n /**\n * Clear content cache for a schema (all formats)\n */\n clearContentCache(schemaName: string): void {\n const keysToDelete: string[] = [];\n for (const key of this.feedContentCache.keys()) {\n if (key.startsWith(`${schemaName}:`)) {\n keysToDelete.push(key);\n }\n }\n keysToDelete.forEach(key => this.feedContentCache.delete(key));\n }\n\n /**\n * Get cached image metadata for a transaction ID\n */\n getImageMetadata(transactionId: string): ImageMetadata | null {\n const cached = this.imageMetadataCache.get(transactionId);\n if (!cached) {\n return null;\n }\n\n // Check if cache is expired\n const now = Math.floor(Date.now() / 1000);\n if (now > cached.expiresAt) {\n this.imageMetadataCache.delete(transactionId);\n return null;\n }\n\n return cached.metadata;\n }\n\n /**\n * Set cached image metadata for a transaction ID\n */\n setImageMetadata(transactionId: string, metadata: ImageMetadata): void {\n const now = Math.floor(Date.now() / 1000);\n const imageMetadataTtl = this.config.imageMetadata?.ttl || 604800; // Default 7 days\n const expiresAt = now + imageMetadataTtl;\n\n const cached: CachedImageMetadata = {\n metadata,\n cachedAt: now,\n expiresAt,\n };\n\n this.imageMetadataCache.set(transactionId, cached);\n }\n\n /**\n * Clear all caches\n */\n clearAll(): void {\n this.feedDataCache.clear();\n this.feedContentCache.clear();\n this.imageMetadataCache.clear();\n }\n\n /**\n * Get or create a refresh lock for a schema\n * Prevents concurrent fetches for the same schema\n */\n async withRefreshLock<T>(\n schemaName: string,\n fn: () => Promise<T>\n ): Promise<T> {\n const existingLock = this.refreshLocks.get(schemaName);\n if (existingLock) {\n // Wait for existing refresh to complete\n await existingLock;\n // Try to get cached data after waiting\n const cached = this.getFeedData(schemaName);\n if (cached) {\n // Re-fetch might not be needed, but we'll let the caller decide\n // For now, we'll proceed with the function\n }\n }\n\n // Create new lock\n const lockPromise = (async () => {\n try {\n return await fn();\n } finally {\n this.refreshLocks.delete(schemaName);\n }\n })();\n\n this.refreshLocks.set(schemaName, lockPromise as Promise<void>);\n return lockPromise;\n }\n\n /**\n * Update configuration\n */\n updateConfig(config: Partial<CacheConfig>): void {\n this.config = { ...this.config, ...config };\n }\n\n /**\n * Get cache statistics\n */\n getStats(): {\n feedDataCount: number;\n feedContentCount: number;\n imageMetadataCount: number;\n activeLocks: number;\n } {\n return {\n feedDataCount: this.feedDataCache.size,\n feedContentCount: this.feedContentCache.size,\n imageMetadataCount: this.imageMetadataCache.size,\n activeLocks: this.refreshLocks.size,\n };\n }\n}\n","import { promises as fs } from 'fs';\nimport { join } from 'path';\nimport type { CachedFeedData, CachedFeedContent, CacheConfig, CachedImageMetadata } from './types';\nimport type { GraphQLItem, ImageMetadata } from '../types';\n\n/**\n * File-based persistent cache implementation\n */\nexport class FileCache {\n private cacheDir: string;\n private config: CacheConfig;\n\n constructor(config: CacheConfig) {\n this.cacheDir = config.cacheDir;\n this.config = config;\n // Ensure cache directory exists\n this.ensureCacheDir().catch(err => {\n console.error('Failed to create cache directory:', err);\n });\n }\n\n /**\n * Ensure cache directory exists\n */\n private async ensureCacheDir(): Promise<void> {\n try {\n await fs.mkdir(this.cacheDir, { recursive: true });\n } catch (error) {\n // Directory might already exist, which is fine\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {\n throw error;\n }\n }\n }\n\n /**\n * Get file path for feed data\n */\n private getFeedDataPath(schemaName: string): string {\n return join(this.cacheDir, `${schemaName}.json`);\n }\n\n /**\n * Get file path for feed content\n */\n private getFeedContentPath(schemaName: string, format: string): string {\n return join(this.cacheDir, `${schemaName}-${format}.json`);\n }\n\n /**\n * Get file path for image metadata\n */\n private getImageMetadataPath(transactionId: string): string {\n const imageMetadataDir = join(this.cacheDir, 'image-metadata');\n return join(imageMetadataDir, `${transactionId}.json`);\n }\n\n /**\n * Ensure image metadata directory exists\n */\n private async ensureImageMetadataDir(): Promise<void> {\n try {\n const imageMetadataDir = join(this.cacheDir, 'image-metadata');\n await fs.mkdir(imageMetadataDir, { recursive: true });\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {\n throw error;\n }\n }\n }\n\n /**\n * Get cached feed data for a schema\n */\n async getFeedData(schemaName: string): Promise<CachedFeedData | null> {\n try {\n const filePath = this.getFeedDataPath(schemaName);\n const data = await fs.readFile(filePath, 'utf-8');\n const cached: CachedFeedData = JSON.parse(data);\n\n // Check if cache is expired\n const now = Math.floor(Date.now() / 1000);\n const age = now - cached.lastUpdated;\n if (age > this.config.ttl) {\n // Cache expired, delete file\n await this.clearFeedData(schemaName);\n return null;\n }\n\n return cached;\n } catch (error) {\n // File doesn't exist or is corrupted\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return null;\n }\n console.error(`Error reading feed data cache for ${schemaName}:`, error);\n return null;\n }\n }\n\n /**\n * Set cached feed data for a schema\n */\n async setFeedData(schemaName: string, data: CachedFeedData): Promise<void> {\n try {\n await this.ensureCacheDir();\n const filePath = this.getFeedDataPath(schemaName);\n await fs.writeFile(filePath, JSON.stringify(data, null, 2), 'utf-8');\n } catch (error) {\n console.error(`Error writing feed data cache for ${schemaName}:`, error);\n // Don't throw - file cache is best effort\n }\n }\n\n /**\n * Get cached feed content for a schema and format\n */\n async getFeedContent(\n schemaName: string,\n format: string\n ): Promise<CachedFeedContent | null> {\n try {\n const filePath = this.getFeedContentPath(schemaName, format);\n const data = await fs.readFile(filePath, 'utf-8');\n const cached: CachedFeedContent = JSON.parse(data);\n\n // Check if cache is expired\n const now = Math.floor(Date.now() / 1000);\n if (now > cached.expiresAt) {\n // Cache expired, delete file\n await this.clearFeedContent(schemaName, format);\n return null;\n }\n\n return cached;\n } catch (error) {\n // File doesn't exist or is corrupted\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return null;\n }\n console.error(\n `Error reading feed content cache for ${schemaName}:${format}:`,\n error\n );\n return null;\n }\n }\n\n /**\n * Set cached feed content for a schema and format\n */\n async setFeedContent(\n schemaName: string,\n format: string,\n content: CachedFeedContent\n ): Promise<void> {\n try {\n await this.ensureCacheDir();\n const filePath = this.getFeedContentPath(schemaName, format);\n await fs.writeFile(filePath, JSON.stringify(content, null, 2), 'utf-8');\n } catch (error) {\n console.error(\n `Error writing feed content cache for ${schemaName}:${format}:`,\n error\n );\n // Don't throw - file cache is best effort\n }\n }\n\n /**\n * Clear feed data cache for a schema\n */\n async clearFeedData(schemaName: string): Promise<void> {\n try {\n const filePath = this.getFeedDataPath(schemaName);\n await fs.unlink(filePath);\n } catch (error) {\n // File might not exist, which is fine\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n console.error(`Error clearing feed data cache for ${schemaName}:`, error);\n }\n }\n }\n\n /**\n * Clear content cache for a schema and format\n */\n async clearFeedContent(schemaName: string, format: string): Promise<void> {\n try {\n const filePath = this.getFeedContentPath(schemaName, format);\n await fs.unlink(filePath);\n } catch (error) {\n // File might not exist, which is fine\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n console.error(\n `Error clearing feed content cache for ${schemaName}:${format}:`,\n error\n );\n }\n }\n }\n\n /**\n * Clear all content cache for a schema (all formats)\n */\n async clearAllContentCache(schemaName: string): Promise<void> {\n try {\n const files = await fs.readdir(this.cacheDir);\n const prefix = `${schemaName}-`;\n const suffix = '.json';\n\n for (const file of files) {\n if (file.startsWith(prefix) && file.endsWith(suffix) && file !== `${schemaName}.json`) {\n await fs.unlink(join(this.cacheDir, file));\n }\n }\n } catch (error) {\n console.error(`Error clearing all content cache for ${schemaName}:`, error);\n }\n }\n\n /**\n * Get cached image metadata for a transaction ID\n */\n async getImageMetadata(transactionId: string): Promise<ImageMetadata | null> {\n try {\n const filePath = this.getImageMetadataPath(transactionId);\n const data = await fs.readFile(filePath, 'utf-8');\n const cached: CachedImageMetadata = JSON.parse(data);\n\n // Check if cache is expired\n const now = Math.floor(Date.now() / 1000);\n if (now > cached.expiresAt) {\n // Cache expired, delete file\n await this.clearImageMetadata(transactionId);\n return null;\n }\n\n return cached.metadata;\n } catch (error) {\n // File doesn't exist or is corrupted\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return null;\n }\n console.error(`Error reading image metadata cache for ${transactionId}:`, error);\n return null;\n }\n }\n\n /**\n * Set cached image metadata for a transaction ID\n */\n async setImageMetadata(transactionId: string, metadata: ImageMetadata): Promise<void> {\n try {\n await this.ensureImageMetadataDir();\n const filePath = this.getImageMetadataPath(transactionId);\n const now = Math.floor(Date.now() / 1000);\n const imageMetadataTtl = this.config.imageMetadata?.ttl || 604800; // Default 7 days\n const expiresAt = now + imageMetadataTtl;\n\n const cached: CachedImageMetadata = {\n metadata,\n cachedAt: now,\n expiresAt,\n };\n\n await fs.writeFile(filePath, JSON.stringify(cached, null, 2), 'utf-8');\n } catch (error) {\n console.error(`Error writing image metadata cache for ${transactionId}:`, error);\n // Don't throw - file cache is best effort\n }\n }\n\n /**\n * Clear image metadata cache for a transaction ID\n */\n async clearImageMetadata(transactionId: string): Promise<void> {\n try {\n const filePath = this.getImageMetadataPath(transactionId);\n await fs.unlink(filePath);\n } catch (error) {\n // File might not exist, which is fine\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n console.error(`Error clearing image metadata cache for ${transactionId}:`, error);\n }\n }\n }\n\n /**\n * Clear all caches\n */\n async clearAll(): Promise<void> {\n try {\n const files = await fs.readdir(this.cacheDir);\n for (const file of files) {\n if (file.endsWith('.json')) {\n await fs.unlink(join(this.cacheDir, file));\n }\n }\n // Also clear image metadata directory\n const imageMetadataDir = join(this.cacheDir, 'image-metadata');\n try {\n const imageFiles = await fs.readdir(imageMetadataDir);\n for (const file of imageFiles) {\n if (file.endsWith('.json')) {\n await fs.unlink(join(imageMetadataDir, file));\n }\n }\n } catch {\n // Directory might not exist, which is fine\n }\n } catch (error) {\n console.error('Error clearing all caches:', error);\n }\n }\n}\n","import { MemoryCache } from './MemoryCache';\nimport { FileCache } from './FileCache';\nimport type {\n CachedFeedData,\n CachedFeedContent,\n CacheConfig,\n CacheStats,\n} from './types';\nimport type { GraphQLItem, ImageMetadata } from '../types';\nimport type { FeedFormat } from '../types';\n\n/**\n * Unified cache manager that combines in-memory and file-based caching\n */\nexport class CacheManager {\n private memoryCache: MemoryCache;\n private fileCache: FileCache;\n private config: CacheConfig;\n private stats: CacheStats = {\n hits: 0,\n misses: 0,\n refreshes: 0,\n errors: 0,\n };\n\n constructor(config: CacheConfig) {\n this.config = config;\n this.memoryCache = new MemoryCache(config);\n this.fileCache = new FileCache(config);\n }\n\n /**\n * Get cached feed data for a schema\n * Checks memory cache first, then file cache\n */\n async getFeedData(schemaName: string): Promise<CachedFeedData | null> {\n if (!this.config.enabled) {\n return null;\n }\n\n try {\n // Check memory cache first\n let cached = this.memoryCache.getFeedData(schemaName);\n if (cached) {\n this.stats.hits++;\n return cached;\n }\n\n // Check file cache\n cached = await this.fileCache.getFeedData(schemaName);\n if (cached) {\n // Restore to memory cache\n this.memoryCache.setFeedData(schemaName, cached.items);\n this.stats.hits++;\n return cached;\n }\n\n this.stats.misses++;\n return null;\n } catch (error) {\n console.error(`Error getting feed data cache for ${schemaName}:`, error);\n this.stats.errors++;\n return null;\n }\n }\n\n /**\n * Set cached feed data for a schema\n * Updates both memory and file cache\n */\n async setFeedData(schemaName: string, items: GraphQLItem[]): Promise<void> {\n if (!this.config.enabled) {\n return;\n }\n\n try {\n // Update memory cache\n this.memoryCache.setFeedData(schemaName, items);\n\n // Get the cached data to save to file\n const cached = this.memoryCache.getFeedData(schemaName);\n if (cached) {\n await this.fileCache.setFeedData(schemaName, cached);\n }\n } catch (error) {\n console.error(`Error setting feed data cache for ${schemaName}:`, error);\n this.stats.errors++;\n }\n }\n\n /**\n * Get cached feed content for a schema and format\n * Checks memory cache first, then file cache\n */\n async getFeedContent(\n schemaName: string,\n format: FeedFormat\n ): Promise<CachedFeedContent | null> {\n if (!this.config.enabled) {\n return null;\n }\n\n try {\n // Check memory cache first\n let cached = this.memoryCache.getFeedContent(schemaName, format);\n if (cached) {\n this.stats.hits++;\n return cached;\n }\n\n // Check file cache\n cached = await this.fileCache.getFeedContent(schemaName, format);\n if (cached) {\n // Restore to memory cache\n this.memoryCache.setFeedContent(\n schemaName,\n format,\n cached.content,\n cached.contentType\n );\n this.stats.hits++;\n return cached;\n }\n\n this.stats.misses++;\n return null;\n } catch (error) {\n console.error(\n `Error getting feed content cache for ${schemaName}:${format}:`,\n error\n );\n this.stats.errors++;\n return null;\n }\n }\n\n /**\n * Set cached feed content for a schema and format\n * Updates both memory and file cache\n */\n async setFeedContent(\n schemaName: string,\n format: FeedFormat,\n content: string,\n contentType: string\n ): Promise<void> {\n if (!this.config.enabled) {\n return;\n }\n\n try {\n // Update memory cache\n this.memoryCache.setFeedContent(schemaName, format, content, contentType);\n\n // Get the cached content to save to file\n const cached = this.memoryCache.getFeedContent(schemaName, format);\n if (cached) {\n await this.fileCache.setFeedContent(schemaName, format, cached);\n }\n } catch (error) {\n console.error(\n `Error setting feed content cache for ${schemaName}:${format}:`,\n error\n );\n this.stats.errors++;\n }\n }\n\n /**\n * Clear feed data cache for a schema\n */\n async clearFeedData(schemaName: string): Promise<void> {\n this.memoryCache.clearFeedData(schemaName);\n await this.fileCache.clearFeedData(schemaName);\n this.memoryCache.clearContentCache(schemaName);\n await this.fileCache.clearAllContentCache(schemaName);\n }\n\n /**\n * Clear all caches\n */\n async clearAll(): Promise<void> {\n this.memoryCache.clearAll();\n await this.fileCache.clearAll();\n }\n\n /**\n * Get or create a refresh lock for a schema\n * Prevents concurrent fetches for the same schema\n */\n async withRefreshLock<T>(\n schemaName: string,\n fn: () => Promise<T>\n ): Promise<T> {\n return this.memoryCache.withRefreshLock(schemaName, fn);\n }\n\n /**\n * Merge new items with cached items\n * Deduplicates by item ID and sorts by timeCreated (descending)\n */\n mergeItems(cachedItems: GraphQLItem[], newItems: GraphQLItem[]): GraphQLItem[] {\n // Create a map of existing items by ID\n const itemMap = new Map<string, GraphQLItem>();\n \n // Add cached items first\n for (const item of cachedItems) {\n const itemId = item.id || (item as any).seedUid || (item as any).SeedUid || '';\n if (itemId) {\n itemMap.set(itemId, item);\n }\n }\n\n // Add or update with new items\n for (const item of newItems) {\n const itemId = item.id || (item as any).seedUid || (item as any).SeedUid || '';\n if (itemId) {\n itemMap.set(itemId, item);\n } else {\n // If no ID, add it anyway (might be a new item)\n itemMap.set(`temp-${Date.now()}-${Math.random()}`, item);\n }\n }\n\n // Convert back to array and sort by timeCreated (descending)\n const merged = Array.from(itemMap.values());\n merged.sort((a, b) => {\n const timeA = (a as any).timeCreated || 0;\n const timeB = (b as any).timeCreated || 0;\n return timeB - timeA; // Descending order\n });\n\n return merged;\n }\n\n /**\n * Filter items to only include those newer than the given timestamp\n */\n filterNewItems(items: GraphQLItem[], lastProcessedTimestamp: number): GraphQLItem[] {\n return items.filter(item => {\n const timeCreated = (item as any).timeCreated;\n return timeCreated && timeCreated > lastProcessedTimestamp;\n });\n }\n\n /**\n * Update configuration\n */\n updateConfig(config: Partial<CacheConfig>): void {\n this.config = { ...this.config, ...config };\n this.memoryCache.updateConfig(this.config);\n // FileCache doesn't need config update as it's read-only after construction\n }\n\n /**\n * Get cache statistics\n */\n getStats(): CacheStats & { memoryStats: ReturnType<MemoryCache['getStats']> } {\n return {\n ...this.stats,\n memoryStats: this.memoryCache.getStats(),\n };\n }\n\n /**\n * Get cached image metadata for a transaction ID\n * Checks memory cache first, then file cache\n */\n async getImageMetadata(transactionId: string): Promise<ImageMetadata | null> {\n if (!this.config.enabled || !this.config.imageMetadata?.enabled) {\n return null;\n }\n\n try {\n // Check memory cache first\n let cached = this.memoryCache.getImageMetadata(transactionId);\n if (cached) {\n this.stats.hits++;\n return cached;\n }\n\n // Check file cache\n cached = await this.fileCache.getImageMetadata(transactionId);\n if (cached) {\n // Restore to memory cache\n this.memoryCache.setImageMetadata(transactionId, cached);\n this.stats.hits++;\n return cached;\n }\n\n this.stats.misses++;\n return null;\n } catch (error) {\n console.error(`Error getting image metadata cache for ${transactionId}:`, error);\n this.stats.errors++;\n return null;\n }\n }\n\n /**\n * Set cached image metadata for a transaction ID\n * Updates both memory and file cache\n */\n async setImageMetadata(transactionId: string, metadata: ImageMetadata): Promise<void> {\n if (!this.config.enabled || !this.config.imageMetadata?.enabled) {\n return;\n }\n\n try {\n // Update memory cache\n this.memoryCache.setImageMetadata(transactionId, metadata);\n\n // Also update file cache\n await this.fileCache.setImageMetadata(transactionId, metadata);\n } catch (error) {\n console.error(`Error setting image metadata cache for ${transactionId}:`, error);\n this.stats.errors++;\n }\n }\n\n /**\n * Reset statistics\n */\n resetStats(): void {\n this.stats = {\n hits: 0,\n misses: 0,\n refreshes: 0,\n errors: 0,\n };\n }\n}\n","import { DEFAULT_ARWEAVE_GATEWAYS } from '@seedprotocol/sdk'\nimport type { CacheConfig, ImageMetadataConfig } from './types';\n\n/**\n * Load cache configuration from environment variables\n *\n * Environment variables:\n * - CACHE_ENABLED: Set to 'false' to disable caching. Set to 'true' to enable (overrides NODE_ENV).\n * In development (NODE_ENV=development), cache is disabled by default unless CACHE_ENABLED='true'.\n * - CACHE_TTL: Cache time-to-live in seconds (default: 3600)\n * - CACHE_DIR: Directory for cache files (default: './cache')\n * - CACHE_BACKGROUND_REFRESH: Enable background refresh (default: 'false')\n * - CACHE_REFRESH_INTERVAL: Background refresh interval in seconds (default: 300)\n */\nexport function loadCacheConfig(): CacheConfig {\n const ttl = parseInt(process.env.CACHE_TTL || '3600', 10);\n const cacheDir = process.env.CACHE_DIR || './cache';\n\n // CACHE_ENABLED explicitly disables: 'false', '0', 'no', 'off'\n const cacheDisabledByEnvVar =\n process.env.CACHE_ENABLED === 'false' ||\n process.env.CACHE_ENABLED === '0' ||\n process.env.CACHE_ENABLED === 'no' ||\n process.env.CACHE_ENABLED === 'off';\n // CACHE_ENABLED explicitly enables (overrides NODE_ENV=development)\n const cacheEnabledByEnvVar =\n process.env.CACHE_ENABLED === 'true' ||\n process.env.CACHE_ENABLED === '1' ||\n process.env.CACHE_ENABLED === 'yes';\n const isDev = process.env.NODE_ENV === 'development';\n\n let enabled: boolean;\n if (cacheDisabledByEnvVar) {\n enabled = false;\n } else if (cacheEnabledByEnvVar) {\n enabled = true;\n } else if (isDev) {\n enabled = false; // Disable cache in dev mode by default\n } else {\n enabled = true;\n }\n\n const backgroundRefresh = process.env.CACHE_BACKGROUND_REFRESH === 'true';\n const refreshInterval = parseInt(\n process.env.CACHE_REFRESH_INTERVAL || '300',\n 10\n );\n\n // Log cache status on startup\n if (!enabled) {\n const reason = cacheDisabledByEnvVar\n ? 'CACHE_ENABLED=false'\n : isDev\n ? 'NODE_ENV=development'\n : 'CACHE_ENABLED=false';\n console.log(`⚠️ Cache is DISABLED (${reason})`);\n }\n\n // Image metadata configuration\n const imageMetadataEnabled = process.env.IMAGE_METADATA_ENABLED !== 'false'; // Default to true\n const imageMetadataTtl = parseInt(process.env.IMAGE_METADATA_TTL || '604800', 10); // 7 days default\n const imageMetadataGateways = process.env.IMAGE_METADATA_GATEWAYS\n ? process.env.IMAGE_METADATA_GATEWAYS.split(',').map(g => g.trim())\n : [...DEFAULT_ARWEAVE_GATEWAYS];\n const imageMetadataTimeout = parseInt(process.env.IMAGE_METADATA_TIMEOUT || '5000', 10);\n\n const imageMetadata: ImageMetadataConfig = {\n enabled: imageMetadataEnabled,\n ttl: imageMetadataTtl,\n gateways: imageMetadataGateways,\n timeout: imageMetadataTimeout,\n };\n\n return {\n ttl,\n cacheDir,\n enabled,\n backgroundRefresh,\n refreshInterval,\n imageMetadata,\n };\n}\n","import { DEFAULT_ARWEAVE_GATEWAYS } from '@seedprotocol/sdk'\nimport sizeOf from 'image-size'\nimport type { ImageMetadata } from '../types'\n\nexport interface ArweaveImageServiceConfig {\n gateways: string[]\n timeout: number // milliseconds\n}\n\n/**\n * Service for detecting and extracting metadata from images stored on Arweave\n */\nexport class ArweaveImageService {\n private config: ArweaveImageServiceConfig\n\n constructor(config: ArweaveImageServiceConfig) {\n this.config = config\n }\n\n /**\n * Detect if an Arweave transaction ID links to an image and extract metadata\n */\n async detectImage(transactionId: string): Promise<ImageMetadata> {\n const gateways = this.config.gateways || [...DEFAULT_ARWEAVE_GATEWAYS]\n \n // Try each gateway until one succeeds\n for (const gateway of gateways) {\n try {\n const url = `https://${gateway}/${transactionId}`\n const metadata = await this.getImageMetadata(url)\n if (metadata.isImage) {\n return metadata\n }\n } catch (error) {\n // Log but continue to next gateway\n console.warn(`Failed to fetch from gateway ${gateway} for transaction ${transactionId}:`, error)\n continue\n }\n }\n\n // If all gateways failed, return non-image result\n return {\n isImage: false,\n url: `https://${gateways[0]}/${transactionId}`,\n }\n }\n\n /**\n * Get image metadata from a URL\n */\n async getImageMetadata(url: string): Promise<ImageMetadata> {\n try {\n // First, try HEAD request to check Content-Type (more efficient)\n const headResponse = await this.fetchWithTimeout(url, { method: 'HEAD' })\n \n if (!headResponse.ok) {\n return { isImage: false, url }\n }\n\n const contentType = headResponse.headers.get('content-type') || ''\n const contentLength = headResponse.headers.get('content-length')\n \n // Check if it's an image based on Content-Type\n if (!this.isImageContentType(contentType)) {\n return {\n isImage: false,\n url,\n mimeType: contentType || undefined,\n size: contentLength ? parseInt(contentLength, 10) : undefined,\n }\n }\n\n // If it's an image, fetch first few KB to extract dimensions\n const rangeResponse = await this.fetchWithTimeout(url, {\n headers: {\n 'Range': 'bytes=0-8192', // First 8KB should be enough for most image headers\n },\n })\n\n if (!rangeResponse.ok) {\n // If range request fails, try full request (some servers don't support range)\n return await this.getImageMetadataFromFullRequest(url, contentType, contentLength)\n }\n\n const buffer = Buffer.from(await rangeResponse.arrayBuffer())\n const dimensions = this.extractImageDimensions(buffer, contentType)\n const format = this.getImageFormat(contentType, buffer)\n\n return {\n isImage: true,\n url,\n mimeType: contentType,\n width: dimensions.width,\n height: dimensions.height,\n size: contentLength ? parseInt(contentLength, 10) : undefined,\n format,\n }\n } catch (error) {\n console.warn(`Error fetching image metadata from ${url}:`, error)\n return { isImage: false, url }\n }\n }\n\n /**\n * Fetch image metadata from full request (fallback when range requests fail)\n */\n private async getImageMetadataFromFullRequest(\n url: string,\n contentType: string,\n contentLength: string | null\n ): Promise<ImageMetadata> {\n try {\n const response = await this.fetchWithTimeout(url)\n if (!response.ok) {\n return { isImage: false, url }\n }\n\n const buffer = Buffer.from(await response.arrayBuffer())\n const dimensions = this.extractImageDimensions(buffer, contentType)\n const format = this.getImageFormat(contentType, buffer)\n\n return {\n isImage: true,\n url,\n mimeType: contentType,\n width: dimensions.width,\n height: dimensions.height,\n size: buffer.length,\n format,\n }\n } catch (error) {\n console.warn(`Error in full request for ${url}:`, error)\n return { isImage: false, url }\n }\n }\n\n /**\n * Validate if Content-Type indicates an image\n */\n private isImageContentType(contentType: string): boolean {\n if (!contentType) return false\n \n const normalized = (contentType.toLowerCase().split(';')[0] ?? '').trim()\n return normalized.startsWith('image/')\n }\n\n /**\n * Extract image dimensions from buffer\n */\n private extractImageDimensions(\n buffer: Buffer,\n contentType: string\n ): { width: number; height: number } {\n try {\n const dimensions = sizeOf(buffer)\n if (dimensions.width && dimensions.height) {\n return {\n width: dimensions.width,\n height: dimensions.height,\n }\n }\n } catch (error) {\n // image-size might fail for some formats or corrupted data\n console.warn(`Failed to extract dimensions:`, error)\n }\n\n // Return default if extraction failed\n return { width: 0, height: 0 }\n }\n\n /**\n * Get image format from Content-Type or buffer analysis\n */\n private getImageFormat(contentType: string, buffer: Buffer): string | undefined {\n // Try to get format from Content-Type first\n if (contentType) {\n const mimeMatch = contentType.toLowerCase().match(/image\\/([^;]+)/)\n if (mimeMatch && mimeMatch[1]) {\n const format = mimeMatch[1].toLowerCase()\n // Normalize common formats\n if (format === 'jpeg') return 'jpeg'\n if (format === 'png') return 'png'\n if (format === 'gif') return 'gif'\n if (format === 'webp') return 'webp'\n if (format === 'svg+xml') return 'svg'\n return format\n }\n }\n\n // Try to detect from buffer magic bytes\n try {\n const dimensions = sizeOf(buffer)\n if (dimensions.type) {\n return dimensions.type.toLowerCase()\n }\n } catch {\n // Ignore errors\n }\n\n return undefined\n }\n\n /**\n * Fetch with timeout\n */\n private async fetchWithTimeout(\n url: string,\n options: RequestInit = {}\n ): Promise<Response> {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), this.config.timeout)\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n })\n clearTimeout(timeoutId)\n return response\n } catch (error) {\n clearTimeout(timeoutId)\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Request timeout after ${this.config.timeout}ms`)\n }\n throw error\n }\n }\n}\n","import { client as seedClient, DEFAULT_ARWEAVE_HOST, getArweaveUrlForTransaction } from '@seedprotocol/sdk';\nimport { getFeedItemsBySchemaName } from './getFeedItems';\n\nexport { getFeedItemsBySchemaName };\nimport pluralize from 'pluralize';\nimport type { FeedFormat, GraphQLItem, TransformOptions, FeedConfig, ImageMetadata } from './types';\nimport { generateAtomFeed, generateJsonFeed } from 'feedsmith';\nimport { generateRssXml } from './rss/generateRssXml';\nimport { CacheManager } from './cache/CacheManager';\nimport { loadCacheConfig } from './cache/config';\nimport { loadFeedConfig } from './config';\nimport { checkIfNoneMatch } from './utils/etag';\nimport { ArweaveImageService } from './services/arweaveImageService';\n\nlet client: any;\nlet initializationPromise: Promise<void> | null = null;\n\n// Initialize cache manager\nlet cacheManager: CacheManager | null = null;\n\nfunction getCacheManager(): CacheManager {\n if (!cacheManager) {\n const config = loadCacheConfig();\n cacheManager = new CacheManager(config);\n }\n return cacheManager;\n}\n\n/**\n * Reset cache manager (useful for testing)\n */\nexport function resetCacheManager(): void {\n cacheManager = null;\n}\n\n/**\n * Initialize the Seed Protocol client\n * This should be called as soon as the app is ready\n */\nexport const initializeSeedClient = async (): Promise<void> => {\n // If already initializing, wait for that to complete\n if (initializationPromise) {\n return initializationPromise;\n }\n\n // If already initialized, return immediately\n if (client) {\n return;\n }\n\n initializationPromise = (async () => {\n try {\n console.log('Initializing Seed Protocol client...');\n\n \n await seedClient.init({ config: {\n endpoints: {\n filePaths: 'app-files',\n files: '/app-files',\n },\n arweaveDomain: DEFAULT_ARWEAVE_HOST,\n }, addresses: [], });\n console.log('✅ Seed Protocol client initialized successfully');\n client = seedClient;\n initializationPromise = null; // Clear the promise after successful initialization\n } catch (error) {\n console.error('❌ Failed to initialize Seed Protocol client:', error);\n initializationPromise = null; // Clear the promise on error so we can retry\n throw error;\n }\n })();\n\n return initializationPromise;\n}\n\n/**\n * Get the Seed Protocol client, initializing it if necessary\n * This function can be called from any context (Electron main process or Vite dev server)\n */\nexport const getClient = async (): Promise<any> => {\n // If client is already initialized, return it\n if (client) {\n return client;\n }\n\n // If initialization is in progress, wait for it\n if (initializationPromise) {\n await initializationPromise;\n return client;\n }\n\n // Otherwise, initialize it now\n await initializeSeedClient();\n return client;\n}\n\n\n/**\n * Teardown the Seed Protocol client\n * This should be called when the app is quitting\n */\nexport const teardownSeedClient = async (): Promise<void> => {\n try {\n console.log('Tearing down Seed Protocol client...');\n \n if (typeof seedClient.stop === 'function') {\n await seedClient.stop();\n console.log('✅ Seed Protocol client stopped');\n }\n \n if (typeof seedClient.unload === 'function') {\n await seedClient.unload();\n console.log('✅ Seed Protocol client unloaded');\n }\n \n console.log('✅ Seed Protocol client teardown complete');\n } catch (error) {\n console.error('❌ Failed to teardown Seed Protocol client:', error);\n // Don't throw - we want the app to quit even if teardown fails\n }\n}\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\nconst SITE_CONFIG: FeedConfig = {\n title: 'Seed Protocol',\n description: 'Content published via Seed Protocol',\n siteUrl: 'https://seedprotocol.io',\n feedUrl: 'https://feed.seedprotocol.io',\n language: 'en',\n copyright: `© ${new Date().getFullYear()} All rights reserved`,\n author: {\n name: 'Seed Protocol',\n email: 'info@seedprotocol.io',\n link: 'https://seedprotocol.io',\n },\n}\n\n// ============================================================================\n// GraphQL Client (replace with your actual client)\n// ============================================================================\n\n// ============================================================================\n// Feed Transformation\n// ============================================================================\n\n/**\n * Enriches feed items with media properties from Arweave storageTransactionId\n */\nasync function enrichFeedItemsWithMedia(\n items: GraphQLItem[],\n imageService: ArweaveImageService,\n cache: CacheManager\n): Promise<GraphQLItem[]> {\n // Filter items that have storageTransactionId\n const itemsToProcess = items.filter(\n (item: any) => item.storageTransactionId || item.storage_transaction_id\n )\n\n if (itemsToProcess.length === 0) {\n return items\n }\n\n console.log(`Enriching ${itemsToProcess.length} items with image metadata`)\n\n // Process all items in parallel\n const enrichmentPromises = itemsToProcess.map(async (item: any) => {\n const transactionId = item.storageTransactionId || item.storage_transaction_id\n if (!transactionId) return item\n\n try {\n // Check cache first\n let imageMetadata = await cache.getImageMetadata(transactionId)\n\n // If not cached, detect image\n if (!imageMetadata) {\n console.log(`Detecting image for transaction ${transactionId}`)\n imageMetadata = await imageService.detectImage(transactionId)\n // Cache the result (even if it's not an image, to avoid retries)\n await cache.setImageMetadata(transactionId, imageMetadata)\n }\n\n // If image detected, add media properties\n if (imageMetadata.isImage) {\n // Add media properties to the item\n item._imageMetadata = imageMetadata\n item._hasImage = true\n }\n\n return item\n } catch (error) {\n console.warn(`Error enriching item with transaction ${transactionId}:`, error)\n return item // Return item unchanged on error\n }\n })\n\n // Wait for all enrichments to complete\n const enrichedItems = await Promise.all(enrichmentPromises)\n\n // Create a map of enriched items by their ID\n const enrichedMap = new Map<string, GraphQLItem>()\n enrichedItems.forEach(item => {\n const itemId = item.id || (item as any).seedUid || (item as any).SeedUid || ''\n if (itemId) {\n enrichedMap.set(itemId, item)\n }\n })\n\n // Merge enriched items back into original array\n return items.map(item => {\n const itemId = item.id || (item as any).seedUid || (item as any).SeedUid || ''\n const enriched = enrichedMap.get(itemId)\n return enriched || item\n })\n}\n\n/**\n * Transforms GraphQL items into feed items.\n * This function preserves all dynamic properties from the items.\n */\nfunction transformToFeedItems(\n items: GraphQLItem[],\n options: TransformOptions\n): any[] {\n const { schemaName, siteUrl, itemUrlBase, itemUrlPath } = options\n\n return items.map((item: any) => {\n // Determine item ID - try multiple possible fields\n const itemId = item.id || item.seedUid || item.SeedUid || item.storageTransactionId || item.storage_transaction_id\n\n // Determine item URL - prefer link/Link, then import_url/importUrl, fallback to constructed URL\n const fallbackUrl =\n itemUrlBase != null\n ? `${itemUrlBase.replace(/\\/$/, '')}/${(itemUrlPath ?? 'attestation/view').replace(/^\\//, '')}/${itemId}`\n : `${siteUrl}/${pluralize(schemaName)}/${itemId}`\n const itemUrl = item.link || item.Link || item.import_url || item.importUrl || fallbackUrl\n \n // Determine publication date - try multiple sources\n let date: Date\n if (item.pubDate || item.PubDate) {\n // pubDate might be a string like \"Mon, 07 Apr 2025 00:03:29 GMT\"\n const pubDateStr = item.pubDate || item.PubDate\n date = new Date(pubDateStr)\n } else if (item.timeCreated) {\n // timeCreated is a Unix timestamp\n date = new Date(item.timeCreated * 1000)\n } else if (item.publishedAt || item.createdAt || item.updatedAt) {\n const dateValue = item.publishedAt || item.createdAt || item.updatedAt\n date = dateValue && typeof dateValue === 'object' && dateValue.constructor === Date\n ? dateValue as Date\n : new Date(dateValue as string | number)\n } else {\n date = new Date()\n }\n\n // Start with all properties from the item to preserve dynamic schema\n const feedItem: any = {\n ...item, // Preserve all dynamic properties first\n // Map to standard feed fields\n id: itemId,\n title: item.title || item.Title || 'Untitled',\n link: itemUrl,\n description: item.summary || item.description || item.text || '',\n content: item.html || item.content || item.summary || item.text || '',\n pubDate: date,\n date: date,\n // Map guid - use full URL when available for proper permalink\n guid: item.guid || item.Guid || item.link || item.Link || itemUrl,\n }\n\n // Add image metadata if available from enrichment\n if (item._imageMetadata && item._hasImage) {\n const imageMeta: ImageMetadata = item._imageMetadata\n feedItem._imageMetadata = imageMeta\n feedItem._hasImage = true\n }\n\n // Convert any date-like string properties to Date objects\n Object.keys(feedItem).forEach((key) => {\n const value = feedItem[key]\n if (typeof value === 'string' && /date|time|published|created|updated/i.test(key) && key !== 'pubDate' && key !== 'date') {\n const dateValue = new Date(value)\n if (!isNaN(dateValue.getTime())) {\n feedItem[key] = dateValue\n }\n }\n })\n\n return feedItem\n })\n}\n\n// ============================================================================\n// Feed Generator\n// ============================================================================\n\nexport const createFeed = (\n items: GraphQLItem[],\n schemaName: string,\n format: FeedFormat,\n cacheBust?: string\n): Promise<string> => {\n const collectionName = pluralize(schemaName)\n // Add cache busting parameter to feed URL if provided\n const feedUrlBase = `${SITE_CONFIG.siteUrl}/${collectionName}/${format}`\n const feedUrl = cacheBust ? `${feedUrlBase}?v=${cacheBust}` : feedUrlBase\n const feedTitle = `${SITE_CONFIG.title} - ${capitalize(collectionName)}`\n const now = new Date()\n\n const feedConfig = loadFeedConfig()\n\n // Transform items to preserve all dynamic properties\n const transformedItems = transformToFeedItems(items, {\n schemaName,\n siteUrl: SITE_CONFIG.siteUrl,\n itemUrlBase: feedConfig.itemUrlBase,\n itemUrlPath: feedConfig.itemUrlPath,\n })\n\n // Generate feed based on format using FeedSmith\n switch (format) {\n case 'atom': {\n // Atom feed requires: id, title, updated, links, entries\n const atomFeed = {\n id: feedUrl,\n title: feedTitle,\n updated: now,\n links: [\n { href: feedUrl, rel: 'self' },\n { href: SITE_CONFIG.siteUrl },\n ],\n subtitle: SITE_CONFIG.description,\n rights: SITE_CONFIG.copyright,\n author: SITE_CONFIG.author ? {\n name: SITE_CONFIG.author.name,\n email: SITE_CONFIG.author.email,\n uri: SITE_CONFIG.author.link,\n } : undefined,\n entries: transformedItems.map((item) => {\n // Atom entries require: id, title, updated, links\n const entry: any = {\n id: item.id || item.link,\n title: item.title || 'Untitled',\n updated: item.date || item.pubDate || now,\n links: item.link ? [{ href: item.link }] : [],\n ...item, // Preserve all dynamic properties\n }\n if (item.content) entry.content = item.content\n if (item.description) entry.summary = item.description\n \n // Add image as enclosure link if available\n if (item._imageMetadata && item._hasImage) {\n const imageMeta: ImageMetadata = item._imageMetadata\n if (!entry.links) entry.links = []\n entry.links.push({\n href: imageMeta.url,\n rel: 'enclosure',\n type: imageMeta.mimeType || 'image/jpeg',\n length: imageMeta.size,\n })\n \n // Enhance content with image if not already present\n if (imageMeta.url && entry.content && !entry.content.includes(imageMeta.url)) {\n const imageHtml = `<img src=\"${imageMeta.url}\" alt=\"${item.title || ''}\"${imageMeta.width ? ` width=\"${imageMeta.width}\"` : ''}${imageMeta.height ? ` height=\"${imageMeta.height}\"` : ''} />`\n entry.content = typeof entry.content === 'string' \n ? `${imageHtml}\\n${entry.content}`\n : { type: 'html', value: `${imageHtml}\\n${entry.content.value || entry.content}` }\n }\n }\n \n return entry\n }),\n }\n return Promise.resolve(generateAtomFeed(atomFeed) as string)\n }\n case 'json': {\n // JSON feed requires: title, items (with id)\n const jsonFeed = {\n title: feedTitle,\n home_page_url: SITE_CONFIG.siteUrl,\n feed_url: feedUrl,\n description: SITE_CONFIG.description,\n author: SITE_CONFIG.author ? {\n name: SITE_CONFIG.author.name,\n url: SITE_CONFIG.author.link,\n } : undefined,\n items: transformedItems.map((item) => {\n // JSON items require: id\n const jsonItem: any = {\n id: item.id || item.link,\n ...item, // Preserve all dynamic properties\n }\n if (item.title) jsonItem.title = item.title\n if (item.link) jsonItem.url = item.link\n if (item.content) jsonItem.content_html = item.content\n if (item.description) jsonItem.summary = item.description\n if (item.date || item.pubDate) jsonItem.date_published = item.date || item.pubDate\n \n // Add image and attachments if available\n if (item._imageMetadata && item._hasImage) {\n const imageMeta: ImageMetadata = item._imageMetadata\n jsonItem.image = imageMeta.url\n \n // Add as attachment\n if (!jsonItem.attachments) jsonItem.attachments = []\n jsonItem.attachments.push({\n url: imageMeta.url,\n mime_type: imageMeta.mimeType || 'image/jpeg',\n size_in_bytes: imageMeta.size,\n title: item.title || 'Image',\n })\n \n // Enhance content_html with image if not already present\n if (imageMeta.url && jsonItem.content_html && !jsonItem.content_html.includes(imageMeta.url)) {\n const imageHtml = `<img src=\"${imageMeta.url}\" alt=\"${item.title || ''}\"${imageMeta.width ? ` width=\"${imageMeta.width}\"` : ''}${imageMeta.height ? ` height=\"${imageMeta.height}\"` : ''} />`\n jsonItem.content_html = `${imageHtml}\\n${jsonItem.content_html}`\n }\n }\n \n return jsonItem\n }),\n }\n const jsonResult = generateJsonFeed(jsonFeed)\n return Promise.resolve(typeof jsonResult === 'string' ? jsonResult : JSON.stringify(jsonResult))\n }\n case 'rss':\n default: {\n // RSS feed requires: title, link, description, items\n const rssFeed = {\n title: feedTitle,\n link: SITE_CONFIG.siteUrl,\n description: SITE_CONFIG.description,\n language: SITE_CONFIG.language,\n copyright: SITE_CONFIG.copyright,\n webMaster: SITE_CONFIG.author?.email,\n pubDate: now,\n lastBuildDate: now,\n items: transformedItems.map((item: any) => {\n // RSS items can have dynamic properties\n const rssItem: any = {\n ...item, // Preserve all dynamic properties\n }\n \n // Ensure required/standard RSS fields\n if (item.title) rssItem.title = item.title\n if (item.link) rssItem.link = item.link\n if (item.description) rssItem.description = item.description\n if (item.date || item.pubDate) rssItem.pubDate = item.date || item.pubDate\n \n // Map guid - RSS requires guid for proper item identification\n if (item.guid) {\n rssItem.guid = {\n value: item.guid,\n isPermaLink: typeof item.guid === 'string' && (item.guid.startsWith('http://') || item.guid.startsWith('https://'))\n }\n } else if (item.id) {\n rssItem.guid = {\n value: item.id,\n isPermaLink: typeof item.id === 'string' && (item.id.startsWith('http://') || item.id.startsWith('https://'))\n }\n }\n \n // Add image from storageTransactionId if available (from enrichment)\n if (item._imageMetadata && item._hasImage) {\n const imageMeta: ImageMetadata = item._imageMetadata\n \n // Add enclosure for RSS (standard RSS 2.0)\n rssItem.enclosures = [{\n url: imageMeta.url,\n type: imageMeta.mimeType || 'image/jpeg',\n length: imageMeta.size,\n }]\n \n // Build comprehensive Media RSS properties\n const mediaContent: any = {\n url: imageMeta.url,\n type: imageMeta.mimeType || 'image/jpeg',\n medium: 'image', // image, video, audio, document\n }\n \n // Add dimensions if available\n if (imageMeta.width) mediaContent.width = imageMeta.width\n if (imageMeta.height) mediaContent.height = imageMeta.height\n if (imageMeta.size) mediaContent.fileSize = imageMeta.size\n if (imageMeta.format) mediaContent.format = imageMeta.format\n \n // Add Media RSS namespace properties\n rssItem['media:content'] = [mediaContent]\n \n // Add media:thumbnail (required by many readers)\n const thumbnail: any = {\n url: imageMeta.url,\n }\n if (imageMeta.width) thumbnail.width = imageMeta.width\n if (imageMeta.height) thumbnail.height = imageMeta.height\n rssItem['media:thumbnail'] = [thumbnail]\n \n // Add media:title (many readers look for this)\n if (item.title) {\n rssItem['media:title'] = item.title\n }\n \n // Add media:description (enhanced)\n const mediaDescription = item.description || item.summary || item.title || ''\n if (mediaDescription) {\n rssItem['media:description'] = mediaDescription\n }\n \n // Add media:keywords for better discoverability\n if (item.title) {\n // Extract keywords from title and description\n const keywords: string[] = []\n if (item.title) {\n keywords.push(...item.title.toLowerCase().split(/\\s+/).filter((w: string) => w.length > 3))\n }\n if (item.description) {\n keywords.push(...item.description.toLowerCase().split(/\\s+/).filter((w: string) => w.length > 3))\n }\n if (keywords.length > 0) {\n rssItem['media:keywords'] = [...new Set(keywords)].slice(0, 10).join(', ')\n }\n }\n \n // Add media:credit (author information)\n if (item.authors && Array.isArray(item.authors) && item.authors.length > 0) {\n rssItem['media:credit'] = item.authors.map((author: any) => ({\n value: author.name || author.displayName || 'Unknown',\n role: 'author',\n }))\n } else if (SITE_CONFIG.author) {\n rssItem['media:credit'] = [{\n value: SITE_CONFIG.author.name,\n role: 'author',\n }]\n }\n \n // Add media:copyright\n if (SITE_CONFIG.copyright) {\n rssItem['media:copyright'] = SITE_CONFIG.copyright\n }\n \n // Add media:category (can be used for content categorization)\n if (schemaName) {\n rssItem['media:category'] = [{\n value: schemaName,\n scheme: 'http://www.schema.org/',\n }]\n }\n \n // Add media:group to bundle all media elements together\n // This is useful for readers that prefer grouped media\n rssItem['media:group'] = [{\n 'media:content': [mediaContent],\n 'media:thumbnail': [thumbnail],\n 'media:title': item.title || 'Untitled',\n 'media:description': mediaDescription,\n }]\n \n // Enhance content:encoded with image if not already present\n if (imageMeta.url && item.content && !item.content.includes(imageMeta.url)) {\n const imageHtml = `<img src=\"${imageMeta.url}\" alt=\"${item.title || ''}\"${imageMeta.width ? ` width=\"${imageMeta.width}\"` : ''}${imageMeta.height ? ` height=\"${imageMeta.height}\"` : ''} />`\n rssItem['content:encoded'] = `${imageHtml}\\n${item.content}`\n }\n }\n \n // Map feature_image to enclosure for RSS (media attachments) - fallback\n if (!rssItem.enclosures && (item.feature_image || item.featureImage)) {\n const imageUrl = item.feature_image || item.featureImage\n // If it's a URL, use it directly; if it's an Arweave transaction ID, construct URL\n const enclosureUrl = typeof imageUrl === 'string' && imageUrl.startsWith('http')\n ? imageUrl\n : getArweaveUrlForTransaction(imageUrl)\n \n rssItem.enclosures = [{\n url: enclosureUrl,\n type: 'image/jpeg', // Default, could be determined from URL or metadata\n }]\n }\n \n // Use Dublin Core namespace for additional metadata\n rssItem.dc = {}\n \n // Add date using dc namespace (supports multiple dates)\n if (item.date || item.pubDate) {\n rssItem.dc.date = item.date || item.pubDate\n }\n if (item.timeCreated) {\n // Convert Unix timestamp to Date for dc namespace\n const timeCreatedDate = new Date(item.timeCreated * 1000)\n if (!rssItem.dc.dates) rssItem.dc.dates = []\n rssItem.dc.dates.push(timeCreatedDate)\n }\n \n // Add identifier using dc namespace\n if (item.seedUid || item.SeedUid) {\n if (!rssItem.dc.identifier) rssItem.dc.identifier = []\n rssItem.dc.identifier.push(item.seedUid || item.SeedUid)\n }\n if (item.storageTransactionId || item.storage_transaction_id) {\n if (!rssItem.dc.identifier) rssItem.dc.identifier = []\n rssItem.dc.identifier.push(item.storageTransactionId || item.storage_transaction_id)\n }\n \n // Add source/relation for import_url\n if (item.import_url || item.importUrl) {\n rssItem.dc.source = item.import_url || item.importUrl\n }\n \n // Include raw custom fields as well (FeedSmith may preserve them)\n if (item.seedUid || item.SeedUid) rssItem.seedUid = item.seedUid || item.SeedUid\n if (item.storageTransactionId || item.storage_transaction_id) {\n rssItem.storageTransactionId = item.storageTransactionId || item.storage_transaction_id\n }\n if (item.timeCreated) rssItem.timeCreated = item.timeCreated\n \n return rssItem\n }),\n }\n return Promise.resolve(generateRssXml(rssFeed))\n }\n }\n}\n\n// ============================================================================\n// Route Handler\n// ============================================================================\n\nfunction getContentType(format: FeedFormat): string {\n switch (format) {\n case 'atom':\n return 'application/atom+xml; charset=utf-8'\n case 'json':\n return 'application/feed+json; charset=utf-8'\n case 'rss':\n default:\n return 'application/rss+xml; charset=utf-8'\n }\n}\n\nfunction parseFormat(segment: string): FeedFormat | null {\n const normalized = segment.toLowerCase()\n if (['rss', 'atom', 'json'].includes(normalized)) {\n return normalized as FeedFormat\n }\n return null\n}\n\nfunction capitalize(str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1)\n}\n\n/**\n * Main route handler for feed generation.\n * \n * URL Pattern: /:collection/:format\n * Examples:\n * - /posts/rss → RSS feed of posts\n * - /posts/atom → Atom feed of posts\n * - /identities/json → JSON feed of identities\n * - /posts/rss?v=1234567890 → RSS feed with cache busting parameter\n */\nexport async function handleFeedRequest(\n collectionSegment: string,\n formatSegment: string,\n ifNoneMatch?: string | null,\n cacheBust?: string\n): Promise<Response> {\n // Validate format\n const format = parseFormat(formatSegment)\n if (!format) {\n return new Response(\n JSON.stringify({ error: `Invalid feed format: ${formatSegment}` }),\n {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n }\n )\n }\n\n // De-pluralize the collection name to get model type\n const schemaName = pluralize.singular(collectionSegment.toLowerCase())\n const collectionName = pluralize(schemaName)\n\n console.log(`Schema name: ${schemaName}`);\n console.log(`Collection name: ${collectionName}`);\n\n const cache = getCacheManager();\n const config = loadCacheConfig();\n\n try {\n // Check if we have cached feed content\n if (config.enabled) {\n const cachedContent = await cache.getFeedContent(schemaName, format);\n \n if (cachedContent) {\n // Check ETag for conditional request\n if (ifNoneMatch && checkIfNoneMatch(ifNoneMatch, cachedContent.etag)) {\n console.log(`Cache hit with ETag match for ${schemaName}:${format} - returning 304`);\n return new Response(null, {\n status: 304,\n headers: {\n 'ETag': cachedContent.etag,\n 'Last-Modified': new Date(cachedContent.lastModified * 1000).toUTCString(),\n 'Cache-Control': 'public, max-age=3600, s-maxage=3600, must-revalidate',\n },\n });\n }\n\n // Return cached content\n console.log(`Cache hit for ${schemaName}:${format}`);\n return new Response(cachedContent.content, {\n status: 200,\n headers: {\n 'Content-Type': cachedContent.contentType,\n 'ETag': cachedContent.etag,\n 'Last-Modified': new Date(cachedContent.lastModified * 1000).toUTCString(),\n 'Cache-Control': 'public, max-age=3600, s-maxage=3600, must-revalidate',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n 'X-Cache': 'HIT',\n },\n });\n }\n }\n\n // Cache miss or disabled - fetch and process items\n console.log(`Cache miss for ${schemaName}:${format} - fetching items`);\n\n // Use refresh lock to prevent concurrent fetches (only if cache is enabled)\n const feedItems = config.enabled\n ? await cache.withRefreshLock(schemaName, async () => {\n // Check cache again after acquiring lock (another request might have populated it)\n const cachedData = await cache.getFeedData(schemaName);\n \n let items: GraphQLItem[];\n \n if (cachedData) {\n // Incremental fetch: only get new items\n console.log(`Incremental fetch: last processed timestamp: ${cachedData.lastProcessedTimestamp}`);\n \n // Fetch all items (SDK might not support filtering by timestamp)\n const allItems = await getFeedItemsBySchemaName(schemaName) as GraphQLItem[];\n\n console.log(`First item: ${JSON.stringify(allItems[0])}`);\n \n // Filter to only new items\n const newItems = cache.filterNewItems(allItems, cachedData.lastProcessedTimestamp);\n \n if (newItems.length > 0) {\n console.log(`Found ${newItems.length} new items, merging with ${cachedData.items.length} cached items`);\n // Merge new items with cached items\n items = cache.mergeItems(cachedData.items, newItems);\n } else {\n console.log(`No new items found, using cached items`);\n // No new items, use cached items\n items = cachedData.items;\n }\n } else {\n // Cold cache - fetch all items\n console.log(`Cold cache - fetching all items`);\n const client = await getClient()\n if (client) {\n console.log(`Client initialized: ${client.isInitialized()}`);\n }\n \n items = await getFeedItemsBySchemaName(schemaName) as GraphQLItem[];\n console.log(`Found ${items.length} feed items for schema ${schemaName}`);\n }\n\n // Enrich items with image metadata if enabled\n let enrichedItems = items;\n if (config.imageMetadata?.enabled) {\n const imageService = new ArweaveImageService({\n gateways: config.imageMetadata.gateways,\n timeout: config.imageMetadata.timeout,\n });\n enrichedItems = await enrichFeedItemsWithMedia(items, imageService, cache);\n }\n\n // Update cache with items (use original items, not enriched, to keep cache clean)\n await cache.setFeedData(schemaName, items);\n \n return enrichedItems;\n })\n : await (async () => {\n // Cache disabled - fetch items directly without caching\n console.log(`Cache disabled - fetching all items directly`);\n const client = await getClient()\n if (client) {\n console.log(`Client initialized: ${client.isInitialized()}`);\n }\n \n const items = await getFeedItemsBySchemaName(schemaName) as GraphQLItem[];\n console.log(`Found ${items.length} feed items for schema ${schemaName}`);\n\n // Enrich items with image metadata if enabled (even when cache is disabled)\n let enrichedItems = items;\n if (config.imageMetadata?.enabled) {\n const imageService = new ArweaveImageService({\n gateways: config.imageMetadata.gateways,\n timeout: config.imageMetadata.timeout,\n });\n enrichedItems = await enrichFeedItemsWithMedia(items, imageService, cache);\n }\n\n return enrichedItems;\n })();\n\n // Generate the feed (pass cache busting parameter if provided)\n const feedContent = await createFeed(feedItems, schemaName, format, cacheBust);\n const contentType = getContentType(format);\n\n // Cache the generated feed content\n if (config.enabled) {\n await cache.setFeedContent(schemaName, format, feedContent, contentType);\n \n // Get the cached content to retrieve ETag\n const cachedContent = await cache.getFeedContent(schemaName, format);\n const etag = cachedContent?.etag || '';\n const lastModified = cachedContent?.lastModified || Math.floor(Date.now() / 1000);\n\n return new Response(feedContent, {\n status: 200,\n headers: {\n 'Content-Type': contentType,\n 'ETag': etag,\n 'Last-Modified': new Date(lastModified * 1000).toUTCString(),\n 'Cache-Control': 'public, max-age=3600, s-maxage=3600, must-revalidate',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n 'X-Cache': 'MISS',\n },\n });\n }\n\n // Cache disabled - return without caching\n return new Response(feedContent, {\n status: 200,\n headers: {\n 'Content-Type': contentType,\n 'Cache-Control': 'public, max-age=3600, s-maxage=3600',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n },\n });\n } catch (error) {\n console.error('Feed generation error:', error);\n \n // Try to serve stale cache on error if available\n if (config.enabled) {\n const staleContent = await cache.getFeedContent(schemaName, format);\n if (staleContent) {\n console.log(`Serving stale cache due to error`);\n return new Response(staleContent.content, {\n status: 200,\n headers: {\n 'Content-Type': staleContent.contentType,\n 'ETag': staleContent.etag,\n 'Cache-Control': 'public, max-age=3600, s-maxage=3600, must-revalidate',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n 'X-Cache': 'STALE',\n 'Warning': '299 - \"Cache is stale\"',\n },\n });\n }\n }\n \n return new Response(\n JSON.stringify({\n error: 'Failed to generate feed',\n message: error instanceof Error ? error.message : 'Unknown error',\n }),\n {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n }\n )\n }\n}"],"names":["loadFeedConfig","itemUrlBase","itemUrlPath","siteUrl","relationValuesToExclude","GET_SEEDS","gql","toCamelCase","str","_","letter","formatRfc822Date","timestamp","date","days","months","day","month","year","dayNum","hours","minutes","seconds","getCollectionPath","schemaName","parseEasRelationPropertyName","easPropertyName","singularProperty","modelName","idSegment","isList","setFeedItemDefaults","item","seedUid","options","storageTransactionIdSnake","storageTransactionIdCamel","storageTransactionId","collectionPath","validSeedUid","defaultLink","getArweaveUrlForTransaction","error","currentLink","currentGuid","pubDate","seedUidToModelType","relatedSeedUids","versionUidToSeedUid","assembledFeedItems","versionsBySeedUid","latestVersionUidsBySeedUid","processItemProperty","property","itemSeeds","metadata","propertyNameSnake","schemaUid","setSchemaUidForSchemaDefinition","isRelation","easType","isBytes32Relation","isNamingConventionRelation","result","value","propertyValue","seed","seedUidForProperty","existingFeedItem","propertyNameCamel","processSeeds","seeds","seedUids","i","modelType","itemVersions","getItemVersionsFromEas","itemVersion","existingVersions","latestVersionUids","versions","latestVersion","a","b","itemProperties","getItemPropertiesFromEas","RESERVED_KEYS","resolveRelationPropertiesToUrls","itemsToProcess","keysToProcess","k","key","uids","v","urls","resolved","uid","related","txId","outputKey","camelKey","getFeedItemsBySchemaName","feedConfig","easClient","EasClient","getSeedsBySchemaName","relatedSeedUidsArray","relatedSeeds","setFeedItemDefaultsOptions","DC_NS","CONTENT_NS","MEDIA_NS","CAPITALIZED_DUPLICATES","isRedundantCapitalizedKey","RSS_ITEM_ORDER","d","needsCdata","addTextOrCdata","parent","parts","serializeItemProperty","itemParent","guid","val","attrs","guidEle","src","title","enc","dc","dcKey","dcVal","ele","localName","textContent","attr","attrVal","mediaEle","group","groupEle","gk","gv","ln","child","ck","cv","authorStr","c","cat","tagName","generateRssXml","rssFeed","doc","create","channel","orderedSet","processedKeys","otherKeys","generateETag","createHash","generateFeedETag","format","lastProcessedTimestamp","itemCount","etagValue","generateContentETag","lastModified","contentLength","etagMatches","etag1","etag2","normalize","etag","parseIfNoneMatch","headerValue","checkIfNoneMatch","ifNoneMatchHeader","currentETag","etags","MemoryCache","config","cached","items","now","lastProcessedItemId","timeCreated","cachedData","content","contentType","expiresAt","cachedContent","keysToDelete","transactionId","imageMetadataTtl","fn","existingLock","lockPromise","FileCache","err","fs","join","imageMetadataDir","filePath","data","files","prefix","suffix","file","imageFiles","CacheManager","cachedItems","newItems","itemMap","itemId","merged","timeA","loadCacheConfig","ttl","cacheDir","cacheDisabledByEnvVar","cacheEnabledByEnvVar","isDev","enabled","backgroundRefresh","refreshInterval","imageMetadataEnabled","imageMetadataGateways","g","DEFAULT_ARWEAVE_GATEWAYS","imageMetadataTimeout","ArweaveImageService","gateways","gateway","url","headResponse","rangeResponse","buffer","dimensions","response","sizeOf","mimeMatch","controller","timeoutId","client","initializationPromise","cacheManager","getCacheManager","resetCacheManager","initializeSeedClient","seedClient","DEFAULT_ARWEAVE_HOST","getClient","teardownSeedClient","SITE_CONFIG","enrichFeedItemsWithMedia","imageService","cache","enrichmentPromises","imageMetadata","enrichedItems","enrichedMap","transformToFeedItems","fallbackUrl","pluralize","itemUrl","pubDateStr","dateValue","feedItem","imageMeta","createFeed","cacheBust","collectionName","feedUrlBase","feedUrl","feedTitle","capitalize","transformedItems","atomFeed","entry","imageHtml","generateAtomFeed","jsonFeed","jsonItem","jsonResult","generateJsonFeed","rssItem","mediaContent","thumbnail","mediaDescription","keywords","w","author","imageUrl","enclosureUrl","timeCreatedDate","getContentType","parseFormat","segment","normalized","handleFeedRequest","collectionSegment","formatSegment","ifNoneMatch","feedItems","allItems","feedContent","staleContent"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/utils/arweaveUrl.ts","../src/config.ts","../src/getFeedItems.ts","../src/rss/generateRssXml.ts","../src/utils/etag.ts","../src/cache/MemoryCache.ts","../src/cache/FileCache.ts","../src/cache/CacheManager.ts","../src/cache/config.ts","../src/services/arweaveImageService.ts","../src/index.ts"],"sourcesContent":["import { BaseArweaveClient } from '@seedprotocol/sdk'\n\n/**\n * Builds an Arweave URL for a transaction ID (without the /raw segment).\n * Returns URLs like https://arweave.net/{transactionId} instead of\n * https://arweave.net/raw/{transactionId}.\n */\nexport function getArweaveUrlForTransaction(transactionId: string): string {\n return `https://${BaseArweaveClient.getHost()}/${transactionId}`\n}\n","/**\n * Load feed configuration from environment variables\n *\n * Environment variables:\n * - FEED_ITEM_URL_BASE: Base URL for attestation links (e.g. https://optimism-sepolia.easscan.org or https://easscan.org).\n * When set, item links use {base}/attestation/view/{uid}. Default: 'https://easscan.org'. Set to override.\n * - FEED_ITEM_URL_PATH: Path segment for attestation links (default: 'attestation/view').\n * Only used when FEED_ITEM_URL_BASE is set.\n * - FEED_SITE_URL: Site URL for fallback when FEED_ITEM_URL_BASE is unset (default: 'https://seedprotocol.io').\n */\nexport function loadFeedConfig(): {\n itemUrlBase: string;\n itemUrlPath: string;\n siteUrl: string;\n} {\n const itemUrlBase = process.env.FEED_ITEM_URL_BASE?.trim() || 'https://optimism-sepolia.easscan.org';\n const itemUrlPath =\n process.env.FEED_ITEM_URL_PATH?.trim() || 'attestation/view';\n const siteUrl =\n process.env.FEED_SITE_URL?.trim() || 'https://seedprotocol.io';\n\n return { itemUrlBase, itemUrlPath, siteUrl };\n}\n","import {\n getSeedsBySchemaName,\n getItemVersionsFromEas,\n getItemPropertiesFromEas,\n EasClient,\n setSchemaUidForSchemaDefinition,\n} from '@seedprotocol/sdk';\nimport { getArweaveUrlForTransaction } from './utils/arweaveUrl';\nimport { gql } from 'graphql-request';\nimport { loadFeedConfig } from './config';\n\nconst relationValuesToExclude = [\n '0x0000000000000000000000000000000000000000000000000000000000000020',\n];\n\nconst GET_SEEDS = gql`\n query GetSeeds($where: AttestationWhereInput!, $take: Int) {\n itemSeeds: attestations(where: $where, orderBy: [{ timeCreated: desc }], take: $take) {\n id\n decodedDataJson\n attester\n schema {\n schemaNames {\n name\n }\n }\n refUID\n revoked\n schemaId\n timeCreated\n isOffchain\n }\n }\n`;\n\ninterface AttestationLike {\n id: string;\n decodedDataJson: string;\n refUID: string;\n schemaId: string;\n timeCreated: number;\n attester?: string;\n schema?: { schemaNames?: Array<{ name: string }> };\n}\n\ntype SetFeedItemDefaultsOptions = {\n itemUrlBase?: string;\n itemUrlPath: string;\n siteUrl: string;\n};\n\n// Helper to convert snake_case to camelCase\nconst toCamelCase = (str: string): string => {\n return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\n};\n\n// Helper to format timestamp as RFC 822 date string for RSS pubDate\nconst formatRfc822Date = (timestamp: number): string => {\n const date = new Date(timestamp * 1000);\n const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];\n const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n\n const day = days[date.getUTCDay()];\n const month = months[date.getUTCMonth()];\n const year = date.getUTCFullYear();\n const dayNum = date.getUTCDate().toString().padStart(2, '0');\n const hours = date.getUTCHours().toString().padStart(2, '0');\n const minutes = date.getUTCMinutes().toString().padStart(2, '0');\n const seconds = date.getUTCSeconds().toString().padStart(2, '0');\n\n return `${day}, ${dayNum} ${month} ${year} ${hours}:${minutes}:${seconds} GMT`;\n};\n\nconst getCollectionPath = (schemaName: string): string => {\n return schemaName === 'image' ? 'images' : schemaName === 'post' ? 'posts' : schemaName.toLowerCase() + 's';\n};\n\n// Inlined from SDK - parses relation property names like \"author_id\" -> { propertyName: \"authors\", modelName: \"author\", isList: false }\nconst parseEasRelationPropertyName = (easPropertyName: string): { propertyName: string; modelName: string; isList: boolean } | null => {\n const [singularProperty, modelName, idSegment] = easPropertyName.split('_');\n if (!singularProperty || !modelName) return null;\n const isList = idSegment === 'ids';\n const propertyName = singularProperty.endsWith('s') ? singularProperty : singularProperty + 's';\n return { propertyName, modelName, isList };\n};\n\n// Helper to set default values for feed items\nconst setFeedItemDefaults = (\n item: Record<string, unknown>,\n seedUid: string,\n schemaName: string,\n options: SetFeedItemDefaultsOptions\n): void => {\n const { itemUrlBase, itemUrlPath, siteUrl } = options;\n\n // Set default title if not present (check both snake_case and camelCase)\n if (!item.title && !item.Title) {\n item.title = seedUid;\n item.Title = seedUid;\n } else if (item.title && !item.Title) {\n item.Title = item.title;\n } else if (item.Title && !item.title) {\n item.title = item.Title;\n }\n\n const storageTransactionIdSnake =\n item.storage_transaction_id &&\n typeof item.storage_transaction_id === 'string' &&\n item.storage_transaction_id.trim() !== '' &&\n item.storage_transaction_id !== 'undefined' &&\n item.storage_transaction_id !== seedUid\n ? (item.storage_transaction_id as string).trim()\n : null;\n const storageTransactionIdCamel =\n item.storageTransactionId &&\n typeof item.storageTransactionId === 'string' &&\n item.storageTransactionId.trim() !== '' &&\n item.storageTransactionId !== 'undefined' &&\n item.storageTransactionId !== seedUid\n ? (item.storageTransactionId as string).trim()\n : null;\n const storageTransactionId = storageTransactionIdSnake || storageTransactionIdCamel;\n\n const collectionPath = getCollectionPath(schemaName);\n const validSeedUid =\n seedUid && typeof seedUid === 'string' && seedUid.trim() !== '' ? seedUid : 'unknown';\n\n let defaultLink: string;\n if (storageTransactionId && storageTransactionId !== seedUid && storageTransactionId.length > 0) {\n try {\n defaultLink = getArweaveUrlForTransaction(storageTransactionId);\n } catch (error) {\n console.error('[feed] [setFeedItemDefaults] Error generating Arweave URL:', error);\n if (itemUrlBase != null) {\n defaultLink = `${itemUrlBase.replace(/\\/$/, '')}/${(itemUrlPath ?? 'attestation/view').replace(/^\\//, '')}/${validSeedUid}`;\n } else {\n defaultLink = `${siteUrl.replace(/\\/$/, '')}/${collectionPath}/${validSeedUid}`;\n }\n }\n } else {\n if (itemUrlBase != null) {\n defaultLink = `${itemUrlBase.replace(/\\/$/, '')}/${(itemUrlPath ?? 'attestation/view').replace(/^\\//, '')}/${validSeedUid}`;\n } else {\n defaultLink = `${siteUrl.replace(/\\/$/, '')}/${collectionPath}/${validSeedUid}`;\n }\n }\n\n const currentLink = (item.link || item.Link) as string | undefined;\n if (!currentLink || currentLink === 'undefined' || (typeof currentLink === 'string' && currentLink.trim() === '')) {\n item.link = defaultLink;\n item.Link = defaultLink;\n } else {\n if (!item.link || item.link === 'undefined') {\n item.link = currentLink;\n }\n if (!item.Link || item.Link === 'undefined') {\n item.Link = currentLink;\n }\n }\n\n const currentGuid = (item.guid || item.Guid) as string | undefined;\n if (!currentGuid || currentGuid === 'undefined' || (typeof currentGuid === 'string' && currentGuid.trim() === '')) {\n item.guid = item.link || defaultLink;\n item.Guid = item.guid;\n } else {\n if (!item.guid || item.guid === 'undefined') {\n item.guid = currentGuid;\n }\n if (!item.Guid || item.Guid === 'undefined') {\n item.Guid = currentGuid;\n }\n }\n\n if (item.timeCreated && !item.pubDate && !item.PubDate) {\n const pubDate = formatRfc822Date(item.timeCreated as number);\n item.pubDate = pubDate;\n item.PubDate = pubDate;\n } else if (item.pubDate && !item.PubDate) {\n item.PubDate = item.pubDate;\n } else if (item.PubDate && !item.pubDate) {\n item.pubDate = item.PubDate;\n }\n\n if (!item.seedUid && !item.SeedUid) {\n item.seedUid = seedUid;\n item.SeedUid = seedUid;\n } else if (item.seedUid && !item.SeedUid) {\n item.SeedUid = item.seedUid;\n } else if (item.SeedUid && !item.seedUid) {\n item.seedUid = item.SeedUid;\n }\n\n if (item.attester && !item.Attester) {\n item.Attester = item.attester;\n } else if (item.Attester && !item.attester) {\n item.attester = item.Attester;\n }\n};\n\nconst seedUidToModelType = new Map<string, string>();\nconst relatedSeedUids = new Set<string>();\nconst versionUidToSeedUid = new Map<string, string>();\nconst assembledFeedItems = new Map<string, Record<string, unknown>>();\nconst versionsBySeedUid = new Map<string, AttestationLike[]>();\nconst latestVersionUidsBySeedUid = new Map<string, string>();\n\nconst processItemProperty = async (\n property: AttestationLike,\n itemSeeds: AttestationLike[]\n): Promise<void> => {\n let metadata: { name: string; value: string | string[]; type?: string };\n try {\n metadata = JSON.parse(property.decodedDataJson)[0]?.value ?? {};\n } catch (error) {\n console.error('[feed] [processItemProperty] Error parsing metadata:', error);\n return;\n }\n\n let propertyNameSnake = metadata.name;\n if (!propertyNameSnake) {\n return;\n }\n\n const schemaUid = property.schemaId;\n setSchemaUidForSchemaDefinition({\n text: propertyNameSnake,\n schemaUid,\n });\n\n let isRelation = false;\n let isList = false;\n const easType = metadata.type;\n const isBytes32Relation =\n (easType === 'bytes32' || easType === 'bytes32[]') &&\n propertyNameSnake !== 'storage_transaction_id' &&\n propertyNameSnake !== 'storage_provider_transaction_id';\n const isNamingConventionRelation =\n !isBytes32Relation &&\n (propertyNameSnake.endsWith('_id') || propertyNameSnake.endsWith('_ids')) &&\n propertyNameSnake !== 'storage_transaction_id' &&\n propertyNameSnake !== 'storage_provider_transaction_id';\n\n if (isBytes32Relation || isNamingConventionRelation) {\n isRelation = true;\n if (Array.isArray(metadata.value)) {\n isList = true;\n if (isNamingConventionRelation) {\n const result = parseEasRelationPropertyName(propertyNameSnake);\n if (result) {\n propertyNameSnake = result.propertyName;\n }\n }\n metadata.value.forEach((value: string) => {\n if (!relationValuesToExclude.includes(value)) relatedSeedUids.add(value);\n });\n } else if (!relationValuesToExclude.includes(metadata.value as string)) {\n relatedSeedUids.add(metadata.value as string);\n }\n }\n\n let propertyValue = metadata.value;\n if (typeof propertyValue !== 'string') {\n propertyValue = JSON.stringify(propertyValue);\n }\n\n if (isRelation && !isList) {\n const relatedSeed = itemSeeds.find((seed) => seed.id === metadata.value);\n if (relatedSeed?.schema?.schemaNames) {\n // refSeedType and refSchemaUid used for type resolution - not needed for feed assembly\n }\n }\n\n if (isRelation && isList) {\n const relatedSeeds = itemSeeds.filter((seed) =>\n Array.isArray(metadata.value) && metadata.value.includes(seed.id)\n );\n if (relatedSeeds.length > 0) {\n // refSeedType and refSchemaUid used for type resolution - not needed for feed assembly\n }\n }\n\n const seedUidForProperty = versionUidToSeedUid.get(property.refUID);\n if (!seedUidForProperty) {\n return;\n }\n\n const existingFeedItem = assembledFeedItems.get(seedUidForProperty) || {};\n existingFeedItem[propertyNameSnake] = propertyValue;\n const propertyNameCamel = toCamelCase(propertyNameSnake);\n if (propertyNameCamel !== propertyNameSnake) {\n existingFeedItem[propertyNameCamel] = propertyValue;\n }\n assembledFeedItems.set(seedUidForProperty, existingFeedItem);\n};\n\nconst processSeeds = async (seeds: AttestationLike[]): Promise<void> => {\n const seedUids = seeds.map((seed) => seed.id);\n\n for (let i = 0; i < seeds.length; i++) {\n const seed = seeds[i];\n if (!seed) continue;\n seedUids.push(seed.id);\n const modelType = seed.schema?.schemaNames?.[0]?.name ?? 'unknown';\n seedUidToModelType.set(seed.id, modelType);\n\n if (!assembledFeedItems.has(seed.id)) {\n assembledFeedItems.set(seed.id, {\n seedUid: seed.id,\n timeCreated: seed.timeCreated,\n attester: seed.attester,\n });\n }\n }\n\n const itemVersions = await getItemVersionsFromEas({ seedUids });\n\n for (let i = 0; i < itemVersions.length; i++) {\n const itemVersion = itemVersions[i] as AttestationLike;\n const seedUid = itemVersion.refUID;\n versionUidToSeedUid.set(itemVersion.id, seedUid);\n const existingVersions = versionsBySeedUid.get(seedUid) || [];\n versionsBySeedUid.set(seedUid, [...existingVersions, itemVersion]);\n }\n\n const latestVersionUids: string[] = [];\n for (const [seedUid, versions] of versionsBySeedUid.entries()) {\n const sortedVersions = [...versions].sort((a, b) => b.timeCreated - a.timeCreated);\n const latestVersion = sortedVersions[0];\n if (latestVersion) {\n latestVersionUids.push(latestVersion.id);\n latestVersionUidsBySeedUid.set(seedUid, latestVersion.id);\n }\n }\n\n const itemProperties = await getItemPropertiesFromEas({ versionUids: latestVersionUids });\n\n for (let i = 0; i < itemProperties.length; i++) {\n await processItemProperty(itemProperties[i] as AttestationLike, seeds);\n }\n};\n\nconst RESERVED_KEYS = new Set([\n 'seedUid',\n 'SeedUid',\n 'timeCreated',\n 'attester',\n 'Attester',\n 'storage_transaction_id',\n 'storage_provider_transaction_id',\n 'storageTransactionId',\n 'storageProviderTransactionId',\n]);\n\n/**\n * Resolves relation properties to Arweave URLs when the related item has a\n * storageTransactionId. Detects relations by value-in-assembledFeedItems\n * (value is an attestation UID we fetched). Supports optional _id/_ids key\n * normalization for backward compatibility.\n */\nfunction resolveRelationPropertiesToUrls(schemaName: string): void {\n const itemsToProcess = Array.from(assembledFeedItems.entries()).filter(\n ([seedUid]) => seedUidToModelType.get(seedUid) === schemaName\n );\n\n for (const [, item] of itemsToProcess) {\n const keysToProcess = Object.keys(item).filter(\n (k) => !k.startsWith('_') && !RESERVED_KEYS.has(k)\n );\n\n for (const key of keysToProcess) {\n const value = item[key];\n const isList = Array.isArray(value);\n\n if (isList) {\n const uids = value as unknown[];\n const hasRelationUid = uids.some(\n (v) =>\n typeof v === 'string' &&\n !relationValuesToExclude.includes(v) &&\n assembledFeedItems.has(v)\n );\n if (!hasRelationUid) continue;\n\n const urls: string[] = [];\n let resolved = false;\n for (const uid of uids) {\n if (typeof uid !== 'string' || relationValuesToExclude.includes(uid)) {\n urls.push(String(uid));\n continue;\n }\n const related = assembledFeedItems.get(uid);\n const txId =\n (related?.storageTransactionId ?? related?.storage_transaction_id) as string | undefined;\n if (txId && typeof txId === 'string' && txId.trim()) {\n try {\n urls.push(getArweaveUrlForTransaction(txId));\n resolved = true;\n } catch {\n urls.push(uid);\n }\n } else {\n urls.push(uid);\n }\n }\n if (resolved) {\n const outputKey = key.endsWith('_ids') ? key.replace(/_ids$/, '') : key;\n item[outputKey] = urls;\n if (outputKey !== key) {\n delete item[key];\n const camelKey = toCamelCase(key);\n if (camelKey !== key) delete item[camelKey];\n }\n }\n } else if (typeof value === 'string') {\n if (relationValuesToExclude.includes(value)) continue;\n if (!assembledFeedItems.has(value)) continue;\n\n const related = assembledFeedItems.get(value);\n const txId =\n (related?.storageTransactionId ?? related?.storage_transaction_id) as string | undefined;\n if (txId && typeof txId === 'string' && txId.trim()) {\n try {\n const outputKey = key.endsWith('_id') ? key.replace(/_id$/, '') : key;\n item[outputKey] = getArweaveUrlForTransaction(txId);\n if (outputKey !== key) {\n delete item[key];\n const camelKey = toCamelCase(key);\n if (camelKey !== key) delete item[camelKey];\n }\n } catch {\n // keep original on error\n }\n }\n }\n }\n }\n}\n\nexport const getFeedItemsBySchemaName = async (schemaName: string): Promise<Record<string, unknown>[]> => {\n const feedConfig = loadFeedConfig();\n const easClient = EasClient.getEasClient();\n\n const seeds = (await getSeedsBySchemaName(schemaName)) as AttestationLike[];\n await processSeeds(seeds);\n\n const relatedSeedUidsArray = Array.from(relatedSeedUids);\n const { itemSeeds: relatedSeeds } = await easClient.request(GET_SEEDS, {\n where: {\n id: {\n in: relatedSeedUidsArray,\n },\n },\n });\n\n await processSeeds((relatedSeeds ?? []) as AttestationLike[]);\n\n resolveRelationPropertiesToUrls(schemaName);\n\n const setFeedItemDefaultsOptions: SetFeedItemDefaultsOptions = {\n itemUrlBase: feedConfig.itemUrlBase,\n itemUrlPath: feedConfig.itemUrlPath,\n siteUrl: feedConfig.siteUrl,\n };\n\n const feedItems = Array.from(assembledFeedItems.entries())\n .filter(([seedUid]) => seedUidToModelType.get(seedUid) === schemaName)\n .map(([, item]) => {\n const seedUid = (item.seedUid || item.SeedUid) as string;\n setFeedItemDefaults(item, seedUid, schemaName, setFeedItemDefaultsOptions);\n return item;\n });\n\n return feedItems;\n};\n","import { create } from 'xmlbuilder2'\nimport type { XMLBuilder } from 'xmlbuilder2/lib/interfaces'\n\nconst DC_NS = 'http://purl.org/dc/elements/1.1/'\nconst CONTENT_NS = 'http://purl.org/rss/1.0/modules/content/'\nconst MEDIA_NS = 'http://search.yahoo.com/mrss/'\n\nconst CAPITALIZED_DUPLICATES = new Set(['Title', 'Link', 'Guid', 'PubDate', 'SeedUid'])\n\nfunction isRedundantCapitalizedKey(key: string, item: Record<string, unknown>): boolean {\n if (!CAPITALIZED_DUPLICATES.has(key)) return false\n const lowerKey = key.charAt(0).toLowerCase() + key.slice(1)\n return lowerKey in item\n}\n\nconst RSS_ITEM_ORDER = [\n 'title',\n 'link',\n 'description',\n 'guid',\n 'pubDate',\n 'authors',\n 'categories',\n 'comments',\n 'enclosures',\n 'source',\n 'dc',\n 'content:encoded',\n 'media:content',\n 'media:thumbnail',\n 'media:title',\n 'media:description',\n 'media:keywords',\n 'media:credit',\n 'media:copyright',\n 'media:category',\n 'media:group',\n]\n\nfunction formatRfc822Date(date: Date | string): string {\n if (date instanceof Date) {\n return date.toUTCString()\n }\n if (typeof date === 'string') {\n const d = new Date(date)\n return !Number.isNaN(d.getTime()) ? d.toUTCString() : date\n }\n return String(date)\n}\n\nfunction needsCdata(value: string): boolean {\n return /[<>&]|]]>/.test(value)\n}\n\nfunction addTextOrCdata(parent: XMLBuilder, value: string): void {\n const str = String(value)\n if (str === '') return\n\n if (needsCdata(str)) {\n if (str.includes(']]>')) {\n const parts = str.split(']]>')\n for (let i = 0; i < parts.length; i++) {\n parent.dat(parts[i]!)\n if (i < parts.length - 1) {\n parent.txt(']]>')\n }\n }\n } else {\n parent.dat(str)\n }\n } else {\n parent.txt(str)\n }\n}\n\nfunction serializeItemProperty(\n itemParent: XMLBuilder,\n key: string,\n value: unknown,\n item: Record<string, unknown>\n): void {\n if (value === undefined || value === null || key.startsWith('_')) return\n if (key === 'items') return\n\n if (key === 'guid') {\n const guid = value as { value?: string; isPermaLink?: boolean }\n const val = guid?.value ?? String(value)\n if (!val) return\n const attrs: Record<string, string> = {}\n if (typeof guid?.isPermaLink === 'boolean') {\n attrs.isPermaLink = guid.isPermaLink ? 'true' : 'false'\n }\n const guidEle = itemParent.ele('guid', attrs)\n addTextOrCdata(guidEle, val)\n return\n }\n\n if (key === 'source' && value !== null && typeof value === 'object') {\n const src = value as { title?: string; url?: string }\n const title = src.title ?? String(value)\n if (!title) return\n const attrs = src.url ? { url: src.url } : {}\n itemParent.ele('source', attrs).txt(title)\n return\n }\n\n if (key === 'enclosures' && Array.isArray(value)) {\n for (const enc of value) {\n if (enc && typeof enc === 'object' && 'url' in enc) {\n const attrs: Record<string, string> = {\n url: String(enc.url),\n type: String(enc.type ?? 'application/octet-stream'),\n }\n if (enc.length != null) attrs.length = String(enc.length)\n itemParent.ele('enclosure', attrs)\n }\n }\n return\n }\n\n if (key === 'dc' && value !== null && typeof value === 'object') {\n const dc = value as Record<string, unknown>\n for (const [dcKey, dcVal] of Object.entries(dc)) {\n if (dcVal === undefined || dcVal === null) continue\n if (Array.isArray(dcVal)) {\n for (const v of dcVal) {\n if (v !== undefined && v !== null) {\n const ele = itemParent.ele(DC_NS, dcKey)\n if (v instanceof Date) {\n ele.txt(v.toISOString())\n } else {\n addTextOrCdata(ele, String(v))\n }\n }\n }\n } else {\n const ele = itemParent.ele(DC_NS, dcKey)\n if (dcVal instanceof Date) {\n ele.txt(dcVal.toISOString())\n } else {\n addTextOrCdata(ele, String(dcVal))\n }\n }\n }\n return\n }\n\n if (key === 'content:encoded') {\n const ele = itemParent.ele(CONTENT_NS, 'encoded')\n addTextOrCdata(ele, String(value))\n return\n }\n if (key === 'content' && typeof value === 'string' && !('content:encoded' in item)) {\n const ele = itemParent.ele(CONTENT_NS, 'encoded')\n addTextOrCdata(ele, String(value))\n return\n }\n if (key === 'content') return\n\n if (key.startsWith('media:') && Array.isArray(value)) {\n const localName = key.slice(6)\n for (const item of value) {\n if (item === null || typeof item !== 'object') continue\n const attrs: Record<string, string> = {}\n let textContent: string | null = null\n for (const [attr, attrVal] of Object.entries(item)) {\n if (attrVal === undefined || attrVal === null) continue\n if (attr === 'value') {\n textContent = String(attrVal)\n } else {\n attrs[attr] = String(attrVal)\n }\n }\n const mediaEle = itemParent.ele(MEDIA_NS, localName, attrs)\n if (textContent !== null) {\n addTextOrCdata(mediaEle, textContent)\n }\n }\n return\n }\n\n if (key.startsWith('media:') && typeof value === 'string') {\n const localName = key.slice(6)\n const ele = itemParent.ele(MEDIA_NS, localName)\n addTextOrCdata(ele, value)\n return\n }\n\n if (key === 'media:group' && Array.isArray(value)) {\n for (const group of value) {\n if (group && typeof group === 'object') {\n const groupEle = itemParent.ele(MEDIA_NS, 'group')\n for (const [gk, gv] of Object.entries(group)) {\n if (gv === undefined || gv === null) continue\n if (gk.startsWith('media:')) {\n const ln = gk.slice(6)\n if (Array.isArray(gv)) {\n for (const v of gv) {\n if (v && typeof v === 'object') {\n const child = groupEle.ele(MEDIA_NS, ln)\n for (const [ck, cv] of Object.entries(v)) {\n if (cv !== undefined && cv !== null) {\n if (ck === 'url' || ck === 'value') {\n addTextOrCdata(child, String(cv))\n } else {\n child.att(ck, String(cv))\n }\n }\n }\n }\n }\n } else if (typeof gv === 'string') {\n addTextOrCdata(groupEle.ele(MEDIA_NS, ln), gv)\n }\n }\n }\n }\n }\n return\n }\n\n if (value instanceof Date) {\n const ele = itemParent.ele(key)\n ele.txt(formatRfc822Date(value))\n return\n }\n\n if (key === 'authors' && Array.isArray(value)) {\n for (const a of value) {\n const authorStr =\n typeof a === 'string'\n ? a\n : a && typeof a === 'object' && 'name' in a\n ? `${(a as { email?: string }).email ?? ''} (${(a as { name?: string }).name ?? ''})`.trim() ||\n (a as { name?: string }).name\n : String(a)\n if (authorStr) itemParent.ele('author').txt(authorStr)\n }\n return\n }\n if (key === 'categories' && Array.isArray(value)) {\n for (const c of value) {\n const cat =\n c && typeof c === 'object' && 'name' in c ? (c as { name: string }).name : String(c)\n if (cat) {\n const attrs =\n c && typeof c === 'object' && 'domain' in c && (c as { domain?: string }).domain\n ? { domain: (c as { domain: string }).domain }\n : {}\n itemParent.ele('category', attrs).txt(cat)\n }\n }\n return\n }\n\n // Array of strings (e.g. resolved relation URLs for list relations like images)\n if (Array.isArray(value) && value.every((v) => typeof v === 'string')) {\n const tagName = key.endsWith('s') ? key.slice(0, -1) : key\n for (const v of value) {\n if (v) {\n const ele = itemParent.ele(tagName)\n addTextOrCdata(ele, v)\n }\n }\n return\n }\n\n const ele = itemParent.ele(key)\n addTextOrCdata(ele, String(value))\n}\n\nexport interface RssFeedInput {\n title: string\n link?: string\n description: string\n language?: string\n copyright?: string\n webMaster?: string\n pubDate?: Date\n lastBuildDate?: Date\n items: Record<string, unknown>[]\n}\n\nexport function generateRssXml(rssFeed: RssFeedInput): string {\n const doc = create({ version: '1.0', encoding: 'UTF-8' })\n const rss = doc.ele('rss', { version: '2.0' })\n const channel = rss.ele('channel', {\n 'xmlns:dc': DC_NS,\n 'xmlns:content': CONTENT_NS,\n 'xmlns:media': MEDIA_NS,\n })\n\n channel.ele('title').txt(rssFeed.title)\n if (rssFeed.link) channel.ele('link').txt(rssFeed.link)\n channel.ele('description').txt(rssFeed.description)\n if (rssFeed.language) channel.ele('language').txt(rssFeed.language)\n if (rssFeed.copyright) channel.ele('copyright').txt(rssFeed.copyright)\n if (rssFeed.webMaster) channel.ele('webMaster').txt(rssFeed.webMaster)\n if (rssFeed.pubDate) channel.ele('pubDate').txt(formatRfc822Date(rssFeed.pubDate))\n if (rssFeed.lastBuildDate) channel.ele('lastBuildDate').txt(formatRfc822Date(rssFeed.lastBuildDate))\n\n const orderedSet = new Set(RSS_ITEM_ORDER)\n\n for (const item of rssFeed.items) {\n const itemParent = channel.ele('item')\n const processedKeys = new Set<string>()\n\n for (const key of RSS_ITEM_ORDER) {\n if (key in item) {\n serializeItemProperty(itemParent, key, item[key], item)\n processedKeys.add(key)\n }\n }\n const otherKeys = Object.keys(item)\n .filter(\n (k) =>\n !processedKeys.has(k) &&\n !orderedSet.has(k) &&\n !k.startsWith('_') &&\n !isRedundantCapitalizedKey(k, item)\n )\n .sort()\n for (const key of otherKeys) {\n serializeItemProperty(itemParent, key, item[key], item)\n }\n }\n\n return doc.end({ prettyPrint: true })\n}\n","import { createHash } from 'crypto';\n\n/**\n * Generate an ETag from a string value\n * Uses MD5 hash for consistency and performance\n */\nexport function generateETag(value: string): string {\n const hash = createHash('md5').update(value).digest('hex');\n return `\"${hash.substring(0, 16)}\"`;\n}\n\n/**\n * Generate an ETag for feed data based on schema, format, timestamp, and item count\n */\nexport function generateFeedETag(\n schemaName: string,\n format: string,\n lastProcessedTimestamp: number,\n itemCount: number\n): string {\n const etagValue = `${schemaName}-${format}-${lastProcessedTimestamp}-${itemCount}`;\n return generateETag(etagValue);\n}\n\n/**\n * Generate an ETag for cached feed content\n */\nexport function generateContentETag(\n schemaName: string,\n format: string,\n lastModified: number,\n contentLength: number\n): string {\n const etagValue = `${schemaName}-${format}-${lastModified}-${contentLength}`;\n return generateETag(etagValue);\n}\n\n/**\n * Check if an ETag matches the provided value\n * Handles both quoted and unquoted ETags\n */\nexport function etagMatches(etag1: string, etag2: string): boolean {\n // Remove quotes if present\n const normalize = (etag: string) => etag.replace(/^\"|\"$/g, '');\n return normalize(etag1) === normalize(etag2);\n}\n\n/**\n * Parse If-None-Match header value\n * Returns array of ETags (can be comma-separated)\n */\nexport function parseIfNoneMatch(headerValue: string | null | undefined): string[] {\n if (!headerValue) {\n return [];\n }\n return headerValue\n .split(',')\n .map(etag => etag.trim())\n .filter(etag => etag.length > 0);\n}\n\n/**\n * Check if any of the provided ETags match the current ETag\n */\nexport function checkIfNoneMatch(\n ifNoneMatchHeader: string | null | undefined,\n currentETag: string\n): boolean {\n const etags = parseIfNoneMatch(ifNoneMatchHeader);\n if (etags.length === 0) {\n return false;\n }\n \n // Check for wildcard\n if (etags.includes('*')) {\n return true;\n }\n \n // Check if any ETag matches\n return etags.some(etag => etagMatches(etag, currentETag));\n}\n","import type { CachedFeedData, CachedFeedContent, CacheConfig, CachedImageMetadata } from './types';\nimport type { GraphQLItem, ImageMetadata } from '../types';\nimport { generateFeedETag, generateContentETag } from '../utils/etag';\n\n/**\n * In-memory cache implementation with TTL support\n */\nexport class MemoryCache {\n private feedDataCache: Map<string, CachedFeedData> = new Map();\n private feedContentCache: Map<string, CachedFeedContent> = new Map();\n private imageMetadataCache: Map<string, CachedImageMetadata> = new Map();\n private config: CacheConfig;\n private refreshLocks: Map<string, Promise<void>> = new Map();\n\n constructor(config: CacheConfig) {\n this.config = config;\n }\n\n /**\n * Get cached feed data for a schema\n */\n getFeedData(schemaName: string): CachedFeedData | null {\n const cached = this.feedDataCache.get(schemaName);\n if (!cached) {\n return null;\n }\n\n // Check if cache is expired\n const now = Math.floor(Date.now() / 1000);\n const age = now - cached.lastUpdated;\n if (age > this.config.ttl) {\n this.feedDataCache.delete(schemaName);\n // Also clear related content cache\n this.clearContentCache(schemaName);\n return null;\n }\n\n return cached;\n }\n\n /**\n * Set cached feed data for a schema\n */\n setFeedData(schemaName: string, items: GraphQLItem[]): void {\n const now = Math.floor(Date.now() / 1000);\n \n // Find the newest item by timeCreated\n let lastProcessedTimestamp = 0;\n let lastProcessedItemId = '';\n \n for (const item of items) {\n const timeCreated = (item as any).timeCreated;\n if (timeCreated && timeCreated > lastProcessedTimestamp) {\n lastProcessedTimestamp = timeCreated;\n lastProcessedItemId = item.id || (item as any).seedUid || (item as any).SeedUid || '';\n }\n }\n\n // If no timeCreated found, use current time\n if (lastProcessedTimestamp === 0) {\n lastProcessedTimestamp = now;\n }\n\n const etag = generateFeedETag(schemaName, 'data', lastProcessedTimestamp, items.length);\n\n const cachedData: CachedFeedData = {\n items: [...items], // Create a copy\n lastProcessedTimestamp,\n lastProcessedItemId,\n lastUpdated: now,\n etag,\n };\n\n this.feedDataCache.set(schemaName, cachedData);\n }\n\n /**\n * Get cached feed content for a schema and format\n */\n getFeedContent(schemaName: string, format: string): CachedFeedContent | null {\n const key = `${schemaName}:${format}`;\n const cached = this.feedContentCache.get(key);\n if (!cached) {\n return null;\n }\n\n // Check if cache is expired\n const now = Math.floor(Date.now() / 1000);\n if (now > cached.expiresAt) {\n this.feedContentCache.delete(key);\n return null;\n }\n\n return cached;\n }\n\n /**\n * Set cached feed content for a schema and format\n */\n setFeedContent(\n schemaName: string,\n format: string,\n content: string,\n contentType: string\n ): void {\n const key = `${schemaName}:${format}`;\n const now = Math.floor(Date.now() / 1000);\n const expiresAt = now + this.config.ttl;\n\n const etag = generateContentETag(schemaName, format, now, content.length);\n\n const cachedContent: CachedFeedContent = {\n content,\n contentType,\n etag,\n lastModified: now,\n expiresAt,\n };\n\n this.feedContentCache.set(key, cachedContent);\n }\n\n /**\n * Clear feed data cache for a schema\n */\n clearFeedData(schemaName: string): void {\n this.feedDataCache.delete(schemaName);\n }\n\n /**\n * Clear content cache for a schema (all formats)\n */\n clearContentCache(schemaName: string): void {\n const keysToDelete: string[] = [];\n for (const key of this.feedContentCache.keys()) {\n if (key.startsWith(`${schemaName}:`)) {\n keysToDelete.push(key);\n }\n }\n keysToDelete.forEach(key => this.feedContentCache.delete(key));\n }\n\n /**\n * Get cached image metadata for a transaction ID\n */\n getImageMetadata(transactionId: string): ImageMetadata | null {\n const cached = this.imageMetadataCache.get(transactionId);\n if (!cached) {\n return null;\n }\n\n // Check if cache is expired\n const now = Math.floor(Date.now() / 1000);\n if (now > cached.expiresAt) {\n this.imageMetadataCache.delete(transactionId);\n return null;\n }\n\n return cached.metadata;\n }\n\n /**\n * Set cached image metadata for a transaction ID\n */\n setImageMetadata(transactionId: string, metadata: ImageMetadata): void {\n const now = Math.floor(Date.now() / 1000);\n const imageMetadataTtl = this.config.imageMetadata?.ttl || 604800; // Default 7 days\n const expiresAt = now + imageMetadataTtl;\n\n const cached: CachedImageMetadata = {\n metadata,\n cachedAt: now,\n expiresAt,\n };\n\n this.imageMetadataCache.set(transactionId, cached);\n }\n\n /**\n * Clear all caches\n */\n clearAll(): void {\n this.feedDataCache.clear();\n this.feedContentCache.clear();\n this.imageMetadataCache.clear();\n }\n\n /**\n * Get or create a refresh lock for a schema\n * Prevents concurrent fetches for the same schema\n */\n async withRefreshLock<T>(\n schemaName: string,\n fn: () => Promise<T>\n ): Promise<T> {\n const existingLock = this.refreshLocks.get(schemaName);\n if (existingLock) {\n // Wait for existing refresh to complete\n await existingLock;\n // Try to get cached data after waiting\n const cached = this.getFeedData(schemaName);\n if (cached) {\n // Re-fetch might not be needed, but we'll let the caller decide\n // For now, we'll proceed with the function\n }\n }\n\n // Create new lock\n const lockPromise = (async () => {\n try {\n return await fn();\n } finally {\n this.refreshLocks.delete(schemaName);\n }\n })();\n\n this.refreshLocks.set(schemaName, lockPromise as Promise<void>);\n return lockPromise;\n }\n\n /**\n * Update configuration\n */\n updateConfig(config: Partial<CacheConfig>): void {\n this.config = { ...this.config, ...config };\n }\n\n /**\n * Get cache statistics\n */\n getStats(): {\n feedDataCount: number;\n feedContentCount: number;\n imageMetadataCount: number;\n activeLocks: number;\n } {\n return {\n feedDataCount: this.feedDataCache.size,\n feedContentCount: this.feedContentCache.size,\n imageMetadataCount: this.imageMetadataCache.size,\n activeLocks: this.refreshLocks.size,\n };\n }\n}\n","import { promises as fs } from 'fs';\nimport { join } from 'path';\nimport type { CachedFeedData, CachedFeedContent, CacheConfig, CachedImageMetadata } from './types';\nimport type { GraphQLItem, ImageMetadata } from '../types';\n\n/**\n * File-based persistent cache implementation\n */\nexport class FileCache {\n private cacheDir: string;\n private config: CacheConfig;\n\n constructor(config: CacheConfig) {\n this.cacheDir = config.cacheDir;\n this.config = config;\n // Ensure cache directory exists\n this.ensureCacheDir().catch(err => {\n console.error('Failed to create cache directory:', err);\n });\n }\n\n /**\n * Ensure cache directory exists\n */\n private async ensureCacheDir(): Promise<void> {\n try {\n await fs.mkdir(this.cacheDir, { recursive: true });\n } catch (error) {\n // Directory might already exist, which is fine\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {\n throw error;\n }\n }\n }\n\n /**\n * Get file path for feed data\n */\n private getFeedDataPath(schemaName: string): string {\n return join(this.cacheDir, `${schemaName}.json`);\n }\n\n /**\n * Get file path for feed content\n */\n private getFeedContentPath(schemaName: string, format: string): string {\n return join(this.cacheDir, `${schemaName}-${format}.json`);\n }\n\n /**\n * Get file path for image metadata\n */\n private getImageMetadataPath(transactionId: string): string {\n const imageMetadataDir = join(this.cacheDir, 'image-metadata');\n return join(imageMetadataDir, `${transactionId}.json`);\n }\n\n /**\n * Ensure image metadata directory exists\n */\n private async ensureImageMetadataDir(): Promise<void> {\n try {\n const imageMetadataDir = join(this.cacheDir, 'image-metadata');\n await fs.mkdir(imageMetadataDir, { recursive: true });\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {\n throw error;\n }\n }\n }\n\n /**\n * Get cached feed data for a schema\n */\n async getFeedData(schemaName: string): Promise<CachedFeedData | null> {\n try {\n const filePath = this.getFeedDataPath(schemaName);\n const data = await fs.readFile(filePath, 'utf-8');\n const cached: CachedFeedData = JSON.parse(data);\n\n // Check if cache is expired\n const now = Math.floor(Date.now() / 1000);\n const age = now - cached.lastUpdated;\n if (age > this.config.ttl) {\n // Cache expired, delete file\n await this.clearFeedData(schemaName);\n return null;\n }\n\n return cached;\n } catch (error) {\n // File doesn't exist or is corrupted\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return null;\n }\n console.error(`Error reading feed data cache for ${schemaName}:`, error);\n return null;\n }\n }\n\n /**\n * Set cached feed data for a schema\n */\n async setFeedData(schemaName: string, data: CachedFeedData): Promise<void> {\n try {\n await this.ensureCacheDir();\n const filePath = this.getFeedDataPath(schemaName);\n await fs.writeFile(filePath, JSON.stringify(data, null, 2), 'utf-8');\n } catch (error) {\n console.error(`Error writing feed data cache for ${schemaName}:`, error);\n // Don't throw - file cache is best effort\n }\n }\n\n /**\n * Get cached feed content for a schema and format\n */\n async getFeedContent(\n schemaName: string,\n format: string\n ): Promise<CachedFeedContent | null> {\n try {\n const filePath = this.getFeedContentPath(schemaName, format);\n const data = await fs.readFile(filePath, 'utf-8');\n const cached: CachedFeedContent = JSON.parse(data);\n\n // Check if cache is expired\n const now = Math.floor(Date.now() / 1000);\n if (now > cached.expiresAt) {\n // Cache expired, delete file\n await this.clearFeedContent(schemaName, format);\n return null;\n }\n\n return cached;\n } catch (error) {\n // File doesn't exist or is corrupted\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return null;\n }\n console.error(\n `Error reading feed content cache for ${schemaName}:${format}:`,\n error\n );\n return null;\n }\n }\n\n /**\n * Set cached feed content for a schema and format\n */\n async setFeedContent(\n schemaName: string,\n format: string,\n content: CachedFeedContent\n ): Promise<void> {\n try {\n await this.ensureCacheDir();\n const filePath = this.getFeedContentPath(schemaName, format);\n await fs.writeFile(filePath, JSON.stringify(content, null, 2), 'utf-8');\n } catch (error) {\n console.error(\n `Error writing feed content cache for ${schemaName}:${format}:`,\n error\n );\n // Don't throw - file cache is best effort\n }\n }\n\n /**\n * Clear feed data cache for a schema\n */\n async clearFeedData(schemaName: string): Promise<void> {\n try {\n const filePath = this.getFeedDataPath(schemaName);\n await fs.unlink(filePath);\n } catch (error) {\n // File might not exist, which is fine\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n console.error(`Error clearing feed data cache for ${schemaName}:`, error);\n }\n }\n }\n\n /**\n * Clear content cache for a schema and format\n */\n async clearFeedContent(schemaName: string, format: string): Promise<void> {\n try {\n const filePath = this.getFeedContentPath(schemaName, format);\n await fs.unlink(filePath);\n } catch (error) {\n // File might not exist, which is fine\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n console.error(\n `Error clearing feed content cache for ${schemaName}:${format}:`,\n error\n );\n }\n }\n }\n\n /**\n * Clear all content cache for a schema (all formats)\n */\n async clearAllContentCache(schemaName: string): Promise<void> {\n try {\n const files = await fs.readdir(this.cacheDir);\n const prefix = `${schemaName}-`;\n const suffix = '.json';\n\n for (const file of files) {\n if (file.startsWith(prefix) && file.endsWith(suffix) && file !== `${schemaName}.json`) {\n await fs.unlink(join(this.cacheDir, file));\n }\n }\n } catch (error) {\n console.error(`Error clearing all content cache for ${schemaName}:`, error);\n }\n }\n\n /**\n * Get cached image metadata for a transaction ID\n */\n async getImageMetadata(transactionId: string): Promise<ImageMetadata | null> {\n try {\n const filePath = this.getImageMetadataPath(transactionId);\n const data = await fs.readFile(filePath, 'utf-8');\n const cached: CachedImageMetadata = JSON.parse(data);\n\n // Check if cache is expired\n const now = Math.floor(Date.now() / 1000);\n if (now > cached.expiresAt) {\n // Cache expired, delete file\n await this.clearImageMetadata(transactionId);\n return null;\n }\n\n return cached.metadata;\n } catch (error) {\n // File doesn't exist or is corrupted\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return null;\n }\n console.error(`Error reading image metadata cache for ${transactionId}:`, error);\n return null;\n }\n }\n\n /**\n * Set cached image metadata for a transaction ID\n */\n async setImageMetadata(transactionId: string, metadata: ImageMetadata): Promise<void> {\n try {\n await this.ensureImageMetadataDir();\n const filePath = this.getImageMetadataPath(transactionId);\n const now = Math.floor(Date.now() / 1000);\n const imageMetadataTtl = this.config.imageMetadata?.ttl || 604800; // Default 7 days\n const expiresAt = now + imageMetadataTtl;\n\n const cached: CachedImageMetadata = {\n metadata,\n cachedAt: now,\n expiresAt,\n };\n\n await fs.writeFile(filePath, JSON.stringify(cached, null, 2), 'utf-8');\n } catch (error) {\n console.error(`Error writing image metadata cache for ${transactionId}:`, error);\n // Don't throw - file cache is best effort\n }\n }\n\n /**\n * Clear image metadata cache for a transaction ID\n */\n async clearImageMetadata(transactionId: string): Promise<void> {\n try {\n const filePath = this.getImageMetadataPath(transactionId);\n await fs.unlink(filePath);\n } catch (error) {\n // File might not exist, which is fine\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n console.error(`Error clearing image metadata cache for ${transactionId}:`, error);\n }\n }\n }\n\n /**\n * Clear all caches\n */\n async clearAll(): Promise<void> {\n try {\n const files = await fs.readdir(this.cacheDir);\n for (const file of files) {\n if (file.endsWith('.json')) {\n await fs.unlink(join(this.cacheDir, file));\n }\n }\n // Also clear image metadata directory\n const imageMetadataDir = join(this.cacheDir, 'image-metadata');\n try {\n const imageFiles = await fs.readdir(imageMetadataDir);\n for (const file of imageFiles) {\n if (file.endsWith('.json')) {\n await fs.unlink(join(imageMetadataDir, file));\n }\n }\n } catch {\n // Directory might not exist, which is fine\n }\n } catch (error) {\n console.error('Error clearing all caches:', error);\n }\n }\n}\n","import { MemoryCache } from './MemoryCache';\nimport { FileCache } from './FileCache';\nimport type {\n CachedFeedData,\n CachedFeedContent,\n CacheConfig,\n CacheStats,\n} from './types';\nimport type { GraphQLItem, ImageMetadata } from '../types';\nimport type { FeedFormat } from '../types';\n\n/**\n * Unified cache manager that combines in-memory and file-based caching\n */\nexport class CacheManager {\n private memoryCache: MemoryCache;\n private fileCache: FileCache;\n private config: CacheConfig;\n private stats: CacheStats = {\n hits: 0,\n misses: 0,\n refreshes: 0,\n errors: 0,\n };\n\n constructor(config: CacheConfig) {\n this.config = config;\n this.memoryCache = new MemoryCache(config);\n this.fileCache = new FileCache(config);\n }\n\n /**\n * Get cached feed data for a schema\n * Checks memory cache first, then file cache\n */\n async getFeedData(schemaName: string): Promise<CachedFeedData | null> {\n if (!this.config.enabled) {\n return null;\n }\n\n try {\n // Check memory cache first\n let cached = this.memoryCache.getFeedData(schemaName);\n if (cached) {\n this.stats.hits++;\n return cached;\n }\n\n // Check file cache\n cached = await this.fileCache.getFeedData(schemaName);\n if (cached) {\n // Restore to memory cache\n this.memoryCache.setFeedData(schemaName, cached.items);\n this.stats.hits++;\n return cached;\n }\n\n this.stats.misses++;\n return null;\n } catch (error) {\n console.error(`Error getting feed data cache for ${schemaName}:`, error);\n this.stats.errors++;\n return null;\n }\n }\n\n /**\n * Set cached feed data for a schema\n * Updates both memory and file cache\n */\n async setFeedData(schemaName: string, items: GraphQLItem[]): Promise<void> {\n if (!this.config.enabled) {\n return;\n }\n\n try {\n // Update memory cache\n this.memoryCache.setFeedData(schemaName, items);\n\n // Get the cached data to save to file\n const cached = this.memoryCache.getFeedData(schemaName);\n if (cached) {\n await this.fileCache.setFeedData(schemaName, cached);\n }\n } catch (error) {\n console.error(`Error setting feed data cache for ${schemaName}:`, error);\n this.stats.errors++;\n }\n }\n\n /**\n * Get cached feed content for a schema and format\n * Checks memory cache first, then file cache\n */\n async getFeedContent(\n schemaName: string,\n format: FeedFormat\n ): Promise<CachedFeedContent | null> {\n if (!this.config.enabled) {\n return null;\n }\n\n try {\n // Check memory cache first\n let cached = this.memoryCache.getFeedContent(schemaName, format);\n if (cached) {\n this.stats.hits++;\n return cached;\n }\n\n // Check file cache\n cached = await this.fileCache.getFeedContent(schemaName, format);\n if (cached) {\n // Restore to memory cache\n this.memoryCache.setFeedContent(\n schemaName,\n format,\n cached.content,\n cached.contentType\n );\n this.stats.hits++;\n return cached;\n }\n\n this.stats.misses++;\n return null;\n } catch (error) {\n console.error(\n `Error getting feed content cache for ${schemaName}:${format}:`,\n error\n );\n this.stats.errors++;\n return null;\n }\n }\n\n /**\n * Set cached feed content for a schema and format\n * Updates both memory and file cache\n */\n async setFeedContent(\n schemaName: string,\n format: FeedFormat,\n content: string,\n contentType: string\n ): Promise<void> {\n if (!this.config.enabled) {\n return;\n }\n\n try {\n // Update memory cache\n this.memoryCache.setFeedContent(schemaName, format, content, contentType);\n\n // Get the cached content to save to file\n const cached = this.memoryCache.getFeedContent(schemaName, format);\n if (cached) {\n await this.fileCache.setFeedContent(schemaName, format, cached);\n }\n } catch (error) {\n console.error(\n `Error setting feed content cache for ${schemaName}:${format}:`,\n error\n );\n this.stats.errors++;\n }\n }\n\n /**\n * Clear feed data cache for a schema\n */\n async clearFeedData(schemaName: string): Promise<void> {\n this.memoryCache.clearFeedData(schemaName);\n await this.fileCache.clearFeedData(schemaName);\n this.memoryCache.clearContentCache(schemaName);\n await this.fileCache.clearAllContentCache(schemaName);\n }\n\n /**\n * Clear all caches\n */\n async clearAll(): Promise<void> {\n this.memoryCache.clearAll();\n await this.fileCache.clearAll();\n }\n\n /**\n * Get or create a refresh lock for a schema\n * Prevents concurrent fetches for the same schema\n */\n async withRefreshLock<T>(\n schemaName: string,\n fn: () => Promise<T>\n ): Promise<T> {\n return this.memoryCache.withRefreshLock(schemaName, fn);\n }\n\n /**\n * Merge new items with cached items\n * Deduplicates by item ID and sorts by timeCreated (descending)\n */\n mergeItems(cachedItems: GraphQLItem[], newItems: GraphQLItem[]): GraphQLItem[] {\n // Create a map of existing items by ID\n const itemMap = new Map<string, GraphQLItem>();\n \n // Add cached items first\n for (const item of cachedItems) {\n const itemId = item.id || (item as any).seedUid || (item as any).SeedUid || '';\n if (itemId) {\n itemMap.set(itemId, item);\n }\n }\n\n // Add or update with new items\n for (const item of newItems) {\n const itemId = item.id || (item as any).seedUid || (item as any).SeedUid || '';\n if (itemId) {\n itemMap.set(itemId, item);\n } else {\n // If no ID, add it anyway (might be a new item)\n itemMap.set(`temp-${Date.now()}-${Math.random()}`, item);\n }\n }\n\n // Convert back to array and sort by timeCreated (descending)\n const merged = Array.from(itemMap.values());\n merged.sort((a, b) => {\n const timeA = (a as any).timeCreated || 0;\n const timeB = (b as any).timeCreated || 0;\n return timeB - timeA; // Descending order\n });\n\n return merged;\n }\n\n /**\n * Filter items to only include those newer than the given timestamp\n */\n filterNewItems(items: GraphQLItem[], lastProcessedTimestamp: number): GraphQLItem[] {\n return items.filter(item => {\n const timeCreated = (item as any).timeCreated;\n return timeCreated && timeCreated > lastProcessedTimestamp;\n });\n }\n\n /**\n * Update configuration\n */\n updateConfig(config: Partial<CacheConfig>): void {\n this.config = { ...this.config, ...config };\n this.memoryCache.updateConfig(this.config);\n // FileCache doesn't need config update as it's read-only after construction\n }\n\n /**\n * Get cache statistics\n */\n getStats(): CacheStats & { memoryStats: ReturnType<MemoryCache['getStats']> } {\n return {\n ...this.stats,\n memoryStats: this.memoryCache.getStats(),\n };\n }\n\n /**\n * Get cached image metadata for a transaction ID\n * Checks memory cache first, then file cache\n */\n async getImageMetadata(transactionId: string): Promise<ImageMetadata | null> {\n if (!this.config.enabled || !this.config.imageMetadata?.enabled) {\n return null;\n }\n\n try {\n // Check memory cache first\n let cached = this.memoryCache.getImageMetadata(transactionId);\n if (cached) {\n this.stats.hits++;\n return cached;\n }\n\n // Check file cache\n cached = await this.fileCache.getImageMetadata(transactionId);\n if (cached) {\n // Restore to memory cache\n this.memoryCache.setImageMetadata(transactionId, cached);\n this.stats.hits++;\n return cached;\n }\n\n this.stats.misses++;\n return null;\n } catch (error) {\n console.error(`Error getting image metadata cache for ${transactionId}:`, error);\n this.stats.errors++;\n return null;\n }\n }\n\n /**\n * Set cached image metadata for a transaction ID\n * Updates both memory and file cache\n */\n async setImageMetadata(transactionId: string, metadata: ImageMetadata): Promise<void> {\n if (!this.config.enabled || !this.config.imageMetadata?.enabled) {\n return;\n }\n\n try {\n // Update memory cache\n this.memoryCache.setImageMetadata(transactionId, metadata);\n\n // Also update file cache\n await this.fileCache.setImageMetadata(transactionId, metadata);\n } catch (error) {\n console.error(`Error setting image metadata cache for ${transactionId}:`, error);\n this.stats.errors++;\n }\n }\n\n /**\n * Reset statistics\n */\n resetStats(): void {\n this.stats = {\n hits: 0,\n misses: 0,\n refreshes: 0,\n errors: 0,\n };\n }\n}\n","import { DEFAULT_ARWEAVE_GATEWAYS } from '@seedprotocol/sdk'\nimport type { CacheConfig, ImageMetadataConfig } from './types';\n\n/**\n * Load cache configuration from environment variables\n *\n * Environment variables:\n * - CACHE_ENABLED: Set to 'false' to disable caching. Set to 'true' to enable (overrides NODE_ENV).\n * In development (NODE_ENV=development), cache is disabled by default unless CACHE_ENABLED='true'.\n * - CACHE_TTL: Cache time-to-live in seconds (default: 3600)\n * - CACHE_DIR: Directory for cache files (default: './cache')\n * - CACHE_BACKGROUND_REFRESH: Enable background refresh (default: 'false')\n * - CACHE_REFRESH_INTERVAL: Background refresh interval in seconds (default: 300)\n */\nexport function loadCacheConfig(): CacheConfig {\n const ttl = parseInt(process.env.CACHE_TTL || '3600', 10);\n const cacheDir = process.env.CACHE_DIR || './cache';\n\n // CACHE_ENABLED explicitly disables: 'false', '0', 'no', 'off'\n const cacheDisabledByEnvVar =\n process.env.CACHE_ENABLED === 'false' ||\n process.env.CACHE_ENABLED === '0' ||\n process.env.CACHE_ENABLED === 'no' ||\n process.env.CACHE_ENABLED === 'off';\n // CACHE_ENABLED explicitly enables (overrides NODE_ENV=development)\n const cacheEnabledByEnvVar =\n process.env.CACHE_ENABLED === 'true' ||\n process.env.CACHE_ENABLED === '1' ||\n process.env.CACHE_ENABLED === 'yes';\n const isDev = process.env.NODE_ENV === 'development';\n\n let enabled: boolean;\n if (cacheDisabledByEnvVar) {\n enabled = false;\n } else if (cacheEnabledByEnvVar) {\n enabled = true;\n } else if (isDev) {\n enabled = false; // Disable cache in dev mode by default\n } else {\n enabled = true;\n }\n\n const backgroundRefresh = process.env.CACHE_BACKGROUND_REFRESH === 'true';\n const refreshInterval = parseInt(\n process.env.CACHE_REFRESH_INTERVAL || '300',\n 10\n );\n\n // Log cache status on startup\n if (!enabled) {\n const reason = cacheDisabledByEnvVar\n ? 'CACHE_ENABLED=false'\n : isDev\n ? 'NODE_ENV=development'\n : 'CACHE_ENABLED=false';\n console.log(`⚠️ Cache is DISABLED (${reason})`);\n }\n\n // Image metadata configuration\n const imageMetadataEnabled = process.env.IMAGE_METADATA_ENABLED !== 'false'; // Default to true\n const imageMetadataTtl = parseInt(process.env.IMAGE_METADATA_TTL || '604800', 10); // 7 days default\n const imageMetadataGateways = process.env.IMAGE_METADATA_GATEWAYS\n ? process.env.IMAGE_METADATA_GATEWAYS.split(',').map(g => g.trim())\n : [...DEFAULT_ARWEAVE_GATEWAYS];\n const imageMetadataTimeout = parseInt(process.env.IMAGE_METADATA_TIMEOUT || '5000', 10);\n\n const imageMetadata: ImageMetadataConfig = {\n enabled: imageMetadataEnabled,\n ttl: imageMetadataTtl,\n gateways: imageMetadataGateways,\n timeout: imageMetadataTimeout,\n };\n\n return {\n ttl,\n cacheDir,\n enabled,\n backgroundRefresh,\n refreshInterval,\n imageMetadata,\n };\n}\n","import { DEFAULT_ARWEAVE_GATEWAYS } from '@seedprotocol/sdk'\nimport sizeOf from 'image-size'\nimport type { ImageMetadata } from '../types'\n\nexport interface ArweaveImageServiceConfig {\n gateways: string[]\n timeout: number // milliseconds\n}\n\n/**\n * Service for detecting and extracting metadata from images stored on Arweave\n */\nexport class ArweaveImageService {\n private config: ArweaveImageServiceConfig\n\n constructor(config: ArweaveImageServiceConfig) {\n this.config = config\n }\n\n /**\n * Detect if an Arweave transaction ID links to an image and extract metadata\n */\n async detectImage(transactionId: string): Promise<ImageMetadata> {\n const gateways = this.config.gateways || [...DEFAULT_ARWEAVE_GATEWAYS]\n \n // Try each gateway until one succeeds\n for (const gateway of gateways) {\n try {\n const url = `https://${gateway}/${transactionId}`\n const metadata = await this.getImageMetadata(url)\n if (metadata.isImage) {\n return metadata\n }\n } catch (error) {\n // Log but continue to next gateway\n console.warn(`Failed to fetch from gateway ${gateway} for transaction ${transactionId}:`, error)\n continue\n }\n }\n\n // If all gateways failed, return non-image result\n return {\n isImage: false,\n url: `https://${gateways[0]}/${transactionId}`,\n }\n }\n\n /**\n * Get image metadata from a URL\n */\n async getImageMetadata(url: string): Promise<ImageMetadata> {\n try {\n // First, try HEAD request to check Content-Type (more efficient)\n const headResponse = await this.fetchWithTimeout(url, { method: 'HEAD' })\n \n if (!headResponse.ok) {\n return { isImage: false, url }\n }\n\n const contentType = headResponse.headers.get('content-type') || ''\n const contentLength = headResponse.headers.get('content-length')\n \n // Check if it's an image based on Content-Type\n if (!this.isImageContentType(contentType)) {\n return {\n isImage: false,\n url,\n mimeType: contentType || undefined,\n size: contentLength ? parseInt(contentLength, 10) : undefined,\n }\n }\n\n // If it's an image, fetch first few KB to extract dimensions\n const rangeResponse = await this.fetchWithTimeout(url, {\n headers: {\n 'Range': 'bytes=0-8192', // First 8KB should be enough for most image headers\n },\n })\n\n if (!rangeResponse.ok) {\n // If range request fails, try full request (some servers don't support range)\n return await this.getImageMetadataFromFullRequest(url, contentType, contentLength)\n }\n\n const buffer = Buffer.from(await rangeResponse.arrayBuffer())\n const dimensions = this.extractImageDimensions(buffer, contentType)\n const format = this.getImageFormat(contentType, buffer)\n\n return {\n isImage: true,\n url,\n mimeType: contentType,\n width: dimensions.width,\n height: dimensions.height,\n size: contentLength ? parseInt(contentLength, 10) : undefined,\n format,\n }\n } catch (error) {\n console.warn(`Error fetching image metadata from ${url}:`, error)\n return { isImage: false, url }\n }\n }\n\n /**\n * Fetch image metadata from full request (fallback when range requests fail)\n */\n private async getImageMetadataFromFullRequest(\n url: string,\n contentType: string,\n contentLength: string | null\n ): Promise<ImageMetadata> {\n try {\n const response = await this.fetchWithTimeout(url)\n if (!response.ok) {\n return { isImage: false, url }\n }\n\n const buffer = Buffer.from(await response.arrayBuffer())\n const dimensions = this.extractImageDimensions(buffer, contentType)\n const format = this.getImageFormat(contentType, buffer)\n\n return {\n isImage: true,\n url,\n mimeType: contentType,\n width: dimensions.width,\n height: dimensions.height,\n size: buffer.length,\n format,\n }\n } catch (error) {\n console.warn(`Error in full request for ${url}:`, error)\n return { isImage: false, url }\n }\n }\n\n /**\n * Validate if Content-Type indicates an image\n */\n private isImageContentType(contentType: string): boolean {\n if (!contentType) return false\n \n const normalized = (contentType.toLowerCase().split(';')[0] ?? '').trim()\n return normalized.startsWith('image/')\n }\n\n /**\n * Extract image dimensions from buffer\n */\n private extractImageDimensions(\n buffer: Buffer,\n contentType: string\n ): { width: number; height: number } {\n try {\n const dimensions = sizeOf(buffer)\n if (dimensions.width && dimensions.height) {\n return {\n width: dimensions.width,\n height: dimensions.height,\n }\n }\n } catch (error) {\n // image-size might fail for some formats or corrupted data\n console.warn(`Failed to extract dimensions:`, error)\n }\n\n // Return default if extraction failed\n return { width: 0, height: 0 }\n }\n\n /**\n * Get image format from Content-Type or buffer analysis\n */\n private getImageFormat(contentType: string, buffer: Buffer): string | undefined {\n // Try to get format from Content-Type first\n if (contentType) {\n const mimeMatch = contentType.toLowerCase().match(/image\\/([^;]+)/)\n if (mimeMatch && mimeMatch[1]) {\n const format = mimeMatch[1].toLowerCase()\n // Normalize common formats\n if (format === 'jpeg') return 'jpeg'\n if (format === 'png') return 'png'\n if (format === 'gif') return 'gif'\n if (format === 'webp') return 'webp'\n if (format === 'svg+xml') return 'svg'\n return format\n }\n }\n\n // Try to detect from buffer magic bytes\n try {\n const dimensions = sizeOf(buffer)\n if (dimensions.type) {\n return dimensions.type.toLowerCase()\n }\n } catch {\n // Ignore errors\n }\n\n return undefined\n }\n\n /**\n * Fetch with timeout\n */\n private async fetchWithTimeout(\n url: string,\n options: RequestInit = {}\n ): Promise<Response> {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), this.config.timeout)\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n })\n clearTimeout(timeoutId)\n return response\n } catch (error) {\n clearTimeout(timeoutId)\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Request timeout after ${this.config.timeout}ms`)\n }\n throw error\n }\n }\n}\n","import { client as seedClient, DEFAULT_ARWEAVE_HOST } from '@seedprotocol/sdk';\nimport { getArweaveUrlForTransaction } from './utils/arweaveUrl';\nimport { getFeedItemsBySchemaName } from './getFeedItems';\n\nexport { getFeedItemsBySchemaName };\nimport pluralize from 'pluralize';\nimport type { FeedFormat, GraphQLItem, TransformOptions, FeedConfig, ImageMetadata } from './types';\nimport { generateAtomFeed, generateJsonFeed } from 'feedsmith';\nimport { generateRssXml } from './rss/generateRssXml';\nimport { CacheManager } from './cache/CacheManager';\nimport { loadCacheConfig } from './cache/config';\nimport { loadFeedConfig } from './config';\nimport { checkIfNoneMatch } from './utils/etag';\nimport { ArweaveImageService } from './services/arweaveImageService';\n\nlet client: any;\nlet initializationPromise: Promise<void> | null = null;\n\n// Initialize cache manager\nlet cacheManager: CacheManager | null = null;\n\nfunction getCacheManager(): CacheManager {\n if (!cacheManager) {\n const config = loadCacheConfig();\n cacheManager = new CacheManager(config);\n }\n return cacheManager;\n}\n\n/**\n * Reset cache manager (useful for testing)\n */\nexport function resetCacheManager(): void {\n cacheManager = null;\n}\n\n/**\n * Initialize the Seed Protocol client\n * This should be called as soon as the app is ready\n */\nexport const initializeSeedClient = async (): Promise<void> => {\n // If already initializing, wait for that to complete\n if (initializationPromise) {\n return initializationPromise;\n }\n\n // If already initialized, return immediately\n if (client) {\n return;\n }\n\n initializationPromise = (async () => {\n try {\n console.log('Initializing Seed Protocol client...');\n\n \n await seedClient.init({ config: {\n endpoints: {\n filePaths: 'app-files',\n files: '/app-files',\n },\n arweaveDomain: DEFAULT_ARWEAVE_HOST,\n }, addresses: [], });\n console.log('✅ Seed Protocol client initialized successfully');\n client = seedClient;\n initializationPromise = null; // Clear the promise after successful initialization\n } catch (error) {\n console.error('❌ Failed to initialize Seed Protocol client:', error);\n initializationPromise = null; // Clear the promise on error so we can retry\n throw error;\n }\n })();\n\n return initializationPromise;\n}\n\n/**\n * Get the Seed Protocol client, initializing it if necessary\n * This function can be called from any context (Electron main process or Vite dev server)\n */\nexport const getClient = async (): Promise<any> => {\n // If client is already initialized, return it\n if (client) {\n return client;\n }\n\n // If initialization is in progress, wait for it\n if (initializationPromise) {\n await initializationPromise;\n return client;\n }\n\n // Otherwise, initialize it now\n await initializeSeedClient();\n return client;\n}\n\n\n/**\n * Teardown the Seed Protocol client\n * This should be called when the app is quitting\n */\nexport const teardownSeedClient = async (): Promise<void> => {\n try {\n console.log('Tearing down Seed Protocol client...');\n \n if (typeof seedClient.stop === 'function') {\n await seedClient.stop();\n console.log('✅ Seed Protocol client stopped');\n }\n \n if (typeof seedClient.unload === 'function') {\n await seedClient.unload();\n console.log('✅ Seed Protocol client unloaded');\n }\n \n console.log('✅ Seed Protocol client teardown complete');\n } catch (error) {\n console.error('❌ Failed to teardown Seed Protocol client:', error);\n // Don't throw - we want the app to quit even if teardown fails\n }\n}\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\nconst SITE_CONFIG: FeedConfig = {\n title: 'Seed Protocol',\n description: 'Content published via Seed Protocol',\n siteUrl: 'https://seedprotocol.io',\n feedUrl: 'https://feed.seedprotocol.io',\n language: 'en',\n copyright: `© ${new Date().getFullYear()} All rights reserved`,\n author: {\n name: 'Seed Protocol',\n email: 'info@seedprotocol.io',\n link: 'https://seedprotocol.io',\n },\n}\n\n// ============================================================================\n// GraphQL Client (replace with your actual client)\n// ============================================================================\n\n// ============================================================================\n// Feed Transformation\n// ============================================================================\n\n/**\n * Enriches feed items with media properties from Arweave storageTransactionId\n */\nasync function enrichFeedItemsWithMedia(\n items: GraphQLItem[],\n imageService: ArweaveImageService,\n cache: CacheManager\n): Promise<GraphQLItem[]> {\n // Filter items that have storageTransactionId\n const itemsToProcess = items.filter(\n (item: any) => item.storageTransactionId || item.storage_transaction_id\n )\n\n if (itemsToProcess.length === 0) {\n return items\n }\n\n console.log(`Enriching ${itemsToProcess.length} items with image metadata`)\n\n // Process all items in parallel\n const enrichmentPromises = itemsToProcess.map(async (item: any) => {\n const transactionId = item.storageTransactionId || item.storage_transaction_id\n if (!transactionId) return item\n\n try {\n // Check cache first\n let imageMetadata = await cache.getImageMetadata(transactionId)\n\n // If not cached, detect image\n if (!imageMetadata) {\n console.log(`Detecting image for transaction ${transactionId}`)\n imageMetadata = await imageService.detectImage(transactionId)\n // Cache the result (even if it's not an image, to avoid retries)\n await cache.setImageMetadata(transactionId, imageMetadata)\n }\n\n // If image detected, add media properties\n if (imageMetadata.isImage) {\n // Add media properties to the item\n item._imageMetadata = imageMetadata\n item._hasImage = true\n }\n\n return item\n } catch (error) {\n console.warn(`Error enriching item with transaction ${transactionId}:`, error)\n return item // Return item unchanged on error\n }\n })\n\n // Wait for all enrichments to complete\n const enrichedItems = await Promise.all(enrichmentPromises)\n\n // Create a map of enriched items by their ID\n const enrichedMap = new Map<string, GraphQLItem>()\n enrichedItems.forEach(item => {\n const itemId = item.id || (item as any).seedUid || (item as any).SeedUid || ''\n if (itemId) {\n enrichedMap.set(itemId, item)\n }\n })\n\n // Merge enriched items back into original array\n return items.map(item => {\n const itemId = item.id || (item as any).seedUid || (item as any).SeedUid || ''\n const enriched = enrichedMap.get(itemId)\n return enriched || item\n })\n}\n\n/**\n * Transforms GraphQL items into feed items.\n * This function preserves all dynamic properties from the items.\n */\nfunction transformToFeedItems(\n items: GraphQLItem[],\n options: TransformOptions\n): any[] {\n const { schemaName, siteUrl, itemUrlBase, itemUrlPath } = options\n\n return items.map((item: any) => {\n // Determine item ID - try multiple possible fields\n const itemId = item.id || item.seedUid || item.SeedUid || item.storageTransactionId || item.storage_transaction_id\n\n // Determine item URL - prefer link/Link, then import_url/importUrl, fallback to constructed URL\n const fallbackUrl =\n itemUrlBase != null\n ? `${itemUrlBase.replace(/\\/$/, '')}/${(itemUrlPath ?? 'attestation/view').replace(/^\\//, '')}/${itemId}`\n : `${siteUrl}/${pluralize(schemaName)}/${itemId}`\n const itemUrl = item.link || item.Link || item.import_url || item.importUrl || fallbackUrl\n \n // Determine publication date - try multiple sources\n let date: Date\n if (item.pubDate || item.PubDate) {\n // pubDate might be a string like \"Mon, 07 Apr 2025 00:03:29 GMT\"\n const pubDateStr = item.pubDate || item.PubDate\n date = new Date(pubDateStr)\n } else if (item.timeCreated) {\n // timeCreated is a Unix timestamp\n date = new Date(item.timeCreated * 1000)\n } else if (item.publishedAt || item.createdAt || item.updatedAt) {\n const dateValue = item.publishedAt || item.createdAt || item.updatedAt\n date = dateValue && typeof dateValue === 'object' && dateValue.constructor === Date\n ? dateValue as Date\n : new Date(dateValue as string | number)\n } else {\n date = new Date()\n }\n\n // Start with all properties from the item to preserve dynamic schema\n const feedItem: any = {\n ...item, // Preserve all dynamic properties first\n // Map to standard feed fields\n id: itemId,\n title: item.title || item.Title || 'Untitled',\n link: itemUrl,\n description: item.summary || item.description || item.text || '',\n content: item.html || item.content || item.summary || item.text || '',\n pubDate: date,\n date: date,\n // Map guid - use full URL when available for proper permalink\n guid: item.guid || item.Guid || item.link || item.Link || itemUrl,\n }\n\n // Add image metadata if available from enrichment\n if (item._imageMetadata && item._hasImage) {\n const imageMeta: ImageMetadata = item._imageMetadata\n feedItem._imageMetadata = imageMeta\n feedItem._hasImage = true\n }\n\n // Convert any date-like string properties to Date objects\n Object.keys(feedItem).forEach((key) => {\n const value = feedItem[key]\n if (typeof value === 'string' && /date|time|published|created|updated/i.test(key) && key !== 'pubDate' && key !== 'date') {\n const dateValue = new Date(value)\n if (!isNaN(dateValue.getTime())) {\n feedItem[key] = dateValue\n }\n }\n })\n\n return feedItem\n })\n}\n\n// ============================================================================\n// Feed Generator\n// ============================================================================\n\nexport const createFeed = (\n items: GraphQLItem[],\n schemaName: string,\n format: FeedFormat,\n cacheBust?: string\n): Promise<string> => {\n const collectionName = pluralize(schemaName)\n // Add cache busting parameter to feed URL if provided\n const feedUrlBase = `${SITE_CONFIG.siteUrl}/${collectionName}/${format}`\n const feedUrl = cacheBust ? `${feedUrlBase}?v=${cacheBust}` : feedUrlBase\n const feedTitle = `${SITE_CONFIG.title} - ${capitalize(collectionName)}`\n const now = new Date()\n\n const feedConfig = loadFeedConfig()\n\n // Transform items to preserve all dynamic properties\n const transformedItems = transformToFeedItems(items, {\n schemaName,\n siteUrl: SITE_CONFIG.siteUrl,\n itemUrlBase: feedConfig.itemUrlBase,\n itemUrlPath: feedConfig.itemUrlPath,\n })\n\n // Generate feed based on format using FeedSmith\n switch (format) {\n case 'atom': {\n // Atom feed requires: id, title, updated, links, entries\n const atomFeed = {\n id: feedUrl,\n title: feedTitle,\n updated: now,\n links: [\n { href: feedUrl, rel: 'self' },\n { href: SITE_CONFIG.siteUrl },\n ],\n subtitle: SITE_CONFIG.description,\n rights: SITE_CONFIG.copyright,\n author: SITE_CONFIG.author ? {\n name: SITE_CONFIG.author.name,\n email: SITE_CONFIG.author.email,\n uri: SITE_CONFIG.author.link,\n } : undefined,\n entries: transformedItems.map((item) => {\n // Atom entries require: id, title, updated, links\n const entry: any = {\n id: item.id || item.link,\n title: item.title || 'Untitled',\n updated: item.date || item.pubDate || now,\n links: item.link ? [{ href: item.link }] : [],\n ...item, // Preserve all dynamic properties\n }\n if (item.content) entry.content = item.content\n if (item.description) entry.summary = item.description\n \n // Add image as enclosure link if available\n if (item._imageMetadata && item._hasImage) {\n const imageMeta: ImageMetadata = item._imageMetadata\n if (!entry.links) entry.links = []\n entry.links.push({\n href: imageMeta.url,\n rel: 'enclosure',\n type: imageMeta.mimeType || 'image/jpeg',\n length: imageMeta.size,\n })\n \n // Enhance content with image if not already present\n if (imageMeta.url && entry.content && !entry.content.includes(imageMeta.url)) {\n const imageHtml = `<img src=\"${imageMeta.url}\" alt=\"${item.title || ''}\"${imageMeta.width ? ` width=\"${imageMeta.width}\"` : ''}${imageMeta.height ? ` height=\"${imageMeta.height}\"` : ''} />`\n entry.content = typeof entry.content === 'string' \n ? `${imageHtml}\\n${entry.content}`\n : { type: 'html', value: `${imageHtml}\\n${entry.content.value || entry.content}` }\n }\n }\n \n return entry\n }),\n }\n return Promise.resolve(generateAtomFeed(atomFeed) as string)\n }\n case 'json': {\n // JSON feed requires: title, items (with id)\n const jsonFeed = {\n title: feedTitle,\n home_page_url: SITE_CONFIG.siteUrl,\n feed_url: feedUrl,\n description: SITE_CONFIG.description,\n author: SITE_CONFIG.author ? {\n name: SITE_CONFIG.author.name,\n url: SITE_CONFIG.author.link,\n } : undefined,\n items: transformedItems.map((item) => {\n // JSON items require: id\n const jsonItem: any = {\n id: item.id || item.link,\n ...item, // Preserve all dynamic properties\n }\n if (item.title) jsonItem.title = item.title\n if (item.link) jsonItem.url = item.link\n if (item.content) jsonItem.content_html = item.content\n if (item.description) jsonItem.summary = item.description\n if (item.date || item.pubDate) jsonItem.date_published = item.date || item.pubDate\n \n // Add image and attachments if available\n if (item._imageMetadata && item._hasImage) {\n const imageMeta: ImageMetadata = item._imageMetadata\n jsonItem.image = imageMeta.url\n \n // Add as attachment\n if (!jsonItem.attachments) jsonItem.attachments = []\n jsonItem.attachments.push({\n url: imageMeta.url,\n mime_type: imageMeta.mimeType || 'image/jpeg',\n size_in_bytes: imageMeta.size,\n title: item.title || 'Image',\n })\n \n // Enhance content_html with image if not already present\n if (imageMeta.url && jsonItem.content_html && !jsonItem.content_html.includes(imageMeta.url)) {\n const imageHtml = `<img src=\"${imageMeta.url}\" alt=\"${item.title || ''}\"${imageMeta.width ? ` width=\"${imageMeta.width}\"` : ''}${imageMeta.height ? ` height=\"${imageMeta.height}\"` : ''} />`\n jsonItem.content_html = `${imageHtml}\\n${jsonItem.content_html}`\n }\n }\n \n return jsonItem\n }),\n }\n const jsonResult = generateJsonFeed(jsonFeed)\n return Promise.resolve(typeof jsonResult === 'string' ? jsonResult : JSON.stringify(jsonResult))\n }\n case 'rss':\n default: {\n // RSS feed requires: title, link, description, items\n const rssFeed = {\n title: feedTitle,\n link: SITE_CONFIG.siteUrl,\n description: SITE_CONFIG.description,\n language: SITE_CONFIG.language,\n copyright: SITE_CONFIG.copyright,\n webMaster: SITE_CONFIG.author?.email,\n pubDate: now,\n lastBuildDate: now,\n items: transformedItems.map((item: any) => {\n // RSS items can have dynamic properties\n const rssItem: any = {\n ...item, // Preserve all dynamic properties\n }\n \n // Ensure required/standard RSS fields\n if (item.title) rssItem.title = item.title\n if (item.link) rssItem.link = item.link\n if (item.description) rssItem.description = item.description\n if (item.date || item.pubDate) rssItem.pubDate = item.date || item.pubDate\n \n // Map guid - RSS requires guid for proper item identification\n if (item.guid) {\n rssItem.guid = {\n value: item.guid,\n isPermaLink: typeof item.guid === 'string' && (item.guid.startsWith('http://') || item.guid.startsWith('https://'))\n }\n } else if (item.id) {\n rssItem.guid = {\n value: item.id,\n isPermaLink: typeof item.id === 'string' && (item.id.startsWith('http://') || item.id.startsWith('https://'))\n }\n }\n \n // Add image from storageTransactionId if available (from enrichment)\n if (item._imageMetadata && item._hasImage) {\n const imageMeta: ImageMetadata = item._imageMetadata\n \n // Add enclosure for RSS (standard RSS 2.0)\n rssItem.enclosures = [{\n url: imageMeta.url,\n type: imageMeta.mimeType || 'image/jpeg',\n length: imageMeta.size,\n }]\n \n // Build comprehensive Media RSS properties\n const mediaContent: any = {\n url: imageMeta.url,\n type: imageMeta.mimeType || 'image/jpeg',\n medium: 'image', // image, video, audio, document\n }\n \n // Add dimensions if available\n if (imageMeta.width) mediaContent.width = imageMeta.width\n if (imageMeta.height) mediaContent.height = imageMeta.height\n if (imageMeta.size) mediaContent.fileSize = imageMeta.size\n if (imageMeta.format) mediaContent.format = imageMeta.format\n \n // Add Media RSS namespace properties\n rssItem['media:content'] = [mediaContent]\n \n // Add media:thumbnail (required by many readers)\n const thumbnail: any = {\n url: imageMeta.url,\n }\n if (imageMeta.width) thumbnail.width = imageMeta.width\n if (imageMeta.height) thumbnail.height = imageMeta.height\n rssItem['media:thumbnail'] = [thumbnail]\n \n // Add media:title (many readers look for this)\n if (item.title) {\n rssItem['media:title'] = item.title\n }\n \n // Add media:description (enhanced)\n const mediaDescription = item.description || item.summary || item.title || ''\n if (mediaDescription) {\n rssItem['media:description'] = mediaDescription\n }\n \n // Add media:keywords for better discoverability\n if (item.title) {\n // Extract keywords from title and description\n const keywords: string[] = []\n if (item.title) {\n keywords.push(...item.title.toLowerCase().split(/\\s+/).filter((w: string) => w.length > 3))\n }\n if (item.description) {\n keywords.push(...item.description.toLowerCase().split(/\\s+/).filter((w: string) => w.length > 3))\n }\n if (keywords.length > 0) {\n rssItem['media:keywords'] = [...new Set(keywords)].slice(0, 10).join(', ')\n }\n }\n \n // Add media:credit (author information)\n if (item.authors && Array.isArray(item.authors) && item.authors.length > 0) {\n rssItem['media:credit'] = item.authors.map((author: any) => ({\n value: author.name || author.displayName || 'Unknown',\n role: 'author',\n }))\n } else if (SITE_CONFIG.author) {\n rssItem['media:credit'] = [{\n value: SITE_CONFIG.author.name,\n role: 'author',\n }]\n }\n \n // Add media:copyright\n if (SITE_CONFIG.copyright) {\n rssItem['media:copyright'] = SITE_CONFIG.copyright\n }\n \n // Add media:category (can be used for content categorization)\n if (schemaName) {\n rssItem['media:category'] = [{\n value: schemaName,\n scheme: 'http://www.schema.org/',\n }]\n }\n \n // Add media:group to bundle all media elements together\n // This is useful for readers that prefer grouped media\n rssItem['media:group'] = [{\n 'media:content': [mediaContent],\n 'media:thumbnail': [thumbnail],\n 'media:title': item.title || 'Untitled',\n 'media:description': mediaDescription,\n }]\n \n // Enhance content:encoded with image if not already present\n if (imageMeta.url && item.content && !item.content.includes(imageMeta.url)) {\n const imageHtml = `<img src=\"${imageMeta.url}\" alt=\"${item.title || ''}\"${imageMeta.width ? ` width=\"${imageMeta.width}\"` : ''}${imageMeta.height ? ` height=\"${imageMeta.height}\"` : ''} />`\n rssItem['content:encoded'] = `${imageHtml}\\n${item.content}`\n }\n }\n \n // Map feature_image to enclosure for RSS (media attachments) - fallback\n if (!rssItem.enclosures && (item.feature_image || item.featureImage)) {\n const imageUrl = item.feature_image || item.featureImage\n // If it's a URL, use it directly; if it's an Arweave transaction ID, construct URL\n const enclosureUrl = typeof imageUrl === 'string' && imageUrl.startsWith('http')\n ? imageUrl\n : getArweaveUrlForTransaction(imageUrl)\n \n rssItem.enclosures = [{\n url: enclosureUrl,\n type: 'image/jpeg', // Default, could be determined from URL or metadata\n }]\n }\n \n // Use Dublin Core namespace for additional metadata\n rssItem.dc = {}\n \n // Add date using dc namespace (supports multiple dates)\n if (item.date || item.pubDate) {\n rssItem.dc.date = item.date || item.pubDate\n }\n if (item.timeCreated) {\n // Convert Unix timestamp to Date for dc namespace\n const timeCreatedDate = new Date(item.timeCreated * 1000)\n if (!rssItem.dc.dates) rssItem.dc.dates = []\n rssItem.dc.dates.push(timeCreatedDate)\n }\n \n // Add identifier using dc namespace\n if (item.seedUid || item.SeedUid) {\n if (!rssItem.dc.identifier) rssItem.dc.identifier = []\n rssItem.dc.identifier.push(item.seedUid || item.SeedUid)\n }\n if (item.storageTransactionId || item.storage_transaction_id) {\n if (!rssItem.dc.identifier) rssItem.dc.identifier = []\n rssItem.dc.identifier.push(item.storageTransactionId || item.storage_transaction_id)\n }\n \n // Add source/relation for import_url\n if (item.import_url || item.importUrl) {\n rssItem.dc.source = item.import_url || item.importUrl\n }\n \n // Include raw custom fields as well (FeedSmith may preserve them)\n if (item.seedUid || item.SeedUid) rssItem.seedUid = item.seedUid || item.SeedUid\n if (item.storageTransactionId || item.storage_transaction_id) {\n rssItem.storageTransactionId = item.storageTransactionId || item.storage_transaction_id\n }\n if (item.timeCreated) rssItem.timeCreated = item.timeCreated\n \n return rssItem\n }),\n }\n return Promise.resolve(generateRssXml(rssFeed))\n }\n }\n}\n\n// ============================================================================\n// Route Handler\n// ============================================================================\n\nfunction getContentType(format: FeedFormat): string {\n switch (format) {\n case 'atom':\n return 'application/atom+xml; charset=utf-8'\n case 'json':\n return 'application/feed+json; charset=utf-8'\n case 'rss':\n default:\n return 'application/rss+xml; charset=utf-8'\n }\n}\n\nfunction parseFormat(segment: string): FeedFormat | null {\n const normalized = segment.toLowerCase()\n if (['rss', 'atom', 'json'].includes(normalized)) {\n return normalized as FeedFormat\n }\n return null\n}\n\nfunction capitalize(str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1)\n}\n\n/**\n * Main route handler for feed generation.\n * \n * URL Pattern: /:collection/:format\n * Examples:\n * - /posts/rss → RSS feed of posts\n * - /posts/atom → Atom feed of posts\n * - /identities/json → JSON feed of identities\n * - /posts/rss?v=1234567890 → RSS feed with cache busting parameter\n */\nexport async function handleFeedRequest(\n collectionSegment: string,\n formatSegment: string,\n ifNoneMatch?: string | null,\n cacheBust?: string\n): Promise<Response> {\n // Validate format\n const format = parseFormat(formatSegment)\n if (!format) {\n return new Response(\n JSON.stringify({ error: `Invalid feed format: ${formatSegment}` }),\n {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n }\n )\n }\n\n // De-pluralize the collection name to get model type\n const schemaName = pluralize.singular(collectionSegment.toLowerCase())\n const collectionName = pluralize(schemaName)\n\n console.log(`Schema name: ${schemaName}`);\n console.log(`Collection name: ${collectionName}`);\n\n const cache = getCacheManager();\n const config = loadCacheConfig();\n\n try {\n // Check if we have cached feed content\n if (config.enabled) {\n const cachedContent = await cache.getFeedContent(schemaName, format);\n \n if (cachedContent) {\n // Check ETag for conditional request\n if (ifNoneMatch && checkIfNoneMatch(ifNoneMatch, cachedContent.etag)) {\n console.log(`Cache hit with ETag match for ${schemaName}:${format} - returning 304`);\n return new Response(null, {\n status: 304,\n headers: {\n 'ETag': cachedContent.etag,\n 'Last-Modified': new Date(cachedContent.lastModified * 1000).toUTCString(),\n 'Cache-Control': 'public, max-age=3600, s-maxage=3600, must-revalidate',\n },\n });\n }\n\n // Return cached content\n console.log(`Cache hit for ${schemaName}:${format}`);\n return new Response(cachedContent.content, {\n status: 200,\n headers: {\n 'Content-Type': cachedContent.contentType,\n 'ETag': cachedContent.etag,\n 'Last-Modified': new Date(cachedContent.lastModified * 1000).toUTCString(),\n 'Cache-Control': 'public, max-age=3600, s-maxage=3600, must-revalidate',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n 'X-Cache': 'HIT',\n },\n });\n }\n }\n\n // Cache miss or disabled - fetch and process items\n console.log(`Cache miss for ${schemaName}:${format} - fetching items`);\n\n // Use refresh lock to prevent concurrent fetches (only if cache is enabled)\n const feedItems = config.enabled\n ? await cache.withRefreshLock(schemaName, async () => {\n // Check cache again after acquiring lock (another request might have populated it)\n const cachedData = await cache.getFeedData(schemaName);\n \n let items: GraphQLItem[];\n \n if (cachedData) {\n // Incremental fetch: only get new items\n console.log(`Incremental fetch: last processed timestamp: ${cachedData.lastProcessedTimestamp}`);\n \n // Fetch all items (SDK might not support filtering by timestamp)\n const allItems = await getFeedItemsBySchemaName(schemaName) as GraphQLItem[];\n\n console.log(`First item: ${JSON.stringify(allItems[0])}`);\n \n // Filter to only new items\n const newItems = cache.filterNewItems(allItems, cachedData.lastProcessedTimestamp);\n \n if (newItems.length > 0) {\n console.log(`Found ${newItems.length} new items, merging with ${cachedData.items.length} cached items`);\n // Merge new items with cached items\n items = cache.mergeItems(cachedData.items, newItems);\n } else {\n console.log(`No new items found, using cached items`);\n // No new items, use cached items\n items = cachedData.items;\n }\n } else {\n // Cold cache - fetch all items\n console.log(`Cold cache - fetching all items`);\n const client = await getClient()\n if (client) {\n console.log(`Client initialized: ${client.isInitialized()}`);\n }\n \n items = await getFeedItemsBySchemaName(schemaName) as GraphQLItem[];\n console.log(`Found ${items.length} feed items for schema ${schemaName}`);\n }\n\n // Enrich items with image metadata if enabled\n let enrichedItems = items;\n if (config.imageMetadata?.enabled) {\n const imageService = new ArweaveImageService({\n gateways: config.imageMetadata.gateways,\n timeout: config.imageMetadata.timeout,\n });\n enrichedItems = await enrichFeedItemsWithMedia(items, imageService, cache);\n }\n\n // Update cache with items (use original items, not enriched, to keep cache clean)\n await cache.setFeedData(schemaName, items);\n \n return enrichedItems;\n })\n : await (async () => {\n // Cache disabled - fetch items directly without caching\n console.log(`Cache disabled - fetching all items directly`);\n const client = await getClient()\n if (client) {\n console.log(`Client initialized: ${client.isInitialized()}`);\n }\n \n const items = await getFeedItemsBySchemaName(schemaName) as GraphQLItem[];\n console.log(`Found ${items.length} feed items for schema ${schemaName}`);\n\n // Enrich items with image metadata if enabled (even when cache is disabled)\n let enrichedItems = items;\n if (config.imageMetadata?.enabled) {\n const imageService = new ArweaveImageService({\n gateways: config.imageMetadata.gateways,\n timeout: config.imageMetadata.timeout,\n });\n enrichedItems = await enrichFeedItemsWithMedia(items, imageService, cache);\n }\n\n return enrichedItems;\n })();\n\n // Generate the feed (pass cache busting parameter if provided)\n const feedContent = await createFeed(feedItems, schemaName, format, cacheBust);\n const contentType = getContentType(format);\n\n // Cache the generated feed content\n if (config.enabled) {\n await cache.setFeedContent(schemaName, format, feedContent, contentType);\n \n // Get the cached content to retrieve ETag\n const cachedContent = await cache.getFeedContent(schemaName, format);\n const etag = cachedContent?.etag || '';\n const lastModified = cachedContent?.lastModified || Math.floor(Date.now() / 1000);\n\n return new Response(feedContent, {\n status: 200,\n headers: {\n 'Content-Type': contentType,\n 'ETag': etag,\n 'Last-Modified': new Date(lastModified * 1000).toUTCString(),\n 'Cache-Control': 'public, max-age=3600, s-maxage=3600, must-revalidate',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n 'X-Cache': 'MISS',\n },\n });\n }\n\n // Cache disabled - return without caching\n return new Response(feedContent, {\n status: 200,\n headers: {\n 'Content-Type': contentType,\n 'Cache-Control': 'public, max-age=3600, s-maxage=3600',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n },\n });\n } catch (error) {\n console.error('Feed generation error:', error);\n \n // Try to serve stale cache on error if available\n if (config.enabled) {\n const staleContent = await cache.getFeedContent(schemaName, format);\n if (staleContent) {\n console.log(`Serving stale cache due to error`);\n return new Response(staleContent.content, {\n status: 200,\n headers: {\n 'Content-Type': staleContent.contentType,\n 'ETag': staleContent.etag,\n 'Cache-Control': 'public, max-age=3600, s-maxage=3600, must-revalidate',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n 'X-Cache': 'STALE',\n 'Warning': '299 - \"Cache is stale\"',\n },\n });\n }\n }\n \n return new Response(\n JSON.stringify({\n error: 'Failed to generate feed',\n message: error instanceof Error ? error.message : 'Unknown error',\n }),\n {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n }\n )\n }\n}"],"names":["getArweaveUrlForTransaction","transactionId","BaseArweaveClient","loadFeedConfig","itemUrlBase","itemUrlPath","siteUrl","relationValuesToExclude","GET_SEEDS","gql","toCamelCase","str","_","letter","formatRfc822Date","timestamp","date","days","months","day","month","year","dayNum","hours","minutes","seconds","getCollectionPath","schemaName","parseEasRelationPropertyName","easPropertyName","singularProperty","modelName","idSegment","isList","setFeedItemDefaults","item","seedUid","options","storageTransactionIdSnake","storageTransactionIdCamel","storageTransactionId","collectionPath","validSeedUid","defaultLink","error","currentLink","currentGuid","pubDate","seedUidToModelType","relatedSeedUids","versionUidToSeedUid","assembledFeedItems","versionsBySeedUid","latestVersionUidsBySeedUid","processItemProperty","property","itemSeeds","metadata","propertyNameSnake","schemaUid","setSchemaUidForSchemaDefinition","isRelation","easType","isBytes32Relation","isNamingConventionRelation","result","value","propertyValue","seed","seedUidForProperty","existingFeedItem","propertyNameCamel","processSeeds","seeds","seedUids","i","modelType","itemVersions","getItemVersionsFromEas","itemVersion","existingVersions","latestVersionUids","versions","latestVersion","a","b","itemProperties","getItemPropertiesFromEas","RESERVED_KEYS","resolveRelationPropertiesToUrls","itemsToProcess","keysToProcess","k","key","uids","v","urls","resolved","uid","related","txId","outputKey","camelKey","getFeedItemsBySchemaName","feedConfig","easClient","EasClient","getSeedsBySchemaName","relatedSeedUidsArray","relatedSeeds","setFeedItemDefaultsOptions","DC_NS","CONTENT_NS","MEDIA_NS","CAPITALIZED_DUPLICATES","isRedundantCapitalizedKey","RSS_ITEM_ORDER","d","needsCdata","addTextOrCdata","parent","parts","serializeItemProperty","itemParent","guid","val","attrs","guidEle","src","title","enc","dc","dcKey","dcVal","ele","localName","textContent","attr","attrVal","mediaEle","group","groupEle","gk","gv","ln","child","ck","cv","authorStr","c","cat","tagName","generateRssXml","rssFeed","doc","create","channel","orderedSet","processedKeys","otherKeys","generateETag","createHash","generateFeedETag","format","lastProcessedTimestamp","itemCount","etagValue","generateContentETag","lastModified","contentLength","etagMatches","etag1","etag2","normalize","etag","parseIfNoneMatch","headerValue","checkIfNoneMatch","ifNoneMatchHeader","currentETag","etags","MemoryCache","config","cached","items","now","lastProcessedItemId","timeCreated","cachedData","content","contentType","expiresAt","cachedContent","keysToDelete","imageMetadataTtl","fn","existingLock","lockPromise","FileCache","err","fs","join","imageMetadataDir","filePath","data","files","prefix","suffix","file","imageFiles","CacheManager","cachedItems","newItems","itemMap","itemId","merged","timeA","loadCacheConfig","ttl","cacheDir","cacheDisabledByEnvVar","cacheEnabledByEnvVar","isDev","enabled","backgroundRefresh","refreshInterval","imageMetadataEnabled","imageMetadataGateways","g","DEFAULT_ARWEAVE_GATEWAYS","imageMetadataTimeout","ArweaveImageService","gateways","gateway","url","headResponse","rangeResponse","buffer","dimensions","response","sizeOf","mimeMatch","controller","timeoutId","client","initializationPromise","cacheManager","getCacheManager","resetCacheManager","initializeSeedClient","seedClient","DEFAULT_ARWEAVE_HOST","getClient","teardownSeedClient","SITE_CONFIG","enrichFeedItemsWithMedia","imageService","cache","enrichmentPromises","imageMetadata","enrichedItems","enrichedMap","transformToFeedItems","fallbackUrl","pluralize","itemUrl","pubDateStr","dateValue","feedItem","imageMeta","createFeed","cacheBust","collectionName","feedUrlBase","feedUrl","feedTitle","capitalize","transformedItems","atomFeed","entry","imageHtml","generateAtomFeed","jsonFeed","jsonItem","jsonResult","generateJsonFeed","rssItem","mediaContent","thumbnail","mediaDescription","keywords","w","author","imageUrl","enclosureUrl","timeCreatedDate","getContentType","parseFormat","segment","normalized","handleFeedRequest","collectionSegment","formatSegment","ifNoneMatch","feedItems","allItems","feedContent","staleContent"],"mappings":"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"}