{"version":3,"file":"index.cjs","names":["escapeRegExp","escapeUrlAttribute","escapeHtml","escapeTags","escapeAttribute","escapeAttribute","resolveClassNames","escapeHtml","expandEmoji"],"sources":["../src/dates.ts","../src/regexp.ts","../src/patterns.ts","../src/controlSequences.ts","../src/markdown.ts","../src/placeholders.ts","../src/index.ts"],"sourcesContent":["/*\n * Renders Slack's date control sequence:\n *\n *   <!date^1392734382^Posted {date_num} {time}^https://example.com|Feb 18, 2014>\n *\n * Slack expects each client to format the timestamp in the reader's timezone.\n * A server-side renderer has no reader, so this formats in UTC unless a\n * `dateTimeZone` is given.\n */\n\nconst MILLISECONDS_PER_DAY = 86_400_000\n\nconst partsFor = (date: Date, timeZone: string, options: Intl.DateTimeFormatOptions) =>\n  new Intl.DateTimeFormat('en-US', { timeZone, ...options }).formatToParts(date)\n\nconst partValue = (parts: Intl.DateTimeFormatPart[], type: Intl.DateTimeFormatPartTypes): string =>\n  parts.find((part) => part.type === type)?.value ?? ''\n\nconst ordinalSuffix = (day: number): string => {\n  if (day % 100 >= 11 && day % 100 <= 13) {\n    return 'th'\n  }\n  switch (day % 10) {\n    case 1:\n      return 'st'\n    case 2:\n      return 'nd'\n    case 3:\n      return 'rd'\n    default:\n      return 'th'\n  }\n}\n\n/** The calendar day of a timestamp in the given zone, as a `YYYY-MM-DD` string. */\nconst calendarDay = (date: Date, timeZone: string): string => {\n  const parts = partsFor(date, timeZone, { year: 'numeric', month: '2-digit', day: '2-digit' })\n  return `${partValue(parts, 'year')}-${partValue(parts, 'month')}-${partValue(parts, 'day')}`\n}\n\n/** `today`, `yesterday` or `tomorrow` when the date is one of them. */\nconst relativeDay = (date: Date, timeZone: string, now: Date): string | undefined => {\n  const target = calendarDay(date, timeZone)\n  if (target === calendarDay(now, timeZone)) {\n    return 'today'\n  }\n  if (target === calendarDay(new Date(now.getTime() - MILLISECONDS_PER_DAY), timeZone)) {\n    return 'yesterday'\n  }\n  if (target === calendarDay(new Date(now.getTime() + MILLISECONDS_PER_DAY), timeZone)) {\n    return 'tomorrow'\n  }\n  return undefined\n}\n\nexport interface DateTokenOptions {\n  timeZone: string\n  /** Injectable for deterministic tests of the `_pretty` tokens. */\n  now?: Date\n}\n\nconst buildTokens = (date: Date, { timeZone, now }: DateTokenOptions): Record<string, string> => {\n  const dateParts = partsFor(date, timeZone, {\n    year: 'numeric',\n    month: 'long',\n    day: 'numeric',\n    weekday: 'long',\n  })\n  const shortParts = partsFor(date, timeZone, { year: 'numeric', month: 'short', day: 'numeric' })\n  const timeParts = partsFor(date, timeZone, { hour: 'numeric', minute: '2-digit', hour12: true })\n  const timeSecsParts = partsFor(date, timeZone, {\n    hour: 'numeric',\n    minute: '2-digit',\n    second: '2-digit',\n    hour12: true,\n  })\n\n  const day = Number(partValue(dateParts, 'day'))\n  const dayWithSuffix = `${day}${ordinalSuffix(day)}`\n  const month = partValue(dateParts, 'month')\n  const year = partValue(dateParts, 'year')\n  const weekday = partValue(dateParts, 'weekday')\n\n  // ICU separates the time from AM/PM with U+202F, which varies between Node\n  // versions and is surprising in HTML output, so it is normalized to a space.\n  const formatTime = (parts: Intl.DateTimeFormatPart[]): string =>\n    parts\n      .map((part) => part.value)\n      .join('')\n      .replace(/[\\u00A0\\u202F]/g, ' ')\n      .trim()\n\n  const dateNum = calendarDay(date, timeZone)\n  const dateText = `${month} ${dayWithSuffix}, ${year}`\n  const dateShort = `${partValue(shortParts, 'month')} ${partValue(shortParts, 'day')}, ${partValue(shortParts, 'year')}`\n  const dateLong = `${weekday}, ${month} ${dayWithSuffix}, ${year}`\n\n  const relative = relativeDay(date, timeZone, now ?? new Date())\n  const pretty = (fallback: string): string => relative ?? fallback\n\n  return {\n    date_num: dateNum,\n    date: dateText,\n    date_short: dateShort,\n    date_long: dateLong,\n    date_pretty: pretty(dateText),\n    date_short_pretty: pretty(dateShort),\n    date_long_pretty: pretty(dateLong),\n    time: formatTime(timeParts),\n    time_secs: formatTime(timeSecsParts),\n  }\n}\n\nconst TOKEN_PATTERN = /\\{(\\w+)\\}/g\n\n/**\n * Substitutes `{date_num}`-style tokens. Returns `undefined` when the timestamp\n * cannot be formatted, so the caller can fall back to Slack's own text.\n */\nexport const formatSlackDate = (\n  timestampSeconds: string,\n  format: string,\n  options: DateTokenOptions\n): string | undefined => {\n  const seconds = Number(timestampSeconds)\n  if (!Number.isFinite(seconds)) {\n    return undefined\n  }\n  const date = new Date(seconds * 1000)\n  if (Number.isNaN(date.getTime())) {\n    return undefined\n  }\n\n  let tokens: Record<string, string>\n  try {\n    tokens = buildTokens(date, options)\n  } catch {\n    // An invalid `dateTimeZone` makes Intl throw a RangeError.\n    return undefined\n  }\n\n  return format.replace(TOKEN_PATTERN, (match, token: string) => tokens[token] ?? match)\n}\n\n/** The machine-readable value for a `<time datetime=\"...\">` attribute. */\nexport const toIsoTimestamp = (timestampSeconds: string): string | undefined => {\n  const seconds = Number(timestampSeconds)\n  if (!Number.isFinite(seconds)) {\n    return undefined\n  }\n  const date = new Date(seconds * 1000)\n  return Number.isNaN(date.getTime()) ? undefined : date.toISOString()\n}\n","export type MatchGroups = Record<string, string | undefined>\n\nexport type GroupReplacer = (groups: MatchGroups, fullMatch: string) => string\n\nconst cache = new Map<string, RegExp>()\n\n/**\n * Compiles a pattern once and reuses it. Every cached expression carries the `g`\n * flag so it can be searched from an arbitrary offset via {@link execFrom};\n * callers must therefore always set the offset rather than relying on\n * `lastIndex` carrying over between calls.\n */\nexport const cacheRegExp = (pattern: string, flags = ''): RegExp => {\n  const key = `${flags}\\u0000${pattern}`\n  let compiled = cache.get(key)\n  if (!compiled) {\n    compiled = new RegExp(pattern, flags.includes('g') ? flags : `${flags}g`)\n    cache.set(key, compiled)\n  }\n  return compiled\n}\n\nexport const execFrom = (\n  text: string,\n  pattern: RegExp,\n  position: number\n): RegExpExecArray | null => {\n  pattern.lastIndex = position > 0 ? position : 0\n  return pattern.exec(text)\n}\n\n/** Applies each replacement over the whole string, in order. */\nexport const replaceEach = (\n  text: string,\n  replacements: readonly (readonly [RegExp, GroupReplacer])[]\n): string =>\n  replacements.reduce(\n    (accumulated, [pattern, replacer]) =>\n      accumulated.replace(pattern, (...args: unknown[]) => {\n        const last = args[args.length - 1]\n        const groups = (typeof last === 'object' && last !== null ? last : {}) as MatchGroups\n        return replacer(groups, args[0] as string)\n      }),\n    text\n  )\n","import { escapeRegExp } from './escape.js'\nimport { cacheRegExp } from './regexp.js'\n\n// https://docs.slack.dev/messaging/formatting-message-text\nexport const userMentionRegExp = cacheRegExp(\n  '<@(((?<userID>U[^|>]+)(\\\\|(?<userName>[^>]+))?)|(?<userNameWithoutID>[^>]+))>'\n)\nexport const channelMentionRegExp = cacheRegExp(\n  '<#(((?<channelID>C[^|>]+)(\\\\|(?<channelName>[^>]+))?)|(?<channelNameWithoutID>[^>]+))>'\n)\nexport const linkRegExp = cacheRegExp('<(?<linkUrl>https?:[^|>]+)(\\\\|(?<linkHtml>[^>]+))?>')\nexport const mailToRegExp = cacheRegExp('<mailto:(?<mailTo>[^|>]+)(\\\\|(?<mailToName>[^>]+))?>')\nexport const telRegExp = cacheRegExp('<tel:(?<tel>[^|>]+)(\\\\|(?<telName>[^>]+))?>')\nexport const subteamCommandRegExp = cacheRegExp(\n  '<!subteam\\\\^(?<subteamID>S[^|>]+)(\\\\|(?<subteamName>[^>]+))?>'\n)\n// <!date^1392734382^Posted {date_num} {time}^https://example.com|Feb 18, 2014>\nexport const dateCommandRegExp = cacheRegExp(\n  '<!date\\\\^(?<dateTimestamp>-?\\\\d+)\\\\^(?<dateFormat>[^^|>]*)(\\\\^(?<dateLink>[^|>]*))?(\\\\|(?<dateFallback>[^>]*))?>'\n)\nexport const commandRegExp = cacheRegExp('<!(?<commandLiteral>[^|>]+)(\\\\|(?<commandName>[^>]+))?>')\n\nexport const whitespaceRegExp = cacheRegExp('\\\\s', 's')\n\nexport const knownCommands = ['here', 'channel', 'group', 'everyone']\n\nexport interface DelimiterOptions {\n  prefixPattern?: string\n  spacePadded?: boolean\n  escapeDelimiter?: boolean\n}\n\nexport const buildOpeningDelimiterRegExp = (\n  delimiter: string,\n  { prefixPattern = '', spacePadded = false, escapeDelimiter = true }: DelimiterOptions = {}\n): RegExp => {\n  const escapedDelimiter = escapeDelimiter ? escapeRegExp(delimiter) : delimiter\n  const openingWhitespace = spacePadded ? '(?<openingCapturedWhitespace>^|\\\\s)' : ''\n  return cacheRegExp(`${openingWhitespace}${prefixPattern}${escapedDelimiter}`, 's')\n}\n\n// A negative lookahead cannot be used to capture the last consecutive delimiter,\n// because delimiters can be more than one character long.\nexport const buildClosingDelimiterRegExp = (\n  delimiter: string,\n  { spacePadded = false, escapeDelimiter = true }: DelimiterOptions = {}\n): RegExp => {\n  const escapedDelimiter = escapeDelimiter ? escapeRegExp(delimiter) : delimiter\n  const closingWhitespace = spacePadded ? '(?<closingCapturedWhitespace>\\\\s|$)' : ''\n  return cacheRegExp(`${escapedDelimiter}${closingWhitespace}`, 's')\n}\n\n/** The expressions this library matches against, exposed for debugging. */\nexport const buildSlackPatterns = (): Record<string, RegExp> => ({\n  userMentionRegExp,\n  channelMentionRegExp,\n  linkRegExp,\n  mailToRegExp,\n  telRegExp,\n  subteamCommandRegExp,\n  boldOpeningDelimiterRegExp: buildOpeningDelimiterRegExp('*'),\n  boldClosingDelimiterRegExp: buildClosingDelimiterRegExp('*'),\n  italicsOpeningDelimiterRegExp: buildOpeningDelimiterRegExp('_', { spacePadded: true }),\n  italicsClosingDelimiterRegExp: buildClosingDelimiterRegExp('_', { spacePadded: true }),\n  strikethroughOpeningDelimiterRegExp: buildOpeningDelimiterRegExp('~'),\n  strikethroughClosingDelimiterRegExp: buildClosingDelimiterRegExp('~'),\n  blockDivOpeningDelimiterRegExp: buildOpeningDelimiterRegExp('&gt;&gt;&gt;'),\n  blockDivClosingDelimiterRegExp: buildClosingDelimiterRegExp('$', { escapeDelimiter: false }),\n  blockSpanOpeningDelimiterRegExp: buildOpeningDelimiterRegExp('&gt;'),\n  blockSpanClosingDelimiterRegExp: buildClosingDelimiterRegExp('\\\\n|$', {\n    escapeDelimiter: false,\n  }),\n})\n","import { formatSlackDate, toIsoTimestamp } from './dates.js'\nimport { escapeAttribute, escapeHtml, escapeTags, escapeUrlAttribute } from './escape.js'\nimport {\n  channelMentionRegExp,\n  commandRegExp,\n  dateCommandRegExp,\n  knownCommands,\n  linkRegExp,\n  mailToRegExp,\n  subteamCommandRegExp,\n  telRegExp,\n  userMentionRegExp,\n} from './patterns.js'\nimport type { PlaceholderStore } from './placeholders.js'\nimport { replaceEach } from './regexp.js'\nimport type { MatchGroups } from './regexp.js'\nimport type { ClassNames, SlackEscapeOptions } from './types.js'\n\nconst anchor = (href: string, label: string, external: boolean): string => {\n  const safeHref = escapeUrlAttribute(href)\n  if (!safeHref) {\n    return escapeHtml(label)\n  }\n  const rel = external ? ' target=\"_blank\" rel=\"noopener noreferrer\"' : ''\n  return `<a href=\"${safeHref}\"${rel}>${escapeHtml(label)}</a>`\n}\n\nconst renderDate = (\n  groups: MatchGroups,\n  fullMatch: string,\n  classNames: ClassNames,\n  timeZone: string\n): string => {\n  const timestamp = groups.dateTimestamp ?? ''\n  const fallback = groups.dateFallback\n  const formatted = formatSlackDate(timestamp, groups.dateFormat ?? '', { timeZone })\n  // Slack always sends a fallback; prefer it over rendering nothing at all.\n  const label = formatted?.trim() ? formatted : fallback\n\n  if (label === undefined || label === '') {\n    return escapeTags(fullMatch)\n  }\n\n  const body = groups.dateLink ? anchor(groups.dateLink, label, true) : escapeHtml(label)\n  const isoTimestamp = toIsoTimestamp(timestamp)\n  if (!isoTimestamp) {\n    return body\n  }\n  return `<time datetime=\"${escapeAttribute(isoTimestamp)}\" class=\"${escapeAttribute(\n    classNames.date\n  )}\">${body}</time>`\n}\n\n/**\n * Renders every Slack `<...>` control sequence into HTML and swaps it for a\n * placeholder, so the surrounding text can be escaped without touching it.\n */\nexport const tokenizeControlSequences = (\n  text: string,\n  options: SlackEscapeOptions,\n  classNames: ClassNames,\n  placeholders: PlaceholderStore\n): string => {\n  const users = options.users ?? {}\n  const channels = options.channels ?? {}\n  const usergroups = options.usergroups ?? {}\n  const timeZone = options.dateTimeZone ?? 'UTC'\n\n  const store = (html: string): string => placeholders.add(html)\n\n  return replaceEach(text, [\n    [\n      userMentionRegExp,\n      (groups, fullMatch) => {\n        const userName =\n          groups.userName ??\n          groups.userNameWithoutID ??\n          (groups.userID ? users[groups.userID] : undefined)\n        return store(\n          userName\n            ? `<span class=\"${escapeAttribute(classNames.userMention)}\">@${escapeHtml(userName)}</span>`\n            : escapeTags(fullMatch)\n        )\n      },\n    ],\n    [\n      channelMentionRegExp,\n      (groups, fullMatch) => {\n        const channelName =\n          groups.channelName ??\n          groups.channelNameWithoutID ??\n          (groups.channelID ? channels[groups.channelID] : undefined)\n        return store(channelName ? `#${escapeHtml(channelName)}` : escapeTags(fullMatch))\n      },\n    ],\n    [\n      linkRegExp,\n      (groups) => {\n        const url = groups.linkUrl ?? ''\n        return store(anchor(url, groups.linkHtml ?? url, true))\n      },\n    ],\n    [\n      mailToRegExp,\n      (groups) => {\n        const address = groups.mailTo ?? ''\n        return store(anchor(`mailto:${address}`, groups.mailToName ?? address, true))\n      },\n    ],\n    [\n      telRegExp,\n      (groups) => {\n        const number = groups.tel ?? ''\n        return store(anchor(`tel:${number}`, groups.telName ?? number, false))\n      },\n    ],\n    [\n      dateCommandRegExp,\n      (groups, fullMatch) => store(renderDate(groups, fullMatch, classNames, timeZone)),\n    ],\n    [\n      subteamCommandRegExp,\n      (groups, fullMatch) => {\n        const userGroupName =\n          groups.subteamName ?? (groups.subteamID ? usergroups[groups.subteamID] : undefined)\n        return store(userGroupName ? escapeHtml(userGroupName) : escapeTags(fullMatch))\n      },\n    ],\n    [\n      commandRegExp,\n      (groups, fullMatch) => {\n        const { commandLiteral, commandName } = groups\n        if (commandLiteral?.startsWith('subteam') || commandLiteral?.startsWith('date')) {\n          // Left for the dedicated patterns above; reaching here means the\n          // sequence was malformed, so it is rendered as literal text.\n          return store(escapeTags(fullMatch))\n        }\n        if (commandLiteral && knownCommands.includes(commandLiteral)) {\n          return store(`@${escapeHtml(commandLiteral)}`)\n        }\n        return store(escapeTags(`<${commandName ?? commandLiteral ?? ''}>`))\n      },\n    ],\n  ])\n}\n","import { escapeAttribute } from './escape.js'\nimport {\n  buildClosingDelimiterRegExp,\n  buildOpeningDelimiterRegExp,\n  whitespaceRegExp,\n} from './patterns.js'\nimport { execFrom } from './regexp.js'\nimport type { ClassNames } from './types.js'\n\nconst CLOSING_DIV = '</div>'\nconst CLOSING_SPAN = '</span>'\nconst CODE_OPENING = '<code>'\nconst CODE_CLOSING = '</code>'\nconst LINE_BREAK_TAG = '<br>'\n\n/** A `[start, end)` region of the text that markdown may still be applied to. */\ntype TagWindow = [number, number]\n\ninterface ExpandedText {\n  text: string\n  windows: TagWindow[]\n}\n\ninterface ReplaceOptions {\n  /** Split the window in two around the match, so nested passes skip the match. */\n  partitionWindowOnMatch?: boolean\n  /** Require the delimiters to be surrounded by whitespace or string boundaries. */\n  spacePadded?: boolean\n  /** Close on this pattern instead of a second occurrence of the delimiter. */\n  endingPattern?: string\n  replaceNewlines?: boolean\n  prefixPattern?: string\n  maxReplacements?: number\n}\n\n/** A region of the text to rewrite, in the coordinates of the text read. */\ninterface Replacement {\n  start: number\n  end: number\n  text: string\n}\n\nconst applyReplacements = (text: string, replacements: Replacement[]): string => {\n  if (replacements.length === 0) {\n    return text\n  }\n  const parts: string[] = []\n  let position = 0\n  for (const replacement of replacements) {\n    parts.push(text.slice(position, replacement.start), replacement.text)\n    position = replacement.end\n  }\n  parts.push(text.slice(position))\n  return parts.join('')\n}\n\n/**\n * Moves window bounds into the coordinates of the replaced text. A bound shifts\n * by every replacement beginning before it, which holds a window truncated at a\n * replacement's start in place while moving one that resumes after it.\n *\n * Replacements and windows are both in increasing order and windows never\n * overlap, so the bounds are visited in increasing order and the replacements can\n * be consumed in a single pass.\n */\nconst translateWindows = (windows: TagWindow[], replacements: Replacement[]): void => {\n  if (replacements.length === 0) {\n    return\n  }\n  let replacementIndex = 0\n  let shift = 0\n  for (const tagWindow of windows) {\n    for (const bound of [0, 1] as const) {\n      let pending = replacements[replacementIndex]\n      while (pending && pending.start < tagWindow[bound]) {\n        shift += pending.text.length - (pending.end - pending.start)\n        replacementIndex += 1\n        pending = replacements[replacementIndex]\n      }\n      tagWindow[bound] += shift\n    }\n  }\n}\n\nconst replaceInWindows = (\n  text: string,\n  delimiterLiteral: string,\n  replacementOpeningLiteral: string,\n  replacementClosingLiteral: string,\n  closedTagWindows: TagWindow[],\n  options: ReplaceOptions = {}\n): ExpandedText => {\n  const { partitionWindowOnMatch, spacePadded, replaceNewlines, prefixPattern } = options\n  const asymmetric = options.endingPattern\n  let maxReplacements = options.maxReplacements\n\n  // An opening match always contains the delimiter literally, so a text without\n  // it cannot produce one. Worth checking up front: an earlier pass may have\n  // split the text into very many windows, each of which would otherwise be\n  // scanned separately below.\n  if (!text.includes(delimiterLiteral)) {\n    return { text, windows: closedTagWindows }\n  }\n\n  const openingDelimiterRegExp = buildOpeningDelimiterRegExp(delimiterLiteral, {\n    spacePadded,\n    prefixPattern,\n  })\n  const closingDelimiterRegExp = asymmetric\n    ? buildClosingDelimiterRegExp(asymmetric, { escapeDelimiter: false })\n    : buildClosingDelimiterRegExp(delimiterLiteral, { spacePadded })\n\n  /*\n   * The text is read as it was received and rewritten once at the end, rather\n   * than rebuilt around every delimiter pair. This is safe because scanning\n   * always resumes at or after the end of the pair just matched, so no region\n   * still to be read is ever rewritten beforehand. Rebuilding in place cost time\n   * proportional to the length of the whole text for every pair, which on a text\n   * with many pairs outweighed everything else the pass does.\n   *\n   * Every position therefore stays in the coordinates of the text received,\n   * including the window bounds, which are moved into the coordinates of the\n   * result once the pass is finished. Keeping one coordinate system is also what\n   * fixes overlapping delimiter matches: the bounds used to be advanced by a\n   * predicted length change that was wrong when the opening and closing matches\n   * shared a character, leaving them out of step with the text for later passes.\n   */\n  const replacements: Replacement[] = []\n  const finish = (): ExpandedText => {\n    translateWindows(closedTagWindows, replacements)\n    return { text: applyReplacements(text, replacements), windows: closedTagWindows }\n  }\n\n  let tagWindowIndex = 0\n  let tagWindowOffset = 0\n\n  /*\n   * A scan is not bounded by the window it was started for, so it can run to the\n   * end of the text and find a match belonging to a later window. Remembering the\n   * result lets the following windows reuse it instead of repeating the same scan,\n   * which is what made a text split into many windows cost quadratic time.\n   *\n   * Reuse is sound because `lastScan.match` is the first match at or after\n   * `lastScan.from`: for any position in `[lastScan.from, lastScan.match.index]`\n   * the answer is the same match, and a null result means there is no match after\n   * that position at all.\n   */\n  let lastScan: { from: number; match: RegExpExecArray | null } | null = null\n\n  const findOpeningDelimiter = (from: number): RegExpExecArray | null => {\n    if (lastScan && lastScan.from <= from && (!lastScan.match || lastScan.match.index >= from)) {\n      return lastScan.match\n    }\n    const match = execFrom(text, openingDelimiterRegExp, from)\n    lastScan = { from, match }\n    return match\n  }\n\n  for (;;) {\n    // A limit of exactly 0 does not halt the loop, only a negative one does, so\n    // `maxReplacements: n` permits n + 1 replacements. Preserved from 1.x.\n    const exhausted = maxReplacements !== undefined && maxReplacements < 0\n    if (tagWindowIndex >= closedTagWindows.length || exhausted) {\n      return finish()\n    }\n\n    const currentClosedTagWindow = closedTagWindows[tagWindowIndex] as TagWindow\n    const tagWindowStartIndex = currentClosedTagWindow[0]\n    const tagWindowEndIndex = currentClosedTagWindow[1]\n\n    if (\n      tagWindowStartIndex >= tagWindowEndIndex ||\n      tagWindowStartIndex + tagWindowOffset > tagWindowEndIndex\n    ) {\n      tagWindowIndex += 1\n      tagWindowOffset = 0\n      continue\n    }\n\n    const openingMatch = findOpeningDelimiter(tagWindowStartIndex + tagWindowOffset)\n\n    if (openingMatch && openingMatch.index < tagWindowEndIndex) {\n      const closingDelimiterLength = asymmetric ? 0 : delimiterLiteral.length\n      // Allow matching the end of the string if on the last window.\n      const lastWindow =\n        tagWindowIndex === closedTagWindows.length - 1 && tagWindowEndIndex === text.length\n      const closingMatchMaxIndex =\n        (lastWindow ? tagWindowEndIndex + 1 : tagWindowEndIndex) - closingDelimiterLength + 1\n\n      // Look ahead at the next index to greedily capture as much inside the\n      // delimiters as possible.\n      let closingMatch = execFrom(\n        text,\n        closingDelimiterRegExp,\n        openingMatch.index + delimiterLiteral.length\n      )\n      let nextClosingMatch =\n        closingMatch && execFrom(text, closingDelimiterRegExp, closingMatch.index + 1)\n      while (closingMatch && nextClosingMatch) {\n        // If the next match is still in the window and there is no whitespace in\n        // between the two, use the later one.\n        const nextWhitespace = execFrom(\n          text,\n          whitespaceRegExp,\n          closingMatch.index + delimiterLiteral.length\n        )\n        const crossedWhitespace = nextWhitespace && nextWhitespace.index < closingMatchMaxIndex\n        if (nextClosingMatch.index >= closingMatchMaxIndex || crossedWhitespace) {\n          break\n        }\n        closingMatch = nextClosingMatch\n        nextClosingMatch = execFrom(text, closingDelimiterRegExp, closingMatch.index + 1)\n      }\n\n      if (closingMatch && closingMatch.index < closingMatchMaxIndex) {\n        const afterDelimitersIndex = closingMatch.index + closingMatch[0].length\n\n        const openingWhitespace = spacePadded\n          ? (openingMatch.groups?.openingCapturedWhitespace ?? '')\n          : ''\n        const closingWhitespace = spacePadded\n          ? (closingMatch.groups?.closingCapturedWhitespace ?? '')\n          : ''\n        const openingReplacementString = `${openingWhitespace}${replacementOpeningLiteral}`\n        const closingReplacementString = `${replacementClosingLiteral}${closingWhitespace}${\n          asymmetric ? closingMatch[0] : ''\n        }`\n\n        const textBetweenDelimiters = text.slice(\n          openingMatch.index + openingMatch[0].length,\n          closingMatch.index\n        )\n        const replacedTextBetweenDelimiters = replaceNewlines\n          ? textBetweenDelimiters.replaceAll('\\n', LINE_BREAK_TAG)\n          : textBetweenDelimiters\n\n        const replacedDelimiterText = `${openingReplacementString}${replacedTextBetweenDelimiters}${closingReplacementString}`\n\n        const nextWindowIndex = partitionWindowOnMatch ? tagWindowIndex + 1 : tagWindowIndex\n\n        if (partitionWindowOnMatch) {\n          // Split the current window into two around the delimiter pair.\n          currentClosedTagWindow[1] = openingMatch.index\n          closedTagWindows.splice(nextWindowIndex, 0, [\n            closingMatch.index + closingDelimiterLength,\n            tagWindowEndIndex,\n          ])\n        }\n        if (maxReplacements !== undefined) {\n          maxReplacements -= 1\n        }\n\n        replacements.push({\n          start: openingMatch.index,\n          end: afterDelimitersIndex,\n          text: replacedDelimiterText,\n        })\n        tagWindowIndex = nextWindowIndex\n        tagWindowOffset = partitionWindowOnMatch\n          ? 0\n          : afterDelimitersIndex + 1 - tagWindowStartIndex\n        continue\n      }\n    }\n\n    tagWindowIndex += 1\n    tagWindowOffset = 0\n  }\n}\n\n/** Applies Slack's `mrkdwn` delimiters, outermost first. */\nexport const expandText = (text: string, classNames: ClassNames): string => {\n  const openingTag = (tag: 'div' | 'span', className: string): string =>\n    `<${tag} class=\"${escapeAttribute(className)}\">`\n\n  let expanded: ExpandedText = { text, windows: [[0, text.length]] }\n\n  expanded = replaceInWindows(\n    expanded.text,\n    '```',\n    openingTag('div', classNames.code) + CODE_OPENING,\n    CODE_CLOSING + CLOSING_DIV,\n    expanded.windows,\n    { partitionWindowOnMatch: true, replaceNewlines: true }\n  )\n  expanded = replaceInWindows(\n    expanded.text,\n    '`',\n    openingTag('span', classNames.code) + CODE_OPENING,\n    CODE_CLOSING + CLOSING_SPAN,\n    expanded.windows,\n    { partitionWindowOnMatch: true }\n  )\n  expanded = replaceInWindows(\n    expanded.text,\n    '*',\n    openingTag('span', classNames.bold),\n    CLOSING_SPAN,\n    expanded.windows,\n    { maxReplacements: 100 }\n  )\n  expanded = replaceInWindows(\n    expanded.text,\n    '~',\n    openingTag('span', classNames.strikethrough),\n    CLOSING_SPAN,\n    expanded.windows,\n    { maxReplacements: 100 }\n  )\n  expanded = replaceInWindows(\n    expanded.text,\n    '_',\n    openingTag('span', classNames.italics),\n    CLOSING_SPAN,\n    expanded.windows,\n    { spacePadded: true, maxReplacements: 100 }\n  )\n  expanded = replaceInWindows(\n    expanded.text,\n    '&gt;&gt;&gt;',\n    openingTag('div', classNames.block),\n    CLOSING_DIV,\n    expanded.windows,\n    { prefixPattern: '^\\\\s*', endingPattern: '$', replaceNewlines: true, maxReplacements: 100 }\n  )\n  expanded = replaceInWindows(\n    expanded.text,\n    '&gt;',\n    openingTag('span', classNames.block),\n    CLOSING_SPAN,\n    expanded.windows,\n    { prefixPattern: '^\\\\s*', endingPattern: '\\\\n|$', maxReplacements: 100 }\n  )\n\n  return expanded.text\n}\n","/*\n * Control sequences are rendered before the text around them is escaped, so\n * their HTML has to be hidden from the escaping, markdown and emoji passes. Each\n * one is swapped for a placeholder and restored at the very end.\n *\n * Placeholders are delimited by NUL, which is stripped from the input first, so\n * a message cannot forge one. NUL also survives the later passes untouched: it\n * is not a markdown delimiter, and an emoji lookup containing it always misses,\n * which leaves the match unchanged.\n */\n\nconst NUL = '\\u0000'\n/* eslint-disable no-control-regex -- matching NUL is the point of these patterns */\nconst PLACEHOLDER_PATTERN = /\\u0000(\\d+)\\u0000/g\nconst FORGEABLE_CHARACTERS = /\\u0000/g\n/* eslint-enable no-control-regex */\n\nexport const stripPlaceholderCharacters = (text: string): string =>\n  text.replace(FORGEABLE_CHARACTERS, '')\n\nexport class PlaceholderStore {\n  private readonly rendered: string[] = []\n\n  /** Stores rendered HTML and returns the placeholder that stands in for it. */\n  add(html: string): string {\n    this.rendered.push(html)\n    return `${NUL}${this.rendered.length - 1}${NUL}`\n  }\n\n  /** Substitutes every placeholder back into the text. */\n  restore(text: string): string {\n    if (this.rendered.length === 0) {\n      return text\n    }\n    return text.replace(PLACEHOLDER_PATTERN, (match, index: string) => {\n      return this.rendered[Number(index)] ?? match\n    })\n  }\n}\n","import { tokenizeControlSequences } from './controlSequences.js'\nimport { expandEmoji } from './emoji.js'\nimport { escapeHtml } from './escape.js'\nimport { expandText } from './markdown.js'\nimport { buildSlackPatterns } from './patterns.js'\nimport { PlaceholderStore, stripPlaceholderCharacters } from './placeholders.js'\nimport type { SlackEscapeOptions } from './types.js'\nimport { resolveClassNames } from './types.js'\n\nexport type { ClassNames, CustomEmojiMap, NameMap, SlackEscapeOptions } from './types.js'\nexport { defaultClassNames } from './types.js'\nexport { emojiData, expandEmoji } from './emoji.js'\nexport { buildSlackPatterns }\n\n/** Mirrors 1.x, which coerced any falsy input to an empty string. */\nconst normalizeInput = (text: string | null | undefined): string => (text ? `${text}` : '')\n\n/**\n * Renders a Slack message as HTML.\n *\n * The passes run in a fixed order, because each one depends on the previous:\n * control sequences are rendered first (before escaping could destroy their\n * `<...>` delimiters) and parked as placeholders, then the remaining text is\n * escaped, formatted as markdown, and finally emoji-expanded.\n */\nexport const escapeForSlack = (\n  text: string | null | undefined,\n  options: SlackEscapeOptions = {}\n): string => {\n  const classNames = resolveClassNames(options.classNames)\n  const placeholders = new PlaceholderStore()\n\n  const source = stripPlaceholderCharacters(normalizeInput(text))\n  const tokenized = tokenizeControlSequences(source, options, classNames, placeholders)\n  const escaped = options.escapeHtml === false ? tokenized : escapeHtml(tokenized)\n  const formatted = options.markdown ? expandText(escaped, classNames) : escaped\n  const withEmoji = expandEmoji(formatted, {\n    customEmoji: options.customEmoji ?? {},\n    classNames,\n  })\n\n  return placeholders.restore(withEmoji)\n}\n\n/** {@link escapeForSlack} with Slack's `mrkdwn` formatting enabled. */\nexport const escapeForSlackWithMarkdown = (\n  text: string | null | undefined,\n  options: SlackEscapeOptions = {}\n): string => escapeForSlack(text, { ...options, markdown: true })\n\n/**\n * @deprecated Renamed to {@link buildSlackPatterns} in 2.0.0.\n */\nexport const buildSlackHawkDownRegExps = buildSlackPatterns\n"],"mappings":"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"}