@seedprotocol/feed 0.5.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -173,17 +173,21 @@ function I(e, t) {
173
173
  }
174
174
  var ye = async (e, t) => {
175
175
  await D();
176
- let n = M(), { items: r } = await te(e, {
177
- limit: t?.limit ?? 100,
178
- skip: t?.skip ?? 0,
176
+ let n = M(), r = t?.limit ?? 100, i = t?.skip ?? 0, { items: a } = await te(e, {
177
+ limit: r,
178
+ skip: i,
179
179
  expandRelations: n.expandRelations !== !1,
180
180
  hydrateStorage: !0
181
181
  });
182
- return I(e, r);
183
- }, be = async (e, t, n) => (await D(), I(e, await ne(e, t, n, {
184
- expandRelations: M().expandRelations !== !1,
185
- hydrateStorage: !0
186
- })));
182
+ return I(e, a);
183
+ }, be = async (e, t, n) => {
184
+ await D();
185
+ let r = M();
186
+ return I(e, await ne(e, t, n, {
187
+ expandRelations: r.expandRelations !== !1,
188
+ hydrateStorage: !0
189
+ }));
190
+ };
187
191
  //#endregion
188
192
  //#region src/consume/parseRss.ts
189
193
  function xe(e) {
@@ -214,7 +218,7 @@ async function Ce(e) {
214
218
  var L = "http://purl.org/dc/elements/1.1/", R = "http://purl.org/rss/1.0/modules/content/", z = "http://search.yahoo.com/mrss/", we = { publication: {
215
219
  prefix: "publication",
216
220
  uri: "https://seedprotocol.io/ns/publication/1.0"
217
- } }, Te = "https://seedprotocol.io/ns/relations", Ee = new Set([
221
+ } }, Te = "https://seedprotocol.io/ns/relations", Ee = /* @__PURE__ */ new Set([
218
222
  "xml",
219
223
  "xmlns",
220
224
  "dc",
@@ -222,7 +226,7 @@ var L = "http://purl.org/dc/elements/1.1/", R = "http://purl.org/rss/1.0/modules
222
226
  "content",
223
227
  "atom",
224
228
  "fh"
225
- ]), De = new Set([
229
+ ]), De = /* @__PURE__ */ new Set([
226
230
  "Title",
227
231
  "Link",
228
232
  "Guid",
@@ -269,16 +273,19 @@ function ke(e) {
269
273
  }
270
274
  function H(e, t) {
271
275
  let n = String(t);
272
- if (n !== "") if (ke(n)) if (n.includes("]]>")) {
273
- let t = n.split("]]>");
274
- for (let n = 0; n < t.length; n++) e.dat(t[n]), n < t.length - 1 && e.txt("]]>");
275
- } else e.dat(n);
276
- else e.txt(n);
276
+ if (n !== "") {
277
+ if (ke(n)) {
278
+ if (n.includes("]]>")) {
279
+ let t = n.split("]]>");
280
+ for (let n = 0; n < t.length; n++) e.dat(t[n]), n < t.length - 1 && e.txt("]]>");
281
+ } else e.dat(n);
282
+ } else e.txt(n);
283
+ }
277
284
  }
278
285
  function U(e) {
279
286
  return typeof e == "object" && !!e && !Array.isArray(e) && !(e instanceof Date);
280
287
  }
281
- var Ae = 4, je = new Set([
288
+ var Ae = 4, je = /* @__PURE__ */ new Set([
282
289
  ...B,
283
290
  "guid",
284
291
  "source",
@@ -309,7 +316,7 @@ function Ie(e) {
309
316
  let t = { ...we }, n = new Set(Object.values(t).map((e) => e.prefix)), r = /* @__PURE__ */ new Set();
310
317
  for (let t of e) for (let [e, n] of Object.entries(t)) {
311
318
  let t = e === "author" && U(n) || e === "authors" && Fe(n);
312
- e.startsWith("_") || je.has(e) && !t || (U(n) || Fe(n)) && r.add(Me(e));
319
+ e.startsWith("_") || (!je.has(e) || t) && (U(n) || Fe(n)) && r.add(Me(e));
313
320
  }
314
321
  let i = [...r].sort();
315
322
  for (let e of i) t[e] || (t[e] = {
@@ -381,14 +388,16 @@ function Re(e, t, n, r, i) {
381
388
  }
382
389
  if (t === "dc" && typeof n == "object" && n) {
383
390
  let t = n;
384
- for (let [n, r] of Object.entries(t)) if (r != null) if (Array.isArray(r)) {
385
- for (let t of r) if (t != null) {
386
- let r = e.ele(L, n);
387
- t instanceof Date ? r.txt(t.toISOString()) : H(r, String(t));
391
+ for (let [n, r] of Object.entries(t)) if (r != null) {
392
+ if (Array.isArray(r)) {
393
+ for (let t of r) if (t != null) {
394
+ let r = e.ele(L, n);
395
+ t instanceof Date ? r.txt(t.toISOString()) : H(r, String(t));
396
+ }
397
+ } else {
398
+ let t = e.ele(L, n);
399
+ r instanceof Date ? t.txt(r.toISOString()) : H(t, String(r));
388
400
  }
389
- } else {
390
- let t = e.ele(L, n);
391
- r instanceof Date ? t.txt(r.toISOString()) : H(t, String(r));
392
401
  }
393
402
  return;
394
403
  }
@@ -614,7 +623,8 @@ var qe = class {
614
623
  return T(this.cacheDir, `${r}.json`);
615
624
  }
616
625
  getImageMetadataPath(e) {
617
- return T(T(this.cacheDir, "image-metadata"), `${e}.json`);
626
+ let t = T(this.cacheDir, "image-metadata");
627
+ return T(t, `${e}.json`);
618
628
  }
619
629
  async ensureImageMetadataDir() {
620
630
  try {
@@ -759,7 +769,7 @@ var qe = class {
759
769
  }
760
770
  }
761
771
  async setImageMetadata(e, t) {
762
- if (!(!this.config.enabled || !this.config.imageMetadata?.enabled)) try {
772
+ if (this.config.enabled && this.config.imageMetadata?.enabled) try {
763
773
  this.memoryCache.setImageMetadata(e, t), await this.fileCache.setImageMetadata(e, t);
764
774
  } catch (t) {
765
775
  console.error(`Error setting image metadata cache for ${e}:`, t), this.stats.errors++;
@@ -1003,7 +1013,7 @@ function rt(e) {
1003
1013
  return n === "" ? J(e, $e) : n;
1004
1014
  }
1005
1015
  function Y(e) {
1006
- return typeof e == "string" ? e.includes("data:image/") && e.includes(";base64,") : !1;
1016
+ return typeof e == "string" && e.includes("data:image/") && e.includes(";base64,");
1007
1017
  }
1008
1018
  function it(e) {
1009
1019
  let t = x(e);
@@ -1267,7 +1277,7 @@ var $ = (e, t, n, r, i, a, o, s, c) => {
1267
1277
  return Promise.resolve(ie(t));
1268
1278
  }
1269
1279
  case "json": {
1270
- let e = i?.hasNext ? `${i.baseUrl.replace(/\?.*$/, "")}?page=${i.page + 1}` : void 0, t = ae({
1280
+ let e = i?.hasNext ? `${i.baseUrl.replace(/\?.*$/, "")}?page=${i.page + 1}` : void 0, t = {
1271
1281
  title: p,
1272
1282
  home_page_url: l.siteUrl,
1273
1283
  feed_url: f,
@@ -1293,8 +1303,8 @@ var $ = (e, t, n, r, i, a, o, s, c) => {
1293
1303
  }
1294
1304
  return t;
1295
1305
  })
1296
- });
1297
- return Promise.resolve(typeof t == "string" ? t : JSON.stringify(t));
1306
+ }, n = ae(t);
1307
+ return Promise.resolve(typeof n == "string" ? n : JSON.stringify(n));
1298
1308
  }
1299
1309
  default: {
1300
1310
  let e = {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/bootstrap.ts","../src/config.ts","../src/getFeedItems.ts","../src/consume/parseRss.ts","../src/rss/generateRssXml.ts","../src/cache/types.ts","../src/utils/etag.ts","../src/cache/MemoryCache.ts","../src/cache/FileCache.ts","../src/cache/CacheManager.ts","../src/cache/config.ts","../src/services/arweaveImageService.ts","../src/pickFeedItemRichText.ts","../src/index.ts"],"sourcesContent":["import { DEFAULT_ARWEAVE_HOST, resolveArweaveHostFromEnv } from '@seedprotocol/arweave'\n\nlet initialized = false\n\nexport type InitializeFeedPlatformOptions = {\n arweaveDomain?: string\n}\n\nfunction resolveFeedArweaveDomain(explicit?: string): string {\n return explicit ?? resolveArweaveHostFromEnv() ?? DEFAULT_ARWEAVE_HOST\n}\n\n/** Register Node EAS + Arweave clients for feed generation (no full SDK client). */\nexport async function initializeFeedPlatform(\n options?: InitializeFeedPlatformOptions,\n): Promise<void> {\n if (initialized) return\n const [{ registerNodeEasPlatform }, { registerNodeArweavePlatform }] = await Promise.all([\n import('@seedprotocol/eas/node'),\n import('@seedprotocol/arweave/node'),\n ])\n registerNodeEasPlatform()\n const arweaveDomain = resolveFeedArweaveDomain(options?.arweaveDomain)\n registerNodeArweavePlatform({ arweaveDomain })\n if (options?.arweaveDomain) {\n const { BaseArweaveClient } = await import('@seedprotocol/arweave')\n BaseArweaveClient.setHost(options.arweaveDomain)\n }\n initialized = true\n}\n\n/** @deprecated Use initializeFeedPlatform */\nexport const initializeSeedClient = initializeFeedPlatform\n\nexport async function getClient(): Promise<{ isInitialized: () => boolean }> {\n await initializeFeedPlatform()\n return { isInitialized: () => initialized }\n}\n\nexport async function teardownSeedClient(): Promise<void> {\n initialized = false\n}\n\nexport { DEFAULT_ARWEAVE_HOST } from '@seedprotocol/arweave'\n","import type { FeedConfig } from './types';\n\n/**\n * Partial site/feed metadata overrides. Unspecified fields keep current/default values.\n * `author` fields are merged individually when provided.\n */\nexport type SiteConfigOverrides = Partial<Omit<FeedConfig, 'author'>> & {\n author?: Partial<NonNullable<FeedConfig['author']>>;\n};\n\n/** Hardcoded Seed Protocol defaults for feed channel metadata. */\nexport const DEFAULT_SITE_CONFIG: FeedConfig = {\n title: 'Seed Protocol',\n description: 'Content published via Seed Protocol',\n siteUrl: 'https://seedprotocol.io',\n feedUrl: 'https://feed.seedprotocol.io',\n language: 'en',\n copyright: `© ${new Date().getFullYear()} All rights reserved`,\n author: {\n name: 'Seed Protocol',\n email: 'info@seedprotocol.io',\n link: 'https://seedprotocol.io',\n },\n};\n\nfunction cloneSiteConfig(config: FeedConfig): FeedConfig {\n return {\n ...config,\n author: config.author ? { ...config.author } : undefined,\n };\n}\n\nlet siteConfig: FeedConfig = cloneSiteConfig(DEFAULT_SITE_CONFIG);\n\n/**\n * Merge overrides onto a base site config. Missing fields keep the base values.\n */\nexport function resolveSiteConfig(\n overrides?: SiteConfigOverrides,\n base: FeedConfig = siteConfig,\n): FeedConfig {\n if (!overrides) {\n return cloneSiteConfig(base);\n }\n\n const resolved: FeedConfig = {\n ...base,\n ...overrides,\n author: base.author ? { ...base.author } : undefined,\n };\n\n if (overrides.author !== undefined) {\n resolved.author = {\n ...(resolved.author ?? { name: '' }),\n ...overrides.author,\n name: overrides.author.name ?? resolved.author?.name ?? '',\n };\n }\n\n return resolved;\n}\n\n/** Current site/feed channel config (defaults until `setSiteConfig` is called). */\nexport function getSiteConfig(): FeedConfig {\n return cloneSiteConfig(siteConfig);\n}\n\n/**\n * Override site/feed channel metadata for this process.\n * Partial values are merged onto the Seed Protocol defaults (not the previous override set).\n */\nexport function setSiteConfig(overrides: SiteConfigOverrides): FeedConfig {\n siteConfig = resolveSiteConfig(overrides, DEFAULT_SITE_CONFIG);\n return getSiteConfig();\n}\n\n/** Restore hardcoded Seed Protocol site config defaults. */\nexport function resetSiteConfig(): void {\n siteConfig = cloneSiteConfig(DEFAULT_SITE_CONFIG);\n}\n\n/**\n * Load feed configuration from environment variables\n *\n * Environment variables:\n * - FEED_ITEM_URL_BASE: Base URL for attestation links (e.g. https://optimism-sepolia.easscan.org or https://easscan.org).\n * When set, item links use {base}/attestation/view/{uid}. Default: 'https://easscan.org'. Set to override.\n * - FEED_ITEM_URL_PATH: Path segment for attestation links (default: 'attestation/view').\n * Only used when FEED_ITEM_URL_BASE is set.\n * - FEED_SITE_URL: Site URL for fallback when FEED_ITEM_URL_BASE is unset (default: 'https://seedprotocol.io').\n * - FEED_EXPAND_RELATIONS: When 'false', relation properties stay as UIDs. Default: true (expand to nested objects).\n * - FEED_PAGE_SIZE: Items per page for paged feeds (default: 25).\n * - FEED_INCLUDE_DATA_URI_HTML_ITEMS: When 'true', include items whose body/html contain embedded data-URI images.\n * Default: false (omit such items to avoid huge RSS payloads).\n */\nexport function loadFeedConfig(): {\n itemUrlBase: string;\n itemUrlPath: string;\n siteUrl: string;\n expandRelations: boolean;\n pageSize: number;\n richTextDataUriImages: 'omit_items' | 'include_items';\n} {\n const itemUrlBase = process.env.FEED_ITEM_URL_BASE?.trim() || 'https://optimism-sepolia.easscan.org';\n const itemUrlPath =\n process.env.FEED_ITEM_URL_PATH?.trim() || 'attestation/view';\n const siteUrl =\n process.env.FEED_SITE_URL?.trim() || 'https://seedprotocol.io';\n const expandRelations = process.env.FEED_EXPAND_RELATIONS?.toLowerCase() !== 'false';\n const pageSize = parseInt(process.env.FEED_PAGE_SIZE || '25', 10);\n const richTextDataUriImages =\n process.env.FEED_INCLUDE_DATA_URI_HTML_ITEMS?.toLowerCase() === 'true'\n ? 'include_items'\n : 'omit_items';\n\n return { itemUrlBase, itemUrlPath, siteUrl, expandRelations, pageSize, richTextDataUriImages };\n}\n","import {\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 siteUrl: 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\nconst getCollectionPath = (schemaName: string): string => {\n return schemaName === 'image'\n ? 'images'\n : schemaName === 'post'\n ? 'posts'\n : schemaName.toLowerCase() + 's'\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, siteUrl } = 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 collectionPath = getCollectionPath(schemaName)\n const validSeedUid =\n seedUid && typeof seedUid === 'string' && seedUid.trim() !== '' ? seedUid : 'unknown'\n\n let defaultLink: string\n if (storageTransactionId && storageTransactionId !== seedUid && storageTransactionId.length > 0) {\n try {\n defaultLink = getArweaveUrlForTransaction(storageTransactionId)\n } catch (error) {\n console.error('[feed] [setFeedItemDefaults] Error generating Arweave URL:', error)\n if (itemUrlBase != null) {\n defaultLink = `${itemUrlBase.replace(/\\/$/, '')}/${(itemUrlPath ?? 'attestation/view').replace(/^\\//, '')}/${validSeedUid}`\n } else {\n defaultLink = `${siteUrl.replace(/\\/$/, '')}/${collectionPath}/${validSeedUid}`\n }\n }\n } else {\n if (itemUrlBase != null) {\n defaultLink = `${itemUrlBase.replace(/\\/$/, '')}/${(itemUrlPath ?? 'attestation/view').replace(/^\\//, '')}/${validSeedUid}`\n } else {\n defaultLink = `${siteUrl.replace(/\\/$/, '')}/${collectionPath}/${validSeedUid}`\n }\n }\n\n const currentLink = (item.link || item.Link) as string | undefined\n if (\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\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 */\nfunction applyFeedDefaultsDeep(\n item: Record<string, unknown>,\n schemaName: string,\n options: SetFeedItemDefaultsOptions,\n): void {\n const seedUid = (item.seedUid || item.SeedUid || '') as string\n setFeedItemDefaults(item, seedUid, schemaName, options)\n\n for (const key of Object.keys(item)) {\n if (key.startsWith('_')) continue\n const value = item[key]\n if (Array.isArray(value)) {\n for (const el of value) {\n if (looksLikeSeedClone(el)) {\n const nestedUid = (el.seedUid || el.SeedUid || '') as string\n const nestedSchema =\n typeof el.schemaName === 'string' ? el.schemaName : schemaName\n if (nestedSchema === 'image') {\n setImageRelationCloneForFeed(el, nestedUid)\n } else {\n setFeedItemDefaults(el, nestedUid, nestedSchema, options)\n }\n }\n }\n } else if (looksLikeSeedClone(value)) {\n const nestedUid = (value.seedUid || value.SeedUid || '') as string\n const nestedSchema =\n typeof value.schemaName === 'string' ? value.schemaName : schemaName\n if (nestedSchema === 'image') {\n setImageRelationCloneForFeed(value, nestedUid)\n } else {\n setFeedItemDefaults(value, nestedUid, nestedSchema, options)\n }\n }\n }\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 siteUrl: feedConfig.siteUrl,\n }\n\n return records.map((record) => {\n const item = record.data\n applyFeedDefaultsDeep(item, schemaName, defaultsOptions)\n return item\n })\n}\n\nexport const getFeedItemsBySchemaName = async (\n schemaName: string,\n options?: { limit?: number; skip?: number },\n): Promise<Record<string, unknown>[]> => {\n await initializeFeedPlatform()\n const feedConfig = loadFeedConfig()\n const limit = options?.limit ?? 100\n const skip = options?.skip ?? 0\n\n const { items } = await queryBySchema(schemaName, {\n limit,\n skip,\n expandRelations: feedConfig.expandRelations !== false,\n hydrateStorage: true,\n })\n return seedRecordsToFeedItems(schemaName, items)\n}\n\nexport const getFeedItemsBySchemaNameForMonth = async (\n schemaName: string,\n year: number,\n month: number,\n): Promise<Record<string, unknown>[]> => {\n await initializeFeedPlatform()\n const feedConfig = loadFeedConfig()\n\n const records = await queryBySchemaForMonth(schemaName, year, month, {\n expandRelations: feedConfig.expandRelations !== false,\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 { GraphQLItem, 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\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 backgroundRefresh: boolean; // Enable background refresh job (unused)\n refreshInterval: number; // Background refresh interval in seconds\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\n/**\n * Cache statistics for monitoring\n */\nexport interface CacheStats {\n hits: number;\n misses: number;\n refreshes: number;\n errors: number;\n}\n\n// Re-export for callers that imported GraphQLItem from cache types historically\nexport type { GraphQLItem };\n","import { createHash } from 'crypto';\n\n/**\n * Generate an ETag from a string value\n * Uses MD5 hash for consistency and performance\n */\nexport function generateETag(value: string): string {\n const hash = createHash('md5').update(value).digest('hex');\n return `\"${hash.substring(0, 16)}\"`;\n}\n\n/**\n * Generate an ETag for cached feed content\n */\nexport function generateContentETag(\n schemaName: string,\n format: string,\n lastModified: number,\n contentLength: number\n): string {\n const etagValue = `${schemaName}-${format}-${lastModified}-${contentLength}`;\n return generateETag(etagValue);\n}\n\n/**\n * Check if an ETag matches the provided value\n * Handles both quoted and unquoted ETags\n */\nexport function etagMatches(etag1: string, etag2: string): boolean {\n // Remove quotes if present\n const normalize = (etag: string) => etag.replace(/^\"|\"$/g, '');\n return normalize(etag1) === normalize(etag2);\n}\n\n/**\n * Parse If-None-Match header value\n * Returns array of ETags (can be comma-separated)\n */\nexport function parseIfNoneMatch(headerValue: string | null | undefined): string[] {\n if (!headerValue) {\n return [];\n }\n return headerValue\n .split(',')\n .map(etag => etag.trim())\n .filter(etag => etag.length > 0);\n}\n\n/**\n * Check if any of the provided ETags match the current ETag\n */\nexport function checkIfNoneMatch(\n ifNoneMatchHeader: string | null | undefined,\n currentETag: string\n): boolean {\n const etags = parseIfNoneMatch(ifNoneMatchHeader);\n if (etags.length === 0) {\n return false;\n }\n \n // Check for wildcard\n if (etags.includes('*')) {\n return true;\n }\n \n // Check if any ETag matches\n return etags.some(etag => etagMatches(etag, currentETag));\n}\n","import type { CachedFeedContent, CacheConfig, CacheContentKeyOptions, CachedImageMetadata } from './types';\nimport { buildContentKey } from './types';\nimport type { ImageMetadata } from '../types';\nimport { generateContentETag } from '../utils/etag';\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 setFeedContent(\n schemaName: string,\n format: string,\n content: string,\n contentType: string,\n contentKeyOptions?: CacheContentKeyOptions,\n ttlOverride?: number\n ): void {\n const key = buildContentKey(schemaName, format, contentKeyOptions);\n const now = Math.floor(Date.now() / 1000);\n const ttl = ttlOverride ?? this.config.ttl;\n const expiresAt = now + ttl;\n\n const etag = generateContentETag(schemaName, format, now, content.length);\n\n const cachedContent: CachedFeedContent = {\n content,\n contentType,\n etag,\n lastModified: now,\n expiresAt,\n };\n\n this.feedContentCache.set(key, cachedContent);\n }\n\n clearContentCache(schemaName: string): void {\n const keysToDelete: string[] = [];\n for (const key of this.feedContentCache.keys()) {\n if (key.startsWith(`${schemaName}:`)) {\n keysToDelete.push(key);\n }\n }\n keysToDelete.forEach(key => this.feedContentCache.delete(key));\n }\n\n 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 clearAll(): void {\n this.feedContentCache.clear();\n this.imageMetadataCache.clear();\n }\n\n updateConfig(config: Partial<CacheConfig>): void {\n this.config = { ...this.config, ...config };\n }\n\n getStats(): {\n feedContentCount: number;\n imageMetadataCount: number;\n } {\n return {\n feedContentCount: this.feedContentCache.size,\n imageMetadataCount: this.imageMetadataCache.size,\n };\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 clearAllContentCache(schemaName: string): Promise<void> {\n try {\n const files = await fs.readdir(this.cacheDir);\n const prefix = `${schemaName}-`;\n const suffix = '.json';\n\n for (const file of files) {\n // Content keys are schema-format[-page|archive]; skip bare schema.json\n // (owned by @seedprotocol/query collection cache).\n if (file.startsWith(prefix) && file.endsWith(suffix) && file !== `${schemaName}.json`) {\n await fs.unlink(join(this.cacheDir, file));\n }\n }\n } catch (error) {\n console.error(`Error clearing all content cache for ${schemaName}:`, error);\n }\n }\n\n 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 async clearAll(): Promise<void> {\n try {\n const files = await fs.readdir(this.cacheDir);\n for (const file of files) {\n // Do not delete bare {schema}.json — query collection cache files\n if (file.endsWith('.json') && file.includes('-')) {\n await fs.unlink(join(this.cacheDir, file));\n }\n }\n const imageMetadataDir = join(this.cacheDir, 'image-metadata');\n try {\n const imageFiles = await fs.readdir(imageMetadataDir);\n for (const file of imageFiles) {\n if (file.endsWith('.json')) {\n await fs.unlink(join(imageMetadataDir, file));\n }\n }\n } catch {\n // Directory might not exist\n }\n } catch (error) {\n console.error('Error clearing all caches:', error);\n }\n }\n}\n","import { MemoryCache } from './MemoryCache';\nimport { FileCache } from './FileCache';\nimport type {\n CachedFeedContent,\n CacheConfig,\n CacheContentKeyOptions,\n CacheStats,\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 private stats: CacheStats = {\n hits: 0,\n misses: 0,\n refreshes: 0,\n errors: 0,\n };\n\n constructor(config: CacheConfig) {\n this.config = config;\n this.memoryCache = new MemoryCache(config);\n this.fileCache = new FileCache(config);\n }\n\n async getFeedContent(\n schemaName: string,\n format: FeedFormat,\n contentKeyOptions?: CacheContentKeyOptions\n ): Promise<CachedFeedContent | null> {\n if (!this.config.enabled) {\n return null;\n }\n\n try {\n let cached = this.memoryCache.getFeedContent(schemaName, format, contentKeyOptions);\n if (cached) {\n this.stats.hits++;\n return cached;\n }\n\n cached = await this.fileCache.getFeedContent(schemaName, format, contentKeyOptions);\n if (cached) {\n this.memoryCache.setFeedContent(\n schemaName,\n format,\n cached.content,\n cached.contentType,\n contentKeyOptions\n );\n this.stats.hits++;\n return cached;\n }\n\n this.stats.misses++;\n return null;\n } catch (error) {\n console.error(\n `Error getting feed content cache for ${schemaName}:${format}:`,\n error\n );\n this.stats.errors++;\n return null;\n }\n }\n\n async setFeedContent(\n schemaName: string,\n format: FeedFormat,\n content: string,\n contentType: string,\n contentKeyOptions?: CacheContentKeyOptions\n ): Promise<void> {\n if (!this.config.enabled) {\n return;\n }\n\n try {\n let ttlOverride: number | undefined;\n if (contentKeyOptions?.archive) {\n ttlOverride = this.config.archiveTtl ?? 86400;\n } else if (contentKeyOptions?.page != null && contentKeyOptions.page > 1) {\n ttlOverride = this.config.pageTtl ?? 300;\n }\n\n this.memoryCache.setFeedContent(\n schemaName,\n format,\n content,\n contentType,\n contentKeyOptions,\n ttlOverride\n );\n\n const cached = this.memoryCache.getFeedContent(schemaName, format, contentKeyOptions);\n if (cached) {\n await this.fileCache.setFeedContent(schemaName, format, cached, contentKeyOptions);\n }\n } catch (error) {\n console.error(\n `Error setting feed content cache for ${schemaName}:${format}:`,\n error\n );\n this.stats.errors++;\n }\n }\n\n async clearContent(schemaName: string): Promise<void> {\n this.memoryCache.clearContentCache(schemaName);\n await this.fileCache.clearAllContentCache(schemaName);\n }\n\n async clearAll(): Promise<void> {\n this.memoryCache.clearAll();\n await this.fileCache.clearAll();\n }\n\n updateConfig(config: Partial<CacheConfig>): void {\n this.config = { ...this.config, ...config };\n this.memoryCache.updateConfig(this.config);\n }\n\n getStats(): CacheStats & { memoryStats: ReturnType<MemoryCache['getStats']> } {\n return {\n ...this.stats,\n memoryStats: this.memoryCache.getStats(),\n };\n }\n\n async getImageMetadata(transactionId: string): Promise<ImageMetadata | null> {\n if (!this.config.enabled || !this.config.imageMetadata?.enabled) {\n return null;\n }\n\n try {\n let cached = this.memoryCache.getImageMetadata(transactionId);\n if (cached) {\n this.stats.hits++;\n return cached;\n }\n\n cached = await this.fileCache.getImageMetadata(transactionId);\n if (cached) {\n this.memoryCache.setImageMetadata(transactionId, cached);\n this.stats.hits++;\n return cached;\n }\n\n this.stats.misses++;\n return null;\n } catch (error) {\n console.error(`Error getting image metadata cache for ${transactionId}:`, error);\n this.stats.errors++;\n return null;\n }\n }\n\n async setImageMetadata(transactionId: string, metadata: ImageMetadata): Promise<void> {\n if (!this.config.enabled || !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 this.stats.errors++;\n }\n }\n\n resetStats(): void {\n this.stats = {\n hits: 0,\n misses: 0,\n refreshes: 0,\n errors: 0,\n };\n }\n}\n","import {\n BaseArweaveClient,\n getArweaveReadGatewayHostsForPrimary,\n resolveArweaveHostFromEnv,\n} from '@seedprotocol/arweave'\nimport type { CacheConfig, ImageMetadataConfig } from './types';\n\n/**\n * Load cache configuration from environment variables\n *\n * Environment variables:\n * - CACHE_ENABLED: Set to 'false' to disable caching. Set to 'true' to enable (overrides NODE_ENV).\n * In development (NODE_ENV=development), cache is disabled by default unless CACHE_ENABLED='true'.\n * - CACHE_TTL: Cache time-to-live in seconds (default: 3600)\n * - CACHE_DIR: Directory for cache files (default: './cache')\n * - CACHE_BACKGROUND_REFRESH: Enable background refresh (default: 'false')\n * - CACHE_REFRESH_INTERVAL: Background refresh interval in seconds (default: 300)\n */\nexport function loadCacheConfig(): CacheConfig {\n const ttl = parseInt(process.env.CACHE_TTL || '3600', 10);\n const cacheDir = process.env.CACHE_DIR || './cache';\n\n // CACHE_ENABLED explicitly disables: 'false', '0', 'no', 'off'\n const cacheDisabledByEnvVar =\n process.env.CACHE_ENABLED === 'false' ||\n process.env.CACHE_ENABLED === '0' ||\n process.env.CACHE_ENABLED === 'no' ||\n process.env.CACHE_ENABLED === 'off';\n // CACHE_ENABLED explicitly enables (overrides NODE_ENV=development)\n const cacheEnabledByEnvVar =\n process.env.CACHE_ENABLED === 'true' ||\n process.env.CACHE_ENABLED === '1' ||\n process.env.CACHE_ENABLED === 'yes';\n const isDev = process.env.NODE_ENV === 'development';\n\n let enabled: boolean;\n if (cacheDisabledByEnvVar) {\n enabled = false;\n } else if (cacheEnabledByEnvVar) {\n enabled = true;\n } else if (isDev) {\n enabled = false; // Disable cache in dev mode by default\n } else {\n enabled = true;\n }\n\n const backgroundRefresh = process.env.CACHE_BACKGROUND_REFRESH === 'true';\n const refreshInterval = parseInt(\n process.env.CACHE_REFRESH_INTERVAL || '300',\n 10\n );\n\n // Log cache status on startup\n if (!enabled) {\n const reason = cacheDisabledByEnvVar\n ? 'CACHE_ENABLED=false'\n : isDev\n ? 'NODE_ENV=development'\n : 'CACHE_ENABLED=false';\n console.log(`⚠️ Cache is DISABLED (${reason})`);\n }\n\n // Image metadata configuration\n const imageMetadataEnabled = process.env.IMAGE_METADATA_ENABLED !== 'false'; // Default to true\n const imageMetadataTtl = parseInt(process.env.IMAGE_METADATA_TTL || '604800', 10); // 7 days default\n const imageMetadataGateways = process.env.IMAGE_METADATA_GATEWAYS\n ? process.env.IMAGE_METADATA_GATEWAYS.split(',').map(g => g.trim())\n : getArweaveReadGatewayHostsForPrimary(\n resolveArweaveHostFromEnv() ?? BaseArweaveClient.getHost(),\n );\n const imageMetadataTimeout = parseInt(process.env.IMAGE_METADATA_TIMEOUT || '5000', 10);\n\n const imageMetadata: ImageMetadataConfig = {\n enabled: imageMetadataEnabled,\n ttl: imageMetadataTtl,\n gateways: imageMetadataGateways,\n timeout: imageMetadataTimeout,\n };\n\n const pageTtl = parseInt(process.env.CACHE_PAGE_TTL || '300', 10);\n const archiveTtl = parseInt(process.env.CACHE_ARCHIVE_TTL || '86400', 10);\n\n return {\n ttl,\n cacheDir,\n enabled,\n backgroundRefresh,\n refreshInterval,\n imageMetadata,\n pageTtl,\n archiveTtl,\n };\n}\n","import {\n BaseArweaveClient,\n ensureReadGatewaySelected,\n getArweaveReadGatewayHostsForPrimary,\n getDefaultArweaveReadGatewayHostsOrdered,\n getReadGatewayHostsForConfig,\n getResolvedSeedGatewayEndpoints,\n} from '@seedprotocol/arweave'\nimport sizeOf from 'image-size'\nimport type { ImageMetadata } from '../types'\n\nexport interface ArweaveImageServiceConfig {\n gateways: string[]\n timeout: number // milliseconds\n}\n\n/**\n * Service for detecting and extracting metadata from images stored on Arweave\n */\nexport class ArweaveImageService {\n private config: ArweaveImageServiceConfig\n\n constructor(config: ArweaveImageServiceConfig) {\n this.config = config\n }\n\n /**\n * Detect if an Arweave transaction ID links to an image and extract metadata\n */\n async detectImage(transactionId: string): Promise<ImageMetadata> {\n const resolved = getResolvedSeedGatewayEndpoints()\n if (!resolved || (resolved.activePath !== 'hyper-sidecar' && resolved.activePath !== 'http-proxy')) {\n await ensureReadGatewaySelected().catch(() => {\n /* feed may run without browser client init */\n })\n }\n\n const gatewayHosts = resolved\n ? getReadGatewayHostsForConfig(resolved, getDefaultArweaveReadGatewayHostsOrdered())\n : this.config.gateways?.length\n ? this.config.gateways\n : getArweaveReadGatewayHostsForPrimary(BaseArweaveClient.getHost())\n\n const protocol = resolved?.arweaveProtocol ?? 'https'\n\n // Try each gateway until one succeeds\n for (const gateway of gatewayHosts) {\n try {\n const host = gateway.trim().replace(/\\/$/, '')\n const url = `${protocol}://${host}/${transactionId}`\n const metadata = await this.getImageMetadata(url)\n if (metadata.isImage) {\n return metadata\n }\n } catch (error) {\n // Log but continue to next gateway\n console.warn(`Failed to fetch from gateway ${gateway} for transaction ${transactionId}:`, error)\n continue\n }\n }\n\n // If all gateways failed, return non-image result\n return {\n isImage: false,\n url: `${protocol}://${gatewayHosts[0]?.trim() || BaseArweaveClient.getHost()}/${transactionId}`,\n }\n }\n\n /**\n * Get image metadata from a URL\n */\n async getImageMetadata(url: string): Promise<ImageMetadata> {\n try {\n // First, try HEAD request to check Content-Type (more efficient)\n const headResponse = await this.fetchWithTimeout(url, { method: 'HEAD' })\n \n if (!headResponse.ok) {\n return { isImage: false, url }\n }\n\n const contentType = headResponse.headers.get('content-type') || ''\n const contentLength = headResponse.headers.get('content-length')\n \n // Check if it's an image based on Content-Type\n if (!this.isImageContentType(contentType)) {\n return {\n isImage: false,\n url,\n mimeType: contentType || undefined,\n size: contentLength ? parseInt(contentLength, 10) : undefined,\n }\n }\n\n // If it's an image, fetch first few KB to extract dimensions\n const rangeResponse = await this.fetchWithTimeout(url, {\n headers: {\n 'Range': 'bytes=0-8192', // First 8KB should be enough for most image headers\n },\n })\n\n if (!rangeResponse.ok) {\n // If range request fails, try full request (some servers don't support range)\n return await this.getImageMetadataFromFullRequest(url, contentType, contentLength)\n }\n\n const buffer = Buffer.from(await rangeResponse.arrayBuffer())\n const dimensions = this.extractImageDimensions(buffer, contentType)\n const format = this.getImageFormat(contentType, buffer)\n\n return {\n isImage: true,\n url,\n mimeType: contentType,\n width: dimensions.width,\n height: dimensions.height,\n size: contentLength ? parseInt(contentLength, 10) : undefined,\n format,\n }\n } catch (error) {\n console.warn(`Error fetching image metadata from ${url}:`, error)\n return { isImage: false, url }\n }\n }\n\n /**\n * Fetch image metadata from full request (fallback when range requests fail)\n */\n private async getImageMetadataFromFullRequest(\n url: string,\n contentType: string,\n contentLength: string | null\n ): Promise<ImageMetadata> {\n try {\n const response = await this.fetchWithTimeout(url)\n if (!response.ok) {\n return { isImage: false, url }\n }\n\n const buffer = Buffer.from(await response.arrayBuffer())\n const dimensions = this.extractImageDimensions(buffer, contentType)\n const format = this.getImageFormat(contentType, buffer)\n\n return {\n isImage: true,\n url,\n mimeType: contentType,\n width: dimensions.width,\n height: dimensions.height,\n size: buffer.length,\n format,\n }\n } catch (error) {\n console.warn(`Error in full request for ${url}:`, error)\n return { isImage: false, url }\n }\n }\n\n /**\n * Validate if Content-Type indicates an image\n */\n private isImageContentType(contentType: string): boolean {\n if (!contentType) return false\n \n const normalized = (contentType.toLowerCase().split(';')[0] ?? '').trim()\n return normalized.startsWith('image/')\n }\n\n /**\n * Extract image dimensions from buffer\n */\n private extractImageDimensions(\n buffer: Buffer,\n contentType: string\n ): { width: number; height: number } {\n try {\n const dimensions = sizeOf(buffer)\n if (dimensions.width && dimensions.height) {\n return {\n width: dimensions.width,\n height: dimensions.height,\n }\n }\n } catch (error) {\n // image-size might fail for some formats or corrupted data\n console.warn(`Failed to extract dimensions:`, error)\n }\n\n // Return default if extraction failed\n return { width: 0, height: 0 }\n }\n\n /**\n * Get image format from Content-Type or buffer analysis\n */\n private getImageFormat(contentType: string, buffer: Buffer): string | undefined {\n // Try to get format from Content-Type first\n if (contentType) {\n const mimeMatch = contentType.toLowerCase().match(/image\\/([^;]+)/)\n if (mimeMatch && mimeMatch[1]) {\n const format = mimeMatch[1].toLowerCase()\n // Normalize common formats\n if (format === 'jpeg') return 'jpeg'\n if (format === 'png') return 'png'\n if (format === 'gif') return 'gif'\n if (format === 'webp') return 'webp'\n if (format === 'svg+xml') return 'svg'\n return format\n }\n }\n\n // Try to detect from buffer magic bytes\n try {\n const dimensions = sizeOf(buffer)\n if (dimensions.type) {\n return dimensions.type.toLowerCase()\n }\n } catch {\n // Ignore errors\n }\n\n return undefined\n }\n\n /**\n * Fetch with timeout\n */\n private async fetchWithTimeout(\n url: string,\n options: RequestInit = {}\n ): Promise<Response> {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), this.config.timeout)\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n })\n clearTimeout(timeoutId)\n return response\n } catch (error) {\n clearTimeout(timeoutId)\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Request timeout after ${this.config.timeout}ms`)\n }\n throw error\n }\n }\n}\n","/**\n * Resolves RSS/Atom/JSON feed `description` and `content` from dynamic Seed item shapes.\n * Html-type properties often use `body`; `html` / `content` are alternate sources.\n */\n\nimport {\n feedRichBodyModelPriority,\n getFeedFieldStorageModels,\n getFeedListElementStorageModels,\n isFeedRichBodyStorageSchema,\n} from './feedFieldStorageModel'\n\nfunction firstNonEmptyString(item: Record<string, unknown>, keys: readonly string[]): string {\n for (const k of keys) {\n const v = item[k]\n if (typeof v === 'string' && v.trim() !== '') {\n return v\n }\n }\n return ''\n}\n\n/** Short text fields only (excerpt / summary) — not full HTML body keys. */\nexport const DESCRIPTION_KEYS = ['summary', 'Summary', 'description', 'Description', 'text', 'Text'] as const\n\n/**\n * Primary HTML / rich body keys first, then plain fallbacks (same as legacy `content` chain).\n */\nexport const CONTENT_PRIMARY_KEYS = ['html', 'Html', 'body', 'Body', 'content', 'Content'] as const\n\nconst CONTENT_FALLBACK_KEYS = ['summary', 'Summary', 'description', 'Description', 'text', 'Text'] as const\n\ntype RichBodyCandidate = {\n model: string\n key: string\n index: number\n value: string\n}\n\nfunction compareRichBodyCandidates(a: RichBodyCandidate, b: RichBodyCandidate): number {\n const d = feedRichBodyModelPriority(a.model) - feedRichBodyModelPriority(b.model)\n if (d !== 0) return d\n const k = a.key.localeCompare(b.key)\n if (k !== 0) return k\n return a.index - b.index\n}\n\nfunction pickTypedRichBodyContent(item: Record<string, unknown>): string {\n const fieldModels = getFeedFieldStorageModels(item)\n const listModels = getFeedListElementStorageModels(item)\n let best: RichBodyCandidate | null = null\n\n if (fieldModels) {\n for (const [key, model] of Object.entries(fieldModels)) {\n if (!isFeedRichBodyStorageSchema(model)) continue\n const v = item[key]\n if (typeof v !== 'string' || v.trim() === '') continue\n const cand: RichBodyCandidate = { model, key, index: 0, value: v }\n if (!best || compareRichBodyCandidates(cand, best) < 0) best = cand\n }\n }\n\n if (listModels) {\n for (const [listKey, models] of Object.entries(listModels)) {\n const arr = item[listKey]\n if (!Array.isArray(arr)) continue\n const n = Math.min(models.length, arr.length)\n for (let i = 0; i < n; i++) {\n const model = models[i]!\n if (!isFeedRichBodyStorageSchema(model)) continue\n const el = arr[i]\n if (typeof el !== 'string' || el.trim() === '') continue\n const cand: RichBodyCandidate = { model, key: listKey, index: i, value: el }\n if (!best || compareRichBodyCandidates(cand, best) < 0) best = cand\n }\n }\n }\n\n return best?.value ?? ''\n}\n\nexport function pickFeedItemDescription(item: Record<string, unknown>): string {\n return firstNonEmptyString(item, DESCRIPTION_KEYS)\n}\n\nexport function pickFeedItemContent(item: Record<string, unknown>): string {\n const typed = pickTypedRichBodyContent(item)\n if (typed !== '') return typed\n const primary = firstNonEmptyString(item, CONTENT_PRIMARY_KEYS)\n if (primary !== '') return primary\n return firstNonEmptyString(item, CONTENT_FALLBACK_KEYS)\n}\n\nfunction stringFieldContainsDataUriImage(value: unknown): boolean {\n if (typeof value !== 'string') return false\n return value.includes('data:image/') && value.includes(';base64,')\n}\n\nfunction feedItemTypedRichFieldsContainDataUriImage(item: Record<string, unknown>): boolean {\n const fieldModels = getFeedFieldStorageModels(item)\n if (fieldModels) {\n for (const [key, model] of Object.entries(fieldModels)) {\n if (!isFeedRichBodyStorageSchema(model)) continue\n if (stringFieldContainsDataUriImage(item[key])) return true\n }\n }\n const listModels = getFeedListElementStorageModels(item)\n if (listModels) {\n for (const [listKey, models] of Object.entries(listModels)) {\n const arr = item[listKey]\n if (!Array.isArray(arr)) continue\n const n = Math.min(models.length, arr.length)\n for (let i = 0; i < n; i++) {\n if (!isFeedRichBodyStorageSchema(models[i]!)) continue\n if (stringFieldContainsDataUriImage(arr[i])) return true\n }\n }\n }\n return false\n}\n\n/**\n * True when primary body keys or excerpt-style keys contain an embedded `data:image/...;base64,...` URI.\n * Used by feed generation to omit oversized RSS entries by default.\n */\nexport function feedItemRichTextContainsDataUriImage(item: Record<string, unknown>): boolean {\n if (feedItemTypedRichFieldsContainDataUriImage(item)) return true\n for (const k of CONTENT_PRIMARY_KEYS) {\n if (stringFieldContainsDataUriImage(item[k])) return true\n }\n for (const k of DESCRIPTION_KEYS) {\n if (stringFieldContainsDataUriImage(item[k])) return true\n }\n return false\n}\n\nexport function filterItemsByRichTextDataUriImagePolicy<T extends Record<string, unknown>>(\n items: readonly T[],\n richTextDataUriImages: 'omit_items' | 'include_items' = 'omit_items',\n): T[] {\n if (richTextDataUriImages === 'include_items') return [...items]\n return items.filter((item) => !feedItemRichTextContainsDataUriImage(item))\n}\n","import {\n initializeFeedPlatform,\n initializeSeedClient,\n teardownSeedClient,\n DEFAULT_ARWEAVE_HOST,\n} from './bootstrap';\nimport { getArweaveUrlForTransaction } from './utils/arweaveUrl';\nimport { getFeedItemsBySchemaName, getFeedItemsBySchemaNameForMonth } from './getFeedItems';\n\nexport { getFeedItemsBySchemaName, getFeedItemsBySchemaNameForMonth } from './getFeedItems';\nexport {\n loadFeedConfig,\n DEFAULT_SITE_CONFIG,\n getSiteConfig,\n setSiteConfig,\n resetSiteConfig,\n resolveSiteConfig,\n type SiteConfigOverrides,\n} from './config';\nexport { parseRssString, type ParsedRssChannel } from './consume/parseRss';\nexport type { FeedConfig, FeedFormat, GraphQLItem } from './types';\nexport {\n initializeFeedPlatform,\n initializeSeedClient,\n getClient,\n teardownSeedClient,\n DEFAULT_ARWEAVE_HOST,\n} from './bootstrap';\nexport {\n classifyMediaRef,\n normalizeFeedItemFields,\n getFeedItemStringField,\n 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, GraphQLItem, TransformOptions, FeedConfig, ImageMetadata } from './types';\n\nexport type { TransformOptions } from './types';\nimport { generateAtomFeed, generateJsonFeed } from 'feedsmith';\nimport { generateRssXml } from './rss/generateRssXml';\nimport { CacheManager } from './cache/CacheManager';\nimport { loadCacheConfig } from './cache/config';\nimport { loadFeedConfig, resolveSiteConfig, type SiteConfigOverrides } from './config';\nimport { checkIfNoneMatch } from './utils/etag';\nimport { ArweaveImageService } from './services/arweaveImageService';\nimport { resolveSeedRssImageRelationRef } from '@seedprotocol/arweave';\nimport {\n pickFeedItemContent,\n pickFeedItemDescription,\n filterItemsByRichTextDataUriImagePolicy,\n} from './pickFeedItemRichText';\n\nexport { enrichImageSeedCloneForFeed } from './imageRelationEnrichment';\nexport {\n pickFeedItemContent,\n pickFeedItemDescription,\n feedItemRichTextContainsDataUriImage,\n filterItemsByRichTextDataUriImagePolicy,\n} from './pickFeedItemRichText';\nexport {\n FEED_RICH_BODY_STORAGE_SCHEMAS,\n isFeedRichBodyStorageSchema,\n} from './feedFieldStorageModel';\n\n/** RSS enclosure URL from a plain string (URL or tx id) or nested Image relation object. */\nfunction getEnclosureUrlFromImageRelationField(value: unknown): string | undefined {\n 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/**\n * Enriches feed items with media properties from Arweave storageTransactionId\n */\nasync function enrichFeedItemsWithMedia(\n items: GraphQLItem[],\n imageService: ArweaveImageService,\n cache: CacheManager\n): Promise<GraphQLItem[]> {\n // Filter items that have storageTransactionId\n const itemsToProcess = items.filter(\n (item: any) => item.storageTransactionId || item.storage_transaction_id\n )\n\n if (itemsToProcess.length === 0) {\n return items\n }\n\n console.log(`Enriching ${itemsToProcess.length} items with image metadata`)\n\n // Process all items in parallel\n const enrichmentPromises = itemsToProcess.map(async (item: any) => {\n const transactionId = item.storageTransactionId || item.storage_transaction_id\n if (!transactionId) return item\n\n try {\n // Check cache first\n let imageMetadata = await cache.getImageMetadata(transactionId)\n\n // If not cached, detect image\n if (!imageMetadata) {\n console.log(`Detecting image for transaction ${transactionId}`)\n imageMetadata = await imageService.detectImage(transactionId)\n // Cache the result (even if it's not an image, to avoid retries)\n await cache.setImageMetadata(transactionId, imageMetadata)\n }\n\n // If image detected, add media properties\n if (imageMetadata.isImage) {\n // Add media properties to the item\n item._imageMetadata = imageMetadata\n item._hasImage = true\n }\n\n return item\n } catch (error) {\n console.warn(`Error enriching item with transaction ${transactionId}:`, error)\n return item // Return item unchanged on error\n }\n })\n\n // Wait for all enrichments to complete\n const enrichedItems = await Promise.all(enrichmentPromises)\n\n // Create a map of enriched items by their ID\n const enrichedMap = new Map<string, GraphQLItem>()\n enrichedItems.forEach(item => {\n const itemId = item.id || (item as any).seedUid || (item as any).SeedUid || ''\n if (itemId) {\n enrichedMap.set(itemId, item)\n }\n })\n\n // Merge enriched items back into original array\n return items.map(item => {\n const itemId = item.id || (item as any).seedUid || (item as any).SeedUid || ''\n const enriched = enrichedMap.get(itemId)\n return enriched || item\n })\n}\n\n/**\n * Transforms GraphQL items into feed items.\n * This function preserves all dynamic properties from the items.\n */\nfunction transformToFeedItems(\n items: GraphQLItem[],\n options: TransformOptions\n): any[] {\n const { schemaName, siteUrl, itemUrlBase, itemUrlPath, richTextDataUriImages = 'omit_items' } = options\n\n const filtered = filterItemsByRichTextDataUriImagePolicy(\n items as Record<string, unknown>[],\n richTextDataUriImages,\n ) as GraphQLItem[]\n\n return filtered.map((item: any) => {\n // Determine item ID - try multiple possible fields\n const itemId = item.id || item.seedUid || item.SeedUid || item.storageTransactionId || item.storage_transaction_id\n\n // Determine item URL - prefer link/Link, then import_url/importUrl, fallback to constructed URL\n const fallbackUrl =\n itemUrlBase != null\n ? `${itemUrlBase.replace(/\\/$/, '')}/${(itemUrlPath ?? 'attestation/view').replace(/^\\//, '')}/${itemId}`\n : `${siteUrl}/${pluralize(schemaName)}/${itemId}`\n const itemUrl = item.link || item.Link || item.import_url || item.importUrl || fallbackUrl\n \n // Determine publication date - try multiple sources\n let date: Date\n if (item.pubDate || item.PubDate) {\n // pubDate might be a string like \"Mon, 07 Apr 2025 00:03:29 GMT\"\n const pubDateStr = item.pubDate || item.PubDate\n date = new Date(pubDateStr)\n } else if (item.timeCreated) {\n // timeCreated is a Unix timestamp\n date = new Date(item.timeCreated * 1000)\n } else if (item.publishedAt || item.createdAt || item.updatedAt) {\n const dateValue = item.publishedAt || item.createdAt || item.updatedAt\n date = dateValue && typeof dateValue === 'object' && dateValue.constructor === Date\n ? dateValue as Date\n : new Date(dateValue as string | number)\n } else {\n date = new Date()\n }\n\n const description = pickFeedItemDescription(item)\n const content = pickFeedItemContent(item)\n\n // Start with all properties from the item to preserve dynamic schema\n const feedItem: any = {\n ...item, // Preserve all dynamic properties first\n // Map to standard feed fields\n id: itemId,\n title: item.title || item.Title || 'Untitled',\n link: itemUrl,\n description,\n content,\n pubDate: date,\n date: date,\n // Map guid - use full URL when available for proper permalink\n guid: item.guid || item.Guid || item.link || item.Link || itemUrl,\n }\n\n if (typeof feedItem.html === 'string' && feedItem.Html === undefined) {\n feedItem.Html = feedItem.html\n }\n if (typeof feedItem.Html === 'string' && feedItem.html === undefined) {\n feedItem.html = feedItem.Html\n }\n if (typeof feedItem.body === 'string' && feedItem.Body === undefined) {\n feedItem.Body = feedItem.body\n }\n if (typeof feedItem.Body === 'string' && feedItem.body === undefined) {\n feedItem.body = feedItem.Body\n }\n\n // Add image metadata if available from enrichment\n if (item._imageMetadata && item._hasImage) {\n const imageMeta: ImageMetadata = item._imageMetadata\n feedItem._imageMetadata = imageMeta\n feedItem._hasImage = true\n }\n\n // Convert any date-like string properties to Date objects\n Object.keys(feedItem).forEach((key) => {\n const value = feedItem[key]\n if (typeof value === 'string' && /date|time|published|created|updated/i.test(key) && key !== 'pubDate' && key !== 'date') {\n const dateValue = new Date(value)\n if (!isNaN(dateValue.getTime())) {\n feedItem[key] = dateValue\n }\n }\n })\n\n return feedItem\n })\n}\n\n// ============================================================================\n// Feed Generator\n// ============================================================================\n\nexport interface FeedPaginationOptions {\n page: number\n pageSize: number\n hasNext: boolean\n baseUrl: string\n}\n\nexport interface FeedArchiveLink {\n rel: string\n href: string\n}\n\nexport const createFeed = (\n items: GraphQLItem[],\n schemaName: string,\n format: FeedFormat,\n cacheBust?: string,\n pagination?: FeedPaginationOptions,\n archiveLinks?: FeedArchiveLink[],\n isArchive?: boolean,\n transformOverrides?: Partial<TransformOptions>,\n siteConfigOverrides?: SiteConfigOverrides,\n): Promise<string> => {\n const siteConfig = resolveSiteConfig(siteConfigOverrides)\n const collectionName = pluralize(schemaName)\n // Feed document self-links use feedUrl; channel home/link uses siteUrl\n const feedUrlBase = `${siteConfig.feedUrl}/${collectionName}/${format}`\n const feedUrl = cacheBust ? `${feedUrlBase}?v=${cacheBust}` : feedUrlBase\n const feedTitle = `${siteConfig.title} - ${capitalize(collectionName)}`\n const now = new Date()\n\n const feedConfig = loadFeedConfig()\n\n // Build RFC 5005 links from pagination or archiveLinks\n const links: Array<{ rel: string; href: string; type?: string }> = []\n if (pagination) {\n const base = pagination.baseUrl.replace(/\\?.*$/, '')\n const sep = base.includes('?') ? '&' : '?'\n if (pagination.hasNext) {\n links.push({ rel: 'next', href: `${base}${sep}page=${pagination.page + 1}` })\n }\n if (pagination.page > 1) {\n links.push({ rel: 'previous', href: `${base}${sep}page=${pagination.page - 1}` })\n links.push({ rel: 'first', href: `${base}${sep}page=1` })\n }\n }\n if (archiveLinks?.length) {\n for (const al of archiveLinks) {\n links.push({ rel: al.rel, href: al.href })\n }\n }\n\n // Transform items to preserve all dynamic properties\n const transformedItems = transformToFeedItems(items, {\n schemaName,\n siteUrl: siteConfig.siteUrl,\n itemUrlBase: feedConfig.itemUrlBase,\n itemUrlPath: feedConfig.itemUrlPath,\n richTextDataUriImages:\n transformOverrides?.richTextDataUriImages ?? feedConfig.richTextDataUriImages,\n })\n\n // Generate feed based on format using FeedSmith\n switch (format) {\n case 'atom': {\n // Atom feed requires: id, title, updated, links, entries\n const atomLinks: Array<{ href: string; rel?: string; type?: string }> = [\n { href: feedUrl, rel: 'self' },\n { href: siteConfig.siteUrl },\n ]\n for (const link of links) {\n atomLinks.push({\n href: link.href,\n rel: link.rel,\n type: link.type,\n })\n }\n const atomFeed = {\n id: feedUrl,\n title: feedTitle,\n updated: now,\n links: atomLinks,\n subtitle: siteConfig.description,\n rights: siteConfig.copyright,\n author: siteConfig.author ? {\n name: siteConfig.author.name,\n email: siteConfig.author.email,\n uri: siteConfig.author.link,\n } : undefined,\n entries: transformedItems.map((item) => {\n // Atom entries require: id, title, updated, links\n const entry: any = {\n id: item.id || item.link,\n title: item.title || 'Untitled',\n updated: item.date || item.pubDate || now,\n links: item.link ? [{ href: item.link }] : [],\n ...item, // Preserve all dynamic properties\n }\n if (item.content) entry.content = item.content\n if (item.description) entry.summary = item.description\n \n // Add image as enclosure link if available\n if (item._imageMetadata && item._hasImage) {\n const imageMeta: ImageMetadata = item._imageMetadata\n if (!entry.links) entry.links = []\n entry.links.push({\n href: imageMeta.url,\n rel: 'enclosure',\n type: imageMeta.mimeType || 'image/jpeg',\n length: imageMeta.size,\n })\n \n // Enhance content with image if not already present\n if (imageMeta.url && entry.content && !entry.content.includes(imageMeta.url)) {\n const imageHtml = `<img src=\"${imageMeta.url}\" alt=\"${item.title || ''}\"${imageMeta.width ? ` width=\"${imageMeta.width}\"` : ''}${imageMeta.height ? ` height=\"${imageMeta.height}\"` : ''} />`\n entry.content = typeof entry.content === 'string' \n ? `${imageHtml}\\n${entry.content}`\n : { type: 'html', value: `${imageHtml}\\n${entry.content.value || entry.content}` }\n }\n }\n \n return entry\n }),\n }\n return Promise.resolve(generateAtomFeed(atomFeed) as string)\n }\n case 'json': {\n // JSON feed requires: title, items (with id)\n const nextUrl = pagination?.hasNext\n ? `${pagination.baseUrl.replace(/\\?.*$/, '')}?page=${pagination.page + 1}`\n : undefined\n const jsonFeed = {\n title: feedTitle,\n home_page_url: siteConfig.siteUrl,\n feed_url: feedUrl,\n ...(nextUrl && { next_url: nextUrl }),\n description: siteConfig.description,\n author: siteConfig.author ? {\n name: siteConfig.author.name,\n url: siteConfig.author.link,\n } : undefined,\n items: transformedItems.map((item) => {\n // JSON items require: id\n const jsonItem: any = {\n id: item.id || item.link,\n ...item, // Preserve all dynamic properties\n }\n if (item.title) jsonItem.title = item.title\n if (item.link) jsonItem.url = item.link\n if (item.content) jsonItem.content_html = item.content\n if (item.description) jsonItem.summary = item.description\n if (item.date || item.pubDate) jsonItem.date_published = item.date || item.pubDate\n \n // Add image and attachments if available\n if (item._imageMetadata && item._hasImage) {\n const imageMeta: ImageMetadata = item._imageMetadata\n jsonItem.image = imageMeta.url\n \n // Add as attachment\n if (!jsonItem.attachments) jsonItem.attachments = []\n jsonItem.attachments.push({\n url: imageMeta.url,\n mime_type: imageMeta.mimeType || 'image/jpeg',\n size_in_bytes: imageMeta.size,\n title: item.title || 'Image',\n })\n \n // Enhance content_html with image if not already present\n if (imageMeta.url && jsonItem.content_html && !jsonItem.content_html.includes(imageMeta.url)) {\n const imageHtml = `<img src=\"${imageMeta.url}\" alt=\"${item.title || ''}\"${imageMeta.width ? ` width=\"${imageMeta.width}\"` : ''}${imageMeta.height ? ` height=\"${imageMeta.height}\"` : ''} />`\n jsonItem.content_html = `${imageHtml}\\n${jsonItem.content_html}`\n }\n }\n \n return jsonItem\n }),\n }\n const jsonResult = generateJsonFeed(jsonFeed)\n return Promise.resolve(typeof jsonResult === 'string' ? jsonResult : JSON.stringify(jsonResult))\n }\n case 'rss':\n default: {\n // RSS feed requires: title, link, description, items\n const rssFeed = {\n title: feedTitle,\n link: siteConfig.siteUrl,\n description: siteConfig.description,\n links: links.length ? links.map((l) => ({ rel: l.rel, href: l.href, type: l.type })) : undefined,\n isArchive: isArchive ?? false,\n language: siteConfig.language,\n copyright: siteConfig.copyright,\n webMaster: siteConfig.author?.email,\n pubDate: now,\n lastBuildDate: now,\n items: transformedItems.map((item: any) => {\n // RSS items can have dynamic properties\n const rssItem: any = {\n ...item, // Preserve all dynamic properties\n }\n \n // Ensure required/standard RSS fields\n if (item.title) rssItem.title = item.title\n if (item.link) rssItem.link = item.link\n if (item.description) rssItem.description = item.description\n if (item.date || item.pubDate) rssItem.pubDate = item.date || item.pubDate\n \n // Map guid - RSS requires guid for proper item identification\n if (item.guid) {\n rssItem.guid = {\n value: item.guid,\n isPermaLink: typeof item.guid === 'string' && (item.guid.startsWith('http://') || item.guid.startsWith('https://'))\n }\n } else if (item.id) {\n rssItem.guid = {\n value: item.id,\n isPermaLink: typeof item.id === 'string' && (item.id.startsWith('http://') || item.id.startsWith('https://'))\n }\n }\n \n const imageCandidate = extractRssImageCandidate(item)\n if (imageCandidate) {\n applyRssImageCandidate(\n rssItem,\n item,\n imageCandidate,\n schemaName,\n siteConfig.author,\n siteConfig.copyright\n )\n }\n\n if (\n typeof item.content === 'string' &&\n item.content.trim() !== '' &&\n rssItem['content:encoded'] === undefined\n ) {\n rssItem['content:encoded'] = item.content\n }\n \n // Use Dublin Core namespace for additional metadata\n rssItem.dc = {}\n \n // Add date using dc namespace (supports multiple dates)\n if (item.date || item.pubDate) {\n rssItem.dc.date = item.date || item.pubDate\n }\n if (item.timeCreated) {\n // Convert Unix timestamp to Date for dc namespace\n const timeCreatedDate = new Date(item.timeCreated * 1000)\n if (!rssItem.dc.dates) rssItem.dc.dates = []\n rssItem.dc.dates.push(timeCreatedDate)\n }\n \n // Add identifier using dc namespace\n if (item.seedUid || item.SeedUid) {\n if (!rssItem.dc.identifier) rssItem.dc.identifier = []\n rssItem.dc.identifier.push(item.seedUid || item.SeedUid)\n }\n if (item.storageTransactionId || item.storage_transaction_id) {\n if (!rssItem.dc.identifier) rssItem.dc.identifier = []\n rssItem.dc.identifier.push(item.storageTransactionId || item.storage_transaction_id)\n }\n \n // Add source/relation for import_url\n if (item.import_url || item.importUrl) {\n rssItem.dc.source = item.import_url || item.importUrl\n }\n \n // Include raw custom fields as well (FeedSmith may preserve them)\n if (item.seedUid || item.SeedUid) rssItem.seedUid = item.seedUid || item.SeedUid\n if (item.storageTransactionId || item.storage_transaction_id) {\n rssItem.storageTransactionId = item.storageTransactionId || item.storage_transaction_id\n }\n if (item.timeCreated) rssItem.timeCreated = item.timeCreated\n \n return rssItem\n }),\n }\n return Promise.resolve(generateRssXml(rssFeed))\n }\n }\n}\n\n// ============================================================================\n// Route Handler\n// ============================================================================\n\nfunction getContentType(format: FeedFormat): string {\n switch (format) {\n case 'atom':\n return 'application/atom+xml; charset=utf-8'\n case 'json':\n return 'application/feed+json; charset=utf-8'\n case 'rss':\n default:\n return 'application/rss+xml; charset=utf-8'\n }\n}\n\nfunction parseFormat(segment: string): FeedFormat | null {\n const normalized = segment.toLowerCase()\n if (['rss', 'atom', 'json'].includes(normalized)) {\n return normalized as FeedFormat\n }\n return null\n}\n\nfunction capitalize(str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1)\n}\n\n/**\n * Main route handler for feed generation.\n * \n * URL Pattern: /:collection/:format\n * Examples:\n * - /posts/rss → RSS feed of posts\n * - /posts/atom → Atom feed of posts\n * - /identities/json → JSON feed of identities\n * - /posts/rss?v=1234567890 → RSS feed with cache busting parameter\n */\nexport async function handleFeedRequest(\n collectionSegment: string,\n formatSegment: string,\n ifNoneMatch?: string | null,\n cacheBust?: string,\n page?: number,\n siteConfigOverrides?: SiteConfigOverrides,\n): Promise<Response> {\n // Validate format\n const format = parseFormat(formatSegment)\n if (!format) {\n return new Response(\n JSON.stringify({ error: `Invalid feed format: ${formatSegment}` }),\n {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n }\n )\n }\n\n // De-pluralize the collection name to get model type\n const schemaName = pluralize.singular(collectionSegment.toLowerCase())\n const collectionName = pluralize(schemaName)\n const siteConfig = resolveSiteConfig(siteConfigOverrides)\n\n const feedConfig = loadFeedConfig()\n const pageNum = Math.max(1, page ?? 1)\n const pageSize = feedConfig.pageSize\n const skip = (pageNum - 1) * pageSize\n\n const contentKeyOptions =\n pageNum > 1 ? { page: pageNum } : undefined\n\n console.log(`Schema name: ${schemaName}`);\n console.log(`Collection name: ${collectionName}`);\n\n const cache = getCacheManager();\n const config = loadCacheConfig();\n\n try {\n // Check if we have cached feed content\n if (config.enabled) {\n const cachedContent = await cache.getFeedContent(schemaName, format, contentKeyOptions);\n\n if (cachedContent) {\n // Check ETag for conditional request\n if (ifNoneMatch && checkIfNoneMatch(ifNoneMatch, cachedContent.etag)) {\n console.log(`Cache hit with ETag match for ${schemaName}:${format} - returning 304`);\n return new Response(null, {\n status: 304,\n headers: {\n 'ETag': cachedContent.etag,\n 'Last-Modified': new Date(cachedContent.lastModified * 1000).toUTCString(),\n 'Cache-Control': 'public, max-age=3600, s-maxage=3600, must-revalidate',\n },\n });\n }\n\n // Return cached content\n console.log(`Cache hit for ${schemaName}:${format}`);\n return new Response(cachedContent.content, {\n status: 200,\n headers: {\n 'Content-Type': cachedContent.contentType,\n 'ETag': cachedContent.etag,\n 'Last-Modified': new Date(cachedContent.lastModified * 1000).toUTCString(),\n 'Cache-Control': 'public, max-age=3600, s-maxage=3600, must-revalidate',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n 'X-Cache': 'HIT',\n },\n });\n }\n }\n\n // Content cache miss or disabled — fetch items via query (owns collection cache)\n console.log(`Cache miss for ${schemaName}:${format} - fetching items`);\n\n const feedItems = await (async (): Promise<GraphQLItem[]> => {\n const items = await getFeedItemsBySchemaName(schemaName, {\n limit: pageSize,\n skip,\n }) as GraphQLItem[];\n console.log(`Found ${items.length} feed items for schema ${schemaName}`);\n if (config.imageMetadata?.enabled) {\n const imageService = new ArweaveImageService({\n gateways: config.imageMetadata.gateways,\n timeout: config.imageMetadata.timeout,\n });\n return enrichFeedItemsWithMedia(items, imageService, cache);\n }\n return items;\n })();\n\n const baseUrl = `${siteConfig.feedUrl}/${collectionName}/${format}`;\n const hasNext = feedItems.length === pageSize;\n const archiveLinksForMain: FeedArchiveLink[] = [];\n if (pageNum === 1) {\n const now = new Date();\n const prevMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);\n archiveLinksForMain.push({\n rel: 'prev-archive',\n href: `${siteConfig.feedUrl}/${collectionName}/archive/${prevMonth.getFullYear()}/${prevMonth.getMonth() + 1}/${format}`,\n });\n }\n\n const paginationOptions: FeedPaginationOptions | undefined = {\n page: pageNum,\n pageSize,\n hasNext,\n baseUrl,\n };\n\n const feedContent = await createFeed(\n feedItems,\n schemaName,\n format,\n cacheBust,\n paginationOptions,\n archiveLinksForMain.length ? archiveLinksForMain : undefined,\n false,\n undefined,\n siteConfigOverrides,\n );\n const contentType = getContentType(format);\n\n // Cache the generated feed content\n if (config.enabled) {\n await cache.setFeedContent(schemaName, format, feedContent, contentType, contentKeyOptions);\n \n // Get the cached content to retrieve ETag\n const cachedContent = await cache.getFeedContent(schemaName, format, contentKeyOptions);\n const etag = cachedContent?.etag || '';\n const lastModified = cachedContent?.lastModified || Math.floor(Date.now() / 1000);\n\n return new Response(feedContent, {\n status: 200,\n headers: {\n 'Content-Type': contentType,\n 'ETag': etag,\n 'Last-Modified': new Date(lastModified * 1000).toUTCString(),\n 'Cache-Control': 'public, max-age=3600, s-maxage=3600, must-revalidate',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n 'X-Cache': 'MISS',\n },\n });\n }\n\n // Cache disabled - return without caching\n return new Response(feedContent, {\n status: 200,\n headers: {\n 'Content-Type': contentType,\n 'Cache-Control': 'public, max-age=3600, s-maxage=3600',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n },\n });\n } catch (error) {\n console.error('Feed generation error:', error);\n \n // Try to serve stale cache on error if available\n if (config.enabled) {\n const staleContent = await cache.getFeedContent(schemaName, format, contentKeyOptions);\n if (staleContent) {\n console.log(`Serving stale cache due to error`);\n return new Response(staleContent.content, {\n status: 200,\n headers: {\n 'Content-Type': staleContent.contentType,\n 'ETag': staleContent.etag,\n 'Cache-Control': 'public, max-age=3600, s-maxage=3600, must-revalidate',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n 'X-Cache': 'STALE',\n 'Warning': '299 - \"Cache is stale\"',\n },\n });\n }\n }\n \n return new Response(\n JSON.stringify({\n error: 'Failed to generate feed',\n message: error instanceof Error ? error.message : 'Unknown error',\n }),\n {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n }\n )\n }\n}\n\n/**\n * Route handler for monthly archive feeds (RFC 5005).\n *\n * URL Pattern: /:collection/archive/:year/:month/:format\n * Examples:\n * - /posts/archive/2024/02/rss → RSS feed of posts from February 2024\n */\nexport async function handleArchiveFeedRequest(\n collectionSegment: string,\n year: number,\n month: number,\n formatSegment: string,\n ifNoneMatch?: string | null,\n cacheBust?: string,\n siteConfigOverrides?: SiteConfigOverrides,\n): Promise<Response> {\n if (year < 1970 || year > 2100) {\n return new Response(\n JSON.stringify({ error: `Invalid year: ${year}` }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n if (month < 1 || month > 12) {\n return new Response(\n JSON.stringify({ error: `Invalid month: ${month}` }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const format = parseFormat(formatSegment);\n if (!format) {\n return new Response(\n JSON.stringify({ error: `Invalid feed format: ${formatSegment}` }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const schemaName = pluralize.singular(collectionSegment.toLowerCase());\n const collectionName = pluralize(schemaName);\n const siteConfig = resolveSiteConfig(siteConfigOverrides);\n\n const contentKeyOptions = { archive: { year, month } };\n const cache = getCacheManager();\n const config = loadCacheConfig();\n\n try {\n if (config.enabled) {\n const cachedContent = await cache.getFeedContent(schemaName, format, contentKeyOptions);\n if (cachedContent) {\n if (ifNoneMatch && checkIfNoneMatch(ifNoneMatch, cachedContent.etag)) {\n return new Response(null, {\n status: 304,\n headers: {\n ETag: cachedContent.etag,\n 'Last-Modified': new Date(cachedContent.lastModified * 1000).toUTCString(),\n 'Cache-Control': 'public, max-age=86400, s-maxage=86400, must-revalidate',\n },\n });\n }\n return new Response(cachedContent.content, {\n status: 200,\n headers: {\n 'Content-Type': cachedContent.contentType,\n ETag: cachedContent.etag,\n 'Last-Modified': new Date(cachedContent.lastModified * 1000).toUTCString(),\n 'Cache-Control': 'public, max-age=86400, s-maxage=86400, must-revalidate',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n 'X-Cache': 'HIT',\n },\n });\n }\n }\n\n const items = await getFeedItemsBySchemaNameForMonth(schemaName, year, month) as GraphQLItem[];\n\n const baseUrl = `${siteConfig.feedUrl}/${collectionName}/${format}`;\n const archiveBase = `${siteConfig.feedUrl}/${collectionName}/archive`;\n\n const archiveLinks: FeedArchiveLink[] = [\n { rel: 'prev-archive', href: `${archiveBase}/${month === 1 ? year - 1 : year}/${month === 1 ? 12 : month - 1}/${format}` },\n { rel: 'current', href: baseUrl },\n ];\n\n const nextYear = month === 12 ? year + 1 : year;\n const nextMonth = month === 12 ? 1 : month + 1;\n const now = new Date();\n if (nextYear < now.getFullYear() || (nextYear === now.getFullYear() && nextMonth <= now.getMonth() + 1)) {\n archiveLinks.push({ rel: 'next-archive', href: `${archiveBase}/${nextYear}/${nextMonth}/${format}` });\n }\n\n const feedContent = await createFeed(\n items,\n schemaName,\n format,\n cacheBust,\n undefined,\n archiveLinks,\n true,\n undefined,\n siteConfigOverrides,\n );\n\n const contentType = getContentType(format);\n\n if (config.enabled) {\n await cache.setFeedContent(schemaName, format, feedContent, contentType, contentKeyOptions);\n const cachedContent = await cache.getFeedContent(schemaName, format, contentKeyOptions);\n const etag = cachedContent?.etag || '';\n const lastModified = cachedContent?.lastModified || Math.floor(Date.now() / 1000);\n\n return new Response(feedContent, {\n status: 200,\n headers: {\n 'Content-Type': contentType,\n ETag: etag,\n 'Last-Modified': new Date(lastModified * 1000).toUTCString(),\n 'Cache-Control': 'public, max-age=86400, s-maxage=86400, must-revalidate',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n 'X-Cache': 'MISS',\n },\n });\n }\n\n return new Response(feedContent, {\n status: 200,\n headers: {\n 'Content-Type': contentType,\n 'Cache-Control': 'public, max-age=86400, s-maxage=86400',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n },\n });\n } catch (error) {\n console.error('Archive feed generation error:', error);\n return new Response(\n JSON.stringify({\n error: 'Failed to generate archive feed',\n message: error instanceof Error ? error.message : 'Unknown error',\n }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n );\n }\n}"],"mappings":";;;;;;;;;;;AAEA,IAAI,IAAc;AAMlB,SAAS,GAAyB,GAA2B;CAC3D,OAAO,KAAY,EAA0B,KAAK;AACpD;AAGA,eAAsB,EACpB,GACe;CACf,IAAI,GAAa;CACjB,IAAM,CAAC,EAAE,8BAA2B,EAAE,oCAAiC,MAAM,QAAQ,IAAI,CACvF,OAAO,2BACP,OAAO,6BACT,CAAC;CAID,IAHA,EAAwB,GAExB,EAA4B,EAAE,eADR,GAAyB,GAAS,aAC1B,EAAc,CAAC,GACzC,GAAS,eAAe;EAC1B,IAAM,EAAE,yBAAsB,MAAM,OAAO;EAC3C,EAAkB,QAAQ,EAAQ,aAAa;CACjD;CACA,IAAc;AAChB;AAGA,IAAa,KAAuB;AAEpC,eAAsB,KAAuD;CAE3E,OADA,MAAM,EAAuB,GACtB,EAAE,qBAAqB,EAAY;AAC5C;AAEA,eAAsB,KAAoC;CACxD,IAAc;AAChB;;;AC9BA,IAAa,IAAkC;CAC7C,OAAO;CACP,aAAa;CACb,SAAS;CACT,SAAS;CACT,UAAU;CACV,WAAW,sBAAK,IAAI,KAAK,GAAE,YAAY,EAAE;CACzC,QAAQ;EACN,MAAM;EACN,OAAO;EACP,MAAM;CACR;AACF;AAEA,SAAS,EAAgB,GAAgC;CACvD,OAAO;EACL,GAAG;EACH,QAAQ,EAAO,SAAS,EAAE,GAAG,EAAO,OAAO,IAAI,KAAA;CACjD;AACF;AAEA,IAAI,IAAyB,EAAgB,CAAmB;AAKhE,SAAgB,EACd,GACA,IAAmB,GACP;CACZ,IAAI,CAAC,GACH,OAAO,EAAgB,CAAI;CAG7B,IAAM,IAAuB;EAC3B,GAAG;EACH,GAAG;EACH,QAAQ,EAAK,SAAS,EAAE,GAAG,EAAK,OAAO,IAAI,KAAA;CAC7C;CAUA,OARI,EAAU,WAAW,KAAA,MACvB,EAAS,SAAS;EAChB,GAAI,EAAS,UAAU,EAAE,MAAM,GAAG;EAClC,GAAG,EAAU;EACb,MAAM,EAAU,OAAO,QAAQ,EAAS,QAAQ,QAAQ;CAC1D,IAGK;AACT;AAGA,SAAgB,KAA4B;CAC1C,OAAO,EAAgB,CAAU;AACnC;AAMA,SAAgB,GAAc,GAA4C;CAExE,OADA,IAAa,EAAkB,GAAW,CAAmB,GACtD,GAAc;AACvB;AAGA,SAAgB,KAAwB;CACtC,IAAa,EAAgB,CAAmB;AAClD;AAgBA,SAAgB,IAOd;CAaA,OAAO;EAAE,aAZW,QAAQ,IAAI,oBAAoB,KAAK,KAAK;EAYxC,aAVpB,QAAQ,IAAI,oBAAoB,KAAK,KAAK;EAUT,SARjC,QAAQ,IAAI,eAAe,KAAK,KAAK;EAQK,iBAPpB,QAAQ,IAAI,uBAAuB,YAAY,MAAM;EAOhB,UAN5C,SAAS,QAAQ,IAAI,kBAAkB,MAAM,EAMD;EAAU,uBAJrE,QAAQ,IAAI,kCAAkC,YAAY,MAAM,SAC5D,kBACA;CAEuF;AAC/F;;;ACrGA,IAAM,MAAoB,MAA8B;CACtD,IAAM,oBAAO,IAAI,KAAK,IAAY,GAAI,GAChC,IAAO;EAAC;EAAO;EAAO;EAAO;EAAO;EAAO;EAAO;CAAK,GACvD,IAAS;EAAC;EAAO;EAAO;EAAO;EAAO;EAAO;EAAO;EAAO;EAAO;EAAO;EAAO;EAAO;CAAK,GAE5F,IAAM,EAAK,EAAK,UAAU,IAC1B,IAAQ,EAAO,EAAK,YAAY,IAChC,IAAO,EAAK,eAAe;CAMjC,OAAO,GAAG,EAAI,IALC,EAAK,WAAW,EAAE,SAAS,EAAE,SAAS,GAAG,GAKtC,EAAO,GAAG,EAAM,GAAG,EAAK,GAJ5B,EAAK,YAAY,EAAE,SAAS,EAAE,SAAS,GAAG,GAIX,EAAM,GAHnC,EAAK,cAAc,EAAE,SAAS,EAAE,SAAS,GAAG,GAGN,EAAQ,GAF9C,EAAK,cAAc,EAAE,SAAS,EAAE,SAAS,GAAG,GAEK,EAAQ;AAC3E,GAEM,MAAqB,MAClB,MAAe,UAClB,WACA,MAAe,SACb,UACA,EAAW,YAAY,IAAI,KAMtB,KACX,GACA,GACA,GACA,MACS;CACT,IAAM,EAAE,gBAAa,gBAAa,eAAY;CAE9C,AAAI,CAAC,EAAK,SAAS,CAAC,EAAK,SACvB,EAAK,QAAQ,GACb,EAAK,QAAQ,KACJ,EAAK,SAAS,CAAC,EAAK,QAC7B,EAAK,QAAQ,EAAK,QACT,EAAK,SAAS,CAAC,EAAK,UAC7B,EAAK,QAAQ,EAAK;CAGpB,IAAM,IACJ,EAAK,0BACL,OAAO,EAAK,0BAA2B,YACvC,EAAK,uBAAuB,KAAK,MAAM,MACvC,EAAK,2BAA2B,eAChC,EAAK,2BAA2B,IAC3B,EAAK,uBAAkC,KAAK,IAC7C,MACA,IACJ,EAAK,wBACL,OAAO,EAAK,wBAAyB,YACrC,EAAK,qBAAqB,KAAK,MAAM,MACrC,EAAK,yBAAyB,eAC9B,EAAK,yBAAyB,IACzB,EAAK,qBAAgC,KAAK,IAC3C,MACA,IAAuB,KAA6B,GAEpD,IAAiB,GAAkB,CAAU,GAC7C,IACJ,KAAW,OAAO,KAAY,YAAY,EAAQ,KAAK,MAAM,KAAK,IAAU,WAE1E;CACJ,IAAI,KAAwB,MAAyB,KAAW,EAAqB,SAAS,GAC5F,IAAI;EACF,IAAc,EAA4B,CAAoB;CAChE,SAAS,GAAO;EAEd,AADA,QAAQ,MAAM,8DAA8D,CAAK,GACjF,AACE,IADE,KAAe,OAGH,GAAG,EAAQ,QAAQ,OAAO,EAAE,EAAE,GAAG,EAAe,GAAG,MAFnD,GAAG,EAAY,QAAQ,OAAO,EAAE,EAAE,IAAI,KAAe,oBAAoB,QAAQ,OAAO,EAAE,EAAE,GAAG;CAIjH;MAEA,AACE,IADE,KAAe,OAGH,GAAG,EAAQ,QAAQ,OAAO,EAAE,EAAE,GAAG,EAAe,GAAG,MAFnD,GAAG,EAAY,QAAQ,OAAO,EAAE,EAAE,IAAI,KAAe,oBAAoB,QAAQ,OAAO,EAAE,EAAE,GAAG;CAMjH,IAAM,IAAe,EAAK,QAAQ,EAAK;CACvC,AACE,CAAC,KACD,MAAgB,eACf,OAAO,KAAgB,YAAY,EAAY,KAAK,MAAM,MAE3D,EAAK,OAAO,GACZ,EAAK,OAAO,OAER,CAAC,EAAK,QAAQ,EAAK,SAAS,iBAC9B,EAAK,OAAO,KAEV,CAAC,EAAK,QAAQ,EAAK,SAAS,iBAC9B,EAAK,OAAO;CAIhB,IAAM,IAAe,EAAK,QAAQ,EAAK;CAiBvC,IAfE,CAAC,KACD,MAAgB,eACf,OAAO,KAAgB,YAAY,EAAY,KAAK,MAAM,MAE3D,EAAK,OAAO,EAAK,QAAQ,GACzB,EAAK,OAAO,EAAK,UAEb,CAAC,EAAK,QAAQ,EAAK,SAAS,iBAC9B,EAAK,OAAO,KAEV,CAAC,EAAK,QAAQ,EAAK,SAAS,iBAC9B,EAAK,OAAO,KAIZ,EAAK,eAAe,CAAC,EAAK,WAAW,CAAC,EAAK,SAAS;EACtD,IAAM,IAAU,GAAiB,EAAK,WAAqB;EAE3D,AADA,EAAK,UAAU,GACf,EAAK,UAAU;CACjB,OAAO,AAAI,EAAK,WAAW,CAAC,EAAK,UAC/B,EAAK,UAAU,EAAK,UACX,EAAK,WAAW,CAAC,EAAK,YAC/B,EAAK,UAAU,EAAK;CAYtB,AATI,CAAC,EAAK,WAAW,CAAC,EAAK,WACzB,EAAK,UAAU,GACf,EAAK,UAAU,KACN,EAAK,WAAW,CAAC,EAAK,UAC/B,EAAK,UAAU,EAAK,UACX,EAAK,WAAW,CAAC,EAAK,YAC/B,EAAK,UAAU,EAAK,UAGlB,EAAK,YAAY,CAAC,EAAK,WACzB,EAAK,WAAW,EAAK,WACZ,EAAK,YAAY,CAAC,EAAK,aAChC,EAAK,WAAW,EAAK;AAEzB,GAMM,KACJ,GACA,MACS;CAUT,IATI,CAAC,EAAM,SAAS,CAAC,EAAM,SACzB,EAAM,QAAQ,GACd,EAAM,QAAQ,KACL,EAAM,SAAS,CAAC,EAAM,QAC/B,EAAM,QAAQ,EAAM,QACX,EAAM,SAAS,CAAC,EAAM,UAC/B,EAAM,QAAQ,EAAM,QAGlB,EAAM,eAAe,CAAC,EAAM,WAAW,CAAC,EAAM,SAAS;EACzD,IAAM,IAAU,GAAiB,EAAM,WAAqB;EAE5D,AADA,EAAM,UAAU,GAChB,EAAM,UAAU;CAClB,OAAO,AAAI,EAAM,WAAW,CAAC,EAAM,UACjC,EAAM,UAAU,EAAM,UACb,EAAM,WAAW,CAAC,EAAM,YACjC,EAAM,UAAU,EAAM;CAYxB,AATI,CAAC,EAAM,WAAW,CAAC,EAAM,WAC3B,EAAM,UAAU,GAChB,EAAM,UAAU,KACP,EAAM,WAAW,CAAC,EAAM,UACjC,EAAM,UAAU,EAAM,UACb,EAAM,WAAW,CAAC,EAAM,YACjC,EAAM,UAAU,EAAM,UAGpB,EAAM,YAAY,CAAC,EAAM,WAC3B,EAAM,WAAW,EAAM,WACd,EAAM,YAAY,CAAC,EAAM,aAClC,EAAM,WAAW,EAAM;CAGzB,IAAM,IACJ,EAAM,0BACN,OAAO,EAAM,0BAA2B,YACxC,EAAM,uBAAuB,KAAK,MAAM,MACxC,EAAM,2BAA2B,eACjC,EAAM,2BAA2B,IAC5B,EAAM,uBAAkC,KAAK,IAC9C,MACA,IACJ,EAAM,wBACN,OAAO,EAAM,wBAAyB,YACtC,EAAM,qBAAqB,KAAK,MAAM,MACtC,EAAM,yBAAyB,eAC/B,EAAM,yBAAyB,IAC1B,EAAM,qBAAgC,KAAK,IAC5C,MACA,IAAuB,KAA6B;CAO1D,IALA,OAAO,EAAM,MACb,OAAO,EAAM,MACb,OAAO,EAAM,MACb,OAAO,EAAM,MAET,KAAwB,EAAqB,SAAS,GACxD,IAAI;EACF,IAAM,IAAY,EAA4B,CAAoB;EAElE,AADA,EAAM,OAAO,GACb,EAAM,OAAO;CACf,QAAQ,CAER;AAEJ;AAEA,SAAS,EAAmB,GAAkD;CAC5E,IAAI,CAAC,KAAS,OAAO,KAAU,YAAY,MAAM,QAAQ,CAAK,GAAG,OAAO;CACxE,IAAM,IAAM;CACZ,OAAO,OAAO,EAAI,WAAY,YAAY,OAAO,EAAI,WAAY;AACnE;AAMA,SAAS,GACP,GACA,GACA,GACM;CAEN,EAAoB,GADH,EAAK,WAAW,EAAK,WAAW,IACd,GAAY,CAAO;CAEtD,KAAK,IAAM,KAAO,OAAO,KAAK,CAAI,GAAG;EACnC,IAAI,EAAI,WAAW,GAAG,GAAG;EACzB,IAAM,IAAQ,EAAK;EACnB,IAAI,MAAM,QAAQ,CAAK;QAChB,IAAM,KAAM,GACf,IAAI,EAAmB,CAAE,GAAG;IAC1B,IAAM,IAAa,EAAG,WAAW,EAAG,WAAW,IACzC,IACJ,OAAO,EAAG,cAAe,WAAW,EAAG,aAAa;IACtD,AAAI,MAAiB,UACnB,EAA6B,GAAI,CAAS,IAE1C,EAAoB,GAAI,GAAW,GAAc,CAAO;GAE5D;SAEG,IAAI,EAAmB,CAAK,GAAG;GACpC,IAAM,IAAa,EAAM,WAAW,EAAM,WAAW,IAC/C,IACJ,OAAO,EAAM,cAAe,WAAW,EAAM,aAAa;GAC5D,AAAI,MAAiB,UACnB,EAA6B,GAAO,CAAS,IAE7C,EAAoB,GAAO,GAAW,GAAc,CAAO;EAE/D;CACF;AACF;AAEA,SAAS,EACP,GACA,GAC2B;CAC3B,IAAM,IAAa,EAAe,GAC5B,IAA8C;EAClD,aAAa,EAAW;EACxB,aAAa,EAAW;EACxB,SAAS,EAAW;CACtB;CAEA,OAAO,EAAQ,KAAK,MAAW;EAC7B,IAAM,IAAO,EAAO;EAEpB,OADA,GAAsB,GAAM,GAAY,CAAe,GAChD;CACT,CAAC;AACH;AAEA,IAAa,KAA2B,OACtC,GACA,MACuC;CACvC,MAAM,EAAuB;CAC7B,IAAM,IAAa,EAAe,GAI5B,EAAE,aAAU,MAAM,GAAc,GAAY;EAChD,OAJY,GAAS,SAAS;EAK9B,MAJW,GAAS,QAAQ;EAK5B,iBAAiB,EAAW,oBAAoB;EAChD,gBAAgB;CAClB,CAAC;CACD,OAAO,EAAuB,GAAY,CAAK;AACjD,GAEa,KAAmC,OAC9C,GACA,GACA,OAEA,MAAM,EAAuB,GAOtB,EAAuB,GAAY,MAJpB,GAAsB,GAAY,GAAM,GAAO;CACnE,iBAHiB,EAGA,EAAW,oBAAoB;CAChD,gBAAgB;AAClB,CAAC,CACgD;;;AClUnD,SAAS,GAAY,GAAwC;CAC3D,IAAI;EACF,OAAO,KAAK,MAAM,KAAK,UAAU,CAAI,CAAC;CACxC,QAAQ;EACN,OAAO,EAAE,GAAI,EAAiC;CAChD;AACF;AAGA,IAAM,KAAS,IAAI,GAAO,EACxB,cAAc,EACZ,MAAM;CACJ;CACA;CACA;CACA;CACA;AACF,EACF,EACF,CAAC;AAED,eAAsB,GAAe,GAAwC;CAC3E,IAAM,IAAO,MAAM,GAAO,YAAY,CAAG;CACzC,OAAO;EACL,OAAO,EAAK;EACZ,MAAM,EAAK;EACX,aAAa,EAAK;EAClB,QAAQ,EAAK,SAAS,CAAC,GAAG,IAAI,EAAW;CAC3C;AACF;;;ACpCA,IAAM,IAAQ,oCACR,IAAa,4CACb,IAAW,iCAGX,KAA4C,EAChD,aAAa;CACX,QAAQ;CACR,KAAK;AACP,EACF,GACM,KAA8B,wCAC9B,KAAwB,IAAI,IAAI;CAAC;CAAO;CAAS;CAAM;CAAS;CAAW;CAAQ;AAAI,CAAC,GAExF,KAAyB,IAAI,IAAI;CAAC;CAAS;CAAQ;CAAQ;CAAW;AAAS,CAAC;AAEtF,SAAS,GAA0B,GAAa,GAAwC;CAGtF,OAFK,GAAuB,IAAI,CAAG,IAClB,EAAI,OAAO,CAAC,EAAE,YAAY,IAAI,EAAI,MAAM,CAAC,KACvC,IAF0B;AAG/C;AAEA,IAAM,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,SAAS,EAAiB,GAA6B;CACrD,IAAI,aAAgB,MAClB,OAAO,EAAK,YAAY;CAE1B,IAAI,OAAO,KAAS,UAAU;EAC5B,IAAM,IAAI,IAAI,KAAK,CAAI;EACvB,OAAQ,OAAO,MAAM,EAAE,QAAQ,CAAC,IAAsB,IAAlB,EAAE,YAAY;CACpD;CACA,OAAO,OAAO,CAAI;AACpB;AAEA,SAAS,GAAW,GAAwB;CAC1C,OAAO,YAAY,KAAK,CAAK;AAC/B;AAEA,SAAS,EAAe,GAAoB,GAAqB;CAC/D,IAAM,IAAM,OAAO,CAAK;CACpB,UAAQ,IAEZ,IAAI,GAAW,CAAG,GAChB,IAAI,EAAI,SAAS,KAAK,GAAG;EACvB,IAAM,IAAQ,EAAI,MAAM,KAAK;EAC7B,KAAK,IAAI,IAAI,GAAG,IAAI,EAAM,QAAQ,KAEhC,AADA,EAAO,IAAI,EAAM,EAAG,GAChB,IAAI,EAAM,SAAS,KACrB,EAAO,IAAI,KAAK;CAGtB,OACE,EAAO,IAAI,CAAG;MAGhB,EAAO,IAAI,CAAG;AAElB;AAEA,SAAS,EAAyB,GAAkD;CAClF,OAEE,OAAO,KAAU,cADjB,KAEA,CAAC,MAAM,QAAQ,CAAK,KACpB,EAAE,aAAiB;AAEvB;AAEA,IAAM,KAA8B,GAC9B,KAAoB,IAAI,IAAI;CAChC,GAAG;CACH;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,SAAS,GAAqB,GAAqB;CACjD,OAAO,EAAI,SAAS,GAAG,KAAK,EAAI,SAAS,IAAI,EAAI,MAAM,GAAG,EAAE,IAAI;AAClE;AAEA,SAAS,GAAoB,GAAwB;CAMnD,OALc,EACX,KAAK,EACL,QAAQ,qBAAqB,GAAG,EAChC,QAAQ,gBAAgB,EAAE,EAC1B,QAAQ,MAAM,KACV,EAAM,YAAY;AAC3B;AAEA,SAAS,GACP,GACA,GACQ;CACR,IAAI,CAAC,EAAa,IAAI,CAAe,KAAK,CAAC,GAAsB,IAAI,CAAe,GAElF,OADA,EAAa,IAAI,CAAe,GACzB;CAGT,IAAI,IAAS;CACb,SAAa;EACX,IAAM,IAAY,GAAG,IAAkB;EACvC,IAAI,CAAC,EAAa,IAAI,CAAS,KAAK,CAAC,GAAsB,IAAI,CAAS,GAEtE,OADA,EAAa,IAAI,CAAS,GACnB;EAET,KAAU;CACZ;AACF;AAEA,SAAS,GAAgB,GAAoD;CAC3E,OACE,MAAM,QAAQ,CAAK,KACnB,EAAM,SAAS,KACf,EAAM,OAAO,MAAU,EAAyB,CAAK,CAAC;AAE1D;AAEA,SAAS,GAA0B,GAAwD;CACzF,IAAM,IAA2C,EAAE,GAAG,GAAoB,GACpE,IAAe,IAAI,IACvB,OAAO,OAAO,CAAkB,EAAE,KAAK,MAAqB,EAAiB,MAAM,CACrF,GAEM,oBAAyB,IAAI,IAAY;CAC/C,KAAK,IAAM,KAAQ,GACjB,KAAK,IAAM,CAAC,GAAK,MAAU,OAAO,QAAQ,CAAI,GAAG;EAC/C,IAAM,IACH,MAAQ,YAAY,EAAyB,CAAK,KAClD,MAAQ,aAAa,GAAgB,CAAK;EACzC,EAAI,WAAW,GAAG,KAClB,GAAkB,IAAI,CAAG,KAAK,CAAC,MAC/B,EAAyB,CAAK,KAAK,GAAgB,CAAK,MAC1D,EAAuB,IAAI,GAAqB,CAAG,CAAC;CAExD;CAGF,IAAM,IAA+B,CAAC,GAAG,CAAsB,EAAE,KAAK;CACtE,KAAK,IAAM,KAAe,GACpB,EAAmB,OAGvB,EAAmB,KAAe;EAChC,QAFa,GADS,GAAoB,CACV,GAAiB,CAEjD;EACA,KAAK,GAAG,GAA4B,GAAG,EAAY;CACrD;CAGF,OAAO;AACT;AAEA,SAAS,EACP,GACA,GACsC;CACtC,IAAI,KAAO,GAAoB,OAAO,EAAmB;CACzD,IAAI,EAAI,SAAS,GAAG,KAAK,EAAI,SAAS,GAAG;EACvC,IAAM,IAAW,EAAI,MAAM,GAAG,EAAE;EAChC,IAAI,KAAY,GAAoB,OAAO,EAAmB;CAChE;AAEF;AAEA,SAAS,GAA0B,GAAmB,GAAqD;CAEzG,OADK,IACE,GAAG,EAAiB,OAAO,GAAG,MADP;AAEhC;AAEA,SAAS,EACP,GACA,GACA,GACA,GACA,IAAQ,GACR,GACM;CACN,IAAM,IAAM,EAAO,IAAI,CAAO;CAC9B,KAAK,IAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,CAAG,GAAG;EACxC,IAAI,KAAK,QAAQ,EAAE,WAAW,GAAG,KAAK,MAAM,SAAS;EACrD,IAAM,IAAe,GAA0B,GAAG,CAAgB;EAClE,IAAI,KAAS,IAA6B;GAExC,EADc,EAAI,IAAI,CACP,GAAO,OAAO,KAAM,WAAW,KAAK,UAAU,CAAC,IAAI,OAAO,CAAC,CAAC;GAC3E;EACF;EACA,IAAI,MAAM,QAAQ,CAAC,GAAG;GAEpB,IAAM,IAAe,GADL,EAAE,SAAS,GAAG,KAAK,EAAE,SAAS,IAAI,EAAE,MAAM,GAAG,EAAE,IAAI,GACX,CAAgB;GACxE,KAAK,IAAM,KAAS,GACd,KAAiC,SACjC,EAAyB,CAAK,IAChC,EAA0B,GAAK,GAAO,GAAc,IAAO,IAAQ,GAAG,CAAgB,IAGtF,EADa,EAAI,IAAI,CACN,GAAM,OAAO,CAAK,CAAC;GAGtC;EACF;EACA,IAAI,EAAyB,CAAC,GAAG;GAC/B,EACE,GACA,GACA,GACA,IACA,IAAQ,GACR,CACF;GACA;EACF;EAEA,EADc,EAAI,IAAI,CACP,GAAO,OAAO,CAAC,CAAC;CACjC;CACA,IAAI,GAAc;EAChB,IAAM,IAAO,EAAI,QAAQ,EAAI;EAC7B,AAAI,KAAM,EAAO,IAAI,GAAO,SAAS,EAAE,IAAI,OAAO,CAAI,CAAC;CACzD;AACF;AAEA,SAAS,GACP,GACA,GACA,GACA,GACA,GACM;CACF,WAAiC,QAAQ,EAAI,WAAW,GAAG,MAC3D,MAAQ,SAEZ;MAAI,MAAQ,QAAQ;GAClB,IAAM,IAAO,GACP,IAAM,GAAM,SAAS,OAAO,CAAK;GACvC,IAAI,CAAC,GAAK;GACV,IAAM,IAAgC,CAAC;GAKvC,AAJI,OAAO,GAAM,eAAgB,cAC/B,EAAM,cAAc,EAAK,cAAc,SAAS,UAGlD,EADgB,EAAW,IAAI,QAAQ,CACxB,GAAS,CAAG;GAC3B;EACF;EAEA,IAAI,MAAQ,YAA8B,OAAO,KAAU,YAAnC,GAA6C;GACnE,IAAM,IAAM,GACN,IAAQ,EAAI,SAAS,OAAO,CAAK;GACvC,IAAI,CAAC,GAAO;GACZ,IAAM,IAAQ,EAAI,MAAM,EAAE,KAAK,EAAI,IAAI,IAAI,CAAC;GAC5C,EAAW,IAAI,UAAU,CAAK,EAAE,IAAI,CAAK;GACzC;EACF;EAEA,IAAI,MAAQ,gBAAgB,MAAM,QAAQ,CAAK,GAAG;GAChD,KAAK,IAAM,KAAO,GAChB,IAAI,KAAO,OAAO,KAAQ,YAAY,SAAS,GAAK;IAClD,IAAM,IAAgC;KACpC,KAAK,OAAO,EAAI,GAAG;KACnB,MAAM,OAAO,EAAI,QAAQ,0BAA0B;IACrD;IAEA,AADI,EAAI,UAAU,SAAM,EAAM,SAAS,OAAO,EAAI,MAAM,IACxD,EAAW,IAAI,aAAa,CAAK;GACnC;GAEF;EACF;EAEA,IAAI,MAAQ,QAA0B,OAAO,KAAU,YAAnC,GAA6C;GAC/D,IAAM,IAAK;GACX,KAAK,IAAM,CAAC,GAAO,MAAU,OAAO,QAAQ,CAAE,GACxC,SAAiC,MACrC,IAAI,MAAM,QAAQ,CAAK;SAChB,IAAM,KAAK,GACd,IAAI,KAAyB,MAAM;KACjC,IAAM,IAAM,EAAW,IAAI,GAAO,CAAK;KACvC,AAAI,aAAa,OACf,EAAI,IAAI,EAAE,YAAY,CAAC,IAEvB,EAAe,GAAK,OAAO,CAAC,CAAC;IAEjC;UAEG;IACL,IAAM,IAAM,EAAW,IAAI,GAAO,CAAK;IACvC,AAAI,aAAiB,OACnB,EAAI,IAAI,EAAM,YAAY,CAAC,IAE3B,EAAe,GAAK,OAAO,CAAK,CAAC;GAErC;GAEF;EACF;EAEA,IAAI,MAAQ,mBAAmB;GAE7B,EADY,EAAW,IAAI,GAAY,SACxB,GAAK,OAAO,CAAK,CAAC;GACjC;EACF;EACA,IAAI,MAAQ,aAAa,OAAO,KAAU,YAAY,EAAE,qBAAqB,IAAO;GAElF,EADY,EAAW,IAAI,GAAY,SACxB,GAAK,OAAO,CAAK,CAAC;GACjC;EACF;EACI,UAAQ,WAEZ;OAAI,MAAQ,iBAAiB,MAAM,QAAQ,CAAK,GAAG;IACjD,KAAK,IAAM,KAAS,GAClB,IAAI,KAAS,OAAO,KAAU,UAAU;KACtC,IAAM,IAAW,EAAW,IAAI,GAAU,OAAO;KACjD,KAAK,IAAM,CAAC,GAAI,MAAO,OAAO,QAAQ,CAAK,GACrC,SAA2B,QAC3B,EAAG,WAAW,QAAQ,GAAG;MAC3B,IAAM,IAAK,EAAG,MAAM,CAAC;MACrB,IAAI,MAAM,QAAQ,CAAE;YACb,IAAM,KAAK,GACd,IAAI,KAAK,OAAO,KAAM,UAAU;QAC9B,IAAM,IAAQ,EAAS,IAAI,GAAU,CAAE;QACvC,KAAK,IAAM,CAAC,GAAI,MAAO,OAAO,QAAQ,CAAC,GACrC,AAAI,KAA2B,SACzB,MAAO,SAAS,MAAO,UACzB,EAAe,GAAO,OAAO,CAAE,CAAC,IAEhC,EAAM,IAAI,GAAI,OAAO,CAAE,CAAC;OAIhC;aAEG,AAAI,OAAO,KAAO,YACvB,EAAe,EAAS,IAAI,GAAU,CAAE,GAAG,CAAE;KAEjD;IAEJ;IAEF;GACF;GAEA,IAAI,EAAI,WAAW,QAAQ,KAAK,MAAM,QAAQ,CAAK,GAAG;IACpD,IAAM,IAAY,EAAI,MAAM,CAAC;IAC7B,KAAK,IAAM,KAAQ,GAAO;KACxB,IAAqB,OAAO,KAAS,aAAjC,GAA2C;KAC/C,IAAM,IAAgC,CAAC,GACnC,IAA6B;KACjC,KAAK,IAAM,CAAC,GAAM,MAAY,OAAO,QAAQ,CAAI,GAC3C,KAAqC,SACrC,MAAS,UACX,IAAc,OAAO,CAAO,IAE5B,EAAM,KAAQ,OAAO,CAAO;KAGhC,IAAM,IAAW,EAAW,IAAI,GAAU,GAAW,CAAK;KAC1D,AAAI,MAAgB,QAClB,EAAe,GAAU,CAAW;IAExC;IACA;GACF;GAEA,IAAI,EAAI,WAAW,QAAQ,KAAK,OAAO,KAAU,UAAU;IACzD,IAAM,IAAY,EAAI,MAAM,CAAC;IAE7B,EADY,EAAW,IAAI,GAAU,CACtB,GAAK,CAAK;IACzB;GACF;GAEA,IAAI,aAAiB,MAAM;IAEzB,EADuB,IAAI,CAC3B,EAAI,IAAI,EAAiB,CAAK,CAAC;IAC/B;GACF;GAEA,IAAI,MAAQ,YAAY,EAAyB,CAAK,GAAG;IAEvD,EAA0B,GAAY,GAAO,UAAU,IAAM,GADpC,EAA4B,GAAK,CACM,CAAgB;IAChF;GACF;GAEA,IAAI,MAAQ,aAAa,MAAM,QAAQ,CAAK,GAAG;IAC7C,IAAM,IAAmB,EAA4B,GAAK,CAAkB;IAC5E,KAAK,IAAM,KAAK,GACd,IAAI,KAAK,EAAyB,CAAC,GACjC,EAA0B,GAAY,GAAG,UAAU,IAAM,GAAG,CAAgB;SACvE;KACL,IAAM,IACJ,OAAO,KAAM,WACT,IACA,KAAK,OAAO,KAAM,YAAY,UAAU,IACtC,GAAI,EAAyB,SAAS,GAAG,IAAK,EAAwB,QAAQ,GAAG,GAAG,KAAK,KACxF,EAAwB,OACzB,OAAO,CAAC;KAChB,AAAI,KAAW,EAAW,IAAI,QAAQ,EAAE,IAAI,CAAS;IACvD;IAEF;GACF;GACA,IAAI,MAAQ,gBAAgB,MAAM,QAAQ,CAAK,GAAG;IAChD,KAAK,IAAM,KAAK,GAAO;KACrB,IAAM,IACJ,KAAK,OAAO,KAAM,YAAY,UAAU,IAAK,EAAuB,OAAO,OAAO,CAAC;KACrF,IAAI,GAAK;MACP,IAAM,IACJ,KAAK,OAAO,KAAM,YAAY,YAAY,KAAM,EAA0B,SACtE,EAAE,QAAS,EAAyB,OAAO,IAC3C,CAAC;MACP,EAAW,IAAI,YAAY,CAAK,EAAE,IAAI,CAAG;KAC3C;IACF;IACA;GACF;GAGA,IAAI,MAAM,QAAQ,CAAK,KAAK,EAAM,SAAS,KAAK,EAAM,OAAO,MAAM,EAAyB,CAAC,CAAC,GAAG;IAC/F,IAAM,IAAU,EAAI,SAAS,GAAG,IAAI,EAAI,MAAM,GAAG,EAAE,IAAI,GACjD,IAAmB,EAA4B,GAAK,CAAkB;IAC5E,KAAK,IAAM,KAAK,GACd,AAAI,KAAG,EAA0B,GAAY,GAAG,GAAS,IAAO,GAAG,CAAgB;IAErF;GACF;GAGA,IAAI,MAAM,QAAQ,CAAK,KAAK,EAAM,OAAO,MAAM,OAAO,KAAM,QAAQ,GAAG;IACrE,IAAM,IAAU,EAAI,SAAS,GAAG,IAAI,EAAI,MAAM,GAAG,EAAE,IAAI;IACvD,KAAK,IAAM,KAAK,GACd,AAAI,KAEF,EADY,EAAW,IAAI,CACZ,GAAK,CAAC;IAGzB;GACF;GAGA,IAAI,EAAyB,CAAK,GAAG;IAEnC,EAA0B,GAAY,GAAO,GAAK,IAAO,GADhC,EAA4B,GAAK,CACE,CAAgB;IAC5E;GACF;GAGA,EADY,EAAW,IAAI,CACZ,GAAK,OAAO,CAAK,CAAC;EA1GjC;CA/FA;AA0MF;AAEA,IAAM,KAAU,+BACV,KAAQ;AAwBd,SAAgB,GAAe,GAA+B;CAC5D,IAAM,IAAM,GAAO;EAAE,SAAS;EAAO,UAAU;CAAQ,CAAC,GAClD,IAAqB,GAA0B,EAAQ,KAAK,GAQ5D,IAAmC;EACvC,YAAY;EACZ,iBAAiB;EACjB,eAAe;EACf,cAAc;EACd,GAZ6B,OAAO,OAAO,CAAkB,EAAE,QAC9D,GAAK,OACJ,EAAI,SAAS,EAAkB,YAAY,EAAkB,KACtD,IAET,CAAC,CAOE;CACL;CACA,AAAI,EAAQ,cACV,EAAS,cAAc;CAGzB,IAAM,IADM,EAAI,IAAI,OAAO;EAAE,SAAS;EAAO,GAAG;CAAS,CACzC,EAAI,IAAI,WAAW;EACjC,YAAY;EACZ,iBAAiB;EACjB,eAAe;CACjB,CAAC;CAWD,IATA,EAAQ,IAAI,OAAO,EAAE,IAAI,EAAQ,KAAK,GAClC,EAAQ,QAAM,EAAQ,IAAI,MAAM,EAAE,IAAI,EAAQ,IAAI,GACtD,EAAQ,IAAI,aAAa,EAAE,IAAI,EAAQ,WAAW,GAC9C,EAAQ,YAAU,EAAQ,IAAI,UAAU,EAAE,IAAI,EAAQ,QAAQ,GAC9D,EAAQ,aAAW,EAAQ,IAAI,WAAW,EAAE,IAAI,EAAQ,SAAS,GACjE,EAAQ,aAAW,EAAQ,IAAI,WAAW,EAAE,IAAI,EAAQ,SAAS,GACjE,EAAQ,WAAS,EAAQ,IAAI,SAAS,EAAE,IAAI,EAAiB,EAAQ,OAAO,CAAC,GAC7E,EAAQ,iBAAe,EAAQ,IAAI,eAAe,EAAE,IAAI,EAAiB,EAAQ,aAAa,CAAC,GAE/F,EAAQ,OAAO,QACjB,KAAK,IAAM,KAAQ,EAAQ,OAAO;EAChC,IAAM,IAAgC;GAAE,KAAK,EAAK;GAAK,MAAM,EAAK;EAAK;EAEvE,AADI,EAAK,SAAM,EAAM,OAAO,EAAK,OACjC,EAAQ,IAAI,IAAS,QAAQ,CAAK;CACpC;CAEF,AAAI,EAAQ,aACV,EAAQ,IAAI,IAAO,SAAS;CAG9B,IAAM,IAAa,IAAI,IAAI,CAAc;CAEzC,KAAK,IAAM,KAAQ,EAAQ,OAAO;EAChC,IAAM,IAAa,EAAQ,IAAI,MAAM,GAC/B,oBAAgB,IAAI,IAAY;EAEtC,KAAK,IAAM,KAAO,GAChB,AAAI,KAAO,MACT,GAAsB,GAAY,GAAK,EAAK,IAAM,GAAM,CAAkB,GAC1E,EAAc,IAAI,CAAG;EAGzB,IAAM,IAAY,OAAO,KAAK,CAAI,EAC/B,QACE,MACC,CAAC,EAAc,IAAI,CAAC,KACpB,CAAC,EAAW,IAAI,CAAC,KACjB,CAAC,EAAE,WAAW,GAAG,KACjB,CAAC,GAA0B,GAAG,CAAI,CACtC,EACC,KAAK;EACR,KAAK,IAAM,KAAO,GAChB,GAAsB,GAAY,GAAK,EAAK,IAAM,GAAM,CAAkB;CAE9E;CAEA,OAAO,EAAI,IAAI,EAAE,aAAa,GAAK,CAAC;AACtC;;;ACtgBA,SAAgB,EACd,GACA,GACA,GACQ;CAOR,OANI,GAAS,UACJ,GAAG,EAAW,GAAG,EAAO,WAAW,EAAQ,QAAQ,KAAK,GAAG,EAAQ,QAAQ,UAEhF,GAAS,QAAQ,QAAQ,EAAQ,OAAO,IACnC,GAAG,EAAW,GAAG,EAAO,QAAQ,EAAQ,SAE1C,GAAG,EAAW,GAAG;AAC1B;;;AC7DA,SAAgB,GAAa,GAAuB;CAElD,OAAO,IADM,GAAW,KAAK,EAAE,OAAO,CAAK,EAAE,OAAO,KACzC,EAAK,UAAU,GAAG,EAAE,EAAE;AACnC;AAKA,SAAgB,GACd,GACA,GACA,GACA,GACQ;CAER,OAAO,GAAa,GADC,EAAW,GAAG,EAAO,GAAG,EAAa,GAAG,GAChC;AAC/B;AAMA,SAAgB,GAAY,GAAe,GAAwB;CAEjE,IAAM,KAAa,MAAiB,EAAK,QAAQ,UAAU,EAAE;CAC7D,OAAO,EAAU,CAAK,MAAM,EAAU,CAAK;AAC7C;AAMA,SAAgB,GAAiB,GAAkD;CAIjF,OAHK,IAGE,EACJ,MAAM,GAAG,EACT,KAAI,MAAQ,EAAK,KAAK,CAAC,EACvB,QAAO,MAAQ,EAAK,SAAS,CAAC,IALxB,CAAC;AAMZ;AAKA,SAAgB,GACd,GACA,GACS;CACT,IAAM,IAAQ,GAAiB,CAAiB;CAWhD,OAVI,EAAM,WAAW,IACZ,KAIL,EAAM,SAAS,GAAG,IACb,KAIF,EAAM,MAAK,MAAQ,GAAY,GAAM,CAAW,CAAC;AAC1D;;;AC1DA,IAAa,KAAb,MAAyB;CACvB,mCAA2D,IAAI,IAAI;CACnE,qCAA+D,IAAI,IAAI;CACvE;CAEA,YAAY,GAAqB;EAC/B,KAAK,SAAS;CAChB;CAEA,eACE,GACA,GACA,GAC0B;EAC1B,IAAM,IAAM,EAAgB,GAAY,GAAQ,CAAiB,GAC3D,IAAS,KAAK,iBAAiB,IAAI,CAAG;EAW5C,OAVK,IAIO,KAAK,MAAM,KAAK,IAAI,IAAI,GAChC,IAAM,EAAO,aACf,KAAK,iBAAiB,OAAO,CAAG,GACzB,QAGF,IATE;CAUX;CAEA,eACE,GACA,GACA,GACA,GACA,GACA,GACM;EACN,IAAM,IAAM,EAAgB,GAAY,GAAQ,CAAiB,GAC3D,IAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,GAElC,IAAY,KADN,KAAe,KAAK,OAAO,MAKjC,IAAmC;GACvC;GACA;GACA,MALW,GAAoB,GAAY,GAAQ,GAAK,EAAQ,MAKhE;GACA,cAAc;GACd;EACF;EAEA,KAAK,iBAAiB,IAAI,GAAK,CAAa;CAC9C;CAEA,kBAAkB,GAA0B;EAC1C,IAAM,IAAyB,CAAC;EAChC,KAAK,IAAM,KAAO,KAAK,iBAAiB,KAAK,GAC3C,AAAI,EAAI,WAAW,GAAG,EAAW,EAAE,KACjC,EAAa,KAAK,CAAG;EAGzB,EAAa,SAAQ,MAAO,KAAK,iBAAiB,OAAO,CAAG,CAAC;CAC/D;CAEA,iBAAiB,GAA6C;EAC5D,IAAM,IAAS,KAAK,mBAAmB,IAAI,CAAa;EAWxD,OAVK,IAIO,KAAK,MAAM,KAAK,IAAI,IAAI,GAChC,IAAM,EAAO,aACf,KAAK,mBAAmB,OAAO,CAAa,GACrC,QAGF,EAAO,WATL;CAUX;CAEA,iBAAiB,GAAuB,GAA+B;EACrE,IAAM,IAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,GAIlC,IAA8B;GAClC;GACA,UAAU;GACV,WALgB,KADO,KAAK,OAAO,eAAe,OAAO;EAO3D;EAEA,KAAK,mBAAmB,IAAI,GAAe,CAAM;CACnD;CAEA,WAAiB;EAEf,AADA,KAAK,iBAAiB,MAAM,GAC5B,KAAK,mBAAmB,MAAM;CAChC;CAEA,aAAa,GAAoC;EAC/C,KAAK,SAAS;GAAE,GAAG,KAAK;GAAQ,GAAG;EAAO;CAC5C;CAEA,WAGE;EACA,OAAO;GACL,kBAAkB,KAAK,iBAAiB;GACxC,oBAAoB,KAAK,mBAAmB;EAC9C;CACF;AACF,GChHa,KAAb,MAAuB;CACrB;CACA;CAEA,YAAY,GAAqB;EAG/B,AAFA,KAAK,WAAW,EAAO,UACvB,KAAK,SAAS,GACd,KAAK,eAAe,EAAE,OAAM,MAAO;GACjC,QAAQ,MAAM,qCAAqC,CAAG;EACxD,CAAC;CACH;CAEA,MAAc,iBAAgC;EAC5C,IAAI;GACF,MAAM,EAAG,MAAM,KAAK,UAAU,EAAE,WAAW,GAAK,CAAC;EACnD,SAAS,GAAO;GACd,IAAK,EAAgC,SAAS,UAC5C,MAAM;EAEV;CACF;CAEA,mBACE,GACA,GACA,GACQ;EAER,IAAM,IADM,EAAgB,GAAY,GAAQ,CAC/B,EAAI,QAAQ,MAAM,GAAG;EACtC,OAAO,EAAK,KAAK,UAAU,GAAG,EAAS,MAAM;CAC/C;CAEA,qBAA6B,GAA+B;EAE1D,OAAO,EADkB,EAAK,KAAK,UAAU,gBACjC,GAAkB,GAAG,EAAc,MAAM;CACvD;CAEA,MAAc,yBAAwC;EACpD,IAAI;GACF,IAAM,IAAmB,EAAK,KAAK,UAAU,gBAAgB;GAC7D,MAAM,EAAG,MAAM,GAAkB,EAAE,WAAW,GAAK,CAAC;EACtD,SAAS,GAAO;GACd,IAAK,EAAgC,SAAS,UAC5C,MAAM;EAEV;CACF;CAEA,MAAM,eACJ,GACA,GACA,GACmC;EACnC,IAAI;GACF,IAAM,IAAW,KAAK,mBAAmB,GAAY,GAAQ,CAAiB,GACxE,IAAO,MAAM,EAAG,SAAS,GAAU,OAAO,GAC1C,IAA4B,KAAK,MAAM,CAAI;GAQjD,OANY,KAAK,MAAM,KAAK,IAAI,IAAI,GAChC,IAAM,EAAO,aACf,MAAM,KAAK,iBAAiB,GAAY,GAAQ,CAAiB,GAC1D,QAGF;EACT,SAAS,GAAO;GAQd,OAPK,EAAgC,SAAS,YAG9C,QAAQ,MACN,wCAAwC,EAAW,GAAG,EAAO,IAC7D,CACF,GALS;EAOX;CACF;CAEA,MAAM,eACJ,GACA,GACA,GACA,GACe;EACf,IAAI;GACF,MAAM,KAAK,eAAe;GAC1B,IAAM,IAAW,KAAK,mBAAmB,GAAY,GAAQ,CAAiB;GAC9E,MAAM,EAAG,UAAU,GAAU,KAAK,UAAU,GAAS,MAAM,CAAC,GAAG,OAAO;EACxE,SAAS,GAAO;GACd,QAAQ,MACN,wCAAwC,EAAW,GAAG,EAAO,IAC7D,CACF;EACF;CACF;CAEA,MAAM,iBACJ,GACA,GACA,GACe;EACf,IAAI;GACF,IAAM,IAAW,KAAK,mBAAmB,GAAY,GAAQ,CAAiB;GAC9E,MAAM,EAAG,OAAO,CAAQ;EAC1B,SAAS,GAAO;GACd,AAAK,EAAgC,SAAS,YAC5C,QAAQ,MACN,yCAAyC,EAAW,GAAG,EAAO,IAC9D,CACF;EAEJ;CACF;CAEA,MAAM,qBAAqB,GAAmC;EAC5D,IAAI;GACF,IAAM,IAAQ,MAAM,EAAG,QAAQ,KAAK,QAAQ,GACtC,IAAS,GAAG,EAAW;GAG7B,KAAK,IAAM,KAAQ,GAGjB,AAAI,EAAK,WAAW,CAAM,KAAK,EAAK,SAAS,OAAM,KAAK,MAAS,GAAG,EAAW,UAC7E,MAAM,EAAG,OAAO,EAAK,KAAK,UAAU,CAAI,CAAC;EAG/C,SAAS,GAAO;GACd,QAAQ,MAAM,wCAAwC,EAAW,IAAI,CAAK;EAC5E;CACF;CAEA,MAAM,iBAAiB,GAAsD;EAC3E,IAAI;GACF,IAAM,IAAW,KAAK,qBAAqB,CAAa,GAClD,IAAO,MAAM,EAAG,SAAS,GAAU,OAAO,GAC1C,IAA8B,KAAK,MAAM,CAAI;GAQnD,OANY,KAAK,MAAM,KAAK,IAAI,IAAI,GAChC,IAAM,EAAO,aACf,MAAM,KAAK,mBAAmB,CAAa,GACpC,QAGF,EAAO;EAChB,SAAS,GAAO;GAKd,OAJK,EAAgC,SAAS,YAG9C,QAAQ,MAAM,0CAA0C,EAAc,IAAI,CAAK,GAFtE;EAIX;CACF;CAEA,MAAM,iBAAiB,GAAuB,GAAwC;EACpF,IAAI;GACF,MAAM,KAAK,uBAAuB;GAClC,IAAM,IAAW,KAAK,qBAAqB,CAAa,GAClD,IAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,GAIlC,IAA8B;IAClC;IACA,UAAU;IACV,WALgB,KADO,KAAK,OAAO,eAAe,OAAO;GAO3D;GAEA,MAAM,EAAG,UAAU,GAAU,KAAK,UAAU,GAAQ,MAAM,CAAC,GAAG,OAAO;EACvE,SAAS,GAAO;GACd,QAAQ,MAAM,0CAA0C,EAAc,IAAI,CAAK;EACjF;CACF;CAEA,MAAM,mBAAmB,GAAsC;EAC7D,IAAI;GACF,IAAM,IAAW,KAAK,qBAAqB,CAAa;GACxD,MAAM,EAAG,OAAO,CAAQ;EAC1B,SAAS,GAAO;GACd,AAAK,EAAgC,SAAS,YAC5C,QAAQ,MAAM,2CAA2C,EAAc,IAAI,CAAK;EAEpF;CACF;CAEA,MAAM,WAA0B;EAC9B,IAAI;GACF,IAAM,IAAQ,MAAM,EAAG,QAAQ,KAAK,QAAQ;GAC5C,KAAK,IAAM,KAAQ,GAEjB,AAAI,EAAK,SAAS,OAAO,KAAK,EAAK,SAAS,GAAG,KAC7C,MAAM,EAAG,OAAO,EAAK,KAAK,UAAU,CAAI,CAAC;GAG7C,IAAM,IAAmB,EAAK,KAAK,UAAU,gBAAgB;GAC7D,IAAI;IACF,IAAM,IAAa,MAAM,EAAG,QAAQ,CAAgB;IACpD,KAAK,IAAM,KAAQ,GACjB,AAAI,EAAK,SAAS,OAAO,KACvB,MAAM,EAAG,OAAO,EAAK,GAAkB,CAAI,CAAC;GAGlD,QAAQ,CAER;EACF,SAAS,GAAO;GACd,QAAQ,MAAM,8BAA8B,CAAK;EACnD;CACF;AACF,GC1Ma,KAAb,MAA0B;CACxB;CACA;CACA;CACA,QAA4B;EAC1B,MAAM;EACN,QAAQ;EACR,WAAW;EACX,QAAQ;CACV;CAEA,YAAY,GAAqB;EAG/B,AAFA,KAAK,SAAS,GACd,KAAK,cAAc,IAAI,GAAY,CAAM,GACzC,KAAK,YAAY,IAAI,GAAU,CAAM;CACvC;CAEA,MAAM,eACJ,GACA,GACA,GACmC;EACnC,IAAI,CAAC,KAAK,OAAO,SACf,OAAO;EAGT,IAAI;GACF,IAAI,IAAS,KAAK,YAAY,eAAe,GAAY,GAAQ,CAAiB;GAoBlF,OAnBI,KACF,KAAK,MAAM,QACJ,MAGT,IAAS,MAAM,KAAK,UAAU,eAAe,GAAY,GAAQ,CAAiB,GAC9E,KACF,KAAK,YAAY,eACf,GACA,GACA,EAAO,SACP,EAAO,aACP,CACF,GACA,KAAK,MAAM,QACJ,MAGT,KAAK,MAAM,UACJ;EACT,SAAS,GAAO;GAMd,OALA,QAAQ,MACN,wCAAwC,EAAW,GAAG,EAAO,IAC7D,CACF,GACA,KAAK,MAAM,UACJ;EACT;CACF;CAEA,MAAM,eACJ,GACA,GACA,GACA,GACA,GACe;EACV,SAAK,OAAO,SAIjB,IAAI;GACF,IAAI;GAOJ,AANI,GAAmB,UACrB,IAAc,KAAK,OAAO,cAAc,QAC/B,GAAmB,QAAQ,QAAQ,EAAkB,OAAO,MACrE,IAAc,KAAK,OAAO,WAAW,MAGvC,KAAK,YAAY,eACf,GACA,GACA,GACA,GACA,GACA,CACF;GAEA,IAAM,IAAS,KAAK,YAAY,eAAe,GAAY,GAAQ,CAAiB;GACpF,AAAI,KACF,MAAM,KAAK,UAAU,eAAe,GAAY,GAAQ,GAAQ,CAAiB;EAErF,SAAS,GAAO;GAKd,AAJA,QAAQ,MACN,wCAAwC,EAAW,GAAG,EAAO,IAC7D,CACF,GACA,KAAK,MAAM;EACb;CACF;CAEA,MAAM,aAAa,GAAmC;EAEpD,AADA,KAAK,YAAY,kBAAkB,CAAU,GAC7C,MAAM,KAAK,UAAU,qBAAqB,CAAU;CACtD;CAEA,MAAM,WAA0B;EAE9B,AADA,KAAK,YAAY,SAAS,GAC1B,MAAM,KAAK,UAAU,SAAS;CAChC;CAEA,aAAa,GAAoC;EAE/C,AADA,KAAK,SAAS;GAAE,GAAG,KAAK;GAAQ,GAAG;EAAO,GAC1C,KAAK,YAAY,aAAa,KAAK,MAAM;CAC3C;CAEA,WAA8E;EAC5E,OAAO;GACL,GAAG,KAAK;GACR,aAAa,KAAK,YAAY,SAAS;EACzC;CACF;CAEA,MAAM,iBAAiB,GAAsD;EAC3E,IAAI,CAAC,KAAK,OAAO,WAAW,CAAC,KAAK,OAAO,eAAe,SACtD,OAAO;EAGT,IAAI;GACF,IAAI,IAAS,KAAK,YAAY,iBAAiB,CAAa;GAc5D,OAbI,KACF,KAAK,MAAM,QACJ,MAGT,IAAS,MAAM,KAAK,UAAU,iBAAiB,CAAa,GACxD,KACF,KAAK,YAAY,iBAAiB,GAAe,CAAM,GACvD,KAAK,MAAM,QACJ,MAGT,KAAK,MAAM,UACJ;EACT,SAAS,GAAO;GAGd,OAFA,QAAQ,MAAM,0CAA0C,EAAc,IAAI,CAAK,GAC/E,KAAK,MAAM,UACJ;EACT;CACF;CAEA,MAAM,iBAAiB,GAAuB,GAAwC;EAChF,OAAC,KAAK,OAAO,WAAW,CAAC,KAAK,OAAO,eAAe,UAIxD,IAAI;GAEF,AADA,KAAK,YAAY,iBAAiB,GAAe,CAAQ,GACzD,MAAM,KAAK,UAAU,iBAAiB,GAAe,CAAQ;EAC/D,SAAS,GAAO;GAEd,AADA,QAAQ,MAAM,0CAA0C,EAAc,IAAI,CAAK,GAC/E,KAAK,MAAM;EACb;CACF;CAEA,aAAmB;EACjB,KAAK,QAAQ;GACX,MAAM;GACN,QAAQ;GACR,WAAW;GACX,QAAQ;EACV;CACF;AACF;;;ACxKA,SAAgB,IAA+B;CAC7C,IAAM,IAAM,SAAS,QAAQ,IAAI,aAAa,QAAQ,EAAE,GAClD,IAAW,QAAQ,IAAI,aAAa,WAGpC,IACJ,QAAQ,IAAI,kBAAkB,WAC9B,QAAQ,IAAI,kBAAkB,OAC9B,QAAQ,IAAI,kBAAkB,QAC9B,QAAQ,IAAI,kBAAkB,OAE1B,IACJ,QAAQ,IAAI,kBAAkB,UAC9B,QAAQ,IAAI,kBAAkB,OAC9B,QAAQ,IAAI,kBAAkB,OAC1B,IAAA,QAAA,IAAA,aAAiC,eAEnC;CACJ,AAOE,IAPE,IACQ,KACD,IACC,KACL,CAAI;CAMX,IAAM,IAAoB,QAAQ,IAAI,6BAA6B,QAC7D,IAAkB,SACtB,QAAQ,IAAI,0BAA0B,OACtC,EACF;CAGA,AAAK,KAMH,QAAQ,IAAI,0BALG,IACX,wBACA,IACE,yBACA,sBACuC,EAAE;CAajD,IAAM,IAAqC;EACzC,SAV2B,QAAQ,IAAI,2BAA2B;EAWlE,KAVuB,SAAS,QAAQ,IAAI,sBAAsB,UAAU,EAUvE;EACL,UAV4B,QAAQ,IAAI,0BACtC,QAAQ,IAAI,wBAAwB,MAAM,GAAG,EAAE,KAAI,MAAK,EAAE,KAAK,CAAC,IAChE,EACE,EAA0B,KAAK,EAAkB,QAAQ,CAC3D;EAOF,SAN2B,SAAS,QAAQ,IAAI,0BAA0B,QAAQ,EAMzE;CACX,GAEM,IAAU,SAAS,QAAQ,IAAI,kBAAkB,OAAO,EAAE,GAC1D,IAAa,SAAS,QAAQ,IAAI,qBAAqB,SAAS,EAAE;CAExE,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF;;;ACzEA,IAAa,KAAb,MAAiC;CAC/B;CAEA,YAAY,GAAmC;EAC7C,KAAK,SAAS;CAChB;CAKA,MAAM,YAAY,GAA+C;EAC/D,IAAM,IAAW,EAAgC;EACjD,CAAI,CAAC,KAAa,EAAS,eAAe,mBAAmB,EAAS,eAAe,iBACnF,MAAM,EAA0B,EAAE,YAAY,CAE9C,CAAC;EAGH,IAAM,IAAe,IACjB,EAA6B,GAAU,EAAyC,CAAC,IACjF,KAAK,OAAO,UAAU,SACpB,KAAK,OAAO,WACZ,EAAqC,EAAkB,QAAQ,CAAC,GAEhE,IAAW,GAAU,mBAAmB;EAG9C,KAAK,IAAM,KAAW,GACpB,IAAI;GAEF,IAAM,IAAM,GAAG,EAAS,KADX,EAAQ,KAAK,EAAE,QAAQ,OAAO,EACd,EAAK,GAAG,KAC/B,IAAW,MAAM,KAAK,iBAAiB,CAAG;GAChD,IAAI,EAAS,SACX,OAAO;EAEX,SAAS,GAAO;GAEd,QAAQ,KAAK,gCAAgC,EAAQ,mBAAmB,EAAc,IAAI,CAAK;GAC/F;EACF;EAIF,OAAO;GACL,SAAS;GACT,KAAK,GAAG,EAAS,KAAK,EAAa,IAAI,KAAK,KAAK,EAAkB,QAAQ,EAAE,GAAG;EAClF;CACF;CAKA,MAAM,iBAAiB,GAAqC;EAC1D,IAAI;GAEF,IAAM,IAAe,MAAM,KAAK,iBAAiB,GAAK,EAAE,QAAQ,OAAO,CAAC;GAExE,IAAI,CAAC,EAAa,IAChB,OAAO;IAAE,SAAS;IAAO;GAAI;GAG/B,IAAM,IAAc,EAAa,QAAQ,IAAI,cAAc,KAAK,IAC1D,IAAgB,EAAa,QAAQ,IAAI,gBAAgB;GAG/D,IAAI,CAAC,KAAK,mBAAmB,CAAW,GACtC,OAAO;IACL,SAAS;IACT;IACA,UAAU,KAAe,KAAA;IACzB,MAAM,IAAgB,SAAS,GAAe,EAAE,IAAI,KAAA;GACtD;GAIF,IAAM,IAAgB,MAAM,KAAK,iBAAiB,GAAK,EACrD,SAAS,EACP,OAAS,eACX,EACF,CAAC;GAED,IAAI,CAAC,EAAc,IAEjB,OAAO,MAAM,KAAK,gCAAgC,GAAK,GAAa,CAAa;GAGnF,IAAM,IAAS,OAAO,KAAK,MAAM,EAAc,YAAY,CAAC,GACtD,IAAa,KAAK,uBAAuB,GAAQ,CAAW,GAC5D,IAAS,KAAK,eAAe,GAAa,CAAM;GAEtD,OAAO;IACL,SAAS;IACT;IACA,UAAU;IACV,OAAO,EAAW;IAClB,QAAQ,EAAW;IACnB,MAAM,IAAgB,SAAS,GAAe,EAAE,IAAI,KAAA;IACpD;GACF;EACF,SAAS,GAAO;GAEd,OADA,QAAQ,KAAK,sCAAsC,EAAI,IAAI,CAAK,GACzD;IAAE,SAAS;IAAO;GAAI;EAC/B;CACF;CAKA,MAAc,gCACZ,GACA,GACA,GACwB;EACxB,IAAI;GACF,IAAM,IAAW,MAAM,KAAK,iBAAiB,CAAG;GAChD,IAAI,CAAC,EAAS,IACZ,OAAO;IAAE,SAAS;IAAO;GAAI;GAG/B,IAAM,IAAS,OAAO,KAAK,MAAM,EAAS,YAAY,CAAC,GACjD,IAAa,KAAK,uBAAuB,GAAQ,CAAW,GAC5D,IAAS,KAAK,eAAe,GAAa,CAAM;GAEtD,OAAO;IACL,SAAS;IACT;IACA,UAAU;IACV,OAAO,EAAW;IAClB,QAAQ,EAAW;IACnB,MAAM,EAAO;IACb;GACF;EACF,SAAS,GAAO;GAEd,OADA,QAAQ,KAAK,6BAA6B,EAAI,IAAI,CAAK,GAChD;IAAE,SAAS;IAAO;GAAI;EAC/B;CACF;CAKA,mBAA2B,GAA8B;EAIvD,OAHK,KAEe,EAAY,YAAY,EAAE,MAAM,GAAG,EAAE,MAAM,IAAI,KAC5D,EAAW,WAAW,QAAQ,IAHZ;CAI3B;CAKA,uBACE,GACA,GACmC;EACnC,IAAI;GACF,IAAM,IAAa,GAAO,CAAM;GAChC,IAAI,EAAW,SAAS,EAAW,QACjC,OAAO;IACL,OAAO,EAAW;IAClB,QAAQ,EAAW;GACrB;EAEJ,SAAS,GAAO;GAEd,QAAQ,KAAK,iCAAiC,CAAK;EACrD;EAGA,OAAO;GAAE,OAAO;GAAG,QAAQ;EAAE;CAC/B;CAKA,eAAuB,GAAqB,GAAoC;EAE9E,IAAI,GAAa;GACf,IAAM,IAAY,EAAY,YAAY,EAAE,MAAM,gBAAgB;GAClE,IAAI,KAAa,EAAU,IAAI;IAC7B,IAAM,IAAS,EAAU,GAAG,YAAY;IAOxC,OALI,MAAW,SAAe,SAC1B,MAAW,QAAc,QACzB,MAAW,QAAc,QACzB,MAAW,SAAe,SAC1B,MAAW,YAAkB,QAC1B;GACT;EACF;EAGA,IAAI;GACF,IAAM,IAAa,GAAO,CAAM;GAChC,IAAI,EAAW,MACb,OAAO,EAAW,KAAK,YAAY;EAEvC,QAAQ,CAER;CAGF;CAKA,MAAc,iBACZ,GACA,IAAuB,CAAC,GACL;EACnB,IAAM,IAAa,IAAI,gBAAgB,GACjC,IAAY,iBAAiB,EAAW,MAAM,GAAG,KAAK,OAAO,OAAO;EAE1E,IAAI;GACF,IAAM,IAAW,MAAM,MAAM,GAAK;IAChC,GAAG;IACH,QAAQ,EAAW;GACrB,CAAC;GAED,OADA,aAAa,CAAS,GACf;EACT,SAAS,GAAO;GAKd,MAJA,aAAa,CAAS,GAClB,aAAiB,SAAS,EAAM,SAAS,eACjC,MAAM,yBAAyB,KAAK,OAAO,QAAQ,GAAG,IAE5D;EACR;CACF;AACF;;;AC5OA,SAAS,EAAoB,GAA+B,GAAiC;CAC3F,KAAK,IAAM,KAAK,GAAM;EACpB,IAAM,IAAI,EAAK;EACf,IAAI,OAAO,KAAM,YAAY,EAAE,KAAK,MAAM,IACxC,OAAO;CAEX;CACA,OAAO;AACT;AAGA,IAAa,KAAmB;CAAC;CAAW;CAAW;CAAe;CAAe;CAAQ;AAAM,GAKtF,KAAuB;CAAC;CAAQ;CAAQ;CAAQ;CAAQ;CAAW;AAAS,GAEnF,KAAwB;CAAC;CAAW;CAAW;CAAe;CAAe;CAAQ;AAAM;AASjG,SAAS,GAA0B,GAAsB,GAA8B;CACrF,IAAM,IAAI,EAA0B,EAAE,KAAK,IAAI,EAA0B,EAAE,KAAK;CAChF,IAAI,MAAM,GAAG,OAAO;CACpB,IAAM,IAAI,EAAE,IAAI,cAAc,EAAE,GAAG;CAEnC,OADI,MAAM,IACH,EAAE,QAAQ,EAAE,QADC;AAEtB;AAEA,SAAS,GAAyB,GAAuC;CACvE,IAAM,IAAc,EAA0B,CAAI,GAC5C,IAAa,GAAgC,CAAI,GACnD,IAAiC;CAErC,IAAI,GACF,KAAK,IAAM,CAAC,GAAK,MAAU,OAAO,QAAQ,CAAW,GAAG;EACtD,IAAI,CAAC,EAA4B,CAAK,GAAG;EACzC,IAAM,IAAI,EAAK;EACf,IAAI,OAAO,KAAM,YAAY,EAAE,KAAK,MAAM,IAAI;EAC9C,IAAM,IAA0B;GAAE;GAAO;GAAK,OAAO;GAAG,OAAO;EAAE;EACjE,CAAI,CAAC,KAAQ,GAA0B,GAAM,CAAI,IAAI,OAAG,IAAO;CACjE;CAGF,IAAI,GACF,KAAK,IAAM,CAAC,GAAS,MAAW,OAAO,QAAQ,CAAU,GAAG;EAC1D,IAAM,IAAM,EAAK;EACjB,IAAI,CAAC,MAAM,QAAQ,CAAG,GAAG;EACzB,IAAM,IAAI,KAAK,IAAI,EAAO,QAAQ,EAAI,MAAM;EAC5C,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;GAC1B,IAAM,IAAQ,EAAO;GACrB,IAAI,CAAC,EAA4B,CAAK,GAAG;GACzC,IAAM,IAAK,EAAI;GACf,IAAI,OAAO,KAAO,YAAY,EAAG,KAAK,MAAM,IAAI;GAChD,IAAM,IAA0B;IAAE;IAAO,KAAK;IAAS,OAAO;IAAG,OAAO;GAAG;GAC3E,CAAI,CAAC,KAAQ,GAA0B,GAAM,CAAI,IAAI,OAAG,IAAO;EACjE;CACF;CAGF,OAAO,GAAM,SAAS;AACxB;AAEA,SAAgB,GAAwB,GAAuC;CAC7E,OAAO,EAAoB,GAAM,EAAgB;AACnD;AAEA,SAAgB,GAAoB,GAAuC;CACzE,IAAM,IAAQ,GAAyB,CAAI;CAC3C,IAAI,MAAU,IAAI,OAAO;CACzB,IAAM,IAAU,EAAoB,GAAM,EAAoB;CAE9D,OADI,MAAY,KACT,EAAoB,GAAM,EAAqB,IAD3B;AAE7B;AAEA,SAAS,EAAgC,GAAyB;CAEhE,OADI,OAAO,KAAU,WACd,EAAM,SAAS,aAAa,KAAK,EAAM,SAAS,UAAU,IAD3B;AAExC;AAEA,SAAS,GAA2C,GAAwC;CAC1F,IAAM,IAAc,EAA0B,CAAI;CAClD,IAAI,GACF;OAAK,IAAM,CAAC,GAAK,MAAU,OAAO,QAAQ,CAAW,GAC9C,MAA4B,CAAK,KAClC,EAAgC,EAAK,EAAI,GAAG,OAAO;CACzD;CAEF,IAAM,IAAa,GAAgC,CAAI;CACvD,IAAI,GACF,KAAK,IAAM,CAAC,GAAS,MAAW,OAAO,QAAQ,CAAU,GAAG;EAC1D,IAAM,IAAM,EAAK;EACjB,IAAI,CAAC,MAAM,QAAQ,CAAG,GAAG;EACzB,IAAM,IAAI,KAAK,IAAI,EAAO,QAAQ,EAAI,MAAM;EAC5C,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAChB,MAA4B,EAAO,EAAG,KACvC,EAAgC,EAAI,EAAE,GAAG,OAAO;CAExD;CAEF,OAAO;AACT;AAMA,SAAgB,EAAqC,GAAwC;CAC3F,IAAI,GAA2C,CAAI,GAAG,OAAO;CAC7D,KAAK,IAAM,KAAK,IACd,IAAI,EAAgC,EAAK,EAAE,GAAG,OAAO;CAEvD,KAAK,IAAM,KAAK,IACd,IAAI,EAAgC,EAAK,EAAE,GAAG,OAAO;CAEvD,OAAO;AACT;AAEA,SAAgB,GACd,GACA,IAAwD,cACnD;CAEL,OADI,MAA0B,kBAAwB,CAAC,GAAG,CAAK,IACxD,EAAM,QAAQ,MAAS,CAAC,EAAqC,CAAI,CAAC;AAC3E;;;AC5DA,SAAS,GAAsC,GAAoC;CACjF,OAAO,EAA+B,CAAK,GAAG;AAChD;AAWA,SAAS,EAAqB,GAAoC;CAChE,IAAI,OAAO,KAAU,YAAY,OAAO,SAAS,CAAK,KAAK,IAAQ,GAAG,OAAO;CAC7E,IAAI,OAAO,KAAU,UAAU;EAC7B,IAAM,IAAS,OAAO,CAAK;EAC3B,IAAI,OAAO,SAAS,CAAM,KAAK,IAAS,GAAG,OAAO;CACpD;AAEF;AAEA,SAAS,GACP,GAC+B;CAC/B,IAAI,MAAM,QAAQ,CAAK,GAAG;EACxB,KAAK,IAAM,KAAS,GAAO;GACzB,IAAM,IAAY,GAA+C,CAAK;GACtE,IAAI,GAAW,OAAO;EACxB;EACA;CACF;CAEA,IAAM,IAAM,GAAsC,CAAK;CAClD,OAEL;MAAI,KAAS,OAAO,KAAU,YAAY,CAAC,MAAM,QAAQ,CAAK,GAAG;GAC/D,IAAM,IAAS;GAMf,OAAO;IACL;IACA,UANC,OAAO,EAAO,YAAa,YAAY,EAAO,YAC9C,OAAO,EAAO,QAAS,YAAY,EAAO,QAC1C,OAAO,EAAO,eAAgB,YAAY,EAAO,eAClD,KAAA;IAIA,OAAO,EAAqB,EAAO,KAAK;IACxC,QAAQ,EAAqB,EAAO,MAAM;IAC1C,MAAM,EAAqB,EAAO,QAAQ,EAAO,QAAQ;IACzD,QAAQ,OAAO,EAAO,UAAW,WAAW,EAAO,SAAS,KAAA;GAC9D;EACF;EAEA,OAAO,EAAE,OAAI;CAFb;AAGF;AAEA,SAAS,GAAyB,GAA8D;CAC9F,IAAI,EAAK,aAAa,EAAK,kBAAkB,OAAO,EAAK,kBAAmB,UAAU;EACpF,IAAM,IAAY,EAAK;EACvB,IAAI,EAAU,KACZ,OAAO;GACL,KAAK,EAAU;GACf,UAAU,EAAU;GACpB,OAAO,EAAU;GACjB,QAAQ,EAAU;GAClB,MAAM,EAAU;GAChB,QAAQ,EAAU;EACpB;CAEJ;CAcA,KAAK,IAAM,KAAO;EAXhB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CAGgB,GAAsB;EACtC,IAAI,EAAE,KAAO,IAAO;EACpB,IAAM,IAAY,GAA+C,EAAK,EAAI;EAC1E,IAAI,GAAW,OAAO;CACxB;AAGF;AAEA,SAAS,GACP,GACA,GACA,GACA,GACA,GACA,GACM;CACN,IAAM,IAAY,EAAe,YAAY;CAE7C,EAAQ,aAAa,CACnB;EACE,KAAK,EAAe;EACpB,MAAM;EACN,GAAI,EAAe,OAAO,EAAE,QAAQ,EAAe,KAAK,IAAI,CAAC;CAC/D,CACF;CAEA,IAAM,IAAwC;EAC5C,KAAK,EAAe;EACpB,MAAM;EACN,QAAQ;CACV;CAMA,AAJI,EAAe,UAAO,EAAa,QAAQ,EAAe,QAC1D,EAAe,WAAQ,EAAa,SAAS,EAAe,SAC5D,EAAe,SAAM,EAAa,WAAW,EAAe,OAC5D,EAAe,WAAQ,EAAa,SAAS,EAAe,SAChE,EAAQ,mBAAmB,CAAC,CAAY;CAExC,IAAM,IAAqC,EAAE,KAAK,EAAe,IAAI;CAKrE,AAJI,EAAe,UAAO,EAAU,QAAQ,EAAe,QACvD,EAAe,WAAQ,EAAU,SAAS,EAAe,SAC7D,EAAQ,qBAAqB,CAAC,CAAS,GAEnC,EAAK,UACP,EAAQ,iBAAiB,EAAK;CAGhC,IAAM,IAAmB,EAAK,eAAe,EAAK,WAAW,EAAK,SAAS;CAK3E,IAJI,MACF,EAAQ,uBAAuB,IAG7B,EAAK,OAAO;EACd,IAAM,IAAqB,CAAC;EAK5B,AAJA,EAAS,KAAK,GAAG,OAAO,EAAK,KAAK,EAAE,YAAY,EAAE,MAAM,KAAK,EAAE,QAAQ,MAAM,EAAE,SAAS,CAAC,CAAC,GACtF,EAAK,eACP,EAAS,KAAK,GAAG,OAAO,EAAK,WAAW,EAAE,YAAY,EAAE,MAAM,KAAK,EAAE,QAAQ,MAAM,EAAE,SAAS,CAAC,CAAC,GAE9F,EAAS,SAAS,MACpB,EAAQ,oBAAoB,CAAC,GAAG,IAAI,IAAI,CAAQ,CAAC,EAAE,MAAM,GAAG,EAAE,EAAE,KAAK,IAAI;CAE7E;CAiCA,AA/BI,MAAM,QAAQ,EAAK,OAAO,KAAK,EAAK,QAAQ,SAAS,IACvD,EAAQ,kBAAkB,EAAK,QAAQ,KAAK,OAAiB;EAC3D,OAAO,EAAO,QAAQ,EAAO,eAAe;EAC5C,MAAM;CACR,EAAE,IACO,MACT,EAAQ,kBAAkB,CAAC;EAAE,OAAO,EAAW;EAAM,MAAM;CAAS,CAAC,IAGnE,MACF,EAAQ,qBAAqB,IAG3B,MACF,EAAQ,oBAAoB,CAC1B;EACE,OAAO;EACP,QAAQ;CACV,CACF,IAGF,EAAQ,iBAAiB,CACvB;EACE,iBAAiB,CAAC,CAAY;EAC9B,mBAAmB,CAAC,CAAS;EAC7B,eAAe,EAAK,SAAS;EAC7B,qBAAqB;CACvB,CACF,GAGE,EAAe,OACf,OAAO,EAAK,WAAY,YACxB,EAAK,QAAQ,KAAK,MAAM,MACxB,CAAC,EAAK,QAAQ,SAAS,EAAe,GAAG,MAGzC,EAAQ,qBAAqB,GAAG,aADD,EAAe,IAAI,SAAS,EAAK,SAAS,GAAG,GAAG,EAAe,QAAQ,WAAW,EAAe,MAAM,KAAK,KAAK,EAAe,SAAS,YAAY,EAAe,OAAO,KAAK,GAAG,KACxK,IAAI,EAAK;AAEvD;AAGA,IAAI,IAAoC;AAExC,SAAS,KAAgC;CAKvC,OAJA,AAEE,MAAe,IAAI,GADJ,EACiB,CAAM,GAEjC;AACT;AAKA,SAAgB,KAA0B;CACxC,IAAe;AACjB;AASA,eAAe,GACb,GACA,GACA,GACwB;CAExB,IAAM,IAAiB,EAAM,QAC1B,MAAc,EAAK,wBAAwB,EAAK,sBACnD;CAEA,IAAI,EAAe,WAAW,GAC5B,OAAO;CAGT,QAAQ,IAAI,aAAa,EAAe,OAAO,2BAA2B;CAG1E,IAAM,IAAqB,EAAe,IAAI,OAAO,MAAc;EACjE,IAAM,IAAgB,EAAK,wBAAwB,EAAK;EACxD,IAAI,CAAC,GAAe,OAAO;EAE3B,IAAI;GAEF,IAAI,IAAgB,MAAM,EAAM,iBAAiB,CAAa;GAiB9D,OAdK,MACH,QAAQ,IAAI,mCAAmC,GAAe,GAC9D,IAAgB,MAAM,EAAa,YAAY,CAAa,GAE5D,MAAM,EAAM,iBAAiB,GAAe,CAAa,IAIvD,EAAc,YAEhB,EAAK,iBAAiB,GACtB,EAAK,YAAY,KAGZ;EACT,SAAS,GAAO;GAEd,OADA,QAAQ,KAAK,yCAAyC,EAAc,IAAI,CAAK,GACtE;EACT;CACF,CAAC,GAGK,IAAgB,MAAM,QAAQ,IAAI,CAAkB,GAGpD,oBAAc,IAAI,IAAyB;CASjD,OARA,EAAc,SAAQ,MAAQ;EAC5B,IAAM,IAAS,EAAK,MAAO,EAAa,WAAY,EAAa,WAAW;EAC5E,AAAI,KACF,EAAY,IAAI,GAAQ,CAAI;CAEhC,CAAC,GAGM,EAAM,KAAI,MAAQ;EACvB,IAAM,IAAS,EAAK,MAAO,EAAa,WAAY,EAAa,WAAW;EAE5E,OADiB,EAAY,IAAI,CAC1B,KAAY;CACrB,CAAC;AACH;AAMA,SAAS,GACP,GACA,GACO;CACP,IAAM,EAAE,eAAY,YAAS,gBAAa,gBAAa,2BAAwB,iBAAiB;CAOhG,OALiB,GACf,GACA,CAGK,EAAS,KAAK,MAAc;EAEjC,IAAM,IAAS,EAAK,MAAM,EAAK,WAAW,EAAK,WAAW,EAAK,wBAAwB,EAAK,wBAGtF,IACJ,KAAe,OAEX,GAAG,EAAQ,GAAG,EAAU,CAAU,EAAE,GAAG,MADvC,GAAG,EAAY,QAAQ,OAAO,EAAE,EAAE,IAAI,KAAe,oBAAoB,QAAQ,OAAO,EAAE,EAAE,GAAG,KAE/F,IAAU,EAAK,QAAQ,EAAK,QAAQ,EAAK,cAAc,EAAK,aAAa,GAG3E;EACJ,IAAI,EAAK,WAAW,EAAK,SAAS;GAEhC,IAAM,IAAa,EAAK,WAAW,EAAK;GACxC,IAAO,IAAI,KAAK,CAAU;EAC5B,OAAO,IAAI,EAAK,aAEd,oBAAO,IAAI,KAAK,EAAK,cAAc,GAAI;OAClC,IAAI,EAAK,eAAe,EAAK,aAAa,EAAK,WAAW;GAC/D,IAAM,IAAY,EAAK,eAAe,EAAK,aAAa,EAAK;GAC7D,IAAO,KAAa,OAAO,KAAc,YAAY,EAAU,gBAAgB,OAC3E,IACA,IAAI,KAAK,CAA4B;EAC3C,OACE,oBAAO,IAAI,KAAK;EAGlB,IAAM,IAAc,GAAwB,CAAI,GAC1C,IAAU,GAAoB,CAAI,GAGlC,IAAgB;GACpB,GAAG;GAEH,IAAI;GACJ,OAAO,EAAK,SAAS,EAAK,SAAS;GACnC,MAAM;GACN;GACA;GACA,SAAS;GACH;GAEN,MAAM,EAAK,QAAQ,EAAK,QAAQ,EAAK,QAAQ,EAAK,QAAQ;EAC5D;EAiCA,OA/BI,OAAO,EAAS,QAAS,YAAY,EAAS,SAAS,KAAA,MACzD,EAAS,OAAO,EAAS,OAEvB,OAAO,EAAS,QAAS,YAAY,EAAS,SAAS,KAAA,MACzD,EAAS,OAAO,EAAS,OAEvB,OAAO,EAAS,QAAS,YAAY,EAAS,SAAS,KAAA,MACzD,EAAS,OAAO,EAAS,OAEvB,OAAO,EAAS,QAAS,YAAY,EAAS,SAAS,KAAA,MACzD,EAAS,OAAO,EAAS,OAIvB,EAAK,kBAAkB,EAAK,cAE9B,EAAS,iBADwB,EAAK,gBAEtC,EAAS,YAAY,KAIvB,OAAO,KAAK,CAAQ,EAAE,SAAS,MAAQ;GACrC,IAAM,IAAQ,EAAS;GACvB,IAAI,OAAO,KAAU,YAAY,uCAAuC,KAAK,CAAG,KAAK,MAAQ,aAAa,MAAQ,QAAQ;IACxH,IAAM,IAAY,IAAI,KAAK,CAAK;IAChC,AAAK,MAAM,EAAU,QAAQ,CAAC,MAC5B,EAAS,KAAO;GAEpB;EACF,CAAC,GAEM;CACT,CAAC;AACH;AAkBA,IAAa,KACX,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,MACoB;CACpB,IAAM,IAAa,EAAkB,CAAmB,GAClD,IAAiB,EAAU,CAAU,GAErC,IAAc,GAAG,EAAW,QAAQ,GAAG,EAAe,GAAG,KACzD,IAAU,IAAY,GAAG,EAAY,KAAK,MAAc,GACxD,IAAY,GAAG,EAAW,MAAM,KAAK,GAAW,CAAc,KAC9D,oBAAM,IAAI,KAAK,GAEf,IAAa,EAAe,GAG5B,IAA6D,CAAC;CACpE,IAAI,GAAY;EACd,IAAM,IAAO,EAAW,QAAQ,QAAQ,SAAS,EAAE,GAC7C,IAAM,EAAK,SAAS,GAAG,IAAI,MAAM;EAIvC,AAHI,EAAW,WACb,EAAM,KAAK;GAAE,KAAK;GAAQ,MAAM,GAAG,IAAO,EAAI,OAAO,EAAW,OAAO;EAAI,CAAC,GAE1E,EAAW,OAAO,MACpB,EAAM,KAAK;GAAE,KAAK;GAAY,MAAM,GAAG,IAAO,EAAI,OAAO,EAAW,OAAO;EAAI,CAAC,GAChF,EAAM,KAAK;GAAE,KAAK;GAAS,MAAM,GAAG,IAAO,EAAI;EAAQ,CAAC;CAE5D;CACA,IAAI,GAAc,QAChB,KAAK,IAAM,KAAM,GACf,EAAM,KAAK;EAAE,KAAK,EAAG;EAAK,MAAM,EAAG;CAAK,CAAC;CAK7C,IAAM,IAAmB,GAAqB,GAAO;EACnD;EACA,SAAS,EAAW;EACpB,aAAa,EAAW;EACxB,aAAa,EAAW;EACxB,uBACE,GAAoB,yBAAyB,EAAW;CAC5D,CAAC;CAGD,QAAQ,GAAR;EACE,KAAK,QAAQ;GAEX,IAAM,IAAkE,CACtE;IAAE,MAAM;IAAS,KAAK;GAAO,GAC7B,EAAE,MAAM,EAAW,QAAQ,CAC7B;GACA,KAAK,IAAM,KAAQ,GACjB,EAAU,KAAK;IACb,MAAM,EAAK;IACX,KAAK,EAAK;IACV,MAAM,EAAK;GACb,CAAC;GAEH,IAAM,IAAW;IACf,IAAI;IACJ,OAAO;IACP,SAAS;IACT,OAAO;IACP,UAAU,EAAW;IACrB,QAAQ,EAAW;IACnB,QAAQ,EAAW,SAAS;KAC1B,MAAM,EAAW,OAAO;KACxB,OAAO,EAAW,OAAO;KACzB,KAAK,EAAW,OAAO;IACzB,IAAI,KAAA;IACJ,SAAS,EAAiB,KAAK,MAAS;KAEtC,IAAM,IAAa;MACjB,IAAI,EAAK,MAAM,EAAK;MACpB,OAAO,EAAK,SAAS;MACrB,SAAS,EAAK,QAAQ,EAAK,WAAW;MACtC,OAAO,EAAK,OAAO,CAAC,EAAE,MAAM,EAAK,KAAK,CAAC,IAAI,CAAC;MAC5C,GAAG;KACL;KAKA,IAJI,EAAK,YAAS,EAAM,UAAU,EAAK,UACnC,EAAK,gBAAa,EAAM,UAAU,EAAK,cAGvC,EAAK,kBAAkB,EAAK,WAAW;MACzC,IAAM,IAA2B,EAAK;MAUtC,IATA,AAAkB,EAAM,UAAQ,CAAC,GACjC,EAAM,MAAM,KAAK;OACf,MAAM,EAAU;OAChB,KAAK;OACL,MAAM,EAAU,YAAY;OAC5B,QAAQ,EAAU;MACpB,CAAC,GAGG,EAAU,OAAO,EAAM,WAAW,CAAC,EAAM,QAAQ,SAAS,EAAU,GAAG,GAAG;OAC5E,IAAM,IAAY,aAAa,EAAU,IAAI,SAAS,EAAK,SAAS,GAAG,GAAG,EAAU,QAAQ,WAAW,EAAU,MAAM,KAAK,KAAK,EAAU,SAAS,YAAY,EAAU,OAAO,KAAK,GAAG;OACzL,EAAM,UAAU,OAAO,EAAM,WAAY,WACrC,GAAG,EAAU,IAAI,EAAM,YACvB;QAAE,MAAM;QAAQ,OAAO,GAAG,EAAU,IAAI,EAAM,QAAQ,SAAS,EAAM;OAAU;MACrF;KACF;KAEA,OAAO;IACT,CAAC;GACH;GACA,OAAO,QAAQ,QAAQ,GAAiB,CAAQ,CAAW;EAC7D;EACA,KAAK,QAAQ;GAEX,IAAM,IAAU,GAAY,UACxB,GAAG,EAAW,QAAQ,QAAQ,SAAS,EAAE,EAAE,QAAQ,EAAW,OAAO,MACrE,KAAA,GA+CE,IAAa,GAAiB;IA7ClC,OAAO;IACP,eAAe,EAAW;IAC1B,UAAU;IACV,GAAI,KAAW,EAAE,UAAU,EAAQ;IACnC,aAAa,EAAW;IACxB,QAAQ,EAAW,SAAS;KAC1B,MAAM,EAAW,OAAO;KACxB,KAAK,EAAW,OAAO;IACzB,IAAI,KAAA;IACJ,OAAO,EAAiB,KAAK,MAAS;KAEpC,IAAM,IAAgB;MACpB,IAAI,EAAK,MAAM,EAAK;MACpB,GAAG;KACL;KAQA,IAPI,EAAK,UAAO,EAAS,QAAQ,EAAK,QAClC,EAAK,SAAM,EAAS,MAAM,EAAK,OAC/B,EAAK,YAAS,EAAS,eAAe,EAAK,UAC3C,EAAK,gBAAa,EAAS,UAAU,EAAK,eAC1C,EAAK,QAAQ,EAAK,aAAS,EAAS,iBAAiB,EAAK,QAAQ,EAAK,UAGvE,EAAK,kBAAkB,EAAK,WAAW;MACzC,IAAM,IAA2B,EAAK;MAatC,AAZA,EAAS,QAAQ,EAAU,KAG3B,AAA2B,EAAS,gBAAc,CAAC,GACnD,EAAS,YAAY,KAAK;OACxB,KAAK,EAAU;OACf,WAAW,EAAU,YAAY;OACjC,eAAe,EAAU;OACzB,OAAO,EAAK,SAAS;MACvB,CAAC,GAGG,EAAU,OAAO,EAAS,gBAAgB,CAAC,EAAS,aAAa,SAAS,EAAU,GAAG,MAEzF,EAAS,eAAe,GAAG,aADI,EAAU,IAAI,SAAS,EAAK,SAAS,GAAG,GAAG,EAAU,QAAQ,WAAW,EAAU,MAAM,KAAK,KAAK,EAAU,SAAS,YAAY,EAAU,OAAO,KAAK,GAAG,KACpJ,IAAI,EAAS;KAEtD;KAEA,OAAO;IACT,CAAC;GAEiC,CAAQ;GAC5C,OAAO,QAAQ,QAAQ,OAAO,KAAe,WAAW,IAAa,KAAK,UAAU,CAAU,CAAC;EACjG;EAEA,SAAS;GAEP,IAAM,IAAU;IACd,OAAO;IACP,MAAM,EAAW;IACjB,aAAa,EAAW;IACxB,OAAO,EAAM,SAAS,EAAM,KAAK,OAAO;KAAE,KAAK,EAAE;KAAK,MAAM,EAAE;KAAM,MAAM,EAAE;IAAK,EAAE,IAAI,KAAA;IACvF,WAAW,KAAa;IACxB,UAAU,EAAW;IACrB,WAAW,EAAW;IACtB,WAAW,EAAW,QAAQ;IAC9B,SAAS;IACT,eAAe;IACf,OAAO,EAAiB,KAAK,MAAc;KAEzC,IAAM,IAAe,EACnB,GAAG,EACL;KASA,AANI,EAAK,UAAO,EAAQ,QAAQ,EAAK,QACjC,EAAK,SAAM,EAAQ,OAAO,EAAK,OAC/B,EAAK,gBAAa,EAAQ,cAAc,EAAK,eAC7C,EAAK,QAAQ,EAAK,aAAS,EAAQ,UAAU,EAAK,QAAQ,EAAK,UAG/D,EAAK,OACP,EAAQ,OAAO;MACb,OAAO,EAAK;MACZ,aAAa,OAAO,EAAK,QAAS,aAAa,EAAK,KAAK,WAAW,SAAS,KAAK,EAAK,KAAK,WAAW,UAAU;KACnH,IACS,EAAK,OACd,EAAQ,OAAO;MACb,OAAO,EAAK;MACZ,aAAa,OAAO,EAAK,MAAO,aAAa,EAAK,GAAG,WAAW,SAAS,KAAK,EAAK,GAAG,WAAW,UAAU;KAC7G;KAGF,IAAM,IAAiB,GAAyB,CAAI;KA2BpD,IA1BI,KACF,GACE,GACA,GACA,GACA,GACA,EAAW,QACX,EAAW,SACb,GAIA,OAAO,EAAK,WAAY,YACxB,EAAK,QAAQ,KAAK,MAAM,MACxB,EAAQ,uBAAuB,KAAA,MAE/B,EAAQ,qBAAqB,EAAK,UAIpC,EAAQ,KAAK,CAAC,IAGV,EAAK,QAAQ,EAAK,aACpB,EAAQ,GAAG,OAAO,EAAK,QAAQ,EAAK,UAElC,EAAK,aAAa;MAEpB,IAAM,oBAAkB,IAAI,KAAK,EAAK,cAAc,GAAI;MAExD,AADK,EAAQ,GAAG,UAAO,EAAQ,GAAG,QAAQ,CAAC,IAC3C,EAAQ,GAAG,MAAM,KAAK,CAAe;KACvC;KAwBA,QArBI,EAAK,WAAW,EAAK,aAClB,EAAQ,GAAG,eAAY,EAAQ,GAAG,aAAa,CAAC,IACrD,EAAQ,GAAG,WAAW,KAAK,EAAK,WAAW,EAAK,OAAO,KAErD,EAAK,wBAAwB,EAAK,4BAC/B,EAAQ,GAAG,eAAY,EAAQ,GAAG,aAAa,CAAC,IACrD,EAAQ,GAAG,WAAW,KAAK,EAAK,wBAAwB,EAAK,sBAAsB,KAIjF,EAAK,cAAc,EAAK,eAC1B,EAAQ,GAAG,SAAS,EAAK,cAAc,EAAK,aAI1C,EAAK,WAAW,EAAK,aAAS,EAAQ,UAAU,EAAK,WAAW,EAAK,WACrE,EAAK,wBAAwB,EAAK,4BACpC,EAAQ,uBAAuB,EAAK,wBAAwB,EAAK,yBAE/D,EAAK,gBAAa,EAAQ,cAAc,EAAK,cAE1C;IACT,CAAC;GACH;GACA,OAAO,QAAQ,QAAQ,GAAe,CAAO,CAAC;EAChD;CACF;AACF;AAMA,SAAS,GAAe,GAA4B;CAClD,QAAQ,GAAR;EACE,KAAK,QACH,OAAO;EACT,KAAK,QACH,OAAO;EAET,SACE,OAAO;CACX;AACF;AAEA,SAAS,GAAY,GAAoC;CACvD,IAAM,IAAa,EAAQ,YAAY;CAIvC,OAHI;EAAC;EAAO;EAAQ;CAAM,EAAE,SAAS,CAAU,IACtC,IAEF;AACT;AAEA,SAAS,GAAW,GAAqB;CACvC,OAAO,EAAI,OAAO,CAAC,EAAE,YAAY,IAAI,EAAI,MAAM,CAAC;AAClD;AAYA,eAAsB,GACpB,GACA,GACA,GACA,GACA,GACA,GACmB;CAEnB,IAAM,IAAS,GAAY,CAAa;CACxC,IAAI,CAAC,GACH,OAAO,IAAI,SACT,KAAK,UAAU,EAAE,OAAO,wBAAwB,IAAgB,CAAC,GACjE;EACE,QAAQ;EACR,SAAS,EAAE,gBAAgB,mBAAmB;CAChD,CACF;CAIF,IAAM,IAAa,EAAU,SAAS,EAAkB,YAAY,CAAC,GAC/D,IAAiB,EAAU,CAAU,GACrC,IAAa,EAAkB,CAAmB,GAElD,IAAa,EAAe,GAC5B,IAAU,KAAK,IAAI,GAAG,KAAQ,CAAC,GAC/B,IAAW,EAAW,UACtB,KAAQ,IAAU,KAAK,GAEvB,IACJ,IAAU,IAAI,EAAE,MAAM,EAAQ,IAAI,KAAA;CAGpC,AADA,QAAQ,IAAI,gBAAgB,GAAY,GACxC,QAAQ,IAAI,oBAAoB,GAAgB;CAEhD,IAAM,IAAQ,GAAgB,GACxB,IAAS,EAAgB;CAE/B,IAAI;EAEF,IAAI,EAAO,SAAS;GAClB,IAAM,IAAgB,MAAM,EAAM,eAAe,GAAY,GAAQ,CAAiB;GAEtF,IAAI,GAgBF,OAdI,KAAe,GAAiB,GAAa,EAAc,IAAI,KACjE,QAAQ,IAAI,iCAAiC,EAAW,GAAG,EAAO,iBAAiB,GAC5E,IAAI,SAAS,MAAM;IACxB,QAAQ;IACR,SAAS;KACP,MAAQ,EAAc;KACtB,kCAAiB,IAAI,KAAK,EAAc,eAAe,GAAI,GAAE,YAAY;KACzE,iBAAiB;IACnB;GACF,CAAC,MAIH,QAAQ,IAAI,iBAAiB,EAAW,GAAG,GAAQ,GAC5C,IAAI,SAAS,EAAc,SAAS;IACzC,QAAQ;IACR,SAAS;KACP,gBAAgB,EAAc;KAC9B,MAAQ,EAAc;KACtB,kCAAiB,IAAI,KAAK,EAAc,eAAe,GAAI,GAAE,YAAY;KACzE,iBAAiB;KACjB,iBAAiB;KACjB,iBAAiB;KACjB,WAAW;IACb;GACF,CAAC;EAEL;EAGA,QAAQ,IAAI,kBAAkB,EAAW,GAAG,EAAO,kBAAkB;EAErE,IAAM,IAAY,OAAO,YAAoC;GAC3D,IAAM,IAAQ,MAAM,GAAyB,GAAY;IACvD,OAAO;IACP;GACF,CAAC;GASD,OARA,QAAQ,IAAI,SAAS,EAAM,OAAO,yBAAyB,GAAY,GACnE,EAAO,eAAe,UAKjB,GAAyB,GAAO,IAJd,GAAoB;IAC3C,UAAU,EAAO,cAAc;IAC/B,SAAS,EAAO,cAAc;GAChC,CACuC,GAAc,CAAK,IAErD;EACT,GAAG,GAEG,IAAU,GAAG,EAAW,QAAQ,GAAG,EAAe,GAAG,KACrD,IAAU,EAAU,WAAW,GAC/B,IAAyC,CAAC;EAChD,IAAI,MAAY,GAAG;GACjB,IAAM,oBAAM,IAAI,KAAK,GACf,IAAY,IAAI,KAAK,EAAI,YAAY,GAAG,EAAI,SAAS,IAAI,GAAG,CAAC;GACnE,EAAoB,KAAK;IACvB,KAAK;IACL,MAAM,GAAG,EAAW,QAAQ,GAAG,EAAe,WAAW,EAAU,YAAY,EAAE,GAAG,EAAU,SAAS,IAAI,EAAE,GAAG;GAClH,CAAC;EACH;EASA,IAAM,IAAc,MAAM,EACxB,GACA,GACA,GACA,GACA;GAXA,MAAM;GACN;GACA;GACA;EAQA,GACA,EAAoB,SAAS,IAAsB,KAAA,GACnD,IACA,KAAA,GACA,CACF,GACM,IAAc,GAAe,CAAM;EAGzC,IAAI,EAAO,SAAS;GAClB,MAAM,EAAM,eAAe,GAAY,GAAQ,GAAa,GAAa,CAAiB;GAG1F,IAAM,IAAgB,MAAM,EAAM,eAAe,GAAY,GAAQ,CAAiB,GAChF,IAAO,GAAe,QAAQ,IAC9B,IAAe,GAAe,gBAAgB,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;GAEhF,OAAO,IAAI,SAAS,GAAa;IAC/B,QAAQ;IACR,SAAS;KACP,gBAAgB;KAChB,MAAQ;KACR,kCAAiB,IAAI,KAAK,IAAe,GAAI,GAAE,YAAY;KAC3D,iBAAiB;KACjB,iBAAiB;KACjB,iBAAiB;KACjB,WAAW;IACb;GACF,CAAC;EACH;EAGA,OAAO,IAAI,SAAS,GAAa;GAC/B,QAAQ;GACR,SAAS;IACP,gBAAgB;IAChB,iBAAiB;IACjB,iBAAiB;IACjB,iBAAiB;GACnB;EACF,CAAC;CACH,SAAS,GAAO;EAId,IAHA,QAAQ,MAAM,0BAA0B,CAAK,GAGzC,EAAO,SAAS;GAClB,IAAM,IAAe,MAAM,EAAM,eAAe,GAAY,GAAQ,CAAiB;GACrF,IAAI,GAEF,OADA,QAAQ,IAAI,kCAAkC,GACvC,IAAI,SAAS,EAAa,SAAS;IACxC,QAAQ;IACR,SAAS;KACP,gBAAgB,EAAa;KAC7B,MAAQ,EAAa;KACrB,iBAAiB;KACjB,iBAAiB;KACjB,iBAAiB;KACjB,WAAW;KACX,SAAW;IACb;GACF,CAAC;EAEL;EAEA,OAAO,IAAI,SACT,KAAK,UAAU;GACb,OAAO;GACP,SAAS,aAAiB,QAAQ,EAAM,UAAU;EACpD,CAAC,GACD;GACE,QAAQ;GACR,SAAS,EAAE,gBAAgB,mBAAmB;EAChD,CACF;CACF;AACF;AASA,eAAsB,GACpB,GACA,GACA,GACA,GACA,GACA,GACA,GACmB;CACnB,IAAI,IAAO,QAAQ,IAAO,MACxB,OAAO,IAAI,SACT,KAAK,UAAU,EAAE,OAAO,iBAAiB,IAAO,CAAC,GACjD;EAAE,QAAQ;EAAK,SAAS,EAAE,gBAAgB,mBAAmB;CAAE,CACjE;CAEF,IAAI,IAAQ,KAAK,IAAQ,IACvB,OAAO,IAAI,SACT,KAAK,UAAU,EAAE,OAAO,kBAAkB,IAAQ,CAAC,GACnD;EAAE,QAAQ;EAAK,SAAS,EAAE,gBAAgB,mBAAmB;CAAE,CACjE;CAGF,IAAM,IAAS,GAAY,CAAa;CACxC,IAAI,CAAC,GACH,OAAO,IAAI,SACT,KAAK,UAAU,EAAE,OAAO,wBAAwB,IAAgB,CAAC,GACjE;EAAE,QAAQ;EAAK,SAAS,EAAE,gBAAgB,mBAAmB;CAAE,CACjE;CAGF,IAAM,IAAa,EAAU,SAAS,EAAkB,YAAY,CAAC,GAC/D,IAAiB,EAAU,CAAU,GACrC,IAAa,EAAkB,CAAmB,GAElD,IAAoB,EAAE,SAAS;EAAE;EAAM;CAAM,EAAE,GAC/C,IAAQ,GAAgB,GACxB,IAAS,EAAgB;CAE/B,IAAI;EACF,IAAI,EAAO,SAAS;GAClB,IAAM,IAAgB,MAAM,EAAM,eAAe,GAAY,GAAQ,CAAiB;GACtF,IAAI,GAWF,OAVI,KAAe,GAAiB,GAAa,EAAc,IAAI,IAC1D,IAAI,SAAS,MAAM;IACxB,QAAQ;IACR,SAAS;KACP,MAAM,EAAc;KACpB,kCAAiB,IAAI,KAAK,EAAc,eAAe,GAAI,GAAE,YAAY;KACzE,iBAAiB;IACnB;GACF,CAAC,IAEI,IAAI,SAAS,EAAc,SAAS;IACzC,QAAQ;IACR,SAAS;KACP,gBAAgB,EAAc;KAC9B,MAAM,EAAc;KACpB,kCAAiB,IAAI,KAAK,EAAc,eAAe,GAAI,GAAE,YAAY;KACzE,iBAAiB;KACjB,iBAAiB;KACjB,iBAAiB;KACjB,WAAW;IACb;GACF,CAAC;EAEL;EAEA,IAAM,IAAQ,MAAM,GAAiC,GAAY,GAAM,CAAK,GAEtE,IAAU,GAAG,EAAW,QAAQ,GAAG,EAAe,GAAG,KACrD,IAAc,GAAG,EAAW,QAAQ,GAAG,EAAe,WAEtD,IAAkC,CACtC;GAAE,KAAK;GAAgB,MAAM,GAAG,EAAY,GAAG,MAAU,IAAI,IAAO,IAAI,EAAK,GAAG,MAAU,IAAI,KAAK,IAAQ,EAAE,GAAG;EAAS,GACzH;GAAE,KAAK;GAAW,MAAM;EAAQ,CAClC,GAEM,IAAW,MAAU,KAAK,IAAO,IAAI,GACrC,IAAY,MAAU,KAAK,IAAI,IAAQ,GACvC,oBAAM,IAAI,KAAK;EACrB,CAAI,IAAW,EAAI,YAAY,KAAM,MAAa,EAAI,YAAY,KAAK,KAAa,EAAI,SAAS,IAAI,MACnG,EAAa,KAAK;GAAE,KAAK;GAAgB,MAAM,GAAG,EAAY,GAAG,EAAS,GAAG,EAAU,GAAG;EAAS,CAAC;EAGtG,IAAM,IAAc,MAAM,EACxB,GACA,GACA,GACA,GACA,KAAA,GACA,GACA,IACA,KAAA,GACA,CACF,GAEM,IAAc,GAAe,CAAM;EAEzC,IAAI,EAAO,SAAS;GAClB,MAAM,EAAM,eAAe,GAAY,GAAQ,GAAa,GAAa,CAAiB;GAC1F,IAAM,IAAgB,MAAM,EAAM,eAAe,GAAY,GAAQ,CAAiB,GAChF,IAAO,GAAe,QAAQ,IAC9B,IAAe,GAAe,gBAAgB,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;GAEhF,OAAO,IAAI,SAAS,GAAa;IAC/B,QAAQ;IACR,SAAS;KACP,gBAAgB;KAChB,MAAM;KACN,kCAAiB,IAAI,KAAK,IAAe,GAAI,GAAE,YAAY;KAC3D,iBAAiB;KACjB,iBAAiB;KACjB,iBAAiB;KACjB,WAAW;IACb;GACF,CAAC;EACH;EAEA,OAAO,IAAI,SAAS,GAAa;GAC/B,QAAQ;GACR,SAAS;IACP,gBAAgB;IAChB,iBAAiB;IACjB,iBAAiB;IACjB,iBAAiB;GACnB;EACF,CAAC;CACH,SAAS,GAAO;EAEd,OADA,QAAQ,MAAM,kCAAkC,CAAK,GAC9C,IAAI,SACT,KAAK,UAAU;GACb,OAAO;GACP,SAAS,aAAiB,QAAQ,EAAM,UAAU;EACpD,CAAC,GACD;GAAE,QAAQ;GAAK,SAAS,EAAE,gBAAgB,mBAAmB;EAAE,CACjE;CACF;AACF"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/bootstrap.ts","../src/config.ts","../src/getFeedItems.ts","../src/consume/parseRss.ts","../src/rss/generateRssXml.ts","../src/cache/types.ts","../src/utils/etag.ts","../src/cache/MemoryCache.ts","../src/cache/FileCache.ts","../src/cache/CacheManager.ts","../src/cache/config.ts","../src/services/arweaveImageService.ts","../src/pickFeedItemRichText.ts","../src/index.ts"],"sourcesContent":["import { DEFAULT_ARWEAVE_HOST, resolveArweaveHostFromEnv } from '@seedprotocol/arweave'\n\nlet initialized = false\n\nexport type InitializeFeedPlatformOptions = {\n arweaveDomain?: string\n}\n\nfunction resolveFeedArweaveDomain(explicit?: string): string {\n return explicit ?? resolveArweaveHostFromEnv() ?? DEFAULT_ARWEAVE_HOST\n}\n\n/** Register Node EAS + Arweave clients for feed generation (no full SDK client). */\nexport async function initializeFeedPlatform(\n options?: InitializeFeedPlatformOptions,\n): Promise<void> {\n if (initialized) return\n const [{ registerNodeEasPlatform }, { registerNodeArweavePlatform }] = await Promise.all([\n import('@seedprotocol/eas/node'),\n import('@seedprotocol/arweave/node'),\n ])\n registerNodeEasPlatform()\n const arweaveDomain = resolveFeedArweaveDomain(options?.arweaveDomain)\n registerNodeArweavePlatform({ arweaveDomain })\n if (options?.arweaveDomain) {\n const { BaseArweaveClient } = await import('@seedprotocol/arweave')\n BaseArweaveClient.setHost(options.arweaveDomain)\n }\n initialized = true\n}\n\n/** @deprecated Use initializeFeedPlatform */\nexport const initializeSeedClient = initializeFeedPlatform\n\nexport async function getClient(): Promise<{ isInitialized: () => boolean }> {\n await initializeFeedPlatform()\n return { isInitialized: () => initialized }\n}\n\nexport async function teardownSeedClient(): Promise<void> {\n initialized = false\n}\n\nexport { DEFAULT_ARWEAVE_HOST } from '@seedprotocol/arweave'\n","import type { FeedConfig } from './types';\n\n/**\n * Partial site/feed metadata overrides. Unspecified fields keep current/default values.\n * `author` fields are merged individually when provided.\n */\nexport type SiteConfigOverrides = Partial<Omit<FeedConfig, 'author'>> & {\n author?: Partial<NonNullable<FeedConfig['author']>>;\n};\n\n/** Hardcoded Seed Protocol defaults for feed channel metadata. */\nexport const DEFAULT_SITE_CONFIG: FeedConfig = {\n title: 'Seed Protocol',\n description: 'Content published via Seed Protocol',\n siteUrl: 'https://seedprotocol.io',\n feedUrl: 'https://feed.seedprotocol.io',\n language: 'en',\n copyright: `© ${new Date().getFullYear()} All rights reserved`,\n author: {\n name: 'Seed Protocol',\n email: 'info@seedprotocol.io',\n link: 'https://seedprotocol.io',\n },\n};\n\nfunction cloneSiteConfig(config: FeedConfig): FeedConfig {\n return {\n ...config,\n author: config.author ? { ...config.author } : undefined,\n };\n}\n\nlet siteConfig: FeedConfig = cloneSiteConfig(DEFAULT_SITE_CONFIG);\n\n/**\n * Merge overrides onto a base site config. Missing fields keep the base values.\n */\nexport function resolveSiteConfig(\n overrides?: SiteConfigOverrides,\n base: FeedConfig = siteConfig,\n): FeedConfig {\n if (!overrides) {\n return cloneSiteConfig(base);\n }\n\n const resolved: FeedConfig = {\n ...base,\n ...overrides,\n author: base.author ? { ...base.author } : undefined,\n };\n\n if (overrides.author !== undefined) {\n resolved.author = {\n ...(resolved.author ?? { name: '' }),\n ...overrides.author,\n name: overrides.author.name ?? resolved.author?.name ?? '',\n };\n }\n\n return resolved;\n}\n\n/** Current site/feed channel config (defaults until `setSiteConfig` is called). */\nexport function getSiteConfig(): FeedConfig {\n return cloneSiteConfig(siteConfig);\n}\n\n/**\n * Override site/feed channel metadata for this process.\n * Partial values are merged onto the Seed Protocol defaults (not the previous override set).\n */\nexport function setSiteConfig(overrides: SiteConfigOverrides): FeedConfig {\n siteConfig = resolveSiteConfig(overrides, DEFAULT_SITE_CONFIG);\n return getSiteConfig();\n}\n\n/** Restore hardcoded Seed Protocol site config defaults. */\nexport function resetSiteConfig(): void {\n siteConfig = cloneSiteConfig(DEFAULT_SITE_CONFIG);\n}\n\n/**\n * Load feed configuration from environment variables\n *\n * Environment variables:\n * - FEED_ITEM_URL_BASE: Base URL for attestation links (e.g. https://optimism-sepolia.easscan.org or https://easscan.org).\n * When set, item links use {base}/attestation/view/{uid}. Default: 'https://easscan.org'. Set to override.\n * - FEED_ITEM_URL_PATH: Path segment for attestation links (default: 'attestation/view').\n * Only used when FEED_ITEM_URL_BASE is set.\n * - FEED_SITE_URL: Site URL for fallback when FEED_ITEM_URL_BASE is unset (default: 'https://seedprotocol.io').\n * - FEED_EXPAND_RELATIONS: When 'false', relation properties stay as UIDs. Default: true (expand to nested objects).\n * - FEED_PAGE_SIZE: Items per page for paged feeds (default: 25).\n * - FEED_INCLUDE_DATA_URI_HTML_ITEMS: When 'true', include items whose body/html contain embedded data-URI images.\n * Default: false (omit such items to avoid huge RSS payloads).\n */\nexport function loadFeedConfig(): {\n itemUrlBase: string;\n itemUrlPath: string;\n siteUrl: string;\n expandRelations: boolean;\n pageSize: number;\n richTextDataUriImages: 'omit_items' | 'include_items';\n} {\n const itemUrlBase = process.env.FEED_ITEM_URL_BASE?.trim() || 'https://optimism-sepolia.easscan.org';\n const itemUrlPath =\n process.env.FEED_ITEM_URL_PATH?.trim() || 'attestation/view';\n const siteUrl =\n process.env.FEED_SITE_URL?.trim() || 'https://seedprotocol.io';\n const expandRelations = process.env.FEED_EXPAND_RELATIONS?.toLowerCase() !== 'false';\n const pageSize = parseInt(process.env.FEED_PAGE_SIZE || '25', 10);\n const richTextDataUriImages =\n process.env.FEED_INCLUDE_DATA_URI_HTML_ITEMS?.toLowerCase() === 'true'\n ? 'include_items'\n : 'omit_items';\n\n return { itemUrlBase, itemUrlPath, siteUrl, expandRelations, pageSize, richTextDataUriImages };\n}\n","import {\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 siteUrl: 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\nconst getCollectionPath = (schemaName: string): string => {\n return schemaName === 'image'\n ? 'images'\n : schemaName === 'post'\n ? 'posts'\n : schemaName.toLowerCase() + 's'\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, siteUrl } = 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 collectionPath = getCollectionPath(schemaName)\n const validSeedUid =\n seedUid && typeof seedUid === 'string' && seedUid.trim() !== '' ? seedUid : 'unknown'\n\n let defaultLink: string\n if (storageTransactionId && storageTransactionId !== seedUid && storageTransactionId.length > 0) {\n try {\n defaultLink = getArweaveUrlForTransaction(storageTransactionId)\n } catch (error) {\n console.error('[feed] [setFeedItemDefaults] Error generating Arweave URL:', error)\n if (itemUrlBase != null) {\n defaultLink = `${itemUrlBase.replace(/\\/$/, '')}/${(itemUrlPath ?? 'attestation/view').replace(/^\\//, '')}/${validSeedUid}`\n } else {\n defaultLink = `${siteUrl.replace(/\\/$/, '')}/${collectionPath}/${validSeedUid}`\n }\n }\n } else {\n if (itemUrlBase != null) {\n defaultLink = `${itemUrlBase.replace(/\\/$/, '')}/${(itemUrlPath ?? 'attestation/view').replace(/^\\//, '')}/${validSeedUid}`\n } else {\n defaultLink = `${siteUrl.replace(/\\/$/, '')}/${collectionPath}/${validSeedUid}`\n }\n }\n\n const currentLink = (item.link || item.Link) as string | undefined\n if (\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\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 */\nfunction applyFeedDefaultsDeep(\n item: Record<string, unknown>,\n schemaName: string,\n options: SetFeedItemDefaultsOptions,\n): void {\n const seedUid = (item.seedUid || item.SeedUid || '') as string\n setFeedItemDefaults(item, seedUid, schemaName, options)\n\n for (const key of Object.keys(item)) {\n if (key.startsWith('_')) continue\n const value = item[key]\n if (Array.isArray(value)) {\n for (const el of value) {\n if (looksLikeSeedClone(el)) {\n const nestedUid = (el.seedUid || el.SeedUid || '') as string\n const nestedSchema =\n typeof el.schemaName === 'string' ? el.schemaName : schemaName\n if (nestedSchema === 'image') {\n setImageRelationCloneForFeed(el, nestedUid)\n } else {\n setFeedItemDefaults(el, nestedUid, nestedSchema, options)\n }\n }\n }\n } else if (looksLikeSeedClone(value)) {\n const nestedUid = (value.seedUid || value.SeedUid || '') as string\n const nestedSchema =\n typeof value.schemaName === 'string' ? value.schemaName : schemaName\n if (nestedSchema === 'image') {\n setImageRelationCloneForFeed(value, nestedUid)\n } else {\n setFeedItemDefaults(value, nestedUid, nestedSchema, options)\n }\n }\n }\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 siteUrl: feedConfig.siteUrl,\n }\n\n return records.map((record) => {\n const item = record.data\n applyFeedDefaultsDeep(item, schemaName, defaultsOptions)\n return item\n })\n}\n\nexport const getFeedItemsBySchemaName = async (\n schemaName: string,\n options?: { limit?: number; skip?: number },\n): Promise<Record<string, unknown>[]> => {\n await initializeFeedPlatform()\n const feedConfig = loadFeedConfig()\n const limit = options?.limit ?? 100\n const skip = options?.skip ?? 0\n\n const { items } = await queryBySchema(schemaName, {\n limit,\n skip,\n expandRelations: feedConfig.expandRelations !== false,\n hydrateStorage: true,\n })\n return seedRecordsToFeedItems(schemaName, items)\n}\n\nexport const getFeedItemsBySchemaNameForMonth = async (\n schemaName: string,\n year: number,\n month: number,\n): Promise<Record<string, unknown>[]> => {\n await initializeFeedPlatform()\n const feedConfig = loadFeedConfig()\n\n const records = await queryBySchemaForMonth(schemaName, year, month, {\n expandRelations: feedConfig.expandRelations !== false,\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 { GraphQLItem, 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\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 backgroundRefresh: boolean; // Enable background refresh job (unused)\n refreshInterval: number; // Background refresh interval in seconds\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\n/**\n * Cache statistics for monitoring\n */\nexport interface CacheStats {\n hits: number;\n misses: number;\n refreshes: number;\n errors: number;\n}\n\n// Re-export for callers that imported GraphQLItem from cache types historically\nexport type { GraphQLItem };\n","import { createHash } from 'crypto';\n\n/**\n * Generate an ETag from a string value\n * Uses MD5 hash for consistency and performance\n */\nexport function generateETag(value: string): string {\n const hash = createHash('md5').update(value).digest('hex');\n return `\"${hash.substring(0, 16)}\"`;\n}\n\n/**\n * Generate an ETag for cached feed content\n */\nexport function generateContentETag(\n schemaName: string,\n format: string,\n lastModified: number,\n contentLength: number\n): string {\n const etagValue = `${schemaName}-${format}-${lastModified}-${contentLength}`;\n return generateETag(etagValue);\n}\n\n/**\n * Check if an ETag matches the provided value\n * Handles both quoted and unquoted ETags\n */\nexport function etagMatches(etag1: string, etag2: string): boolean {\n // Remove quotes if present\n const normalize = (etag: string) => etag.replace(/^\"|\"$/g, '');\n return normalize(etag1) === normalize(etag2);\n}\n\n/**\n * Parse If-None-Match header value\n * Returns array of ETags (can be comma-separated)\n */\nexport function parseIfNoneMatch(headerValue: string | null | undefined): string[] {\n if (!headerValue) {\n return [];\n }\n return headerValue\n .split(',')\n .map(etag => etag.trim())\n .filter(etag => etag.length > 0);\n}\n\n/**\n * Check if any of the provided ETags match the current ETag\n */\nexport function checkIfNoneMatch(\n ifNoneMatchHeader: string | null | undefined,\n currentETag: string\n): boolean {\n const etags = parseIfNoneMatch(ifNoneMatchHeader);\n if (etags.length === 0) {\n return false;\n }\n \n // Check for wildcard\n if (etags.includes('*')) {\n return true;\n }\n \n // Check if any ETag matches\n return etags.some(etag => etagMatches(etag, currentETag));\n}\n","import type { CachedFeedContent, CacheConfig, CacheContentKeyOptions, CachedImageMetadata } from './types';\nimport { buildContentKey } from './types';\nimport type { ImageMetadata } from '../types';\nimport { generateContentETag } from '../utils/etag';\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 setFeedContent(\n schemaName: string,\n format: string,\n content: string,\n contentType: string,\n contentKeyOptions?: CacheContentKeyOptions,\n ttlOverride?: number\n ): void {\n const key = buildContentKey(schemaName, format, contentKeyOptions);\n const now = Math.floor(Date.now() / 1000);\n const ttl = ttlOverride ?? this.config.ttl;\n const expiresAt = now + ttl;\n\n const etag = generateContentETag(schemaName, format, now, content.length);\n\n const cachedContent: CachedFeedContent = {\n content,\n contentType,\n etag,\n lastModified: now,\n expiresAt,\n };\n\n this.feedContentCache.set(key, cachedContent);\n }\n\n clearContentCache(schemaName: string): void {\n const keysToDelete: string[] = [];\n for (const key of this.feedContentCache.keys()) {\n if (key.startsWith(`${schemaName}:`)) {\n keysToDelete.push(key);\n }\n }\n keysToDelete.forEach(key => this.feedContentCache.delete(key));\n }\n\n 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 clearAll(): void {\n this.feedContentCache.clear();\n this.imageMetadataCache.clear();\n }\n\n updateConfig(config: Partial<CacheConfig>): void {\n this.config = { ...this.config, ...config };\n }\n\n getStats(): {\n feedContentCount: number;\n imageMetadataCount: number;\n } {\n return {\n feedContentCount: this.feedContentCache.size,\n imageMetadataCount: this.imageMetadataCache.size,\n };\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 clearAllContentCache(schemaName: string): Promise<void> {\n try {\n const files = await fs.readdir(this.cacheDir);\n const prefix = `${schemaName}-`;\n const suffix = '.json';\n\n for (const file of files) {\n // Content keys are schema-format[-page|archive]; skip bare schema.json\n // (owned by @seedprotocol/query collection cache).\n if (file.startsWith(prefix) && file.endsWith(suffix) && file !== `${schemaName}.json`) {\n await fs.unlink(join(this.cacheDir, file));\n }\n }\n } catch (error) {\n console.error(`Error clearing all content cache for ${schemaName}:`, error);\n }\n }\n\n 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 async clearAll(): Promise<void> {\n try {\n const files = await fs.readdir(this.cacheDir);\n for (const file of files) {\n // Do not delete bare {schema}.json — query collection cache files\n if (file.endsWith('.json') && file.includes('-')) {\n await fs.unlink(join(this.cacheDir, file));\n }\n }\n const imageMetadataDir = join(this.cacheDir, 'image-metadata');\n try {\n const imageFiles = await fs.readdir(imageMetadataDir);\n for (const file of imageFiles) {\n if (file.endsWith('.json')) {\n await fs.unlink(join(imageMetadataDir, file));\n }\n }\n } catch {\n // Directory might not exist\n }\n } catch (error) {\n console.error('Error clearing all caches:', error);\n }\n }\n}\n","import { MemoryCache } from './MemoryCache';\nimport { FileCache } from './FileCache';\nimport type {\n CachedFeedContent,\n CacheConfig,\n CacheContentKeyOptions,\n CacheStats,\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 private stats: CacheStats = {\n hits: 0,\n misses: 0,\n refreshes: 0,\n errors: 0,\n };\n\n constructor(config: CacheConfig) {\n this.config = config;\n this.memoryCache = new MemoryCache(config);\n this.fileCache = new FileCache(config);\n }\n\n async getFeedContent(\n schemaName: string,\n format: FeedFormat,\n contentKeyOptions?: CacheContentKeyOptions\n ): Promise<CachedFeedContent | null> {\n if (!this.config.enabled) {\n return null;\n }\n\n try {\n let cached = this.memoryCache.getFeedContent(schemaName, format, contentKeyOptions);\n if (cached) {\n this.stats.hits++;\n return cached;\n }\n\n cached = await this.fileCache.getFeedContent(schemaName, format, contentKeyOptions);\n if (cached) {\n this.memoryCache.setFeedContent(\n schemaName,\n format,\n cached.content,\n cached.contentType,\n contentKeyOptions\n );\n this.stats.hits++;\n return cached;\n }\n\n this.stats.misses++;\n return null;\n } catch (error) {\n console.error(\n `Error getting feed content cache for ${schemaName}:${format}:`,\n error\n );\n this.stats.errors++;\n return null;\n }\n }\n\n async setFeedContent(\n schemaName: string,\n format: FeedFormat,\n content: string,\n contentType: string,\n contentKeyOptions?: CacheContentKeyOptions\n ): Promise<void> {\n if (!this.config.enabled) {\n return;\n }\n\n try {\n let ttlOverride: number | undefined;\n if (contentKeyOptions?.archive) {\n ttlOverride = this.config.archiveTtl ?? 86400;\n } else if (contentKeyOptions?.page != null && contentKeyOptions.page > 1) {\n ttlOverride = this.config.pageTtl ?? 300;\n }\n\n this.memoryCache.setFeedContent(\n schemaName,\n format,\n content,\n contentType,\n contentKeyOptions,\n ttlOverride\n );\n\n const cached = this.memoryCache.getFeedContent(schemaName, format, contentKeyOptions);\n if (cached) {\n await this.fileCache.setFeedContent(schemaName, format, cached, contentKeyOptions);\n }\n } catch (error) {\n console.error(\n `Error setting feed content cache for ${schemaName}:${format}:`,\n error\n );\n this.stats.errors++;\n }\n }\n\n async clearContent(schemaName: string): Promise<void> {\n this.memoryCache.clearContentCache(schemaName);\n await this.fileCache.clearAllContentCache(schemaName);\n }\n\n async clearAll(): Promise<void> {\n this.memoryCache.clearAll();\n await this.fileCache.clearAll();\n }\n\n updateConfig(config: Partial<CacheConfig>): void {\n this.config = { ...this.config, ...config };\n this.memoryCache.updateConfig(this.config);\n }\n\n getStats(): CacheStats & { memoryStats: ReturnType<MemoryCache['getStats']> } {\n return {\n ...this.stats,\n memoryStats: this.memoryCache.getStats(),\n };\n }\n\n async getImageMetadata(transactionId: string): Promise<ImageMetadata | null> {\n if (!this.config.enabled || !this.config.imageMetadata?.enabled) {\n return null;\n }\n\n try {\n let cached = this.memoryCache.getImageMetadata(transactionId);\n if (cached) {\n this.stats.hits++;\n return cached;\n }\n\n cached = await this.fileCache.getImageMetadata(transactionId);\n if (cached) {\n this.memoryCache.setImageMetadata(transactionId, cached);\n this.stats.hits++;\n return cached;\n }\n\n this.stats.misses++;\n return null;\n } catch (error) {\n console.error(`Error getting image metadata cache for ${transactionId}:`, error);\n this.stats.errors++;\n return null;\n }\n }\n\n async setImageMetadata(transactionId: string, metadata: ImageMetadata): Promise<void> {\n if (!this.config.enabled || !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 this.stats.errors++;\n }\n }\n\n resetStats(): void {\n this.stats = {\n hits: 0,\n misses: 0,\n refreshes: 0,\n errors: 0,\n };\n }\n}\n","import {\n BaseArweaveClient,\n getArweaveReadGatewayHostsForPrimary,\n resolveArweaveHostFromEnv,\n} from '@seedprotocol/arweave'\nimport type { CacheConfig, ImageMetadataConfig } from './types';\n\n/**\n * Load cache configuration from environment variables\n *\n * Environment variables:\n * - CACHE_ENABLED: Set to 'false' to disable caching. Set to 'true' to enable (overrides NODE_ENV).\n * In development (NODE_ENV=development), cache is disabled by default unless CACHE_ENABLED='true'.\n * - CACHE_TTL: Cache time-to-live in seconds (default: 3600)\n * - CACHE_DIR: Directory for cache files (default: './cache')\n * - CACHE_BACKGROUND_REFRESH: Enable background refresh (default: 'false')\n * - CACHE_REFRESH_INTERVAL: Background refresh interval in seconds (default: 300)\n */\nexport function loadCacheConfig(): CacheConfig {\n const ttl = parseInt(process.env.CACHE_TTL || '3600', 10);\n const cacheDir = process.env.CACHE_DIR || './cache';\n\n // CACHE_ENABLED explicitly disables: 'false', '0', 'no', 'off'\n const cacheDisabledByEnvVar =\n process.env.CACHE_ENABLED === 'false' ||\n process.env.CACHE_ENABLED === '0' ||\n process.env.CACHE_ENABLED === 'no' ||\n process.env.CACHE_ENABLED === 'off';\n // CACHE_ENABLED explicitly enables (overrides NODE_ENV=development)\n const cacheEnabledByEnvVar =\n process.env.CACHE_ENABLED === 'true' ||\n process.env.CACHE_ENABLED === '1' ||\n process.env.CACHE_ENABLED === 'yes';\n const isDev = process.env.NODE_ENV === 'development';\n\n let enabled: boolean;\n if (cacheDisabledByEnvVar) {\n enabled = false;\n } else if (cacheEnabledByEnvVar) {\n enabled = true;\n } else if (isDev) {\n enabled = false; // Disable cache in dev mode by default\n } else {\n enabled = true;\n }\n\n const backgroundRefresh = process.env.CACHE_BACKGROUND_REFRESH === 'true';\n const refreshInterval = parseInt(\n process.env.CACHE_REFRESH_INTERVAL || '300',\n 10\n );\n\n // Log cache status on startup\n if (!enabled) {\n const reason = cacheDisabledByEnvVar\n ? 'CACHE_ENABLED=false'\n : isDev\n ? 'NODE_ENV=development'\n : 'CACHE_ENABLED=false';\n console.log(`⚠️ Cache is DISABLED (${reason})`);\n }\n\n // Image metadata configuration\n const imageMetadataEnabled = process.env.IMAGE_METADATA_ENABLED !== 'false'; // Default to true\n const imageMetadataTtl = parseInt(process.env.IMAGE_METADATA_TTL || '604800', 10); // 7 days default\n const imageMetadataGateways = process.env.IMAGE_METADATA_GATEWAYS\n ? process.env.IMAGE_METADATA_GATEWAYS.split(',').map(g => g.trim())\n : getArweaveReadGatewayHostsForPrimary(\n resolveArweaveHostFromEnv() ?? BaseArweaveClient.getHost(),\n );\n const imageMetadataTimeout = parseInt(process.env.IMAGE_METADATA_TIMEOUT || '5000', 10);\n\n const imageMetadata: ImageMetadataConfig = {\n enabled: imageMetadataEnabled,\n ttl: imageMetadataTtl,\n gateways: imageMetadataGateways,\n timeout: imageMetadataTimeout,\n };\n\n const pageTtl = parseInt(process.env.CACHE_PAGE_TTL || '300', 10);\n const archiveTtl = parseInt(process.env.CACHE_ARCHIVE_TTL || '86400', 10);\n\n return {\n ttl,\n cacheDir,\n enabled,\n backgroundRefresh,\n refreshInterval,\n imageMetadata,\n pageTtl,\n archiveTtl,\n };\n}\n","import {\n BaseArweaveClient,\n ensureReadGatewaySelected,\n getArweaveReadGatewayHostsForPrimary,\n getDefaultArweaveReadGatewayHostsOrdered,\n getReadGatewayHostsForConfig,\n getResolvedSeedGatewayEndpoints,\n} from '@seedprotocol/arweave'\nimport sizeOf from 'image-size'\nimport type { ImageMetadata } from '../types'\n\nexport interface ArweaveImageServiceConfig {\n gateways: string[]\n timeout: number // milliseconds\n}\n\n/**\n * Service for detecting and extracting metadata from images stored on Arweave\n */\nexport class ArweaveImageService {\n private config: ArweaveImageServiceConfig\n\n constructor(config: ArweaveImageServiceConfig) {\n this.config = config\n }\n\n /**\n * Detect if an Arweave transaction ID links to an image and extract metadata\n */\n async detectImage(transactionId: string): Promise<ImageMetadata> {\n const resolved = getResolvedSeedGatewayEndpoints()\n if (!resolved || (resolved.activePath !== 'hyper-sidecar' && resolved.activePath !== 'http-proxy')) {\n await ensureReadGatewaySelected().catch(() => {\n /* feed may run without browser client init */\n })\n }\n\n const gatewayHosts = resolved\n ? getReadGatewayHostsForConfig(resolved, getDefaultArweaveReadGatewayHostsOrdered())\n : this.config.gateways?.length\n ? this.config.gateways\n : getArweaveReadGatewayHostsForPrimary(BaseArweaveClient.getHost())\n\n const protocol = resolved?.arweaveProtocol ?? 'https'\n\n // Try each gateway until one succeeds\n for (const gateway of gatewayHosts) {\n try {\n const host = gateway.trim().replace(/\\/$/, '')\n const url = `${protocol}://${host}/${transactionId}`\n const metadata = await this.getImageMetadata(url)\n if (metadata.isImage) {\n return metadata\n }\n } catch (error) {\n // Log but continue to next gateway\n console.warn(`Failed to fetch from gateway ${gateway} for transaction ${transactionId}:`, error)\n continue\n }\n }\n\n // If all gateways failed, return non-image result\n return {\n isImage: false,\n url: `${protocol}://${gatewayHosts[0]?.trim() || BaseArweaveClient.getHost()}/${transactionId}`,\n }\n }\n\n /**\n * Get image metadata from a URL\n */\n async getImageMetadata(url: string): Promise<ImageMetadata> {\n try {\n // First, try HEAD request to check Content-Type (more efficient)\n const headResponse = await this.fetchWithTimeout(url, { method: 'HEAD' })\n \n if (!headResponse.ok) {\n return { isImage: false, url }\n }\n\n const contentType = headResponse.headers.get('content-type') || ''\n const contentLength = headResponse.headers.get('content-length')\n \n // Check if it's an image based on Content-Type\n if (!this.isImageContentType(contentType)) {\n return {\n isImage: false,\n url,\n mimeType: contentType || undefined,\n size: contentLength ? parseInt(contentLength, 10) : undefined,\n }\n }\n\n // If it's an image, fetch first few KB to extract dimensions\n const rangeResponse = await this.fetchWithTimeout(url, {\n headers: {\n 'Range': 'bytes=0-8192', // First 8KB should be enough for most image headers\n },\n })\n\n if (!rangeResponse.ok) {\n // If range request fails, try full request (some servers don't support range)\n return await this.getImageMetadataFromFullRequest(url, contentType, contentLength)\n }\n\n const buffer = Buffer.from(await rangeResponse.arrayBuffer())\n const dimensions = this.extractImageDimensions(buffer, contentType)\n const format = this.getImageFormat(contentType, buffer)\n\n return {\n isImage: true,\n url,\n mimeType: contentType,\n width: dimensions.width,\n height: dimensions.height,\n size: contentLength ? parseInt(contentLength, 10) : undefined,\n format,\n }\n } catch (error) {\n console.warn(`Error fetching image metadata from ${url}:`, error)\n return { isImage: false, url }\n }\n }\n\n /**\n * Fetch image metadata from full request (fallback when range requests fail)\n */\n private async getImageMetadataFromFullRequest(\n url: string,\n contentType: string,\n contentLength: string | null\n ): Promise<ImageMetadata> {\n try {\n const response = await this.fetchWithTimeout(url)\n if (!response.ok) {\n return { isImage: false, url }\n }\n\n const buffer = Buffer.from(await response.arrayBuffer())\n const dimensions = this.extractImageDimensions(buffer, contentType)\n const format = this.getImageFormat(contentType, buffer)\n\n return {\n isImage: true,\n url,\n mimeType: contentType,\n width: dimensions.width,\n height: dimensions.height,\n size: buffer.length,\n format,\n }\n } catch (error) {\n console.warn(`Error in full request for ${url}:`, error)\n return { isImage: false, url }\n }\n }\n\n /**\n * Validate if Content-Type indicates an image\n */\n private isImageContentType(contentType: string): boolean {\n if (!contentType) return false\n \n const normalized = (contentType.toLowerCase().split(';')[0] ?? '').trim()\n return normalized.startsWith('image/')\n }\n\n /**\n * Extract image dimensions from buffer\n */\n private extractImageDimensions(\n buffer: Buffer,\n contentType: string\n ): { width: number; height: number } {\n try {\n const dimensions = sizeOf(buffer)\n if (dimensions.width && dimensions.height) {\n return {\n width: dimensions.width,\n height: dimensions.height,\n }\n }\n } catch (error) {\n // image-size might fail for some formats or corrupted data\n console.warn(`Failed to extract dimensions:`, error)\n }\n\n // Return default if extraction failed\n return { width: 0, height: 0 }\n }\n\n /**\n * Get image format from Content-Type or buffer analysis\n */\n private getImageFormat(contentType: string, buffer: Buffer): string | undefined {\n // Try to get format from Content-Type first\n if (contentType) {\n const mimeMatch = contentType.toLowerCase().match(/image\\/([^;]+)/)\n if (mimeMatch && mimeMatch[1]) {\n const format = mimeMatch[1].toLowerCase()\n // Normalize common formats\n if (format === 'jpeg') return 'jpeg'\n if (format === 'png') return 'png'\n if (format === 'gif') return 'gif'\n if (format === 'webp') return 'webp'\n if (format === 'svg+xml') return 'svg'\n return format\n }\n }\n\n // Try to detect from buffer magic bytes\n try {\n const dimensions = sizeOf(buffer)\n if (dimensions.type) {\n return dimensions.type.toLowerCase()\n }\n } catch {\n // Ignore errors\n }\n\n return undefined\n }\n\n /**\n * Fetch with timeout\n */\n private async fetchWithTimeout(\n url: string,\n options: RequestInit = {}\n ): Promise<Response> {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), this.config.timeout)\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n })\n clearTimeout(timeoutId)\n return response\n } catch (error) {\n clearTimeout(timeoutId)\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Request timeout after ${this.config.timeout}ms`)\n }\n throw error\n }\n }\n}\n","/**\n * Resolves RSS/Atom/JSON feed `description` and `content` from dynamic Seed item shapes.\n * Html-type properties often use `body`; `html` / `content` are alternate sources.\n */\n\nimport {\n feedRichBodyModelPriority,\n getFeedFieldStorageModels,\n getFeedListElementStorageModels,\n isFeedRichBodyStorageSchema,\n} from './feedFieldStorageModel'\n\nfunction firstNonEmptyString(item: Record<string, unknown>, keys: readonly string[]): string {\n for (const k of keys) {\n const v = item[k]\n if (typeof v === 'string' && v.trim() !== '') {\n return v\n }\n }\n return ''\n}\n\n/** Short text fields only (excerpt / summary) — not full HTML body keys. */\nexport const DESCRIPTION_KEYS = ['summary', 'Summary', 'description', 'Description', 'text', 'Text'] as const\n\n/**\n * Primary HTML / rich body keys first, then plain fallbacks (same as legacy `content` chain).\n */\nexport const CONTENT_PRIMARY_KEYS = ['html', 'Html', 'body', 'Body', 'content', 'Content'] as const\n\nconst CONTENT_FALLBACK_KEYS = ['summary', 'Summary', 'description', 'Description', 'text', 'Text'] as const\n\ntype RichBodyCandidate = {\n model: string\n key: string\n index: number\n value: string\n}\n\nfunction compareRichBodyCandidates(a: RichBodyCandidate, b: RichBodyCandidate): number {\n const d = feedRichBodyModelPriority(a.model) - feedRichBodyModelPriority(b.model)\n if (d !== 0) return d\n const k = a.key.localeCompare(b.key)\n if (k !== 0) return k\n return a.index - b.index\n}\n\nfunction pickTypedRichBodyContent(item: Record<string, unknown>): string {\n const fieldModels = getFeedFieldStorageModels(item)\n const listModels = getFeedListElementStorageModels(item)\n let best: RichBodyCandidate | null = null\n\n if (fieldModels) {\n for (const [key, model] of Object.entries(fieldModels)) {\n if (!isFeedRichBodyStorageSchema(model)) continue\n const v = item[key]\n if (typeof v !== 'string' || v.trim() === '') continue\n const cand: RichBodyCandidate = { model, key, index: 0, value: v }\n if (!best || compareRichBodyCandidates(cand, best) < 0) best = cand\n }\n }\n\n if (listModels) {\n for (const [listKey, models] of Object.entries(listModels)) {\n const arr = item[listKey]\n if (!Array.isArray(arr)) continue\n const n = Math.min(models.length, arr.length)\n for (let i = 0; i < n; i++) {\n const model = models[i]!\n if (!isFeedRichBodyStorageSchema(model)) continue\n const el = arr[i]\n if (typeof el !== 'string' || el.trim() === '') continue\n const cand: RichBodyCandidate = { model, key: listKey, index: i, value: el }\n if (!best || compareRichBodyCandidates(cand, best) < 0) best = cand\n }\n }\n }\n\n return best?.value ?? ''\n}\n\nexport function pickFeedItemDescription(item: Record<string, unknown>): string {\n return firstNonEmptyString(item, DESCRIPTION_KEYS)\n}\n\nexport function pickFeedItemContent(item: Record<string, unknown>): string {\n const typed = pickTypedRichBodyContent(item)\n if (typed !== '') return typed\n const primary = firstNonEmptyString(item, CONTENT_PRIMARY_KEYS)\n if (primary !== '') return primary\n return firstNonEmptyString(item, CONTENT_FALLBACK_KEYS)\n}\n\nfunction stringFieldContainsDataUriImage(value: unknown): boolean {\n if (typeof value !== 'string') return false\n return value.includes('data:image/') && value.includes(';base64,')\n}\n\nfunction feedItemTypedRichFieldsContainDataUriImage(item: Record<string, unknown>): boolean {\n const fieldModels = getFeedFieldStorageModels(item)\n if (fieldModels) {\n for (const [key, model] of Object.entries(fieldModels)) {\n if (!isFeedRichBodyStorageSchema(model)) continue\n if (stringFieldContainsDataUriImage(item[key])) return true\n }\n }\n const listModels = getFeedListElementStorageModels(item)\n if (listModels) {\n for (const [listKey, models] of Object.entries(listModels)) {\n const arr = item[listKey]\n if (!Array.isArray(arr)) continue\n const n = Math.min(models.length, arr.length)\n for (let i = 0; i < n; i++) {\n if (!isFeedRichBodyStorageSchema(models[i]!)) continue\n if (stringFieldContainsDataUriImage(arr[i])) return true\n }\n }\n }\n return false\n}\n\n/**\n * True when primary body keys or excerpt-style keys contain an embedded `data:image/...;base64,...` URI.\n * Used by feed generation to omit oversized RSS entries by default.\n */\nexport function feedItemRichTextContainsDataUriImage(item: Record<string, unknown>): boolean {\n if (feedItemTypedRichFieldsContainDataUriImage(item)) return true\n for (const k of CONTENT_PRIMARY_KEYS) {\n if (stringFieldContainsDataUriImage(item[k])) return true\n }\n for (const k of DESCRIPTION_KEYS) {\n if (stringFieldContainsDataUriImage(item[k])) return true\n }\n return false\n}\n\nexport function filterItemsByRichTextDataUriImagePolicy<T extends Record<string, unknown>>(\n items: readonly T[],\n richTextDataUriImages: 'omit_items' | 'include_items' = 'omit_items',\n): T[] {\n if (richTextDataUriImages === 'include_items') return [...items]\n return items.filter((item) => !feedItemRichTextContainsDataUriImage(item))\n}\n","import {\n initializeFeedPlatform,\n initializeSeedClient,\n teardownSeedClient,\n DEFAULT_ARWEAVE_HOST,\n} from './bootstrap';\nimport { getArweaveUrlForTransaction } from './utils/arweaveUrl';\nimport { getFeedItemsBySchemaName, getFeedItemsBySchemaNameForMonth } from './getFeedItems';\n\nexport { getFeedItemsBySchemaName, getFeedItemsBySchemaNameForMonth } from './getFeedItems';\nexport {\n loadFeedConfig,\n DEFAULT_SITE_CONFIG,\n getSiteConfig,\n setSiteConfig,\n resetSiteConfig,\n resolveSiteConfig,\n type SiteConfigOverrides,\n} from './config';\nexport { parseRssString, type ParsedRssChannel } from './consume/parseRss';\nexport type { FeedConfig, FeedFormat, GraphQLItem } from './types';\nexport {\n initializeFeedPlatform,\n initializeSeedClient,\n getClient,\n teardownSeedClient,\n DEFAULT_ARWEAVE_HOST,\n} from './bootstrap';\nexport {\n classifyMediaRef,\n normalizeFeedItemFields,\n getFeedItemStringField,\n 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, GraphQLItem, TransformOptions, FeedConfig, ImageMetadata } from './types';\n\nexport type { TransformOptions } from './types';\nimport { generateAtomFeed, generateJsonFeed } from 'feedsmith';\nimport { generateRssXml } from './rss/generateRssXml';\nimport { CacheManager } from './cache/CacheManager';\nimport { loadCacheConfig } from './cache/config';\nimport { loadFeedConfig, resolveSiteConfig, type SiteConfigOverrides } from './config';\nimport { checkIfNoneMatch } from './utils/etag';\nimport { ArweaveImageService } from './services/arweaveImageService';\nimport { resolveSeedRssImageRelationRef } from '@seedprotocol/arweave';\nimport {\n pickFeedItemContent,\n pickFeedItemDescription,\n filterItemsByRichTextDataUriImagePolicy,\n} from './pickFeedItemRichText';\n\nexport { enrichImageSeedCloneForFeed } from './imageRelationEnrichment';\nexport {\n pickFeedItemContent,\n pickFeedItemDescription,\n feedItemRichTextContainsDataUriImage,\n filterItemsByRichTextDataUriImagePolicy,\n} from './pickFeedItemRichText';\nexport {\n FEED_RICH_BODY_STORAGE_SCHEMAS,\n isFeedRichBodyStorageSchema,\n} from './feedFieldStorageModel';\n\n/** RSS enclosure URL from a plain string (URL or tx id) or nested Image relation object. */\nfunction getEnclosureUrlFromImageRelationField(value: unknown): string | undefined {\n 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/**\n * Enriches feed items with media properties from Arweave storageTransactionId\n */\nasync function enrichFeedItemsWithMedia(\n items: GraphQLItem[],\n imageService: ArweaveImageService,\n cache: CacheManager\n): Promise<GraphQLItem[]> {\n // Filter items that have storageTransactionId\n const itemsToProcess = items.filter(\n (item: any) => item.storageTransactionId || item.storage_transaction_id\n )\n\n if (itemsToProcess.length === 0) {\n return items\n }\n\n console.log(`Enriching ${itemsToProcess.length} items with image metadata`)\n\n // Process all items in parallel\n const enrichmentPromises = itemsToProcess.map(async (item: any) => {\n const transactionId = item.storageTransactionId || item.storage_transaction_id\n if (!transactionId) return item\n\n try {\n // Check cache first\n let imageMetadata = await cache.getImageMetadata(transactionId)\n\n // If not cached, detect image\n if (!imageMetadata) {\n console.log(`Detecting image for transaction ${transactionId}`)\n imageMetadata = await imageService.detectImage(transactionId)\n // Cache the result (even if it's not an image, to avoid retries)\n await cache.setImageMetadata(transactionId, imageMetadata)\n }\n\n // If image detected, add media properties\n if (imageMetadata.isImage) {\n // Add media properties to the item\n item._imageMetadata = imageMetadata\n item._hasImage = true\n }\n\n return item\n } catch (error) {\n console.warn(`Error enriching item with transaction ${transactionId}:`, error)\n return item // Return item unchanged on error\n }\n })\n\n // Wait for all enrichments to complete\n const enrichedItems = await Promise.all(enrichmentPromises)\n\n // Create a map of enriched items by their ID\n const enrichedMap = new Map<string, GraphQLItem>()\n enrichedItems.forEach(item => {\n const itemId = item.id || (item as any).seedUid || (item as any).SeedUid || ''\n if (itemId) {\n enrichedMap.set(itemId, item)\n }\n })\n\n // Merge enriched items back into original array\n return items.map(item => {\n const itemId = item.id || (item as any).seedUid || (item as any).SeedUid || ''\n const enriched = enrichedMap.get(itemId)\n return enriched || item\n })\n}\n\n/**\n * Transforms GraphQL items into feed items.\n * This function preserves all dynamic properties from the items.\n */\nfunction transformToFeedItems(\n items: GraphQLItem[],\n options: TransformOptions\n): any[] {\n const { schemaName, siteUrl, itemUrlBase, itemUrlPath, richTextDataUriImages = 'omit_items' } = options\n\n const filtered = filterItemsByRichTextDataUriImagePolicy(\n items as Record<string, unknown>[],\n richTextDataUriImages,\n ) as GraphQLItem[]\n\n return filtered.map((item: any) => {\n // Determine item ID - try multiple possible fields\n const itemId = item.id || item.seedUid || item.SeedUid || item.storageTransactionId || item.storage_transaction_id\n\n // Determine item URL - prefer link/Link, then import_url/importUrl, fallback to constructed URL\n const fallbackUrl =\n itemUrlBase != null\n ? `${itemUrlBase.replace(/\\/$/, '')}/${(itemUrlPath ?? 'attestation/view').replace(/^\\//, '')}/${itemId}`\n : `${siteUrl}/${pluralize(schemaName)}/${itemId}`\n const itemUrl = item.link || item.Link || item.import_url || item.importUrl || fallbackUrl\n \n // Determine publication date - try multiple sources\n let date: Date\n if (item.pubDate || item.PubDate) {\n // pubDate might be a string like \"Mon, 07 Apr 2025 00:03:29 GMT\"\n const pubDateStr = item.pubDate || item.PubDate\n date = new Date(pubDateStr)\n } else if (item.timeCreated) {\n // timeCreated is a Unix timestamp\n date = new Date(item.timeCreated * 1000)\n } else if (item.publishedAt || item.createdAt || item.updatedAt) {\n const dateValue = item.publishedAt || item.createdAt || item.updatedAt\n date = dateValue && typeof dateValue === 'object' && dateValue.constructor === Date\n ? dateValue as Date\n : new Date(dateValue as string | number)\n } else {\n date = new Date()\n }\n\n const description = pickFeedItemDescription(item)\n const content = pickFeedItemContent(item)\n\n // Start with all properties from the item to preserve dynamic schema\n const feedItem: any = {\n ...item, // Preserve all dynamic properties first\n // Map to standard feed fields\n id: itemId,\n title: item.title || item.Title || 'Untitled',\n link: itemUrl,\n description,\n content,\n pubDate: date,\n date: date,\n // Map guid - use full URL when available for proper permalink\n guid: item.guid || item.Guid || item.link || item.Link || itemUrl,\n }\n\n if (typeof feedItem.html === 'string' && feedItem.Html === undefined) {\n feedItem.Html = feedItem.html\n }\n if (typeof feedItem.Html === 'string' && feedItem.html === undefined) {\n feedItem.html = feedItem.Html\n }\n if (typeof feedItem.body === 'string' && feedItem.Body === undefined) {\n feedItem.Body = feedItem.body\n }\n if (typeof feedItem.Body === 'string' && feedItem.body === undefined) {\n feedItem.body = feedItem.Body\n }\n\n // Add image metadata if available from enrichment\n if (item._imageMetadata && item._hasImage) {\n const imageMeta: ImageMetadata = item._imageMetadata\n feedItem._imageMetadata = imageMeta\n feedItem._hasImage = true\n }\n\n // Convert any date-like string properties to Date objects\n Object.keys(feedItem).forEach((key) => {\n const value = feedItem[key]\n if (typeof value === 'string' && /date|time|published|created|updated/i.test(key) && key !== 'pubDate' && key !== 'date') {\n const dateValue = new Date(value)\n if (!isNaN(dateValue.getTime())) {\n feedItem[key] = dateValue\n }\n }\n })\n\n return feedItem\n })\n}\n\n// ============================================================================\n// Feed Generator\n// ============================================================================\n\nexport interface FeedPaginationOptions {\n page: number\n pageSize: number\n hasNext: boolean\n baseUrl: string\n}\n\nexport interface FeedArchiveLink {\n rel: string\n href: string\n}\n\nexport const createFeed = (\n items: GraphQLItem[],\n schemaName: string,\n format: FeedFormat,\n cacheBust?: string,\n pagination?: FeedPaginationOptions,\n archiveLinks?: FeedArchiveLink[],\n isArchive?: boolean,\n transformOverrides?: Partial<TransformOptions>,\n siteConfigOverrides?: SiteConfigOverrides,\n): Promise<string> => {\n const siteConfig = resolveSiteConfig(siteConfigOverrides)\n const collectionName = pluralize(schemaName)\n // Feed document self-links use feedUrl; channel home/link uses siteUrl\n const feedUrlBase = `${siteConfig.feedUrl}/${collectionName}/${format}`\n const feedUrl = cacheBust ? `${feedUrlBase}?v=${cacheBust}` : feedUrlBase\n const feedTitle = `${siteConfig.title} - ${capitalize(collectionName)}`\n const now = new Date()\n\n const feedConfig = loadFeedConfig()\n\n // Build RFC 5005 links from pagination or archiveLinks\n const links: Array<{ rel: string; href: string; type?: string }> = []\n if (pagination) {\n const base = pagination.baseUrl.replace(/\\?.*$/, '')\n const sep = base.includes('?') ? '&' : '?'\n if (pagination.hasNext) {\n links.push({ rel: 'next', href: `${base}${sep}page=${pagination.page + 1}` })\n }\n if (pagination.page > 1) {\n links.push({ rel: 'previous', href: `${base}${sep}page=${pagination.page - 1}` })\n links.push({ rel: 'first', href: `${base}${sep}page=1` })\n }\n }\n if (archiveLinks?.length) {\n for (const al of archiveLinks) {\n links.push({ rel: al.rel, href: al.href })\n }\n }\n\n // Transform items to preserve all dynamic properties\n const transformedItems = transformToFeedItems(items, {\n schemaName,\n siteUrl: siteConfig.siteUrl,\n itemUrlBase: feedConfig.itemUrlBase,\n itemUrlPath: feedConfig.itemUrlPath,\n richTextDataUriImages:\n transformOverrides?.richTextDataUriImages ?? feedConfig.richTextDataUriImages,\n })\n\n // Generate feed based on format using FeedSmith\n switch (format) {\n case 'atom': {\n // Atom feed requires: id, title, updated, links, entries\n const atomLinks: Array<{ href: string; rel?: string; type?: string }> = [\n { href: feedUrl, rel: 'self' },\n { href: siteConfig.siteUrl },\n ]\n for (const link of links) {\n atomLinks.push({\n href: link.href,\n rel: link.rel,\n type: link.type,\n })\n }\n const atomFeed = {\n id: feedUrl,\n title: feedTitle,\n updated: now,\n links: atomLinks,\n subtitle: siteConfig.description,\n rights: siteConfig.copyright,\n author: siteConfig.author ? {\n name: siteConfig.author.name,\n email: siteConfig.author.email,\n uri: siteConfig.author.link,\n } : undefined,\n entries: transformedItems.map((item) => {\n // Atom entries require: id, title, updated, links\n const entry: any = {\n id: item.id || item.link,\n title: item.title || 'Untitled',\n updated: item.date || item.pubDate || now,\n links: item.link ? [{ href: item.link }] : [],\n ...item, // Preserve all dynamic properties\n }\n if (item.content) entry.content = item.content\n if (item.description) entry.summary = item.description\n \n // Add image as enclosure link if available\n if (item._imageMetadata && item._hasImage) {\n const imageMeta: ImageMetadata = item._imageMetadata\n if (!entry.links) entry.links = []\n entry.links.push({\n href: imageMeta.url,\n rel: 'enclosure',\n type: imageMeta.mimeType || 'image/jpeg',\n length: imageMeta.size,\n })\n \n // Enhance content with image if not already present\n if (imageMeta.url && entry.content && !entry.content.includes(imageMeta.url)) {\n const imageHtml = `<img src=\"${imageMeta.url}\" alt=\"${item.title || ''}\"${imageMeta.width ? ` width=\"${imageMeta.width}\"` : ''}${imageMeta.height ? ` height=\"${imageMeta.height}\"` : ''} />`\n entry.content = typeof entry.content === 'string' \n ? `${imageHtml}\\n${entry.content}`\n : { type: 'html', value: `${imageHtml}\\n${entry.content.value || entry.content}` }\n }\n }\n \n return entry\n }),\n }\n return Promise.resolve(generateAtomFeed(atomFeed) as string)\n }\n case 'json': {\n // JSON feed requires: title, items (with id)\n const nextUrl = pagination?.hasNext\n ? `${pagination.baseUrl.replace(/\\?.*$/, '')}?page=${pagination.page + 1}`\n : undefined\n const jsonFeed = {\n title: feedTitle,\n home_page_url: siteConfig.siteUrl,\n feed_url: feedUrl,\n ...(nextUrl && { next_url: nextUrl }),\n description: siteConfig.description,\n author: siteConfig.author ? {\n name: siteConfig.author.name,\n url: siteConfig.author.link,\n } : undefined,\n items: transformedItems.map((item) => {\n // JSON items require: id\n const jsonItem: any = {\n id: item.id || item.link,\n ...item, // Preserve all dynamic properties\n }\n if (item.title) jsonItem.title = item.title\n if (item.link) jsonItem.url = item.link\n if (item.content) jsonItem.content_html = item.content\n if (item.description) jsonItem.summary = item.description\n if (item.date || item.pubDate) jsonItem.date_published = item.date || item.pubDate\n \n // Add image and attachments if available\n if (item._imageMetadata && item._hasImage) {\n const imageMeta: ImageMetadata = item._imageMetadata\n jsonItem.image = imageMeta.url\n \n // Add as attachment\n if (!jsonItem.attachments) jsonItem.attachments = []\n jsonItem.attachments.push({\n url: imageMeta.url,\n mime_type: imageMeta.mimeType || 'image/jpeg',\n size_in_bytes: imageMeta.size,\n title: item.title || 'Image',\n })\n \n // Enhance content_html with image if not already present\n if (imageMeta.url && jsonItem.content_html && !jsonItem.content_html.includes(imageMeta.url)) {\n const imageHtml = `<img src=\"${imageMeta.url}\" alt=\"${item.title || ''}\"${imageMeta.width ? ` width=\"${imageMeta.width}\"` : ''}${imageMeta.height ? ` height=\"${imageMeta.height}\"` : ''} />`\n jsonItem.content_html = `${imageHtml}\\n${jsonItem.content_html}`\n }\n }\n \n return jsonItem\n }),\n }\n const jsonResult = generateJsonFeed(jsonFeed)\n return Promise.resolve(typeof jsonResult === 'string' ? jsonResult : JSON.stringify(jsonResult))\n }\n case 'rss':\n default: {\n // RSS feed requires: title, link, description, items\n const rssFeed = {\n title: feedTitle,\n link: siteConfig.siteUrl,\n description: siteConfig.description,\n links: links.length ? links.map((l) => ({ rel: l.rel, href: l.href, type: l.type })) : undefined,\n isArchive: isArchive ?? false,\n language: siteConfig.language,\n copyright: siteConfig.copyright,\n webMaster: siteConfig.author?.email,\n pubDate: now,\n lastBuildDate: now,\n items: transformedItems.map((item: any) => {\n // RSS items can have dynamic properties\n const rssItem: any = {\n ...item, // Preserve all dynamic properties\n }\n \n // Ensure required/standard RSS fields\n if (item.title) rssItem.title = item.title\n if (item.link) rssItem.link = item.link\n if (item.description) rssItem.description = item.description\n if (item.date || item.pubDate) rssItem.pubDate = item.date || item.pubDate\n \n // Map guid - RSS requires guid for proper item identification\n if (item.guid) {\n rssItem.guid = {\n value: item.guid,\n isPermaLink: typeof item.guid === 'string' && (item.guid.startsWith('http://') || item.guid.startsWith('https://'))\n }\n } else if (item.id) {\n rssItem.guid = {\n value: item.id,\n isPermaLink: typeof item.id === 'string' && (item.id.startsWith('http://') || item.id.startsWith('https://'))\n }\n }\n \n const imageCandidate = extractRssImageCandidate(item)\n if (imageCandidate) {\n applyRssImageCandidate(\n rssItem,\n item,\n imageCandidate,\n schemaName,\n siteConfig.author,\n siteConfig.copyright\n )\n }\n\n if (\n typeof item.content === 'string' &&\n item.content.trim() !== '' &&\n rssItem['content:encoded'] === undefined\n ) {\n rssItem['content:encoded'] = item.content\n }\n \n // Use Dublin Core namespace for additional metadata\n rssItem.dc = {}\n \n // Add date using dc namespace (supports multiple dates)\n if (item.date || item.pubDate) {\n rssItem.dc.date = item.date || item.pubDate\n }\n if (item.timeCreated) {\n // Convert Unix timestamp to Date for dc namespace\n const timeCreatedDate = new Date(item.timeCreated * 1000)\n if (!rssItem.dc.dates) rssItem.dc.dates = []\n rssItem.dc.dates.push(timeCreatedDate)\n }\n \n // Add identifier using dc namespace\n if (item.seedUid || item.SeedUid) {\n if (!rssItem.dc.identifier) rssItem.dc.identifier = []\n rssItem.dc.identifier.push(item.seedUid || item.SeedUid)\n }\n if (item.storageTransactionId || item.storage_transaction_id) {\n if (!rssItem.dc.identifier) rssItem.dc.identifier = []\n rssItem.dc.identifier.push(item.storageTransactionId || item.storage_transaction_id)\n }\n \n // Add source/relation for import_url\n if (item.import_url || item.importUrl) {\n rssItem.dc.source = item.import_url || item.importUrl\n }\n \n // Include raw custom fields as well (FeedSmith may preserve them)\n if (item.seedUid || item.SeedUid) rssItem.seedUid = item.seedUid || item.SeedUid\n if (item.storageTransactionId || item.storage_transaction_id) {\n rssItem.storageTransactionId = item.storageTransactionId || item.storage_transaction_id\n }\n if (item.timeCreated) rssItem.timeCreated = item.timeCreated\n \n return rssItem\n }),\n }\n return Promise.resolve(generateRssXml(rssFeed))\n }\n }\n}\n\n// ============================================================================\n// Route Handler\n// ============================================================================\n\nfunction getContentType(format: FeedFormat): string {\n switch (format) {\n case 'atom':\n return 'application/atom+xml; charset=utf-8'\n case 'json':\n return 'application/feed+json; charset=utf-8'\n case 'rss':\n default:\n return 'application/rss+xml; charset=utf-8'\n }\n}\n\nfunction parseFormat(segment: string): FeedFormat | null {\n const normalized = segment.toLowerCase()\n if (['rss', 'atom', 'json'].includes(normalized)) {\n return normalized as FeedFormat\n }\n return null\n}\n\nfunction capitalize(str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1)\n}\n\n/**\n * Main route handler for feed generation.\n * \n * URL Pattern: /:collection/:format\n * Examples:\n * - /posts/rss → RSS feed of posts\n * - /posts/atom → Atom feed of posts\n * - /identities/json → JSON feed of identities\n * - /posts/rss?v=1234567890 → RSS feed with cache busting parameter\n */\nexport async function handleFeedRequest(\n collectionSegment: string,\n formatSegment: string,\n ifNoneMatch?: string | null,\n cacheBust?: string,\n page?: number,\n siteConfigOverrides?: SiteConfigOverrides,\n): Promise<Response> {\n // Validate format\n const format = parseFormat(formatSegment)\n if (!format) {\n return new Response(\n JSON.stringify({ error: `Invalid feed format: ${formatSegment}` }),\n {\n status: 400,\n headers: { 'Content-Type': 'application/json' },\n }\n )\n }\n\n // De-pluralize the collection name to get model type\n const schemaName = pluralize.singular(collectionSegment.toLowerCase())\n const collectionName = pluralize(schemaName)\n const siteConfig = resolveSiteConfig(siteConfigOverrides)\n\n const feedConfig = loadFeedConfig()\n const pageNum = Math.max(1, page ?? 1)\n const pageSize = feedConfig.pageSize\n const skip = (pageNum - 1) * pageSize\n\n const contentKeyOptions =\n pageNum > 1 ? { page: pageNum } : undefined\n\n console.log(`Schema name: ${schemaName}`);\n console.log(`Collection name: ${collectionName}`);\n\n const cache = getCacheManager();\n const config = loadCacheConfig();\n\n try {\n // Check if we have cached feed content\n if (config.enabled) {\n const cachedContent = await cache.getFeedContent(schemaName, format, contentKeyOptions);\n\n if (cachedContent) {\n // Check ETag for conditional request\n if (ifNoneMatch && checkIfNoneMatch(ifNoneMatch, cachedContent.etag)) {\n console.log(`Cache hit with ETag match for ${schemaName}:${format} - returning 304`);\n return new Response(null, {\n status: 304,\n headers: {\n 'ETag': cachedContent.etag,\n 'Last-Modified': new Date(cachedContent.lastModified * 1000).toUTCString(),\n 'Cache-Control': 'public, max-age=3600, s-maxage=3600, must-revalidate',\n },\n });\n }\n\n // Return cached content\n console.log(`Cache hit for ${schemaName}:${format}`);\n return new Response(cachedContent.content, {\n status: 200,\n headers: {\n 'Content-Type': cachedContent.contentType,\n 'ETag': cachedContent.etag,\n 'Last-Modified': new Date(cachedContent.lastModified * 1000).toUTCString(),\n 'Cache-Control': 'public, max-age=3600, s-maxage=3600, must-revalidate',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n 'X-Cache': 'HIT',\n },\n });\n }\n }\n\n // Content cache miss or disabled — fetch items via query (owns collection cache)\n console.log(`Cache miss for ${schemaName}:${format} - fetching items`);\n\n const feedItems = await (async (): Promise<GraphQLItem[]> => {\n const items = await getFeedItemsBySchemaName(schemaName, {\n limit: pageSize,\n skip,\n }) as GraphQLItem[];\n console.log(`Found ${items.length} feed items for schema ${schemaName}`);\n if (config.imageMetadata?.enabled) {\n const imageService = new ArweaveImageService({\n gateways: config.imageMetadata.gateways,\n timeout: config.imageMetadata.timeout,\n });\n return enrichFeedItemsWithMedia(items, imageService, cache);\n }\n return items;\n })();\n\n const baseUrl = `${siteConfig.feedUrl}/${collectionName}/${format}`;\n const hasNext = feedItems.length === pageSize;\n const archiveLinksForMain: FeedArchiveLink[] = [];\n if (pageNum === 1) {\n const now = new Date();\n const prevMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);\n archiveLinksForMain.push({\n rel: 'prev-archive',\n href: `${siteConfig.feedUrl}/${collectionName}/archive/${prevMonth.getFullYear()}/${prevMonth.getMonth() + 1}/${format}`,\n });\n }\n\n const paginationOptions: FeedPaginationOptions | undefined = {\n page: pageNum,\n pageSize,\n hasNext,\n baseUrl,\n };\n\n const feedContent = await createFeed(\n feedItems,\n schemaName,\n format,\n cacheBust,\n paginationOptions,\n archiveLinksForMain.length ? archiveLinksForMain : undefined,\n false,\n undefined,\n siteConfigOverrides,\n );\n const contentType = getContentType(format);\n\n // Cache the generated feed content\n if (config.enabled) {\n await cache.setFeedContent(schemaName, format, feedContent, contentType, contentKeyOptions);\n \n // Get the cached content to retrieve ETag\n const cachedContent = await cache.getFeedContent(schemaName, format, contentKeyOptions);\n const etag = cachedContent?.etag || '';\n const lastModified = cachedContent?.lastModified || Math.floor(Date.now() / 1000);\n\n return new Response(feedContent, {\n status: 200,\n headers: {\n 'Content-Type': contentType,\n 'ETag': etag,\n 'Last-Modified': new Date(lastModified * 1000).toUTCString(),\n 'Cache-Control': 'public, max-age=3600, s-maxage=3600, must-revalidate',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n 'X-Cache': 'MISS',\n },\n });\n }\n\n // Cache disabled - return without caching\n return new Response(feedContent, {\n status: 200,\n headers: {\n 'Content-Type': contentType,\n 'Cache-Control': 'public, max-age=3600, s-maxage=3600',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n },\n });\n } catch (error) {\n console.error('Feed generation error:', error);\n \n // Try to serve stale cache on error if available\n if (config.enabled) {\n const staleContent = await cache.getFeedContent(schemaName, format, contentKeyOptions);\n if (staleContent) {\n console.log(`Serving stale cache due to error`);\n return new Response(staleContent.content, {\n status: 200,\n headers: {\n 'Content-Type': staleContent.contentType,\n 'ETag': staleContent.etag,\n 'Cache-Control': 'public, max-age=3600, s-maxage=3600, must-revalidate',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n 'X-Cache': 'STALE',\n 'Warning': '299 - \"Cache is stale\"',\n },\n });\n }\n }\n \n return new Response(\n JSON.stringify({\n error: 'Failed to generate feed',\n message: error instanceof Error ? error.message : 'Unknown error',\n }),\n {\n status: 500,\n headers: { 'Content-Type': 'application/json' },\n }\n )\n }\n}\n\n/**\n * Route handler for monthly archive feeds (RFC 5005).\n *\n * URL Pattern: /:collection/archive/:year/:month/:format\n * Examples:\n * - /posts/archive/2024/02/rss → RSS feed of posts from February 2024\n */\nexport async function handleArchiveFeedRequest(\n collectionSegment: string,\n year: number,\n month: number,\n formatSegment: string,\n ifNoneMatch?: string | null,\n cacheBust?: string,\n siteConfigOverrides?: SiteConfigOverrides,\n): Promise<Response> {\n if (year < 1970 || year > 2100) {\n return new Response(\n JSON.stringify({ error: `Invalid year: ${year}` }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n if (month < 1 || month > 12) {\n return new Response(\n JSON.stringify({ error: `Invalid month: ${month}` }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const format = parseFormat(formatSegment);\n if (!format) {\n return new Response(\n JSON.stringify({ error: `Invalid feed format: ${formatSegment}` }),\n { status: 400, headers: { 'Content-Type': 'application/json' } }\n );\n }\n\n const schemaName = pluralize.singular(collectionSegment.toLowerCase());\n const collectionName = pluralize(schemaName);\n const siteConfig = resolveSiteConfig(siteConfigOverrides);\n\n const contentKeyOptions = { archive: { year, month } };\n const cache = getCacheManager();\n const config = loadCacheConfig();\n\n try {\n if (config.enabled) {\n const cachedContent = await cache.getFeedContent(schemaName, format, contentKeyOptions);\n if (cachedContent) {\n if (ifNoneMatch && checkIfNoneMatch(ifNoneMatch, cachedContent.etag)) {\n return new Response(null, {\n status: 304,\n headers: {\n ETag: cachedContent.etag,\n 'Last-Modified': new Date(cachedContent.lastModified * 1000).toUTCString(),\n 'Cache-Control': 'public, max-age=86400, s-maxage=86400, must-revalidate',\n },\n });\n }\n return new Response(cachedContent.content, {\n status: 200,\n headers: {\n 'Content-Type': cachedContent.contentType,\n ETag: cachedContent.etag,\n 'Last-Modified': new Date(cachedContent.lastModified * 1000).toUTCString(),\n 'Cache-Control': 'public, max-age=86400, s-maxage=86400, must-revalidate',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n 'X-Cache': 'HIT',\n },\n });\n }\n }\n\n const items = await getFeedItemsBySchemaNameForMonth(schemaName, year, month) as GraphQLItem[];\n\n const baseUrl = `${siteConfig.feedUrl}/${collectionName}/${format}`;\n const archiveBase = `${siteConfig.feedUrl}/${collectionName}/archive`;\n\n const archiveLinks: FeedArchiveLink[] = [\n { rel: 'prev-archive', href: `${archiveBase}/${month === 1 ? year - 1 : year}/${month === 1 ? 12 : month - 1}/${format}` },\n { rel: 'current', href: baseUrl },\n ];\n\n const nextYear = month === 12 ? year + 1 : year;\n const nextMonth = month === 12 ? 1 : month + 1;\n const now = new Date();\n if (nextYear < now.getFullYear() || (nextYear === now.getFullYear() && nextMonth <= now.getMonth() + 1)) {\n archiveLinks.push({ rel: 'next-archive', href: `${archiveBase}/${nextYear}/${nextMonth}/${format}` });\n }\n\n const feedContent = await createFeed(\n items,\n schemaName,\n format,\n cacheBust,\n undefined,\n archiveLinks,\n true,\n undefined,\n siteConfigOverrides,\n );\n\n const contentType = getContentType(format);\n\n if (config.enabled) {\n await cache.setFeedContent(schemaName, format, feedContent, contentType, contentKeyOptions);\n const cachedContent = await cache.getFeedContent(schemaName, format, contentKeyOptions);\n const etag = cachedContent?.etag || '';\n const lastModified = cachedContent?.lastModified || Math.floor(Date.now() / 1000);\n\n return new Response(feedContent, {\n status: 200,\n headers: {\n 'Content-Type': contentType,\n ETag: etag,\n 'Last-Modified': new Date(lastModified * 1000).toUTCString(),\n 'Cache-Control': 'public, max-age=86400, s-maxage=86400, must-revalidate',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n 'X-Cache': 'MISS',\n },\n });\n }\n\n return new Response(feedContent, {\n status: 200,\n headers: {\n 'Content-Type': contentType,\n 'Cache-Control': 'public, max-age=86400, s-maxage=86400',\n 'X-Feed-Schema': schemaName,\n 'X-Feed-Format': format,\n },\n });\n } catch (error) {\n console.error('Archive feed generation error:', error);\n return new Response(\n JSON.stringify({\n error: 'Failed to generate archive feed',\n message: error instanceof Error ? error.message : 'Unknown error',\n }),\n { status: 500, headers: { 'Content-Type': 'application/json' } }\n );\n }\n}"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seedprotocol/feed",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Feed generation (RSS, Atom, JSON) for Seed Protocol",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -25,9 +25,9 @@
25
25
  "prepublishOnly": "bun run build && node ../../scripts/sync-versions.js"
26
26
  },
27
27
  "dependencies": {
28
- "@seedprotocol/arweave": "0.5.2",
29
- "@seedprotocol/eas": "0.5.2",
30
- "@seedprotocol/query": "0.5.2",
28
+ "@seedprotocol/arweave": "0.6.0",
29
+ "@seedprotocol/eas": "0.6.0",
30
+ "@seedprotocol/query": "0.6.0",
31
31
  "express": "^5.2.1",
32
32
  "feedsmith": "^2.9.0",
33
33
  "graphql-request": "^7.3.5",