{"version":3,"sources":["../src/changelog/escape.ts","../src/changelog/feed.ts","../src/lib/media-slot-adapter.ts"],"names":["XML_ENTITY","CONTROL_CHARS_RE","XML_ENTITY_RE","CDATA_TERMINATOR_RE","escapeXml","input","c","serializeFeed","entries","opts","e","validateDate","serializeRss","serializeJsonFeed","entryId","d","items","title","desc","cat","ASPECT_RATIOS","isAspectRatio","value","toMediaSlotProps","m"],"mappings":"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","file":"chunk-Y7RMUU23.cjs","sourcesContent":["/**\n * XML entity-escape utilities for the changelog feed serializer.\n *\n * The functions here are NOT exported from the package's public barrel —\n * they are an internal correctness primitive of `serializeFeed`. Consumers\n * should never call `escapeXml` directly.\n */\n\nconst XML_ENTITY = new Map<string, string>([\n  ['&', '&amp;'],\n  ['<', '&lt;'],\n  ['>', '&gt;'],\n  ['\"', '&quot;'],\n  [\"'\", '&apos;'],\n])\n\n// Disallowed XML 1.0 control chars: U+0000-U+001F EXCEPT \\t (U+0009),\n// \\n (U+000A), \\r (U+000D). Biome's noControlCharactersInRegex rule\n// requires Unicode escape sequences here rather than literal bytes.\n// biome-ignore lint/suspicious/noControlCharactersInRegex: XML 1.0 spec requires stripping these control chars\nconst CONTROL_CHARS_RE = /[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F]/g\nconst XML_ENTITY_RE = /[&<>\"']/g\nconst CDATA_TERMINATOR_RE = /]]>/g\n\n/**\n * Escape a string for safe embedding in XML element content OR attribute values.\n *\n * Pipeline (order matters):\n *   1. Strip control chars U+0000-U+001F EXCEPT `\\t` (U+0009), `\\n` (U+000A),\n *      `\\r` (U+000D). Stripping first prevents emitting broken entities.\n *   2. Replace the 5 XML special characters with named entities.\n *   3. Defense-in-depth: escape any residual `]]>` sequence to `]]&gt;`. After\n *      step 2 the literal `]]>` cannot exist (the `>` is gone), so this is a\n *      no-op in practice — it stands as a safety net against future edits.\n *\n * NOT for use on JSON content — `JSON.stringify` handles JSON escaping\n * natively and applying this would double-escape literal `<` characters.\n */\nexport function escapeXml(input: string): string {\n  return input\n    .replace(CONTROL_CHARS_RE, '')\n    .replace(XML_ENTITY_RE, (c) => XML_ENTITY.get(c) ?? c)\n    .replace(CDATA_TERMINATOR_RE, ']]&gt;')\n}\n","import type { AnnouncementConfig } from '../types/announcement'\nimport { escapeXml } from './escape'\n\n/**\n * Public, JSON-serializable changelog entry used by `serializeFeed`.\n *\n * Extends `AnnouncementConfig` with publish-time metadata. Reuses the\n * announcement shape so a config that already drives an in-app modal can\n * simultaneously be syndicated as RSS/JSON Feed.\n */\nexport interface ChangelogEntry extends AnnouncementConfig {\n  /**\n   * Stable publish timestamp. Accepts a `Date` or any `Date`-parseable string\n   * (ISO 8601 recommended). Invalid values cause `serializeFeed` to throw a\n   * `TypeError` rather than emitting `Invalid Date` into the feed.\n   */\n  publishedAt: Date | string\n\n  /**\n   * Absolute URL of the canonical changelog page for this entry. Used as\n   * the RSS `<link>` and the JSON Feed `url` field.\n   */\n  permalink: string\n\n  /**\n   * Optional category tag (e.g. `'feature'`, `'fix'`). Surfaces as `<category>`\n   * in RSS and `tags: [category]` in JSON Feed.\n   */\n  category?: string\n\n  /**\n   * Optional rendered HTML body. Emitted only as JSON Feed `content_html`;\n   * RSS `<description>` always uses the plain-string `description` field\n   * (never raw HTML, to keep the entity-escape contract intact).\n   */\n  contentHtml?: string\n}\n\n/**\n * Publisher-level metadata applied once per feed.\n *\n * `feedUrl` is the base URL of the changelog feed routes; the serializer\n * appends `.xml` for the RSS self-link and `.json` for the JSON Feed\n * `feed_url`. Example: `'https://acme.com/changelog'` → routes\n * `https://acme.com/changelog.xml` and `https://acme.com/changelog.json`.\n */\nexport interface SerializeFeedOptions {\n  title: string\n  description: string\n  siteUrl: string\n  feedUrl: string\n  language?: string\n  copyright?: string\n}\n\n/**\n * Pure, side-effect-free RSS 2.0 + JSON Feed 1.1 serializer.\n *\n * Throws `TypeError` when any entry has an invalid `publishedAt` — fail-fast\n * to avoid emitting `Invalid Date` strings into a live feed.\n */\nexport function serializeFeed(\n  entries: ChangelogEntry[],\n  opts: SerializeFeedOptions\n): { rss: string; jsonFeed: string } {\n  for (const e of entries) {\n    validateDate(e.publishedAt, e.id)\n  }\n  return {\n    rss: serializeRss(entries, opts),\n    jsonFeed: serializeJsonFeed(entries, opts),\n  }\n}\n\nfunction validateDate(input: Date | string, entryId: string): Date {\n  const d = new Date(input)\n  if (Number.isNaN(d.getTime())) {\n    throw new TypeError(`serializeFeed: invalid publishedAt for entry \"${entryId}\"`)\n  }\n  return d\n}\n\nfunction serializeRss(entries: ChangelogEntry[], opts: SerializeFeedOptions): string {\n  const items = entries\n    .map((e) => {\n      const title = escapeXml(typeof e.title === 'string' ? e.title : '')\n      const desc = escapeXml(typeof e.description === 'string' ? e.description : '')\n      const cat = e.category ? `<category>${escapeXml(e.category)}</category>` : ''\n      return `<item>\n  <title>${title}</title>\n  <link>${escapeXml(e.permalink)}</link>\n  <guid isPermaLink=\"false\">${escapeXml(e.id)}</guid>\n  <pubDate>${new Date(e.publishedAt).toUTCString()}</pubDate>\n  <description>${desc}</description>\n  ${cat}\n</item>`\n    })\n    .join('\\n')\n\n  return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\">\n<channel>\n  <title>${escapeXml(opts.title)}</title>\n  <link>${escapeXml(opts.siteUrl)}</link>\n  <description>${escapeXml(opts.description)}</description>\n  <language>${escapeXml(opts.language ?? 'en')}</language>\n  <atom:link href=\"${escapeXml(opts.feedUrl)}.xml\" rel=\"self\" type=\"application/rss+xml\"/>\n  ${opts.copyright ? `<copyright>${escapeXml(opts.copyright)}</copyright>` : ''}\n${items}\n</channel>\n</rss>`\n}\n\nfunction serializeJsonFeed(entries: ChangelogEntry[], opts: SerializeFeedOptions): string {\n  return JSON.stringify(\n    {\n      version: 'https://jsonfeed.org/version/1.1',\n      title: opts.title,\n      description: opts.description,\n      home_page_url: opts.siteUrl,\n      feed_url: `${opts.feedUrl}.json`,\n      language: opts.language ?? 'en',\n      items: entries.map((e) => ({\n        id: e.id,\n        url: e.permalink,\n        title: typeof e.title === 'string' ? e.title : undefined,\n        content_html: e.contentHtml,\n        content_text: typeof e.description === 'string' ? e.description : undefined,\n        date_published: new Date(e.publishedAt).toISOString(),\n        tags: e.category ? [e.category] : undefined,\n      })),\n    },\n    null,\n    2\n  )\n}\n","import type { MediaSlotProps } from '@tour-kit/media'\nimport type { AnnouncementMedia } from '../types/announcement'\n\nconst ASPECT_RATIOS = ['16/9', '4/3', '1/1', '9/16', '21/9', 'auto'] as const\ntype AspectRatio = (typeof ASPECT_RATIOS)[number]\n\nfunction isAspectRatio(value: string | undefined): value is AspectRatio {\n  return value !== undefined && (ASPECT_RATIOS as readonly string[]).includes(value)\n}\n\n/**\n * Convert `AnnouncementMedia` into the strict `MediaSlotProps` shape that\n * `<MediaSlot>` expects.\n *\n * `AnnouncementMedia.aspectRatio` is typed `string` (broad) for legacy\n * reasons; `MediaSlotProps.aspectRatio` is the narrow `AspectRatio` union.\n * This adapter narrows via runtime guard so all announcement variants honor\n * the field consistently.\n */\nexport function toMediaSlotProps(m: AnnouncementMedia): MediaSlotProps {\n  return {\n    src: m.src,\n    type: m.type,\n    alt: m.alt,\n    poster: m.poster,\n    aspectRatio: isAspectRatio(m.aspectRatio) ? m.aspectRatio : undefined,\n    autoplay: m.autoplay,\n    loop: m.loop,\n    muted: m.muted,\n  }\n}\n"]}