@seedprotocol/feed 0.7.10 → 0.7.11-next.1384.g8b47246

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.
@@ -1,4 +1,4 @@
1
- import type { FeedItem, ImageMetadata } from '../types';
1
+ import type { ImageMetadata } from '../types';
2
2
  /**
3
3
  * Cached image metadata for an Arweave transaction ID
4
4
  */
@@ -55,5 +55,4 @@ export interface CacheContentKeyOptions {
55
55
  };
56
56
  }
57
57
  export declare function buildContentKey(schemaName: string, format: string, options?: CacheContentKeyOptions): string;
58
- export type { FeedItem };
59
58
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/cache/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,aAAa,CAAA;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3C;AAED,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,sBAAsB,GAC/B,MAAM,CAQR;AAED,YAAY,EAAE,QAAQ,EAAE,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/cache/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,aAAa,CAAA;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3C;AAED,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,sBAAsB,GAC/B,MAAM,CAQR"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["formatRfc822Date","fs","resolveSeedRssImageRelationRef","resolveSeedRssImageRelationRef$1","Parser","pluralize","sizeOf","debug","initializeFeedPlatform","initializeQueryPlatform","options","teardownFeedPlatform","teardownQueryPlatform","DEFAULT_SITE_CONFIG","cloneSiteConfig","config","siteConfig","resolveSiteConfig","overrides","base","resolved","getSiteConfig","setSiteConfig","resetSiteConfig","loadFeedConfig","getEasChainDeployment","getEasReadChainId","formatRfc822Date$1","date","timestamp","days","months","day","month","year","setFeedItemDefaults","item","seedUid","storageTransactionIdSnake","storageTransactionIdCamel","storageTransactionId","validSeedUid","defaultLink","itemUrlBase","itemUrlPath","getArweaveUrlForTransaction","error","currentLink","currentGuid","pubDate","setImageRelationCloneForFeed","clone","mediaLink","looksLikeSeedClone","value","rec","withNestedCloneDefaults","copy","nestedUid","nestedSchema","parentSchemaName","applyFeedDefaultsDeep","out","schemaName","key","el","seedRecordsToFeedItems","feedConfig","defaultsOptions","records","record","fetchFeedItemsBySchemaName","limit","skip","queryBySchema","items","getFeedItemsBySchemaName","getFeedItemsBySchemaNameForMonth","queryBySchemaForMonth","toPlainItem","parser","parseRssString","feed","xml","DC_NS","CONTENT_NS","MEDIA_NS","RELATION_NAMESPACES","RELATION_NAMESPACE_URI_BASE","RESERVED_XML_PREFIXES","CAPITALIZED_DUPLICATES","isRedundantCapitalizedKey","RSS_ITEM_ORDER","formatRfc822Date","d","needsCdata","addTextOrCdata","str","parts","i","parent","isExpandedRelationObject","MAX_EXPANDED_RELATION_DEPTH","NON_RELATION_KEYS","normalizeRelationKey","sanitizePrefixToken","rawKey","createUniquePrefix","usedPrefixes","preferredPrefix","suffix","candidate","isRelationArray","entry","buildRelationNamespaceMap","relationNamespaces","namespaceContext","discoveredRelationKeys","isAuthorRelation","sortedDiscoveredRelationKeys","relationKey","getRelationNamespaceContext","singular","getNamespacedChildTagName","localName","serializeExpandedRelation","ele","tagName","k","v","obj","childTagName","depth","childItemTag","addDcCreator","name","serializeItemProperty","guid","val","attrs","itemParent","src","title","enc","dc","dcKey","dcVal","group","groupEle","gk","gv","ln","child","ck","cv","textContent","attr","attrVal","mediaEle","a","authorStr","c","cat","ATOM_NS","FH_NS","generateRssXml","doc","create","rssFeed","rssAttrs","relationNamespace","acc","channel","link","orderedSet","processedKeys","otherKeys","buildContentKey","format","MemoryCache","contentKeyOptions","cached","transactionId","now","FileCache","err","promises","filePart","join","imageMetadataDir","filePath","data","content","CacheManager","fingerprint","ttl","body","metadata","loadCacheConfig","cacheDir","cacheDisabledByEnvVar","cacheEnabledByEnvVar","isDev","enabled","imageMetadata","g","getArweaveReadGatewayHostsForPrimary","resolveArweaveHostFromEnv","BaseArweaveClient","pageTtl","archiveTtl","generateETag","createHash","etagMatches","normalize","etag","etag1","etag2","parseIfNoneMatch","headerValue","checkIfNoneMatch","etags","ifNoneMatchHeader","currentETag","ArweaveImageService","ensureArweaveReadGatewayForFallback","baseUrls","getArweaveReadBaseUrls","isGatewayHostCircuitOpen","url","hostHint","host","headResponse","contentType","contentLength","rangeResponse","buffer","dimensions","recordGatewayHostFailure","response","mimeMatch","controller","timeoutId","feedDebug","firstNonEmptyString","keys","DESCRIPTION_KEYS","CONTENT_PRIMARY_KEYS","CONTENT_FALLBACK_KEYS","compareRichBodyCandidates","richBodyModelPriority","b","pickTypedRichBodyContent","fieldModels","getFieldStorageModels","listModels","getListElementStorageModels","best","model","isRichBodyStorageSchema","cand","listKey","models","arr","n","pickFeedItemDescription","pickFeedItemContent","typed","primary","stringFieldContainsDataUriImage","feedItemTypedRichFieldsContainDataUriImage","feedItemRichTextContainsDataUriImage","filterItemsByRichTextDataUriImagePolicy","richTextDataUriImages","getEnclosureUrlFromImageRelationField","coercePositiveNumber","parsed","extractRssImageCandidateFromRelationFieldValue","extractRssImageCandidate","imageMeta","applyRssImageCandidate","mediaType","imageCandidate","mediaContent","thumbnail","mediaDescription","keywords","w","author","siteAuthor","siteCopyright","cacheManager","getCacheManager","resetCacheManager","ENRICH_CONCURRENCY","runWithConcurrency","next","tasks","task","imageMetadataFor","cache","imageService","arweaveTransactionIdOf","isArweaveTransactionGatewayUrl","enclosureTransactionOf","enrichFeedItemWithMedia","enrichFeedItemsWithMedia","toEnrich","withImageMetadata","transformToFeedItems","itemId","fallbackUrl","itemUrl","pubDateStr","dateValue","description","feedItem","createFeed","siteConfigOverrides","collectionName","feedUrlBase","feedUrl","cacheBust","feedTitle","capitalize","links","pagination","sep","archiveLinks","al","transformedItems","transformOverrides","atomLinks","atomFeed","imageHtml","generateAtomFeed","nextUrl","jsonFeed","jsonItem","jsonResult","generateJsonFeed","l","isArchive","rssItem","timeCreatedDate","getContentType","parseFormat","normalized","segment","feedFingerprint","inputs","feedResponse","headers","request","extraHeaders","status","cachedFeedResponse","xCache","serveFeed","cacheEnabled","fetched","stored","stale","invalidFormatResponse","formatSegment","handleFeedRequest","collectionSegment","pageNum","page","pageSize","hasNext","getQueryCacheManager","prevMonth","handleArchiveFeedRequest","baseUrl","archiveBase","nextYear","nextMonth","DEFAULT_ARWEAVE_HOST","SEED_RSS_IMAGE_RELATION_FIELD_KEYS","classifyMediaRef","getFeedItemStringField","isEasAttestationExplorerUrl","normalizeFeedItemFields","resolveSeedRssImageRelationFromItem","resolveSeedRssImageRelationRef"],"sources":["../src/bootstrap.ts","../src/config.ts","../src/getFeedItems.ts","../src/consume/parseRss.ts","../src/rss/generateRssXml.ts","../src/cache/types.ts","../src/cache/MemoryCache.ts","../src/cache/FileCache.ts","../src/cache/CacheManager.ts","../src/cache/config.ts","../src/utils/etag.ts","../src/services/arweaveImageService.ts","../src/debug.ts","../src/pickFeedItemRichText.ts","../src/index.ts"],"sourcesContent":["import { initializeQueryPlatform, teardownQueryPlatform } from '@seedprotocol/query'\n\nexport type InitializeFeedPlatformOptions = {\n arweaveDomain?: string\n}\n\n/**\n * Make EAS and Arweave usable for feed generation (via `initializeQueryPlatform`). Clients a host\n * app or the SDK already registered are kept; otherwise the Node EAS and Arweave clients are\n * registered. `arweaveDomain` sets the Arweave host.\n */\nexport async function initializeFeedPlatform(\n options?: InitializeFeedPlatformOptions,\n): Promise<void> {\n await initializeQueryPlatform(options)\n}\n\n/** Lets the next initializeFeedPlatform call run again (tests). Registered clients stay. */\nexport async function teardownFeedPlatform(): Promise<void> {\n await teardownQueryPlatform()\n}\n\nexport { DEFAULT_ARWEAVE_HOST } from '@seedprotocol/arweave'\n","import { getEasChainDeployment, getEasReadChainId } from '@seedprotocol/eas';\nimport 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://base.easscan.org).\n * Item links use {base}/attestation/view/{uid}. Default: the EAS explorer of the configured\n * chain (`EAS_CHAIN_ID`, or the SDK / publish chain in-process), else 'https://easscan.org'.\n * - FEED_ITEM_URL_PATH: Path segment for attestation links (default: 'attestation/view'), with\n * FEED_ITEM_URL_BASE or its default.\n * - FEED_PAGE_SIZE: Items per page for paged feeds (default: 25; a value that isn't a positive\n * integer gives the default).\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 pageSize: number;\n richTextDataUriImages: 'omit_items' | 'include_items';\n} {\n const itemUrlBase =\n process.env.FEED_ITEM_URL_BASE?.trim() ||\n getEasChainDeployment(getEasReadChainId())?.explorerUrl ||\n 'https://easscan.org';\n const itemUrlPath =\n process.env.FEED_ITEM_URL_PATH?.trim() || 'attestation/view';\n const pageSize = Math.max(1, parseInt(process.env.FEED_PAGE_SIZE || '25', 10) || 25);\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, pageSize, richTextDataUriImages };\n}\n","import {\n queryBySchema,\n queryBySchemaForMonth,\n getArweaveUrlForTransaction,\n type SeedRecord,\n} from '@seedprotocol/query'\nimport { initializeFeedPlatform } from './bootstrap'\nimport { loadFeedConfig } from './config'\n\ntype SetFeedItemDefaultsOptions = {\n itemUrlBase: string\n itemUrlPath: string\n}\n\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\n/**\n * Set default values for feed items (title, link, guid, pubDate, dual-case keys).\n */\nexport const setFeedItemDefaults = (\n item: Record<string, unknown>,\n seedUid: string,\n schemaName: string,\n options: SetFeedItemDefaultsOptions,\n): void => {\n const { itemUrlBase, itemUrlPath } = options\n\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 validSeedUid =\n seedUid && typeof seedUid === 'string' && seedUid.trim() !== '' ? seedUid : 'unknown'\n const attestationLink = `${itemUrlBase.replace(/\\/$/, '')}/${itemUrlPath.replace(/^\\//, '')}/${validSeedUid}`\n\n let defaultLink = attestationLink\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 }\n }\n\n const currentLink = (item.link || item.Link) as string | undefined\n if (\n !currentLink ||\n currentLink === 'undefined' ||\n (typeof currentLink === 'string' && currentLink.trim() === '')\n ) {\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 (\n !currentGuid ||\n currentGuid === 'undefined' ||\n (typeof currentGuid === 'string' && currentGuid.trim() === '')\n ) {\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\n/**\n * Defaults for expanded Image relation clones in feeds.\n * Does not emit EAS attestation explorer URLs as `link` — only gateway URLs when storage exists.\n */\nconst setImageRelationCloneForFeed = (\n clone: Record<string, unknown>,\n seedUid: string,\n): void => {\n if (!clone.title && !clone.Title) {\n clone.title = seedUid\n clone.Title = seedUid\n } else if (clone.title && !clone.Title) {\n clone.Title = clone.title\n } else if (clone.Title && !clone.title) {\n clone.title = clone.Title\n }\n\n if (clone.timeCreated && !clone.pubDate && !clone.PubDate) {\n const pubDate = formatRfc822Date(clone.timeCreated as number)\n clone.pubDate = pubDate\n clone.PubDate = pubDate\n } else if (clone.pubDate && !clone.PubDate) {\n clone.PubDate = clone.pubDate\n } else if (clone.PubDate && !clone.pubDate) {\n clone.pubDate = clone.PubDate\n }\n\n if (!clone.seedUid && !clone.SeedUid) {\n clone.seedUid = seedUid\n clone.SeedUid = seedUid\n } else if (clone.seedUid && !clone.SeedUid) {\n clone.SeedUid = clone.seedUid\n } else if (clone.SeedUid && !clone.seedUid) {\n clone.seedUid = clone.SeedUid\n }\n\n if (clone.attester && !clone.Attester) {\n clone.Attester = clone.attester\n } else if (clone.Attester && !clone.attester) {\n clone.attester = clone.Attester\n }\n\n const storageTransactionIdSnake =\n clone.storage_transaction_id &&\n typeof clone.storage_transaction_id === 'string' &&\n clone.storage_transaction_id.trim() !== '' &&\n clone.storage_transaction_id !== 'undefined' &&\n clone.storage_transaction_id !== seedUid\n ? (clone.storage_transaction_id as string).trim()\n : null\n const storageTransactionIdCamel =\n clone.storageTransactionId &&\n typeof clone.storageTransactionId === 'string' &&\n clone.storageTransactionId.trim() !== '' &&\n clone.storageTransactionId !== 'undefined' &&\n clone.storageTransactionId !== seedUid\n ? (clone.storageTransactionId as string).trim()\n : null\n const storageTransactionId = storageTransactionIdSnake || storageTransactionIdCamel\n\n delete clone.link\n delete clone.Link\n delete clone.guid\n delete clone.Guid\n\n if (storageTransactionId && storageTransactionId.length > 0) {\n try {\n const mediaLink = getArweaveUrlForTransaction(storageTransactionId)\n clone.link = mediaLink\n clone.Link = mediaLink\n } catch {\n // omit link when gateway URL cannot be built\n }\n }\n}\n\nfunction looksLikeSeedClone(value: unknown): value is Record<string, unknown> {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false\n const rec = value as Record<string, unknown>\n return typeof rec.seedUid === 'string' || typeof rec.SeedUid === 'string'\n}\n\nfunction withNestedCloneDefaults(\n clone: Record<string, unknown>,\n parentSchemaName: string,\n options: SetFeedItemDefaultsOptions,\n): Record<string, unknown> {\n const copy = { ...clone }\n const nestedUid = (copy.seedUid || copy.SeedUid || '') as string\n const nestedSchema =\n typeof copy.schemaName === 'string' ? copy.schemaName : parentSchemaName\n if (nestedSchema === 'image') {\n setImageRelationCloneForFeed(copy, nestedUid)\n } else {\n setFeedItemDefaults(copy, nestedUid, nestedSchema, options)\n }\n return copy\n}\n\n/**\n * Apply feed defaults to a root item and nested expanded seed clones.\n * Image relation clones get media-only link defaults (no EAS explorer URLs).\n *\n * Returns a copy and leaves `item` untouched: `item` may be a record held by the\n * `@seedprotocol/query` collection cache. Only the objects the defaults write to\n * (the root, expanded seed clones, and arrays holding them) are shallow-copied;\n * other values, notably hydrated HTML strings, are shared rather than cloned.\n */\nfunction applyFeedDefaultsDeep(\n item: Record<string, unknown>,\n schemaName: string,\n options: SetFeedItemDefaultsOptions,\n): Record<string, unknown> {\n const out = { ...item }\n const seedUid = (out.seedUid || out.SeedUid || '') as string\n setFeedItemDefaults(out, seedUid, schemaName, options)\n\n for (const key of Object.keys(out)) {\n if (key.startsWith('_')) continue\n const value = out[key]\n if (Array.isArray(value)) {\n if (value.some(looksLikeSeedClone)) {\n out[key] = value.map((el) =>\n looksLikeSeedClone(el) ? withNestedCloneDefaults(el, schemaName, options) : el,\n )\n }\n } else if (looksLikeSeedClone(value)) {\n out[key] = withNestedCloneDefaults(value, schemaName, options)\n }\n }\n return out\n}\n\nfunction seedRecordsToFeedItems(\n schemaName: string,\n records: SeedRecord[],\n): Record<string, unknown>[] {\n const feedConfig = loadFeedConfig()\n const defaultsOptions: SetFeedItemDefaultsOptions = {\n itemUrlBase: feedConfig.itemUrlBase,\n itemUrlPath: feedConfig.itemUrlPath,\n }\n\n return records.map((record) =>\n applyFeedDefaultsDeep(record.data, schemaName, defaultsOptions),\n )\n}\n\n/**\n * Feed items for a page of the schema, with query's collection ETag when the collection cache\n * answered (skip 0, cache enabled): it changes whenever a listed seed or what it was built from\n * changes, so the feed can tell whether a cached body is still current.\n */\nexport const fetchFeedItemsBySchemaName = async (\n schemaName: string,\n options?: { limit?: number; skip?: number },\n): Promise<{ items: Record<string, unknown>[]; etag?: string }> => {\n await initializeFeedPlatform()\n const limit = options?.limit ?? 100\n const skip = options?.skip ?? 0\n\n // Expanded relations give items their author names and image enclosures.\n const { items, etag } = await queryBySchema(schemaName, {\n limit,\n skip,\n relations: 'expand',\n hydrateStorage: true,\n })\n return { items: seedRecordsToFeedItems(schemaName, items), etag }\n}\n\nexport const getFeedItemsBySchemaName = async (\n schemaName: string,\n options?: { limit?: number; skip?: number },\n): Promise<Record<string, unknown>[]> =>\n (await fetchFeedItemsBySchemaName(schemaName, options)).items\n\nexport const getFeedItemsBySchemaNameForMonth = async (\n schemaName: string,\n year: number,\n month: number,\n): Promise<Record<string, unknown>[]> => {\n await initializeFeedPlatform()\n\n const records = await queryBySchemaForMonth(schemaName, year, month, {\n relations: 'expand',\n hydrateStorage: true,\n })\n return seedRecordsToFeedItems(schemaName, records)\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 { FeedItem, ImageMetadata } from '../types';\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 * What the body was built from (query's collection ETag when known, site config, cache-bust\n * value; see feedFingerprint). A read that passes a fingerprint only gets an entry built from\n * the same inputs. Entries written before fingerprints existed have none and never match.\n */\n fingerprint?: string;\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 (serialized content + image metadata).\n * Collection/item caching lives in @seedprotocol/query.\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 imageMetadata?: ImageMetadataConfig\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\nexport type { FeedItem };\n","import type { CachedFeedContent, CacheConfig, CacheContentKeyOptions, CachedImageMetadata } from './types';\nimport { buildContentKey } from './types';\nimport type { ImageMetadata } from '../types';\n\n/**\n * In-memory cache for serialized feed content and image metadata.\n * Collection/item caching is owned by @seedprotocol/query.\n */\nexport class MemoryCache {\n private feedContentCache: Map<string, CachedFeedContent> = new Map();\n private imageMetadataCache: Map<string, CachedImageMetadata> = new Map();\n private config: CacheConfig;\n\n constructor(config: CacheConfig) {\n this.config = config;\n }\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 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 /** Stores an entry as it is (ETag, lastModified and expiry kept), e.g. one read from disk. */\n putFeedContent(\n schemaName: string,\n format: string,\n cached: CachedFeedContent,\n contentKeyOptions?: CacheContentKeyOptions,\n ): void {\n this.feedContentCache.set(buildContentKey(schemaName, format, contentKeyOptions), cached);\n }\n\n getImageMetadata(transactionId: string): ImageMetadata | null {\n const cached = this.imageMetadataCache.get(transactionId);\n if (!cached) {\n return null;\n }\n\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 setImageMetadata(transactionId: string, metadata: ImageMetadata): void {\n const now = Math.floor(Date.now() / 1000);\n const imageMetadataTtl = this.config.imageMetadata?.ttl || 604800;\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","import { promises as fs } from 'fs';\nimport { join } from 'path';\nimport type { CachedFeedContent, CacheConfig, CacheContentKeyOptions, CachedImageMetadata } from './types';\nimport { buildContentKey } from './types';\nimport type { ImageMetadata } from '../types';\n\n/**\n * File-based persistent cache for serialized feed content and image metadata.\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 this.ensureCacheDir().catch(err => {\n console.error('Failed to create cache directory:', err);\n });\n }\n\n private async ensureCacheDir(): Promise<void> {\n try {\n await fs.mkdir(this.cacheDir, { recursive: true });\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {\n throw error;\n }\n }\n }\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 private getImageMetadataPath(transactionId: string): string {\n const imageMetadataDir = join(this.cacheDir, 'image-metadata');\n return join(imageMetadataDir, `${transactionId}.json`);\n }\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 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 const now = Math.floor(Date.now() / 1000);\n if (now > cached.expiresAt) {\n await this.clearFeedContent(schemaName, format, contentKeyOptions);\n return null;\n }\n\n return cached;\n } catch (error) {\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 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 }\n }\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 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 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 const now = Math.floor(Date.now() / 1000);\n if (now > cached.expiresAt) {\n await this.clearImageMetadata(transactionId);\n return null;\n }\n\n return cached.metadata;\n } catch (error) {\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 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;\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 }\n }\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 if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n console.error(`Error clearing image metadata cache for ${transactionId}:`, error);\n }\n }\n }\n\n}\n","import { MemoryCache } from './MemoryCache';\nimport { FileCache } from './FileCache';\nimport type {\n CachedFeedContent,\n CacheConfig,\n CacheContentKeyOptions,\n} from './types';\nimport type { ImageMetadata } from '../types';\nimport type { FeedFormat } from '../types';\n\n/**\n * Feed cache manager: serialized content + image metadata only.\n * Collection/item caching is owned by @seedprotocol/query.\n */\nexport class CacheManager {\n private memoryCache: MemoryCache;\n private fileCache: FileCache;\n private config: CacheConfig;\n\n constructor(config: CacheConfig) {\n this.config = config;\n this.memoryCache = new MemoryCache(config);\n this.fileCache = new FileCache(config);\n }\n\n getConfig(): CacheConfig {\n return this.config;\n }\n\n /**\n * The cached body for this key, unexpired. With `fingerprint`, only an entry built from the same\n * inputs (see CachedFeedContent.fingerprint); without, any entry (e.g. a stale fallback).\n */\n async getFeedContent(\n schemaName: string,\n format: FeedFormat,\n contentKeyOptions?: CacheContentKeyOptions,\n fingerprint?: string,\n ): Promise<CachedFeedContent | null> {\n if (!this.config.enabled) {\n return null;\n }\n\n try {\n let cached = this.memoryCache.getFeedContent(schemaName, format, contentKeyOptions);\n if (!cached) {\n cached = await this.fileCache.getFeedContent(schemaName, format, contentKeyOptions);\n if (cached) this.memoryCache.putFeedContent(schemaName, format, cached, contentKeyOptions);\n }\n if (!cached) return null;\n if (fingerprint !== undefined && cached.fingerprint !== fingerprint) return null;\n return cached;\n } catch (error) {\n console.error(\n `Error getting feed content cache for ${schemaName}:${format}:`,\n error\n );\n return null;\n }\n }\n\n /** Stores a body with the ETag and fingerprint the caller computed; returns the stored entry. */\n async setFeedContent(\n schemaName: string,\n format: FeedFormat,\n body: { content: string; contentType: string; etag: string; fingerprint: string },\n contentKeyOptions?: CacheContentKeyOptions,\n ): Promise<CachedFeedContent | null> {\n if (!this.config.enabled) {\n return null;\n }\n\n try {\n let ttl = this.config.ttl;\n if (contentKeyOptions?.archive) {\n ttl = this.config.archiveTtl ?? 86400;\n } else if (contentKeyOptions?.page != null && contentKeyOptions.page > 1) {\n ttl = this.config.pageTtl ?? 300;\n }\n const now = Math.floor(Date.now() / 1000);\n const cached: CachedFeedContent = { ...body, lastModified: now, expiresAt: now + ttl };\n this.memoryCache.putFeedContent(schemaName, format, cached, contentKeyOptions);\n await this.fileCache.setFeedContent(schemaName, format, cached, contentKeyOptions);\n return cached;\n } catch (error) {\n console.error(\n `Error setting feed content cache for ${schemaName}:${format}:`,\n error\n );\n return null;\n }\n }\n\n async getImageMetadata(transactionId: string): Promise<ImageMetadata | null> {\n if (!this.config.imageMetadata?.enabled) {\n return null;\n }\n\n try {\n let cached = this.memoryCache.getImageMetadata(transactionId);\n if (cached) {\n return cached;\n }\n\n cached = await this.fileCache.getImageMetadata(transactionId);\n if (cached) {\n this.memoryCache.setImageMetadata(transactionId, cached);\n return cached;\n }\n\n return null;\n } catch (error) {\n console.error(`Error getting image metadata cache for ${transactionId}:`, error);\n return null;\n }\n }\n\n async setImageMetadata(transactionId: string, metadata: ImageMetadata): Promise<void> {\n if (!this.config.imageMetadata?.enabled) {\n return;\n }\n\n try {\n this.memoryCache.setImageMetadata(transactionId, metadata);\n await this.fileCache.setImageMetadata(transactionId, metadata);\n } catch (error) {\n console.error(`Error setting image metadata cache for ${transactionId}:`, error);\n }\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 */\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 // 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 imageMetadata,\n pageTtl,\n archiveTtl,\n };\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 * 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 {\n ensureArweaveReadGatewayForFallback,\n getArweaveReadBaseUrls,\n isGatewayHostCircuitOpen,\n recordGatewayHostFailure,\n recordGatewayHostSuccess,\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 * Whether an Arweave transaction is an image, with its metadata. Gateways are tried in order\n * until one answers (any successful HEAD, image or not); a gateway that errors or returns a\n * non-ok status is skipped. Null when no gateway answered, so the caller can try again later\n * instead of recording the transaction as \"not an image\".\n */\n async detectImage(transactionId: string): Promise<ImageMetadata | null> {\n await ensureArweaveReadGatewayForFallback()\n\n // Preferred gateways first, then resolved / public (this service's own list when unresolved)\n const baseUrls = getArweaveReadBaseUrls({ fallbackHosts: this.config.gateways })\n\n for (const base of baseUrls) {\n if (isGatewayHostCircuitOpen(base)) continue\n const metadata = await this.probe(`${base}/${transactionId}`, base)\n if (metadata) return metadata\n }\n return null\n }\n\n /** Image metadata for a URL; `isImage: false` when it isn't an image or couldn't be read. */\n async getImageMetadata(url: string, hostHint?: string): Promise<ImageMetadata> {\n return (await this.probe(url, hostHint)) ?? { isImage: false, url }\n }\n\n /** The URL's metadata, or null when the host errored or answered with a non-ok status. */\n private async probe(url: string, hostHint?: string): Promise<ImageMetadata | null> {\n const host =\n hostHint ??\n (() => {\n try {\n return new URL(url).host\n } catch {\n return url\n }\n })()\n\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 // HTTP error from a reachable host — do not trip the circuit\n return null\n }\n\n recordGatewayHostSuccess(host)\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 recordGatewayHostFailure(host)\n console.warn(`Error fetching image metadata from ${url}:`, error)\n return null\n }\n }\n\n /**\n * Fetch image metadata from full request (fallback when range requests fail)\n */\n private async getImageMetadataFromFullRequest(\n url: string,\n contentType: string,\n _contentLength: string | null\n ): Promise<ImageMetadata> {\n try {\n const response = await this.fetchWithTimeout(url)\n if (!response.ok) {\n return { isImage: false, url }\n }\n\n const buffer = Buffer.from(await response.arrayBuffer())\n const dimensions = this.extractImageDimensions(buffer, contentType)\n const format = this.getImageFormat(contentType, buffer)\n\n return {\n isImage: true,\n url,\n mimeType: contentType,\n width: dimensions.width,\n height: dimensions.height,\n size: buffer.length,\n format,\n }\n } catch (error) {\n console.warn(`Error in full request for ${url}:`, error)\n return { isImage: false, url }\n }\n }\n\n /**\n * Validate if Content-Type indicates an image\n */\n private isImageContentType(contentType: string): boolean {\n if (!contentType) return false\n\n const normalized = (contentType.toLowerCase().split(';')[0] ?? '').trim()\n return normalized.startsWith('image/')\n }\n\n /**\n * Extract image dimensions from buffer\n */\n private extractImageDimensions(\n buffer: Buffer,\n _contentType: string\n ): { width: number; height: number } {\n try {\n const dimensions = sizeOf(buffer)\n if (dimensions.width && dimensions.height) {\n return {\n width: dimensions.width,\n height: dimensions.height,\n }\n }\n } catch (error) {\n // image-size might fail for some formats or corrupted data\n console.warn(`Failed to extract dimensions:`, error)\n }\n\n // Return default if extraction failed\n return { width: 0, height: 0 }\n }\n\n /**\n * Get image format from Content-Type or buffer analysis\n */\n private getImageFormat(contentType: string, buffer: Buffer): string | undefined {\n // Try to get format from Content-Type first\n if (contentType) {\n const mimeMatch = contentType.toLowerCase().match(/image\\/([^;]+)/)\n if (mimeMatch && mimeMatch[1]) {\n const format = mimeMatch[1].toLowerCase()\n // Normalize common formats\n if (format === 'jpeg') return 'jpeg'\n if (format === 'png') return 'png'\n if (format === 'gif') return 'gif'\n if (format === 'webp') return 'webp'\n if (format === 'svg+xml') return 'svg'\n return format\n }\n }\n\n // Try to detect from buffer magic bytes\n try {\n const dimensions = sizeOf(buffer)\n if (dimensions.type) {\n return dimensions.type.toLowerCase()\n }\n } catch {\n // Ignore errors\n }\n\n return undefined\n }\n\n /**\n * Fetch with timeout\n */\n private async fetchWithTimeout(\n url: string,\n options: RequestInit = {}\n ): Promise<Response> {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), this.config.timeout)\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n })\n clearTimeout(timeoutId)\n return response\n } catch (error) {\n clearTimeout(timeoutId)\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Request timeout after ${this.config.timeout}ms`)\n }\n throw error\n }\n }\n}\n","import debug from 'debug'\n\n/** Per-request feed logs (cache hits and misses, item counts, image probes) under `DEBUG=seedFeed`. */\nexport const feedDebug = debug('seedFeed')\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 richBodyModelPriority,\n getFieldStorageModels,\n getListElementStorageModels,\n isRichBodyStorageSchema,\n} from '@seedprotocol/query'\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 = richBodyModelPriority(a.model) - richBodyModelPriority(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 = getFieldStorageModels(item)\n const listModels = getListElementStorageModels(item)\n let best: RichBodyCandidate | null = null\n\n if (fieldModels) {\n for (const [key, model] of Object.entries(fieldModels)) {\n if (!isRichBodyStorageSchema(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 (!isRichBodyStorageSchema(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 = getFieldStorageModels(item)\n if (fieldModels) {\n for (const [key, model] of Object.entries(fieldModels)) {\n if (!isRichBodyStorageSchema(model)) continue\n if (stringFieldContainsDataUriImage(item[key])) return true\n }\n }\n const listModels = getListElementStorageModels(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 (!isRichBodyStorageSchema(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 { fetchFeedItemsBySchemaName, getFeedItemsBySchemaNameForMonth } from './getFeedItems';\n\nexport {\n fetchFeedItemsBySchemaName,\n getFeedItemsBySchemaName,\n getFeedItemsBySchemaNameForMonth,\n} 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, FeedItem } from './types';\nexport {\n initializeFeedPlatform,\n teardownFeedPlatform,\n DEFAULT_ARWEAVE_HOST,\n} from './bootstrap';\nexport {\n classifyMediaRef,\n normalizeFeedItemFields,\n getFeedItemStringField,\n isEasAttestationExplorerUrl,\n resolveSeedRssImageRelationRef,\n resolveSeedRssImageRelationFromItem,\n SEED_RSS_IMAGE_RELATION_FIELD_KEYS,\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 SeedRssImageRelationRef,\n} from '@seedprotocol/arweave';\nimport pluralize from 'pluralize';\nimport type { FeedFormat, FeedItem, 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, generateETag } from './utils/etag';\nimport { buildContentKey, type CachedFeedContent, type CacheContentKeyOptions } from './cache/types';\nimport { ArweaveImageService } from './services/arweaveImageService';\nimport { feedDebug } from './debug';\nimport { getQueryCacheManager, isArweaveTransactionGatewayUrl } from '@seedprotocol/query';\nimport { resolveSeedRssImageRelationRef } from '@seedprotocol/arweave';\nimport {\n pickFeedItemContent,\n pickFeedItemDescription,\n filterItemsByRichTextDataUriImagePolicy,\n} from './pickFeedItemRichText';\n\nexport {\n pickFeedItemContent,\n pickFeedItemDescription,\n feedItemRichTextContainsDataUriImage,\n filterItemsByRichTextDataUriImagePolicy,\n} from './pickFeedItemRichText';\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 return resolveSeedRssImageRelationRef(value)?.mediaUrl\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/** Image probes in flight at once while enriching a page of items. */\nconst ENRICH_CONCURRENCY = 8\n\nasync function runWithConcurrency(tasks: Array<() => Promise<void>>, limit: number): Promise<void> {\n let next = 0\n const worker = async (): Promise<void> => {\n while (next < tasks.length) {\n const task = tasks[next++]!\n await task()\n }\n }\n await Promise.all(Array.from({ length: Math.min(limit, tasks.length) }, worker))\n}\n\n/** Cached metadata for an Arweave transaction, probing it on a miss. */\nasync function imageMetadataFor(\n transactionId: string,\n imageService: ArweaveImageService,\n cache: CacheManager,\n): Promise<ImageMetadata | null> {\n const cached = await cache.getImageMetadata(transactionId)\n if (cached) return cached\n feedDebug('detecting image for transaction %s', transactionId)\n const metadata = await imageService.detectImage(transactionId)\n // No gateway answered: leave it uncached so the next request tries again. Otherwise cached even\n // when it isn't an image, to avoid probing again.\n if (metadata) await cache.setImageMetadata(transactionId, metadata)\n return metadata\n}\n\n/** The transaction id in an Arweave gateway URL (`https://<gateway>/<txId>`), else undefined. */\nfunction arweaveTransactionIdOf(url: string): string | undefined {\n if (!isArweaveTransactionGatewayUrl(url)) return undefined\n return new URL(url).pathname.replace(/^\\//, '')\n}\n\n/**\n * The Arweave transaction whose metadata fills in an item's image enclosure: an `image` item's\n * own storage transaction; for other schemas, the image `extractRssImageCandidate` picks (a\n * feature or cover image), when it is an Arweave transaction URL and lacks its size or type.\n */\nfunction enclosureTransactionOf(item: FeedItem, schemaName: string): string | undefined {\n const record = item as Record<string, unknown>\n if (schemaName === 'image') {\n return (record.storageTransactionId || record.storage_transaction_id) as string | undefined\n }\n const candidate = extractRssImageCandidate(record)\n if (!candidate || (candidate.width && candidate.height && candidate.mimeType)) return undefined\n return arweaveTransactionIdOf(candidate.url)\n}\n\n/** Adds `_imageMetadata` / `_hasImage` to an item whose enclosure transaction is an image. */\nasync function enrichFeedItemWithMedia(\n item: FeedItem,\n schemaName: string,\n imageService: ArweaveImageService,\n cache: CacheManager,\n): Promise<void> {\n const record = item as Record<string, unknown>\n const transactionId = enclosureTransactionOf(item, schemaName)\n if (!transactionId) return\n try {\n const metadata = await imageMetadataFor(transactionId, imageService, cache)\n if (!metadata?.isImage) return\n // For a related image, keep the URL the item already links to.\n const candidate = schemaName === 'image' ? undefined : extractRssImageCandidate(record)\n record._imageMetadata = candidate ? { ...metadata, url: candidate.url } : metadata\n record._hasImage = true\n } catch (error) {\n console.warn(`Error enriching item with transaction ${transactionId}:`, error)\n }\n}\n\n/** Enriches items in place with image metadata (see enclosureTransactionOf). */\nasync function enrichFeedItemsWithMedia(\n items: FeedItem[],\n schemaName: string,\n imageService: ArweaveImageService,\n cache: CacheManager,\n): Promise<FeedItem[]> {\n const toEnrich = items.filter((item) => enclosureTransactionOf(item, schemaName))\n feedDebug('enriching %d items with image metadata', toEnrich.length)\n await runWithConcurrency(\n toEnrich.map((item) => () => enrichFeedItemWithMedia(item, schemaName, imageService, cache)),\n ENRICH_CONCURRENCY,\n )\n return items\n}\n\n/** Items with image metadata added when image metadata is enabled (main and archive feeds). */\nasync function withImageMetadata(\n items: FeedItem[],\n schemaName: string,\n cache: CacheManager,\n): Promise<FeedItem[]> {\n const imageMetadata = cache.getConfig().imageMetadata\n if (!imageMetadata?.enabled) return items\n const imageService = new ArweaveImageService({\n gateways: imageMetadata.gateways,\n timeout: imageMetadata.timeout,\n })\n return enrichFeedItemsWithMedia(items, schemaName, imageService, cache)\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: FeedItem[],\n options: TransformOptions\n): any[] {\n const { itemUrlBase, itemUrlPath, richTextDataUriImages = 'omit_items' } = options\n\n const filtered = filterItemsByRichTextDataUriImagePolicy(\n items as Record<string, unknown>[],\n richTextDataUriImages,\n ) as FeedItem[]\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 = `${itemUrlBase.replace(/\\/$/, '')}/${itemUrlPath.replace(/^\\//, '')}/${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: FeedItem[],\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 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/** Hash of what a feed body is built from besides its items' content (see CachedFeedContent). */\nfunction feedFingerprint(inputs: {\n sourceEtag?: string\n siteConfig: FeedConfig\n cacheBust?: string\n}): string {\n return generateETag(\n JSON.stringify([inputs.sourceEtag ?? null, inputs.siteConfig, inputs.cacheBust ?? null]),\n )\n}\n\ntype FeedRequest = {\n schemaName: string\n format: FeedFormat\n contentKeyOptions?: CacheContentKeyOptions\n ifNoneMatch?: string | null\n siteConfig: FeedConfig\n cacheBust?: string\n /** Cache-Control max-age / s-maxage in seconds. */\n maxAge: number\n /**\n * Ask query before serving a cached body (page 1): the body is rebuilt when query's collection\n * ETag changed. Otherwise (later pages, archives) a cached body is served until its TTL ends.\n */\n revalidate: boolean\n /** Items, with query's collection ETag when the collection cache answered. */\n fetchItems: () => Promise<{ items: FeedItem[]; sourceEtag?: string }>\n render: (items: FeedItem[]) => Promise<string>\n}\n\nfunction feedResponse(\n request: FeedRequest,\n body: { content: string | null; contentType?: string; etag: string; lastModified: number },\n status: number,\n extraHeaders: Record<string, string> = {},\n): Response {\n const headers: Record<string, string> = {\n ETag: body.etag,\n 'Last-Modified': new Date(body.lastModified * 1000).toUTCString(),\n 'Cache-Control': `public, max-age=${request.maxAge}, s-maxage=${request.maxAge}, must-revalidate`,\n ...extraHeaders,\n }\n if (status !== 304) {\n headers['Content-Type'] = body.contentType ?? getContentType(request.format)\n headers['X-Feed-Schema'] = request.schemaName\n headers['X-Feed-Format'] = request.format\n }\n return new Response(status === 304 ? null : body.content, { status, headers })\n}\n\n/** A cached body, or 304 when the client already has it. */\nfunction cachedFeedResponse(\n request: FeedRequest,\n cached: CachedFeedContent,\n xCache: string,\n extraHeaders?: Record<string, string>,\n): Response {\n if (request.ifNoneMatch && checkIfNoneMatch(request.ifNoneMatch, cached.etag)) {\n feedDebug('%s for %s:%s, 304', xCache, request.schemaName, request.format)\n return feedResponse(request, { ...cached, content: null }, 304)\n }\n feedDebug('%s for %s:%s', xCache, request.schemaName, request.format)\n return feedResponse(request, cached, 200, { 'X-Cache': xCache, ...extraHeaders })\n}\n\n/**\n * The feed body for a request: from the content cache while it was built from the same inputs,\n * else built from fresh items and cached. The ETag is derived from query's collection ETag when\n * known (so it survives rebuilds of an unchanged feed), else from the body.\n */\nasync function serveFeed(request: FeedRequest): Promise<Response> {\n const { schemaName, format, contentKeyOptions } = request\n const cache = getCacheManager()\n const cacheEnabled = cache.getConfig().enabled\n\n try {\n let fetched: { items: FeedItem[]; sourceEtag?: string } | undefined\n if (request.revalidate) fetched = await request.fetchItems()\n const fingerprint = feedFingerprint({\n sourceEtag: fetched?.sourceEtag,\n siteConfig: request.siteConfig,\n cacheBust: request.cacheBust,\n })\n\n if (cacheEnabled) {\n const cached = await cache.getFeedContent(schemaName, format, contentKeyOptions, fingerprint)\n if (cached) return cachedFeedResponse(request, cached, 'HIT')\n }\n\n fetched ??= await request.fetchItems()\n feedDebug('building %s:%s from %d items', schemaName, format, fetched.items.length)\n const content = await request.render(fetched.items)\n const contentType = getContentType(format)\n const etag = fetched.sourceEtag\n ? generateETag(`${fingerprint}:${buildContentKey(schemaName, format, contentKeyOptions)}`)\n : generateETag(content)\n const stored = cacheEnabled\n ? await cache.setFeedContent(\n schemaName,\n format,\n { content, contentType, etag, fingerprint },\n contentKeyOptions,\n )\n : null\n if (stored) return cachedFeedResponse(request, stored, 'MISS')\n return feedResponse(\n request,\n { content, contentType, etag, lastModified: Math.floor(Date.now() / 1000) },\n 200,\n )\n } catch (error) {\n console.error(`Feed generation error for ${schemaName}:${format}:`, error)\n if (cacheEnabled) {\n const stale = await cache.getFeedContent(schemaName, format, contentKeyOptions)\n if (stale) {\n return cachedFeedResponse(request, stale, 'STALE', { Warning: '110 - \"Response is stale\"' })\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 { status: 500, headers: { 'Content-Type': 'application/json' } },\n )\n }\n}\n\nfunction invalidFormatResponse(formatSegment: string): Response {\n return new Response(JSON.stringify({ error: `Invalid feed format: ${formatSegment}` }), {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n })\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 *\n * Page 1 asks `@seedprotocol/query` on every request (one round when its collection is cached,\n * none within `CACHE_MAX_AGE`) and rebuilds only when the collection changed; its Cache-Control\n * max-age is `CACHE_MAX_AGE`. Later pages are cached for `CACHE_PAGE_TTL`.\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 const format = parseFormat(formatSegment)\n if (!format) return invalidFormatResponse(formatSegment)\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 pageNum = Math.max(1, page ?? 1)\n const pageSize = loadFeedConfig().pageSize\n const cache = getCacheManager()\n let hasNext = false\n\n return serveFeed({\n schemaName,\n format,\n contentKeyOptions: pageNum > 1 ? { page: pageNum } : undefined,\n ifNoneMatch,\n siteConfig,\n cacheBust,\n maxAge: pageNum === 1 ? (getQueryCacheManager().getConfig().maxAge ?? 0) : 3600,\n revalidate: pageNum === 1,\n fetchItems: async () => {\n // One item past the page tells whether there is a next page.\n const { items, etag } = await fetchFeedItemsBySchemaName(schemaName, {\n limit: pageSize + 1,\n skip: (pageNum - 1) * pageSize,\n })\n hasNext = items.length > pageSize\n return { items: items.slice(0, pageSize) as FeedItem[], sourceEtag: etag }\n },\n render: async (items) => {\n const archiveLinks: FeedArchiveLink[] = []\n if (pageNum === 1) {\n // Archive months are UTC, like queryBySchemaForMonth's bounds.\n const now = new Date()\n const prevMonth = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - 1, 1))\n archiveLinks.push({\n rel: 'prev-archive',\n href: `${siteConfig.feedUrl}/${collectionName}/archive/${prevMonth.getUTCFullYear()}/${prevMonth.getUTCMonth() + 1}/${format}`,\n })\n }\n return createFeed(\n await withImageMetadata(items, schemaName, cache),\n schemaName,\n format,\n cacheBust,\n { page: pageNum, pageSize, hasNext, baseUrl: `${siteConfig.feedUrl}/${collectionName}/${format}` },\n archiveLinks.length ? archiveLinks : undefined,\n false,\n undefined,\n siteConfigOverrides,\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 (UTC)\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) return invalidFormatResponse(formatSegment)\n\n const schemaName = pluralize.singular(collectionSegment.toLowerCase())\n const collectionName = pluralize(schemaName)\n const siteConfig = resolveSiteConfig(siteConfigOverrides)\n const cache = getCacheManager()\n\n return serveFeed({\n schemaName,\n format,\n contentKeyOptions: { archive: { year, month } },\n ifNoneMatch,\n siteConfig,\n cacheBust,\n maxAge: 86400,\n revalidate: false,\n fetchItems: async () => ({\n items: (await getFeedItemsBySchemaNameForMonth(schemaName, year, month)) as FeedItem[],\n }),\n render: async (items) => {\n const baseUrl = `${siteConfig.feedUrl}/${collectionName}/${format}`\n const archiveBase = `${siteConfig.feedUrl}/${collectionName}/archive`\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 const nextYear = month === 12 ? year + 1 : year\n const nextMonth = month === 12 ? 1 : month + 1\n const now = new Date()\n if (nextYear < now.getUTCFullYear() || (nextYear === now.getUTCFullYear() && nextMonth <= now.getUTCMonth() + 1)) {\n archiveLinks.push({ rel: 'next-archive', href: `${archiveBase}/${nextYear}/${nextMonth}/${format}` })\n }\n return createFeed(\n await withImageMetadata(items, schemaName, cache),\n schemaName,\n format,\n cacheBust,\n undefined,\n archiveLinks,\n true,\n undefined,\n siteConfigOverrides,\n )\n },\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["formatRfc822Date","fs","resolveSeedRssImageRelationRef","resolveSeedRssImageRelationRef$1","Parser","pluralize","sizeOf","debug","initializeFeedPlatform","initializeQueryPlatform","options","teardownFeedPlatform","teardownQueryPlatform","DEFAULT_SITE_CONFIG","cloneSiteConfig","config","siteConfig","resolveSiteConfig","overrides","base","resolved","getSiteConfig","setSiteConfig","resetSiteConfig","loadFeedConfig","getEasChainDeployment","getEasReadChainId","formatRfc822Date$1","date","timestamp","days","months","day","month","year","setFeedItemDefaults","item","seedUid","storageTransactionIdSnake","storageTransactionIdCamel","storageTransactionId","validSeedUid","defaultLink","itemUrlBase","itemUrlPath","getArweaveUrlForTransaction","error","currentLink","currentGuid","pubDate","setImageRelationCloneForFeed","clone","mediaLink","looksLikeSeedClone","value","rec","withNestedCloneDefaults","copy","nestedUid","nestedSchema","parentSchemaName","applyFeedDefaultsDeep","out","schemaName","key","el","seedRecordsToFeedItems","feedConfig","defaultsOptions","records","record","fetchFeedItemsBySchemaName","limit","skip","queryBySchema","items","getFeedItemsBySchemaName","getFeedItemsBySchemaNameForMonth","queryBySchemaForMonth","toPlainItem","parser","parseRssString","feed","xml","DC_NS","CONTENT_NS","MEDIA_NS","RELATION_NAMESPACES","RELATION_NAMESPACE_URI_BASE","RESERVED_XML_PREFIXES","CAPITALIZED_DUPLICATES","isRedundantCapitalizedKey","RSS_ITEM_ORDER","formatRfc822Date","d","needsCdata","addTextOrCdata","str","parts","i","parent","isExpandedRelationObject","MAX_EXPANDED_RELATION_DEPTH","NON_RELATION_KEYS","normalizeRelationKey","sanitizePrefixToken","rawKey","createUniquePrefix","usedPrefixes","preferredPrefix","suffix","candidate","isRelationArray","entry","buildRelationNamespaceMap","relationNamespaces","namespaceContext","discoveredRelationKeys","isAuthorRelation","sortedDiscoveredRelationKeys","relationKey","getRelationNamespaceContext","singular","getNamespacedChildTagName","localName","serializeExpandedRelation","ele","tagName","k","v","obj","childTagName","depth","childItemTag","addDcCreator","name","serializeItemProperty","guid","val","attrs","itemParent","src","title","enc","dc","dcKey","dcVal","group","groupEle","gk","gv","ln","child","ck","cv","textContent","attr","attrVal","mediaEle","a","authorStr","c","cat","ATOM_NS","FH_NS","generateRssXml","doc","create","rssFeed","rssAttrs","relationNamespace","acc","channel","link","orderedSet","processedKeys","otherKeys","buildContentKey","format","MemoryCache","contentKeyOptions","cached","transactionId","now","FileCache","err","promises","filePart","join","imageMetadataDir","filePath","data","content","CacheManager","fingerprint","ttl","body","metadata","loadCacheConfig","cacheDir","cacheDisabledByEnvVar","cacheEnabledByEnvVar","isDev","enabled","imageMetadata","g","getArweaveReadGatewayHostsForPrimary","resolveArweaveHostFromEnv","BaseArweaveClient","pageTtl","archiveTtl","generateETag","createHash","etagMatches","normalize","etag","etag1","etag2","parseIfNoneMatch","headerValue","checkIfNoneMatch","etags","ifNoneMatchHeader","currentETag","ArweaveImageService","ensureArweaveReadGatewayForFallback","baseUrls","getArweaveReadBaseUrls","isGatewayHostCircuitOpen","url","hostHint","host","headResponse","contentType","contentLength","rangeResponse","buffer","dimensions","recordGatewayHostFailure","response","mimeMatch","controller","timeoutId","feedDebug","firstNonEmptyString","keys","DESCRIPTION_KEYS","CONTENT_PRIMARY_KEYS","CONTENT_FALLBACK_KEYS","compareRichBodyCandidates","richBodyModelPriority","b","pickTypedRichBodyContent","fieldModels","getFieldStorageModels","listModels","getListElementStorageModels","best","model","isRichBodyStorageSchema","cand","listKey","models","arr","n","pickFeedItemDescription","pickFeedItemContent","typed","primary","stringFieldContainsDataUriImage","feedItemTypedRichFieldsContainDataUriImage","feedItemRichTextContainsDataUriImage","filterItemsByRichTextDataUriImagePolicy","richTextDataUriImages","getEnclosureUrlFromImageRelationField","coercePositiveNumber","parsed","extractRssImageCandidateFromRelationFieldValue","extractRssImageCandidate","imageMeta","applyRssImageCandidate","mediaType","imageCandidate","mediaContent","thumbnail","mediaDescription","keywords","w","author","siteAuthor","siteCopyright","cacheManager","getCacheManager","resetCacheManager","ENRICH_CONCURRENCY","runWithConcurrency","next","tasks","task","imageMetadataFor","cache","imageService","arweaveTransactionIdOf","isArweaveTransactionGatewayUrl","enclosureTransactionOf","enrichFeedItemWithMedia","enrichFeedItemsWithMedia","toEnrich","withImageMetadata","transformToFeedItems","itemId","fallbackUrl","itemUrl","pubDateStr","dateValue","description","feedItem","createFeed","siteConfigOverrides","collectionName","feedUrlBase","feedUrl","cacheBust","feedTitle","capitalize","links","pagination","sep","archiveLinks","al","transformedItems","transformOverrides","atomLinks","atomFeed","imageHtml","generateAtomFeed","nextUrl","jsonFeed","jsonItem","jsonResult","generateJsonFeed","l","isArchive","rssItem","timeCreatedDate","getContentType","parseFormat","normalized","segment","feedFingerprint","inputs","feedResponse","headers","request","extraHeaders","status","cachedFeedResponse","xCache","serveFeed","cacheEnabled","fetched","stored","stale","invalidFormatResponse","formatSegment","handleFeedRequest","collectionSegment","pageNum","page","pageSize","hasNext","getQueryCacheManager","prevMonth","handleArchiveFeedRequest","baseUrl","archiveBase","nextYear","nextMonth","DEFAULT_ARWEAVE_HOST","SEED_RSS_IMAGE_RELATION_FIELD_KEYS","classifyMediaRef","getFeedItemStringField","isEasAttestationExplorerUrl","normalizeFeedItemFields","resolveSeedRssImageRelationFromItem","resolveSeedRssImageRelationRef"],"sources":["../src/bootstrap.ts","../src/config.ts","../src/getFeedItems.ts","../src/consume/parseRss.ts","../src/rss/generateRssXml.ts","../src/cache/types.ts","../src/cache/MemoryCache.ts","../src/cache/FileCache.ts","../src/cache/CacheManager.ts","../src/cache/config.ts","../src/utils/etag.ts","../src/services/arweaveImageService.ts","../src/debug.ts","../src/pickFeedItemRichText.ts","../src/index.ts"],"sourcesContent":["import { initializeQueryPlatform, teardownQueryPlatform } from '@seedprotocol/query'\n\nexport type InitializeFeedPlatformOptions = {\n arweaveDomain?: string\n}\n\n/**\n * Make EAS and Arweave usable for feed generation (via `initializeQueryPlatform`). Clients a host\n * app or the SDK already registered are kept; otherwise the Node EAS and Arweave clients are\n * registered. `arweaveDomain` sets the Arweave host.\n */\nexport async function initializeFeedPlatform(\n options?: InitializeFeedPlatformOptions,\n): Promise<void> {\n await initializeQueryPlatform(options)\n}\n\n/** Lets the next initializeFeedPlatform call run again (tests). Registered clients stay. */\nexport async function teardownFeedPlatform(): Promise<void> {\n await teardownQueryPlatform()\n}\n\nexport { DEFAULT_ARWEAVE_HOST } from '@seedprotocol/arweave'\n","import { getEasChainDeployment, getEasReadChainId } from '@seedprotocol/eas';\nimport 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://base.easscan.org).\n * Item links use {base}/attestation/view/{uid}. Default: the EAS explorer of the configured\n * chain (`EAS_CHAIN_ID`, or the SDK / publish chain in-process), else 'https://easscan.org'.\n * - FEED_ITEM_URL_PATH: Path segment for attestation links (default: 'attestation/view'), with\n * FEED_ITEM_URL_BASE or its default.\n * - FEED_PAGE_SIZE: Items per page for paged feeds (default: 25; a value that isn't a positive\n * integer gives the default).\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 pageSize: number;\n richTextDataUriImages: 'omit_items' | 'include_items';\n} {\n const itemUrlBase =\n process.env.FEED_ITEM_URL_BASE?.trim() ||\n getEasChainDeployment(getEasReadChainId())?.explorerUrl ||\n 'https://easscan.org';\n const itemUrlPath =\n process.env.FEED_ITEM_URL_PATH?.trim() || 'attestation/view';\n const pageSize = Math.max(1, parseInt(process.env.FEED_PAGE_SIZE || '25', 10) || 25);\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, pageSize, richTextDataUriImages };\n}\n","import {\n queryBySchema,\n queryBySchemaForMonth,\n getArweaveUrlForTransaction,\n type SeedRecord,\n} from '@seedprotocol/query'\nimport { initializeFeedPlatform } from './bootstrap'\nimport { loadFeedConfig } from './config'\n\ntype SetFeedItemDefaultsOptions = {\n itemUrlBase: string\n itemUrlPath: string\n}\n\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\n/**\n * Set default values for feed items (title, link, guid, pubDate, dual-case keys).\n */\nexport const setFeedItemDefaults = (\n item: Record<string, unknown>,\n seedUid: string,\n schemaName: string,\n options: SetFeedItemDefaultsOptions,\n): void => {\n const { itemUrlBase, itemUrlPath } = options\n\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 validSeedUid =\n seedUid && typeof seedUid === 'string' && seedUid.trim() !== '' ? seedUid : 'unknown'\n const attestationLink = `${itemUrlBase.replace(/\\/$/, '')}/${itemUrlPath.replace(/^\\//, '')}/${validSeedUid}`\n\n let defaultLink = attestationLink\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 }\n }\n\n const currentLink = (item.link || item.Link) as string | undefined\n if (\n !currentLink ||\n currentLink === 'undefined' ||\n (typeof currentLink === 'string' && currentLink.trim() === '')\n ) {\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 (\n !currentGuid ||\n currentGuid === 'undefined' ||\n (typeof currentGuid === 'string' && currentGuid.trim() === '')\n ) {\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\n/**\n * Defaults for expanded Image relation clones in feeds.\n * Does not emit EAS attestation explorer URLs as `link` — only gateway URLs when storage exists.\n */\nconst setImageRelationCloneForFeed = (\n clone: Record<string, unknown>,\n seedUid: string,\n): void => {\n if (!clone.title && !clone.Title) {\n clone.title = seedUid\n clone.Title = seedUid\n } else if (clone.title && !clone.Title) {\n clone.Title = clone.title\n } else if (clone.Title && !clone.title) {\n clone.title = clone.Title\n }\n\n if (clone.timeCreated && !clone.pubDate && !clone.PubDate) {\n const pubDate = formatRfc822Date(clone.timeCreated as number)\n clone.pubDate = pubDate\n clone.PubDate = pubDate\n } else if (clone.pubDate && !clone.PubDate) {\n clone.PubDate = clone.pubDate\n } else if (clone.PubDate && !clone.pubDate) {\n clone.pubDate = clone.PubDate\n }\n\n if (!clone.seedUid && !clone.SeedUid) {\n clone.seedUid = seedUid\n clone.SeedUid = seedUid\n } else if (clone.seedUid && !clone.SeedUid) {\n clone.SeedUid = clone.seedUid\n } else if (clone.SeedUid && !clone.seedUid) {\n clone.seedUid = clone.SeedUid\n }\n\n if (clone.attester && !clone.Attester) {\n clone.Attester = clone.attester\n } else if (clone.Attester && !clone.attester) {\n clone.attester = clone.Attester\n }\n\n const storageTransactionIdSnake =\n clone.storage_transaction_id &&\n typeof clone.storage_transaction_id === 'string' &&\n clone.storage_transaction_id.trim() !== '' &&\n clone.storage_transaction_id !== 'undefined' &&\n clone.storage_transaction_id !== seedUid\n ? (clone.storage_transaction_id as string).trim()\n : null\n const storageTransactionIdCamel =\n clone.storageTransactionId &&\n typeof clone.storageTransactionId === 'string' &&\n clone.storageTransactionId.trim() !== '' &&\n clone.storageTransactionId !== 'undefined' &&\n clone.storageTransactionId !== seedUid\n ? (clone.storageTransactionId as string).trim()\n : null\n const storageTransactionId = storageTransactionIdSnake || storageTransactionIdCamel\n\n delete clone.link\n delete clone.Link\n delete clone.guid\n delete clone.Guid\n\n if (storageTransactionId && storageTransactionId.length > 0) {\n try {\n const mediaLink = getArweaveUrlForTransaction(storageTransactionId)\n clone.link = mediaLink\n clone.Link = mediaLink\n } catch {\n // omit link when gateway URL cannot be built\n }\n }\n}\n\nfunction looksLikeSeedClone(value: unknown): value is Record<string, unknown> {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false\n const rec = value as Record<string, unknown>\n return typeof rec.seedUid === 'string' || typeof rec.SeedUid === 'string'\n}\n\nfunction withNestedCloneDefaults(\n clone: Record<string, unknown>,\n parentSchemaName: string,\n options: SetFeedItemDefaultsOptions,\n): Record<string, unknown> {\n const copy = { ...clone }\n const nestedUid = (copy.seedUid || copy.SeedUid || '') as string\n const nestedSchema =\n typeof copy.schemaName === 'string' ? copy.schemaName : parentSchemaName\n if (nestedSchema === 'image') {\n setImageRelationCloneForFeed(copy, nestedUid)\n } else {\n setFeedItemDefaults(copy, nestedUid, nestedSchema, options)\n }\n return copy\n}\n\n/**\n * Apply feed defaults to a root item and nested expanded seed clones.\n * Image relation clones get media-only link defaults (no EAS explorer URLs).\n *\n * Returns a copy and leaves `item` untouched: `item` may be a record held by the\n * `@seedprotocol/query` collection cache. Only the objects the defaults write to\n * (the root, expanded seed clones, and arrays holding them) are shallow-copied;\n * other values, notably hydrated HTML strings, are shared rather than cloned.\n */\nfunction applyFeedDefaultsDeep(\n item: Record<string, unknown>,\n schemaName: string,\n options: SetFeedItemDefaultsOptions,\n): Record<string, unknown> {\n const out = { ...item }\n const seedUid = (out.seedUid || out.SeedUid || '') as string\n setFeedItemDefaults(out, seedUid, schemaName, options)\n\n for (const key of Object.keys(out)) {\n if (key.startsWith('_')) continue\n const value = out[key]\n if (Array.isArray(value)) {\n if (value.some(looksLikeSeedClone)) {\n out[key] = value.map((el) =>\n looksLikeSeedClone(el) ? withNestedCloneDefaults(el, schemaName, options) : el,\n )\n }\n } else if (looksLikeSeedClone(value)) {\n out[key] = withNestedCloneDefaults(value, schemaName, options)\n }\n }\n return out\n}\n\nfunction seedRecordsToFeedItems(\n schemaName: string,\n records: SeedRecord[],\n): Record<string, unknown>[] {\n const feedConfig = loadFeedConfig()\n const defaultsOptions: SetFeedItemDefaultsOptions = {\n itemUrlBase: feedConfig.itemUrlBase,\n itemUrlPath: feedConfig.itemUrlPath,\n }\n\n return records.map((record) =>\n applyFeedDefaultsDeep(record.data, schemaName, defaultsOptions),\n )\n}\n\n/**\n * Feed items for a page of the schema, with query's collection ETag when the collection cache\n * answered (skip 0, cache enabled): it changes whenever a listed seed or what it was built from\n * changes, so the feed can tell whether a cached body is still current.\n */\nexport const fetchFeedItemsBySchemaName = async (\n schemaName: string,\n options?: { limit?: number; skip?: number },\n): Promise<{ items: Record<string, unknown>[]; etag?: string }> => {\n await initializeFeedPlatform()\n const limit = options?.limit ?? 100\n const skip = options?.skip ?? 0\n\n // Expanded relations give items their author names and image enclosures.\n const { items, etag } = await queryBySchema(schemaName, {\n limit,\n skip,\n relations: 'expand',\n hydrateStorage: true,\n })\n return { items: seedRecordsToFeedItems(schemaName, items), etag }\n}\n\nexport const getFeedItemsBySchemaName = async (\n schemaName: string,\n options?: { limit?: number; skip?: number },\n): Promise<Record<string, unknown>[]> =>\n (await fetchFeedItemsBySchemaName(schemaName, options)).items\n\nexport const getFeedItemsBySchemaNameForMonth = async (\n schemaName: string,\n year: number,\n month: number,\n): Promise<Record<string, unknown>[]> => {\n await initializeFeedPlatform()\n\n const records = await queryBySchemaForMonth(schemaName, year, month, {\n relations: 'expand',\n hydrateStorage: true,\n })\n return seedRecordsToFeedItems(schemaName, records)\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 { ImageMetadata } from '../types';\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 * What the body was built from (query's collection ETag when known, site config, cache-bust\n * value; see feedFingerprint). A read that passes a fingerprint only gets an entry built from\n * the same inputs. Entries written before fingerprints existed have none and never match.\n */\n fingerprint?: string;\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 (serialized content + image metadata).\n * Collection/item caching lives in @seedprotocol/query.\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 imageMetadata?: ImageMetadataConfig\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","import type { CachedFeedContent, CacheConfig, CacheContentKeyOptions, CachedImageMetadata } from './types';\nimport { buildContentKey } from './types';\nimport type { ImageMetadata } from '../types';\n\n/**\n * In-memory cache for serialized feed content and image metadata.\n * Collection/item caching is owned by @seedprotocol/query.\n */\nexport class MemoryCache {\n private feedContentCache: Map<string, CachedFeedContent> = new Map();\n private imageMetadataCache: Map<string, CachedImageMetadata> = new Map();\n private config: CacheConfig;\n\n constructor(config: CacheConfig) {\n this.config = config;\n }\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 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 /** Stores an entry as it is (ETag, lastModified and expiry kept), e.g. one read from disk. */\n putFeedContent(\n schemaName: string,\n format: string,\n cached: CachedFeedContent,\n contentKeyOptions?: CacheContentKeyOptions,\n ): void {\n this.feedContentCache.set(buildContentKey(schemaName, format, contentKeyOptions), cached);\n }\n\n getImageMetadata(transactionId: string): ImageMetadata | null {\n const cached = this.imageMetadataCache.get(transactionId);\n if (!cached) {\n return null;\n }\n\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 setImageMetadata(transactionId: string, metadata: ImageMetadata): void {\n const now = Math.floor(Date.now() / 1000);\n const imageMetadataTtl = this.config.imageMetadata?.ttl || 604800;\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","import { promises as fs } from 'fs';\nimport { join } from 'path';\nimport type { CachedFeedContent, CacheConfig, CacheContentKeyOptions, CachedImageMetadata } from './types';\nimport { buildContentKey } from './types';\nimport type { ImageMetadata } from '../types';\n\n/**\n * File-based persistent cache for serialized feed content and image metadata.\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 this.ensureCacheDir().catch(err => {\n console.error('Failed to create cache directory:', err);\n });\n }\n\n private async ensureCacheDir(): Promise<void> {\n try {\n await fs.mkdir(this.cacheDir, { recursive: true });\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {\n throw error;\n }\n }\n }\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 private getImageMetadataPath(transactionId: string): string {\n const imageMetadataDir = join(this.cacheDir, 'image-metadata');\n return join(imageMetadataDir, `${transactionId}.json`);\n }\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 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 const now = Math.floor(Date.now() / 1000);\n if (now > cached.expiresAt) {\n await this.clearFeedContent(schemaName, format, contentKeyOptions);\n return null;\n }\n\n return cached;\n } catch (error) {\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 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 }\n }\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 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 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 const now = Math.floor(Date.now() / 1000);\n if (now > cached.expiresAt) {\n await this.clearImageMetadata(transactionId);\n return null;\n }\n\n return cached.metadata;\n } catch (error) {\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 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;\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 }\n }\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 if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n console.error(`Error clearing image metadata cache for ${transactionId}:`, error);\n }\n }\n }\n\n}\n","import { MemoryCache } from './MemoryCache';\nimport { FileCache } from './FileCache';\nimport type {\n CachedFeedContent,\n CacheConfig,\n CacheContentKeyOptions,\n} from './types';\nimport type { ImageMetadata } from '../types';\nimport type { FeedFormat } from '../types';\n\n/**\n * Feed cache manager: serialized content + image metadata only.\n * Collection/item caching is owned by @seedprotocol/query.\n */\nexport class CacheManager {\n private memoryCache: MemoryCache;\n private fileCache: FileCache;\n private config: CacheConfig;\n\n constructor(config: CacheConfig) {\n this.config = config;\n this.memoryCache = new MemoryCache(config);\n this.fileCache = new FileCache(config);\n }\n\n getConfig(): CacheConfig {\n return this.config;\n }\n\n /**\n * The cached body for this key, unexpired. With `fingerprint`, only an entry built from the same\n * inputs (see CachedFeedContent.fingerprint); without, any entry (e.g. a stale fallback).\n */\n async getFeedContent(\n schemaName: string,\n format: FeedFormat,\n contentKeyOptions?: CacheContentKeyOptions,\n fingerprint?: string,\n ): Promise<CachedFeedContent | null> {\n if (!this.config.enabled) {\n return null;\n }\n\n try {\n let cached = this.memoryCache.getFeedContent(schemaName, format, contentKeyOptions);\n if (!cached) {\n cached = await this.fileCache.getFeedContent(schemaName, format, contentKeyOptions);\n if (cached) this.memoryCache.putFeedContent(schemaName, format, cached, contentKeyOptions);\n }\n if (!cached) return null;\n if (fingerprint !== undefined && cached.fingerprint !== fingerprint) return null;\n return cached;\n } catch (error) {\n console.error(\n `Error getting feed content cache for ${schemaName}:${format}:`,\n error\n );\n return null;\n }\n }\n\n /** Stores a body with the ETag and fingerprint the caller computed; returns the stored entry. */\n async setFeedContent(\n schemaName: string,\n format: FeedFormat,\n body: { content: string; contentType: string; etag: string; fingerprint: string },\n contentKeyOptions?: CacheContentKeyOptions,\n ): Promise<CachedFeedContent | null> {\n if (!this.config.enabled) {\n return null;\n }\n\n try {\n let ttl = this.config.ttl;\n if (contentKeyOptions?.archive) {\n ttl = this.config.archiveTtl ?? 86400;\n } else if (contentKeyOptions?.page != null && contentKeyOptions.page > 1) {\n ttl = this.config.pageTtl ?? 300;\n }\n const now = Math.floor(Date.now() / 1000);\n const cached: CachedFeedContent = { ...body, lastModified: now, expiresAt: now + ttl };\n this.memoryCache.putFeedContent(schemaName, format, cached, contentKeyOptions);\n await this.fileCache.setFeedContent(schemaName, format, cached, contentKeyOptions);\n return cached;\n } catch (error) {\n console.error(\n `Error setting feed content cache for ${schemaName}:${format}:`,\n error\n );\n return null;\n }\n }\n\n async getImageMetadata(transactionId: string): Promise<ImageMetadata | null> {\n if (!this.config.imageMetadata?.enabled) {\n return null;\n }\n\n try {\n let cached = this.memoryCache.getImageMetadata(transactionId);\n if (cached) {\n return cached;\n }\n\n cached = await this.fileCache.getImageMetadata(transactionId);\n if (cached) {\n this.memoryCache.setImageMetadata(transactionId, cached);\n return cached;\n }\n\n return null;\n } catch (error) {\n console.error(`Error getting image metadata cache for ${transactionId}:`, error);\n return null;\n }\n }\n\n async setImageMetadata(transactionId: string, metadata: ImageMetadata): Promise<void> {\n if (!this.config.imageMetadata?.enabled) {\n return;\n }\n\n try {\n this.memoryCache.setImageMetadata(transactionId, metadata);\n await this.fileCache.setImageMetadata(transactionId, metadata);\n } catch (error) {\n console.error(`Error setting image metadata cache for ${transactionId}:`, error);\n }\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 */\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 // 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 imageMetadata,\n pageTtl,\n archiveTtl,\n };\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 * 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 {\n ensureArweaveReadGatewayForFallback,\n getArweaveReadBaseUrls,\n isGatewayHostCircuitOpen,\n recordGatewayHostFailure,\n recordGatewayHostSuccess,\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 * Whether an Arweave transaction is an image, with its metadata. Gateways are tried in order\n * until one answers (any successful HEAD, image or not); a gateway that errors or returns a\n * non-ok status is skipped. Null when no gateway answered, so the caller can try again later\n * instead of recording the transaction as \"not an image\".\n */\n async detectImage(transactionId: string): Promise<ImageMetadata | null> {\n await ensureArweaveReadGatewayForFallback()\n\n // Preferred gateways first, then resolved / public (this service's own list when unresolved)\n const baseUrls = getArweaveReadBaseUrls({ fallbackHosts: this.config.gateways })\n\n for (const base of baseUrls) {\n if (isGatewayHostCircuitOpen(base)) continue\n const metadata = await this.probe(`${base}/${transactionId}`, base)\n if (metadata) return metadata\n }\n return null\n }\n\n /** Image metadata for a URL; `isImage: false` when it isn't an image or couldn't be read. */\n async getImageMetadata(url: string, hostHint?: string): Promise<ImageMetadata> {\n return (await this.probe(url, hostHint)) ?? { isImage: false, url }\n }\n\n /** The URL's metadata, or null when the host errored or answered with a non-ok status. */\n private async probe(url: string, hostHint?: string): Promise<ImageMetadata | null> {\n const host =\n hostHint ??\n (() => {\n try {\n return new URL(url).host\n } catch {\n return url\n }\n })()\n\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 // HTTP error from a reachable host — do not trip the circuit\n return null\n }\n\n recordGatewayHostSuccess(host)\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 recordGatewayHostFailure(host)\n console.warn(`Error fetching image metadata from ${url}:`, error)\n return null\n }\n }\n\n /**\n * Fetch image metadata from full request (fallback when range requests fail)\n */\n private async getImageMetadataFromFullRequest(\n url: string,\n contentType: string,\n _contentLength: string | null\n ): Promise<ImageMetadata> {\n try {\n const response = await this.fetchWithTimeout(url)\n if (!response.ok) {\n return { isImage: false, url }\n }\n\n const buffer = Buffer.from(await response.arrayBuffer())\n const dimensions = this.extractImageDimensions(buffer, contentType)\n const format = this.getImageFormat(contentType, buffer)\n\n return {\n isImage: true,\n url,\n mimeType: contentType,\n width: dimensions.width,\n height: dimensions.height,\n size: buffer.length,\n format,\n }\n } catch (error) {\n console.warn(`Error in full request for ${url}:`, error)\n return { isImage: false, url }\n }\n }\n\n /**\n * Validate if Content-Type indicates an image\n */\n private isImageContentType(contentType: string): boolean {\n if (!contentType) return false\n\n const normalized = (contentType.toLowerCase().split(';')[0] ?? '').trim()\n return normalized.startsWith('image/')\n }\n\n /**\n * Extract image dimensions from buffer\n */\n private extractImageDimensions(\n buffer: Buffer,\n _contentType: string\n ): { width: number; height: number } {\n try {\n const dimensions = sizeOf(buffer)\n if (dimensions.width && dimensions.height) {\n return {\n width: dimensions.width,\n height: dimensions.height,\n }\n }\n } catch (error) {\n // image-size might fail for some formats or corrupted data\n console.warn(`Failed to extract dimensions:`, error)\n }\n\n // Return default if extraction failed\n return { width: 0, height: 0 }\n }\n\n /**\n * Get image format from Content-Type or buffer analysis\n */\n private getImageFormat(contentType: string, buffer: Buffer): string | undefined {\n // Try to get format from Content-Type first\n if (contentType) {\n const mimeMatch = contentType.toLowerCase().match(/image\\/([^;]+)/)\n if (mimeMatch && mimeMatch[1]) {\n const format = mimeMatch[1].toLowerCase()\n // Normalize common formats\n if (format === 'jpeg') return 'jpeg'\n if (format === 'png') return 'png'\n if (format === 'gif') return 'gif'\n if (format === 'webp') return 'webp'\n if (format === 'svg+xml') return 'svg'\n return format\n }\n }\n\n // Try to detect from buffer magic bytes\n try {\n const dimensions = sizeOf(buffer)\n if (dimensions.type) {\n return dimensions.type.toLowerCase()\n }\n } catch {\n // Ignore errors\n }\n\n return undefined\n }\n\n /**\n * Fetch with timeout\n */\n private async fetchWithTimeout(\n url: string,\n options: RequestInit = {}\n ): Promise<Response> {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), this.config.timeout)\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n })\n clearTimeout(timeoutId)\n return response\n } catch (error) {\n clearTimeout(timeoutId)\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Request timeout after ${this.config.timeout}ms`)\n }\n throw error\n }\n }\n}\n","import debug from 'debug'\n\n/** Per-request feed logs (cache hits and misses, item counts, image probes) under `DEBUG=seedFeed`. */\nexport const feedDebug = debug('seedFeed')\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 richBodyModelPriority,\n getFieldStorageModels,\n getListElementStorageModels,\n isRichBodyStorageSchema,\n} from '@seedprotocol/query'\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 = richBodyModelPriority(a.model) - richBodyModelPriority(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 = getFieldStorageModels(item)\n const listModels = getListElementStorageModels(item)\n let best: RichBodyCandidate | null = null\n\n if (fieldModels) {\n for (const [key, model] of Object.entries(fieldModels)) {\n if (!isRichBodyStorageSchema(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 (!isRichBodyStorageSchema(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 = getFieldStorageModels(item)\n if (fieldModels) {\n for (const [key, model] of Object.entries(fieldModels)) {\n if (!isRichBodyStorageSchema(model)) continue\n if (stringFieldContainsDataUriImage(item[key])) return true\n }\n }\n const listModels = getListElementStorageModels(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 (!isRichBodyStorageSchema(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 { fetchFeedItemsBySchemaName, getFeedItemsBySchemaNameForMonth } from './getFeedItems';\n\nexport {\n fetchFeedItemsBySchemaName,\n getFeedItemsBySchemaName,\n getFeedItemsBySchemaNameForMonth,\n} 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, FeedItem } from './types';\nexport {\n initializeFeedPlatform,\n teardownFeedPlatform,\n DEFAULT_ARWEAVE_HOST,\n} from './bootstrap';\nexport {\n classifyMediaRef,\n normalizeFeedItemFields,\n getFeedItemStringField,\n isEasAttestationExplorerUrl,\n resolveSeedRssImageRelationRef,\n resolveSeedRssImageRelationFromItem,\n SEED_RSS_IMAGE_RELATION_FIELD_KEYS,\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 SeedRssImageRelationRef,\n} from '@seedprotocol/arweave';\nimport pluralize from 'pluralize';\nimport type { FeedFormat, FeedItem, 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, generateETag } from './utils/etag';\nimport { buildContentKey, type CachedFeedContent, type CacheContentKeyOptions } from './cache/types';\nimport { ArweaveImageService } from './services/arweaveImageService';\nimport { feedDebug } from './debug';\nimport { getQueryCacheManager, isArweaveTransactionGatewayUrl } from '@seedprotocol/query';\nimport { resolveSeedRssImageRelationRef } from '@seedprotocol/arweave';\nimport {\n pickFeedItemContent,\n pickFeedItemDescription,\n filterItemsByRichTextDataUriImagePolicy,\n} from './pickFeedItemRichText';\n\nexport {\n pickFeedItemContent,\n pickFeedItemDescription,\n feedItemRichTextContainsDataUriImage,\n filterItemsByRichTextDataUriImagePolicy,\n} from './pickFeedItemRichText';\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 return resolveSeedRssImageRelationRef(value)?.mediaUrl\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/** Image probes in flight at once while enriching a page of items. */\nconst ENRICH_CONCURRENCY = 8\n\nasync function runWithConcurrency(tasks: Array<() => Promise<void>>, limit: number): Promise<void> {\n let next = 0\n const worker = async (): Promise<void> => {\n while (next < tasks.length) {\n const task = tasks[next++]!\n await task()\n }\n }\n await Promise.all(Array.from({ length: Math.min(limit, tasks.length) }, worker))\n}\n\n/** Cached metadata for an Arweave transaction, probing it on a miss. */\nasync function imageMetadataFor(\n transactionId: string,\n imageService: ArweaveImageService,\n cache: CacheManager,\n): Promise<ImageMetadata | null> {\n const cached = await cache.getImageMetadata(transactionId)\n if (cached) return cached\n feedDebug('detecting image for transaction %s', transactionId)\n const metadata = await imageService.detectImage(transactionId)\n // No gateway answered: leave it uncached so the next request tries again. Otherwise cached even\n // when it isn't an image, to avoid probing again.\n if (metadata) await cache.setImageMetadata(transactionId, metadata)\n return metadata\n}\n\n/** The transaction id in an Arweave gateway URL (`https://<gateway>/<txId>`), else undefined. */\nfunction arweaveTransactionIdOf(url: string): string | undefined {\n if (!isArweaveTransactionGatewayUrl(url)) return undefined\n return new URL(url).pathname.replace(/^\\//, '')\n}\n\n/**\n * The Arweave transaction whose metadata fills in an item's image enclosure: an `image` item's\n * own storage transaction; for other schemas, the image `extractRssImageCandidate` picks (a\n * feature or cover image), when it is an Arweave transaction URL and lacks its size or type.\n */\nfunction enclosureTransactionOf(item: FeedItem, schemaName: string): string | undefined {\n const record = item as Record<string, unknown>\n if (schemaName === 'image') {\n return (record.storageTransactionId || record.storage_transaction_id) as string | undefined\n }\n const candidate = extractRssImageCandidate(record)\n if (!candidate || (candidate.width && candidate.height && candidate.mimeType)) return undefined\n return arweaveTransactionIdOf(candidate.url)\n}\n\n/** Adds `_imageMetadata` / `_hasImage` to an item whose enclosure transaction is an image. */\nasync function enrichFeedItemWithMedia(\n item: FeedItem,\n schemaName: string,\n imageService: ArweaveImageService,\n cache: CacheManager,\n): Promise<void> {\n const record = item as Record<string, unknown>\n const transactionId = enclosureTransactionOf(item, schemaName)\n if (!transactionId) return\n try {\n const metadata = await imageMetadataFor(transactionId, imageService, cache)\n if (!metadata?.isImage) return\n // For a related image, keep the URL the item already links to.\n const candidate = schemaName === 'image' ? undefined : extractRssImageCandidate(record)\n record._imageMetadata = candidate ? { ...metadata, url: candidate.url } : metadata\n record._hasImage = true\n } catch (error) {\n console.warn(`Error enriching item with transaction ${transactionId}:`, error)\n }\n}\n\n/** Enriches items in place with image metadata (see enclosureTransactionOf). */\nasync function enrichFeedItemsWithMedia(\n items: FeedItem[],\n schemaName: string,\n imageService: ArweaveImageService,\n cache: CacheManager,\n): Promise<FeedItem[]> {\n const toEnrich = items.filter((item) => enclosureTransactionOf(item, schemaName))\n feedDebug('enriching %d items with image metadata', toEnrich.length)\n await runWithConcurrency(\n toEnrich.map((item) => () => enrichFeedItemWithMedia(item, schemaName, imageService, cache)),\n ENRICH_CONCURRENCY,\n )\n return items\n}\n\n/** Items with image metadata added when image metadata is enabled (main and archive feeds). */\nasync function withImageMetadata(\n items: FeedItem[],\n schemaName: string,\n cache: CacheManager,\n): Promise<FeedItem[]> {\n const imageMetadata = cache.getConfig().imageMetadata\n if (!imageMetadata?.enabled) return items\n const imageService = new ArweaveImageService({\n gateways: imageMetadata.gateways,\n timeout: imageMetadata.timeout,\n })\n return enrichFeedItemsWithMedia(items, schemaName, imageService, cache)\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: FeedItem[],\n options: TransformOptions\n): any[] {\n const { itemUrlBase, itemUrlPath, richTextDataUriImages = 'omit_items' } = options\n\n const filtered = filterItemsByRichTextDataUriImagePolicy(\n items as Record<string, unknown>[],\n richTextDataUriImages,\n ) as FeedItem[]\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 = `${itemUrlBase.replace(/\\/$/, '')}/${itemUrlPath.replace(/^\\//, '')}/${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: FeedItem[],\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 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/** Hash of what a feed body is built from besides its items' content (see CachedFeedContent). */\nfunction feedFingerprint(inputs: {\n sourceEtag?: string\n siteConfig: FeedConfig\n cacheBust?: string\n}): string {\n return generateETag(\n JSON.stringify([inputs.sourceEtag ?? null, inputs.siteConfig, inputs.cacheBust ?? null]),\n )\n}\n\ntype FeedRequest = {\n schemaName: string\n format: FeedFormat\n contentKeyOptions?: CacheContentKeyOptions\n ifNoneMatch?: string | null\n siteConfig: FeedConfig\n cacheBust?: string\n /** Cache-Control max-age / s-maxage in seconds. */\n maxAge: number\n /**\n * Ask query before serving a cached body (page 1): the body is rebuilt when query's collection\n * ETag changed. Otherwise (later pages, archives) a cached body is served until its TTL ends.\n */\n revalidate: boolean\n /** Items, with query's collection ETag when the collection cache answered. */\n fetchItems: () => Promise<{ items: FeedItem[]; sourceEtag?: string }>\n render: (items: FeedItem[]) => Promise<string>\n}\n\nfunction feedResponse(\n request: FeedRequest,\n body: { content: string | null; contentType?: string; etag: string; lastModified: number },\n status: number,\n extraHeaders: Record<string, string> = {},\n): Response {\n const headers: Record<string, string> = {\n ETag: body.etag,\n 'Last-Modified': new Date(body.lastModified * 1000).toUTCString(),\n 'Cache-Control': `public, max-age=${request.maxAge}, s-maxage=${request.maxAge}, must-revalidate`,\n ...extraHeaders,\n }\n if (status !== 304) {\n headers['Content-Type'] = body.contentType ?? getContentType(request.format)\n headers['X-Feed-Schema'] = request.schemaName\n headers['X-Feed-Format'] = request.format\n }\n return new Response(status === 304 ? null : body.content, { status, headers })\n}\n\n/** A cached body, or 304 when the client already has it. */\nfunction cachedFeedResponse(\n request: FeedRequest,\n cached: CachedFeedContent,\n xCache: string,\n extraHeaders?: Record<string, string>,\n): Response {\n if (request.ifNoneMatch && checkIfNoneMatch(request.ifNoneMatch, cached.etag)) {\n feedDebug('%s for %s:%s, 304', xCache, request.schemaName, request.format)\n return feedResponse(request, { ...cached, content: null }, 304)\n }\n feedDebug('%s for %s:%s', xCache, request.schemaName, request.format)\n return feedResponse(request, cached, 200, { 'X-Cache': xCache, ...extraHeaders })\n}\n\n/**\n * The feed body for a request: from the content cache while it was built from the same inputs,\n * else built from fresh items and cached. The ETag is derived from query's collection ETag when\n * known (so it survives rebuilds of an unchanged feed), else from the body.\n */\nasync function serveFeed(request: FeedRequest): Promise<Response> {\n const { schemaName, format, contentKeyOptions } = request\n const cache = getCacheManager()\n const cacheEnabled = cache.getConfig().enabled\n\n try {\n let fetched: { items: FeedItem[]; sourceEtag?: string } | undefined\n if (request.revalidate) fetched = await request.fetchItems()\n const fingerprint = feedFingerprint({\n sourceEtag: fetched?.sourceEtag,\n siteConfig: request.siteConfig,\n cacheBust: request.cacheBust,\n })\n\n if (cacheEnabled) {\n const cached = await cache.getFeedContent(schemaName, format, contentKeyOptions, fingerprint)\n if (cached) return cachedFeedResponse(request, cached, 'HIT')\n }\n\n fetched ??= await request.fetchItems()\n feedDebug('building %s:%s from %d items', schemaName, format, fetched.items.length)\n const content = await request.render(fetched.items)\n const contentType = getContentType(format)\n const etag = fetched.sourceEtag\n ? generateETag(`${fingerprint}:${buildContentKey(schemaName, format, contentKeyOptions)}`)\n : generateETag(content)\n const stored = cacheEnabled\n ? await cache.setFeedContent(\n schemaName,\n format,\n { content, contentType, etag, fingerprint },\n contentKeyOptions,\n )\n : null\n if (stored) return cachedFeedResponse(request, stored, 'MISS')\n return feedResponse(\n request,\n { content, contentType, etag, lastModified: Math.floor(Date.now() / 1000) },\n 200,\n )\n } catch (error) {\n console.error(`Feed generation error for ${schemaName}:${format}:`, error)\n if (cacheEnabled) {\n const stale = await cache.getFeedContent(schemaName, format, contentKeyOptions)\n if (stale) {\n return cachedFeedResponse(request, stale, 'STALE', { Warning: '110 - \"Response is stale\"' })\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 { status: 500, headers: { 'Content-Type': 'application/json' } },\n )\n }\n}\n\nfunction invalidFormatResponse(formatSegment: string): Response {\n return new Response(JSON.stringify({ error: `Invalid feed format: ${formatSegment}` }), {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n })\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 *\n * Page 1 asks `@seedprotocol/query` on every request (one round when its collection is cached,\n * none within `CACHE_MAX_AGE`) and rebuilds only when the collection changed; its Cache-Control\n * max-age is `CACHE_MAX_AGE`. Later pages are cached for `CACHE_PAGE_TTL`.\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 const format = parseFormat(formatSegment)\n if (!format) return invalidFormatResponse(formatSegment)\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 pageNum = Math.max(1, page ?? 1)\n const pageSize = loadFeedConfig().pageSize\n const cache = getCacheManager()\n let hasNext = false\n\n return serveFeed({\n schemaName,\n format,\n contentKeyOptions: pageNum > 1 ? { page: pageNum } : undefined,\n ifNoneMatch,\n siteConfig,\n cacheBust,\n maxAge: pageNum === 1 ? (getQueryCacheManager().getConfig().maxAge ?? 0) : 3600,\n revalidate: pageNum === 1,\n fetchItems: async () => {\n // One item past the page tells whether there is a next page.\n const { items, etag } = await fetchFeedItemsBySchemaName(schemaName, {\n limit: pageSize + 1,\n skip: (pageNum - 1) * pageSize,\n })\n hasNext = items.length > pageSize\n return { items: items.slice(0, pageSize) as FeedItem[], sourceEtag: etag }\n },\n render: async (items) => {\n const archiveLinks: FeedArchiveLink[] = []\n if (pageNum === 1) {\n // Archive months are UTC, like queryBySchemaForMonth's bounds.\n const now = new Date()\n const prevMonth = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - 1, 1))\n archiveLinks.push({\n rel: 'prev-archive',\n href: `${siteConfig.feedUrl}/${collectionName}/archive/${prevMonth.getUTCFullYear()}/${prevMonth.getUTCMonth() + 1}/${format}`,\n })\n }\n return createFeed(\n await withImageMetadata(items, schemaName, cache),\n schemaName,\n format,\n cacheBust,\n { page: pageNum, pageSize, hasNext, baseUrl: `${siteConfig.feedUrl}/${collectionName}/${format}` },\n archiveLinks.length ? archiveLinks : undefined,\n false,\n undefined,\n siteConfigOverrides,\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 (UTC)\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) return invalidFormatResponse(formatSegment)\n\n const schemaName = pluralize.singular(collectionSegment.toLowerCase())\n const collectionName = pluralize(schemaName)\n const siteConfig = resolveSiteConfig(siteConfigOverrides)\n const cache = getCacheManager()\n\n return serveFeed({\n schemaName,\n format,\n contentKeyOptions: { archive: { year, month } },\n ifNoneMatch,\n siteConfig,\n cacheBust,\n maxAge: 86400,\n revalidate: false,\n fetchItems: async () => ({\n items: (await getFeedItemsBySchemaNameForMonth(schemaName, year, month)) as FeedItem[],\n }),\n render: async (items) => {\n const baseUrl = `${siteConfig.feedUrl}/${collectionName}/${format}`\n const archiveBase = `${siteConfig.feedUrl}/${collectionName}/archive`\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 const nextYear = month === 12 ? year + 1 : year\n const nextMonth = month === 12 ? 1 : month + 1\n const now = new Date()\n if (nextYear < now.getUTCFullYear() || (nextYear === now.getUTCFullYear() && nextMonth <= now.getUTCMonth() + 1)) {\n archiveLinks.push({ rel: 'next-archive', href: `${archiveBase}/${nextYear}/${nextMonth}/${format}` })\n }\n return createFeed(\n await withImageMetadata(items, schemaName, cache),\n schemaName,\n format,\n cacheBust,\n undefined,\n archiveLinks,\n true,\n undefined,\n siteConfigOverrides,\n )\n },\n })\n}\n"],"mappings":";;;;;;;;;;;;;AAWA,eAAsBQ,EACpB,GACe;CACf,MAAMC,EAAwBC,CAAO;AACvC;AAGA,eAAsBC,KAAsC;CAC1D,MAAMC,EAAsB;AAC9B;;;ACRA,IAAaC,IAAkC;CAC7C,OAAO;CACP,aAAa;CACb,SAAS;CACT,SAAS;CACT,UAAU;CACV,WAAW,sBAAK,IAAI,KAAK,EAAA,CAAE,YAAY,EAAE;CACzC,QAAQ;EACN,MAAM;EACN,OAAO;EACP,MAAM;CACR;AACF;AAEA,SAASC,EAAgB,GAAgC;CACvD,OAAO;EACL,GAAGC;EACH,QAAQA,EAAO,SAAS,EAAE,GAAGA,EAAO,OAAO,IAAI,KAAA;CACjD;AACF;AAEA,IAAIC,IAAyBF,EAAgBD,CAAmB;AAKhE,SAAgBI,EACd,GACA,IAAmBD,GACP;CACZ,IAAI,CAACE,GACH,OAAOJ,EAAgBK,CAAI;CAG7B,IAAMC,IAAuB;EAC3B,GAAGD;EACH,GAAGD;EACH,QAAQC,EAAK,SAAS,EAAE,GAAGA,EAAK,OAAO,IAAI,KAAA;CAC7C;CAUA,OARID,EAAU,WAAW,KAAA,MACvB,EAAS,SAAS;EAChB,GAAIE,EAAS,UAAU,EAAE,MAAM,GAAG;EAClC,GAAGF,EAAU;EACb,MAAMA,EAAU,OAAO,QAAQE,EAAS,QAAQ,QAAQ;CAC1D,IAGKA;AACT;AAGA,SAAgBC,KAA4B;CAC1C,OAAOP,EAAgBE,CAAU;AACnC;AAMA,SAAgBM,GAAc,GAA4C;CAExE,OADA,IAAaL,EAAkBC,GAAWL,CAAmB,GACtDQ,GAAc;AACvB;AAGA,SAAgBE,KAAwB;CACtC,IAAaT,EAAgBD,CAAmB;AAClD;AAgBA,SAAgBW,IAKd;CAaA,OAAO;EAAE,aAXP,QAAQ,IAAI,oBAAoB,KAAK,KACrCC,GAAsBC,GAAkB,CAAC,CAAC,EAAE,eAC5C;EASoB,aAPpB,QAAQ,IAAI,oBAAoB,KAAK,KAAK;EAOT,UANlB,KAAK,IAAI,GAAG,SAAS,QAAQ,IAAI,kBAAkB,MAAM,EAAE,KAAK,EAM9C;EAAU,uBAJ3C,QAAQ,IAAI,kCAAkC,YAAY,MAAM,SAC5D,kBACA;CAE6D;AACrE;;;ACrGA,IAAM1B,MAAoB,MAA8B;CACtD,IAAM4B,oBAAO,IAAI,KAAKC,IAAY,GAAI,GAChCC,IAAO;EAAC;EAAO;EAAO;EAAO;EAAO;EAAO;EAAO;CAAK,GACvDC,IAAS;EAAC;EAAO;EAAO;EAAO;EAAO;EAAO;EAAO;EAAO;EAAO;EAAO;EAAO;EAAO;CAAK,GAE5FC,IAAMF,EAAKF,EAAK,UAAU,IAC1BK,IAAQF,EAAOH,EAAK,YAAY,IAChCM,IAAON,EAAK,eAAe;CAMjC,OAAO,GAAGI,EAAI,IALCJ,EAAK,WAAW,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,GAAG,GAKtC,EAAO,GAAGK,EAAM,GAAGC,EAAK,GAJ5BN,EAAK,YAAY,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,GAAG,GAIX,EAAM,GAHnCA,EAAK,cAAc,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,GAAG,GAGN,EAAQ,GAF9CA,EAAK,cAAc,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,GAAG,GAEK,EAAQ;AAC3E,GAKaO,MACX,GACA,GACA,GACA,MACS;CACT,IAAM,EAAE,gBAAa,mBAAgBzB;CAErC,AAAI,CAAC0B,EAAK,SAAS,CAACA,EAAK,SACvB,EAAK,QAAQC,GACb,EAAK,QAAQA,KACJD,EAAK,SAAS,CAACA,EAAK,QAC7B,EAAK,QAAQA,EAAK,QACTA,EAAK,SAAS,CAACA,EAAK,UAC7B,EAAK,QAAQA,EAAK;CAGpB,IAAME,IACJF,EAAK,0BACL,OAAOA,EAAK,0BAA2B,YACvCA,EAAK,uBAAuB,KAAK,MAAM,MACvCA,EAAK,2BAA2B,eAChCA,EAAK,2BAA2BC,IAC3BD,EAAK,uBAAkC,KAAK,IAC7C,MACAG,IACJH,EAAK,wBACL,OAAOA,EAAK,wBAAyB,YACrCA,EAAK,qBAAqB,KAAK,MAAM,MACrCA,EAAK,yBAAyB,eAC9BA,EAAK,yBAAyBC,IACzBD,EAAK,qBAAgC,KAAK,IAC3C,MACAI,IAAuBF,KAA6BC,GAEpDE,IACJJ,KAAW,OAAOA,KAAY,YAAYA,EAAQ,KAAK,MAAM,KAAKA,IAAU,WAG1EK,IAAc,GAFSC,EAAY,QAAQ,OAAO,EAAE,EAAE,GAAGC,EAAY,QAAQ,OAAO,EAAE,EAAE,GAAGH;CAG/F,IAAID,KAAwBA,MAAyBH,KAAWG,EAAqB,SAAS,GAC5F,IAAI;EACF,IAAcK,EAA4BL,CAAoB;CAChE,SAASM,GAAO;EACd,QAAQ,MAAM,8DAA8DA,CAAK;CACnF;CAGF,IAAMC,IAAeX,EAAK,QAAQA,EAAK;CACvC,AACE,CAACW,KACDA,MAAgB,eACf,OAAOA,KAAgB,YAAYA,EAAY,KAAK,MAAM,MAE3D,EAAK,OAAOL,GACZ,EAAK,OAAOA,OAER,CAACN,EAAK,QAAQA,EAAK,SAAS,iBAC9B,EAAK,OAAOW,KAEV,CAACX,EAAK,QAAQA,EAAK,SAAS,iBAC9B,EAAK,OAAOW;CAIhB,IAAMC,IAAeZ,EAAK,QAAQA,EAAK;CAiBvC,IAfE,CAACY,KACDA,MAAgB,eACf,OAAOA,KAAgB,YAAYA,EAAY,KAAK,MAAM,MAE3D,EAAK,OAAOZ,EAAK,QAAQM,GACzB,EAAK,OAAON,EAAK,UAEb,CAACA,EAAK,QAAQA,EAAK,SAAS,iBAC9B,EAAK,OAAOY,KAEV,CAACZ,EAAK,QAAQA,EAAK,SAAS,iBAC9B,EAAK,OAAOY,KAIZZ,EAAK,eAAe,CAACA,EAAK,WAAW,CAACA,EAAK,SAAS;EACtD,IAAMa,IAAUjD,GAAiBoC,EAAK,WAAqB;EAE3D,AADA,EAAK,UAAUa,GACf,EAAK,UAAUA;CACjB,OAAO,AAAIb,EAAK,WAAW,CAACA,EAAK,UAC/B,EAAK,UAAUA,EAAK,UACXA,EAAK,WAAW,CAACA,EAAK,YAC/B,EAAK,UAAUA,EAAK;CAYtB,AATI,CAACA,EAAK,WAAW,CAACA,EAAK,WACzB,EAAK,UAAUC,GACf,EAAK,UAAUA,KACND,EAAK,WAAW,CAACA,EAAK,UAC/B,EAAK,UAAUA,EAAK,UACXA,EAAK,WAAW,CAACA,EAAK,YAC/B,EAAK,UAAUA,EAAK,UAGlBA,EAAK,YAAY,CAACA,EAAK,WACzB,EAAK,WAAWA,EAAK,WACZA,EAAK,YAAY,CAACA,EAAK,aAChC,EAAK,WAAWA,EAAK;AAEzB,GAMMc,MACJ,GACA,MACS;CAUT,IATI,CAACC,EAAM,SAAS,CAACA,EAAM,SACzB,EAAM,QAAQd,GACd,EAAM,QAAQA,KACLc,EAAM,SAAS,CAACA,EAAM,QAC/B,EAAM,QAAQA,EAAM,QACXA,EAAM,SAAS,CAACA,EAAM,UAC/B,EAAM,QAAQA,EAAM,QAGlBA,EAAM,eAAe,CAACA,EAAM,WAAW,CAACA,EAAM,SAAS;EACzD,IAAMF,IAAUjD,GAAiBmD,EAAM,WAAqB;EAE5D,AADA,EAAM,UAAUF,GAChB,EAAM,UAAUA;CAClB,OAAO,AAAIE,EAAM,WAAW,CAACA,EAAM,UACjC,EAAM,UAAUA,EAAM,UACbA,EAAM,WAAW,CAACA,EAAM,YACjC,EAAM,UAAUA,EAAM;CAYxB,AATI,CAACA,EAAM,WAAW,CAACA,EAAM,WAC3B,EAAM,UAAUd,GAChB,EAAM,UAAUA,KACPc,EAAM,WAAW,CAACA,EAAM,UACjC,EAAM,UAAUA,EAAM,UACbA,EAAM,WAAW,CAACA,EAAM,YACjC,EAAM,UAAUA,EAAM,UAGpBA,EAAM,YAAY,CAACA,EAAM,WAC3B,EAAM,WAAWA,EAAM,WACdA,EAAM,YAAY,CAACA,EAAM,aAClC,EAAM,WAAWA,EAAM;CAGzB,IAAMb,IACJa,EAAM,0BACN,OAAOA,EAAM,0BAA2B,YACxCA,EAAM,uBAAuB,KAAK,MAAM,MACxCA,EAAM,2BAA2B,eACjCA,EAAM,2BAA2Bd,IAC5Bc,EAAM,uBAAkC,KAAK,IAC9C,MACAZ,IACJY,EAAM,wBACN,OAAOA,EAAM,wBAAyB,YACtCA,EAAM,qBAAqB,KAAK,MAAM,MACtCA,EAAM,yBAAyB,eAC/BA,EAAM,yBAAyBd,IAC1Bc,EAAM,qBAAgC,KAAK,IAC5C,MACAX,IAAuBF,KAA6BC;CAO1D,IALA,OAAOY,EAAM,MACb,OAAOA,EAAM,MACb,OAAOA,EAAM,MACb,OAAOA,EAAM,MAETX,KAAwBA,EAAqB,SAAS,GACxD,IAAI;EACF,IAAMY,IAAYP,EAA4BL,CAAoB;EAElE,AADA,EAAM,OAAOY,GACb,EAAM,OAAOA;CACf,QAAQ,CAER;AAEJ;AAEA,SAASC,EAAmB,GAAkD;CAC5E,IAAI,CAACC,KAAS,OAAOA,KAAU,YAAY,MAAM,QAAQA,CAAK,GAAG,OAAO;CACxE,IAAMC,IAAMD;CACZ,OAAO,OAAOC,EAAI,WAAY,YAAY,OAAOA,EAAI,WAAY;AACnE;AAEA,SAASC,GACP,GACA,GACA,GACyB;CACzB,IAAMC,IAAO,EAAE,GAAGN,EAAM,GAClBO,IAAaD,EAAK,WAAWA,EAAK,WAAW,IAC7CE,IACJ,OAAOF,EAAK,cAAe,WAAWA,EAAK,aAAaG;CAM1D,OALID,MAAiB,UACnBT,GAA6BO,GAAMC,CAAS,IAE5CvB,GAAoBsB,GAAMC,GAAWC,GAAcjD,CAAO,GAErD+C;AACT;AAWA,SAASI,GACP,GACA,GACA,GACyB;CACzB,IAAMC,IAAM,EAAE,GAAG1B,EAAK;CAEtB,GAAoB0B,GADHA,EAAI,WAAWA,EAAI,WAAW,IACbC,GAAYrD,CAAO;CAErD,KAAK,IAAMsD,KAAO,OAAO,KAAKF,CAAG,GAAG;EAClC,IAAIE,EAAI,WAAW,GAAG,GAAG;EACzB,IAAMV,IAAQQ,EAAIE;EAClB,AAAI,MAAM,QAAQV,CAAK,IACjBA,EAAM,KAAKD,CAAkB,MAC/B,EAAIW,KAAOV,EAAM,KAAK,MACpBD,EAAmBY,CAAE,IAAIT,GAAwBS,GAAIF,GAAYrD,CAAO,IAAIuD,CAC9E,KAEOZ,EAAmBC,CAAK,MACjC,EAAIU,KAAOR,GAAwBF,GAAOS,GAAYrD,CAAO;CAEjE;CACA,OAAOoD;AACT;AAEA,SAASI,GACP,GACA,GAC2B;CAC3B,IAAMC,IAAa3C,EAAe,GAC5B4C,IAA8C;EAClD,aAAaD,EAAW;EACxB,aAAaA,EAAW;CAC1B;CAEA,OAAOE,EAAQ,KAAK,MAClBR,GAAsBS,EAAO,MAAMP,GAAYK,CAAe,CAChE;AACF;AAOA,IAAaG,IAA6B,OACxC,GACA,MACiE;CACjE,MAAM/D,EAAuB;CAC7B,IAAMgE,IAAQ9D,GAAS,SAAS,KAC1B+D,IAAO/D,GAAS,QAAQ,GAGxB,EAAE,UAAO,YAAS,MAAMgE,EAAcX,GAAY;EACtD;EACA;EACA,WAAW;EACX,gBAAgB;CAClB,CAAC;CACD,OAAO;EAAE,OAAOG,GAAuBH,GAAYY,CAAK;EAAG;CAAK;AAClE,GAEaC,KAA2B,OACtC,GACA,OAEC,MAAML,EAA2BR,GAAYrD,CAAO,EAAA,CAAG,OAE7CmE,IAAmC,OAC9C,GACA,GACA,OAEA,MAAMrE,EAAuB,GAMtB0D,GAAuBH,GAAY,MAJpBe,EAAsBf,GAAY7B,GAAMD,GAAO;CACnE,WAAW;CACX,gBAAgB;AAClB,CAAC,CACgD;;;AC/TnD,SAAS8C,GAAY,GAAwC;CAC3D,IAAI;EACF,OAAO,KAAK,MAAM,KAAK,UAAU3C,CAAI,CAAC;CACxC,QAAQ;EACN,OAAO,EAAE,GAAIA,EAAiC;CAChD;AACF;AAGA,IAAM4C,KAAS,IAAI5E,GAAO,EACxB,cAAc,EACZ,MAAM;CACJ;CACA;CACA;CACA;CACA;AACF,EACF,EACF,CAAC;AAED,eAAsB6E,GAAe,GAAwC;CAC3E,IAAMC,IAAO,MAAMF,GAAO,YAAYG,CAAG;CACzC,OAAO;EACL,OAAOD,EAAK;EACZ,MAAMA,EAAK;EACX,aAAaA,EAAK;EAClB,QAAQA,EAAK,SAAS,CAAC,EAAA,CAAG,IAAIH,EAAW;CAC3C;AACF;;;ACpCA,IAAMK,IAAQ,oCACRC,IAAa,4CACbC,IAAW,iCAGXC,KAA4C,EAChD,aAAa;CACX,QAAQ;CACR,KAAK;AACP,EACF,GACMC,KAA8B,wCAC9BC,oBAAwB,IAAI,IAAI;CAAC;CAAO;CAAS;CAAM;CAAS;CAAW;CAAQ;AAAI,CAAC,GAExFC,qBAAyB,IAAI,IAAI;CAAC;CAAS;CAAQ;CAAQ;CAAW;AAAS,CAAC;AAEtF,SAASC,GAA0B,GAAa,GAAwC;CAGtF,OAFKD,GAAuB,IAAI1B,CAAG,IAClBA,EAAI,OAAO,CAAC,CAAC,CAAC,YAAY,IAAIA,EAAI,MAAM,CAAC,KACvC5B,IAF0B;AAG/C;AAEA,IAAMwD,IAAiB;CACrB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAASC,EAAiB,GAA6B;CACrD,IAAIjE,aAAgB,MAClB,OAAOA,EAAK,YAAY;CAE1B,IAAI,OAAOA,KAAS,UAAU;EAC5B,IAAMkE,IAAI,IAAI,KAAKlE,CAAI;EACvB,OAAQ,OAAO,MAAMkE,EAAE,QAAQ,CAAC,IAAsBlE,IAAlBkE,EAAE,YAAY;CACpD;CACA,OAAO,OAAOlE,CAAI;AACpB;AAEA,SAASmE,GAAW,GAAwB;CAC1C,OAAO,YAAY,KAAKzC,CAAK;AAC/B;AAEA,SAAS0C,EAAe,GAAoB,GAAqB;CAC/D,IAAMC,IAAM,OAAO3C,CAAK;CACxB,IAAI2C,MAAQ,IAEZ;MAAIF,GAAWE,CAAG,GAAG;GACnB,IAAIA,EAAI,SAAS,KAAK,GAAG;IACvB,IAAMC,IAAQD,EAAI,MAAM,KAAK;IAC7B,KAAK,IAAIE,IAAI,GAAGA,IAAID,EAAM,QAAQ,KAEhC,AADAE,EAAO,IAAIF,EAAMC,EAAG,GAChBA,IAAID,EAAM,SAAS,KACrBE,EAAO,IAAI,KAAK;GAGtB,OACE,EAAO,IAAIH,CAAG;EAElB,OACE,EAAO,IAAIA,CAAG;CAAA;AAElB;AAEA,SAASI,EAAyB,GAAkD;CAClF,OAEE,OAAO/C,KAAU,cADjBA,KAEA,CAAC,MAAM,QAAQA,CAAK,KACpB,EAAEA,aAAiB;AAEvB;AAEA,IAAMgD,KAA8B,GAC9BC,qBAAoB,IAAI,IAAI;CAChC,GAAGX;CACH;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,SAASY,GAAqB,GAAqB;CACjD,OAAOxC,EAAI,SAAS,GAAG,KAAKA,EAAI,SAAS,IAAIA,EAAI,MAAM,GAAG,EAAE,IAAIA;AAClE;AAEA,SAASyC,GAAoB,GAAwB;CAMnD,OALcC,EACX,KAAK,CAAC,CACN,QAAQ,qBAAqB,GAAG,CAAC,CACjC,QAAQ,gBAAgB,EAAE,CAAC,CAC3B,QAAQ,MAAM,KACV,CAAA,CAAM,YAAY;AAC3B;AAEA,SAASC,GACP,GACA,GACQ;CACR,IAAI,CAACC,EAAa,IAAIC,CAAe,KAAK,CAACpB,EAAsB,IAAIoB,CAAe,GAElF,OADAD,EAAa,IAAIC,CAAe,GACzBA;CAGT,IAAIC,IAAS;CACb,SAAa;EACX,IAAMC,IAAY,GAAGF,IAAkBC;EACvC,IAAI,CAACF,EAAa,IAAIG,CAAS,KAAK,CAACtB,EAAsB,IAAIsB,CAAS,GAEtE,OADAH,EAAa,IAAIG,CAAS,GACnBA;EAET,KAAU;CACZ;AACF;AAEA,SAASC,EAAgB,GAAoD;CAC3E,OACE,MAAM,QAAQ1D,CAAK,KACnBA,EAAM,SAAS,KACfA,EAAM,OAAO,MAAU+C,EAAyBY,CAAK,CAAC;AAE1D;AAEA,SAASC,GAA0B,GAAwD;CACzF,IAAMC,IAA2C,EAAE,GAAG5B,GAAoB,GACpEqB,IAAe,IAAI,IACvB,OAAO,OAAOO,CAAkB,CAAC,CAAC,KAAK,MAAqBC,EAAiB,MAAM,CACrF,GAEMC,oBAAyB,IAAI,IAAY;CAC/C,KAAK,IAAMjF,KAAQuC,GACjB,KAAK,IAAM,CAACX,GAAKV,MAAU,OAAO,QAAQlB,CAAI,GAAG;EAC/C,IAAMkF,IACHtD,MAAQ,YAAYqC,EAAyB/C,CAAK,KAClDU,MAAQ,aAAagD,EAAgB1D,CAAK;EAC7C,AAAIU,EAAI,WAAW,GAAG,MAClB,IAAkB,IAAIA,CAAG,KAAMsD,OAC/BjB,EAAyB/C,CAAK,KAAK0D,EAAgB1D,CAAK,MAC1D+D,EAAuB,IAAIb,GAAqBxC,CAAG,CAAC;CAExD;CAGF,IAAMuD,IAA+B,CAAC,GAAGF,CAAsB,CAAC,CAAC,KAAK;CACtE,KAAK,IAAMG,KAAeD,GACxB,AAAIJ,EAAmBK,OAGvB,EAAmBA,KAAe;EAChC,QAFab,GADSF,GAAoBe,CACV,GAAiBZ,CAEjD;EACA,KAAK,GAAGpB,GAA4B,GAAGgC,EAAY;CACrD;CAGF,OAAOL;AACT;AAEA,SAASM,EACP,GACA,GACsC;CACtC,IAAIzD,KAAOmD,GAAoB,OAAOA,EAAmBnD;CACzD,IAAIA,EAAI,SAAS,GAAG,KAAKA,EAAI,SAAS,GAAG;EACvC,IAAM0D,IAAW1D,EAAI,MAAM,GAAG,EAAE;EAChC,IAAI0D,KAAYP,GAAoB,OAAOA,EAAmBO;CAChE;AAEF;AAEA,SAASC,GAA0B,GAAmB,GAAqD;CAEzG,OADKP,IACE,GAAGA,EAAiB,OAAO,GAAGQ,MADPA;AAEhC;AAEA,SAASC,EACP,GACA,GACA,GACA,GACA,IAAQ,GACR,GACM;CACN,IAAMC,IAAM1B,EAAO,IAAI2B,CAAO;CAC9B,KAAK,IAAM,CAACC,GAAGC,MAAM,OAAO,QAAQC,CAAG,GAAG;EACxC,IAAID,KAAK,QAAQD,EAAE,WAAW,GAAG,KAAKA,MAAM,SAAS;EACrD,IAAMG,IAAeR,GAA0BK,GAAGZ,CAAgB;EAClE,IAAIgB,KAAS9B,IAEX,EADcwB,EAAI,IAAIK,CACP,GAAO,OAAOF,KAAM,WAAW,KAAK,UAAUA,CAAC,IAAI,OAAOA,CAAC,CAAC;OAG7E,IAAI,MAAM,QAAQA,CAAC,GAAG;GAEpB,IAAMI,IAAeV,GADLK,EAAE,SAAS,GAAG,KAAKA,EAAE,SAAS,IAAIA,EAAE,MAAM,GAAG,EAAE,IAAIA,GACXZ,CAAgB;GACxE,KAAK,IAAMH,KAASgB,GAClB,AAAIhB,KAAiC,SACjCZ,EAAyBY,CAAK,IAChCY,EAA0BC,GAAKb,GAAOoB,GAAc,IAAOD,IAAQ,GAAGhB,CAAgB,IAGtFpB,EADa8B,EAAI,IAAIO,CACN,GAAM,OAAOpB,CAAK,CAAC;EAIxC,OACA,AAAIZ,EAAyB4B,CAAC,IAC5BJ,EACEC,GACAG,GACAE,GACA,IACAC,IAAQ,GACRhB,CACF,IAIFpB,EADc8B,EAAI,IAAIK,CACP,GAAO,OAAOF,CAAC,CAAC;CACjC;CACA,IAAIK,GAAc;EAChB,IAAMC,IAAOL,EAAI,QAAQA,EAAI;EAC7B,AAAIK,KAAMnC,EAAO,IAAIhB,GAAO,SAAS,CAAC,CAAC,IAAI,OAAOmD,CAAI,CAAC;CACzD;AACF;AAEA,SAASC,GACP,GACA,GACA,GACA,GACA,GACM;CACN,IAAI,OAAiC,QAAQxE,EAAI,WAAW,GAAG,MAC3DA,MAAQ,SAEZ;MAAIA,MAAQ,QAAQ;GAClB,IAAMyE,IAAOnF,GACPoF,IAAMD,GAAM,SAAS,OAAOnF,CAAK;GACvC,IAAI,CAACoF,GAAK;GACV,IAAMC,IAAgC,CAAC;GAKvC,AAJI,OAAOF,GAAM,eAAgB,cAC/B,EAAM,cAAcA,EAAK,cAAc,SAAS,UAGlDzC,EADgB4C,EAAW,IAAI,QAAQD,CACxB,GAASD,CAAG;EAE7B,OAEA,IAAI1E,MAAQ,YAA8B,OAAOV,KAAU,YAAnCA,GAA6C;GACnE,IAAMuF,IAAMvF,GACNwF,IAAQD,EAAI,SAAS,OAAOvF,CAAK;GACvC,IAAI,CAACwF,GAAO;GACZ,IAAMH,IAAQE,EAAI,MAAM,EAAE,KAAKA,EAAI,IAAI,IAAI,CAAC;GAC5C,EAAW,IAAI,UAAUF,CAAK,CAAC,CAAC,IAAIG,CAAK;EAE3C,OAEA,IAAI9E,MAAQ,gBAAgB,MAAM,QAAQV,CAAK,GAC7C;QAAK,IAAMyF,KAAOzF,GAChB,IAAIyF,KAAO,OAAOA,KAAQ,YAAY,SAASA,GAAK;IAClD,IAAMJ,IAAgC;KACpC,KAAK,OAAOI,EAAI,GAAG;KACnB,MAAM,OAAOA,EAAI,QAAQ,0BAA0B;IACrD;IAEA,AADIA,EAAI,UAAU,SAAM,EAAM,SAAS,OAAOA,EAAI,MAAM,IACxDH,EAAW,IAAI,aAAaD,CAAK;GACnC;SAKJ,IAAI3E,MAAQ,QAA0B,OAAOV,KAAU,YAAnCA,GAA6C;GAC/D,IAAM0F,IAAK1F;GACX,KAAK,IAAM,CAAC2F,GAAOC,MAAU,OAAO,QAAQF,CAAE,GAC5C,IAAIE,KAAiC,MACrC;QAAI,MAAM,QAAQA,CAAK,GAChB;UAAA,IAAMjB,KAAKiB,GACd,IAAIjB,KAAyB,MAAM;MACjC,IAAMH,IAAMc,EAAW,IAAIxD,GAAO6D,CAAK;MACvC,AAAIhB,aAAa,OACfH,EAAI,IAAIG,EAAE,YAAY,CAAC,IAEvBjC,EAAe8B,GAAK,OAAOG,CAAC,CAAC;KAEjC;WAEG;KACL,IAAMH,IAAMc,EAAW,IAAIxD,GAAO6D,CAAK;KACvC,AAAIC,aAAiB,OACnBpB,EAAI,IAAIoB,EAAM,YAAY,CAAC,IAE3BlD,EAAe8B,GAAK,OAAOoB,CAAK,CAAC;IAErC;;EAGJ,OAEA,IAAIlF,MAAQ,mBAEV,EADY4E,EAAW,IAAIvD,GAAY,SACxB,GAAK,OAAO/B,CAAK,CAAC;OAGnC,IAAIU,MAAQ,aAAa,OAAOV,KAAU,YAAY,EAAE,qBAAqBlB,IAE3E,EADYwG,EAAW,IAAIvD,GAAY,SACxB,GAAK,OAAO/B,CAAK,CAAC;OAGnC,IAAIU,MAAQ,WAEZ;OAAIA,MAAQ,iBAAiB,MAAM,QAAQV,CAAK,GAC9C;SAAK,IAAM6F,KAAS7F,GAClB,IAAI6F,KAAS,OAAOA,KAAU,UAAU;KACtC,IAAMC,IAAWR,EAAW,IAAItD,GAAU,OAAO;KACjD,KAAK,IAAM,CAAC+D,GAAIC,MAAO,OAAO,QAAQH,CAAK,GACzC,IAAIG,KAA2B,QAC3BD,EAAG,WAAW,QAAQ,GAAG;MAC3B,IAAME,IAAKF,EAAG,MAAM,CAAC;MACrB,IAAI,MAAM,QAAQC,CAAE,GACb;YAAA,IAAMrB,KAAKqB,GACd,IAAIrB,KAAK,OAAOA,KAAM,UAAU;QAC9B,IAAMuB,IAAQJ,EAAS,IAAI9D,GAAUiE,CAAE;QACvC,KAAK,IAAM,CAACE,GAAIC,MAAO,OAAO,QAAQzB,CAAC,GACrC,AAAIyB,KAA2B,SACzBD,MAAO,SAASA,MAAO,UACzBzD,EAAewD,GAAO,OAAOE,CAAE,CAAC,IAEhCF,EAAM,IAAIC,GAAI,OAAOC,CAAE,CAAC;OAIhC;aAEG,AAAI,OAAOJ,KAAO,YACvBtD,EAAeoD,EAAS,IAAI9D,GAAUiE,CAAE,GAAGD,CAAE;KAEjD;IAEJ;UAKJ,IAAItF,EAAI,WAAW,QAAQ,KAAK,MAAM,QAAQV,CAAK,GAAG;IACpD,IAAMsE,IAAY5D,EAAI,MAAM,CAAC;IAC7B,KAAK,IAAM5B,KAAQkB,GAAO;KACxB,IAAqB,OAAOlB,KAAS,aAAjCA,GAA2C;KAC/C,IAAMuG,IAAgC,CAAC,GACnCgB,IAA6B;KACjC,KAAK,IAAM,CAACC,GAAMC,MAAY,OAAO,QAAQzH,CAAI,GAC/C,AAAIyH,KAAqC,SACrCD,MAAS,UACX,IAAc,OAAOC,CAAO,IAE5B,EAAMD,KAAQ,OAAOC,CAAO;KAGhC,IAAMC,IAAWlB,EAAW,IAAItD,GAAUsC,GAAWe,CAAK;KAC1D,AAAIgB,MAAgB,QAClB3D,EAAe8D,GAAUH,CAAW;IAExC;GAEF,OAEA,IAAI3F,EAAI,WAAW,QAAQ,KAAK,OAAOV,KAAU,UAAU;IACzD,IAAMsE,IAAY5D,EAAI,MAAM,CAAC;IAE7B,EADY4E,EAAW,IAAItD,GAAUsC,CACtB,GAAKtE,CAAK;GAE3B,OAEA,IAAIA,aAAiB,MAEnB,EADuB,IAAIU,CAC3B,CAAA,CAAI,IAAI6B,EAAiBvC,CAAK,CAAC;QAIjC,IAAIU,MAAQ,YAAYqC,EAAyB/C,CAAK,GAEpD,EAA0BsF,GAAYtF,GAAO,UAAU,IAAM,GADpCmE,EAA4BzD,GAAKmD,CACM,CAAgB;QAIlF,IAAInD,MAAQ,aAAa,MAAM,QAAQV,CAAK,GAAG;IAC7C,IAAM8D,IAAmBK,EAA4BzD,GAAKmD,CAAkB;IAC5E,KAAK,IAAM4C,KAAKzG,GACd,IAAIyG,KAAK1D,EAAyB0D,CAAC,GACjC,EAA0BnB,GAAYmB,GAAG,UAAU,IAAM,GAAG3C,CAAgB;SACvE;KACL,IAAM4C,IACJ,OAAOD,KAAM,WACTA,IACAA,KAAK,OAAOA,KAAM,YAAY,UAAUA,IACtC,GAAIA,EAAyB,SAAS,GAAG,IAAKA,EAAwB,QAAQ,GAAG,GAAG,KAAK,KACxFA,EAAwB,OACzB,OAAOA,CAAC;KAChB,AAAIC,KAAWpB,EAAW,IAAI,QAAQ,CAAC,CAAC,IAAIoB,CAAS;IACvD;GAGJ,OACA,IAAIhG,MAAQ,gBAAgB,MAAM,QAAQV,CAAK,GAC7C,KAAK,IAAM2G,KAAK3G,GAAO;IACrB,IAAM4G,IACJD,KAAK,OAAOA,KAAM,YAAY,UAAUA,IAAKA,EAAuB,OAAO,OAAOA,CAAC;IACrF,IAAIC,GAAK;KACP,IAAMvB,IACJsB,KAAK,OAAOA,KAAM,YAAY,YAAYA,KAAMA,EAA0B,SACtE,EAAE,QAASA,EAAyB,OAAO,IAC3C,CAAC;KACP,EAAW,IAAI,YAAYtB,CAAK,CAAC,CAAC,IAAIuB,CAAG;IAC3C;GACF;QAKF,IAAI,MAAM,QAAQ5G,CAAK,KAAKA,EAAM,SAAS,KAAKA,EAAM,OAAO,MAAM+C,EAAyB4B,CAAC,CAAC,GAAG;IAC/F,IAAMF,IAAU/D,EAAI,SAAS,GAAG,IAAIA,EAAI,MAAM,GAAG,EAAE,IAAIA,GACjDoD,IAAmBK,EAA4BzD,GAAKmD,CAAkB;IAC5E,KAAK,IAAMc,KAAK3E,GACd,AAAI2E,KAAGJ,EAA0Be,GAAYX,GAAGF,GAAS,IAAO,GAAGX,CAAgB;GAGvF,OAGA,IAAI,MAAM,QAAQ9D,CAAK,KAAKA,EAAM,OAAO,MAAM,OAAO2E,KAAM,QAAQ,GAAG;IACrE,IAAMF,IAAU/D,EAAI,SAAS,GAAG,IAAIA,EAAI,MAAM,GAAG,EAAE,IAAIA;IACvD,KAAK,IAAMiE,KAAK3E,GACd,AAAI2E,KAEFjC,EADY4C,EAAW,IAAIb,CACZ,GAAKE,CAAC;GAI3B,OAGA,AAAI5B,EAAyB/C,CAAK,IAEhCuE,EAA0Be,GAAYtF,GAAOU,GAAK,IAAO,GADhCyD,EAA4BzD,GAAKmD,CACE,CAAgB,IAK9EnB,EADY4C,EAAW,IAAI5E,CACZ,GAAK,OAAOV,CAAK,CAAC;EA1GjC;CA/FA;AA0MF;AAEA,IAAM6G,KAAU,+BACVC,KAAQ;AAwBd,SAAgBC,GAAe,GAA+B;CAC5D,IAAMC,IAAMC,GAAO;EAAE,SAAS;EAAO,UAAU;CAAQ,CAAC,GAClDpD,IAAqBD,GAA0BsD,EAAQ,KAAK,GAQ5DC,IAAmC;EACvC,YAAYrF;EACZ,iBAAiBC;EACjB,eAAeC;EACf,cAAc6E;EACd,GAZ6B,OAAO,OAAOhD,CAAkB,CAAC,CAAC,QAC9D,GAAK,OACJ,EAAI,SAASuD,EAAkB,YAAYA,EAAkB,KACtDC,IAET,CAAC,CAOE;CACL;CACA,AAAIH,EAAQ,cACV,EAAS,cAAcJ;CAGzB,IAAMQ,IADMN,EAAI,IAAI,OAAO;EAAE,SAAS;EAAO,GAAGG;CAAS,CACzC,CAAA,CAAI,IAAI,WAAW;EACjC,YAAYrF;EACZ,iBAAiBC;EACjB,eAAeC;CACjB,CAAC;CAWD,IATAsF,EAAQ,IAAI,OAAO,CAAC,CAAC,IAAIJ,EAAQ,KAAK,GAClCA,EAAQ,QAAMI,EAAQ,IAAI,MAAM,CAAC,CAAC,IAAIJ,EAAQ,IAAI,GACtDI,EAAQ,IAAI,aAAa,CAAC,CAAC,IAAIJ,EAAQ,WAAW,GAC9CA,EAAQ,YAAUI,EAAQ,IAAI,UAAU,CAAC,CAAC,IAAIJ,EAAQ,QAAQ,GAC9DA,EAAQ,aAAWI,EAAQ,IAAI,WAAW,CAAC,CAAC,IAAIJ,EAAQ,SAAS,GACjEA,EAAQ,aAAWI,EAAQ,IAAI,WAAW,CAAC,CAAC,IAAIJ,EAAQ,SAAS,GACjEA,EAAQ,WAASI,EAAQ,IAAI,SAAS,CAAC,CAAC,IAAI/E,EAAiB2E,EAAQ,OAAO,CAAC,GAC7EA,EAAQ,iBAAeI,EAAQ,IAAI,eAAe,CAAC,CAAC,IAAI/E,EAAiB2E,EAAQ,aAAa,CAAC,GAE/FA,EAAQ,OAAO,QACjB,KAAK,IAAMK,KAAQL,EAAQ,OAAO;EAChC,IAAM7B,IAAgC;GAAE,KAAKkC,EAAK;GAAK,MAAMA,EAAK;EAAK;EAEvE,AADIA,EAAK,SAAM,EAAM,OAAOA,EAAK,OACjCD,EAAQ,IAAIT,IAAS,QAAQxB,CAAK;CACpC;CAEF,AAAI6B,EAAQ,aACVI,EAAQ,IAAIR,IAAO,SAAS;CAG9B,IAAMU,IAAa,IAAI,IAAIlF,CAAc;CAEzC,KAAK,IAAMxD,KAAQoI,EAAQ,OAAO;EAChC,IAAM5B,IAAagC,EAAQ,IAAI,MAAM,GAC/BG,oBAAgB,IAAI,IAAY;EAEtC,KAAK,IAAM/G,KAAO4B,GAChB,AAAI5B,KAAO5B,MACToG,GAAsBI,GAAY5E,GAAK5B,EAAK4B,IAAM5B,GAAM+E,CAAkB,GAC1E4D,EAAc,IAAI/G,CAAG;EAGzB,IAAMgH,IAAY,OAAO,KAAK5I,CAAI,CAAC,CAChC,QACE,MACC,CAAC2I,EAAc,IAAI/C,CAAC,KACpB,CAAC8C,EAAW,IAAI9C,CAAC,KACjB,CAACA,EAAE,WAAW,GAAG,KACjB,CAACrC,GAA0BqC,GAAG5F,CAAI,CACtC,CAAC,CACA,KAAK;EACR,KAAK,IAAM4B,KAAOgH,GAChB,GAAsBpC,GAAY5E,GAAK5B,EAAK4B,IAAM5B,GAAM+E,CAAkB;CAE9E;CAEA,OAAOmD,EAAI,IAAI,EAAE,aAAa,GAAK,CAAC;AACtC;;;AClgBA,SAAgBW,EACd,GACA,GACA,GACQ;CAOR,OANIvK,GAAS,UACJ,GAAGqD,EAAW,GAAGmH,EAAO,WAAWxK,EAAQ,QAAQ,KAAK,GAAGA,EAAQ,QAAQ,UAEhFA,GAAS,QAAQ,QAAQA,EAAQ,OAAO,IACnC,GAAGqD,EAAW,GAAGmH,EAAO,QAAQxK,EAAQ,SAE1C,GAAGqD,EAAW,GAAGmH;AAC1B;;;AC/DA,IAAaC,KAAb,MAAyB;CACvB,mCAA2D,IAAI,IAAI;CACnE,qCAA+D,IAAI,IAAI;CACvE;CAEA,YAAY,GAAqB;EAC/B,KAAK,SAASpK;CAChB;CAEA,eACE,GACA,GACA,GAC0B;EAC1B,IAAMiD,IAAMiH,EAAgBlH,GAAYmH,GAAQE,CAAiB,GAC3DC,IAAS,KAAK,iBAAiB,IAAIrH,CAAG;EAW5C,OAVKqH,IAIO,KAAK,MAAM,KAAK,IAAI,IAAI,GAChC,IAAMA,EAAO,aACf,KAAK,iBAAiB,OAAOrH,CAAG,GACzB,QAGFqH,IATE;CAUX;CAGA,eACE,GACA,GACA,GACA,GACM;EACN,KAAK,iBAAiB,IAAIJ,EAAgBlH,GAAYmH,GAAQE,CAAiB,GAAGC,CAAM;CAC1F;CAEA,iBAAiB,GAA6C;EAC5D,IAAMA,IAAS,KAAK,mBAAmB,IAAIC,CAAa;EAWxD,OAVKD,IAIO,KAAK,MAAM,KAAK,IAAI,IAAI,GAChC,IAAMA,EAAO,aACf,KAAK,mBAAmB,OAAOC,CAAa,GACrC,QAGFD,EAAO,WATL;CAUX;CAEA,iBAAiB,GAAuB,GAA+B;EACrE,IAAME,IAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,GAIlCF,IAA8B;GAClC;GACA,UAAUE;GACV,WALgBA,KADO,KAAK,OAAO,eAAe,OAAO;EAO3D;EAEA,KAAK,mBAAmB,IAAID,GAAeD,CAAM;CACnD;AACF,GClEaG,KAAb,MAAuB;CACrB;CACA;CAEA,YAAY,GAAqB;EAG/B,AAFA,KAAK,WAAWzK,EAAO,UACvB,KAAK,SAASA,GACd,KAAK,eAAe,CAAC,CAAC,OAAM,MAAO;GACjC,QAAQ,MAAM,qCAAqC0K,CAAG;EACxD,CAAC;CACH;CAEA,MAAc,iBAAgC;EAC5C,IAAI;GACF,MAAMxL,EAAG,MAAM,KAAK,UAAU,EAAE,WAAW,GAAK,CAAC;EACnD,SAAS6C,GAAO;GACd,IAAKA,EAAgC,SAAS,UAC5C,MAAMA;EAEV;CACF;CAEA,mBACE,GACA,GACA,GACQ;EAER,IAAM6I,IADMV,EAAgBlH,GAAYmH,GAAQE,CAC/B,CAAA,CAAI,QAAQ,MAAM,GAAG;EACtC,OAAOQ,EAAK,KAAK,UAAU,GAAGD,EAAS,MAAM;CAC/C;CAEA,qBAA6B,GAA+B;EAC1D,IAAME,IAAmBD,EAAK,KAAK,UAAU,gBAAgB;EAC7D,OAAOA,EAAKC,GAAkB,GAAGP,EAAc,MAAM;CACvD;CAEA,MAAc,yBAAwC;EACpD,IAAI;GACF,IAAMO,IAAmBD,EAAK,KAAK,UAAU,gBAAgB;GAC7D,MAAM3L,EAAG,MAAM4L,GAAkB,EAAE,WAAW,GAAK,CAAC;EACtD,SAAS/I,GAAO;GACd,IAAKA,EAAgC,SAAS,UAC5C,MAAMA;EAEV;CACF;CAEA,MAAM,eACJ,GACA,GACA,GACmC;EACnC,IAAI;GACF,IAAMgJ,IAAW,KAAK,mBAAmB/H,GAAYmH,GAAQE,CAAiB,GACxEW,IAAO,MAAM9L,EAAG,SAAS6L,GAAU,OAAO,GAC1CT,IAA4B,KAAK,MAAMU,CAAI;GAQjD,OANY,KAAK,MAAM,KAAK,IAAI,IAAI,GAChC,IAAMV,EAAO,aACf,MAAM,KAAK,iBAAiBtH,GAAYmH,GAAQE,CAAiB,GAC1D,QAGFC;EACT,SAASvI,GAAO;GAQd,OAPKA,EAAgC,SAAS,YAG9C,QAAQ,MACN,wCAAwCiB,EAAW,GAAGmH,EAAO,IAC7DpI,CACF,GACO;EACT;CACF;CAEA,MAAM,eACJ,GACA,GACA,GACA,GACe;EACf,IAAI;GACF,MAAM,KAAK,eAAe;GAC1B,IAAMgJ,IAAW,KAAK,mBAAmB/H,GAAYmH,GAAQE,CAAiB;GAC9E,MAAMnL,EAAG,UAAU6L,GAAU,KAAK,UAAUE,GAAS,MAAM,CAAC,GAAG,OAAO;EACxE,SAASlJ,GAAO;GACd,QAAQ,MACN,wCAAwCiB,EAAW,GAAGmH,EAAO,IAC7DpI,CACF;EACF;CACF;CAEA,MAAM,iBACJ,GACA,GACA,GACe;EACf,IAAI;GACF,IAAMgJ,IAAW,KAAK,mBAAmB/H,GAAYmH,GAAQE,CAAiB;GAC9E,MAAMnL,EAAG,OAAO6L,CAAQ;EAC1B,SAAShJ,GAAO;GACd,AAAKA,EAAgC,SAAS,YAC5C,QAAQ,MACN,yCAAyCiB,EAAW,GAAGmH,EAAO,IAC9DpI,CACF;EAEJ;CACF;CAEA,MAAM,iBAAiB,GAAsD;EAC3E,IAAI;GACF,IAAMgJ,IAAW,KAAK,qBAAqBR,CAAa,GAClDS,IAAO,MAAM9L,EAAG,SAAS6L,GAAU,OAAO,GAC1CT,IAA8B,KAAK,MAAMU,CAAI;GAQnD,OANY,KAAK,MAAM,KAAK,IAAI,IAAI,GAChC,IAAMV,EAAO,aACf,MAAM,KAAK,mBAAmBC,CAAa,GACpC,QAGFD,EAAO;EAChB,SAASvI,GAAO;GAKd,OAJKA,EAAgC,SAAS,YAG9C,QAAQ,MAAM,0CAA0CwI,EAAc,IAAIxI,CAAK,GACxE;EACT;CACF;CAEA,MAAM,iBAAiB,GAAuB,GAAwC;EACpF,IAAI;GACF,MAAM,KAAK,uBAAuB;GAClC,IAAMgJ,IAAW,KAAK,qBAAqBR,CAAa,GAClDC,IAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,GAIlCF,IAA8B;IAClC;IACA,UAAUE;IACV,WALgBA,KADO,KAAK,OAAO,eAAe,OAAO;GAO3D;GAEA,MAAMtL,EAAG,UAAU6L,GAAU,KAAK,UAAUT,GAAQ,MAAM,CAAC,GAAG,OAAO;EACvE,SAASvI,GAAO;GACd,QAAQ,MAAM,0CAA0CwI,EAAc,IAAIxI,CAAK;EACjF;CACF;CAEA,MAAM,mBAAmB,GAAsC;EAC7D,IAAI;GACF,IAAMgJ,IAAW,KAAK,qBAAqBR,CAAa;GACxD,MAAMrL,EAAG,OAAO6L,CAAQ;EAC1B,SAAShJ,GAAO;GACd,AAAKA,EAAgC,SAAS,YAC5C,QAAQ,MAAM,2CAA2CwI,EAAc,IAAIxI,CAAK;EAEpF;CACF;AAEF,GCjKamJ,KAAb,MAA0B;CACxB;CACA;CACA;CAEA,YAAY,GAAqB;EAG/B,AAFA,KAAK,SAASlL,GACd,KAAK,cAAc,IAAIoK,GAAYpK,CAAM,GACzC,KAAK,YAAY,IAAIyK,GAAUzK,CAAM;CACvC;CAEA,YAAyB;EACvB,OAAO,KAAK;CACd;CAMA,MAAM,eACJ,GACA,GACA,GACA,GACmC;EACnC,IAAI,CAAC,KAAK,OAAO,SACf,OAAO;EAGT,IAAI;GACF,IAAIsK,IAAS,KAAK,YAAY,eAAetH,GAAYmH,GAAQE,CAAiB;GAOlF,OANKC,MACH,IAAS,MAAM,KAAK,UAAU,eAAetH,GAAYmH,GAAQE,CAAiB,GAC9EC,KAAQ,KAAK,YAAY,eAAetH,GAAYmH,GAAQG,GAAQD,CAAiB,IAEvF,CAACC,KACDa,MAAgB,KAAA,KAAab,EAAO,gBAAgBa,IAAoB,OACrEb;EACT,SAASvI,GAAO;GAKd,OAJA,QAAQ,MACN,wCAAwCiB,EAAW,GAAGmH,EAAO,IAC7DpI,CACF,GACO;EACT;CACF;CAGA,MAAM,eACJ,GACA,GACA,GACA,GACmC;EACnC,IAAI,CAAC,KAAK,OAAO,SACf,OAAO;EAGT,IAAI;GACF,IAAIqJ,IAAM,KAAK,OAAO;GACtB,AAAIf,GAAmB,UACrB,IAAM,KAAK,OAAO,cAAc,QACvBA,GAAmB,QAAQ,QAAQA,EAAkB,OAAO,MACrE,IAAM,KAAK,OAAO,WAAW;GAE/B,IAAMG,IAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,GAClCF,IAA4B;IAAE,GAAGe;IAAM,cAAcb;IAAK,WAAWA,IAAMY;GAAI;GAGrF,OAFA,KAAK,YAAY,eAAepI,GAAYmH,GAAQG,GAAQD,CAAiB,GAC7E,MAAM,KAAK,UAAU,eAAerH,GAAYmH,GAAQG,GAAQD,CAAiB,GAC1EC;EACT,SAASvI,GAAO;GAKd,OAJA,QAAQ,MACN,wCAAwCiB,EAAW,GAAGmH,EAAO,IAC7DpI,CACF,GACO;EACT;CACF;CAEA,MAAM,iBAAiB,GAAsD;EAC3E,IAAI,CAAC,KAAK,OAAO,eAAe,SAC9B,OAAO;EAGT,IAAI;GACF,IAAIuI,IAAS,KAAK,YAAY,iBAAiBC,CAAa;GAW5D,OAVID,MAIJ,IAAS,MAAM,KAAK,UAAU,iBAAiBC,CAAa,GACxDD,KACF,KAAK,YAAY,iBAAiBC,GAAeD,CAAM,GAChDA,KAGF;EACT,SAASvI,GAAO;GAEd,OADA,QAAQ,MAAM,0CAA0CwI,EAAc,IAAIxI,CAAK,GACxE;EACT;CACF;CAEA,MAAM,iBAAiB,GAAuB,GAAwC;EACpF,IAAK,KAAK,OAAO,eAAe,SAIhC,IAAI;GAEF,AADA,KAAK,YAAY,iBAAiBwI,GAAee,CAAQ,GACzD,MAAM,KAAK,UAAU,iBAAiBf,GAAee,CAAQ;EAC/D,SAASvJ,GAAO;GACd,QAAQ,MAAM,0CAA0CwI,EAAc,IAAIxI,CAAK;EACjF;CACF;AAEF;;;AClHA,SAAgBwJ,KAA+B;CAC7C,IAAMH,IAAM,SAAS,QAAQ,IAAI,aAAa,QAAQ,EAAE,GAClDI,IAAW,QAAQ,IAAI,aAAa,WAGpCC,IACJ,QAAQ,IAAI,kBAAkB,WAC9B,QAAQ,IAAI,kBAAkB,OAC9B,QAAQ,IAAI,kBAAkB,QAC9B,QAAQ,IAAI,kBAAkB,OAE1BC,IACJ,QAAQ,IAAI,kBAAkB,UAC9B,QAAQ,IAAI,kBAAkB,OAC9B,QAAQ,IAAI,kBAAkB,OAC1BC,IAAA,QAAA,IAAA,aAAiC,eAEnCC;CAYJ,AAXA,AAOEA,IAPEH,IACQ,KACDC,IACC,KACL,CAAIC,GAONC,KAMH,QAAQ,IAAI,0BALGH,IACX,wBACAE,IACE,yBACA,sBACuC,EAAE;CAajD,IAAME,IAAqC;EACzC,SAV2B,QAAQ,IAAI,2BAA2B;EAWlE,KAVuB,SAAS,QAAQ,IAAI,sBAAsB,UAAU,EAUvE;EACL,UAV4B,QAAQ,IAAI,0BACtC,QAAQ,IAAI,wBAAwB,MAAM,GAAG,CAAC,CAAC,KAAI,MAAKC,EAAE,KAAK,CAAC,IAChEC,EACEC,GAA0B,KAAKC,EAAkB,QAAQ,CAC3D;EAOF,SAN2B,SAAS,QAAQ,IAAI,0BAA0B,QAAQ,EAMzE;CACX,GAEMC,IAAU,SAAS,QAAQ,IAAI,kBAAkB,OAAO,EAAE,GAC1DC,IAAa,SAAS,QAAQ,IAAI,qBAAqB,SAAS,EAAE;CAExE,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;CACF;AACF;;;AC5EA,SAAgBC,EAAa,GAAuB;CAElD,OAAO,IADMC,GAAW,KAAK,CAAC,CAAC,OAAO9J,CAAK,CAAC,CAAC,OAAO,KACzC,CAAA,CAAK,UAAU,GAAG,EAAE,EAAE;AACnC;AAMA,SAAgB+J,GAAY,GAAe,GAAwB;CAEjE,IAAMC,KAAa,MAAiBC,EAAK,QAAQ,UAAU,EAAE;CAC7D,OAAOD,EAAUE,CAAK,MAAMF,EAAUG,CAAK;AAC7C;AAMA,SAAgBC,GAAiB,GAAkD;CAIjF,OAHKC,IAGEA,EACJ,MAAM,GAAG,CAAC,CACV,KAAI,MAAQJ,EAAK,KAAK,CAAC,CAAC,CACxB,QAAO,MAAQA,EAAK,SAAS,CAAC,IALxB,CAAC;AAMZ;AAKA,SAAgBK,GACd,GACA,GACS;CACT,IAAMC,IAAQH,GAAiBI,CAAiB;CAWhD,OAVID,EAAM,WAAW,IACZ,KAILA,EAAM,SAAS,GAAG,IACb,KAIFA,EAAM,MAAK,MAAQR,GAAYE,GAAMQ,CAAW,CAAC;AAC1D;;;ACpCA,IAAaC,KAAb,MAAiC;CAC/B;CAEA,YAAY,GAAmC;EAC7C,KAAK,SAASjN;CAChB;CAQA,MAAM,YAAY,GAAsD;EACtE,MAAMkN,EAAoC;EAG1C,IAAMC,IAAWC,EAAuB,EAAE,eAAe,KAAK,OAAO,SAAS,CAAC;EAE/E,KAAK,IAAMhN,KAAQ+M,GAAU;GAC3B,IAAIE,GAAyBjN,CAAI,GAAG;GACpC,IAAMkL,IAAW,MAAM,KAAK,MAAM,GAAGlL,EAAK,GAAGmK,KAAiBnK,CAAI;GAClE,IAAIkL,GAAU,OAAOA;EACvB;EACA,OAAO;CACT;CAGA,MAAM,iBAAiB,GAAa,GAA2C;EAC7E,OAAQ,MAAM,KAAK,MAAMgC,GAAKC,CAAQ,KAAM;GAAE,SAAS;GAAO;EAAI;CACpE;CAGA,MAAc,MAAM,GAAa,GAAkD;EACjF,IAAMC,IACJD,YACO;GACL,IAAI;IACF,OAAO,IAAI,IAAID,CAAG,CAAC,CAAC;GACtB,QAAQ;IACN,OAAOA;GACT;EACF,EAAA,CAAG;EAEL,IAAI;GAEF,IAAMG,IAAe,MAAM,KAAK,iBAAiBH,GAAK,EAAE,QAAQ,OAAO,CAAC;GAExE,IAAI,CAACG,EAAa,IAEhB,OAAO;GAGT,GAAyBD,CAAI;GAE7B,IAAME,IAAcD,EAAa,QAAQ,IAAI,cAAc,KAAK,IAC1DE,IAAgBF,EAAa,QAAQ,IAAI,gBAAgB;GAG/D,IAAI,CAAC,KAAK,mBAAmBC,CAAW,GACtC,OAAO;IACL,SAAS;IACT;IACA,UAAUA,KAAe,KAAA;IACzB,MAAMC,IAAgB,SAASA,GAAe,EAAE,IAAI,KAAA;GACtD;GAIF,IAAMC,IAAgB,MAAM,KAAK,iBAAiBN,GAAK,EACrD,SAAS,EACP,OAAO,eACT,EACF,CAAC;GAED,IAAI,CAACM,EAAc,IAEjB,OAAO,MAAM,KAAK,gCAAgCN,GAAKI,GAAaC,CAAa;GAGnF,IAAME,IAAS,OAAO,KAAK,MAAMD,EAAc,YAAY,CAAC,GACtDE,IAAa,KAAK,uBAAuBD,GAAQH,CAAW,GAC5DvD,IAAS,KAAK,eAAeuD,GAAaG,CAAM;GAEtD,OAAO;IACL,SAAS;IACT;IACA,UAAUH;IACV,OAAOI,EAAW;IAClB,QAAQA,EAAW;IACnB,MAAMH,IAAgB,SAASA,GAAe,EAAE,IAAI,KAAA;IACpD;GACF;EACF,SAAS5L,GAAO;GAGd,OAFAgM,GAAyBP,CAAI,GAC7B,QAAQ,KAAK,sCAAsCF,EAAI,IAAIvL,CAAK,GACzD;EACT;CACF;CAKA,MAAc,gCACZ,GACA,GACA,GACwB;EACxB,IAAI;GACF,IAAMiM,IAAW,MAAM,KAAK,iBAAiBV,CAAG;GAChD,IAAI,CAACU,EAAS,IACZ,OAAO;IAAE,SAAS;IAAO;GAAI;GAG/B,IAAMH,IAAS,OAAO,KAAK,MAAMG,EAAS,YAAY,CAAC,GACjDF,IAAa,KAAK,uBAAuBD,GAAQH,CAAW,GAC5DvD,IAAS,KAAK,eAAeuD,GAAaG,CAAM;GAEtD,OAAO;IACL,SAAS;IACT;IACA,UAAUH;IACV,OAAOI,EAAW;IAClB,QAAQA,EAAW;IACnB,MAAMD,EAAO;IACb;GACF;EACF,SAAS9L,GAAO;GAEd,OADA,QAAQ,KAAK,6BAA6BuL,EAAI,IAAIvL,CAAK,GAChD;IAAE,SAAS;IAAO;GAAI;EAC/B;CACF;CAKA,mBAA2B,GAA8B;EAIvD,OAHK2L,KAEeA,EAAY,YAAY,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,GAAA,CAAI,KAC5D,CAAA,CAAW,WAAW,QAAQ,IAHZ;CAI3B;CAKA,uBACE,GACA,GACmC;EACnC,IAAI;GACF,IAAMI,IAAavO,GAAOsO,CAAM;GAChC,IAAIC,EAAW,SAASA,EAAW,QACjC,OAAO;IACL,OAAOA,EAAW;IAClB,QAAQA,EAAW;GACrB;EAEJ,SAAS/L,GAAO;GAEd,QAAQ,KAAK,iCAAiCA,CAAK;EACrD;EAGA,OAAO;GAAE,OAAO;GAAG,QAAQ;EAAE;CAC/B;CAKA,eAAuB,GAAqB,GAAoC;EAE9E,IAAI2L,GAAa;GACf,IAAMO,IAAYP,EAAY,YAAY,CAAC,CAAC,MAAM,gBAAgB;GAClE,IAAIO,KAAaA,EAAU,IAAI;IAC7B,IAAM9D,IAAS8D,EAAU,EAAE,CAAC,YAAY;IAOxC,OALI9D,MAAW,SAAe,SAC1BA,MAAW,QAAc,QACzBA,MAAW,QAAc,QACzBA,MAAW,SAAe,SAC1BA,MAAW,YAAkB,QAC1BA;GACT;EACF;EAGA,IAAI;GACF,IAAM2D,IAAavO,GAAOsO,CAAM;GAChC,IAAIC,EAAW,MACb,OAAOA,EAAW,KAAK,YAAY;EAEvC,QAAQ,CAER;CAGF;CAKA,MAAc,iBACZ,GACA,IAAuB,CAAC,GACL;EACnB,IAAMI,IAAa,IAAI,gBAAgB,GACjCC,IAAY,iBAAiBD,EAAW,MAAM,GAAG,KAAK,OAAO,OAAO;EAE1E,IAAI;GACF,IAAMF,IAAW,MAAM,MAAMV,GAAK;IAChC,GAAG3N;IACH,QAAQuO,EAAW;GACrB,CAAC;GAED,OADA,aAAaC,CAAS,GACfH;EACT,SAASjM,GAAO;GAKd,MAJA,aAAaoM,CAAS,GAClBpM,aAAiB,SAASA,EAAM,SAAS,eACjC,MAAM,yBAAyB,KAAK,OAAO,QAAQ,GAAG,IAE5DA;EACR;CACF;AACF,GC/OaqM,IAAY5O,GAAM,UAAU;;;ACSzC,SAAS6O,EAAoB,GAA+B,GAAiC;CAC3F,KAAK,IAAMpH,KAAKqH,GAAM;EACpB,IAAMpH,IAAI7F,EAAK4F;EACf,IAAI,OAAOC,KAAM,YAAYA,EAAE,KAAK,MAAM,IACxC,OAAOA;CAEX;CACA,OAAO;AACT;AAGA,IAAaqH,KAAmB;CAAC;CAAW;CAAW;CAAe;CAAe;CAAQ;AAAM,GAKtFC,KAAuB;CAAC;CAAQ;CAAQ;CAAQ;CAAQ;CAAW;AAAS,GAEnFC,KAAwB;CAAC;CAAW;CAAW;CAAe;CAAe;CAAQ;AAAM;AASjG,SAASC,GAA0B,GAAsB,GAA8B;CACrF,IAAM3J,IAAI4J,EAAsB3F,EAAE,KAAK,IAAI2F,EAAsBC,EAAE,KAAK;CACxE,IAAI7J,MAAM,GAAG,OAAOA;CACpB,IAAMkC,IAAI+B,EAAE,IAAI,cAAc4F,EAAE,GAAG;CAEnC,OADI3H,MAAM,IACH+B,EAAE,QAAQ4F,EAAE,QADC3H;AAEtB;AAEA,SAAS4H,GAAyB,GAAuC;CACvE,IAAMC,IAAcC,EAAsB1N,CAAI,GACxC2N,IAAaC,EAA4B5N,CAAI,GAC/C6N,IAAiC;CAErC,IAAIJ,GACF,KAAK,IAAM,CAAC7L,GAAKkM,MAAU,OAAO,QAAQL,CAAW,GAAG;EACtD,IAAI,CAACM,EAAwBD,CAAK,GAAG;EACrC,IAAMjI,IAAI7F,EAAK4B;EACf,IAAI,OAAOiE,KAAM,YAAYA,EAAE,KAAK,MAAM,IAAI;EAC9C,IAAMmI,IAA0B;GAAE;GAAO;GAAK,OAAO;GAAG,OAAOnI;EAAE;EACjE,CAAI,CAACgI,KAAQR,GAA0BW,GAAMH,CAAI,IAAI,OAAG,IAAOG;CACjE;CAGF,IAAIL,GACF,KAAK,IAAM,CAACM,GAASC,MAAW,OAAO,QAAQP,CAAU,GAAG;EAC1D,IAAMQ,IAAMnO,EAAKiO;EACjB,IAAI,CAAC,MAAM,QAAQE,CAAG,GAAG;EACzB,IAAMC,IAAI,KAAK,IAAIF,EAAO,QAAQC,EAAI,MAAM;EAC5C,KAAK,IAAIpK,IAAI,GAAGA,IAAIqK,GAAG,KAAK;GAC1B,IAAMN,IAAQI,EAAOnK;GACrB,IAAI,CAACgK,EAAwBD,CAAK,GAAG;GACrC,IAAMjM,IAAKsM,EAAIpK;GACf,IAAI,OAAOlC,KAAO,YAAYA,EAAG,KAAK,MAAM,IAAI;GAChD,IAAMmM,IAA0B;IAAE;IAAO,KAAKC;IAAS,OAAOlK;IAAG,OAAOlC;GAAG;GAC3E,CAAI,CAACgM,KAAQR,GAA0BW,GAAMH,CAAI,IAAI,OAAG,IAAOG;EACjE;CACF;CAGF,OAAOH,GAAM,SAAS;AACxB;AAEA,SAAgBQ,EAAwB,GAAuC;CAC7E,OAAOrB,EAAoBhN,GAAMkN,EAAgB;AACnD;AAEA,SAAgBoB,GAAoB,GAAuC;CACzE,IAAMC,IAAQf,GAAyBxN,CAAI;CAC3C,IAAIuO,MAAU,IAAI,OAAOA;CACzB,IAAMC,IAAUxB,EAAoBhN,GAAMmN,EAAoB;CAE9D,OADIqB,MAAY,KACTxB,EAAoBhN,GAAMoN,EAAqB,IAD3BoB;AAE7B;AAEA,SAASC,EAAgC,GAAyB;CAEhE,OADI,OAAOvN,KAAU,YACdA,EAAM,SAAS,aAAa,KAAKA,EAAM,SAAS,UAAU;AACnE;AAEA,SAASwN,GAA2C,GAAwC;CAC1F,IAAMjB,IAAcC,EAAsB1N,CAAI;CAC9C,IAAIyN,GACF;OAAK,IAAM,CAAC7L,GAAKkM,MAAU,OAAO,QAAQL,CAAW,GACnD,IAAKM,EAAwBD,CAAK,KAC9BW,EAAgCzO,EAAK4B,EAAI,GAAG,OAAO;CACzD;CAEF,IAAM+L,IAAaC,EAA4B5N,CAAI;CACnD,IAAI2N,GACF,KAAK,IAAM,CAACM,GAASC,MAAW,OAAO,QAAQP,CAAU,GAAG;EAC1D,IAAMQ,IAAMnO,EAAKiO;EACjB,IAAI,CAAC,MAAM,QAAQE,CAAG,GAAG;EACzB,IAAMC,IAAI,KAAK,IAAIF,EAAO,QAAQC,EAAI,MAAM;EAC5C,KAAK,IAAIpK,IAAI,GAAGA,IAAIqK,GAAG,KACrB,IAAKL,EAAwBG,EAAOnK,EAAG,KACnC0K,EAAgCN,EAAIpK,EAAE,GAAG,OAAO;CAExD;CAEF,OAAO;AACT;AAMA,SAAgB4K,GAAqC,GAAwC;CAC3F,IAAID,GAA2C1O,CAAI,GAAG,OAAO;CAC7D,KAAK,IAAM4F,KAAKuH,IACd,IAAIsB,EAAgCzO,EAAK4F,EAAE,GAAG,OAAO;CAEvD,KAAK,IAAMA,KAAKsH,IACd,IAAIuB,EAAgCzO,EAAK4F,EAAE,GAAG,OAAO;CAEvD,OAAO;AACT;AAEA,SAAgBgJ,GACd,GACA,IAAwD,cACnD;CAEL,OADIC,MAA0B,kBAAwB,CAAC,GAAGtM,CAAK,IACxDA,EAAM,QAAQ,MAAS,CAACoM,GAAqC3O,CAAI,CAAC;AAC3E;;;ACnEA,SAAS8O,GAAsC,GAAoC;CACjF,OAAOhR,GAA+BoD,CAAK,CAAC,EAAE;AAChD;AAWA,SAAS6N,EAAqB,GAAoC;CAChE,IAAI,OAAO7N,KAAU,YAAY,OAAO,SAASA,CAAK,KAAKA,IAAQ,GAAG,OAAOA;CAC7E,IAAI,OAAOA,KAAU,UAAU;EAC7B,IAAM8N,IAAS,OAAO9N,CAAK;EAC3B,IAAI,OAAO,SAAS8N,CAAM,KAAKA,IAAS,GAAG,OAAOA;CACpD;AAEF;AAEA,SAASC,GACP,GAC+B;CAC/B,IAAI,MAAM,QAAQ/N,CAAK,GAAG;EACxB,KAAK,IAAM2D,KAAS3D,GAAO;GACzB,IAAMyD,IAAYsK,GAA+CpK,CAAK;GACtE,IAAIF,GAAW,OAAOA;EACxB;EACA;CACF;CAEA,IAAMsH,IAAM6C,GAAsC5N,CAAK;CACvD,IAAK+K,GAEL;MAAI/K,KAAS,OAAOA,KAAU,YAAY,CAAC,MAAM,QAAQA,CAAK,GAAG;GAC/D,IAAMgB,IAAShB;GAMf,OAAO;IACL;IACA,UANC,OAAOgB,EAAO,YAAa,YAAYA,EAAO,YAC9C,OAAOA,EAAO,QAAS,YAAYA,EAAO,QAC1C,OAAOA,EAAO,eAAgB,YAAYA,EAAO,eAClD,KAAA;IAIA,OAAO6M,EAAqB7M,EAAO,KAAK;IACxC,QAAQ6M,EAAqB7M,EAAO,MAAM;IAC1C,MAAM6M,EAAqB7M,EAAO,QAAQA,EAAO,QAAQ;IACzD,QAAQ,OAAOA,EAAO,UAAW,WAAWA,EAAO,SAAS,KAAA;GAC9D;EACF;EAEA,OAAO,EAAE,OAAI;CAFb;AAGF;AAEA,SAASgN,EAAyB,GAA8D;CAC9F,IAAIlP,EAAK,aAAaA,EAAK,kBAAkB,OAAOA,EAAK,kBAAmB,UAAU;EACpF,IAAMmP,IAAYnP,EAAK;EACvB,IAAImP,EAAU,KACZ,OAAO;GACL,KAAKA,EAAU;GACf,UAAUA,EAAU;GACpB,OAAOA,EAAU;GACjB,QAAQA,EAAU;GAClB,MAAMA,EAAU;GAChB,QAAQA,EAAU;EACpB;CAEJ;CAcA,KAAK,IAAMvN,KAAO;EAXhB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CAGgB,GAAsB;EACtC,IAAI,EAAEA,KAAO5B,IAAO;EACpB,IAAM2E,IAAYsK,GAA+CjP,EAAK4B,EAAI;EAC1E,IAAI+C,GAAW,OAAOA;CACxB;AAGF;AAEA,SAASyK,GACP,GACA,GACA,GACA,GACA,GACA,GACM;CACN,IAAMC,IAAYC,EAAe,YAAY;CAE7C,EAAQ,aAAa,CACnB;EACE,KAAKA,EAAe;EACpB,MAAMD;EACN,GAAIC,EAAe,OAAO,EAAE,QAAQA,EAAe,KAAK,IAAI,CAAC;CAC/D,CACF;CAEA,IAAMC,IAAwC;EAC5C,KAAKD,EAAe;EACpB,MAAMD;EACN,QAAQ;CACV;CAMA,AAJIC,EAAe,UAAO,EAAa,QAAQA,EAAe,QAC1DA,EAAe,WAAQ,EAAa,SAASA,EAAe,SAC5DA,EAAe,SAAM,EAAa,WAAWA,EAAe,OAC5DA,EAAe,WAAQ,EAAa,SAASA,EAAe,SAChE,EAAQ,mBAAmB,CAACC,CAAY;CAExC,IAAMC,IAAqC,EAAE,KAAKF,EAAe,IAAI;CAKrE,AAJIA,EAAe,UAAO,EAAU,QAAQA,EAAe,QACvDA,EAAe,WAAQ,EAAU,SAASA,EAAe,SAC7D,EAAQ,qBAAqB,CAACE,CAAS,GAEnCxP,EAAK,UACP,EAAQ,iBAAiBA,EAAK;CAGhC,IAAMyP,IAAmBzP,EAAK,eAAeA,EAAK,WAAWA,EAAK,SAAS;CAK3E,IAJIyP,MACF,EAAQ,uBAAuBA,IAG7BzP,EAAK,OAAO;EACd,IAAM0P,IAAqB,CAAC;EAK5B,AAJAA,EAAS,KAAK,GAAG,OAAO1P,EAAK,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,QAAQ,MAAM2P,EAAE,SAAS,CAAC,CAAC,GACtF3P,EAAK,eACP0P,EAAS,KAAK,GAAG,OAAO1P,EAAK,WAAW,CAAC,CAAC,YAAY,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,QAAQ,MAAM2P,EAAE,SAAS,CAAC,CAAC,GAE9FD,EAAS,SAAS,MACpB,EAAQ,oBAAoB,CAAC,GAAG,IAAI,IAAIA,CAAQ,CAAC,CAAC,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,KAAK,IAAI;CAE7E;CAiCA,AA/BI,MAAM,QAAQ1P,EAAK,OAAO,KAAKA,EAAK,QAAQ,SAAS,IACvD,EAAQ,kBAAkBA,EAAK,QAAQ,KAAK,OAAiB;EAC3D,OAAO4P,EAAO,QAAQA,EAAO,eAAe;EAC5C,MAAM;CACR,EAAE,IACOC,MACT,EAAQ,kBAAkB,CAAC;EAAE,OAAOA,EAAW;EAAM,MAAM;CAAS,CAAC,IAGnEC,MACF,EAAQ,qBAAqBA,IAG3BnO,MACF,EAAQ,oBAAoB,CAC1B;EACE,OAAOA;EACP,QAAQ;CACV,CACF,IAGF,EAAQ,iBAAiB,CACvB;EACE,iBAAiB,CAAC4N,CAAY;EAC9B,mBAAmB,CAACC,CAAS;EAC7B,eAAexP,EAAK,SAAS;EAC7B,qBAAqByP;CACvB,CACF,GAGEH,EAAe,OACf,OAAOtP,EAAK,WAAY,YACxBA,EAAK,QAAQ,KAAK,MAAM,MACxB,CAACA,EAAK,QAAQ,SAASsP,EAAe,GAAG,MAGzC,EAAQ,qBAAqB,GAAG,aADDA,EAAe,IAAI,SAAStP,EAAK,SAAS,GAAG,GAAGsP,EAAe,QAAQ,WAAWA,EAAe,MAAM,KAAK,KAAKA,EAAe,SAAS,YAAYA,EAAe,OAAO,KAAK,GAAG,KACxK,IAAItP,EAAK;AAEvD;AAGA,IAAI+P,IAAoC;AAExC,SAASC,IAAgC;CAKvC,OAJA,AAEED,MAAe,IAAIlG,GADJK,GACiB,CAAM,GAEjC6F;AACT;AAKA,SAAgBE,KAA0B;CACxC,IAAe;AACjB;AAOA,IAAMC,KAAqB;AAE3B,eAAeC,GAAmB,GAAmC,GAA8B;CACjG,IAAIC,IAAO;CAOX,MAAM,QAAQ,IAAI,MAAM,KAAK,EAAE,QAAQ,KAAK,IAAIhO,GAAOiO,EAAM,MAAM,EAAE,GAAG,YAN9B;EACxC,OAAOD,IAAOC,EAAM,SAAQ;GAC1B,IAAMC,IAAOD,EAAM;GACnB,MAAMC,EAAK;EACb;CACF,CAC8E,CAAC;AACjF;AAGA,eAAeC,GACb,GACA,GACA,GAC+B;CAC/B,IAAMtH,IAAS,MAAMuH,EAAM,iBAAiBtH,CAAa;CACzD,IAAID,GAAQ,OAAOA;CACnB,EAAU,sCAAsCC,CAAa;CAC7D,IAAMe,IAAW,MAAMwG,EAAa,YAAYvH,CAAa;CAI7D,OADIe,KAAU,MAAMuG,EAAM,iBAAiBtH,GAAee,CAAQ,GAC3DA;AACT;AAGA,SAASyG,GAAuB,GAAiC;CAC/D,IAAKC,EAA+B1E,CAAG,GACvC,OAAO,IAAI,IAAIA,CAAG,CAAC,CAAC,SAAS,QAAQ,OAAO,EAAE;AAChD;AAOA,SAAS2E,GAAuB,GAAgB,GAAwC;CACtF,IAAM1O,IAASlC;CACf,IAAI2B,MAAe,SACjB,OAAQO,EAAO,wBAAwBA,EAAO;CAEhD,IAAMyC,IAAYuK,EAAyBhN,CAAM;CACjD,IAAI,GAACyC,KAAcA,EAAU,SAASA,EAAU,UAAUA,EAAU,WACpE,OAAO+L,GAAuB/L,EAAU,GAAG;AAC7C;AAGA,eAAekM,GACb,GACA,GACA,GACA,GACe;CACf,IAAM3O,IAASlC,GACTkJ,IAAgB0H,GAAuB5Q,GAAM2B,CAAU;CAC7D,IAAKuH,GACL,IAAI;EACF,IAAMe,IAAW,MAAMsG,GAAiBrH,GAAeuH,GAAcD,CAAK;EAC1E,IAAI,CAACvG,GAAU,SAAS;EAExB,IAAMtF,IAAYhD,MAAe,UAAU,KAAA,IAAYuN,EAAyBhN,CAAM;EAEtF,AADA,EAAO,iBAAiByC,IAAY;GAAE,GAAGsF;GAAU,KAAKtF,EAAU;EAAI,IAAIsF,GAC1E,EAAO,YAAY;CACrB,SAASvJ,GAAO;EACd,QAAQ,KAAK,yCAAyCwI,EAAc,IAAIxI,CAAK;CAC/E;AACF;AAGA,eAAeoQ,GACb,GACA,GACA,GACA,GACqB;CACrB,IAAMC,IAAWxO,EAAM,QAAQ,MAASqO,GAAuB5Q,GAAM2B,CAAU,CAAC;CAMhF,OALAoL,EAAU,0CAA0CgE,EAAS,MAAM,GACnE,MAAMZ,GACJY,EAAS,KAAK,YAAeF,GAAwB7Q,GAAM2B,GAAY8O,GAAcD,CAAK,CAAC,GAC3FN,EACF,GACO3N;AACT;AAGA,eAAeyO,GACb,GACA,GACA,GACqB;CACrB,IAAMxG,IAAgBgG,EAAM,UAAU,CAAC,CAAC;CAMxC,OALKhG,GAAe,UAKbsG,GAAyBvO,GAAOZ,GAAY,IAJ1BiK,GAAoB;EAC3C,UAAUpB,EAAc;EACxB,SAASA,EAAc;CACzB,CACmD,GAAcgG,CAAK,IALlCjO;AAMtC;AAMA,SAAS0O,GACP,GACA,GACO;CACP,IAAM,EAAE,gBAAa,gBAAa,2BAAwB,iBAAiB3S;CAO3E,OALiBsQ,GACfrM,GACAsM,CAGK,CAAA,CAAS,KAAK,MAAc;EAEjC,IAAMqC,IAASlR,EAAK,MAAMA,EAAK,WAAWA,EAAK,WAAWA,EAAK,wBAAwBA,EAAK,wBAGtFmR,IAAc,GAAG5Q,EAAY,QAAQ,OAAO,EAAE,EAAE,GAAGC,EAAY,QAAQ,OAAO,EAAE,EAAE,GAAG0Q,KACrFE,IAAUpR,EAAK,QAAQA,EAAK,QAAQA,EAAK,cAAcA,EAAK,aAAamR,GAG3E3R;EACJ,IAAIQ,EAAK,WAAWA,EAAK,SAAS;GAEhC,IAAMqR,IAAarR,EAAK,WAAWA,EAAK;GACxC,IAAO,IAAI,KAAKqR,CAAU;EAC5B,OAAO,IAAIrR,EAAK,aAEd,oBAAO,IAAI,KAAKA,EAAK,cAAc,GAAI;OAClC,IAAIA,EAAK,eAAeA,EAAK,aAAaA,EAAK,WAAW;GAC/D,IAAMsR,IAAYtR,EAAK,eAAeA,EAAK,aAAaA,EAAK;GAC7D,IAAOsR,KAAa,OAAOA,KAAc,YAAYA,EAAU,gBAAgB,OAC3EA,IACA,IAAI,KAAKA,CAA4B;EAC3C,OACE,oBAAO,IAAI,KAAK;EAGlB,IAAMC,IAAclD,EAAwBrO,CAAI,GAC1C4J,IAAU0E,GAAoBtO,CAAI,GAGlCwR,IAAgB;GACpB,GAAGxR;GAEH,IAAIkR;GACJ,OAAOlR,EAAK,SAASA,EAAK,SAAS;GACnC,MAAMoR;GACN;GACA;GACA,SAAS5R;GACH;GAEN,MAAMQ,EAAK,QAAQA,EAAK,QAAQA,EAAK,QAAQA,EAAK,QAAQoR;EAC5D;EAiCA,OA/BI,OAAOI,EAAS,QAAS,YAAYA,EAAS,SAAS,KAAA,MACzD,EAAS,OAAOA,EAAS,OAEvB,OAAOA,EAAS,QAAS,YAAYA,EAAS,SAAS,KAAA,MACzD,EAAS,OAAOA,EAAS,OAEvB,OAAOA,EAAS,QAAS,YAAYA,EAAS,SAAS,KAAA,MACzD,EAAS,OAAOA,EAAS,OAEvB,OAAOA,EAAS,QAAS,YAAYA,EAAS,SAAS,KAAA,MACzD,EAAS,OAAOA,EAAS,OAIvBxR,EAAK,kBAAkBA,EAAK,cAE9B,EAAS,iBADwBA,EAAK,gBAEtC,EAAS,YAAY,KAIvB,OAAO,KAAKwR,CAAQ,CAAC,CAAC,SAAS,MAAQ;GACrC,IAAMtQ,IAAQsQ,EAAS5P;GACvB,IAAI,OAAOV,KAAU,YAAY,uCAAuC,KAAKU,CAAG,KAAKA,MAAQ,aAAaA,MAAQ,QAAQ;IACxH,IAAM0P,IAAY,IAAI,KAAKpQ,CAAK;IAChC,AAAK,MAAMoQ,EAAU,QAAQ,CAAC,MAC5B,EAAS1P,KAAO0P;GAEpB;EACF,CAAC,GAEME;CACT,CAAC;AACH;AAkBA,IAAaC,KACX,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,MACoB;CACpB,IAAM7S,IAAaC,EAAkB6S,CAAmB,GAClDC,IAAiB1T,EAAU0D,CAAU,GAErCiQ,IAAc,GAAGhT,EAAW,QAAQ,GAAG+S,EAAe,GAAG7I,KACzD+I,IAAUC,IAAY,GAAGF,EAAY,KAAKE,MAAcF,GACxDG,IAAY,GAAGnT,EAAW,MAAM,KAAKoT,GAAWL,CAAc,KAC9DxI,oBAAM,IAAI,KAAK,GAEfpH,IAAa3C,EAAe,GAG5B6S,IAA6D,CAAC;CACpE,IAAIC,GAAY;EACd,IAAMnT,IAAOmT,EAAW,QAAQ,QAAQ,SAAS,EAAE,GAC7CC,IAAMpT,EAAK,SAAS,GAAG,IAAI,MAAM;EAIvC,AAHImT,EAAW,WACbD,EAAM,KAAK;GAAE,KAAK;GAAQ,MAAM,GAAGlT,IAAOoT,EAAI,OAAOD,EAAW,OAAO;EAAI,CAAC,GAE1EA,EAAW,OAAO,MACpBD,EAAM,KAAK;GAAE,KAAK;GAAY,MAAM,GAAGlT,IAAOoT,EAAI,OAAOD,EAAW,OAAO;EAAI,CAAC,GAChFD,EAAM,KAAK;GAAE,KAAK;GAAS,MAAM,GAAGlT,IAAOoT,EAAI;EAAQ,CAAC;CAE5D;CACA,IAAIC,GAAc,QAChB,KAAK,IAAMC,KAAMD,GACf,EAAM,KAAK;EAAE,KAAKC,EAAG;EAAK,MAAMA,EAAG;CAAK,CAAC;CAK7C,IAAMC,IAAmBrB,GAAqB1O,GAAO;EACnD;EACA,aAAaR,EAAW;EACxB,aAAaA,EAAW;EACxB,uBACEwQ,GAAoB,yBAAyBxQ,EAAW;CAC5D,CAAC;CAGD,QAAQ+G,GAAR;EACE,KAAK,QAAQ;GAEX,IAAM0J,IAAkE,CACtE;IAAE,MAAMX;IAAS,KAAK;GAAO,GAC7B,EAAE,MAAMjT,EAAW,QAAQ,CAC7B;GACA,KAAK,IAAM6J,KAAQwJ,GACjB,EAAU,KAAK;IACb,MAAMxJ,EAAK;IACX,KAAKA,EAAK;IACV,MAAMA,EAAK;GACb,CAAC;GAEH,IAAMgK,IAAW;IACf,IAAIZ;IACJ,OAAOE;IACP,SAAS5I;IACT,OAAOqJ;IACP,UAAU5T,EAAW;IACrB,QAAQA,EAAW;IACnB,QAAQA,EAAW,SAAS;KAC1B,MAAMA,EAAW,OAAO;KACxB,OAAOA,EAAW,OAAO;KACzB,KAAKA,EAAW,OAAO;IACzB,IAAI,KAAA;IACJ,SAAS0T,EAAiB,KAAK,MAAS;KAEtC,IAAMzN,IAAa;MACjB,IAAI7E,EAAK,MAAMA,EAAK;MACpB,OAAOA,EAAK,SAAS;MACrB,SAASA,EAAK,QAAQA,EAAK,WAAWmJ;MACtC,OAAOnJ,EAAK,OAAO,CAAC,EAAE,MAAMA,EAAK,KAAK,CAAC,IAAI,CAAC;MAC5C,GAAGA;KACL;KAKA,IAJIA,EAAK,YAAS,EAAM,UAAUA,EAAK,UACnCA,EAAK,gBAAa,EAAM,UAAUA,EAAK,cAGvCA,EAAK,kBAAkBA,EAAK,WAAW;MACzC,IAAMmP,IAA2BnP,EAAK;MAUtC,IATA,AAAkB6E,EAAM,UAAQ,CAAC,GACjCA,EAAM,MAAM,KAAK;OACf,MAAMsK,EAAU;OAChB,KAAK;OACL,MAAMA,EAAU,YAAY;OAC5B,QAAQA,EAAU;MACpB,CAAC,GAGGA,EAAU,OAAOtK,EAAM,WAAW,CAACA,EAAM,QAAQ,SAASsK,EAAU,GAAG,GAAG;OAC5E,IAAMuD,IAAY,aAAavD,EAAU,IAAI,SAASnP,EAAK,SAAS,GAAG,GAAGmP,EAAU,QAAQ,WAAWA,EAAU,MAAM,KAAK,KAAKA,EAAU,SAAS,YAAYA,EAAU,OAAO,KAAK,GAAG;OACzL,EAAM,UAAU,OAAOtK,EAAM,WAAY,WACrC,GAAG6N,EAAU,IAAI7N,EAAM,YACvB;QAAE,MAAM;QAAQ,OAAO,GAAG6N,EAAU,IAAI7N,EAAM,QAAQ,SAASA,EAAM;OAAU;MACrF;KACF;KAEA,OAAOA;IACT,CAAC;GACH;GACA,OAAO,QAAQ,QAAQ8N,GAAiBF,CAAQ,CAAW;EAC7D;EACA,KAAK,QAAQ;GAEX,IAAMG,IAAUV,GAAY,UACxB,GAAGA,EAAW,QAAQ,QAAQ,SAAS,EAAE,EAAE,QAAQA,EAAW,OAAO,MACrE,KAAA,GACEW,IAAW;IACf,OAAOd;IACP,eAAenT,EAAW;IAC1B,UAAUiT;IACV,GAAIe,KAAW,EAAE,UAAUA,EAAQ;IACnC,aAAahU,EAAW;IACxB,QAAQA,EAAW,SAAS;KAC1B,MAAMA,EAAW,OAAO;KACxB,KAAKA,EAAW,OAAO;IACzB,IAAI,KAAA;IACJ,OAAO0T,EAAiB,KAAK,MAAS;KAEpC,IAAMQ,IAAgB;MACpB,IAAI9S,EAAK,MAAMA,EAAK;MACpB,GAAGA;KACL;KAQA,IAPIA,EAAK,UAAO,EAAS,QAAQA,EAAK,QAClCA,EAAK,SAAM,EAAS,MAAMA,EAAK,OAC/BA,EAAK,YAAS,EAAS,eAAeA,EAAK,UAC3CA,EAAK,gBAAa,EAAS,UAAUA,EAAK,eAC1CA,EAAK,QAAQA,EAAK,aAAS,EAAS,iBAAiBA,EAAK,QAAQA,EAAK,UAGvEA,EAAK,kBAAkBA,EAAK,WAAW;MACzC,IAAMmP,IAA2BnP,EAAK;MAatC,AAZA,EAAS,QAAQmP,EAAU,KAG3B,AAA2B2D,EAAS,gBAAc,CAAC,GACnDA,EAAS,YAAY,KAAK;OACxB,KAAK3D,EAAU;OACf,WAAWA,EAAU,YAAY;OACjC,eAAeA,EAAU;OACzB,OAAOnP,EAAK,SAAS;MACvB,CAAC,GAGGmP,EAAU,OAAO2D,EAAS,gBAAgB,CAACA,EAAS,aAAa,SAAS3D,EAAU,GAAG,MAEzF,EAAS,eAAe,GAAG,aADIA,EAAU,IAAI,SAASnP,EAAK,SAAS,GAAG,GAAGmP,EAAU,QAAQ,WAAWA,EAAU,MAAM,KAAK,KAAKA,EAAU,SAAS,YAAYA,EAAU,OAAO,KAAK,GAAG,KACpJ,IAAI2D,EAAS;KAEtD;KAEA,OAAOA;IACT,CAAC;GACH,GACMC,IAAaC,GAAiBH,CAAQ;GAC5C,OAAO,QAAQ,QAAQ,OAAOE,KAAe,WAAWA,IAAa,KAAK,UAAUA,CAAU,CAAC;EACjG;EAEA,SAAS;GAEP,IAAM3K,IAAU;IACd,OAAO2J;IACP,MAAMnT,EAAW;IACjB,aAAaA,EAAW;IACxB,OAAOqT,EAAM,SAASA,EAAM,KAAK,OAAO;KAAE,KAAKgB,EAAE;KAAK,MAAMA,EAAE;KAAM,MAAMA,EAAE;IAAK,EAAE,IAAI,KAAA;IACvF,WAAWC,KAAa;IACxB,UAAUtU,EAAW;IACrB,WAAWA,EAAW;IACtB,WAAWA,EAAW,QAAQ;IAC9B,SAASuK;IACT,eAAeA;IACf,OAAOmJ,EAAiB,KAAK,MAAc;KAEzC,IAAMa,IAAe,EACnB,GAAGnT,EACL;KASA,AANIA,EAAK,UAAO,EAAQ,QAAQA,EAAK,QACjCA,EAAK,SAAM,EAAQ,OAAOA,EAAK,OAC/BA,EAAK,gBAAa,EAAQ,cAAcA,EAAK,eAC7CA,EAAK,QAAQA,EAAK,aAAS,EAAQ,UAAUA,EAAK,QAAQA,EAAK,UAG/DA,EAAK,OACP,EAAQ,OAAO;MACb,OAAOA,EAAK;MACZ,aAAa,OAAOA,EAAK,QAAS,aAAaA,EAAK,KAAK,WAAW,SAAS,KAAKA,EAAK,KAAK,WAAW,UAAU;KACnH,IACSA,EAAK,OACd,EAAQ,OAAO;MACb,OAAOA,EAAK;MACZ,aAAa,OAAOA,EAAK,MAAO,aAAaA,EAAK,GAAG,WAAW,SAAS,KAAKA,EAAK,GAAG,WAAW,UAAU;KAC7G;KAGF,IAAMsP,IAAiBJ,EAAyBlP,CAAI;KA2BpD,IA1BIsP,KACFF,GACE+D,GACAnT,GACAsP,GACA3N,GACA/C,EAAW,QACXA,EAAW,SACb,GAIA,OAAOoB,EAAK,WAAY,YACxBA,EAAK,QAAQ,KAAK,MAAM,MACxBmT,EAAQ,uBAAuB,KAAA,MAE/B,EAAQ,qBAAqBnT,EAAK,UAIpC,EAAQ,KAAK,CAAC,IAGVA,EAAK,QAAQA,EAAK,aACpB,EAAQ,GAAG,OAAOA,EAAK,QAAQA,EAAK,UAElCA,EAAK,aAAa;MAEpB,IAAMoT,oBAAkB,IAAI,KAAKpT,EAAK,cAAc,GAAI;MAExD,AADKmT,EAAQ,GAAG,UAAO,EAAQ,GAAG,QAAQ,CAAC,IAC3CA,EAAQ,GAAG,MAAM,KAAKC,CAAe;KACvC;KAwBA,QArBIpT,EAAK,WAAWA,EAAK,aAClBmT,EAAQ,GAAG,eAAY,EAAQ,GAAG,aAAa,CAAC,IACrDA,EAAQ,GAAG,WAAW,KAAKnT,EAAK,WAAWA,EAAK,OAAO,KAErDA,EAAK,wBAAwBA,EAAK,4BAC/BmT,EAAQ,GAAG,eAAY,EAAQ,GAAG,aAAa,CAAC,IACrDA,EAAQ,GAAG,WAAW,KAAKnT,EAAK,wBAAwBA,EAAK,sBAAsB,KAIjFA,EAAK,cAAcA,EAAK,eAC1B,EAAQ,GAAG,SAASA,EAAK,cAAcA,EAAK,aAI1CA,EAAK,WAAWA,EAAK,aAAS,EAAQ,UAAUA,EAAK,WAAWA,EAAK,WACrEA,EAAK,wBAAwBA,EAAK,4BACpC,EAAQ,uBAAuBA,EAAK,wBAAwBA,EAAK,yBAE/DA,EAAK,gBAAa,EAAQ,cAAcA,EAAK,cAE1CmT;IACT,CAAC;GACH;GACA,OAAO,QAAQ,QAAQlL,GAAeG,CAAO,CAAC;EAChD;CACF;AACF;AAMA,SAASiL,GAAe,GAA4B;CAClD,QAAQvK,GAAR;EACE,KAAK,QACH,OAAO;EACT,KAAK,QACH,OAAO;EAET,SACE,OAAO;CACX;AACF;AAEA,SAASwK,GAAY,GAAoC;CACvD,IAAMC,IAAaC,EAAQ,YAAY;CAIvC,OAHI;EAAC;EAAO;EAAQ;CAAM,CAAC,CAAC,SAASD,CAAU,IACtCA,IAEF;AACT;AAEA,SAASvB,GAAW,GAAqB;CACvC,OAAOnO,EAAI,OAAO,CAAC,CAAC,CAAC,YAAY,IAAIA,EAAI,MAAM,CAAC;AAClD;AAGA,SAAS4P,GAAgB,GAId;CACT,OAAO1I,EACL,KAAK,UAAU;EAAC2I,EAAO,cAAc;EAAMA,EAAO;EAAYA,EAAO,aAAa;CAAI,CAAC,CACzF;AACF;AAqBA,SAASC,EACP,GACA,GACA,GACA,IAAuC,CAAC,GAC9B;CACV,IAAMC,IAAkC;EACtC,MAAM5J,EAAK;EACX,kCAAiB,IAAI,KAAKA,EAAK,eAAe,GAAI,EAAA,CAAE,YAAY;EAChE,iBAAiB,mBAAmB6J,EAAQ,OAAO,aAAaA,EAAQ,OAAO;EAC/E,GAAGC;CACL;CAMA,OALIC,MAAW,QACb,EAAQ,kBAAkB/J,EAAK,eAAeqJ,GAAeQ,EAAQ,MAAM,GAC3E,EAAQ,mBAAmBA,EAAQ,YACnC,EAAQ,mBAAmBA,EAAQ,SAE9B,IAAI,SAASE,MAAW,MAAM,OAAO/J,EAAK,SAAS;EAAE;EAAQ;CAAQ,CAAC;AAC/E;AAGA,SAASgK,EACP,GACA,GACA,GACA,GACU;CAMV,OALIH,EAAQ,eAAerI,GAAiBqI,EAAQ,aAAa5K,EAAO,IAAI,KAC1E8D,EAAU,qBAAqBkH,GAAQJ,EAAQ,YAAYA,EAAQ,MAAM,GAClEF,EAAaE,GAAS;EAAE,GAAG5K;EAAQ,SAAS;CAAK,GAAG,GAAG,MAEhE8D,EAAU,gBAAgBkH,GAAQJ,EAAQ,YAAYA,EAAQ,MAAM,GAC7DF,EAAaE,GAAS5K,GAAQ,KAAK;EAAE,WAAWgL;EAAQ,GAAGH;CAAa,CAAC;AAClF;AAOA,eAAeI,GAAU,GAAyC;CAChE,IAAM,EAAE,eAAY,WAAQ,yBAAsBL,GAC5CrD,IAAQR,EAAgB,GACxBmE,IAAe3D,EAAM,UAAU,CAAC,CAAC;CAEvC,IAAI;EACF,IAAI4D;EACJ,AAAIP,EAAQ,eAAY,IAAU,MAAMA,EAAQ,WAAW;EAC3D,IAAM/J,IAAc2J,GAAgB;GAClC,YAAYW,GAAS;GACrB,YAAYP,EAAQ;GACpB,WAAWA,EAAQ;EACrB,CAAC;EAED,IAAIM,GAAc;GAChB,IAAMlL,IAAS,MAAMuH,EAAM,eAAe7O,GAAYmH,GAAQE,GAAmBc,CAAW;GAC5F,IAAIb,GAAQ,OAAO+K,EAAmBH,GAAS5K,GAAQ,KAAK;EAC9D;EAGA,AADA,MAAY,MAAM4K,EAAQ,WAAW,GACrC9G,EAAU,gCAAgCpL,GAAYmH,GAAQsL,EAAQ,MAAM,MAAM;EAClF,IAAMxK,IAAU,MAAMiK,EAAQ,OAAOO,EAAQ,KAAK,GAC5C/H,IAAcgH,GAAevK,CAAM,GACnCqC,IAAOiJ,EAAQ,aACjBrJ,EAAa,GAAGjB,EAAY,GAAGjB,EAAgBlH,GAAYmH,GAAQE,CAAiB,GAAG,IACvF+B,EAAanB,CAAO,GAClByK,IAASF,IACX,MAAM3D,EAAM,eACV7O,GACAmH,GACA;GAAE;GAAS;GAAa;GAAM;EAAY,GAC1CE,CACF,IACA;EAEJ,OADIqL,IAAeL,EAAmBH,GAASQ,GAAQ,MAAM,IACtDV,EACLE,GACA;GAAE;GAAS;GAAa;GAAM,cAAc,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;EAAE,GAC1E,GACF;CACF,SAASnT,GAAO;EAEd,IADA,QAAQ,MAAM,6BAA6BiB,EAAW,GAAGmH,EAAO,IAAIpI,CAAK,GACrEyT,GAAc;GAChB,IAAMG,IAAQ,MAAM9D,EAAM,eAAe7O,GAAYmH,GAAQE,CAAiB;GAC9E,IAAIsL,GACF,OAAON,EAAmBH,GAASS,GAAO,SAAS,EAAE,SAAS,8BAA4B,CAAC;EAE/F;EACA,OAAO,IAAI,SACT,KAAK,UAAU;GACb,OAAO;GACP,SAAS5T,aAAiB,QAAQA,EAAM,UAAU;EACpD,CAAC,GACD;GAAE,QAAQ;GAAK,SAAS,EAAE,gBAAgB,mBAAmB;EAAE,CACjE;CACF;AACF;AAEA,SAAS6T,GAAsB,GAAiC;CAC9D,OAAO,IAAI,SAAS,KAAK,UAAU,EAAE,OAAO,wBAAwBC,IAAgB,CAAC,GAAG;EACtF,QAAQ;EACR,SAAS,EAAE,gBAAgB,mBAAmB;CAChD,CAAC;AACH;AAgBA,eAAsBC,GACpB,GACA,GACA,GACA,GACA,GACA,GACmB;CACnB,IAAM3L,IAASwK,GAAYkB,CAAa;CACxC,IAAI,CAAC1L,GAAQ,OAAOyL,GAAsBC,CAAa;CAGvD,IAAM7S,IAAa1D,EAAU,SAASyW,EAAkB,YAAY,CAAC,GAC/D/C,IAAiB1T,EAAU0D,CAAU,GACrC/C,IAAaC,EAAkB6S,CAAmB,GAElDiD,IAAU,KAAK,IAAI,GAAGC,KAAQ,CAAC,GAC/BC,IAAWzV,EAAe,CAAC,CAAC,UAC5BoR,IAAQR,EAAgB,GAC1B8E,IAAU;CAEd,OAAOZ,GAAU;EACf;EACA;EACA,mBAAmBS,IAAU,IAAI,EAAE,MAAMA,EAAQ,IAAI,KAAA;EACrD;EACA;EACA;EACA,QAAQA,MAAY,IAAKI,EAAqB,CAAC,CAAC,UAAU,CAAC,CAAC,UAAU,IAAK;EAC3E,YAAYJ,MAAY;EACxB,YAAY,YAAY;GAEtB,IAAM,EAAE,UAAO,YAAS,MAAMxS,EAA2BR,GAAY;IACnE,OAAOkT,IAAW;IAClB,OAAOF,IAAU,KAAKE;GACxB,CAAC;GAED,OADA,IAAUtS,EAAM,SAASsS,GAClB;IAAE,OAAOtS,EAAM,MAAM,GAAGsS,CAAQ;IAAiB,YAAY1J;GAAK;EAC3E;EACA,QAAQ,OAAO,MAAU;GACvB,IAAMiH,IAAkC,CAAC;GACzC,IAAIuC,MAAY,GAAG;IAEjB,IAAMxL,oBAAM,IAAI,KAAK,GACf6L,IAAY,IAAI,KAAK,KAAK,IAAI7L,EAAI,eAAe,GAAGA,EAAI,YAAY,IAAI,GAAG,CAAC,CAAC;IACnF,EAAa,KAAK;KAChB,KAAK;KACL,MAAM,GAAGvK,EAAW,QAAQ,GAAG+S,EAAe,WAAWqD,EAAU,eAAe,EAAE,GAAGA,EAAU,YAAY,IAAI,EAAE,GAAGlM;IACxH,CAAC;GACH;GACA,OAAO2I,EACL,MAAMT,GAAkBzO,GAAOZ,GAAY6O,CAAK,GAChD7O,GACAmH,GACAgJ,GACA;IAAE,MAAM6C;IAAS;IAAU;IAAS,SAAS,GAAG/V,EAAW,QAAQ,GAAG+S,EAAe,GAAG7I;GAAS,GACjGsJ,EAAa,SAASA,IAAe,KAAA,GACrC,IACA,KAAA,GACAV,CACF;EACF;CACF,CAAC;AACH;AASA,eAAsBuD,GACpB,GACA,GACA,GACA,GACA,GACA,GACA,GACmB;CACnB,IAAInV,IAAO,QAAQA,IAAO,MACxB,OAAO,IAAI,SACT,KAAK,UAAU,EAAE,OAAO,iBAAiBA,IAAO,CAAC,GACjD;EAAE,QAAQ;EAAK,SAAS,EAAE,gBAAgB,mBAAmB;CAAE,CACjE;CAEF,IAAID,IAAQ,KAAKA,IAAQ,IACvB,OAAO,IAAI,SACT,KAAK,UAAU,EAAE,OAAO,kBAAkBA,IAAQ,CAAC,GACnD;EAAE,QAAQ;EAAK,SAAS,EAAE,gBAAgB,mBAAmB;CAAE,CACjE;CAGF,IAAMiJ,IAASwK,GAAYkB,CAAa;CACxC,IAAI,CAAC1L,GAAQ,OAAOyL,GAAsBC,CAAa;CAEvD,IAAM7S,IAAa1D,EAAU,SAASyW,EAAkB,YAAY,CAAC,GAC/D/C,IAAiB1T,EAAU0D,CAAU,GACrC/C,IAAaC,EAAkB6S,CAAmB,GAClDlB,IAAQR,EAAgB;CAE9B,OAAOkE,GAAU;EACf;EACA;EACA,mBAAmB,EAAE,SAAS;GAAE;GAAM;EAAM,EAAE;EAC9C;EACA;EACA;EACA,QAAQ;EACR,YAAY;EACZ,YAAY,aAAa,EACvB,OAAQ,MAAMzR,EAAiCd,GAAY7B,GAAMD,CAAK,EACxE;EACA,QAAQ,OAAO,MAAU;GACvB,IAAMqV,IAAU,GAAGtW,EAAW,QAAQ,GAAG+S,EAAe,GAAG7I,KACrDqM,IAAc,GAAGvW,EAAW,QAAQ,GAAG+S,EAAe,WACtDS,IAAkC,CACtC;IAAE,KAAK;IAAgB,MAAM,GAAG+C,EAAY,GAAGtV,MAAU,IAAIC,IAAO,IAAIA,EAAK,GAAGD,MAAU,IAAI,KAAKA,IAAQ,EAAE,GAAGiJ;GAAS,GACzH;IAAE,KAAK;IAAW,MAAMoM;GAAQ,CAClC,GACME,IAAWvV,MAAU,KAAKC,IAAO,IAAIA,GACrCuV,IAAYxV,MAAU,KAAK,IAAIA,IAAQ,GACvCsJ,oBAAM,IAAI,KAAK;GAIrB,QAHIiM,IAAWjM,EAAI,eAAe,KAAMiM,MAAajM,EAAI,eAAe,KAAKkM,KAAalM,EAAI,YAAY,IAAI,MAC5GiJ,EAAa,KAAK;IAAE,KAAK;IAAgB,MAAM,GAAG+C,EAAY,GAAGC,EAAS,GAAGC,EAAU,GAAGvM;GAAS,CAAC,GAE/F2I,EACL,MAAMT,GAAkBzO,GAAOZ,GAAY6O,CAAK,GAChD7O,GACAmH,GACAgJ,GACA,KAAA,GACAM,GACA,IACA,KAAA,GACAV,CACF;EACF;CACF,CAAC;AACH"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seedprotocol/feed",
3
- "version": "0.7.10",
3
+ "version": "0.7.11-next.1384.g8b47246",
4
4
  "description": "RSS 2.0, Atom and JSON Feed generation for Seed Protocol items, with HTTP handlers, ETag caching and RFC 5005 archives",
5
5
  "keywords": [
6
6
  "seed-protocol",
@@ -36,9 +36,9 @@
36
36
  "prepublishOnly": "bun run build && node ../../scripts/sync-versions.js"
37
37
  },
38
38
  "dependencies": {
39
- "@seedprotocol/arweave": "0.7.10",
40
- "@seedprotocol/eas": "0.7.10",
41
- "@seedprotocol/query": "0.7.10",
39
+ "@seedprotocol/arweave": "0.7.11-next.1384.g8b47246",
40
+ "@seedprotocol/eas": "0.7.11-next.1384.g8b47246",
41
+ "@seedprotocol/query": "0.7.11-next.1384.g8b47246",
42
42
  "debug": "^4.4.1",
43
43
  "feedsmith": "^2.9.0",
44
44
  "image-size": "^1.1.1",