@seedprotocol/feed 0.4.31 → 0.5.1
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/README.md +36 -0
- package/dist/bootstrap.d.ts +13 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/cache/config.d.ts.map +1 -1
- package/dist/config.d.ts +23 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/getFeedItems.d.ts.map +1 -1
- package/dist/index.d.ts +9 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +556 -525
- package/dist/index.js.map +1 -1
- package/dist/services/arweaveImageService.d.ts.map +1 -1
- package/package.json +3 -5
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/utils/arweaveUrl.ts","../src/config.ts","../src/listRelationKey.ts","../src/imageRelationEnrichment.ts","../src/feedFieldStorageModel.ts","../src/hydrateArweaveRichText.ts","../src/parseEasPropertyMetadataForFeed.ts","../src/getFeedItems.ts","../src/consume/parseRss.ts","../src/rss/generateRssXml.ts","../src/cache/types.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/pickFeedItemRichText.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 * - FEED_EXPAND_RELATIONS: When 'false', relation properties stay as UIDs. Default: true (expand to nested objects).\n * - FEED_PAGE_SIZE: Items per page for paged feeds (default: 25).\n * - FEED_INCLUDE_DATA_URI_HTML_ITEMS: When 'true', include items whose body/html contain embedded data-URI images.\n * Default: false (omit such items to avoid huge RSS payloads).\n */\nexport function loadFeedConfig(): {\n itemUrlBase: string;\n itemUrlPath: string;\n siteUrl: string;\n expandRelations: boolean;\n pageSize: number;\n richTextDataUriImages: 'omit_items' | 'include_items';\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 const expandRelations = process.env.FEED_EXPAND_RELATIONS?.toLowerCase() !== 'false';\n const pageSize = parseInt(process.env.FEED_PAGE_SIZE || '25', 10);\n const richTextDataUriImages =\n process.env.FEED_INCLUDE_DATA_URI_HTML_ITEMS?.toLowerCase() === 'true'\n ? 'include_items'\n : 'omit_items';\n\n return { itemUrlBase, itemUrlPath, siteUrl, expandRelations, pageSize, richTextDataUriImages };\n}\n","import pluralize from 'pluralize'\n\n/** Matches SDK list storage: singular(schemaKey) + PascalCase(ref) + \"Ids\", e.g. authorIdentityIds → authors */\nconst CAMEL_LIST_RELATION_IDS = /^(.+?)([A-Z][a-zA-Z]+)Ids$/\n\n/**\n * Maps list-relation storage / wire names to schema-facing plural keys (e.g. `authorIdentityIds` → `authors`).\n * Non-matching keys are returned unchanged.\n */\nexport function publicListRelationPropertyKey(propertyKey: string): string {\n const camel = CAMEL_LIST_RELATION_IDS.exec(propertyKey)\n if (camel) {\n const singularPart = camel[1]\n if (singularPart) return pluralize(singularPart)\n }\n if (propertyKey.endsWith('_ids')) {\n const base = propertyKey.slice(0, -4)\n const segments = base.split('_').filter(Boolean)\n if (segments.length >= 2) {\n const singularProperty = segments[0]!\n return singularProperty.endsWith('s') ? singularProperty : pluralize(singularProperty)\n }\n return pluralize(base)\n }\n return propertyKey\n}\n\n/**\n * Removes keys that are storage aliases for the same list relation as `publicKey`\n * (e.g. delete `authorIdentityIds` when `authors` is the canonical field).\n */\nexport function stripListRelationStorageAliasesForPublicKey(\n item: Record<string, unknown>,\n publicKey: string\n): void {\n for (const k of Object.keys(item)) {\n if (k === publicKey) continue\n if (publicListRelationPropertyKey(k) === publicKey) {\n delete item[k]\n }\n }\n}\n\n/** If value is a JSON array string from legacy feed assembly, parse to string[]. */\nexport function tryCoerceJsonStringArray(value: unknown): unknown {\n if (Array.isArray(value)) return value\n if (typeof value !== 'string') return value\n const s = value.trim()\n if (!s.startsWith('[')) return value\n try {\n const parsed = JSON.parse(s) as unknown\n if (Array.isArray(parsed)) return parsed\n } catch {\n // keep original\n }\n return value\n}\n","import { getArweaveUrlForTransaction } from './utils/arweaveUrl'\n\n/**\n * Adds `arweaveUrl` to an assembled Image seed clone when `storageTransactionId`\n * (or snake_case) is present, so RSS/JSON feeds can expose both tx id and gateway URL.\n */\nexport function enrichImageSeedCloneForFeed(clone: Record<string, unknown>): void {\n const txId = (clone.storageTransactionId ?? clone.storage_transaction_id) as string | undefined\n if (txId && typeof txId === 'string' && txId.trim()) {\n try {\n clone.arweaveUrl = getArweaveUrlForTransaction(txId.trim())\n } catch {\n // keep clone without arweaveUrl\n }\n }\n}\n","/** Related Seed schema names (lowercased) used as primary RSS/Atom/JSON body sources. */\nexport const FEED_RICH_BODY_STORAGE_SCHEMAS = new Set(['html', 'file', 'json'])\n\nconst FIELD_MODELS_KEY = '_feedFieldStorageModels' as const\nconst LIST_MODELS_KEY = '_feedListElementStorageModels' as const\n\nexport type FeedFieldStorageModels = Record<string, string>\nexport type FeedListElementStorageModels = Record<string, string[]>\n\nfunction normalizeModelName(schemaName: string): string {\n return schemaName.trim().toLowerCase()\n}\n\nexport function isFeedRichBodyStorageSchema(schemaName: string): boolean {\n return FEED_RICH_BODY_STORAGE_SCHEMAS.has(normalizeModelName(schemaName))\n}\n\n/** html < file < json for tie-breaking among rich body fields. */\nexport function feedRichBodyModelPriority(schemaName: string): number {\n const m = normalizeModelName(schemaName)\n if (m === 'html') return 0\n if (m === 'file') return 1\n if (m === 'json') return 2\n return 99\n}\n\nexport function getFeedFieldStorageModels(\n item: Record<string, unknown>,\n): FeedFieldStorageModels | undefined {\n const raw = item[FIELD_MODELS_KEY]\n if (raw !== null && typeof raw === 'object' && !Array.isArray(raw)) {\n return raw as FeedFieldStorageModels\n }\n return undefined\n}\n\nexport function getFeedListElementStorageModels(\n item: Record<string, unknown>,\n): FeedListElementStorageModels | undefined {\n const raw = item[LIST_MODELS_KEY]\n if (raw !== null && typeof raw === 'object' && !Array.isArray(raw)) {\n return raw as FeedListElementStorageModels\n }\n return undefined\n}\n\nexport function setFeedFieldStorageModel(\n item: Record<string, unknown>,\n fieldKey: string,\n schemaName: string,\n): void {\n const normalized = normalizeModelName(schemaName)\n let map = getFeedFieldStorageModels(item)\n if (!map) {\n map = {}\n item[FIELD_MODELS_KEY] = map\n }\n map[fieldKey] = normalized\n}\n\nexport function setFeedListElementStorageModels(\n item: Record<string, unknown>,\n outputKey: string,\n models: string[],\n): void {\n let map = getFeedListElementStorageModels(item)\n if (!map) {\n map = {}\n item[LIST_MODELS_KEY] = map\n }\n map[outputKey] = models.map(normalizeModelName)\n}\n","import { BaseArweaveClient, isKnownArweaveGatewayHostname } from '@seedprotocol/sdk'\nimport {\n getFeedFieldStorageModels,\n getFeedListElementStorageModels,\n isFeedRichBodyStorageSchema,\n} from './feedFieldStorageModel'\n\n/** Same logical fields as legacy `pickFeedItemRichText` primary keys — inline HTML for feeds. */\nconst RICH_TEXT_KEYS = ['html', 'Html', 'body', 'Body', 'content', 'Content'] as const\n\n/** Max UTF-8 bytes for a single rich-body fetch (HTML/JSON on Arweave can exceed 2MB with embeds). */\nconst MAX_BODY_BYTES = 8_000_000\n\n/**\n * True for a single-path gateway URL whose path looks like an Arweave transaction id\n * (e.g. `https://arweave.net/<43-char tx id>`), matching {@link getArweaveUrlForTransaction}.\n */\nexport function isArweaveTransactionGatewayUrl(raw: string): boolean {\n const s = raw.trim()\n if (!s.startsWith('http://') && !s.startsWith('https://')) return false\n try {\n const u = new URL(s)\n const host = u.hostname.toLowerCase()\n const expected = BaseArweaveClient.getHost().toLowerCase()\n if (host !== expected && !isKnownArweaveGatewayHostname(host)) return false\n const segments = u.pathname.replace(/^\\//, '').split('/').filter(Boolean)\n if (segments.length !== 1) return false\n const id = segments[0]!\n return /^[A-Za-z0-9_-]{43}$/.test(id)\n } catch {\n return false\n }\n}\n\nasync function fetchGatewayPayloadAsUtf8(url: string): Promise<string | null> {\n try {\n const res = await fetch(url, {\n headers: { Accept: 'text/html, text/plain, text/markdown, application/json, */*' },\n signal: AbortSignal.timeout(15_000),\n })\n if (!res.ok) return null\n const ct = res.headers.get('content-type') ?? ''\n if (/^(image|video|audio)\\//i.test(ct)) return null\n const buf = await res.arrayBuffer()\n if (buf.byteLength > MAX_BODY_BYTES) return null\n const text = new TextDecoder('utf-8', { fatal: false }).decode(buf)\n if (text.length === 0 || text.includes('\\0')) return null\n return text\n } catch {\n return null\n }\n}\n\nasync function hydrateStringFieldIfGateway(\n item: Record<string, unknown>,\n key: string\n): Promise<void> {\n const v = item[key]\n if (typeof v !== 'string' || v.trim() === '') return\n if (!isArweaveTransactionGatewayUrl(v)) return\n const text = await fetchGatewayPayloadAsUtf8(v)\n if (text === null) return\n item[key] = text\n}\n\n/**\n * After {@link resolveRelationPropertiesToUrls}, Html/File relation fields may be gateway URLs.\n * For RSS/Atom `content:encoded`, replace those with the fetched UTF-8 body (typically HTML).\n */\nexport async function hydrateArweaveRichTextInFeedItems(\n items: Record<string, unknown>[]\n): Promise<void> {\n for (const item of items) {\n const keysToHydrate = new Set<string>([...RICH_TEXT_KEYS])\n const fieldModels = getFeedFieldStorageModels(item)\n if (fieldModels) {\n for (const [k, m] of Object.entries(fieldModels)) {\n if (isFeedRichBodyStorageSchema(m)) keysToHydrate.add(k)\n }\n }\n for (const key of keysToHydrate) {\n await hydrateStringFieldIfGateway(item, key)\n }\n\n const listModels = getFeedListElementStorageModels(item)\n if (listModels) {\n for (const [listKey, models] of Object.entries(listModels)) {\n const arr = item[listKey]\n if (!Array.isArray(arr)) continue\n const n = Math.min(models.length, arr.length)\n for (let i = 0; i < n; i++) {\n if (!isFeedRichBodyStorageSchema(models[i]!)) continue\n const el = arr[i]\n if (typeof el !== 'string' || !isArweaveTransactionGatewayUrl(el)) continue\n const text = await fetchGatewayPayloadAsUtf8(el)\n if (text === null) continue\n arr[i] = text\n }\n }\n }\n }\n}\n","export type EasPropertyMetadataForFeed = {\n name: string;\n value: string | string[];\n type?: string;\n};\n\nexport type ParseEasPropertyMetadataForFeedResult =\n | { ok: true; metadata: EasPropertyMetadataForFeed }\n | { ok: false; reason: 'empty' }\n | { ok: false; reason: 'shape' }\n | { ok: false; reason: 'parse'; error: unknown };\n\n/**\n * Parse EAS property `decodedDataJson` for feed assembly.\n * Matches SDK guards in syncDbWithEas / saveDataToDb (empty trim, parse, array + [0].value).\n */\nexport function parseEasPropertyMetadataForFeed(\n decodedDataJson: string | undefined | null,\n): ParseEasPropertyMetadataForFeedResult {\n const raw =\n typeof decodedDataJson === 'string' ? decodedDataJson.trim() : '';\n if (!raw) {\n return { ok: false, reason: 'empty' };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (e) {\n return { ok: false, reason: 'parse', error: e };\n }\n\n if (!Array.isArray(parsed) || parsed.length === 0 || !parsed[0]?.value) {\n return { ok: false, reason: 'shape' };\n }\n\n const metadata = parsed[0].value as EasPropertyMetadataForFeed;\n return { ok: true, metadata };\n}\n","import {\n getSeedsBySchemaName,\n getItemVersionsFromEas,\n getItemPropertiesFromEas,\n EasClient,\n setSchemaUidForSchemaDefinition,\n withExcludeRevokedFilter,\n pickLatestPropertyAttestationsByRefAndSchema,\n} from '@seedprotocol/sdk';\nimport { getArweaveUrlForTransaction } from './utils/arweaveUrl';\nimport { gql } from 'graphql-request';\nimport { loadFeedConfig } from './config';\nimport {\n publicListRelationPropertyKey,\n stripListRelationStorageAliasesForPublicKey,\n tryCoerceJsonStringArray,\n} from './listRelationKey';\nimport { enrichImageSeedCloneForFeed } from './imageRelationEnrichment';\nimport { hydrateArweaveRichTextInFeedItems } from './hydrateArweaveRichText';\nimport { parseEasPropertyMetadataForFeed } from './parseEasPropertyMetadataForFeed';\nimport {\n setFeedFieldStorageModel,\n setFeedListElementStorageModels,\n} from './feedFieldStorageModel';\n\nconst IMAGE_SCHEMA = 'image';\n\nconst relationValuesToExclude = [\n '0x0000000000000000000000000000000000000000000000000000000000000020',\n];\n\nconst GET_SEEDS = gql`\n query GetSeeds($where: AttestationWhereInput!, $take: Int, $skip: Int) {\n itemSeeds: attestations(where: $where, orderBy: [{ timeCreated: desc }], take: $take, skip: $skip) {\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\nfunction resetProcessingMaps(): void {\n seedUidToModelType.clear();\n relatedSeedUids.clear();\n versionUidToSeedUid.clear();\n assembledFeedItems.clear();\n versionsBySeedUid.clear();\n latestVersionUidsBySeedUid.clear();\n}\n\nconst processItemProperty = async (\n property: AttestationLike,\n itemSeeds: AttestationLike[]\n): Promise<void> => {\n const parsed = parseEasPropertyMetadataForFeed(property.decodedDataJson);\n if (!parsed.ok) {\n const { id, refUID, schemaId } = property;\n if (parsed.reason === 'empty') {\n console.warn(\n '[feed] [processItemProperty] empty decodedDataJson for property:',\n id,\n refUID,\n schemaId,\n );\n } else if (parsed.reason === 'parse') {\n console.warn(\n '[feed] [processItemProperty] failed to parse decodedDataJson for property:',\n id,\n refUID,\n schemaId,\n parsed.error,\n );\n } else {\n console.warn(\n '[feed] [processItemProperty] invalid decodedDataJson structure for property:',\n id,\n refUID,\n schemaId,\n );\n }\n return;\n }\n\n const metadata = parsed.metadata;\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: string | string[] = metadata.value as string | string[];\n if (isRelation && isList && Array.isArray(propertyValue)) {\n propertyValue = propertyValue.map((v) => String(v));\n } else 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 rawProperties = await getItemPropertiesFromEas({ versionUids: latestVersionUids });\n const itemProperties = pickLatestPropertyAttestationsByRefAndSchema(rawProperties);\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. Image schema relations are left as UIDs so\n * expandRelationProperties can emit nested seeds with arweaveUrl.\n * Supports optional _id/_ids key 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 let value = item[key];\n const coerced = tryCoerceJsonStringArray(value);\n if (coerced !== value && Array.isArray(coerced)) {\n item[key] = coerced;\n value = coerced;\n }\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 const models: 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 models.push('unknown');\n continue;\n }\n const modelForUid = seedUidToModelType.get(uid) ?? 'unknown';\n // Keep Image seed UIDs for expandRelationProperties (nested seed + arweaveUrl).\n if (modelForUid === IMAGE_SCHEMA) {\n urls.push(uid);\n models.push(IMAGE_SCHEMA);\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 models.push(modelForUid);\n resolved = true;\n } catch {\n urls.push(uid);\n models.push(modelForUid);\n }\n } else {\n urls.push(uid);\n models.push(modelForUid);\n }\n }\n if (resolved) {\n const outputKey = publicListRelationPropertyKey(key);\n item[outputKey] = urls;\n setFeedListElementStorageModels(item, outputKey, models);\n stripListRelationStorageAliasesForPublicKey(item, outputKey);\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 // Keep Image seed UIDs for expandRelationProperties (nested seed + arweaveUrl).\n if (seedUidToModelType.get(value) === IMAGE_SCHEMA) {\n continue;\n }\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 const relatedModel = seedUidToModelType.get(value) ?? 'unknown';\n setFeedFieldStorageModel(item, outputKey, relatedModel);\n const camelOut = toCamelCase(outputKey);\n if (camelOut !== outputKey) {\n setFeedFieldStorageModel(item, camelOut, relatedModel);\n }\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\n/**\n * Expands relation properties to nested objects from assembledFeedItems.\n * Image seeds always expand (with arweaveUrl enrichment when storage exists).\n * Other relations skip expansion when the target has storage (already URL-resolved).\n */\nfunction expandRelationProperties(\n schemaName: string,\n options: SetFeedItemDefaultsOptions,\n expandRelations: boolean\n): void {\n if (!expandRelations) return;\n\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 let value = item[key];\n const coerced = tryCoerceJsonStringArray(value);\n if (coerced !== value && Array.isArray(coerced)) {\n item[key] = coerced;\n value = coerced;\n }\n const isList = Array.isArray(value);\n const uids = isList ? (value as unknown[]) : [value];\n\n const expanded: unknown[] = [];\n let didExpand = false;\n\n for (const uid of uids) {\n if (typeof uid !== 'string' || relationValuesToExclude.includes(uid)) {\n expanded.push(uid);\n continue;\n }\n const related = assembledFeedItems.get(uid);\n if (!related) {\n expanded.push(uid);\n continue;\n }\n const txId =\n (related?.storageTransactionId ?? related?.storage_transaction_id) as string | undefined;\n const isImage = seedUidToModelType.get(uid) === IMAGE_SCHEMA;\n\n if (isImage) {\n const relatedSchema = seedUidToModelType.get(uid) ?? 'unknown';\n const clone = { ...related } as Record<string, unknown>;\n setFeedItemDefaults(clone, uid, relatedSchema, options);\n enrichImageSeedCloneForFeed(clone);\n expanded.push(clone);\n didExpand = true;\n continue;\n }\n\n // Non-image: skip expansion when related has storage (URL resolution already ran for non-images)\n if (txId && typeof txId === 'string' && txId.trim()) {\n expanded.push(uid);\n continue;\n }\n const relatedSchema = seedUidToModelType.get(uid) ?? 'unknown';\n const clone = { ...related } as Record<string, unknown>;\n setFeedItemDefaults(clone, uid, relatedSchema, options);\n expanded.push(clone);\n didExpand = true;\n }\n\n if (didExpand) {\n const outputKey = isList\n ? publicListRelationPropertyKey(key)\n : key.endsWith('_id')\n ? key.replace(/_id$/, '')\n : key;\n item[outputKey] = isList ? expanded : expanded[0];\n stripListRelationStorageAliasesForPublicKey(item, outputKey);\n if (outputKey !== key) {\n delete item[key];\n const camelKey = toCamelCase(key);\n if (camelKey !== key) delete item[camelKey];\n }\n }\n }\n }\n}\n\nasync function processSeedsToFeedItems(\n schemaName: string,\n seeds: AttestationLike[],\n expandRelations: boolean\n): Promise<Record<string, unknown>[]> {\n resetProcessingMaps();\n\n await processSeeds(seeds);\n\n const easClient = EasClient.getEasClient();\n const relatedSeedUidsArray = Array.from(relatedSeedUids);\n const { itemSeeds: relatedSeeds } = await easClient.request(GET_SEEDS, {\n where: withExcludeRevokedFilter({\n id: {\n in: relatedSeedUidsArray,\n },\n }),\n take: relatedSeedUidsArray.length || 1,\n skip: 0,\n });\n\n await processSeeds((relatedSeeds ?? []) as AttestationLike[]);\n\n resolveRelationPropertiesToUrls(schemaName);\n\n const feedConfig = loadFeedConfig();\n const setFeedItemDefaultsOptions: SetFeedItemDefaultsOptions = {\n itemUrlBase: feedConfig.itemUrlBase,\n itemUrlPath: feedConfig.itemUrlPath,\n siteUrl: feedConfig.siteUrl,\n };\n\n expandRelationProperties(\n schemaName,\n setFeedItemDefaultsOptions,\n expandRelations\n );\n\n const items = 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 await hydrateArweaveRichTextInFeedItems(items);\n\n return items;\n}\n\nexport const getFeedItemsBySchemaName = async (\n schemaName: string,\n options?: { limit?: number; skip?: number }\n): Promise<Record<string, unknown>[]> => {\n const feedConfig = loadFeedConfig();\n const limit = options?.limit ?? 100;\n const skip = options?.skip ?? 0;\n\n const seeds = (await getSeedsBySchemaName(schemaName, limit, skip)) as AttestationLike[];\n const items = await processSeedsToFeedItems(schemaName, seeds, feedConfig.expandRelations !== false);\n return items;\n};\n\nexport const getFeedItemsBySchemaNameForMonth = async (\n schemaName: string,\n year: number,\n month: number\n): Promise<Record<string, unknown>[]> => {\n const feedConfig = loadFeedConfig();\n const startDate = new Date(year, month - 1, 1);\n const endDate = new Date(year, month, 0, 23, 59, 59);\n const startTs = Math.floor(startDate.getTime() / 1000);\n const endTs = Math.floor(endDate.getTime() / 1000) + 1;\n\n const where = withExcludeRevokedFilter({\n AND: [\n {\n schema: {\n is: {\n schemaNames: {\n some: {\n name: { equals: schemaName },\n },\n },\n },\n },\n },\n {\n timeCreated: { gte: startTs, lt: endTs },\n },\n ],\n });\n\n const easClient = EasClient.getEasClient();\n const { itemSeeds } = await easClient.request(GET_SEEDS, {\n where,\n take: 1000,\n skip: 0,\n });\n\n const seeds = (itemSeeds ?? []) as AttestationLike[];\n return processSeedsToFeedItems(schemaName, seeds, feedConfig.expandRelations !== false);\n};\n","import Parser from 'rss-parser'\n\nexport type ParsedRssChannel = {\n title?: string\n link?: string\n description?: string\n /** Plain serializable item records (dates become ISO strings). */\n items: Record<string, unknown>[]\n}\n\nfunction toPlainItem(item: unknown): Record<string, unknown> {\n try {\n return JSON.parse(JSON.stringify(item)) as Record<string, unknown>\n } catch {\n return { ...(item as Record<string, unknown>) }\n }\n}\n\n/** Parse RSS or Atom XML into channel metadata and plain item records for `normalizeFeedItemFields`. */\nconst parser = new Parser({\n customFields: {\n item: [\n 'featureImage',\n 'feature_image',\n 'content:encoded',\n 'seedUid',\n 'SeedUid',\n ],\n },\n})\n\nexport async function parseRssString(xml: string): Promise<ParsedRssChannel> {\n const feed = await parser.parseString(xml)\n return {\n title: feed.title,\n link: feed.link,\n description: feed.description,\n items: (feed.items ?? []).map(toPlainItem),\n }\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/'\ntype RelationNamespaceContext = { prefix: string; uri: string }\ntype RelationNamespaceMap = Record<string, RelationNamespaceContext>\nconst RELATION_NAMESPACES: RelationNamespaceMap = {\n publication: {\n prefix: 'publication',\n uri: 'https://seedprotocol.io/ns/publication/1.0',\n },\n}\nconst RELATION_NAMESPACE_URI_BASE = 'https://seedprotocol.io/ns/relations'\nconst RESERVED_XML_PREFIXES = new Set(['xml', 'xmlns', 'dc', 'media', 'content', 'atom', 'fh'])\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 'author',\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 isExpandedRelationObject(value: unknown): value is Record<string, unknown> {\n return (\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n !(value instanceof Date)\n )\n}\n\nconst MAX_EXPANDED_RELATION_DEPTH = 4\nconst NON_RELATION_KEYS = new Set([\n ...RSS_ITEM_ORDER,\n 'guid',\n 'source',\n 'dc',\n 'content',\n 'content:encoded',\n 'items',\n])\n\nfunction normalizeRelationKey(key: string): string {\n return key.endsWith('s') && key.length > 1 ? key.slice(0, -1) : key\n}\n\nfunction sanitizePrefixToken(rawKey: string): string {\n const token = rawKey\n .trim()\n .replace(/[^A-Za-z0-9_.-]+/g, '_')\n .replace(/^[^A-Za-z_]+/, '')\n .replace(/^$/, 'rel')\n return token.toLowerCase()\n}\n\nfunction createUniquePrefix(\n preferredPrefix: string,\n usedPrefixes: Set<string>\n): string {\n if (!usedPrefixes.has(preferredPrefix) && !RESERVED_XML_PREFIXES.has(preferredPrefix)) {\n usedPrefixes.add(preferredPrefix)\n return preferredPrefix\n }\n\n let suffix = 2\n while (true) {\n const candidate = `${preferredPrefix}${suffix}`\n if (!usedPrefixes.has(candidate) && !RESERVED_XML_PREFIXES.has(candidate)) {\n usedPrefixes.add(candidate)\n return candidate\n }\n suffix += 1\n }\n}\n\nfunction isRelationArray(value: unknown): value is Record<string, unknown>[] {\n return (\n Array.isArray(value) &&\n value.length > 0 &&\n value.every((entry) => isExpandedRelationObject(entry))\n )\n}\n\nfunction buildRelationNamespaceMap(items: Record<string, unknown>[]): RelationNamespaceMap {\n const relationNamespaces: RelationNamespaceMap = { ...RELATION_NAMESPACES }\n const usedPrefixes = new Set(\n Object.values(relationNamespaces).map((namespaceContext) => namespaceContext.prefix)\n )\n\n const discoveredRelationKeys = new Set<string>()\n for (const item of items) {\n for (const [key, value] of Object.entries(item)) {\n const isAuthorRelation =\n (key === 'author' && isExpandedRelationObject(value)) ||\n (key === 'authors' && isRelationArray(value))\n if (key.startsWith('_')) continue\n if (NON_RELATION_KEYS.has(key) && !isAuthorRelation) continue\n if (isExpandedRelationObject(value) || isRelationArray(value)) {\n discoveredRelationKeys.add(normalizeRelationKey(key))\n }\n }\n }\n\n const sortedDiscoveredRelationKeys = [...discoveredRelationKeys].sort()\n for (const relationKey of sortedDiscoveredRelationKeys) {\n if (relationNamespaces[relationKey]) continue\n const preferredPrefix = sanitizePrefixToken(relationKey)\n const prefix = createUniquePrefix(preferredPrefix, usedPrefixes)\n relationNamespaces[relationKey] = {\n prefix,\n uri: `${RELATION_NAMESPACE_URI_BASE}/${relationKey}/1.0`,\n }\n }\n\n return relationNamespaces\n}\n\nfunction getRelationNamespaceContext(\n key: string,\n relationNamespaces: RelationNamespaceMap\n): RelationNamespaceContext | undefined {\n if (key in relationNamespaces) return relationNamespaces[key]\n if (key.endsWith('s') && key.length > 1) {\n const singular = key.slice(0, -1)\n if (singular in relationNamespaces) return relationNamespaces[singular]\n }\n return undefined\n}\n\nfunction getNamespacedChildTagName(localName: string, namespaceContext?: RelationNamespaceContext): string {\n if (!namespaceContext) return localName\n return `${namespaceContext.prefix}:${localName}`\n}\n\nfunction serializeExpandedRelation(\n parent: XMLBuilder,\n obj: Record<string, unknown>,\n tagName: string,\n addDcCreator?: boolean,\n depth = 0,\n namespaceContext?: RelationNamespaceContext\n): void {\n const ele = parent.ele(tagName)\n for (const [k, v] of Object.entries(obj)) {\n if (v == null || k.startsWith('_') || k === 'items') continue\n const childTagName = getNamespacedChildTagName(k, namespaceContext)\n if (depth >= MAX_EXPANDED_RELATION_DEPTH) {\n const child = ele.ele(childTagName)\n addTextOrCdata(child, typeof v === 'object' ? JSON.stringify(v) : String(v))\n continue\n }\n if (Array.isArray(v)) {\n const itemTag = k.endsWith('s') && k.length > 1 ? k.slice(0, -1) : k\n const childItemTag = getNamespacedChildTagName(itemTag, namespaceContext)\n for (const entry of v) {\n if (entry === undefined || entry === null) continue\n if (isExpandedRelationObject(entry)) {\n serializeExpandedRelation(ele, entry, childItemTag, false, depth + 1, namespaceContext)\n } else {\n const leaf = ele.ele(childItemTag)\n addTextOrCdata(leaf, String(entry))\n }\n }\n continue\n }\n if (isExpandedRelationObject(v)) {\n serializeExpandedRelation(\n ele,\n v as Record<string, unknown>,\n childTagName,\n false,\n depth + 1,\n namespaceContext\n )\n continue\n }\n const child = ele.ele(childTagName)\n addTextOrCdata(child, String(v))\n }\n if (addDcCreator) {\n const name = obj.name ?? obj.seedUid\n if (name) parent.ele(DC_NS, 'creator').txt(String(name))\n }\n}\n\nfunction serializeItemProperty(\n itemParent: XMLBuilder,\n key: string,\n value: unknown,\n item: Record<string, unknown>,\n relationNamespaces: RelationNamespaceMap\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 === '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 (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 (value instanceof Date) {\n const ele = itemParent.ele(key)\n ele.txt(formatRfc822Date(value))\n return\n }\n\n if (key === 'author' && isExpandedRelationObject(value)) {\n const namespaceContext = getRelationNamespaceContext(key, relationNamespaces)\n serializeExpandedRelation(itemParent, value, 'author', true, 0, namespaceContext)\n return\n }\n\n if (key === 'authors' && Array.isArray(value)) {\n const namespaceContext = getRelationNamespaceContext(key, relationNamespaces)\n for (const a of value) {\n if (a && isExpandedRelationObject(a)) {\n serializeExpandedRelation(itemParent, a, 'author', true, 0, namespaceContext)\n } else {\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 }\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 expanded relation objects (e.g. coAuthors as list)\n if (Array.isArray(value) && value.length > 0 && value.every((v) => isExpandedRelationObject(v))) {\n const tagName = key.endsWith('s') ? key.slice(0, -1) : key\n const namespaceContext = getRelationNamespaceContext(key, relationNamespaces)\n for (const v of value) {\n if (v) serializeExpandedRelation(itemParent, v, tagName, false, 0, namespaceContext)\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 // Generic expanded relation object (e.g. coAuthor, other relation properties)\n if (isExpandedRelationObject(value)) {\n const namespaceContext = getRelationNamespaceContext(key, relationNamespaces)\n serializeExpandedRelation(itemParent, value, key, false, 0, namespaceContext)\n return\n }\n\n const ele = itemParent.ele(key)\n addTextOrCdata(ele, String(value))\n}\n\nconst ATOM_NS = 'http://www.w3.org/2005/Atom'\nconst FH_NS = 'http://purl.org/syndication/history/1.0'\n\nexport interface RssFeedLink {\n rel: string\n href: string\n type?: string\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 /** RFC 5005 pagination/archive links (atom:link in channel) */\n links?: RssFeedLink[]\n /** RFC 5005 archive document marker */\n isArchive?: boolean\n}\n\nexport function generateRssXml(rssFeed: RssFeedInput): string {\n const doc = create({ version: '1.0', encoding: 'UTF-8' })\n const relationNamespaces = buildRelationNamespaceMap(rssFeed.items)\n const relationNamespaceAttrs = Object.values(relationNamespaces).reduce<Record<string, string>>(\n (acc, relationNamespace) => {\n acc[`xmlns:${relationNamespace.prefix}`] = relationNamespace.uri\n return acc\n },\n {}\n )\n const rssAttrs: Record<string, string> = {\n 'xmlns:dc': DC_NS,\n 'xmlns:content': CONTENT_NS,\n 'xmlns:media': MEDIA_NS,\n 'xmlns:atom': ATOM_NS,\n ...relationNamespaceAttrs,\n }\n if (rssFeed.isArchive) {\n rssAttrs['xmlns:fh'] = FH_NS\n }\n const rss = doc.ele('rss', { version: '2.0', ...rssAttrs })\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 if (rssFeed.links?.length) {\n for (const link of rssFeed.links) {\n const attrs: Record<string, string> = { rel: link.rel, href: link.href }\n if (link.type) attrs.type = link.type\n channel.ele(ATOM_NS, 'link', attrs)\n }\n }\n if (rssFeed.isArchive) {\n channel.ele(FH_NS, 'archive')\n }\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, relationNamespaces)\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, relationNamespaces)\n }\n }\n\n return doc.end({ prettyPrint: true })\n}\n","import type { GraphQLItem, ImageMetadata } from '../types';\n\n/**\n * Cached feed data for a specific schema\n */\nexport interface CachedFeedData {\n items: GraphQLItem[]; // Processed items\n lastProcessedTimestamp: number; // Unix timestamp of newest item\n lastProcessedItemId: string; // ID of newest item (for deduplication)\n lastUpdated: number; // When cache was last updated (Unix timestamp)\n etag: string; // ETag for HTTP conditional requests\n}\n\n/**\n * Cached image metadata for an Arweave transaction ID\n */\nexport interface CachedImageMetadata {\n metadata: ImageMetadata\n cachedAt: number // Unix timestamp when cached\n expiresAt: number // Unix timestamp when cache expires\n}\n\n/**\n * Cached feed content for a specific schema and format\n */\nexport interface CachedFeedContent {\n content: string; // Generated XML/JSON feed\n contentType: string; // MIME type\n etag: string; // ETag for this format\n lastModified: number; // Last modification time (Unix timestamp)\n expiresAt: number; // Cache expiration timestamp (Unix timestamp)\n}\n\n/**\n * Image metadata cache configuration\n */\nexport interface ImageMetadataConfig {\n enabled: boolean\n ttl: number // Time to live in seconds (default: 7 days)\n gateways: string[] // Arweave gateway domains\n timeout: number // Request timeout in milliseconds\n}\n\n/**\n * Cache configuration options\n */\nexport interface CacheConfig {\n ttl: number; // Time to live in seconds\n cacheDir: string; // Directory for persistent cache\n enabled: boolean; // Enable/disable caching\n backgroundRefresh: boolean; // Enable background refresh job\n refreshInterval: number; // Background refresh interval in seconds\n imageMetadata?: ImageMetadataConfig // Image metadata cache configuration\n pageTtl?: number; // TTL for page > 1 (default: 300)\n archiveTtl?: number; // TTL for archives (default: 86400)\n}\n\n/**\n * Options for cache content key (pagination or archive)\n */\nexport interface CacheContentKeyOptions {\n page?: number;\n archive?: { year: number; month: number };\n}\n\nexport function buildContentKey(\n schemaName: string,\n format: string,\n options?: CacheContentKeyOptions\n): string {\n if (options?.archive) {\n return `${schemaName}:${format}:archive-${options.archive.year}-${options.archive.month}`;\n }\n if (options?.page != null && options.page > 1) {\n return `${schemaName}:${format}:page-${options.page}`;\n }\n return `${schemaName}:${format}`;\n}\n\n/**\n * Cache statistics for monitoring\n */\nexport interface CacheStats {\n hits: number;\n misses: number;\n refreshes: number;\n errors: number;\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, CacheContentKeyOptions, CachedImageMetadata } from './types';\nimport { buildContentKey } 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(\n schemaName: string,\n format: string,\n contentKeyOptions?: CacheContentKeyOptions\n ): CachedFeedContent | null {\n const key = buildContentKey(schemaName, format, contentKeyOptions);\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 contentKeyOptions?: CacheContentKeyOptions,\n ttlOverride?: number\n ): void {\n const key = buildContentKey(schemaName, format, contentKeyOptions);\n const now = Math.floor(Date.now() / 1000);\n const ttl = ttlOverride ?? this.config.ttl;\n const expiresAt = now + 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, CacheContentKeyOptions, CachedImageMetadata } from './types';\nimport { buildContentKey } 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(\n schemaName: string,\n format: string,\n contentKeyOptions?: CacheContentKeyOptions\n ): string {\n const key = buildContentKey(schemaName, format, contentKeyOptions);\n const filePart = key.replace(/:/g, '-');\n return join(this.cacheDir, `${filePart}.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 contentKeyOptions?: CacheContentKeyOptions\n ): Promise<CachedFeedContent | null> {\n try {\n const filePath = this.getFeedContentPath(schemaName, format, contentKeyOptions);\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, contentKeyOptions);\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 contentKeyOptions?: CacheContentKeyOptions\n ): Promise<void> {\n try {\n await this.ensureCacheDir();\n const filePath = this.getFeedContentPath(schemaName, format, contentKeyOptions);\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(\n schemaName: string,\n format: string,\n contentKeyOptions?: CacheContentKeyOptions\n ): Promise<void> {\n try {\n const filePath = this.getFeedContentPath(schemaName, format, contentKeyOptions);\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 CacheContentKeyOptions,\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 contentKeyOptions?: CacheContentKeyOptions\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, contentKeyOptions);\n if (cached) {\n this.stats.hits++;\n return cached;\n }\n\n // Check file cache\n cached = await this.fileCache.getFeedContent(schemaName, format, contentKeyOptions);\n if (cached) {\n // Restore to memory cache\n this.memoryCache.setFeedContent(\n schemaName,\n format,\n cached.content,\n cached.contentType,\n contentKeyOptions\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 contentKeyOptions?: CacheContentKeyOptions\n ): Promise<void> {\n if (!this.config.enabled) {\n return;\n }\n\n try {\n let ttlOverride: number | undefined;\n if (contentKeyOptions?.archive) {\n ttlOverride = this.config.archiveTtl ?? 86400;\n } else if (contentKeyOptions?.page != null && contentKeyOptions.page > 1) {\n ttlOverride = this.config.pageTtl ?? 300;\n }\n\n // Update memory cache\n this.memoryCache.setFeedContent(\n schemaName,\n format,\n content,\n contentType,\n contentKeyOptions,\n ttlOverride\n );\n\n // Get the cached content to save to file\n const cached = this.memoryCache.getFeedContent(schemaName, format, contentKeyOptions);\n if (cached) {\n await this.fileCache.setFeedContent(schemaName, format, cached, contentKeyOptions);\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 const pageTtl = parseInt(process.env.CACHE_PAGE_TTL || '300', 10);\n const archiveTtl = parseInt(process.env.CACHE_ARCHIVE_TTL || '86400', 10);\n\n return {\n ttl,\n cacheDir,\n enabled,\n backgroundRefresh,\n refreshInterval,\n imageMetadata,\n pageTtl,\n archiveTtl,\n };\n}\n","import { DEFAULT_ARWEAVE_GATEWAYS, ensureReadGatewaySelected } 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 await ensureReadGatewaySelected().catch(() => {\n /* feed may run without browser client init */\n })\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","/**\n * Resolves RSS/Atom/JSON feed `description` and `content` from dynamic Seed item shapes.\n * Html-type properties often use `body`; `html` / `content` are alternate sources.\n */\n\nimport {\n feedRichBodyModelPriority,\n getFeedFieldStorageModels,\n getFeedListElementStorageModels,\n isFeedRichBodyStorageSchema,\n} from './feedFieldStorageModel'\n\nfunction firstNonEmptyString(item: Record<string, unknown>, keys: readonly string[]): string {\n for (const k of keys) {\n const v = item[k]\n if (typeof v === 'string' && v.trim() !== '') {\n return v\n }\n }\n return ''\n}\n\n/** Short text fields only (excerpt / summary) — not full HTML body keys. */\nexport const DESCRIPTION_KEYS = ['summary', 'Summary', 'description', 'Description', 'text', 'Text'] as const\n\n/**\n * Primary HTML / rich body keys first, then plain fallbacks (same as legacy `content` chain).\n */\nexport const CONTENT_PRIMARY_KEYS = ['html', 'Html', 'body', 'Body', 'content', 'Content'] as const\n\nconst CONTENT_FALLBACK_KEYS = ['summary', 'Summary', 'description', 'Description', 'text', 'Text'] as const\n\ntype RichBodyCandidate = {\n model: string\n key: string\n index: number\n value: string\n}\n\nfunction compareRichBodyCandidates(a: RichBodyCandidate, b: RichBodyCandidate): number {\n const d = feedRichBodyModelPriority(a.model) - feedRichBodyModelPriority(b.model)\n if (d !== 0) return d\n const k = a.key.localeCompare(b.key)\n if (k !== 0) return k\n return a.index - b.index\n}\n\nfunction pickTypedRichBodyContent(item: Record<string, unknown>): string {\n const fieldModels = getFeedFieldStorageModels(item)\n const listModels = getFeedListElementStorageModels(item)\n let best: RichBodyCandidate | null = null\n\n if (fieldModels) {\n for (const [key, model] of Object.entries(fieldModels)) {\n if (!isFeedRichBodyStorageSchema(model)) continue\n const v = item[key]\n if (typeof v !== 'string' || v.trim() === '') continue\n const cand: RichBodyCandidate = { model, key, index: 0, value: v }\n if (!best || compareRichBodyCandidates(cand, best) < 0) best = cand\n }\n }\n\n if (listModels) {\n for (const [listKey, models] of Object.entries(listModels)) {\n const arr = item[listKey]\n if (!Array.isArray(arr)) continue\n const n = Math.min(models.length, arr.length)\n for (let i = 0; i < n; i++) {\n const model = models[i]!\n if (!isFeedRichBodyStorageSchema(model)) continue\n const el = arr[i]\n if (typeof el !== 'string' || el.trim() === '') continue\n const cand: RichBodyCandidate = { model, key: listKey, index: i, value: el }\n if (!best || compareRichBodyCandidates(cand, best) < 0) best = cand\n }\n }\n }\n\n return best?.value ?? ''\n}\n\nexport function pickFeedItemDescription(item: Record<string, unknown>): string {\n return firstNonEmptyString(item, DESCRIPTION_KEYS)\n}\n\nexport function pickFeedItemContent(item: Record<string, unknown>): string {\n const typed = pickTypedRichBodyContent(item)\n if (typed !== '') return typed\n const primary = firstNonEmptyString(item, CONTENT_PRIMARY_KEYS)\n if (primary !== '') return primary\n return firstNonEmptyString(item, CONTENT_FALLBACK_KEYS)\n}\n\nfunction stringFieldContainsDataUriImage(value: unknown): boolean {\n if (typeof value !== 'string') return false\n return value.includes('data:image/') && value.includes(';base64,')\n}\n\nfunction feedItemTypedRichFieldsContainDataUriImage(item: Record<string, unknown>): boolean {\n const fieldModels = getFeedFieldStorageModels(item)\n if (fieldModels) {\n for (const [key, model] of Object.entries(fieldModels)) {\n if (!isFeedRichBodyStorageSchema(model)) continue\n if (stringFieldContainsDataUriImage(item[key])) return true\n }\n }\n const listModels = getFeedListElementStorageModels(item)\n if (listModels) {\n for (const [listKey, models] of Object.entries(listModels)) {\n const arr = item[listKey]\n if (!Array.isArray(arr)) continue\n const n = Math.min(models.length, arr.length)\n for (let i = 0; i < n; i++) {\n if (!isFeedRichBodyStorageSchema(models[i]!)) continue\n if (stringFieldContainsDataUriImage(arr[i])) return true\n }\n }\n }\n return false\n}\n\n/**\n * True when primary body keys or excerpt-style keys contain an embedded `data:image/...;base64,...` URI.\n * Used by feed generation to omit oversized RSS entries by default.\n */\nexport function feedItemRichTextContainsDataUriImage(item: Record<string, unknown>): boolean {\n if (feedItemTypedRichFieldsContainDataUriImage(item)) return true\n for (const k of CONTENT_PRIMARY_KEYS) {\n if (stringFieldContainsDataUriImage(item[k])) return true\n }\n for (const k of DESCRIPTION_KEYS) {\n if (stringFieldContainsDataUriImage(item[k])) return true\n }\n return false\n}\n\nexport function filterItemsByRichTextDataUriImagePolicy<T extends Record<string, unknown>>(\n items: readonly T[],\n richTextDataUriImages: 'omit_items' | 'include_items' = 'omit_items',\n): T[] {\n if (richTextDataUriImages === 'include_items') return [...items]\n return items.filter((item) => !feedItemRichTextContainsDataUriImage(item))\n}\n","import { client as seedClient, DEFAULT_ARWEAVE_HOST } from '@seedprotocol/sdk';\nimport { getArweaveUrlForTransaction } from './utils/arweaveUrl';\nimport { getFeedItemsBySchemaName, getFeedItemsBySchemaNameForMonth } from './getFeedItems';\n\nexport { getFeedItemsBySchemaName, getFeedItemsBySchemaNameForMonth } from './getFeedItems';\nexport { loadFeedConfig } from './config';\nexport { parseRssString, type ParsedRssChannel } from './consume/parseRss';\nexport {\n classifyMediaRef,\n resolveMediaRef,\n normalizeFeedItemFields,\n getFeedItemStringField,\n} from '@seedprotocol/sdk';\nexport type {\n FeedFieldManifest,\n FeedFieldDescriptor,\n FeedFieldRole,\n ClassifyMediaRefOptions,\n MediaRefClassification,\n ResolveMediaRefResult,\n ResolveMediaRefOptions,\n NormalizedMediaField,\n NormalizedHtmlField,\n NormalizedTextField,\n NormalizedFeedFieldValue,\n} from '@seedprotocol/sdk';\nimport pluralize from 'pluralize';\nimport type { FeedFormat, GraphQLItem, TransformOptions, FeedConfig, ImageMetadata } from './types';\n\nexport type { TransformOptions } 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';\nimport {\n pickFeedItemContent,\n pickFeedItemDescription,\n filterItemsByRichTextDataUriImagePolicy,\n} from './pickFeedItemRichText';\n\nexport { enrichImageSeedCloneForFeed } from './imageRelationEnrichment';\nexport {\n pickFeedItemContent,\n pickFeedItemDescription,\n feedItemRichTextContainsDataUriImage,\n filterItemsByRichTextDataUriImagePolicy,\n} from './pickFeedItemRichText';\nexport {\n FEED_RICH_BODY_STORAGE_SCHEMAS,\n isFeedRichBodyStorageSchema,\n} from './feedFieldStorageModel';\n\n/** RSS enclosure URL from a plain string (URL or tx id) or nested Image relation object. */\nfunction getEnclosureUrlFromImageRelationField(value: unknown): string | undefined {\n if (value == null) return undefined\n if (typeof value === 'string') {\n if (value.startsWith('http://') || value.startsWith('https://')) return value\n try {\n return getArweaveUrlForTransaction(value)\n } catch {\n return undefined\n }\n }\n if (typeof value === 'object' && !Array.isArray(value)) {\n const o = value as Record<string, unknown>\n const direct = o.arweaveUrl\n if (typeof direct === 'string' && direct.trim()) return direct.trim()\n const tx = (o.storageTransactionId ?? o.storage_transaction_id) as string | undefined\n if (tx && typeof tx === 'string' && tx.trim()) {\n try {\n return getArweaveUrlForTransaction(tx.trim())\n } catch {\n return undefined\n }\n }\n }\n return undefined\n}\n\ninterface RssImageCandidate {\n url: string\n mimeType?: string\n width?: number\n height?: number\n size?: number\n format?: string\n}\n\nfunction coercePositiveNumber(value: unknown): number | undefined {\n if (typeof value === 'number' && Number.isFinite(value) && value > 0) return value\n if (typeof value === 'string') {\n const parsed = Number(value)\n if (Number.isFinite(parsed) && parsed > 0) return parsed\n }\n return undefined\n}\n\nfunction extractRssImageCandidateFromRelationFieldValue(\n value: unknown\n): RssImageCandidate | undefined {\n if (Array.isArray(value)) {\n for (const entry of value) {\n const candidate = extractRssImageCandidateFromRelationFieldValue(entry)\n if (candidate) return candidate\n }\n return undefined\n }\n\n const url = getEnclosureUrlFromImageRelationField(value)\n if (!url) return undefined\n\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n const record = value as Record<string, unknown>\n const mimeType =\n (typeof record.mimeType === 'string' && record.mimeType) ||\n (typeof record.type === 'string' && record.type) ||\n (typeof record.contentType === 'string' && record.contentType) ||\n undefined\n return {\n url,\n mimeType,\n width: coercePositiveNumber(record.width),\n height: coercePositiveNumber(record.height),\n size: coercePositiveNumber(record.size ?? record.fileSize),\n format: typeof record.format === 'string' ? record.format : undefined,\n }\n }\n\n return { url }\n}\n\nfunction extractRssImageCandidate(item: Record<string, unknown>): RssImageCandidate | undefined {\n if (item._hasImage && item._imageMetadata && typeof item._imageMetadata === 'object') {\n const imageMeta = item._imageMetadata as ImageMetadata\n if (imageMeta.url) {\n return {\n url: imageMeta.url,\n mimeType: imageMeta.mimeType,\n width: imageMeta.width,\n height: imageMeta.height,\n size: imageMeta.size,\n format: imageMeta.format,\n }\n }\n }\n\n const imageCandidateFields = [\n 'feature_image',\n 'featureImage',\n 'image',\n 'images',\n 'cover_image',\n 'coverImage',\n 'thumbnail',\n 'thumbnail_image',\n 'thumbnailImage',\n ]\n\n for (const key of imageCandidateFields) {\n if (!(key in item)) continue\n const candidate = extractRssImageCandidateFromRelationFieldValue(item[key])\n if (candidate) return candidate\n }\n\n return undefined\n}\n\nfunction applyRssImageCandidate(\n rssItem: Record<string, unknown>,\n item: Record<string, unknown>,\n imageCandidate: RssImageCandidate,\n schemaName: string,\n siteAuthor: FeedConfig['author'],\n siteCopyright: FeedConfig['copyright'],\n): void {\n const mediaType = imageCandidate.mimeType || 'image/jpeg'\n\n rssItem.enclosures = [\n {\n url: imageCandidate.url,\n type: mediaType,\n ...(imageCandidate.size ? { length: imageCandidate.size } : {}),\n },\n ]\n\n const mediaContent: Record<string, unknown> = {\n url: imageCandidate.url,\n type: mediaType,\n medium: 'image',\n }\n\n if (imageCandidate.width) mediaContent.width = imageCandidate.width\n if (imageCandidate.height) mediaContent.height = imageCandidate.height\n if (imageCandidate.size) mediaContent.fileSize = imageCandidate.size\n if (imageCandidate.format) mediaContent.format = imageCandidate.format\n rssItem['media:content'] = [mediaContent]\n\n const thumbnail: Record<string, unknown> = { url: imageCandidate.url }\n if (imageCandidate.width) thumbnail.width = imageCandidate.width\n if (imageCandidate.height) thumbnail.height = imageCandidate.height\n rssItem['media:thumbnail'] = [thumbnail]\n\n if (item.title) {\n rssItem['media:title'] = item.title\n }\n\n const mediaDescription = item.description || item.summary || item.title || ''\n if (mediaDescription) {\n rssItem['media:description'] = mediaDescription\n }\n\n if (item.title) {\n const keywords: string[] = []\n keywords.push(...String(item.title).toLowerCase().split(/\\s+/).filter((w) => w.length > 3))\n if (item.description) {\n keywords.push(...String(item.description).toLowerCase().split(/\\s+/).filter((w) => w.length > 3))\n }\n if (keywords.length > 0) {\n rssItem['media:keywords'] = [...new Set(keywords)].slice(0, 10).join(', ')\n }\n }\n\n if (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 (siteAuthor) {\n rssItem['media:credit'] = [{ value: siteAuthor.name, role: 'author' }]\n }\n\n if (siteCopyright) {\n rssItem['media:copyright'] = siteCopyright\n }\n\n if (schemaName) {\n rssItem['media:category'] = [\n {\n value: schemaName,\n scheme: 'http://www.schema.org/',\n },\n ]\n }\n\n rssItem['media:group'] = [\n {\n 'media:content': [mediaContent],\n 'media:thumbnail': [thumbnail],\n 'media:title': item.title || 'Untitled',\n 'media:description': mediaDescription,\n },\n ]\n\n if (\n imageCandidate.url &&\n typeof item.content === 'string' &&\n item.content.trim() !== '' &&\n !item.content.includes(imageCandidate.url)\n ) {\n const imageHtml = `<img src=\"${imageCandidate.url}\" alt=\"${item.title || ''}\"${imageCandidate.width ? ` width=\"${imageCandidate.width}\"` : ''}${imageCandidate.height ? ` height=\"${imageCandidate.height}\"` : ''} />`\n rssItem['content:encoded'] = `${imageHtml}\\n${item.content}`\n }\n}\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, richTextDataUriImages = 'omit_items' } = options\n\n const filtered = filterItemsByRichTextDataUriImagePolicy(\n items as Record<string, unknown>[],\n richTextDataUriImages,\n ) as GraphQLItem[]\n\n return filtered.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 const description = pickFeedItemDescription(item)\n const content = pickFeedItemContent(item)\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,\n content,\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 if (typeof feedItem.html === 'string' && feedItem.Html === undefined) {\n feedItem.Html = feedItem.html\n }\n if (typeof feedItem.Html === 'string' && feedItem.html === undefined) {\n feedItem.html = feedItem.Html\n }\n if (typeof feedItem.body === 'string' && feedItem.Body === undefined) {\n feedItem.Body = feedItem.body\n }\n if (typeof feedItem.Body === 'string' && feedItem.body === undefined) {\n feedItem.body = feedItem.Body\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 interface FeedPaginationOptions {\n page: number\n pageSize: number\n hasNext: boolean\n baseUrl: string\n}\n\nexport interface FeedArchiveLink {\n rel: string\n href: string\n}\n\nexport const createFeed = (\n items: GraphQLItem[],\n schemaName: string,\n format: FeedFormat,\n cacheBust?: string,\n pagination?: FeedPaginationOptions,\n archiveLinks?: FeedArchiveLink[],\n isArchive?: boolean,\n transformOverrides?: Partial<TransformOptions>,\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 // Build RFC 5005 links from pagination or archiveLinks\n const links: Array<{ rel: string; href: string; type?: string }> = []\n if (pagination) {\n const base = pagination.baseUrl.replace(/\\?.*$/, '')\n const sep = base.includes('?') ? '&' : '?'\n if (pagination.hasNext) {\n links.push({ rel: 'next', href: `${base}${sep}page=${pagination.page + 1}` })\n }\n if (pagination.page > 1) {\n links.push({ rel: 'previous', href: `${base}${sep}page=${pagination.page - 1}` })\n links.push({ rel: 'first', href: `${base}${sep}page=1` })\n }\n }\n if (archiveLinks?.length) {\n for (const al of archiveLinks) {\n links.push({ rel: al.rel, href: al.href })\n }\n }\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 richTextDataUriImages:\n transformOverrides?.richTextDataUriImages ?? feedConfig.richTextDataUriImages,\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 atomLinks: Array<{ href: string; rel?: string; type?: string }> = [\n { href: feedUrl, rel: 'self' },\n { href: SITE_CONFIG.siteUrl },\n ]\n for (const link of links) {\n atomLinks.push({\n href: link.href,\n rel: link.rel,\n type: link.type,\n })\n }\n const atomFeed = {\n id: feedUrl,\n title: feedTitle,\n updated: now,\n links: atomLinks,\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 nextUrl = pagination?.hasNext\n ? `${pagination.baseUrl.replace(/\\?.*$/, '')}?page=${pagination.page + 1}`\n : undefined\n const jsonFeed = {\n title: feedTitle,\n home_page_url: SITE_CONFIG.siteUrl,\n feed_url: feedUrl,\n ...(nextUrl && { next_url: nextUrl }),\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 links: links.length ? links.map((l) => ({ rel: l.rel, href: l.href, type: l.type })) : undefined,\n isArchive: isArchive ?? false,\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 const imageCandidate = extractRssImageCandidate(item)\n if (imageCandidate) {\n applyRssImageCandidate(\n rssItem,\n item,\n imageCandidate,\n schemaName,\n SITE_CONFIG.author,\n SITE_CONFIG.copyright\n )\n }\n\n if (\n typeof item.content === 'string' &&\n item.content.trim() !== '' &&\n rssItem['content:encoded'] === undefined\n ) {\n rssItem['content:encoded'] = item.content\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 page?: number\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 const feedConfig = loadFeedConfig()\n const pageNum = Math.max(1, page ?? 1)\n const pageSize = feedConfig.pageSize\n const skip = (pageNum - 1) * pageSize\n\n const contentKeyOptions =\n pageNum > 1 ? { page: pageNum } : undefined\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, contentKeyOptions);\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 const fetchItems = async (): Promise<GraphQLItem[]> => {\n const items = await getFeedItemsBySchemaName(schemaName, { limit: pageSize, skip }) as GraphQLItem[];\n if (config.imageMetadata?.enabled) {\n const imageService = new ArweaveImageService({\n gateways: config.imageMetadata.gateways,\n timeout: config.imageMetadata.timeout,\n });\n return enrichFeedItemsWithMedia(items, imageService, cache);\n }\n return items;\n };\n\n const feedItems = config.enabled && pageNum === 1\n ? await cache.withRefreshLock(schemaName, async () => {\n const cachedData = await cache.getFeedData(schemaName);\n let items: GraphQLItem[];\n\n if (cachedData) {\n console.log(`Incremental fetch: last processed timestamp: ${cachedData.lastProcessedTimestamp}`);\n const allItems = await getFeedItemsBySchemaName(schemaName, { limit: pageSize, skip: 0 }) as GraphQLItem[];\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 items = cache.mergeItems(cachedData.items, newItems).slice(0, pageSize);\n } else {\n items = cachedData.items;\n }\n } else {\n console.log(`Cold cache - fetching page 1`);\n const client = await getClient();\n if (client) console.log(`Client initialized: ${client.isInitialized()}`);\n items = await getFeedItemsBySchemaName(schemaName, { limit: pageSize, skip: 0 }) as GraphQLItem[];\n console.log(`Found ${items.length} feed items for schema ${schemaName}`);\n }\n\n if (config.imageMetadata?.enabled) {\n const imageService = new ArweaveImageService({\n gateways: config.imageMetadata.gateways,\n timeout: config.imageMetadata.timeout,\n });\n const enrichedItems = await enrichFeedItemsWithMedia(items, imageService, cache);\n await cache.setFeedData(schemaName, items);\n return enrichedItems;\n }\n await cache.setFeedData(schemaName, items);\n return items;\n })\n : await fetchItems();\n\n const baseUrl = `${SITE_CONFIG.feedUrl}/${collectionName}/${format}`;\n const hasNext = feedItems.length === pageSize;\n const archiveLinksForMain: FeedArchiveLink[] = [];\n if (pageNum === 1) {\n const now = new Date();\n const prevMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);\n archiveLinksForMain.push({\n rel: 'prev-archive',\n href: `${SITE_CONFIG.feedUrl}/${collectionName}/archive/${prevMonth.getFullYear()}/${prevMonth.getMonth() + 1}/${format}`,\n });\n }\n\n const paginationOptions: FeedPaginationOptions | undefined = {\n page: pageNum,\n pageSize,\n hasNext,\n baseUrl,\n };\n\n const feedContent = await createFeed(\n feedItems,\n schemaName,\n format,\n cacheBust,\n paginationOptions,\n archiveLinksForMain.length ? archiveLinksForMain : undefined,\n false\n );\n const contentType = getContentType(format);\n\n // Cache the generated feed content\n if (config.enabled) {\n await cache.setFeedContent(schemaName, format, feedContent, contentType, contentKeyOptions);\n \n // Get the cached content to retrieve ETag\n const cachedContent = await cache.getFeedContent(schemaName, format, contentKeyOptions);\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, contentKeyOptions);\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}\n\n/**\n * Route handler for monthly archive feeds (RFC 5005).\n *\n * URL Pattern: /:collection/archive/:year/:month/:format\n * Examples:\n * - /posts/archive/2024/02/rss → RSS feed of posts from February 2024\n */\nexport async function handleArchiveFeedRequest(\n collectionSegment: string,\n year: number,\n month: number,\n formatSegment: string,\n ifNoneMatch?: string | null,\n cacheBust?: string\n): Promise<Response> {\n if (year < 1970 || year > 2100) {\n return new Response(\n JSON.stringify({ error: `Invalid year: ${year}` }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n if (month < 1 || month > 12) {\n return new Response(\n JSON.stringify({ error: `Invalid month: ${month}` }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const format = parseFormat(formatSegment);\n if (!format) {\n return new Response(\n JSON.stringify({ error: `Invalid feed format: ${formatSegment}` }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const schemaName = pluralize.singular(collectionSegment.toLowerCase());\n const collectionName = pluralize(schemaName);\n\n const contentKeyOptions = { archive: { year, month } };\n const cache = getCacheManager();\n const config = loadCacheConfig();\n\n try {\n if (config.enabled) {\n const cachedContent = await cache.getFeedContent(schemaName, format, contentKeyOptions);\n if (cachedContent) {\n if (ifNoneMatch && checkIfNoneMatch(ifNoneMatch, cachedContent.etag)) {\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=86400, s-maxage=86400, must-revalidate',\n },\n });\n }\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=86400, s-maxage=86400, must-revalidate',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n 'X-Cache': 'HIT',\n },\n });\n }\n }\n\n const items = await getFeedItemsBySchemaNameForMonth(schemaName, year, month) as GraphQLItem[];\n\n const baseUrl = `${SITE_CONFIG.feedUrl}/${collectionName}/${format}`;\n const archiveBase = `${SITE_CONFIG.feedUrl}/${collectionName}/archive`;\n\n const archiveLinks: FeedArchiveLink[] = [\n { rel: 'prev-archive', href: `${archiveBase}/${month === 1 ? year - 1 : year}/${month === 1 ? 12 : month - 1}/${format}` },\n { rel: 'current', href: baseUrl },\n ];\n\n const nextYear = month === 12 ? year + 1 : year;\n const nextMonth = month === 12 ? 1 : month + 1;\n const now = new Date();\n if (nextYear < now.getFullYear() || (nextYear === now.getFullYear() && nextMonth <= now.getMonth() + 1)) {\n archiveLinks.push({ rel: 'next-archive', href: `${archiveBase}/${nextYear}/${nextMonth}/${format}` });\n }\n\n const feedContent = await createFeed(\n items,\n schemaName,\n format,\n cacheBust,\n undefined,\n archiveLinks,\n true\n );\n\n const contentType = getContentType(format);\n\n if (config.enabled) {\n await cache.setFeedContent(schemaName, format, feedContent, contentType, contentKeyOptions);\n const cachedContent = await cache.getFeedContent(schemaName, format, contentKeyOptions);\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=86400, s-maxage=86400, must-revalidate',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n 'X-Cache': 'MISS',\n },\n });\n }\n\n return new Response(feedContent, {\n status: 200,\n headers: {\n 'Content-Type': contentType,\n 'Cache-Control': 'public, max-age=86400, s-maxage=86400',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n },\n });\n } catch (error) {\n console.error('Archive feed generation error:', error);\n return new Response(\n JSON.stringify({\n error: 'Failed to generate archive feed',\n message: error instanceof Error ? error.message : 'Unknown error',\n }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n );\n }\n}"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/bootstrap.ts","../src/utils/arweaveUrl.ts","../src/config.ts","../src/listRelationKey.ts","../src/imageRelationEnrichment.ts","../src/feedFieldStorageModel.ts","../src/hydrateArweaveRichText.ts","../src/parseEasPropertyMetadataForFeed.ts","../src/getFeedItems.ts","../src/consume/parseRss.ts","../src/rss/generateRssXml.ts","../src/cache/types.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/pickFeedItemRichText.ts","../src/index.ts"],"sourcesContent":["import { DEFAULT_ARWEAVE_HOST, resolveArweaveHostFromEnv } from '@seedprotocol/arweave'\n\nlet initialized = false\n\nexport type InitializeFeedPlatformOptions = {\n arweaveDomain?: string\n}\n\nfunction resolveFeedArweaveDomain(explicit?: string): string {\n return explicit ?? resolveArweaveHostFromEnv() ?? DEFAULT_ARWEAVE_HOST\n}\n\n/** Register Node EAS + Arweave clients for feed generation (no full SDK client). */\nexport async function initializeFeedPlatform(\n options?: InitializeFeedPlatformOptions,\n): Promise<void> {\n if (initialized) return\n const [{ registerNodeEasPlatform }, { registerNodeArweavePlatform }] = await Promise.all([\n import('@seedprotocol/eas/node'),\n import('@seedprotocol/arweave/node'),\n ])\n registerNodeEasPlatform()\n const arweaveDomain = resolveFeedArweaveDomain(options?.arweaveDomain)\n registerNodeArweavePlatform({ arweaveDomain })\n if (options?.arweaveDomain) {\n const { BaseArweaveClient } = await import('@seedprotocol/arweave')\n BaseArweaveClient.setHost(options.arweaveDomain)\n }\n initialized = true\n}\n\n/** @deprecated Use initializeFeedPlatform */\nexport const initializeSeedClient = initializeFeedPlatform\n\nexport async function getClient(): Promise<{ isInitialized: () => boolean }> {\n await initializeFeedPlatform()\n return { isInitialized: () => initialized }\n}\n\nexport async function teardownSeedClient(): Promise<void> {\n initialized = false\n}\n\nexport { DEFAULT_ARWEAVE_HOST } from '@seedprotocol/arweave'\n","import { BaseArweaveClient } from '@seedprotocol/arweave'\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","import type { FeedConfig } from './types';\n\n/**\n * Partial site/feed metadata overrides. Unspecified fields keep current/default values.\n * `author` fields are merged individually when provided.\n */\nexport type SiteConfigOverrides = Partial<Omit<FeedConfig, 'author'>> & {\n author?: Partial<NonNullable<FeedConfig['author']>>;\n};\n\n/** Hardcoded Seed Protocol defaults for feed channel metadata. */\nexport const DEFAULT_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\nfunction cloneSiteConfig(config: FeedConfig): FeedConfig {\n return {\n ...config,\n author: config.author ? { ...config.author } : undefined,\n };\n}\n\nlet siteConfig: FeedConfig = cloneSiteConfig(DEFAULT_SITE_CONFIG);\n\n/**\n * Merge overrides onto a base site config. Missing fields keep the base values.\n */\nexport function resolveSiteConfig(\n overrides?: SiteConfigOverrides,\n base: FeedConfig = siteConfig,\n): FeedConfig {\n if (!overrides) {\n return cloneSiteConfig(base);\n }\n\n const resolved: FeedConfig = {\n ...base,\n ...overrides,\n author: base.author ? { ...base.author } : undefined,\n };\n\n if (overrides.author !== undefined) {\n resolved.author = {\n ...(resolved.author ?? { name: '' }),\n ...overrides.author,\n name: overrides.author.name ?? resolved.author?.name ?? '',\n };\n }\n\n return resolved;\n}\n\n/** Current site/feed channel config (defaults until `setSiteConfig` is called). */\nexport function getSiteConfig(): FeedConfig {\n return cloneSiteConfig(siteConfig);\n}\n\n/**\n * Override site/feed channel metadata for this process.\n * Partial values are merged onto the Seed Protocol defaults (not the previous override set).\n */\nexport function setSiteConfig(overrides: SiteConfigOverrides): FeedConfig {\n siteConfig = resolveSiteConfig(overrides, DEFAULT_SITE_CONFIG);\n return getSiteConfig();\n}\n\n/** Restore hardcoded Seed Protocol site config defaults. */\nexport function resetSiteConfig(): void {\n siteConfig = cloneSiteConfig(DEFAULT_SITE_CONFIG);\n}\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 * - FEED_EXPAND_RELATIONS: When 'false', relation properties stay as UIDs. Default: true (expand to nested objects).\n * - FEED_PAGE_SIZE: Items per page for paged feeds (default: 25).\n * - FEED_INCLUDE_DATA_URI_HTML_ITEMS: When 'true', include items whose body/html contain embedded data-URI images.\n * Default: false (omit such items to avoid huge RSS payloads).\n */\nexport function loadFeedConfig(): {\n itemUrlBase: string;\n itemUrlPath: string;\n siteUrl: string;\n expandRelations: boolean;\n pageSize: number;\n richTextDataUriImages: 'omit_items' | 'include_items';\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 const expandRelations = process.env.FEED_EXPAND_RELATIONS?.toLowerCase() !== 'false';\n const pageSize = parseInt(process.env.FEED_PAGE_SIZE || '25', 10);\n const richTextDataUriImages =\n process.env.FEED_INCLUDE_DATA_URI_HTML_ITEMS?.toLowerCase() === 'true'\n ? 'include_items'\n : 'omit_items';\n\n return { itemUrlBase, itemUrlPath, siteUrl, expandRelations, pageSize, richTextDataUriImages };\n}\n","import pluralize from 'pluralize'\n\n/** Matches SDK list storage: singular(schemaKey) + PascalCase(ref) + \"Ids\", e.g. authorIdentityIds → authors */\nconst CAMEL_LIST_RELATION_IDS = /^(.+?)([A-Z][a-zA-Z]+)Ids$/\n\n/**\n * Maps list-relation storage / wire names to schema-facing plural keys (e.g. `authorIdentityIds` → `authors`).\n * Non-matching keys are returned unchanged.\n */\nexport function publicListRelationPropertyKey(propertyKey: string): string {\n const camel = CAMEL_LIST_RELATION_IDS.exec(propertyKey)\n if (camel) {\n const singularPart = camel[1]\n if (singularPart) return pluralize(singularPart)\n }\n if (propertyKey.endsWith('_ids')) {\n const base = propertyKey.slice(0, -4)\n const segments = base.split('_').filter(Boolean)\n if (segments.length >= 2) {\n const singularProperty = segments[0]!\n return singularProperty.endsWith('s') ? singularProperty : pluralize(singularProperty)\n }\n return pluralize(base)\n }\n return propertyKey\n}\n\n/**\n * Removes keys that are storage aliases for the same list relation as `publicKey`\n * (e.g. delete `authorIdentityIds` when `authors` is the canonical field).\n */\nexport function stripListRelationStorageAliasesForPublicKey(\n item: Record<string, unknown>,\n publicKey: string\n): void {\n for (const k of Object.keys(item)) {\n if (k === publicKey) continue\n if (publicListRelationPropertyKey(k) === publicKey) {\n delete item[k]\n }\n }\n}\n\n/** If value is a JSON array string from legacy feed assembly, parse to string[]. */\nexport function tryCoerceJsonStringArray(value: unknown): unknown {\n if (Array.isArray(value)) return value\n if (typeof value !== 'string') return value\n const s = value.trim()\n if (!s.startsWith('[')) return value\n try {\n const parsed = JSON.parse(s) as unknown\n if (Array.isArray(parsed)) return parsed\n } catch {\n // keep original\n }\n return value\n}\n","import { getArweaveUrlForTransaction } from './utils/arweaveUrl'\n\n/**\n * Adds `arweaveUrl` to an assembled Image seed clone when `storageTransactionId`\n * (or snake_case) is present, so RSS/JSON feeds can expose both tx id and gateway URL.\n */\nexport function enrichImageSeedCloneForFeed(clone: Record<string, unknown>): void {\n const txId = (clone.storageTransactionId ?? clone.storage_transaction_id) as string | undefined\n if (txId && typeof txId === 'string' && txId.trim()) {\n try {\n clone.arweaveUrl = getArweaveUrlForTransaction(txId.trim())\n } catch {\n // keep clone without arweaveUrl\n }\n }\n}\n","/** Related Seed schema names (lowercased) used as primary RSS/Atom/JSON body sources. */\nexport const FEED_RICH_BODY_STORAGE_SCHEMAS = new Set(['html', 'file', 'json'])\n\nconst FIELD_MODELS_KEY = '_feedFieldStorageModels' as const\nconst LIST_MODELS_KEY = '_feedListElementStorageModels' as const\n\nexport type FeedFieldStorageModels = Record<string, string>\nexport type FeedListElementStorageModels = Record<string, string[]>\n\nfunction normalizeModelName(schemaName: string): string {\n return schemaName.trim().toLowerCase()\n}\n\nexport function isFeedRichBodyStorageSchema(schemaName: string): boolean {\n return FEED_RICH_BODY_STORAGE_SCHEMAS.has(normalizeModelName(schemaName))\n}\n\n/** html < file < json for tie-breaking among rich body fields. */\nexport function feedRichBodyModelPriority(schemaName: string): number {\n const m = normalizeModelName(schemaName)\n if (m === 'html') return 0\n if (m === 'file') return 1\n if (m === 'json') return 2\n return 99\n}\n\nexport function getFeedFieldStorageModels(\n item: Record<string, unknown>,\n): FeedFieldStorageModels | undefined {\n const raw = item[FIELD_MODELS_KEY]\n if (raw !== null && typeof raw === 'object' && !Array.isArray(raw)) {\n return raw as FeedFieldStorageModels\n }\n return undefined\n}\n\nexport function getFeedListElementStorageModels(\n item: Record<string, unknown>,\n): FeedListElementStorageModels | undefined {\n const raw = item[LIST_MODELS_KEY]\n if (raw !== null && typeof raw === 'object' && !Array.isArray(raw)) {\n return raw as FeedListElementStorageModels\n }\n return undefined\n}\n\nexport function setFeedFieldStorageModel(\n item: Record<string, unknown>,\n fieldKey: string,\n schemaName: string,\n): void {\n const normalized = normalizeModelName(schemaName)\n let map = getFeedFieldStorageModels(item)\n if (!map) {\n map = {}\n item[FIELD_MODELS_KEY] = map\n }\n map[fieldKey] = normalized\n}\n\nexport function setFeedListElementStorageModels(\n item: Record<string, unknown>,\n outputKey: string,\n models: string[],\n): void {\n let map = getFeedListElementStorageModels(item)\n if (!map) {\n map = {}\n item[LIST_MODELS_KEY] = map\n }\n map[outputKey] = models.map(normalizeModelName)\n}\n","import { BaseArweaveClient, isKnownArweaveGatewayHostname } from '@seedprotocol/arweave'\nimport {\n getFeedFieldStorageModels,\n getFeedListElementStorageModels,\n isFeedRichBodyStorageSchema,\n} from './feedFieldStorageModel'\n\n/** Same logical fields as legacy `pickFeedItemRichText` primary keys — inline HTML for feeds. */\nconst RICH_TEXT_KEYS = ['html', 'Html', 'body', 'Body', 'content', 'Content'] as const\n\n/** Max UTF-8 bytes for a single rich-body fetch (HTML/JSON on Arweave can exceed 2MB with embeds). */\nconst MAX_BODY_BYTES = 8_000_000\n\n/**\n * True for a single-path gateway URL whose path looks like an Arweave transaction id\n * (e.g. `https://arweave.net/<43-char tx id>`), matching {@link getArweaveUrlForTransaction}.\n */\nexport function isArweaveTransactionGatewayUrl(raw: string): boolean {\n const s = raw.trim()\n if (!s.startsWith('http://') && !s.startsWith('https://')) return false\n try {\n const u = new URL(s)\n const host = u.hostname.toLowerCase()\n const expected = BaseArweaveClient.getHost().toLowerCase()\n if (host !== expected && !isKnownArweaveGatewayHostname(host)) return false\n const segments = u.pathname.replace(/^\\//, '').split('/').filter(Boolean)\n if (segments.length !== 1) return false\n const id = segments[0]!\n return /^[A-Za-z0-9_-]{43}$/.test(id)\n } catch {\n return false\n }\n}\n\nasync function fetchGatewayPayloadAsUtf8(url: string): Promise<string | null> {\n try {\n const res = await fetch(url, {\n headers: { Accept: 'text/html, text/plain, text/markdown, application/json, */*' },\n signal: AbortSignal.timeout(15_000),\n })\n if (!res.ok) return null\n const ct = res.headers.get('content-type') ?? ''\n if (/^(image|video|audio)\\//i.test(ct)) return null\n const buf = await res.arrayBuffer()\n if (buf.byteLength > MAX_BODY_BYTES) return null\n const text = new TextDecoder('utf-8', { fatal: false }).decode(buf)\n if (text.length === 0 || text.includes('\\0')) return null\n return text\n } catch {\n return null\n }\n}\n\nasync function hydrateStringFieldIfGateway(\n item: Record<string, unknown>,\n key: string\n): Promise<void> {\n const v = item[key]\n if (typeof v !== 'string' || v.trim() === '') return\n if (!isArweaveTransactionGatewayUrl(v)) return\n const text = await fetchGatewayPayloadAsUtf8(v)\n if (text === null) return\n item[key] = text\n}\n\n/**\n * After {@link resolveRelationPropertiesToUrls}, Html/File relation fields may be gateway URLs.\n * For RSS/Atom `content:encoded`, replace those with the fetched UTF-8 body (typically HTML).\n */\nexport async function hydrateArweaveRichTextInFeedItems(\n items: Record<string, unknown>[]\n): Promise<void> {\n for (const item of items) {\n const keysToHydrate = new Set<string>([...RICH_TEXT_KEYS])\n const fieldModels = getFeedFieldStorageModels(item)\n if (fieldModels) {\n for (const [k, m] of Object.entries(fieldModels)) {\n if (isFeedRichBodyStorageSchema(m)) keysToHydrate.add(k)\n }\n }\n for (const key of keysToHydrate) {\n await hydrateStringFieldIfGateway(item, key)\n }\n\n const listModels = getFeedListElementStorageModels(item)\n if (listModels) {\n for (const [listKey, models] of Object.entries(listModels)) {\n const arr = item[listKey]\n if (!Array.isArray(arr)) continue\n const n = Math.min(models.length, arr.length)\n for (let i = 0; i < n; i++) {\n if (!isFeedRichBodyStorageSchema(models[i]!)) continue\n const el = arr[i]\n if (typeof el !== 'string' || !isArweaveTransactionGatewayUrl(el)) continue\n const text = await fetchGatewayPayloadAsUtf8(el)\n if (text === null) continue\n arr[i] = text\n }\n }\n }\n }\n}\n","export type EasPropertyMetadataForFeed = {\n name: string;\n value: string | string[];\n type?: string;\n};\n\nexport type ParseEasPropertyMetadataForFeedResult =\n | { ok: true; metadata: EasPropertyMetadataForFeed }\n | { ok: false; reason: 'empty' }\n | { ok: false; reason: 'shape' }\n | { ok: false; reason: 'parse'; error: unknown };\n\n/**\n * Parse EAS property `decodedDataJson` for feed assembly.\n * Matches SDK guards in syncDbWithEas / saveDataToDb (empty trim, parse, array + [0].value).\n */\nexport function parseEasPropertyMetadataForFeed(\n decodedDataJson: string | undefined | null,\n): ParseEasPropertyMetadataForFeedResult {\n const raw =\n typeof decodedDataJson === 'string' ? decodedDataJson.trim() : '';\n if (!raw) {\n return { ok: false, reason: 'empty' };\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (e) {\n return { ok: false, reason: 'parse', error: e };\n }\n\n if (!Array.isArray(parsed) || parsed.length === 0 || !parsed[0]?.value) {\n return { ok: false, reason: 'shape' };\n }\n\n const metadata = parsed[0].value as EasPropertyMetadataForFeed;\n return { ok: true, metadata };\n}\n","import {\n getSeedsBySchemaName,\n getItemVersionsFromEas,\n getItemPropertiesFromEas,\n EasClient,\n setSchemaUidForSchemaDefinition,\n withExcludeRevokedFilter,\n pickLatestPropertyAttestationsByRefAndSchema,\n} from '@seedprotocol/eas';\nimport { initializeFeedPlatform } from './bootstrap';\nimport { getArweaveUrlForTransaction } from './utils/arweaveUrl';\nimport { gql } from 'graphql-request';\nimport { loadFeedConfig } from './config';\nimport {\n publicListRelationPropertyKey,\n stripListRelationStorageAliasesForPublicKey,\n tryCoerceJsonStringArray,\n} from './listRelationKey';\nimport { enrichImageSeedCloneForFeed } from './imageRelationEnrichment';\nimport { hydrateArweaveRichTextInFeedItems } from './hydrateArweaveRichText';\nimport { parseEasPropertyMetadataForFeed } from './parseEasPropertyMetadataForFeed';\nimport {\n setFeedFieldStorageModel,\n setFeedListElementStorageModels,\n} from './feedFieldStorageModel';\n\nconst IMAGE_SCHEMA = 'image';\n\nconst relationValuesToExclude = [\n '0x0000000000000000000000000000000000000000000000000000000000000020',\n];\n\nconst GET_SEEDS = gql`\n query GetSeeds($where: AttestationWhereInput!, $take: Int, $skip: Int) {\n itemSeeds: attestations(where: $where, orderBy: [{ timeCreated: desc }], take: $take, skip: $skip) {\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\nfunction resetProcessingMaps(): void {\n seedUidToModelType.clear();\n relatedSeedUids.clear();\n versionUidToSeedUid.clear();\n assembledFeedItems.clear();\n versionsBySeedUid.clear();\n latestVersionUidsBySeedUid.clear();\n}\n\nconst processItemProperty = async (\n property: AttestationLike,\n itemSeeds: AttestationLike[]\n): Promise<void> => {\n const parsed = parseEasPropertyMetadataForFeed(property.decodedDataJson);\n if (!parsed.ok) {\n const { id, refUID, schemaId } = property;\n if (parsed.reason === 'empty') {\n console.warn(\n '[feed] [processItemProperty] empty decodedDataJson for property:',\n id,\n refUID,\n schemaId,\n );\n } else if (parsed.reason === 'parse') {\n console.warn(\n '[feed] [processItemProperty] failed to parse decodedDataJson for property:',\n id,\n refUID,\n schemaId,\n parsed.error,\n );\n } else {\n console.warn(\n '[feed] [processItemProperty] invalid decodedDataJson structure for property:',\n id,\n refUID,\n schemaId,\n );\n }\n return;\n }\n\n const metadata = parsed.metadata;\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: string | string[] = metadata.value as string | string[];\n if (isRelation && isList && Array.isArray(propertyValue)) {\n propertyValue = propertyValue.map((v) => String(v));\n } else 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 rawProperties = await getItemPropertiesFromEas({ versionUids: latestVersionUids });\n const itemProperties = pickLatestPropertyAttestationsByRefAndSchema(rawProperties);\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. Image schema relations are left as UIDs so\n * expandRelationProperties can emit nested seeds with arweaveUrl.\n * Supports optional _id/_ids key 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 let value = item[key];\n const coerced = tryCoerceJsonStringArray(value);\n if (coerced !== value && Array.isArray(coerced)) {\n item[key] = coerced;\n value = coerced;\n }\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 const models: 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 models.push('unknown');\n continue;\n }\n const modelForUid = seedUidToModelType.get(uid) ?? 'unknown';\n // Keep Image seed UIDs for expandRelationProperties (nested seed + arweaveUrl).\n if (modelForUid === IMAGE_SCHEMA) {\n urls.push(uid);\n models.push(IMAGE_SCHEMA);\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 models.push(modelForUid);\n resolved = true;\n } catch {\n urls.push(uid);\n models.push(modelForUid);\n }\n } else {\n urls.push(uid);\n models.push(modelForUid);\n }\n }\n if (resolved) {\n const outputKey = publicListRelationPropertyKey(key);\n item[outputKey] = urls;\n setFeedListElementStorageModels(item, outputKey, models);\n stripListRelationStorageAliasesForPublicKey(item, outputKey);\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 // Keep Image seed UIDs for expandRelationProperties (nested seed + arweaveUrl).\n if (seedUidToModelType.get(value) === IMAGE_SCHEMA) {\n continue;\n }\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 const relatedModel = seedUidToModelType.get(value) ?? 'unknown';\n setFeedFieldStorageModel(item, outputKey, relatedModel);\n const camelOut = toCamelCase(outputKey);\n if (camelOut !== outputKey) {\n setFeedFieldStorageModel(item, camelOut, relatedModel);\n }\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\n/**\n * Expands relation properties to nested objects from assembledFeedItems.\n * Image seeds always expand (with arweaveUrl enrichment when storage exists).\n * Other relations skip expansion when the target has storage (already URL-resolved).\n */\nfunction expandRelationProperties(\n schemaName: string,\n options: SetFeedItemDefaultsOptions,\n expandRelations: boolean\n): void {\n if (!expandRelations) return;\n\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 let value = item[key];\n const coerced = tryCoerceJsonStringArray(value);\n if (coerced !== value && Array.isArray(coerced)) {\n item[key] = coerced;\n value = coerced;\n }\n const isList = Array.isArray(value);\n const uids = isList ? (value as unknown[]) : [value];\n\n const expanded: unknown[] = [];\n let didExpand = false;\n\n for (const uid of uids) {\n if (typeof uid !== 'string' || relationValuesToExclude.includes(uid)) {\n expanded.push(uid);\n continue;\n }\n const related = assembledFeedItems.get(uid);\n if (!related) {\n expanded.push(uid);\n continue;\n }\n const txId =\n (related?.storageTransactionId ?? related?.storage_transaction_id) as string | undefined;\n const isImage = seedUidToModelType.get(uid) === IMAGE_SCHEMA;\n\n if (isImage) {\n const relatedSchema = seedUidToModelType.get(uid) ?? 'unknown';\n const clone = { ...related } as Record<string, unknown>;\n setFeedItemDefaults(clone, uid, relatedSchema, options);\n enrichImageSeedCloneForFeed(clone);\n expanded.push(clone);\n didExpand = true;\n continue;\n }\n\n // Non-image: skip expansion when related has storage (URL resolution already ran for non-images)\n if (txId && typeof txId === 'string' && txId.trim()) {\n expanded.push(uid);\n continue;\n }\n const relatedSchema = seedUidToModelType.get(uid) ?? 'unknown';\n const clone = { ...related } as Record<string, unknown>;\n setFeedItemDefaults(clone, uid, relatedSchema, options);\n expanded.push(clone);\n didExpand = true;\n }\n\n if (didExpand) {\n const outputKey = isList\n ? publicListRelationPropertyKey(key)\n : key.endsWith('_id')\n ? key.replace(/_id$/, '')\n : key;\n item[outputKey] = isList ? expanded : expanded[0];\n stripListRelationStorageAliasesForPublicKey(item, outputKey);\n if (outputKey !== key) {\n delete item[key];\n const camelKey = toCamelCase(key);\n if (camelKey !== key) delete item[camelKey];\n }\n }\n }\n }\n}\n\nasync function processSeedsToFeedItems(\n schemaName: string,\n seeds: AttestationLike[],\n expandRelations: boolean\n): Promise<Record<string, unknown>[]> {\n resetProcessingMaps();\n\n await processSeeds(seeds);\n\n const easClient = EasClient.getEasClient();\n const relatedSeedUidsArray = Array.from(relatedSeedUids);\n const { itemSeeds: relatedSeeds } = await easClient.request(GET_SEEDS, {\n where: withExcludeRevokedFilter({\n id: {\n in: relatedSeedUidsArray,\n },\n }),\n take: relatedSeedUidsArray.length || 1,\n skip: 0,\n });\n\n await processSeeds((relatedSeeds ?? []) as AttestationLike[]);\n\n resolveRelationPropertiesToUrls(schemaName);\n\n const feedConfig = loadFeedConfig();\n const setFeedItemDefaultsOptions: SetFeedItemDefaultsOptions = {\n itemUrlBase: feedConfig.itemUrlBase,\n itemUrlPath: feedConfig.itemUrlPath,\n siteUrl: feedConfig.siteUrl,\n };\n\n expandRelationProperties(\n schemaName,\n setFeedItemDefaultsOptions,\n expandRelations\n );\n\n const items = 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 await hydrateArweaveRichTextInFeedItems(items);\n\n return items;\n}\n\nexport const getFeedItemsBySchemaName = async (\n schemaName: string,\n options?: { limit?: number; skip?: number }\n): Promise<Record<string, unknown>[]> => {\n await initializeFeedPlatform();\n const feedConfig = loadFeedConfig();\n const limit = options?.limit ?? 100;\n const skip = options?.skip ?? 0;\n\n const seeds = (await getSeedsBySchemaName(schemaName, limit, skip)) as AttestationLike[];\n const items = await processSeedsToFeedItems(schemaName, seeds, feedConfig.expandRelations !== false);\n return items;\n};\n\nexport const getFeedItemsBySchemaNameForMonth = async (\n schemaName: string,\n year: number,\n month: number\n): Promise<Record<string, unknown>[]> => {\n await initializeFeedPlatform();\n const feedConfig = loadFeedConfig();\n const startDate = new Date(year, month - 1, 1);\n const endDate = new Date(year, month, 0, 23, 59, 59);\n const startTs = Math.floor(startDate.getTime() / 1000);\n const endTs = Math.floor(endDate.getTime() / 1000) + 1;\n\n const where = withExcludeRevokedFilter({\n AND: [\n {\n schema: {\n is: {\n schemaNames: {\n some: {\n name: { equals: schemaName },\n },\n },\n },\n },\n },\n {\n timeCreated: { gte: startTs, lt: endTs },\n },\n ],\n });\n\n const easClient = EasClient.getEasClient();\n const { itemSeeds } = await easClient.request(GET_SEEDS, {\n where,\n take: 1000,\n skip: 0,\n });\n\n const seeds = (itemSeeds ?? []) as AttestationLike[];\n return processSeedsToFeedItems(schemaName, seeds, feedConfig.expandRelations !== false);\n};\n","import Parser from 'rss-parser'\n\nexport type ParsedRssChannel = {\n title?: string\n link?: string\n description?: string\n /** Plain serializable item records (dates become ISO strings). */\n items: Record<string, unknown>[]\n}\n\nfunction toPlainItem(item: unknown): Record<string, unknown> {\n try {\n return JSON.parse(JSON.stringify(item)) as Record<string, unknown>\n } catch {\n return { ...(item as Record<string, unknown>) }\n }\n}\n\n/** Parse RSS or Atom XML into channel metadata and plain item records for `normalizeFeedItemFields`. */\nconst parser = new Parser({\n customFields: {\n item: [\n 'featureImage',\n 'feature_image',\n 'content:encoded',\n 'seedUid',\n 'SeedUid',\n ],\n },\n})\n\nexport async function parseRssString(xml: string): Promise<ParsedRssChannel> {\n const feed = await parser.parseString(xml)\n return {\n title: feed.title,\n link: feed.link,\n description: feed.description,\n items: (feed.items ?? []).map(toPlainItem),\n }\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/'\ntype RelationNamespaceContext = { prefix: string; uri: string }\ntype RelationNamespaceMap = Record<string, RelationNamespaceContext>\nconst RELATION_NAMESPACES: RelationNamespaceMap = {\n publication: {\n prefix: 'publication',\n uri: 'https://seedprotocol.io/ns/publication/1.0',\n },\n}\nconst RELATION_NAMESPACE_URI_BASE = 'https://seedprotocol.io/ns/relations'\nconst RESERVED_XML_PREFIXES = new Set(['xml', 'xmlns', 'dc', 'media', 'content', 'atom', 'fh'])\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 'author',\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 isExpandedRelationObject(value: unknown): value is Record<string, unknown> {\n return (\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n !(value instanceof Date)\n )\n}\n\nconst MAX_EXPANDED_RELATION_DEPTH = 4\nconst NON_RELATION_KEYS = new Set([\n ...RSS_ITEM_ORDER,\n 'guid',\n 'source',\n 'dc',\n 'content',\n 'content:encoded',\n 'items',\n])\n\nfunction normalizeRelationKey(key: string): string {\n return key.endsWith('s') && key.length > 1 ? key.slice(0, -1) : key\n}\n\nfunction sanitizePrefixToken(rawKey: string): string {\n const token = rawKey\n .trim()\n .replace(/[^A-Za-z0-9_.-]+/g, '_')\n .replace(/^[^A-Za-z_]+/, '')\n .replace(/^$/, 'rel')\n return token.toLowerCase()\n}\n\nfunction createUniquePrefix(\n preferredPrefix: string,\n usedPrefixes: Set<string>\n): string {\n if (!usedPrefixes.has(preferredPrefix) && !RESERVED_XML_PREFIXES.has(preferredPrefix)) {\n usedPrefixes.add(preferredPrefix)\n return preferredPrefix\n }\n\n let suffix = 2\n while (true) {\n const candidate = `${preferredPrefix}${suffix}`\n if (!usedPrefixes.has(candidate) && !RESERVED_XML_PREFIXES.has(candidate)) {\n usedPrefixes.add(candidate)\n return candidate\n }\n suffix += 1\n }\n}\n\nfunction isRelationArray(value: unknown): value is Record<string, unknown>[] {\n return (\n Array.isArray(value) &&\n value.length > 0 &&\n value.every((entry) => isExpandedRelationObject(entry))\n )\n}\n\nfunction buildRelationNamespaceMap(items: Record<string, unknown>[]): RelationNamespaceMap {\n const relationNamespaces: RelationNamespaceMap = { ...RELATION_NAMESPACES }\n const usedPrefixes = new Set(\n Object.values(relationNamespaces).map((namespaceContext) => namespaceContext.prefix)\n )\n\n const discoveredRelationKeys = new Set<string>()\n for (const item of items) {\n for (const [key, value] of Object.entries(item)) {\n const isAuthorRelation =\n (key === 'author' && isExpandedRelationObject(value)) ||\n (key === 'authors' && isRelationArray(value))\n if (key.startsWith('_')) continue\n if (NON_RELATION_KEYS.has(key) && !isAuthorRelation) continue\n if (isExpandedRelationObject(value) || isRelationArray(value)) {\n discoveredRelationKeys.add(normalizeRelationKey(key))\n }\n }\n }\n\n const sortedDiscoveredRelationKeys = [...discoveredRelationKeys].sort()\n for (const relationKey of sortedDiscoveredRelationKeys) {\n if (relationNamespaces[relationKey]) continue\n const preferredPrefix = sanitizePrefixToken(relationKey)\n const prefix = createUniquePrefix(preferredPrefix, usedPrefixes)\n relationNamespaces[relationKey] = {\n prefix,\n uri: `${RELATION_NAMESPACE_URI_BASE}/${relationKey}/1.0`,\n }\n }\n\n return relationNamespaces\n}\n\nfunction getRelationNamespaceContext(\n key: string,\n relationNamespaces: RelationNamespaceMap\n): RelationNamespaceContext | undefined {\n if (key in relationNamespaces) return relationNamespaces[key]\n if (key.endsWith('s') && key.length > 1) {\n const singular = key.slice(0, -1)\n if (singular in relationNamespaces) return relationNamespaces[singular]\n }\n return undefined\n}\n\nfunction getNamespacedChildTagName(localName: string, namespaceContext?: RelationNamespaceContext): string {\n if (!namespaceContext) return localName\n return `${namespaceContext.prefix}:${localName}`\n}\n\nfunction serializeExpandedRelation(\n parent: XMLBuilder,\n obj: Record<string, unknown>,\n tagName: string,\n addDcCreator?: boolean,\n depth = 0,\n namespaceContext?: RelationNamespaceContext\n): void {\n const ele = parent.ele(tagName)\n for (const [k, v] of Object.entries(obj)) {\n if (v == null || k.startsWith('_') || k === 'items') continue\n const childTagName = getNamespacedChildTagName(k, namespaceContext)\n if (depth >= MAX_EXPANDED_RELATION_DEPTH) {\n const child = ele.ele(childTagName)\n addTextOrCdata(child, typeof v === 'object' ? JSON.stringify(v) : String(v))\n continue\n }\n if (Array.isArray(v)) {\n const itemTag = k.endsWith('s') && k.length > 1 ? k.slice(0, -1) : k\n const childItemTag = getNamespacedChildTagName(itemTag, namespaceContext)\n for (const entry of v) {\n if (entry === undefined || entry === null) continue\n if (isExpandedRelationObject(entry)) {\n serializeExpandedRelation(ele, entry, childItemTag, false, depth + 1, namespaceContext)\n } else {\n const leaf = ele.ele(childItemTag)\n addTextOrCdata(leaf, String(entry))\n }\n }\n continue\n }\n if (isExpandedRelationObject(v)) {\n serializeExpandedRelation(\n ele,\n v as Record<string, unknown>,\n childTagName,\n false,\n depth + 1,\n namespaceContext\n )\n continue\n }\n const child = ele.ele(childTagName)\n addTextOrCdata(child, String(v))\n }\n if (addDcCreator) {\n const name = obj.name ?? obj.seedUid\n if (name) parent.ele(DC_NS, 'creator').txt(String(name))\n }\n}\n\nfunction serializeItemProperty(\n itemParent: XMLBuilder,\n key: string,\n value: unknown,\n item: Record<string, unknown>,\n relationNamespaces: RelationNamespaceMap\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 === '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 (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 (value instanceof Date) {\n const ele = itemParent.ele(key)\n ele.txt(formatRfc822Date(value))\n return\n }\n\n if (key === 'author' && isExpandedRelationObject(value)) {\n const namespaceContext = getRelationNamespaceContext(key, relationNamespaces)\n serializeExpandedRelation(itemParent, value, 'author', true, 0, namespaceContext)\n return\n }\n\n if (key === 'authors' && Array.isArray(value)) {\n const namespaceContext = getRelationNamespaceContext(key, relationNamespaces)\n for (const a of value) {\n if (a && isExpandedRelationObject(a)) {\n serializeExpandedRelation(itemParent, a, 'author', true, 0, namespaceContext)\n } else {\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 }\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 expanded relation objects (e.g. coAuthors as list)\n if (Array.isArray(value) && value.length > 0 && value.every((v) => isExpandedRelationObject(v))) {\n const tagName = key.endsWith('s') ? key.slice(0, -1) : key\n const namespaceContext = getRelationNamespaceContext(key, relationNamespaces)\n for (const v of value) {\n if (v) serializeExpandedRelation(itemParent, v, tagName, false, 0, namespaceContext)\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 // Generic expanded relation object (e.g. coAuthor, other relation properties)\n if (isExpandedRelationObject(value)) {\n const namespaceContext = getRelationNamespaceContext(key, relationNamespaces)\n serializeExpandedRelation(itemParent, value, key, false, 0, namespaceContext)\n return\n }\n\n const ele = itemParent.ele(key)\n addTextOrCdata(ele, String(value))\n}\n\nconst ATOM_NS = 'http://www.w3.org/2005/Atom'\nconst FH_NS = 'http://purl.org/syndication/history/1.0'\n\nexport interface RssFeedLink {\n rel: string\n href: string\n type?: string\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 /** RFC 5005 pagination/archive links (atom:link in channel) */\n links?: RssFeedLink[]\n /** RFC 5005 archive document marker */\n isArchive?: boolean\n}\n\nexport function generateRssXml(rssFeed: RssFeedInput): string {\n const doc = create({ version: '1.0', encoding: 'UTF-8' })\n const relationNamespaces = buildRelationNamespaceMap(rssFeed.items)\n const relationNamespaceAttrs = Object.values(relationNamespaces).reduce<Record<string, string>>(\n (acc, relationNamespace) => {\n acc[`xmlns:${relationNamespace.prefix}`] = relationNamespace.uri\n return acc\n },\n {}\n )\n const rssAttrs: Record<string, string> = {\n 'xmlns:dc': DC_NS,\n 'xmlns:content': CONTENT_NS,\n 'xmlns:media': MEDIA_NS,\n 'xmlns:atom': ATOM_NS,\n ...relationNamespaceAttrs,\n }\n if (rssFeed.isArchive) {\n rssAttrs['xmlns:fh'] = FH_NS\n }\n const rss = doc.ele('rss', { version: '2.0', ...rssAttrs })\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 if (rssFeed.links?.length) {\n for (const link of rssFeed.links) {\n const attrs: Record<string, string> = { rel: link.rel, href: link.href }\n if (link.type) attrs.type = link.type\n channel.ele(ATOM_NS, 'link', attrs)\n }\n }\n if (rssFeed.isArchive) {\n channel.ele(FH_NS, 'archive')\n }\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, relationNamespaces)\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, relationNamespaces)\n }\n }\n\n return doc.end({ prettyPrint: true })\n}\n","import type { GraphQLItem, ImageMetadata } from '../types';\n\n/**\n * Cached feed data for a specific schema\n */\nexport interface CachedFeedData {\n items: GraphQLItem[]; // Processed items\n lastProcessedTimestamp: number; // Unix timestamp of newest item\n lastProcessedItemId: string; // ID of newest item (for deduplication)\n lastUpdated: number; // When cache was last updated (Unix timestamp)\n etag: string; // ETag for HTTP conditional requests\n}\n\n/**\n * Cached image metadata for an Arweave transaction ID\n */\nexport interface CachedImageMetadata {\n metadata: ImageMetadata\n cachedAt: number // Unix timestamp when cached\n expiresAt: number // Unix timestamp when cache expires\n}\n\n/**\n * Cached feed content for a specific schema and format\n */\nexport interface CachedFeedContent {\n content: string; // Generated XML/JSON feed\n contentType: string; // MIME type\n etag: string; // ETag for this format\n lastModified: number; // Last modification time (Unix timestamp)\n expiresAt: number; // Cache expiration timestamp (Unix timestamp)\n}\n\n/**\n * Image metadata cache configuration\n */\nexport interface ImageMetadataConfig {\n enabled: boolean\n ttl: number // Time to live in seconds (default: 7 days)\n gateways: string[] // Arweave gateway domains\n timeout: number // Request timeout in milliseconds\n}\n\n/**\n * Cache configuration options\n */\nexport interface CacheConfig {\n ttl: number; // Time to live in seconds\n cacheDir: string; // Directory for persistent cache\n enabled: boolean; // Enable/disable caching\n backgroundRefresh: boolean; // Enable background refresh job\n refreshInterval: number; // Background refresh interval in seconds\n imageMetadata?: ImageMetadataConfig // Image metadata cache configuration\n pageTtl?: number; // TTL for page > 1 (default: 300)\n archiveTtl?: number; // TTL for archives (default: 86400)\n}\n\n/**\n * Options for cache content key (pagination or archive)\n */\nexport interface CacheContentKeyOptions {\n page?: number;\n archive?: { year: number; month: number };\n}\n\nexport function buildContentKey(\n schemaName: string,\n format: string,\n options?: CacheContentKeyOptions\n): string {\n if (options?.archive) {\n return `${schemaName}:${format}:archive-${options.archive.year}-${options.archive.month}`;\n }\n if (options?.page != null && options.page > 1) {\n return `${schemaName}:${format}:page-${options.page}`;\n }\n return `${schemaName}:${format}`;\n}\n\n/**\n * Cache statistics for monitoring\n */\nexport interface CacheStats {\n hits: number;\n misses: number;\n refreshes: number;\n errors: number;\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, CacheContentKeyOptions, CachedImageMetadata } from './types';\nimport { buildContentKey } 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(\n schemaName: string,\n format: string,\n contentKeyOptions?: CacheContentKeyOptions\n ): CachedFeedContent | null {\n const key = buildContentKey(schemaName, format, contentKeyOptions);\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 contentKeyOptions?: CacheContentKeyOptions,\n ttlOverride?: number\n ): void {\n const key = buildContentKey(schemaName, format, contentKeyOptions);\n const now = Math.floor(Date.now() / 1000);\n const ttl = ttlOverride ?? this.config.ttl;\n const expiresAt = now + 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, CacheContentKeyOptions, CachedImageMetadata } from './types';\nimport { buildContentKey } 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(\n schemaName: string,\n format: string,\n contentKeyOptions?: CacheContentKeyOptions\n ): string {\n const key = buildContentKey(schemaName, format, contentKeyOptions);\n const filePart = key.replace(/:/g, '-');\n return join(this.cacheDir, `${filePart}.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 contentKeyOptions?: CacheContentKeyOptions\n ): Promise<CachedFeedContent | null> {\n try {\n const filePath = this.getFeedContentPath(schemaName, format, contentKeyOptions);\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, contentKeyOptions);\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 contentKeyOptions?: CacheContentKeyOptions\n ): Promise<void> {\n try {\n await this.ensureCacheDir();\n const filePath = this.getFeedContentPath(schemaName, format, contentKeyOptions);\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(\n schemaName: string,\n format: string,\n contentKeyOptions?: CacheContentKeyOptions\n ): Promise<void> {\n try {\n const filePath = this.getFeedContentPath(schemaName, format, contentKeyOptions);\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 CacheContentKeyOptions,\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 contentKeyOptions?: CacheContentKeyOptions\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, contentKeyOptions);\n if (cached) {\n this.stats.hits++;\n return cached;\n }\n\n // Check file cache\n cached = await this.fileCache.getFeedContent(schemaName, format, contentKeyOptions);\n if (cached) {\n // Restore to memory cache\n this.memoryCache.setFeedContent(\n schemaName,\n format,\n cached.content,\n cached.contentType,\n contentKeyOptions\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 contentKeyOptions?: CacheContentKeyOptions\n ): Promise<void> {\n if (!this.config.enabled) {\n return;\n }\n\n try {\n let ttlOverride: number | undefined;\n if (contentKeyOptions?.archive) {\n ttlOverride = this.config.archiveTtl ?? 86400;\n } else if (contentKeyOptions?.page != null && contentKeyOptions.page > 1) {\n ttlOverride = this.config.pageTtl ?? 300;\n }\n\n // Update memory cache\n this.memoryCache.setFeedContent(\n schemaName,\n format,\n content,\n contentType,\n contentKeyOptions,\n ttlOverride\n );\n\n // Get the cached content to save to file\n const cached = this.memoryCache.getFeedContent(schemaName, format, contentKeyOptions);\n if (cached) {\n await this.fileCache.setFeedContent(schemaName, format, cached, contentKeyOptions);\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 {\n BaseArweaveClient,\n getArweaveReadGatewayHostsForPrimary,\n resolveArweaveHostFromEnv,\n} from '@seedprotocol/arweave'\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 : getArweaveReadGatewayHostsForPrimary(\n resolveArweaveHostFromEnv() ?? BaseArweaveClient.getHost(),\n );\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 const pageTtl = parseInt(process.env.CACHE_PAGE_TTL || '300', 10);\n const archiveTtl = parseInt(process.env.CACHE_ARCHIVE_TTL || '86400', 10);\n\n return {\n ttl,\n cacheDir,\n enabled,\n backgroundRefresh,\n refreshInterval,\n imageMetadata,\n pageTtl,\n archiveTtl,\n };\n}\n","import {\n BaseArweaveClient,\n ensureReadGatewaySelected,\n getArweaveReadGatewayHostsForPrimary,\n getDefaultArweaveReadGatewayHostsOrdered,\n getReadGatewayHostsForConfig,\n getResolvedSeedGatewayEndpoints,\n} from '@seedprotocol/arweave'\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 resolved = getResolvedSeedGatewayEndpoints()\n if (!resolved || resolved.activePath !== 'hyper-sidecar') {\n await ensureReadGatewaySelected().catch(() => {\n /* feed may run without browser client init */\n })\n }\n\n const gatewayHosts = resolved\n ? getReadGatewayHostsForConfig(resolved, getDefaultArweaveReadGatewayHostsOrdered())\n : this.config.gateways?.length\n ? this.config.gateways\n : getArweaveReadGatewayHostsForPrimary(BaseArweaveClient.getHost())\n\n const protocol = resolved?.arweaveProtocol ?? 'https'\n\n // Try each gateway until one succeeds\n for (const gateway of gatewayHosts) {\n try {\n const host = gateway.trim().replace(/\\/$/, '')\n const url = `${protocol}://${host}/${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: `${protocol}://${gatewayHosts[0]?.trim() || BaseArweaveClient.getHost()}/${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","/**\n * Resolves RSS/Atom/JSON feed `description` and `content` from dynamic Seed item shapes.\n * Html-type properties often use `body`; `html` / `content` are alternate sources.\n */\n\nimport {\n feedRichBodyModelPriority,\n getFeedFieldStorageModels,\n getFeedListElementStorageModels,\n isFeedRichBodyStorageSchema,\n} from './feedFieldStorageModel'\n\nfunction firstNonEmptyString(item: Record<string, unknown>, keys: readonly string[]): string {\n for (const k of keys) {\n const v = item[k]\n if (typeof v === 'string' && v.trim() !== '') {\n return v\n }\n }\n return ''\n}\n\n/** Short text fields only (excerpt / summary) — not full HTML body keys. */\nexport const DESCRIPTION_KEYS = ['summary', 'Summary', 'description', 'Description', 'text', 'Text'] as const\n\n/**\n * Primary HTML / rich body keys first, then plain fallbacks (same as legacy `content` chain).\n */\nexport const CONTENT_PRIMARY_KEYS = ['html', 'Html', 'body', 'Body', 'content', 'Content'] as const\n\nconst CONTENT_FALLBACK_KEYS = ['summary', 'Summary', 'description', 'Description', 'text', 'Text'] as const\n\ntype RichBodyCandidate = {\n model: string\n key: string\n index: number\n value: string\n}\n\nfunction compareRichBodyCandidates(a: RichBodyCandidate, b: RichBodyCandidate): number {\n const d = feedRichBodyModelPriority(a.model) - feedRichBodyModelPriority(b.model)\n if (d !== 0) return d\n const k = a.key.localeCompare(b.key)\n if (k !== 0) return k\n return a.index - b.index\n}\n\nfunction pickTypedRichBodyContent(item: Record<string, unknown>): string {\n const fieldModels = getFeedFieldStorageModels(item)\n const listModels = getFeedListElementStorageModels(item)\n let best: RichBodyCandidate | null = null\n\n if (fieldModels) {\n for (const [key, model] of Object.entries(fieldModels)) {\n if (!isFeedRichBodyStorageSchema(model)) continue\n const v = item[key]\n if (typeof v !== 'string' || v.trim() === '') continue\n const cand: RichBodyCandidate = { model, key, index: 0, value: v }\n if (!best || compareRichBodyCandidates(cand, best) < 0) best = cand\n }\n }\n\n if (listModels) {\n for (const [listKey, models] of Object.entries(listModels)) {\n const arr = item[listKey]\n if (!Array.isArray(arr)) continue\n const n = Math.min(models.length, arr.length)\n for (let i = 0; i < n; i++) {\n const model = models[i]!\n if (!isFeedRichBodyStorageSchema(model)) continue\n const el = arr[i]\n if (typeof el !== 'string' || el.trim() === '') continue\n const cand: RichBodyCandidate = { model, key: listKey, index: i, value: el }\n if (!best || compareRichBodyCandidates(cand, best) < 0) best = cand\n }\n }\n }\n\n return best?.value ?? ''\n}\n\nexport function pickFeedItemDescription(item: Record<string, unknown>): string {\n return firstNonEmptyString(item, DESCRIPTION_KEYS)\n}\n\nexport function pickFeedItemContent(item: Record<string, unknown>): string {\n const typed = pickTypedRichBodyContent(item)\n if (typed !== '') return typed\n const primary = firstNonEmptyString(item, CONTENT_PRIMARY_KEYS)\n if (primary !== '') return primary\n return firstNonEmptyString(item, CONTENT_FALLBACK_KEYS)\n}\n\nfunction stringFieldContainsDataUriImage(value: unknown): boolean {\n if (typeof value !== 'string') return false\n return value.includes('data:image/') && value.includes(';base64,')\n}\n\nfunction feedItemTypedRichFieldsContainDataUriImage(item: Record<string, unknown>): boolean {\n const fieldModels = getFeedFieldStorageModels(item)\n if (fieldModels) {\n for (const [key, model] of Object.entries(fieldModels)) {\n if (!isFeedRichBodyStorageSchema(model)) continue\n if (stringFieldContainsDataUriImage(item[key])) return true\n }\n }\n const listModels = getFeedListElementStorageModels(item)\n if (listModels) {\n for (const [listKey, models] of Object.entries(listModels)) {\n const arr = item[listKey]\n if (!Array.isArray(arr)) continue\n const n = Math.min(models.length, arr.length)\n for (let i = 0; i < n; i++) {\n if (!isFeedRichBodyStorageSchema(models[i]!)) continue\n if (stringFieldContainsDataUriImage(arr[i])) return true\n }\n }\n }\n return false\n}\n\n/**\n * True when primary body keys or excerpt-style keys contain an embedded `data:image/...;base64,...` URI.\n * Used by feed generation to omit oversized RSS entries by default.\n */\nexport function feedItemRichTextContainsDataUriImage(item: Record<string, unknown>): boolean {\n if (feedItemTypedRichFieldsContainDataUriImage(item)) return true\n for (const k of CONTENT_PRIMARY_KEYS) {\n if (stringFieldContainsDataUriImage(item[k])) return true\n }\n for (const k of DESCRIPTION_KEYS) {\n if (stringFieldContainsDataUriImage(item[k])) return true\n }\n return false\n}\n\nexport function filterItemsByRichTextDataUriImagePolicy<T extends Record<string, unknown>>(\n items: readonly T[],\n richTextDataUriImages: 'omit_items' | 'include_items' = 'omit_items',\n): T[] {\n if (richTextDataUriImages === 'include_items') return [...items]\n return items.filter((item) => !feedItemRichTextContainsDataUriImage(item))\n}\n","import {\n initializeFeedPlatform,\n initializeSeedClient,\n getClient,\n teardownSeedClient,\n DEFAULT_ARWEAVE_HOST,\n} from './bootstrap';\nimport { getArweaveUrlForTransaction } from './utils/arweaveUrl';\nimport { getFeedItemsBySchemaName, getFeedItemsBySchemaNameForMonth } from './getFeedItems';\n\nexport { getFeedItemsBySchemaName, getFeedItemsBySchemaNameForMonth } from './getFeedItems';\nexport {\n loadFeedConfig,\n DEFAULT_SITE_CONFIG,\n getSiteConfig,\n setSiteConfig,\n resetSiteConfig,\n resolveSiteConfig,\n type SiteConfigOverrides,\n} from './config';\nexport { parseRssString, type ParsedRssChannel } from './consume/parseRss';\nexport type { FeedConfig, FeedFormat, GraphQLItem } from './types';\nexport {\n initializeFeedPlatform,\n initializeSeedClient,\n getClient,\n teardownSeedClient,\n DEFAULT_ARWEAVE_HOST,\n} from './bootstrap';\nexport {\n classifyMediaRef,\n normalizeFeedItemFields,\n getFeedItemStringField,\n} from '@seedprotocol/arweave';\nexport type {\n FeedFieldManifest,\n FeedFieldDescriptor,\n FeedFieldRole,\n ClassifyMediaRefOptions,\n MediaRefClassification,\n ResolveMediaRefResult,\n ResolveMediaRefOptions,\n NormalizedMediaField,\n NormalizedHtmlField,\n NormalizedTextField,\n NormalizedFeedFieldValue,\n} from '@seedprotocol/arweave';\nimport pluralize from 'pluralize';\nimport type { FeedFormat, GraphQLItem, TransformOptions, FeedConfig, ImageMetadata } from './types';\n\nexport type { TransformOptions } from './types';\nimport { generateAtomFeed, generateJsonFeed } from 'feedsmith';\nimport { generateRssXml } from './rss/generateRssXml';\nimport { CacheManager } from './cache/CacheManager';\nimport { loadCacheConfig } from './cache/config';\nimport { loadFeedConfig, resolveSiteConfig, type SiteConfigOverrides } from './config';\nimport { checkIfNoneMatch } from './utils/etag';\nimport { ArweaveImageService } from './services/arweaveImageService';\nimport {\n pickFeedItemContent,\n pickFeedItemDescription,\n filterItemsByRichTextDataUriImagePolicy,\n} from './pickFeedItemRichText';\n\nexport { enrichImageSeedCloneForFeed } from './imageRelationEnrichment';\nexport {\n pickFeedItemContent,\n pickFeedItemDescription,\n feedItemRichTextContainsDataUriImage,\n filterItemsByRichTextDataUriImagePolicy,\n} from './pickFeedItemRichText';\nexport {\n FEED_RICH_BODY_STORAGE_SCHEMAS,\n isFeedRichBodyStorageSchema,\n} from './feedFieldStorageModel';\n\n/** RSS enclosure URL from a plain string (URL or tx id) or nested Image relation object. */\nfunction getEnclosureUrlFromImageRelationField(value: unknown): string | undefined {\n if (value == null) return undefined\n if (typeof value === 'string') {\n if (value.startsWith('http://') || value.startsWith('https://')) return value\n try {\n return getArweaveUrlForTransaction(value)\n } catch {\n return undefined\n }\n }\n if (typeof value === 'object' && !Array.isArray(value)) {\n const o = value as Record<string, unknown>\n const direct = o.arweaveUrl\n if (typeof direct === 'string' && direct.trim()) return direct.trim()\n const tx = (o.storageTransactionId ?? o.storage_transaction_id) as string | undefined\n if (tx && typeof tx === 'string' && tx.trim()) {\n try {\n return getArweaveUrlForTransaction(tx.trim())\n } catch {\n return undefined\n }\n }\n }\n return undefined\n}\n\ninterface RssImageCandidate {\n url: string\n mimeType?: string\n width?: number\n height?: number\n size?: number\n format?: string\n}\n\nfunction coercePositiveNumber(value: unknown): number | undefined {\n if (typeof value === 'number' && Number.isFinite(value) && value > 0) return value\n if (typeof value === 'string') {\n const parsed = Number(value)\n if (Number.isFinite(parsed) && parsed > 0) return parsed\n }\n return undefined\n}\n\nfunction extractRssImageCandidateFromRelationFieldValue(\n value: unknown\n): RssImageCandidate | undefined {\n if (Array.isArray(value)) {\n for (const entry of value) {\n const candidate = extractRssImageCandidateFromRelationFieldValue(entry)\n if (candidate) return candidate\n }\n return undefined\n }\n\n const url = getEnclosureUrlFromImageRelationField(value)\n if (!url) return undefined\n\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n const record = value as Record<string, unknown>\n const mimeType =\n (typeof record.mimeType === 'string' && record.mimeType) ||\n (typeof record.type === 'string' && record.type) ||\n (typeof record.contentType === 'string' && record.contentType) ||\n undefined\n return {\n url,\n mimeType,\n width: coercePositiveNumber(record.width),\n height: coercePositiveNumber(record.height),\n size: coercePositiveNumber(record.size ?? record.fileSize),\n format: typeof record.format === 'string' ? record.format : undefined,\n }\n }\n\n return { url }\n}\n\nfunction extractRssImageCandidate(item: Record<string, unknown>): RssImageCandidate | undefined {\n if (item._hasImage && item._imageMetadata && typeof item._imageMetadata === 'object') {\n const imageMeta = item._imageMetadata as ImageMetadata\n if (imageMeta.url) {\n return {\n url: imageMeta.url,\n mimeType: imageMeta.mimeType,\n width: imageMeta.width,\n height: imageMeta.height,\n size: imageMeta.size,\n format: imageMeta.format,\n }\n }\n }\n\n const imageCandidateFields = [\n 'feature_image',\n 'featureImage',\n 'image',\n 'images',\n 'cover_image',\n 'coverImage',\n 'thumbnail',\n 'thumbnail_image',\n 'thumbnailImage',\n ]\n\n for (const key of imageCandidateFields) {\n if (!(key in item)) continue\n const candidate = extractRssImageCandidateFromRelationFieldValue(item[key])\n if (candidate) return candidate\n }\n\n return undefined\n}\n\nfunction applyRssImageCandidate(\n rssItem: Record<string, unknown>,\n item: Record<string, unknown>,\n imageCandidate: RssImageCandidate,\n schemaName: string,\n siteAuthor: FeedConfig['author'],\n siteCopyright: FeedConfig['copyright'],\n): void {\n const mediaType = imageCandidate.mimeType || 'image/jpeg'\n\n rssItem.enclosures = [\n {\n url: imageCandidate.url,\n type: mediaType,\n ...(imageCandidate.size ? { length: imageCandidate.size } : {}),\n },\n ]\n\n const mediaContent: Record<string, unknown> = {\n url: imageCandidate.url,\n type: mediaType,\n medium: 'image',\n }\n\n if (imageCandidate.width) mediaContent.width = imageCandidate.width\n if (imageCandidate.height) mediaContent.height = imageCandidate.height\n if (imageCandidate.size) mediaContent.fileSize = imageCandidate.size\n if (imageCandidate.format) mediaContent.format = imageCandidate.format\n rssItem['media:content'] = [mediaContent]\n\n const thumbnail: Record<string, unknown> = { url: imageCandidate.url }\n if (imageCandidate.width) thumbnail.width = imageCandidate.width\n if (imageCandidate.height) thumbnail.height = imageCandidate.height\n rssItem['media:thumbnail'] = [thumbnail]\n\n if (item.title) {\n rssItem['media:title'] = item.title\n }\n\n const mediaDescription = item.description || item.summary || item.title || ''\n if (mediaDescription) {\n rssItem['media:description'] = mediaDescription\n }\n\n if (item.title) {\n const keywords: string[] = []\n keywords.push(...String(item.title).toLowerCase().split(/\\s+/).filter((w) => w.length > 3))\n if (item.description) {\n keywords.push(...String(item.description).toLowerCase().split(/\\s+/).filter((w) => w.length > 3))\n }\n if (keywords.length > 0) {\n rssItem['media:keywords'] = [...new Set(keywords)].slice(0, 10).join(', ')\n }\n }\n\n if (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 (siteAuthor) {\n rssItem['media:credit'] = [{ value: siteAuthor.name, role: 'author' }]\n }\n\n if (siteCopyright) {\n rssItem['media:copyright'] = siteCopyright\n }\n\n if (schemaName) {\n rssItem['media:category'] = [\n {\n value: schemaName,\n scheme: 'http://www.schema.org/',\n },\n ]\n }\n\n rssItem['media:group'] = [\n {\n 'media:content': [mediaContent],\n 'media:thumbnail': [thumbnail],\n 'media:title': item.title || 'Untitled',\n 'media:description': mediaDescription,\n },\n ]\n\n if (\n imageCandidate.url &&\n typeof item.content === 'string' &&\n item.content.trim() !== '' &&\n !item.content.includes(imageCandidate.url)\n ) {\n const imageHtml = `<img src=\"${imageCandidate.url}\" alt=\"${item.title || ''}\"${imageCandidate.width ? ` width=\"${imageCandidate.width}\"` : ''}${imageCandidate.height ? ` height=\"${imageCandidate.height}\"` : ''} />`\n rssItem['content:encoded'] = `${imageHtml}\\n${item.content}`\n }\n}\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// 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, richTextDataUriImages = 'omit_items' } = options\n\n const filtered = filterItemsByRichTextDataUriImagePolicy(\n items as Record<string, unknown>[],\n richTextDataUriImages,\n ) as GraphQLItem[]\n\n return filtered.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 const description = pickFeedItemDescription(item)\n const content = pickFeedItemContent(item)\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,\n content,\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 if (typeof feedItem.html === 'string' && feedItem.Html === undefined) {\n feedItem.Html = feedItem.html\n }\n if (typeof feedItem.Html === 'string' && feedItem.html === undefined) {\n feedItem.html = feedItem.Html\n }\n if (typeof feedItem.body === 'string' && feedItem.Body === undefined) {\n feedItem.Body = feedItem.body\n }\n if (typeof feedItem.Body === 'string' && feedItem.body === undefined) {\n feedItem.body = feedItem.Body\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 interface FeedPaginationOptions {\n page: number\n pageSize: number\n hasNext: boolean\n baseUrl: string\n}\n\nexport interface FeedArchiveLink {\n rel: string\n href: string\n}\n\nexport const createFeed = (\n items: GraphQLItem[],\n schemaName: string,\n format: FeedFormat,\n cacheBust?: string,\n pagination?: FeedPaginationOptions,\n archiveLinks?: FeedArchiveLink[],\n isArchive?: boolean,\n transformOverrides?: Partial<TransformOptions>,\n siteConfigOverrides?: SiteConfigOverrides,\n): Promise<string> => {\n const siteConfig = resolveSiteConfig(siteConfigOverrides)\n const collectionName = pluralize(schemaName)\n // Feed document self-links use feedUrl; channel home/link uses siteUrl\n const feedUrlBase = `${siteConfig.feedUrl}/${collectionName}/${format}`\n const feedUrl = cacheBust ? `${feedUrlBase}?v=${cacheBust}` : feedUrlBase\n const feedTitle = `${siteConfig.title} - ${capitalize(collectionName)}`\n const now = new Date()\n\n const feedConfig = loadFeedConfig()\n\n // Build RFC 5005 links from pagination or archiveLinks\n const links: Array<{ rel: string; href: string; type?: string }> = []\n if (pagination) {\n const base = pagination.baseUrl.replace(/\\?.*$/, '')\n const sep = base.includes('?') ? '&' : '?'\n if (pagination.hasNext) {\n links.push({ rel: 'next', href: `${base}${sep}page=${pagination.page + 1}` })\n }\n if (pagination.page > 1) {\n links.push({ rel: 'previous', href: `${base}${sep}page=${pagination.page - 1}` })\n links.push({ rel: 'first', href: `${base}${sep}page=1` })\n }\n }\n if (archiveLinks?.length) {\n for (const al of archiveLinks) {\n links.push({ rel: al.rel, href: al.href })\n }\n }\n\n // Transform items to preserve all dynamic properties\n const transformedItems = transformToFeedItems(items, {\n schemaName,\n siteUrl: siteConfig.siteUrl,\n itemUrlBase: feedConfig.itemUrlBase,\n itemUrlPath: feedConfig.itemUrlPath,\n richTextDataUriImages:\n transformOverrides?.richTextDataUriImages ?? feedConfig.richTextDataUriImages,\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 atomLinks: Array<{ href: string; rel?: string; type?: string }> = [\n { href: feedUrl, rel: 'self' },\n { href: siteConfig.siteUrl },\n ]\n for (const link of links) {\n atomLinks.push({\n href: link.href,\n rel: link.rel,\n type: link.type,\n })\n }\n const atomFeed = {\n id: feedUrl,\n title: feedTitle,\n updated: now,\n links: atomLinks,\n subtitle: siteConfig.description,\n rights: siteConfig.copyright,\n author: siteConfig.author ? {\n name: siteConfig.author.name,\n email: siteConfig.author.email,\n uri: siteConfig.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 nextUrl = pagination?.hasNext\n ? `${pagination.baseUrl.replace(/\\?.*$/, '')}?page=${pagination.page + 1}`\n : undefined\n const jsonFeed = {\n title: feedTitle,\n home_page_url: siteConfig.siteUrl,\n feed_url: feedUrl,\n ...(nextUrl && { next_url: nextUrl }),\n description: siteConfig.description,\n author: siteConfig.author ? {\n name: siteConfig.author.name,\n url: siteConfig.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: siteConfig.siteUrl,\n description: siteConfig.description,\n links: links.length ? links.map((l) => ({ rel: l.rel, href: l.href, type: l.type })) : undefined,\n isArchive: isArchive ?? false,\n language: siteConfig.language,\n copyright: siteConfig.copyright,\n webMaster: siteConfig.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 const imageCandidate = extractRssImageCandidate(item)\n if (imageCandidate) {\n applyRssImageCandidate(\n rssItem,\n item,\n imageCandidate,\n schemaName,\n siteConfig.author,\n siteConfig.copyright\n )\n }\n\n if (\n typeof item.content === 'string' &&\n item.content.trim() !== '' &&\n rssItem['content:encoded'] === undefined\n ) {\n rssItem['content:encoded'] = item.content\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 page?: number,\n siteConfigOverrides?: SiteConfigOverrides,\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 const siteConfig = resolveSiteConfig(siteConfigOverrides)\n\n const feedConfig = loadFeedConfig()\n const pageNum = Math.max(1, page ?? 1)\n const pageSize = feedConfig.pageSize\n const skip = (pageNum - 1) * pageSize\n\n const contentKeyOptions =\n pageNum > 1 ? { page: pageNum } : undefined\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, contentKeyOptions);\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 const fetchItems = async (): Promise<GraphQLItem[]> => {\n const items = await getFeedItemsBySchemaName(schemaName, { limit: pageSize, skip }) as GraphQLItem[];\n if (config.imageMetadata?.enabled) {\n const imageService = new ArweaveImageService({\n gateways: config.imageMetadata.gateways,\n timeout: config.imageMetadata.timeout,\n });\n return enrichFeedItemsWithMedia(items, imageService, cache);\n }\n return items;\n };\n\n const feedItems = config.enabled && pageNum === 1\n ? await cache.withRefreshLock(schemaName, async () => {\n const cachedData = await cache.getFeedData(schemaName);\n let items: GraphQLItem[];\n\n if (cachedData) {\n console.log(`Incremental fetch: last processed timestamp: ${cachedData.lastProcessedTimestamp}`);\n const allItems = await getFeedItemsBySchemaName(schemaName, { limit: pageSize, skip: 0 }) as GraphQLItem[];\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 items = cache.mergeItems(cachedData.items, newItems).slice(0, pageSize);\n } else {\n items = cachedData.items;\n }\n } else {\n console.log(`Cold cache - fetching page 1`);\n const client = await getClient();\n if (client) console.log(`Client initialized: ${client.isInitialized()}`);\n items = await getFeedItemsBySchemaName(schemaName, { limit: pageSize, skip: 0 }) as GraphQLItem[];\n console.log(`Found ${items.length} feed items for schema ${schemaName}`);\n }\n\n if (config.imageMetadata?.enabled) {\n const imageService = new ArweaveImageService({\n gateways: config.imageMetadata.gateways,\n timeout: config.imageMetadata.timeout,\n });\n const enrichedItems = await enrichFeedItemsWithMedia(items, imageService, cache);\n await cache.setFeedData(schemaName, items);\n return enrichedItems;\n }\n await cache.setFeedData(schemaName, items);\n return items;\n })\n : await fetchItems();\n\n const baseUrl = `${siteConfig.feedUrl}/${collectionName}/${format}`;\n const hasNext = feedItems.length === pageSize;\n const archiveLinksForMain: FeedArchiveLink[] = [];\n if (pageNum === 1) {\n const now = new Date();\n const prevMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);\n archiveLinksForMain.push({\n rel: 'prev-archive',\n href: `${siteConfig.feedUrl}/${collectionName}/archive/${prevMonth.getFullYear()}/${prevMonth.getMonth() + 1}/${format}`,\n });\n }\n\n const paginationOptions: FeedPaginationOptions | undefined = {\n page: pageNum,\n pageSize,\n hasNext,\n baseUrl,\n };\n\n const feedContent = await createFeed(\n feedItems,\n schemaName,\n format,\n cacheBust,\n paginationOptions,\n archiveLinksForMain.length ? archiveLinksForMain : undefined,\n false,\n undefined,\n siteConfigOverrides,\n );\n const contentType = getContentType(format);\n\n // Cache the generated feed content\n if (config.enabled) {\n await cache.setFeedContent(schemaName, format, feedContent, contentType, contentKeyOptions);\n \n // Get the cached content to retrieve ETag\n const cachedContent = await cache.getFeedContent(schemaName, format, contentKeyOptions);\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, contentKeyOptions);\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}\n\n/**\n * Route handler for monthly archive feeds (RFC 5005).\n *\n * URL Pattern: /:collection/archive/:year/:month/:format\n * Examples:\n * - /posts/archive/2024/02/rss → RSS feed of posts from February 2024\n */\nexport async function handleArchiveFeedRequest(\n collectionSegment: string,\n year: number,\n month: number,\n formatSegment: string,\n ifNoneMatch?: string | null,\n cacheBust?: string,\n siteConfigOverrides?: SiteConfigOverrides,\n): Promise<Response> {\n if (year < 1970 || year > 2100) {\n return new Response(\n JSON.stringify({ error: `Invalid year: ${year}` }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n if (month < 1 || month > 12) {\n return new Response(\n JSON.stringify({ error: `Invalid month: ${month}` }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const format = parseFormat(formatSegment);\n if (!format) {\n return new Response(\n JSON.stringify({ error: `Invalid feed format: ${formatSegment}` }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const schemaName = pluralize.singular(collectionSegment.toLowerCase());\n const collectionName = pluralize(schemaName);\n const siteConfig = resolveSiteConfig(siteConfigOverrides);\n\n const contentKeyOptions = { archive: { year, month } };\n const cache = getCacheManager();\n const config = loadCacheConfig();\n\n try {\n if (config.enabled) {\n const cachedContent = await cache.getFeedContent(schemaName, format, contentKeyOptions);\n if (cachedContent) {\n if (ifNoneMatch && checkIfNoneMatch(ifNoneMatch, cachedContent.etag)) {\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=86400, s-maxage=86400, must-revalidate',\n },\n });\n }\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=86400, s-maxage=86400, must-revalidate',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n 'X-Cache': 'HIT',\n },\n });\n }\n }\n\n const items = await getFeedItemsBySchemaNameForMonth(schemaName, year, month) as GraphQLItem[];\n\n const baseUrl = `${siteConfig.feedUrl}/${collectionName}/${format}`;\n const archiveBase = `${siteConfig.feedUrl}/${collectionName}/archive`;\n\n const archiveLinks: FeedArchiveLink[] = [\n { rel: 'prev-archive', href: `${archiveBase}/${month === 1 ? year - 1 : year}/${month === 1 ? 12 : month - 1}/${format}` },\n { rel: 'current', href: baseUrl },\n ];\n\n const nextYear = month === 12 ? year + 1 : year;\n const nextMonth = month === 12 ? 1 : month + 1;\n const now = new Date();\n if (nextYear < now.getFullYear() || (nextYear === now.getFullYear() && nextMonth <= now.getMonth() + 1)) {\n archiveLinks.push({ rel: 'next-archive', href: `${archiveBase}/${nextYear}/${nextMonth}/${format}` });\n }\n\n const feedContent = await createFeed(\n items,\n schemaName,\n format,\n cacheBust,\n undefined,\n archiveLinks,\n true,\n undefined,\n siteConfigOverrides,\n );\n\n const contentType = getContentType(format);\n\n if (config.enabled) {\n await cache.setFeedContent(schemaName, format, feedContent, contentType, contentKeyOptions);\n const cachedContent = await cache.getFeedContent(schemaName, format, contentKeyOptions);\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=86400, s-maxage=86400, must-revalidate',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n 'X-Cache': 'MISS',\n },\n });\n }\n\n return new Response(feedContent, {\n status: 200,\n headers: {\n 'Content-Type': contentType,\n 'Cache-Control': 'public, max-age=86400, s-maxage=86400',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n },\n });\n } catch (error) {\n console.error('Archive feed generation error:', error);\n return new Response(\n JSON.stringify({\n error: 'Failed to generate archive feed',\n message: error instanceof Error ? error.message : 'Unknown error',\n }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n );\n }\n}"],"mappings":"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"}
|