@assistant-ui/react-generative-ui 0.0.13 → 0.0.14

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.
Files changed (44) hide show
  1. package/dist/JSONGenerativeUI.client.d.ts +5 -0
  2. package/dist/JSONGenerativeUI.client.d.ts.map +1 -1
  3. package/dist/JSONGenerativeUI.client.js +12 -3
  4. package/dist/JSONGenerativeUI.client.js.map +1 -1
  5. package/dist/slack/constants.d.ts +11 -4
  6. package/dist/slack/constants.d.ts.map +1 -1
  7. package/dist/slack/constants.js +11 -4
  8. package/dist/slack/constants.js.map +1 -1
  9. package/dist/slack/fromSlackBlocks.js +2 -2
  10. package/dist/slack/toSlackBlocks.d.ts.map +1 -1
  11. package/dist/slack/toSlackBlocks.js +111 -17
  12. package/dist/slack/toSlackBlocks.js.map +1 -1
  13. package/dist/slack/types.d.ts +7 -1
  14. package/dist/slack/types.d.ts.map +1 -1
  15. package/dist/teams/constants.d.ts +9 -2
  16. package/dist/teams/constants.d.ts.map +1 -1
  17. package/dist/teams/constants.js +10 -3
  18. package/dist/teams/constants.js.map +1 -1
  19. package/dist/teams/toAdaptiveCard.d.ts +10 -2
  20. package/dist/teams/toAdaptiveCard.d.ts.map +1 -1
  21. package/dist/teams/toAdaptiveCard.js +50 -13
  22. package/dist/teams/toAdaptiveCard.js.map +1 -1
  23. package/dist/teams/toTeamsAttachments.d.ts.map +1 -1
  24. package/dist/teams/toTeamsAttachments.js +18 -11
  25. package/dist/teams/toTeamsAttachments.js.map +1 -1
  26. package/dist/teams/types.d.ts +11 -2
  27. package/dist/teams/types.d.ts.map +1 -1
  28. package/dist/vocabulary/layout.js +2 -1
  29. package/dist/vocabulary/layout.js.map +1 -1
  30. package/package.json +3 -3
  31. package/src/JSONGenerativeUI.client.tsx +13 -5
  32. package/src/JSONGenerativeUI.test.tsx +21 -2
  33. package/src/slack/constants.ts +11 -3
  34. package/src/slack/toSlackBlocks.test.ts +374 -8
  35. package/src/slack/toSlackBlocks.ts +181 -24
  36. package/src/slack/types.ts +7 -1
  37. package/src/teams/constants.ts +10 -2
  38. package/src/teams/toAdaptiveCard.test.ts +204 -10
  39. package/src/teams/toAdaptiveCard.ts +79 -15
  40. package/src/teams/toTeamsAttachments.test.ts +47 -2
  41. package/src/teams/toTeamsAttachments.ts +17 -11
  42. package/src/teams/types.ts +11 -2
  43. package/src/vocabulary/layout.test.tsx +16 -1
  44. package/src/vocabulary/layout.tsx +4 -1
@@ -1 +1 @@
1
- {"version":3,"file":"toSlackBlocks.js","names":[],"sources":["../../src/slack/toSlackBlocks.ts"],"sourcesContent":["import {\n normalizeSpec,\n type NormalizedUIElement,\n type NormalizedUINode,\n} from \"../ir\";\nimport {\n ACTION_ID_CAP,\n ACTIONS_ELEMENT_CAP,\n ALERT_TEXT_CAP,\n BUTTON_VALUE_CAP,\n CARD_ACTIONS_CAP,\n CARD_BODY_CAP,\n CARD_SUBTEXT_CAP,\n CARD_TITLE_CAP,\n CAROUSEL_CARD_CAP,\n CAROUSEL_CARD_MIN,\n CHILDREN_CAP,\n CONTEXT_ELEMENT_CAP,\n CONTEXT_TEXT_CAP,\n DATA_TABLE_CHAR_BUDGET,\n DATA_TABLE_COLUMN_CAP,\n DATA_TABLE_ROW_CAP,\n FACT_FIELD_CAP,\n FACT_FIELD_TEXT_CAP,\n HEADER_TEXT_CAP,\n INPUT_LABEL_CAP,\n INTERACTIVE_TEXT_CAP,\n MARKDOWN_TEXT_BUDGET,\n MAX_TRAVERSAL_DEPTH,\n MESSAGE_BLOCK_CAP,\n MODAL_BLOCK_CAP,\n NODE_BUDGET,\n PLACEHOLDER_TEXT_CAP,\n RADIO_OPTION_CAP,\n SECTION_TEXT_CAP,\n SELECT_OPTION_CAP,\n buildAlertBlock,\n buildAlertFallback,\n buildBlockBudgetNote,\n buildCardBlock,\n buildCarouselBlock,\n buildDataTableBlock,\n} from \"./constants\";\nimport type {\n SlackActionElement,\n SlackBlock,\n SlackButtonElement,\n SlackCardBlock,\n SlackConversionWarning,\n SlackDataTableCell,\n SlackImageBlock,\n SlackOption,\n SlackPlainText,\n SlackTextObject,\n SlackBlocksResult,\n ToSlackBlocksOptions,\n} from \"./types\";\n\ntype ConversionContext = {\n readonly blockCap: number;\n readonly surface: \"message\" | \"modal\";\n readonly warnings: SlackConversionWarning[];\n markdownCharacters: number;\n markdownExhausted: boolean;\n dataTableCharacters: number;\n};\n\nconst INTERACTIVE_TYPES = new Set([\n \"Button\",\n \"Select\",\n \"DatePicker\",\n \"Checkbox\",\n \"RadioGroup\",\n]);\n\nconst DATE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isElement = (node: NormalizedUINode): node is NormalizedUIElement =>\n isRecord(node);\n\nconst asString = (value: unknown): string =>\n typeof value === \"string\" ? value : \"\";\n\nconst warn = (\n context: ConversionContext,\n code: SlackConversionWarning[\"code\"],\n component: string,\n detail: string,\n) => {\n context.warnings.push({ code, component, detail });\n};\n\nconst clampText = (\n value: string,\n cap: number,\n component: string,\n field: string,\n context: ConversionContext,\n): string => {\n if (value.length <= cap) return value;\n warn(\n context,\n \"clamped\",\n component,\n `${field} was clamped to ${cap} characters.`,\n );\n return value.slice(0, cap);\n};\n\nconst asActionId = (\n action: unknown,\n component: string,\n context: ConversionContext,\n): string =>\n clampText(\n isRecord(action) && typeof action[\"type\"] === \"string\"\n ? action[\"type\"]\n : \"action\",\n ACTION_ID_CAP,\n component,\n \"action_id\",\n context,\n );\n\nconst plainText = (text: string): SlackPlainText => ({\n type: \"plain_text\",\n text,\n});\n\nconst actionValue = (action: unknown): string | undefined => {\n if (!isRecord(action)) return undefined;\n const { type: _type, ...payload } = action;\n if (Object.keys(payload).length === 0) return undefined;\n try {\n const serialized = JSON.stringify(payload);\n return serialized === \"{}\" ? undefined : serialized;\n } catch {\n return undefined;\n }\n};\n\nconst buttonElement = (\n label: string,\n buttonStyle: unknown,\n action: unknown,\n component: string,\n context: ConversionContext,\n): SlackButtonElement => {\n const serializedValue = actionValue(action);\n let value = serializedValue;\n if (value !== undefined && value.length > BUTTON_VALUE_CAP) {\n warn(\n context,\n \"dropped\",\n component,\n \"value was dropped because the action payload was too large to round-trip.\",\n );\n value = undefined;\n }\n const style =\n buttonStyle === \"primary\" || buttonStyle === \"danger\"\n ? buttonStyle\n : undefined;\n return {\n type: \"button\",\n text: plainText(\n clampText(label, INTERACTIVE_TEXT_CAP, component, \"label\", context),\n ),\n action_id: asActionId(action, component, context),\n ...(value !== undefined ? { value } : {}),\n ...(style !== undefined ? { style } : {}),\n };\n};\n\nconst optionFrom = (\n value: unknown,\n component: string,\n context: ConversionContext,\n): SlackOption | undefined => {\n if (\n !isRecord(value) ||\n typeof value[\"label\"] !== \"string\" ||\n typeof value[\"value\"] !== \"string\"\n ) {\n return undefined;\n }\n return {\n text: plainText(\n clampText(\n value[\"label\"],\n INTERACTIVE_TEXT_CAP,\n component,\n \"option label\",\n context,\n ),\n ),\n value: value[\"value\"],\n };\n};\n\nconst toActionElement = (\n element: NormalizedUIElement,\n context: ConversionContext,\n): SlackActionElement | undefined => {\n const { props, action } = element;\n switch (element.type) {\n case \"Button\":\n return buttonElement(\n asString(props[\"label\"]),\n props[\"buttonStyle\"],\n action,\n \"Button\",\n context,\n );\n case \"Select\": {\n const rawOptions = Array.isArray(props[\"options\"])\n ? props[\"options\"]\n : [];\n if (rawOptions.length > SELECT_OPTION_CAP) {\n warn(\n context,\n \"clamped\",\n \"Select\",\n `options were clamped to ${SELECT_OPTION_CAP} entries.`,\n );\n }\n const options = rawOptions\n .slice(0, SELECT_OPTION_CAP)\n .map((option) => optionFrom(option, \"Select\", context))\n .filter((option): option is SlackOption => option !== undefined);\n const placeholder = clampText(\n asString(props[\"placeholder\"]),\n PLACEHOLDER_TEXT_CAP,\n \"Select\",\n \"placeholder\",\n context,\n );\n return {\n type: \"static_select\",\n action_id: asActionId(action, \"Select\", context),\n options,\n ...(placeholder ? { placeholder: plainText(placeholder) } : {}),\n };\n }\n case \"DatePicker\": {\n const rawDate = asString(props[\"value\"]);\n const initialDate = DATE_PATTERN.test(rawDate) ? rawDate : undefined;\n if (rawDate && initialDate === undefined) {\n warn(\n context,\n \"dropped\",\n \"DatePicker\",\n \"value did not match the YYYY-MM-DD format and was dropped.\",\n );\n }\n return {\n type: \"datepicker\",\n action_id: asActionId(action, \"DatePicker\", context),\n ...(initialDate !== undefined ? { initial_date: initialDate } : {}),\n };\n }\n case \"Checkbox\": {\n const label = clampText(\n asString(props[\"label\"]),\n INTERACTIVE_TEXT_CAP,\n \"Checkbox\",\n \"label\",\n context,\n );\n const name = asString(props[\"name\"]);\n const option: SlackOption = {\n text: plainText(label),\n value: name || label,\n };\n return {\n type: \"checkboxes\",\n action_id: asActionId(action, \"Checkbox\", context),\n options: [option],\n ...(props[\"defaultChecked\"] === true\n ? { initial_options: [option] }\n : {}),\n };\n }\n case \"RadioGroup\": {\n const rawOptions = Array.isArray(props[\"options\"])\n ? props[\"options\"]\n : [];\n if (rawOptions.length > RADIO_OPTION_CAP) {\n warn(\n context,\n \"clamped\",\n \"RadioGroup\",\n `options were clamped to ${RADIO_OPTION_CAP} entries.`,\n );\n }\n const options = rawOptions\n .slice(0, RADIO_OPTION_CAP)\n .map((option) => optionFrom(option, \"RadioGroup\", context))\n .filter((option): option is SlackOption => option !== undefined);\n const selectedValue =\n typeof props[\"value\"] === \"string\"\n ? props[\"value\"]\n : asString(props[\"defaultValue\"]);\n const initialOption = options.find(\n (option) => option.value === selectedValue,\n );\n return {\n type: \"radio_buttons\",\n action_id: asActionId(action, \"RadioGroup\", context),\n options,\n ...(initialOption !== undefined\n ? { initial_option: initialOption }\n : {}),\n };\n }\n default:\n return undefined;\n }\n};\n\nconst normalizedList = (\n node: NormalizedUINode | undefined,\n): NormalizedUINode[] => {\n if (node === undefined || node === null) return [];\n if (!Array.isArray(node)) return [node];\n return node.flatMap((child) => normalizedList(child));\n};\n\nconst convertFacts = (\n facts: readonly NormalizedUIElement[],\n context: ConversionContext,\n): SlackBlock[] => {\n const fields = facts.map((fact) => {\n const label = asString(fact.props[\"label\"]);\n const value = asString(fact.props[\"value\"]);\n return {\n type: \"mrkdwn\" as const,\n text: clampText(\n `*${label}*\\n${value}`,\n FACT_FIELD_TEXT_CAP,\n \"Fact\",\n \"field\",\n context,\n ),\n };\n });\n const blocks: SlackBlock[] = [];\n for (let index = 0; index < fields.length; index += FACT_FIELD_CAP) {\n blocks.push({\n type: \"section\",\n fields: fields.slice(index, index + FACT_FIELD_CAP),\n });\n }\n return blocks;\n};\n\nconst convertActions = (\n elements: readonly NormalizedUIElement[],\n context: ConversionContext,\n): SlackBlock[] => {\n const converted = elements\n .map((element) => toActionElement(element, context))\n .filter((element): element is SlackActionElement => element !== undefined);\n const blocks: SlackBlock[] = [];\n for (let index = 0; index < converted.length; index += ACTIONS_ELEMENT_CAP) {\n blocks.push({\n type: \"actions\",\n elements: converted.slice(index, index + ACTIONS_ELEMENT_CAP),\n });\n }\n return blocks;\n};\n\nconst cardActionButtons = (\n props: Readonly<Record<string, unknown>>,\n context: ConversionContext,\n): SlackButtonElement[] => {\n const buttons: SlackButtonElement[] = [];\n for (const key of [\"confirm\", \"cancel\"] as const) {\n const footer = props[key];\n if (!isRecord(footer)) continue;\n buttons.push(\n buttonElement(\n asString(footer[\"label\"]),\n key === \"confirm\" ? \"primary\" : undefined,\n footer[\"$action\"],\n \"Card\",\n context,\n ),\n );\n }\n return buttons;\n};\n\nconst cardTitleText = (\n props: Readonly<Record<string, unknown>>,\n context: ConversionContext,\n): string | undefined => {\n const rawTitle = asString(props[\"title\"]);\n return rawTitle\n ? clampText(rawTitle, CARD_TITLE_CAP, \"Card\", \"title\", context)\n : undefined;\n};\n\nconst isCardNoOp = (node: NormalizedUINode): boolean =>\n isElement(node) && (node.type === \"Spacer\" || node.type === \"Icon\");\n\nconst collectText = (\n node: NormalizedUINode | undefined,\n depth: number,\n): string[] => {\n if (node === undefined || node === null || depth > MAX_TRAVERSAL_DEPTH) {\n return [];\n }\n if (typeof node === \"string\" || typeof node === \"number\") {\n return [String(node)];\n }\n if (Array.isArray(node)) {\n return node.flatMap((child) => collectText(child, depth + 1));\n }\n const element = node as NormalizedUIElement;\n const own = [\"text\", \"value\", \"label\", \"title\", \"description\"]\n .map((key) => element.props[key])\n .filter((value): value is string => typeof value === \"string\");\n return [...own, ...collectText(element.children, depth + 1)];\n};\n\ntype CardFields = {\n readonly heroImage: SlackImageBlock | undefined;\n readonly bodyRaw: string | undefined;\n readonly subtextRaw: string | undefined;\n readonly leftover: readonly NormalizedUINode[];\n};\n\n/**\n * Scans a card's children for the pieces the native card block shape can\n * carry (a hero image, body text, and subtext), each taken from the first\n * matching child. Anything left over means the card cannot map cleanly onto\n * the native shape.\n */\nconst extractCardFields = (element: NormalizedUIElement): CardFields => {\n const children = normalizedList(element.children);\n let heroImage: SlackImageBlock | undefined;\n let bodyRaw: string | undefined;\n let subtextRaw: string | undefined;\n const leftover: NormalizedUINode[] = [];\n for (const child of children) {\n if (heroImage === undefined && isElement(child) && child.type === \"Image\") {\n heroImage = {\n type: \"image\",\n image_url: asString(child.props[\"src\"]),\n alt_text: asString(child.props[\"alt\"]),\n };\n continue;\n }\n if (\n bodyRaw === undefined &&\n isElement(child) &&\n (child.type === \"Text\" || child.type === \"Markdown\")\n ) {\n bodyRaw = asString(child.props[\"value\"]);\n continue;\n }\n if (\n subtextRaw === undefined &&\n isElement(child) &&\n child.type === \"Caption\"\n ) {\n subtextRaw = asString(child.props[\"value\"]);\n continue;\n }\n if (isCardNoOp(child)) continue;\n leftover.push(child);\n }\n return { heroImage, bodyRaw, subtextRaw, leftover };\n};\n\n/** Assembles a native card block, clamping its body and subtext text. */\nconst assembleCleanCard = (\n titleText: string | undefined,\n fields: CardFields,\n buttons: readonly SlackButtonElement[],\n context: ConversionContext,\n): SlackCardBlock => {\n const body: SlackTextObject | undefined =\n fields.bodyRaw !== undefined\n ? {\n type: \"mrkdwn\",\n text: clampText(\n fields.bodyRaw,\n CARD_BODY_CAP,\n \"Card\",\n \"body\",\n context,\n ),\n }\n : undefined;\n const subtext: SlackTextObject | undefined =\n fields.subtextRaw !== undefined\n ? {\n type: \"mrkdwn\",\n text: clampText(\n fields.subtextRaw,\n CARD_SUBTEXT_CAP,\n \"Card\",\n \"subtext\",\n context,\n ),\n }\n : undefined;\n if (buttons.length > CARD_ACTIONS_CAP) {\n warn(\n context,\n \"clamped\",\n \"Card\",\n `actions were clamped to ${CARD_ACTIONS_CAP} entries.`,\n );\n }\n return buildCardBlock({\n ...(fields.heroImage !== undefined ? { heroImage: fields.heroImage } : {}),\n ...(titleText !== undefined\n ? { title: { type: \"mrkdwn\", text: titleText } }\n : {}),\n ...(body !== undefined ? { body } : {}),\n ...(subtext !== undefined ? { subtext } : {}),\n ...(buttons.length > 0\n ? { actions: buttons.slice(0, CARD_ACTIONS_CAP) }\n : {}),\n });\n};\n\n/**\n * A card requires at least one of `hero_image`, `title`, `body`, or `actions`\n * (see {@link SlackCardBlock}); a card with none of them is not a valid\n * native block and must be dropped instead of emitted.\n */\nconst isEmptyCard = (card: SlackCardBlock): boolean =>\n card.hero_image === undefined &&\n card.title === undefined &&\n card.body === undefined &&\n (card.actions === undefined || card.actions.length === 0);\n\nconst convertCard = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackBlock[] => {\n const titleText = cardTitleText(element.props, context);\n const fields = extractCardFields(element);\n const buttons = cardActionButtons(element.props, context);\n\n if (fields.leftover.length > 0) {\n warn(\n context,\n \"fallback\",\n \"Card\",\n \"A card with content beyond hero_image, body, subtext, and actions was rendered inline.\",\n );\n return [\n ...(titleText\n ? [{ type: \"header\" as const, text: plainText(titleText) }]\n : []),\n ...convertSequence(element.children, context, depth + 1),\n ...(buttons.length > 0\n ? [{ type: \"actions\" as const, elements: buttons }]\n : []),\n ];\n }\n const card = assembleCleanCard(titleText, fields, buttons, context);\n if (isEmptyCard(card)) {\n warn(\n context,\n \"dropped\",\n \"Card\",\n \"A card with none of hero_image, title, body, or actions was dropped.\",\n );\n return [];\n }\n return [card];\n};\n\n/**\n * Degrades a card that cannot map cleanly into a title-and-body card block.\n * A carousel cannot fall back to a block sequence like a standalone card\n * can, so its content is clamped down to fit the card shape instead.\n */\nconst degradeCard = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackCardBlock => {\n const rawTitle = asString(element.props[\"title\"]);\n const textChunks = collectText(element.children, depth + 1);\n const titleSource = rawTitle || textChunks[0] || \"\";\n const bodyChunks = rawTitle ? textChunks : textChunks.slice(1);\n const titleText = titleSource.slice(0, CARD_TITLE_CAP);\n const bodyText = bodyChunks.join(\"\\n\").slice(0, CARD_BODY_CAP);\n warn(\n context,\n \"clamped\",\n \"Card\",\n \"A card inside a carousel was degraded to title and body.\",\n );\n return buildCardBlock({\n ...(titleText ? { title: { type: \"mrkdwn\", text: titleText } } : {}),\n ...(bodyText ? { body: { type: \"mrkdwn\", text: bodyText } } : {}),\n });\n};\n\nconst convertCarouselCard = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackCardBlock | undefined => {\n const fields = extractCardFields(element);\n const card =\n fields.leftover.length === 0\n ? assembleCleanCard(\n cardTitleText(element.props, context),\n fields,\n cardActionButtons(element.props, context),\n context,\n )\n : degradeCard(element, context, depth);\n if (isEmptyCard(card)) {\n warn(\n context,\n \"dropped\",\n \"Card\",\n \"A card with none of hero_image, title, body, or actions was dropped.\",\n );\n return undefined;\n }\n return card;\n};\n\nconst isBadgeOrCaption = (\n node: NormalizedUINode,\n): node is NormalizedUIElement =>\n isElement(node) && (node.type === \"Badge\" || node.type === \"Caption\");\n\nconst contextRow = (\n element: NormalizedUIElement,\n context: ConversionContext,\n): SlackBlock[] | undefined => {\n const allChildren = normalizedList(element.children);\n const children = allChildren.filter(isBadgeOrCaption);\n if (allChildren.length === 0 || children.length !== allChildren.length) {\n return undefined;\n }\n if (children.length > CONTEXT_ELEMENT_CAP) {\n warn(\n context,\n \"clamped\",\n \"Row\",\n `context elements were clamped to ${CONTEXT_ELEMENT_CAP} entries.`,\n );\n }\n return [\n {\n type: \"context\",\n elements: children.slice(0, CONTEXT_ELEMENT_CAP).map((child) => ({\n type: \"mrkdwn\",\n text: clampText(\n asString(child.props[\"value\"]),\n CONTEXT_TEXT_CAP,\n child.type,\n \"value\",\n context,\n ),\n })),\n },\n ];\n};\n\nconst convertListItem = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackBlock => {\n const text = clampText(\n collectText(element.children, depth + 1).join(\"\\n\"),\n SECTION_TEXT_CAP,\n \"ListViewItem\",\n \"text\",\n context,\n );\n const accessory = isRecord(element.action)\n ? buttonElement(\"Open\", undefined, element.action, \"ListViewItem\", context)\n : undefined;\n return {\n type: \"section\",\n text: { type: \"mrkdwn\", text },\n ...(accessory !== undefined ? { accessory } : {}),\n };\n};\n\nconst convertListView = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackBlock[] => {\n const items = normalizedList(element.children).filter(\n (child): child is NormalizedUIElement =>\n isElement(child) && child.type === \"ListViewItem\",\n );\n return items.flatMap((item, index) => [\n ...(index > 0 ? [{ type: \"divider\" as const }] : []),\n convertListItem(item, context, depth + 1),\n ]);\n};\n\nconst convertCarousel = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackBlock[] => {\n const cardChildren: NormalizedUIElement[] = [];\n for (const child of normalizedList(element.children)) {\n if (isElement(child) && child.type === \"Card\") {\n cardChildren.push(child);\n } else {\n warn(context, \"dropped\", \"Carousel\", \"A non-card child was dropped.\");\n }\n }\n if (cardChildren.length > CAROUSEL_CARD_CAP) {\n warn(\n context,\n \"clamped\",\n \"Carousel\",\n `cards were clamped to ${CAROUSEL_CARD_CAP} entries.`,\n );\n }\n const cards = cardChildren\n .slice(0, CAROUSEL_CARD_CAP)\n .map((card) => convertCarouselCard(card, context, depth + 1))\n .filter((card): card is SlackCardBlock => card !== undefined);\n if (cards.length < CAROUSEL_CARD_MIN) {\n warn(\n context,\n \"dropped\",\n \"Carousel\",\n \"A carousel without renderable cards was dropped.\",\n );\n return [];\n }\n return [buildCarouselBlock(cards)];\n};\n\nconst toDataTableCell = (value: unknown): SlackDataTableCell | undefined => {\n if (typeof value === \"number\") {\n return { type: \"raw_number\", text: String(value) };\n }\n if (typeof value === \"string\" || typeof value === \"boolean\") {\n return { type: \"raw_text\", text: String(value) };\n }\n return undefined;\n};\n\nconst rowCharacters = (row: readonly SlackDataTableCell[]): number =>\n row.reduce((sum, cell) => sum + cell.text.length, 0);\n\nconst convertTable = (\n element: NormalizedUIElement,\n context: ConversionContext,\n): SlackBlock[] => {\n const rawColumns = Array.isArray(element.props[\"columns\"])\n ? element.props[\"columns\"]\n : [];\n const rawRows = Array.isArray(element.props[\"rows\"])\n ? element.props[\"rows\"]\n : [];\n if (rawColumns.length > DATA_TABLE_COLUMN_CAP) {\n warn(\n context,\n \"clamped\",\n \"Table\",\n `columns were clamped to ${DATA_TABLE_COLUMN_CAP} entries.`,\n );\n }\n if (rawRows.length > DATA_TABLE_ROW_CAP) {\n warn(\n context,\n \"clamped\",\n \"Table\",\n `rows were clamped to ${DATA_TABLE_ROW_CAP} entries.`,\n );\n }\n\n const columnHeaderRow: SlackDataTableCell[] = rawColumns\n .slice(0, DATA_TABLE_COLUMN_CAP)\n .filter(isRecord)\n .map((column) => ({\n type: \"raw_text\" as const,\n text: asString(column[\"label\"]),\n }));\n const dataRows: SlackDataTableCell[][] = rawRows\n .slice(0, DATA_TABLE_ROW_CAP)\n .map((row) =>\n (Array.isArray(row) ? row : [])\n .slice(0, DATA_TABLE_COLUMN_CAP)\n .map(\n (value) =>\n toDataTableCell(value) ?? { type: \"raw_text\" as const, text: \"\" },\n ),\n );\n const width = Math.max(\n columnHeaderRow.length,\n 0,\n ...dataRows.map((row) => row.length),\n );\n const padRow = (row: SlackDataTableCell[]): SlackDataTableCell[] =>\n row.length >= width\n ? row\n : [\n ...row,\n ...Array.from({ length: width - row.length }, () => ({\n type: \"raw_text\" as const,\n text: \"\",\n })),\n ];\n const headerRow = padRow(columnHeaderRow);\n\n const headerCharacters = rowCharacters(headerRow);\n if (context.dataTableCharacters + headerCharacters > DATA_TABLE_CHAR_BUDGET) {\n warn(\n context,\n \"dropped\",\n \"Table\",\n `the table was dropped because its header row exceeds the ${DATA_TABLE_CHAR_BUDGET}-character table budget.`,\n );\n return [];\n }\n\n const kept: SlackDataTableCell[][] = [headerRow];\n let tableCharacters = headerCharacters;\n let budgetClamped = false;\n for (const row of dataRows.map(padRow)) {\n const chars = rowCharacters(row);\n if (\n context.dataTableCharacters + tableCharacters + chars >\n DATA_TABLE_CHAR_BUDGET\n ) {\n budgetClamped = true;\n break;\n }\n kept.push(row);\n tableCharacters += chars;\n }\n context.dataTableCharacters += tableCharacters;\n if (budgetClamped) {\n warn(\n context,\n \"clamped\",\n \"Table\",\n `rows were clamped to fit the ${DATA_TABLE_CHAR_BUDGET}-character table budget.`,\n );\n }\n\n return [buildDataTableBlock(kept)];\n};\n\nconst convertElement = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackBlock[] => {\n const { props } = element;\n switch (element.type) {\n case \"Header\":\n return [\n {\n type: \"header\",\n text: plainText(\n clampText(\n asString(props[\"text\"]),\n HEADER_TEXT_CAP,\n \"Header\",\n \"text\",\n context,\n ),\n ),\n },\n ];\n case \"Text\":\n return [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: clampText(\n asString(props[\"value\"]),\n SECTION_TEXT_CAP,\n \"Text\",\n \"value\",\n context,\n ),\n },\n },\n ];\n case \"Caption\":\n case \"Badge\":\n return [\n {\n type: \"context\",\n elements: [\n {\n type: \"mrkdwn\",\n text: clampText(\n asString(props[\"value\"]),\n CONTEXT_TEXT_CAP,\n element.type,\n \"value\",\n context,\n ),\n },\n ],\n },\n ];\n case \"Fact\":\n return convertFacts([element], context);\n case \"Image\":\n return [\n {\n type: \"image\",\n image_url: asString(props[\"src\"]),\n alt_text: asString(props[\"alt\"]),\n },\n ];\n case \"Divider\":\n return [{ type: \"divider\" }];\n case \"Button\":\n case \"Select\":\n case \"DatePicker\":\n case \"Checkbox\":\n case \"RadioGroup\":\n return convertActions([element], context);\n case \"Input\": {\n const label = clampText(\n asString(props[\"label\"]),\n INPUT_LABEL_CAP,\n \"Input\",\n \"label\",\n context,\n );\n const placeholder = clampText(\n asString(props[\"placeholder\"]),\n PLACEHOLDER_TEXT_CAP,\n \"Input\",\n \"placeholder\",\n context,\n );\n return [\n {\n type: \"input\",\n label: plainText(label),\n element: {\n type: \"plain_text_input\",\n action_id: asActionId(element.action, \"Input\", context),\n ...(props[\"multiline\"] === true ? { multiline: true } : {}),\n ...(placeholder ? { placeholder: plainText(placeholder) } : {}),\n },\n },\n ];\n }\n case \"Alert\": {\n const tone =\n props[\"tone\"] === \"success\" ||\n props[\"tone\"] === \"warning\" ||\n props[\"tone\"] === \"danger\"\n ? props[\"tone\"]\n : \"info\";\n const titleText = asString(props[\"title\"]);\n const description = asString(props[\"description\"]);\n if (context.surface === \"modal\") {\n const combined = [titleText, description].filter(Boolean).join(\"\\n\");\n return [\n buildAlertBlock(\n clampText(combined, ALERT_TEXT_CAP, \"Alert\", \"text\", context),\n tone,\n ),\n ];\n }\n warn(\n context,\n \"fallback\",\n \"Alert\",\n \"Alert was rendered with message-surface fallback blocks.\",\n );\n return [\n ...buildAlertFallback(\n titleText,\n clampText(\n description,\n SECTION_TEXT_CAP,\n \"Alert\",\n \"description\",\n context,\n ),\n tone,\n ),\n ];\n }\n case \"Card\":\n return convertCard(element, context, depth);\n case \"Carousel\":\n return convertCarousel(element, context, depth);\n case \"Col\":\n case \"Box\":\n return convertSequence(element.children, context, depth + 1);\n case \"Row\":\n return (\n contextRow(element, context) ??\n convertSequence(element.children, context, depth + 1)\n );\n case \"Spacer\":\n case \"Icon\":\n return [];\n case \"Table\":\n return convertTable(element, context);\n case \"Markdown\": {\n const value = asString(props[\"value\"]);\n if (\n !context.markdownExhausted &&\n context.markdownCharacters + value.length <= MARKDOWN_TEXT_BUDGET\n ) {\n context.markdownCharacters += value.length;\n return [{ type: \"markdown\", text: value }];\n }\n context.markdownExhausted = true;\n warn(\n context,\n \"fallback\",\n \"Markdown\",\n `The ${MARKDOWN_TEXT_BUDGET}-character markdown budget was exceeded.`,\n );\n return [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: clampText(\n value,\n SECTION_TEXT_CAP,\n \"Markdown\",\n \"fallback text\",\n context,\n ),\n },\n },\n ];\n }\n case \"Chart\":\n warn(\n context,\n \"dropped\",\n \"Chart\",\n \"Chart was replaced by a Slack omission note.\",\n );\n return [\n {\n type: \"context\",\n elements: [{ type: \"mrkdwn\", text: \"Chart omitted on Slack.\" }],\n },\n ];\n case \"ListView\":\n return convertListView(element, context, depth);\n case \"ListViewItem\":\n return [convertListItem(element, context, depth)];\n case \"Form\":\n return [\n ...convertSequence(element.children, context, depth + 1),\n {\n type: \"actions\",\n elements: [\n buttonElement(\"Submit\", \"primary\", element.action, \"Form\", context),\n ],\n },\n ];\n default:\n warn(\n context,\n \"dropped\",\n element.type,\n \"Unknown component type was dropped.\",\n );\n return [];\n }\n};\n\nfunction convertSequence(\n node: NormalizedUINode | undefined,\n context: ConversionContext,\n depth: number,\n): SlackBlock[] {\n if (node === undefined || node === null || depth > MAX_TRAVERSAL_DEPTH) {\n return [];\n }\n const nodes = Array.isArray(node) ? node : [node];\n const blocks: SlackBlock[] = [];\n for (let index = 0; index < nodes.length;) {\n const current = nodes[index];\n if (current === undefined || current === null) {\n index += 1;\n continue;\n }\n if (isElement(current) && current.type === \"Fact\") {\n const facts: NormalizedUIElement[] = [];\n while (index < nodes.length) {\n const candidate = nodes[index];\n if (!candidate || !isElement(candidate) || candidate.type !== \"Fact\") {\n break;\n }\n facts.push(candidate);\n index += 1;\n }\n blocks.push(...convertFacts(facts, context));\n continue;\n }\n if (isElement(current) && INTERACTIVE_TYPES.has(current.type)) {\n const controls: NormalizedUIElement[] = [];\n while (index < nodes.length) {\n const candidate = nodes[index];\n if (\n !candidate ||\n !isElement(candidate) ||\n !INTERACTIVE_TYPES.has(candidate.type)\n ) {\n break;\n }\n controls.push(candidate);\n index += 1;\n }\n blocks.push(...convertActions(controls, context));\n continue;\n }\n if (typeof current === \"string\" || typeof current === \"number\") {\n blocks.push({\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: clampText(\n String(current),\n SECTION_TEXT_CAP,\n \"Text\",\n \"value\",\n context,\n ),\n },\n });\n } else if (Array.isArray(current)) {\n blocks.push(...convertSequence(current, context, depth + 1));\n } else {\n blocks.push(\n ...convertElement(current as NormalizedUIElement, context, depth + 1),\n );\n }\n index += 1;\n }\n return blocks;\n}\n\ninterface BoundState {\n remaining: number;\n exhausted: boolean;\n}\n\ntype ClampReason = \"children\" | \"budget\" | \"cycle\" | \"depth\";\n\nfunction boundNode(\n value: unknown,\n depth: number,\n onClamp: (reason: ClampReason) => void,\n state: BoundState,\n ancestors: WeakSet<object>,\n): unknown {\n if (state.remaining <= 0) {\n if (!state.exhausted) {\n state.exhausted = true;\n onClamp(\"budget\");\n }\n return null;\n }\n state.remaining -= 1;\n if (depth > MAX_TRAVERSAL_DEPTH) {\n onClamp(\"depth\");\n return null;\n }\n if (Array.isArray(value)) {\n if (ancestors.has(value)) {\n onClamp(\"cycle\");\n return null;\n }\n ancestors.add(value);\n const bounded = Array.prototype.slice.call(\n value,\n 0,\n CHILDREN_CAP,\n ) as unknown[];\n if (value.length > CHILDREN_CAP) onClamp(\"children\");\n const result = bounded.map((item) =>\n boundNode(item, depth + 1, onClamp, state, ancestors),\n );\n ancestors.delete(value);\n return result;\n }\n if (\n value !== null &&\n typeof value === \"object\" &&\n \"children\" in (value as Record<string, unknown>)\n ) {\n if (ancestors.has(value)) {\n onClamp(\"cycle\");\n return null;\n }\n ancestors.add(value);\n const record = value as Record<string, unknown>;\n const result = {\n ...record,\n children: boundNode(\n record[\"children\"],\n depth + 1,\n onClamp,\n state,\n ancestors,\n ),\n };\n ancestors.delete(value);\n return result;\n }\n return value;\n}\n\n/**\n * Produces a bounded plain copy of a raw generative-ui spec before it\n * reaches `normalizeSpec`, whose own traversal of the root array or any\n * `children` array walks the full reported length of a hostile proxied\n * array before any per-field cap downstream ever applies. Every array\n * (root, or `children` at any depth) is capped to {@link CHILDREN_CAP}\n * entries via `Array.prototype.slice`, which bounds even a proxy with a\n * fabricated `length`; recursion itself is capped at\n * {@link MAX_TRAVERSAL_DEPTH}. `onClamp` fires once per level that was\n * truncated, receiving the reason for that truncation: `\"children\"`,\n * `\"depth\"`, `\"budget\"`, or `\"cycle\"`. The walk also spends a total budget of\n * {@link NODE_BUDGET} nodes, so shared references cannot multiply work\n * exponentially, and a node that is its own ancestor is cut to `null`.\n */\nfunction boundSpec(\n spec: unknown,\n onClamp: (reason: ClampReason) => void,\n): unknown {\n return boundNode(\n spec,\n 0,\n onClamp,\n { remaining: NODE_BUDGET, exhausted: false },\n new WeakSet(),\n );\n}\n\n/** Converts a generative-UI tree into Slack Block Kit JSON and downgrade warnings. */\nexport function toSlackBlocks(\n node: unknown,\n options?: ToSlackBlocksOptions,\n): SlackBlocksResult {\n const surface = options?.surface === \"modal\" ? \"modal\" : \"message\";\n const context: ConversionContext = {\n blockCap: surface === \"modal\" ? MODAL_BLOCK_CAP : MESSAGE_BLOCK_CAP,\n surface,\n warnings: [],\n markdownCharacters: 0,\n markdownExhausted: false,\n dataTableCharacters: 0,\n };\n try {\n const bounded = boundSpec(node, (reason) => {\n const detail =\n reason === \"budget\"\n ? `the tree was truncated after ${NODE_BUDGET} nodes.`\n : reason === \"cycle\"\n ? \"a self-referencing node was dropped.\"\n : reason === \"depth\"\n ? `nodes deeper than ${MAX_TRAVERSAL_DEPTH} levels were dropped.`\n : `children were clamped to ${CHILDREN_CAP} entries.`;\n warn(context, \"clamped\", \"Root\", detail);\n });\n const { root } = normalizeSpec(bounded as never);\n const converted = convertSequence(root, context, 0);\n if (converted.length <= context.blockCap) {\n return { blocks: converted, warnings: context.warnings };\n }\n const kept = converted.slice(0, context.blockCap - 1);\n const omitted = converted.length - kept.length;\n warn(\n context,\n \"clamped\",\n \"Root\",\n `${omitted} ${omitted === 1 ? \"block was\" : \"blocks were\"} omitted.`,\n );\n return {\n blocks: [...kept, buildBlockBudgetNote(omitted)],\n warnings: context.warnings,\n };\n } catch {\n return {\n blocks: [],\n warnings: [\n {\n code: \"dropped\",\n component: \"Root\",\n detail: \"Input could not be converted.\",\n },\n ],\n };\n }\n}\n"],"mappings":";;;AAmEA,MAAM,oCAAoB,IAAI,IAAI;CAChC;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,MAAM,eAAe;AAErB,MAAM,YAAY,UAChB,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAErE,MAAM,aAAa,SACjB,SAAS,IAAI;AAEf,MAAM,YAAY,UAChB,OAAO,UAAU,WAAW,QAAQ;AAEtC,MAAM,QACJ,SACA,MACA,WACA,WACG;CACH,QAAQ,SAAS,KAAK;EAAE;EAAM;EAAW;CAAO,CAAC;AACnD;AAEA,MAAM,aACJ,OACA,KACA,WACA,OACA,YACW;CACX,IAAI,MAAM,UAAU,KAAK,OAAO;CAChC,KACE,SACA,WACA,WACA,GAAG,MAAM,kBAAkB,IAAI,aACjC;CACA,OAAO,MAAM,MAAM,GAAG,GAAG;AAC3B;AAEA,MAAM,cACJ,QACA,WACA,YAEA,UACE,SAAS,MAAM,KAAK,OAAO,OAAO,YAAY,WAC1C,OAAO,UACP,UAAA,KAEJ,WACA,aACA,OACF;AAEF,MAAM,aAAa,UAAkC;CACnD,MAAM;CACN;AACF;AAEA,MAAM,eAAe,WAAwC;CAC3D,IAAI,CAAC,SAAS,MAAM,GAAG,OAAO,KAAA;CAC9B,MAAM,EAAE,MAAM,OAAO,GAAG,YAAY;CACpC,IAAI,OAAO,KAAK,OAAO,CAAC,CAAC,WAAW,GAAG,OAAO,KAAA;CAC9C,IAAI;EACF,MAAM,aAAa,KAAK,UAAU,OAAO;EACzC,OAAO,eAAe,OAAO,KAAA,IAAY;CAC3C,QAAQ;EACN;CACF;AACF;AAEA,MAAM,iBACJ,OACA,aACA,QACA,WACA,YACuB;CAEvB,IAAI,QADoB,YAAY,MACV;CAC1B,IAAI,UAAU,KAAA,KAAa,MAAM,SAAA,KAA2B;EAC1D,KACE,SACA,WACA,WACA,2EACF;EACA,QAAQ,KAAA;CACV;CACA,MAAM,QACJ,gBAAgB,aAAa,gBAAgB,WACzC,cACA,KAAA;CACN,OAAO;EACL,MAAM;EACN,MAAM,UACJ,UAAU,OAAA,IAA6B,WAAW,SAAS,OAAO,CACpE;EACA,WAAW,WAAW,QAAQ,WAAW,OAAO;EAChD,GAAI,UAAU,KAAA,IAAY,EAAE,MAAM,IAAI,CAAC;EACvC,GAAI,UAAU,KAAA,IAAY,EAAE,MAAM,IAAI,CAAC;CACzC;AACF;AAEA,MAAM,cACJ,OACA,WACA,YAC4B;CAC5B,IACE,CAAC,SAAS,KAAK,KACf,OAAO,MAAM,aAAa,YAC1B,OAAO,MAAM,aAAa,UAE1B;CAEF,OAAO;EACL,MAAM,UACJ,UACE,MAAM,UAAA,IAEN,WACA,gBACA,OACF,CACF;EACA,OAAO,MAAM;CACf;AACF;AAEA,MAAM,mBACJ,SACA,YACmC;CACnC,MAAM,EAAE,OAAO,WAAW;CAC1B,QAAQ,QAAQ,MAAhB;EACE,KAAK,UACH,OAAO,cACL,SAAS,MAAM,QAAQ,GACvB,MAAM,gBACN,QACA,UACA,OACF;EACF,KAAK,UAAU;GACb,MAAM,aAAa,MAAM,QAAQ,MAAM,UAAU,IAC7C,MAAM,aACN,CAAC;GACL,IAAI,WAAW,SAAA,KACb,KACE,SACA,WACA,UACA,sCACF;GAEF,MAAM,UAAU,WACb,MAAM,GAAA,GAAoB,CAAC,CAC3B,KAAK,WAAW,WAAW,QAAQ,UAAU,OAAO,CAAC,CAAC,CACtD,QAAQ,WAAkC,WAAW,KAAA,CAAS;GACjE,MAAM,cAAc,UAClB,SAAS,MAAM,cAAc,GAAA,KAE7B,UACA,eACA,OACF;GACA,OAAO;IACL,MAAM;IACN,WAAW,WAAW,QAAQ,UAAU,OAAO;IAC/C;IACA,GAAI,cAAc,EAAE,aAAa,UAAU,WAAW,EAAE,IAAI,CAAC;GAC/D;EACF;EACA,KAAK,cAAc;GACjB,MAAM,UAAU,SAAS,MAAM,QAAQ;GACvC,MAAM,cAAc,aAAa,KAAK,OAAO,IAAI,UAAU,KAAA;GAC3D,IAAI,WAAW,gBAAgB,KAAA,GAC7B,KACE,SACA,WACA,cACA,4DACF;GAEF,OAAO;IACL,MAAM;IACN,WAAW,WAAW,QAAQ,cAAc,OAAO;IACnD,GAAI,gBAAgB,KAAA,IAAY,EAAE,cAAc,YAAY,IAAI,CAAC;GACnE;EACF;EACA,KAAK,YAAY;GACf,MAAM,QAAQ,UACZ,SAAS,MAAM,QAAQ,GAAA,IAEvB,YACA,SACA,OACF;GACA,MAAM,OAAO,SAAS,MAAM,OAAO;GACnC,MAAM,SAAsB;IAC1B,MAAM,UAAU,KAAK;IACrB,OAAO,QAAQ;GACjB;GACA,OAAO;IACL,MAAM;IACN,WAAW,WAAW,QAAQ,YAAY,OAAO;IACjD,SAAS,CAAC,MAAM;IAChB,GAAI,MAAM,sBAAsB,OAC5B,EAAE,iBAAiB,CAAC,MAAM,EAAE,IAC5B,CAAC;GACP;EACF;EACA,KAAK,cAAc;GACjB,MAAM,aAAa,MAAM,QAAQ,MAAM,UAAU,IAC7C,MAAM,aACN,CAAC;GACL,IAAI,WAAW,SAAA,IACb,KACE,SACA,WACA,cACA,qCACF;GAEF,MAAM,UAAU,WACb,MAAM,GAAA,EAAmB,CAAC,CAC1B,KAAK,WAAW,WAAW,QAAQ,cAAc,OAAO,CAAC,CAAC,CAC1D,QAAQ,WAAkC,WAAW,KAAA,CAAS;GACjE,MAAM,gBACJ,OAAO,MAAM,aAAa,WACtB,MAAM,WACN,SAAS,MAAM,eAAe;GACpC,MAAM,gBAAgB,QAAQ,MAC3B,WAAW,OAAO,UAAU,aAC/B;GACA,OAAO;IACL,MAAM;IACN,WAAW,WAAW,QAAQ,cAAc,OAAO;IACnD;IACA,GAAI,kBAAkB,KAAA,IAClB,EAAE,gBAAgB,cAAc,IAChC,CAAC;GACP;EACF;EACA,SACE;CACJ;AACF;AAEA,MAAM,kBACJ,SACuB;CACvB,IAAI,SAAS,KAAA,KAAa,SAAS,MAAM,OAAO,CAAC;CACjD,IAAI,CAAC,MAAM,QAAQ,IAAI,GAAG,OAAO,CAAC,IAAI;CACtC,OAAO,KAAK,SAAS,UAAU,eAAe,KAAK,CAAC;AACtD;AAEA,MAAM,gBACJ,OACA,YACiB;CACjB,MAAM,SAAS,MAAM,KAAK,SAAS;EACjC,MAAM,QAAQ,SAAS,KAAK,MAAM,QAAQ;EAC1C,MAAM,QAAQ,SAAS,KAAK,MAAM,QAAQ;EAC1C,OAAO;GACL,MAAM;GACN,MAAM,UACJ,IAAI,MAAM,KAAK,SACf,qBACA,QACA,SACA,OACF;EACF;CACF,CAAC;CACD,MAAM,SAAuB,CAAC;CAC9B,KAAK,IAAI,QAAQ,GAAG,QAAQ,OAAO,QAAQ,SAAA,IACzC,OAAO,KAAK;EACV,MAAM;EACN,QAAQ,OAAO,MAAM,OAAO,QAAA,EAAsB;CACpD,CAAC;CAEH,OAAO;AACT;AAEA,MAAM,kBACJ,UACA,YACiB;CACjB,MAAM,YAAY,SACf,KAAK,YAAY,gBAAgB,SAAS,OAAO,CAAC,CAAC,CACnD,QAAQ,YAA2C,YAAY,KAAA,CAAS;CAC3E,MAAM,SAAuB,CAAC;CAC9B,KAAK,IAAI,QAAQ,GAAG,QAAQ,UAAU,QAAQ,SAAA,IAC5C,OAAO,KAAK;EACV,MAAM;EACN,UAAU,UAAU,MAAM,OAAO,QAAA,EAA2B;CAC9D,CAAC;CAEH,OAAO;AACT;AAEA,MAAM,qBACJ,OACA,YACyB;CACzB,MAAM,UAAgC,CAAC;CACvC,KAAK,MAAM,OAAO,CAAC,WAAW,QAAQ,GAAY;EAChD,MAAM,SAAS,MAAM;EACrB,IAAI,CAAC,SAAS,MAAM,GAAG;EACvB,QAAQ,KACN,cACE,SAAS,OAAO,QAAQ,GACxB,QAAQ,YAAY,YAAY,KAAA,GAChC,OAAO,YACP,QACA,OACF,CACF;CACF;CACA,OAAO;AACT;AAEA,MAAM,iBACJ,OACA,YACuB;CACvB,MAAM,WAAW,SAAS,MAAM,QAAQ;CACxC,OAAO,WACH,UAAU,UAAA,KAA0B,QAAQ,SAAS,OAAO,IAC5D,KAAA;AACN;AAEA,MAAM,cAAc,SAClB,UAAU,IAAI,MAAM,KAAK,SAAS,YAAY,KAAK,SAAS;AAE9D,MAAM,eACJ,MACA,UACa;CACb,IAAI,SAAS,KAAA,KAAa,SAAS,QAAQ,QAAA,IACzC,OAAO,CAAC;CAEV,IAAI,OAAO,SAAS,YAAY,OAAO,SAAS,UAC9C,OAAO,CAAC,OAAO,IAAI,CAAC;CAEtB,IAAI,MAAM,QAAQ,IAAI,GACpB,OAAO,KAAK,SAAS,UAAU,YAAY,OAAO,QAAQ,CAAC,CAAC;CAE9D,MAAM,UAAU;CAIhB,OAAO,CAAC,GAHI;EAAC;EAAQ;EAAS;EAAS;EAAS;CAAa,CAAC,CAC3D,KAAK,QAAQ,QAAQ,MAAM,IAAI,CAAC,CAChC,QAAQ,UAA2B,OAAO,UAAU,QAC1C,GAAG,GAAG,YAAY,QAAQ,UAAU,QAAQ,CAAC,CAAC;AAC7D;;;;;;;AAeA,MAAM,qBAAqB,YAA6C;CACtE,MAAM,WAAW,eAAe,QAAQ,QAAQ;CAChD,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,MAAM,WAA+B,CAAC;CACtC,KAAK,MAAM,SAAS,UAAU;EAC5B,IAAI,cAAc,KAAA,KAAa,UAAU,KAAK,KAAK,MAAM,SAAS,SAAS;GACzE,YAAY;IACV,MAAM;IACN,WAAW,SAAS,MAAM,MAAM,MAAM;IACtC,UAAU,SAAS,MAAM,MAAM,MAAM;GACvC;GACA;EACF;EACA,IACE,YAAY,KAAA,KACZ,UAAU,KAAK,MACd,MAAM,SAAS,UAAU,MAAM,SAAS,aACzC;GACA,UAAU,SAAS,MAAM,MAAM,QAAQ;GACvC;EACF;EACA,IACE,eAAe,KAAA,KACf,UAAU,KAAK,KACf,MAAM,SAAS,WACf;GACA,aAAa,SAAS,MAAM,MAAM,QAAQ;GAC1C;EACF;EACA,IAAI,WAAW,KAAK,GAAG;EACvB,SAAS,KAAK,KAAK;CACrB;CACA,OAAO;EAAE;EAAW;EAAS;EAAY;CAAS;AACpD;;AAGA,MAAM,qBACJ,WACA,QACA,SACA,YACmB;CACnB,MAAM,OACJ,OAAO,YAAY,KAAA,IACf;EACE,MAAM;EACN,MAAM,UACJ,OAAO,SAAA,KAEP,QACA,QACA,OACF;CACF,IACA,KAAA;CACN,MAAM,UACJ,OAAO,eAAe,KAAA,IAClB;EACE,MAAM;EACN,MAAM,UACJ,OAAO,YAAA,KAEP,QACA,WACA,OACF;CACF,IACA,KAAA;CACN,IAAI,QAAQ,SAAA,GACV,KACE,SACA,WACA,QACA,oCACF;CAEF,OAAO,eAAe;EACpB,GAAI,OAAO,cAAc,KAAA,IAAY,EAAE,WAAW,OAAO,UAAU,IAAI,CAAC;EACxE,GAAI,cAAc,KAAA,IACd,EAAE,OAAO;GAAE,MAAM;GAAU,MAAM;EAAU,EAAE,IAC7C,CAAC;EACL,GAAI,SAAS,KAAA,IAAY,EAAE,KAAK,IAAI,CAAC;EACrC,GAAI,YAAY,KAAA,IAAY,EAAE,QAAQ,IAAI,CAAC;EAC3C,GAAI,QAAQ,SAAS,IACjB,EAAE,SAAS,QAAQ,MAAM,GAAA,CAAmB,EAAE,IAC9C,CAAC;CACP,CAAC;AACH;;;;;;AAOA,MAAM,eAAe,SACnB,KAAK,eAAe,KAAA,KACpB,KAAK,UAAU,KAAA,KACf,KAAK,SAAS,KAAA,MACb,KAAK,YAAY,KAAA,KAAa,KAAK,QAAQ,WAAW;AAEzD,MAAM,eACJ,SACA,SACA,UACiB;CACjB,MAAM,YAAY,cAAc,QAAQ,OAAO,OAAO;CACtD,MAAM,SAAS,kBAAkB,OAAO;CACxC,MAAM,UAAU,kBAAkB,QAAQ,OAAO,OAAO;CAExD,IAAI,OAAO,SAAS,SAAS,GAAG;EAC9B,KACE,SACA,YACA,QACA,wFACF;EACA,OAAO;GACL,GAAI,YACA,CAAC;IAAE,MAAM;IAAmB,MAAM,UAAU,SAAS;GAAE,CAAC,IACxD,CAAC;GACL,GAAG,gBAAgB,QAAQ,UAAU,SAAS,QAAQ,CAAC;GACvD,GAAI,QAAQ,SAAS,IACjB,CAAC;IAAE,MAAM;IAAoB,UAAU;GAAQ,CAAC,IAChD,CAAC;EACP;CACF;CACA,MAAM,OAAO,kBAAkB,WAAW,QAAQ,SAAS,OAAO;CAClE,IAAI,YAAY,IAAI,GAAG;EACrB,KACE,SACA,WACA,QACA,sEACF;EACA,OAAO,CAAC;CACV;CACA,OAAO,CAAC,IAAI;AACd;;;;;;AAOA,MAAM,eACJ,SACA,SACA,UACmB;CACnB,MAAM,WAAW,SAAS,QAAQ,MAAM,QAAQ;CAChD,MAAM,aAAa,YAAY,QAAQ,UAAU,QAAQ,CAAC;CAC1D,MAAM,cAAc,YAAY,WAAW,MAAM;CACjD,MAAM,aAAa,WAAW,aAAa,WAAW,MAAM,CAAC;CAC7D,MAAM,YAAY,YAAY,MAAM,GAAA,GAAiB;CACrD,MAAM,WAAW,WAAW,KAAK,IAAI,CAAC,CAAC,MAAM,GAAA,GAAgB;CAC7D,KACE,SACA,WACA,QACA,0DACF;CACA,OAAO,eAAe;EACpB,GAAI,YAAY,EAAE,OAAO;GAAE,MAAM;GAAU,MAAM;EAAU,EAAE,IAAI,CAAC;EAClE,GAAI,WAAW,EAAE,MAAM;GAAE,MAAM;GAAU,MAAM;EAAS,EAAE,IAAI,CAAC;CACjE,CAAC;AACH;AAEA,MAAM,uBACJ,SACA,SACA,UAC+B;CAC/B,MAAM,SAAS,kBAAkB,OAAO;CACxC,MAAM,OACJ,OAAO,SAAS,WAAW,IACvB,kBACE,cAAc,QAAQ,OAAO,OAAO,GACpC,QACA,kBAAkB,QAAQ,OAAO,OAAO,GACxC,OACF,IACA,YAAY,SAAS,SAAS,KAAK;CACzC,IAAI,YAAY,IAAI,GAAG;EACrB,KACE,SACA,WACA,QACA,sEACF;EACA;CACF;CACA,OAAO;AACT;AAEA,MAAM,oBACJ,SAEA,UAAU,IAAI,MAAM,KAAK,SAAS,WAAW,KAAK,SAAS;AAE7D,MAAM,cACJ,SACA,YAC6B;CAC7B,MAAM,cAAc,eAAe,QAAQ,QAAQ;CACnD,MAAM,WAAW,YAAY,OAAO,gBAAgB;CACpD,IAAI,YAAY,WAAW,KAAK,SAAS,WAAW,YAAY,QAC9D;CAEF,IAAI,SAAS,SAAA,IACX,KACE,SACA,WACA,OACA,8CACF;CAEF,OAAO,CACL;EACE,MAAM;EACN,UAAU,SAAS,MAAM,GAAA,EAAsB,CAAC,CAAC,KAAK,WAAW;GAC/D,MAAM;GACN,MAAM,UACJ,SAAS,MAAM,MAAM,QAAQ,GAC7B,kBACA,MAAM,MACN,SACA,OACF;EACF,EAAE;CACJ,CACF;AACF;AAEA,MAAM,mBACJ,SACA,SACA,UACe;CACf,MAAM,OAAO,UACX,YAAY,QAAQ,UAAU,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI,GAClD,kBACA,gBACA,QACA,OACF;CACA,MAAM,YAAY,SAAS,QAAQ,MAAM,IACrC,cAAc,QAAQ,KAAA,GAAW,QAAQ,QAAQ,gBAAgB,OAAO,IACxE,KAAA;CACJ,OAAO;EACL,MAAM;EACN,MAAM;GAAE,MAAM;GAAU;EAAK;EAC7B,GAAI,cAAc,KAAA,IAAY,EAAE,UAAU,IAAI,CAAC;CACjD;AACF;AAEA,MAAM,mBACJ,SACA,SACA,UACiB;CAKjB,OAJc,eAAe,QAAQ,QAAQ,CAAC,CAAC,QAC5C,UACC,UAAU,KAAK,KAAK,MAAM,SAAS,cAE5B,CAAC,CAAC,SAAS,MAAM,UAAU,CACpC,GAAI,QAAQ,IAAI,CAAC,EAAE,MAAM,UAAmB,CAAC,IAAI,CAAC,GAClD,gBAAgB,MAAM,SAAS,QAAQ,CAAC,CAC1C,CAAC;AACH;AAEA,MAAM,mBACJ,SACA,SACA,UACiB;CACjB,MAAM,eAAsC,CAAC;CAC7C,KAAK,MAAM,SAAS,eAAe,QAAQ,QAAQ,GACjD,IAAI,UAAU,KAAK,KAAK,MAAM,SAAS,QACrC,aAAa,KAAK,KAAK;MAEvB,KAAK,SAAS,WAAW,YAAY,+BAA+B;CAGxE,IAAI,aAAa,SAAA,IACf,KACE,SACA,WACA,YACA,mCACF;CAEF,MAAM,QAAQ,aACX,MAAM,GAAA,EAAoB,CAAC,CAC3B,KAAK,SAAS,oBAAoB,MAAM,SAAS,QAAQ,CAAC,CAAC,CAAC,CAC5D,QAAQ,SAAiC,SAAS,KAAA,CAAS;CAC9D,IAAI,MAAM,SAAA,GAA4B;EACpC,KACE,SACA,WACA,YACA,kDACF;EACA,OAAO,CAAC;CACV;CACA,OAAO,CAAC,mBAAmB,KAAK,CAAC;AACnC;AAEA,MAAM,mBAAmB,UAAmD;CAC1E,IAAI,OAAO,UAAU,UACnB,OAAO;EAAE,MAAM;EAAc,MAAM,OAAO,KAAK;CAAE;CAEnD,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,WAChD,OAAO;EAAE,MAAM;EAAY,MAAM,OAAO,KAAK;CAAE;AAGnD;AAEA,MAAM,iBAAiB,QACrB,IAAI,QAAQ,KAAK,SAAS,MAAM,KAAK,KAAK,QAAQ,CAAC;AAErD,MAAM,gBACJ,SACA,YACiB;CACjB,MAAM,aAAa,MAAM,QAAQ,QAAQ,MAAM,UAAU,IACrD,QAAQ,MAAM,aACd,CAAC;CACL,MAAM,UAAU,MAAM,QAAQ,QAAQ,MAAM,OAAO,IAC/C,QAAQ,MAAM,UACd,CAAC;CACL,IAAI,WAAW,SAAA,IACb,KACE,SACA,WACA,SACA,qCACF;CAEF,IAAI,QAAQ,SAAA,KACV,KACE,SACA,WACA,SACA,mCACF;CAGF,MAAM,kBAAwC,WAC3C,MAAM,GAAA,EAAwB,CAAC,CAC/B,OAAO,QAAQ,CAAC,CAChB,KAAK,YAAY;EAChB,MAAM;EACN,MAAM,SAAS,OAAO,QAAQ;CAChC,EAAE;CACJ,MAAM,WAAmC,QACtC,MAAM,GAAA,GAAqB,CAAC,CAC5B,KAAK,SACH,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,EAAA,CAC1B,MAAM,GAAA,EAAwB,CAAC,CAC/B,KACE,UACC,gBAAgB,KAAK,KAAK;EAAE,MAAM;EAAqB,MAAM;CAAG,CACpE,CACJ;CACF,MAAM,QAAQ,KAAK,IACjB,gBAAgB,QAChB,GACA,GAAG,SAAS,KAAK,QAAQ,IAAI,MAAM,CACrC;CACA,MAAM,UAAU,QACd,IAAI,UAAU,QACV,MACA,CACE,GAAG,KACH,GAAG,MAAM,KAAK,EAAE,QAAQ,QAAQ,IAAI,OAAO,UAAU;EACnD,MAAM;EACN,MAAM;CACR,EAAE,CACJ;CACN,MAAM,YAAY,OAAO,eAAe;CAExC,MAAM,mBAAmB,cAAc,SAAS;CAChD,IAAI,QAAQ,sBAAsB,mBAAA,KAA2C;EAC3E,KACE,SACA,WACA,SACA,4DAA4D,uBAAuB,yBACrF;EACA,OAAO,CAAC;CACV;CAEA,MAAM,OAA+B,CAAC,SAAS;CAC/C,IAAI,kBAAkB;CACtB,IAAI,gBAAgB;CACpB,KAAK,MAAM,OAAO,SAAS,IAAI,MAAM,GAAG;EACtC,MAAM,QAAQ,cAAc,GAAG;EAC/B,IACE,QAAQ,sBAAsB,kBAAkB,QAAA,KAEhD;GACA,gBAAgB;GAChB;EACF;EACA,KAAK,KAAK,GAAG;EACb,mBAAmB;CACrB;CACA,QAAQ,uBAAuB;CAC/B,IAAI,eACF,KACE,SACA,WACA,SACA,gCAAgC,uBAAuB,yBACzD;CAGF,OAAO,CAAC,oBAAoB,IAAI,CAAC;AACnC;AAEA,MAAM,kBACJ,SACA,SACA,UACiB;CACjB,MAAM,EAAE,UAAU;CAClB,QAAQ,QAAQ,MAAhB;EACE,KAAK,UACH,OAAO,CACL;GACE,MAAM;GACN,MAAM,UACJ,UACE,SAAS,MAAM,OAAO,GAAA,KAEtB,UACA,QACA,OACF,CACF;EACF,CACF;EACF,KAAK,QACH,OAAO,CACL;GACE,MAAM;GACN,MAAM;IACJ,MAAM;IACN,MAAM,UACJ,SAAS,MAAM,QAAQ,GACvB,kBACA,QACA,SACA,OACF;GACF;EACF,CACF;EACF,KAAK;EACL,KAAK,SACH,OAAO,CACL;GACE,MAAM;GACN,UAAU,CACR;IACE,MAAM;IACN,MAAM,UACJ,SAAS,MAAM,QAAQ,GACvB,kBACA,QAAQ,MACR,SACA,OACF;GACF,CACF;EACF,CACF;EACF,KAAK,QACH,OAAO,aAAa,CAAC,OAAO,GAAG,OAAO;EACxC,KAAK,SACH,OAAO,CACL;GACE,MAAM;GACN,WAAW,SAAS,MAAM,MAAM;GAChC,UAAU,SAAS,MAAM,MAAM;EACjC,CACF;EACF,KAAK,WACH,OAAO,CAAC,EAAE,MAAM,UAAU,CAAC;EAC7B,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,cACH,OAAO,eAAe,CAAC,OAAO,GAAG,OAAO;EAC1C,KAAK,SAAS;GACZ,MAAM,QAAQ,UACZ,SAAS,MAAM,QAAQ,GACvB,iBACA,SACA,SACA,OACF;GACA,MAAM,cAAc,UAClB,SAAS,MAAM,cAAc,GAAA,KAE7B,SACA,eACA,OACF;GACA,OAAO,CACL;IACE,MAAM;IACN,OAAO,UAAU,KAAK;IACtB,SAAS;KACP,MAAM;KACN,WAAW,WAAW,QAAQ,QAAQ,SAAS,OAAO;KACtD,GAAI,MAAM,iBAAiB,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;KACzD,GAAI,cAAc,EAAE,aAAa,UAAU,WAAW,EAAE,IAAI,CAAC;IAC/D;GACF,CACF;EACF;EACA,KAAK,SAAS;GACZ,MAAM,OACJ,MAAM,YAAY,aAClB,MAAM,YAAY,aAClB,MAAM,YAAY,WACd,MAAM,UACN;GACN,MAAM,YAAY,SAAS,MAAM,QAAQ;GACzC,MAAM,cAAc,SAAS,MAAM,cAAc;GACjD,IAAI,QAAQ,YAAY,SAAS;IAC/B,MAAM,WAAW,CAAC,WAAW,WAAW,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,IAAI;IACnE,OAAO,CACL,gBACE,UAAU,UAAA,KAA0B,SAAS,QAAQ,OAAO,GAC5D,IACF,CACF;GACF;GACA,KACE,SACA,YACA,SACA,0DACF;GACA,OAAO,CACL,GAAG,mBACD,WACA,UACE,aACA,kBACA,SACA,eACA,OACF,GACA,IACF,CACF;EACF;EACA,KAAK,QACH,OAAO,YAAY,SAAS,SAAS,KAAK;EAC5C,KAAK,YACH,OAAO,gBAAgB,SAAS,SAAS,KAAK;EAChD,KAAK;EACL,KAAK,OACH,OAAO,gBAAgB,QAAQ,UAAU,SAAS,QAAQ,CAAC;EAC7D,KAAK,OACH,OACE,WAAW,SAAS,OAAO,KAC3B,gBAAgB,QAAQ,UAAU,SAAS,QAAQ,CAAC;EAExD,KAAK;EACL,KAAK,QACH,OAAO,CAAC;EACV,KAAK,SACH,OAAO,aAAa,SAAS,OAAO;EACtC,KAAK,YAAY;GACf,MAAM,QAAQ,SAAS,MAAM,QAAQ;GACrC,IACE,CAAC,QAAQ,qBACT,QAAQ,qBAAqB,MAAM,UAAA,MACnC;IACA,QAAQ,sBAAsB,MAAM;IACpC,OAAO,CAAC;KAAE,MAAM;KAAY,MAAM;IAAM,CAAC;GAC3C;GACA,QAAQ,oBAAoB;GAC5B,KACE,SACA,YACA,YACA,OAAO,qBAAqB,yCAC9B;GACA,OAAO,CACL;IACE,MAAM;IACN,MAAM;KACJ,MAAM;KACN,MAAM,UACJ,OACA,kBACA,YACA,iBACA,OACF;IACF;GACF,CACF;EACF;EACA,KAAK;GACH,KACE,SACA,WACA,SACA,8CACF;GACA,OAAO,CACL;IACE,MAAM;IACN,UAAU,CAAC;KAAE,MAAM;KAAU,MAAM;IAA0B,CAAC;GAChE,CACF;EACF,KAAK,YACH,OAAO,gBAAgB,SAAS,SAAS,KAAK;EAChD,KAAK,gBACH,OAAO,CAAC,gBAAgB,SAAS,SAAS,KAAK,CAAC;EAClD,KAAK,QACH,OAAO,CACL,GAAG,gBAAgB,QAAQ,UAAU,SAAS,QAAQ,CAAC,GACvD;GACE,MAAM;GACN,UAAU,CACR,cAAc,UAAU,WAAW,QAAQ,QAAQ,QAAQ,OAAO,CACpE;EACF,CACF;EACF;GACE,KACE,SACA,WACA,QAAQ,MACR,qCACF;GACA,OAAO,CAAC;CACZ;AACF;AAEA,SAAS,gBACP,MACA,SACA,OACc;CACd,IAAI,SAAS,KAAA,KAAa,SAAS,QAAQ,QAAA,IACzC,OAAO,CAAC;CAEV,MAAM,QAAQ,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,IAAI;CAChD,MAAM,SAAuB,CAAC;CAC9B,KAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,SAAS;EACzC,MAAM,UAAU,MAAM;EACtB,IAAI,YAAY,KAAA,KAAa,YAAY,MAAM;GAC7C,SAAS;GACT;EACF;EACA,IAAI,UAAU,OAAO,KAAK,QAAQ,SAAS,QAAQ;GACjD,MAAM,QAA+B,CAAC;GACtC,OAAO,QAAQ,MAAM,QAAQ;IAC3B,MAAM,YAAY,MAAM;IACxB,IAAI,CAAC,aAAa,CAAC,UAAU,SAAS,KAAK,UAAU,SAAS,QAC5D;IAEF,MAAM,KAAK,SAAS;IACpB,SAAS;GACX;GACA,OAAO,KAAK,GAAG,aAAa,OAAO,OAAO,CAAC;GAC3C;EACF;EACA,IAAI,UAAU,OAAO,KAAK,kBAAkB,IAAI,QAAQ,IAAI,GAAG;GAC7D,MAAM,WAAkC,CAAC;GACzC,OAAO,QAAQ,MAAM,QAAQ;IAC3B,MAAM,YAAY,MAAM;IACxB,IACE,CAAC,aACD,CAAC,UAAU,SAAS,KACpB,CAAC,kBAAkB,IAAI,UAAU,IAAI,GAErC;IAEF,SAAS,KAAK,SAAS;IACvB,SAAS;GACX;GACA,OAAO,KAAK,GAAG,eAAe,UAAU,OAAO,CAAC;GAChD;EACF;EACA,IAAI,OAAO,YAAY,YAAY,OAAO,YAAY,UACpD,OAAO,KAAK;GACV,MAAM;GACN,MAAM;IACJ,MAAM;IACN,MAAM,UACJ,OAAO,OAAO,GACd,kBACA,QACA,SACA,OACF;GACF;EACF,CAAC;OACI,IAAI,MAAM,QAAQ,OAAO,GAC9B,OAAO,KAAK,GAAG,gBAAgB,SAAS,SAAS,QAAQ,CAAC,CAAC;OAE3D,OAAO,KACL,GAAG,eAAe,SAAgC,SAAS,QAAQ,CAAC,CACtE;EAEF,SAAS;CACX;CACA,OAAO;AACT;AASA,SAAS,UACP,OACA,OACA,SACA,OACA,WACS;CACT,IAAI,MAAM,aAAa,GAAG;EACxB,IAAI,CAAC,MAAM,WAAW;GACpB,MAAM,YAAY;GAClB,QAAQ,QAAQ;EAClB;EACA,OAAO;CACT;CACA,MAAM,aAAa;CACnB,IAAI,QAAA,IAA6B;EAC/B,QAAQ,OAAO;EACf,OAAO;CACT;CACA,IAAI,MAAM,QAAQ,KAAK,GAAG;EACxB,IAAI,UAAU,IAAI,KAAK,GAAG;GACxB,QAAQ,OAAO;GACf,OAAO;EACT;EACA,UAAU,IAAI,KAAK;EACnB,MAAM,UAAU,MAAM,UAAU,MAAM,KACpC,OACA,GAAA,GAEF;EACA,IAAI,MAAM,SAAA,KAAuB,QAAQ,UAAU;EACnD,MAAM,SAAS,QAAQ,KAAK,SAC1B,UAAU,MAAM,QAAQ,GAAG,SAAS,OAAO,SAAS,CACtD;EACA,UAAU,OAAO,KAAK;EACtB,OAAO;CACT;CACA,IACE,UAAU,QACV,OAAO,UAAU,YACjB,cAAe,OACf;EACA,IAAI,UAAU,IAAI,KAAK,GAAG;GACxB,QAAQ,OAAO;GACf,OAAO;EACT;EACA,UAAU,IAAI,KAAK;EACnB,MAAM,SAAS;EACf,MAAM,SAAS;GACb,GAAG;GACH,UAAU,UACR,OAAO,aACP,QAAQ,GACR,SACA,OACA,SACF;EACF;EACA,UAAU,OAAO,KAAK;EACtB,OAAO;CACT;CACA,OAAO;AACT;;;;;;;;;;;;;;;AAgBA,SAAS,UACP,MACA,SACS;CACT,OAAO,UACL,MACA,GACA,SACA;EAAE,WAAW;EAAa,WAAW;CAAM,mBAC3C,IAAI,QAAQ,CACd;AACF;;AAGA,SAAgB,cACd,MACA,SACmB;CACnB,MAAM,UAAU,SAAS,YAAY,UAAU,UAAU;CACzD,MAAM,UAA6B;EACjC,UAAU,YAAY,UAAA,MAAA;EACtB;EACA,UAAU,CAAC;EACX,oBAAoB;EACpB,mBAAmB;EACnB,qBAAqB;CACvB;CACA,IAAI;EACF,MAAM,UAAU,UAAU,OAAO,WAAW;GAC1C,MAAM,SACJ,WAAW,WACP,gCAAgC,YAAY,WAC5C,WAAW,UACT,yCACA,WAAW,UACT,8CACA;GACV,KAAK,SAAS,WAAW,QAAQ,MAAM;EACzC,CAAC;EACD,MAAM,EAAE,SAAS,cAAc,OAAgB;EAC/C,MAAM,YAAY,gBAAgB,MAAM,SAAS,CAAC;EAClD,IAAI,UAAU,UAAU,QAAQ,UAC9B,OAAO;GAAE,QAAQ;GAAW,UAAU,QAAQ;EAAS;EAEzD,MAAM,OAAO,UAAU,MAAM,GAAG,QAAQ,WAAW,CAAC;EACpD,MAAM,UAAU,UAAU,SAAS,KAAK;EACxC,KACE,SACA,WACA,QACA,GAAG,QAAQ,GAAG,YAAY,IAAI,cAAc,cAAc,UAC5D;EACA,OAAO;GACL,QAAQ,CAAC,GAAG,MAAM,qBAAqB,OAAO,CAAC;GAC/C,UAAU,QAAQ;EACpB;CACF,QAAQ;EACN,OAAO;GACL,QAAQ,CAAC;GACT,UAAU,CACR;IACE,MAAM;IACN,WAAW;IACX,QAAQ;GACV,CACF;EACF;CACF;AACF"}
1
+ {"version":3,"file":"toSlackBlocks.js","names":[],"sources":["../../src/slack/toSlackBlocks.ts"],"sourcesContent":["import {\n normalizeSpec,\n type NormalizedUIElement,\n type NormalizedUINode,\n} from \"../ir\";\nimport {\n ACTION_ID_CAP,\n ACTIONS_ELEMENT_CAP,\n ALERT_TEXT_CAP,\n BUTTON_VALUE_CAP,\n CARD_ACTIONS_CAP,\n CARD_BODY_CAP,\n CARD_SUBTEXT_CAP,\n CARD_TITLE_CAP,\n CAROUSEL_CARD_CAP,\n CAROUSEL_CARD_MIN,\n CHILDREN_CAP,\n CONTEXT_ELEMENT_CAP,\n CONTEXT_TEXT_CAP,\n DATA_TABLE_CHAR_BUDGET,\n DATA_TABLE_COLUMN_CAP,\n DATA_TABLE_ROW_CAP,\n FACT_FIELD_CAP,\n FACT_FIELD_TEXT_CAP,\n HEADER_TEXT_CAP,\n INPUT_LABEL_CAP,\n INTERACTIVE_TEXT_CAP,\n MARKDOWN_TEXT_BUDGET,\n MAX_ELEMENT_DEPTH,\n MAX_TRAVERSAL_DEPTH,\n MESSAGE_BLOCK_CAP,\n MODAL_BLOCK_CAP,\n NODE_BUDGET,\n PLACEHOLDER_TEXT_CAP,\n RADIO_OPTION_CAP,\n SECTION_TEXT_CAP,\n SELECT_OPTION_CAP,\n buildAlertBlock,\n buildAlertFallback,\n buildBlockBudgetNote,\n buildCardBlock,\n buildCarouselBlock,\n buildDataTableBlock,\n} from \"./constants\";\nimport type {\n SlackActionElement,\n SlackBlock,\n SlackButtonElement,\n SlackCardBlock,\n SlackConversionWarning,\n SlackDataTableCell,\n SlackImageBlock,\n SlackOption,\n SlackPlainText,\n SlackTextObject,\n SlackBlocksResult,\n ToSlackBlocksOptions,\n} from \"./types\";\n\ntype ConversionContext = {\n readonly blockCap: number;\n readonly surface: \"message\" | \"modal\";\n readonly warnings: SlackConversionWarning[];\n markdownCharacters: number;\n markdownExhausted: boolean;\n dataTableCharacters: number;\n};\n\nconst INTERACTIVE_TYPES = new Set([\n \"Button\",\n \"Select\",\n \"DatePicker\",\n \"Checkbox\",\n \"RadioGroup\",\n]);\n\n/**\n * Types whose control disappears when a card is reshaped to text. `Input` and\n * `Form` are not in {@link INTERACTIVE_TYPES} because they emit their own\n * block rather than an actions element, but a reshape loses them the same way,\n * and a `Form` gets a Submit button whether or not it carries an action.\n */\nconst CONTROL_TYPES = new Set([...INTERACTIVE_TYPES, \"Input\", \"Form\"]);\n\nconst DATE_PATTERN = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isElement = (node: NormalizedUINode): node is NormalizedUIElement =>\n isRecord(node);\n\nconst asString = (value: unknown): string =>\n typeof value === \"string\" ? value : \"\";\n\nconst warn = (\n context: ConversionContext,\n code: SlackConversionWarning[\"code\"],\n component: string,\n detail: string,\n) => {\n context.warnings.push({ code, component, detail });\n};\n\nconst clampText = (\n value: string,\n cap: number,\n component: string,\n field: string,\n context: ConversionContext,\n): string => {\n if (value.length <= cap) return value;\n warn(\n context,\n \"clamped\",\n component,\n `${field} was clamped to ${cap} characters.`,\n );\n return value.slice(0, cap);\n};\n\nconst asActionId = (\n action: unknown,\n component: string,\n context: ConversionContext,\n): string =>\n clampText(\n isRecord(action) && typeof action[\"type\"] === \"string\"\n ? action[\"type\"]\n : \"action\",\n ACTION_ID_CAP,\n component,\n \"action_id\",\n context,\n );\n\nconst plainText = (text: string): SlackPlainText => ({\n type: \"plain_text\",\n text,\n});\n\nconst actionValue = (action: unknown): string | undefined => {\n if (!isRecord(action)) return undefined;\n const { type: _type, ...payload } = action;\n if (Object.keys(payload).length === 0) return undefined;\n try {\n const serialized = JSON.stringify(payload);\n return serialized === \"{}\" ? undefined : serialized;\n } catch {\n return undefined;\n }\n};\n\nconst buttonElement = (\n label: string,\n buttonStyle: unknown,\n action: unknown,\n component: string,\n context: ConversionContext,\n): SlackButtonElement => {\n const serializedValue = actionValue(action);\n let value = serializedValue;\n if (value !== undefined && value.length > BUTTON_VALUE_CAP) {\n warn(\n context,\n \"dropped\",\n component,\n \"value was dropped because the action payload was too large to round-trip.\",\n );\n value = undefined;\n }\n const style =\n buttonStyle === \"primary\" || buttonStyle === \"danger\"\n ? buttonStyle\n : undefined;\n return {\n type: \"button\",\n text: plainText(\n clampText(label, INTERACTIVE_TEXT_CAP, component, \"label\", context),\n ),\n action_id: asActionId(action, component, context),\n ...(value !== undefined ? { value } : {}),\n ...(style !== undefined ? { style } : {}),\n };\n};\n\nconst optionFrom = (\n value: unknown,\n component: string,\n context: ConversionContext,\n): SlackOption | undefined => {\n if (\n !isRecord(value) ||\n typeof value[\"label\"] !== \"string\" ||\n typeof value[\"value\"] !== \"string\"\n ) {\n return undefined;\n }\n return {\n text: plainText(\n clampText(\n value[\"label\"],\n INTERACTIVE_TEXT_CAP,\n component,\n \"option label\",\n context,\n ),\n ),\n value: value[\"value\"],\n };\n};\n\nconst warnDroppedOptions = (\n kept: number,\n taken: number,\n component: string,\n context: ConversionContext,\n) => {\n const dropped = taken - kept;\n if (dropped === 0) return;\n warn(\n context,\n \"dropped\",\n component,\n `${dropped} ${dropped === 1 ? \"option was\" : \"options were\"} dropped for want of a string label and value.`,\n );\n};\n\nconst toActionElement = (\n element: NormalizedUIElement,\n context: ConversionContext,\n): SlackActionElement | undefined => {\n const { props, action } = element;\n switch (element.type) {\n case \"Button\":\n return buttonElement(\n asString(props[\"label\"]),\n props[\"buttonStyle\"],\n action,\n \"Button\",\n context,\n );\n case \"Select\": {\n const rawOptions = Array.isArray(props[\"options\"])\n ? props[\"options\"]\n : [];\n if (rawOptions.length > SELECT_OPTION_CAP) {\n warn(\n context,\n \"clamped\",\n \"Select\",\n `options were clamped to ${SELECT_OPTION_CAP} entries.`,\n );\n }\n const takenOptions = rawOptions.slice(0, SELECT_OPTION_CAP);\n const options = takenOptions\n .map((option) => optionFrom(option, \"Select\", context))\n .filter((option): option is SlackOption => option !== undefined);\n warnDroppedOptions(\n options.length,\n takenOptions.length,\n \"Select\",\n context,\n );\n const placeholder = clampText(\n asString(props[\"placeholder\"]),\n PLACEHOLDER_TEXT_CAP,\n \"Select\",\n \"placeholder\",\n context,\n );\n return {\n type: \"static_select\",\n action_id: asActionId(action, \"Select\", context),\n options,\n ...(placeholder ? { placeholder: plainText(placeholder) } : {}),\n };\n }\n case \"DatePicker\": {\n const rawDate = asString(props[\"value\"]);\n const initialDate = DATE_PATTERN.test(rawDate) ? rawDate : undefined;\n if (rawDate && initialDate === undefined) {\n warn(\n context,\n \"dropped\",\n \"DatePicker\",\n \"value did not match the YYYY-MM-DD format and was dropped.\",\n );\n }\n return {\n type: \"datepicker\",\n action_id: asActionId(action, \"DatePicker\", context),\n ...(initialDate !== undefined ? { initial_date: initialDate } : {}),\n };\n }\n case \"Checkbox\": {\n const label = clampText(\n asString(props[\"label\"]),\n INTERACTIVE_TEXT_CAP,\n \"Checkbox\",\n \"label\",\n context,\n );\n const name = asString(props[\"name\"]);\n const option: SlackOption = {\n text: plainText(label),\n value: name || label,\n };\n return {\n type: \"checkboxes\",\n action_id: asActionId(action, \"Checkbox\", context),\n options: [option],\n ...(props[\"defaultChecked\"] === true\n ? { initial_options: [option] }\n : {}),\n };\n }\n case \"RadioGroup\": {\n const rawOptions = Array.isArray(props[\"options\"])\n ? props[\"options\"]\n : [];\n if (rawOptions.length > RADIO_OPTION_CAP) {\n warn(\n context,\n \"clamped\",\n \"RadioGroup\",\n `options were clamped to ${RADIO_OPTION_CAP} entries.`,\n );\n }\n const takenOptions = rawOptions.slice(0, RADIO_OPTION_CAP);\n const options = takenOptions\n .map((option) => optionFrom(option, \"RadioGroup\", context))\n .filter((option): option is SlackOption => option !== undefined);\n warnDroppedOptions(\n options.length,\n takenOptions.length,\n \"RadioGroup\",\n context,\n );\n const selectedValue =\n typeof props[\"value\"] === \"string\"\n ? props[\"value\"]\n : asString(props[\"defaultValue\"]);\n const initialOption = options.find(\n (option) => option.value === selectedValue,\n );\n return {\n type: \"radio_buttons\",\n action_id: asActionId(action, \"RadioGroup\", context),\n options,\n ...(initialOption !== undefined\n ? { initial_option: initialOption }\n : {}),\n };\n }\n default:\n return undefined;\n }\n};\n\nconst normalizedList = (\n node: NormalizedUINode | undefined,\n): NormalizedUINode[] => {\n if (node === undefined || node === null) return [];\n if (!Array.isArray(node)) return [node];\n return node.flatMap((child) => normalizedList(child));\n};\n\nconst convertFacts = (\n facts: readonly NormalizedUIElement[],\n context: ConversionContext,\n): SlackBlock[] => {\n const fields = facts.map((fact) => {\n const label = asString(fact.props[\"label\"]);\n const value = asString(fact.props[\"value\"]);\n return {\n type: \"mrkdwn\" as const,\n text: clampText(\n `*${label}*\\n${value}`,\n FACT_FIELD_TEXT_CAP,\n \"Fact\",\n \"field\",\n context,\n ),\n };\n });\n const blocks: SlackBlock[] = [];\n for (let index = 0; index < fields.length; index += FACT_FIELD_CAP) {\n blocks.push({\n type: \"section\",\n fields: fields.slice(index, index + FACT_FIELD_CAP),\n });\n }\n return blocks;\n};\n\nconst convertActions = (\n elements: readonly NormalizedUIElement[],\n context: ConversionContext,\n): SlackBlock[] => {\n const converted = elements\n .map((element) => toActionElement(element, context))\n .filter((element): element is SlackActionElement => element !== undefined);\n const blocks: SlackBlock[] = [];\n for (let index = 0; index < converted.length; index += ACTIONS_ELEMENT_CAP) {\n blocks.push({\n type: \"actions\",\n elements: converted.slice(index, index + ACTIONS_ELEMENT_CAP),\n });\n }\n return blocks;\n};\n\nconst cardActionButtons = (\n props: Readonly<Record<string, unknown>>,\n context: ConversionContext,\n): SlackButtonElement[] => {\n const buttons: SlackButtonElement[] = [];\n for (const key of [\"confirm\", \"cancel\"] as const) {\n const footer = props[key];\n if (!isRecord(footer)) continue;\n buttons.push(\n buttonElement(\n asString(footer[\"label\"]),\n key === \"confirm\" ? \"primary\" : undefined,\n footer[\"$action\"],\n \"Card\",\n context,\n ),\n );\n }\n return buttons;\n};\n\nconst cardTitleText = (\n props: Readonly<Record<string, unknown>>,\n context: ConversionContext,\n): string | undefined => {\n const rawTitle = asString(props[\"title\"]);\n return rawTitle\n ? clampText(rawTitle, CARD_TITLE_CAP, \"Card\", \"title\", context)\n : undefined;\n};\n\nconst isCardNoOp = (node: NormalizedUINode): boolean =>\n isElement(node) && (node.type === \"Spacer\" || node.type === \"Icon\");\n\nconst collectText = (\n node: NormalizedUINode | undefined,\n depth: number,\n): string[] => {\n if (node === undefined || node === null || depth > MAX_TRAVERSAL_DEPTH) {\n return [];\n }\n if (typeof node === \"string\" || typeof node === \"number\") {\n return [String(node)];\n }\n if (Array.isArray(node)) {\n return node.flatMap((child) => collectText(child, depth + 1));\n }\n const element = node as NormalizedUIElement;\n const own = [\"text\", \"value\", \"label\", \"title\", \"description\"]\n .map((key) => element.props[key])\n .filter((value): value is string => typeof value === \"string\");\n return [...own, ...collectText(element.children, depth + 1)];\n};\n\ntype CardFields = {\n readonly heroImage: SlackImageBlock | undefined;\n readonly bodyRaw: string | undefined;\n readonly subtextRaw: string | undefined;\n readonly leftover: readonly NormalizedUINode[];\n};\n\n/**\n * Scans a card's children for the pieces the native card block shape can\n * carry (a hero image, body text, and subtext), each taken from the first\n * matching child. Anything left over means the card cannot map cleanly onto\n * the native shape.\n */\nconst extractCardFields = (element: NormalizedUIElement): CardFields => {\n const children = normalizedList(element.children);\n let heroImage: SlackImageBlock | undefined;\n let bodyRaw: string | undefined;\n let subtextRaw: string | undefined;\n const leftover: NormalizedUINode[] = [];\n for (const child of children) {\n if (heroImage === undefined && isElement(child) && child.type === \"Image\") {\n heroImage = {\n type: \"image\",\n image_url: asString(child.props[\"src\"]),\n alt_text: asString(child.props[\"alt\"]),\n };\n continue;\n }\n if (\n bodyRaw === undefined &&\n isElement(child) &&\n (child.type === \"Text\" || child.type === \"Markdown\")\n ) {\n bodyRaw = asString(child.props[\"value\"]);\n continue;\n }\n if (\n subtextRaw === undefined &&\n isElement(child) &&\n child.type === \"Caption\"\n ) {\n subtextRaw = asString(child.props[\"value\"]);\n continue;\n }\n if (isCardNoOp(child)) continue;\n leftover.push(child);\n }\n return { heroImage, bodyRaw, subtextRaw, leftover };\n};\n\n/** Assembles a native card block, clamping its body and subtext text. */\nconst assembleCleanCard = (\n titleText: string | undefined,\n fields: CardFields,\n buttons: readonly SlackButtonElement[],\n context: ConversionContext,\n): SlackCardBlock => {\n const body: SlackTextObject | undefined =\n fields.bodyRaw !== undefined\n ? {\n type: \"mrkdwn\",\n text: clampText(\n fields.bodyRaw,\n CARD_BODY_CAP,\n \"Card\",\n \"body\",\n context,\n ),\n }\n : undefined;\n const subtext: SlackTextObject | undefined =\n fields.subtextRaw !== undefined\n ? {\n type: \"mrkdwn\",\n text: clampText(\n fields.subtextRaw,\n CARD_SUBTEXT_CAP,\n \"Card\",\n \"subtext\",\n context,\n ),\n }\n : undefined;\n if (buttons.length > CARD_ACTIONS_CAP) {\n warn(\n context,\n \"clamped\",\n \"Card\",\n `actions were clamped to ${CARD_ACTIONS_CAP} entries.`,\n );\n }\n return buildCardBlock({\n ...(fields.heroImage !== undefined ? { heroImage: fields.heroImage } : {}),\n ...(titleText !== undefined\n ? { title: { type: \"mrkdwn\", text: titleText } }\n : {}),\n ...(body !== undefined ? { body } : {}),\n ...(subtext !== undefined ? { subtext } : {}),\n ...(buttons.length > 0\n ? { actions: buttons.slice(0, CARD_ACTIONS_CAP) }\n : {}),\n });\n};\n\n/**\n * A card requires at least one of `hero_image`, `title`, `body`, or `actions`\n * (see {@link SlackCardBlock}); a card with none of them is not a valid\n * native block and must be dropped instead of emitted.\n */\nconst isEmptyCard = (card: SlackCardBlock): boolean =>\n card.hero_image === undefined &&\n card.title === undefined &&\n card.body === undefined &&\n (card.actions === undefined || card.actions.length === 0);\n\nconst convertCard = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackBlock[] => {\n const titleText = cardTitleText(element.props, context);\n const fields = extractCardFields(element);\n const buttons = cardActionButtons(element.props, context);\n\n if (fields.leftover.length > 0) {\n warn(\n context,\n \"fallback\",\n \"Card\",\n \"A card with content beyond hero_image, body, subtext, and actions was rendered inline.\",\n );\n return [\n ...(titleText\n ? [{ type: \"header\" as const, text: plainText(titleText) }]\n : []),\n ...convertSequence(element.children, context, depth + 1),\n ...(buttons.length > 0\n ? [{ type: \"actions\" as const, elements: buttons }]\n : []),\n ];\n }\n const card = assembleCleanCard(titleText, fields, buttons, context);\n if (isEmptyCard(card)) {\n warn(\n context,\n \"dropped\",\n \"Card\",\n \"A card with none of hero_image, title, body, or actions was dropped.\",\n );\n return [];\n }\n return [card];\n};\n\n/** The content kinds a reshaped carousel card cannot carry, in report order. */\nconst LOST_CONTENT_KINDS = [\"images\", \"tables\", \"charts\", \"controls\"] as const;\n\nconst listPhrase = (items: readonly string[]): string =>\n items.length <= 1\n ? (items[0] ?? \"\")\n : `${items.slice(0, -1).join(\", \")} and ${items[items.length - 1]}`;\n\n/**\n * Collects the kinds of content a reshape drops, walking the same tree\n * {@link collectText} does so a node nested below the top level counts too.\n * These are exactly the pieces text cannot represent: an image has no text, a\n * table and a chart carry theirs in array props, and a control is behavior,\n * whether or not it carries an action of its own. An `$action` counts only on\n * a node that renders a control from it, since a `Box` or a `Row` carrying one\n * renders no control on the clean path either.\n */\nconst scanLostContent = (\n node: NormalizedUINode | undefined,\n into: Set<string>,\n depth: number,\n): void => {\n if (depth > MAX_TRAVERSAL_DEPTH) return;\n if (Array.isArray(node)) {\n for (const child of node) scanLostContent(child, into, depth + 1);\n return;\n }\n if (node === undefined || !isElement(node)) return;\n if (\n CONTROL_TYPES.has(node.type) ||\n (node.type === \"ListViewItem\" && isRecord(node.action)) ||\n (node.type === \"Card\" &&\n (isRecord(node.props[\"confirm\"]) || isRecord(node.props[\"cancel\"])))\n ) {\n into.add(\"controls\");\n }\n if (node.type === \"Image\") into.add(\"images\");\n if (node.type === \"Table\") into.add(\"tables\");\n if (node.type === \"Chart\") into.add(\"charts\");\n scanLostContent(node.children, into, depth + 1);\n};\n\n/**\n * Degrades a card that cannot map cleanly into a title-and-body card block.\n * A carousel cannot fall back to a block sequence like a standalone card can,\n * so the card is reshaped to the two text fields the block has. Text reachable\n * by {@link collectText} survives at any depth; an image, a table, a chart,\n * and a control do not, and are reported separately from the reshape itself.\n */\nconst degradeCard = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackCardBlock => {\n const rawTitle = asString(element.props[\"title\"]);\n const textChunks = collectText(element.children, depth + 1);\n const titleSource = rawTitle || textChunks[0] || \"\";\n const bodyChunks = rawTitle ? textChunks : textChunks.slice(1);\n const titleText = clampText(\n titleSource,\n CARD_TITLE_CAP,\n \"Card\",\n \"title\",\n context,\n );\n const bodyText = clampText(\n bodyChunks.join(\"\\n\"),\n CARD_BODY_CAP,\n \"Card\",\n \"body\",\n context,\n );\n warn(\n context,\n \"fallback\",\n \"Card\",\n \"A card inside a carousel was reshaped to title and body.\",\n );\n const lostKinds = new Set<string>();\n if (isRecord(element.props[\"confirm\"]) || isRecord(element.props[\"cancel\"])) {\n lostKinds.add(\"controls\");\n }\n scanLostContent(element.children, lostKinds, depth + 1);\n const lost = LOST_CONTENT_KINDS.filter((kind) => lostKinds.has(kind));\n if (lost.length > 0) {\n warn(\n context,\n \"dropped\",\n \"Card\",\n `A reshaped carousel card's ${listPhrase(lost)} were dropped.`,\n );\n }\n return buildCardBlock({\n ...(titleText ? { title: { type: \"mrkdwn\", text: titleText } } : {}),\n ...(bodyText ? { body: { type: \"mrkdwn\", text: bodyText } } : {}),\n });\n};\n\nconst convertCarouselCard = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackCardBlock | undefined => {\n const fields = extractCardFields(element);\n const card =\n fields.leftover.length === 0\n ? assembleCleanCard(\n cardTitleText(element.props, context),\n fields,\n cardActionButtons(element.props, context),\n context,\n )\n : degradeCard(element, context, depth);\n if (isEmptyCard(card)) {\n warn(\n context,\n \"dropped\",\n \"Card\",\n \"A card with none of hero_image, title, body, or actions was dropped.\",\n );\n return undefined;\n }\n return card;\n};\n\nconst isBadgeOrCaption = (\n node: NormalizedUINode,\n): node is NormalizedUIElement =>\n isElement(node) && (node.type === \"Badge\" || node.type === \"Caption\");\n\nconst contextRow = (\n element: NormalizedUIElement,\n context: ConversionContext,\n): SlackBlock[] | undefined => {\n const allChildren = normalizedList(element.children);\n const children = allChildren.filter(isBadgeOrCaption);\n if (allChildren.length === 0 || children.length !== allChildren.length) {\n return undefined;\n }\n if (children.length > CONTEXT_ELEMENT_CAP) {\n warn(\n context,\n \"clamped\",\n \"Row\",\n `context elements were clamped to ${CONTEXT_ELEMENT_CAP} entries.`,\n );\n }\n return [\n {\n type: \"context\",\n elements: children.slice(0, CONTEXT_ELEMENT_CAP).map((child) => ({\n type: \"mrkdwn\",\n text: clampText(\n asString(child.props[\"value\"]),\n CONTEXT_TEXT_CAP,\n child.type,\n \"value\",\n context,\n ),\n })),\n },\n ];\n};\n\nconst convertListItem = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackBlock => {\n const text = clampText(\n collectText(element.children, depth + 1).join(\"\\n\"),\n SECTION_TEXT_CAP,\n \"ListViewItem\",\n \"text\",\n context,\n );\n const accessory = isRecord(element.action)\n ? buttonElement(\"Open\", undefined, element.action, \"ListViewItem\", context)\n : undefined;\n return {\n type: \"section\",\n text: { type: \"mrkdwn\", text },\n ...(accessory !== undefined ? { accessory } : {}),\n };\n};\n\n/**\n * Converts a child whose output is thrown away, and reports whether anything\n * was lost with it. A scratch context keeps the throwaway out of the shared\n * markdown and data-table budgets that surviving blocks still need; only its\n * `dropped` warnings are forwarded, because those describe the tree the caller\n * wrote, while a clamp or a fallback would describe content never delivered.\n */\nconst discardedChild = (\n child: NormalizedUINode,\n context: ConversionContext,\n depth: number,\n): boolean => {\n const scratch: ConversionContext = { ...context, warnings: [] };\n const produced = convertSequence(child, scratch, depth).length > 0;\n const lost = scratch.warnings.filter((warning) => warning.code === \"dropped\");\n context.warnings.push(...lost);\n return produced || lost.length > 0;\n};\n\nconst convertListView = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackBlock[] => {\n const items: NormalizedUIElement[] = [];\n let discarded = 0;\n for (const child of normalizedList(element.children)) {\n if (isElement(child) && child.type === \"ListViewItem\") {\n items.push(child);\n continue;\n }\n if (discardedChild(child, context, depth + 1)) discarded += 1;\n }\n if (discarded > 0) {\n warn(\n context,\n \"dropped\",\n \"ListView\",\n `${discarded} non-item ${discarded === 1 ? \"child was\" : \"children were\"} dropped.`,\n );\n }\n return items.flatMap((item, index) => [\n ...(index > 0 ? [{ type: \"divider\" as const }] : []),\n convertListItem(item, context, depth + 1),\n ]);\n};\n\nconst convertCarousel = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackBlock[] => {\n const cardChildren: NormalizedUIElement[] = [];\n let droppedCards = 0;\n for (const child of normalizedList(element.children)) {\n if (isElement(child) && child.type === \"Card\") {\n cardChildren.push(child);\n continue;\n }\n if (discardedChild(child, context, depth + 1)) droppedCards += 1;\n }\n if (droppedCards > 0) {\n warn(\n context,\n \"dropped\",\n \"Carousel\",\n `${droppedCards} non-card ${droppedCards === 1 ? \"child was\" : \"children were\"} dropped.`,\n );\n }\n if (cardChildren.length > CAROUSEL_CARD_CAP) {\n warn(\n context,\n \"clamped\",\n \"Carousel\",\n `cards were clamped to ${CAROUSEL_CARD_CAP} entries.`,\n );\n }\n const cards = cardChildren\n .slice(0, CAROUSEL_CARD_CAP)\n .map((card) => convertCarouselCard(card, context, depth + 1))\n .filter((card): card is SlackCardBlock => card !== undefined);\n if (cards.length < CAROUSEL_CARD_MIN) {\n warn(\n context,\n \"dropped\",\n \"Carousel\",\n \"A carousel without renderable cards was dropped.\",\n );\n return [];\n }\n return [buildCarouselBlock(cards)];\n};\n\nconst toDataTableCell = (value: unknown): SlackDataTableCell | undefined => {\n if (typeof value === \"number\") {\n return { type: \"raw_number\", text: String(value) };\n }\n if (typeof value === \"string\" || typeof value === \"boolean\") {\n return { type: \"raw_text\", text: String(value) };\n }\n return undefined;\n};\n\nconst rowCharacters = (row: readonly SlackDataTableCell[]): number =>\n row.reduce((sum, cell) => sum + cell.text.length, 0);\n\nconst convertTable = (\n element: NormalizedUIElement,\n context: ConversionContext,\n): SlackBlock[] => {\n const rawColumns = Array.isArray(element.props[\"columns\"])\n ? element.props[\"columns\"]\n : [];\n const rawRows = Array.isArray(element.props[\"rows\"])\n ? element.props[\"rows\"]\n : [];\n if (rawColumns.length > DATA_TABLE_COLUMN_CAP) {\n warn(\n context,\n \"clamped\",\n \"Table\",\n `columns were clamped to ${DATA_TABLE_COLUMN_CAP} entries.`,\n );\n }\n if (rawRows.length > DATA_TABLE_ROW_CAP) {\n warn(\n context,\n \"clamped\",\n \"Table\",\n `rows were clamped to ${DATA_TABLE_ROW_CAP} entries.`,\n );\n }\n\n const takenColumns = rawColumns.slice(0, DATA_TABLE_COLUMN_CAP);\n const unlabeled = takenColumns.filter(\n (column) => !isRecord(column) || typeof column[\"label\"] !== \"string\",\n ).length;\n if (unlabeled > 0) {\n warn(\n context,\n \"dropped\",\n \"Table\",\n `${unlabeled} column ${unlabeled === 1 ? \"header was\" : \"headers were\"} left blank for want of a string label.`,\n );\n }\n const columnHeaderRow: SlackDataTableCell[] = takenColumns.map((column) => ({\n type: \"raw_text\" as const,\n text: isRecord(column) ? asString(column[\"label\"]) : \"\",\n }));\n const dataRows: SlackDataTableCell[][] = rawRows\n .slice(0, DATA_TABLE_ROW_CAP)\n .map((row) =>\n (Array.isArray(row) ? row : [])\n .slice(0, DATA_TABLE_COLUMN_CAP)\n .map(\n (value) =>\n toDataTableCell(value) ?? { type: \"raw_text\" as const, text: \"\" },\n ),\n );\n const width = Math.max(\n columnHeaderRow.length,\n 0,\n ...dataRows.map((row) => row.length),\n );\n const padRow = (row: SlackDataTableCell[]): SlackDataTableCell[] =>\n row.length >= width\n ? row\n : [\n ...row,\n ...Array.from({ length: width - row.length }, () => ({\n type: \"raw_text\" as const,\n text: \"\",\n })),\n ];\n const headerRow = padRow(columnHeaderRow);\n\n const headerCharacters = rowCharacters(headerRow);\n if (context.dataTableCharacters + headerCharacters > DATA_TABLE_CHAR_BUDGET) {\n warn(\n context,\n \"dropped\",\n \"Table\",\n `the table was dropped because its header row exceeds the ${DATA_TABLE_CHAR_BUDGET}-character table budget.`,\n );\n return [];\n }\n\n const kept: SlackDataTableCell[][] = [headerRow];\n let tableCharacters = headerCharacters;\n let budgetClamped = false;\n for (const row of dataRows.map(padRow)) {\n const chars = rowCharacters(row);\n if (\n context.dataTableCharacters + tableCharacters + chars >\n DATA_TABLE_CHAR_BUDGET\n ) {\n budgetClamped = true;\n break;\n }\n kept.push(row);\n tableCharacters += chars;\n }\n context.dataTableCharacters += tableCharacters;\n if (budgetClamped) {\n warn(\n context,\n \"clamped\",\n \"Table\",\n `rows were clamped to fit the ${DATA_TABLE_CHAR_BUDGET}-character table budget.`,\n );\n }\n\n return [buildDataTableBlock(kept)];\n};\n\nconst convertElement = (\n element: NormalizedUIElement,\n context: ConversionContext,\n depth: number,\n): SlackBlock[] => {\n const { props } = element;\n switch (element.type) {\n case \"Header\":\n return [\n {\n type: \"header\",\n text: plainText(\n clampText(\n asString(props[\"text\"]),\n HEADER_TEXT_CAP,\n \"Header\",\n \"text\",\n context,\n ),\n ),\n },\n ];\n case \"Text\":\n return [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: clampText(\n asString(props[\"value\"]),\n SECTION_TEXT_CAP,\n \"Text\",\n \"value\",\n context,\n ),\n },\n },\n ];\n case \"Caption\":\n case \"Badge\":\n return [\n {\n type: \"context\",\n elements: [\n {\n type: \"mrkdwn\",\n text: clampText(\n asString(props[\"value\"]),\n CONTEXT_TEXT_CAP,\n element.type,\n \"value\",\n context,\n ),\n },\n ],\n },\n ];\n case \"Fact\":\n return convertFacts([element], context);\n case \"Image\":\n return [\n {\n type: \"image\",\n image_url: asString(props[\"src\"]),\n alt_text: asString(props[\"alt\"]),\n },\n ];\n case \"Divider\":\n return [{ type: \"divider\" }];\n case \"Button\":\n case \"Select\":\n case \"DatePicker\":\n case \"Checkbox\":\n case \"RadioGroup\":\n return convertActions([element], context);\n case \"Input\": {\n const label = clampText(\n asString(props[\"label\"]),\n INPUT_LABEL_CAP,\n \"Input\",\n \"label\",\n context,\n );\n const placeholder = clampText(\n asString(props[\"placeholder\"]),\n PLACEHOLDER_TEXT_CAP,\n \"Input\",\n \"placeholder\",\n context,\n );\n return [\n {\n type: \"input\",\n label: plainText(label),\n element: {\n type: \"plain_text_input\",\n action_id: asActionId(element.action, \"Input\", context),\n ...(props[\"multiline\"] === true ? { multiline: true } : {}),\n ...(placeholder ? { placeholder: plainText(placeholder) } : {}),\n },\n },\n ];\n }\n case \"Alert\": {\n const tone =\n props[\"tone\"] === \"success\" ||\n props[\"tone\"] === \"warning\" ||\n props[\"tone\"] === \"danger\"\n ? props[\"tone\"]\n : \"info\";\n const titleText = asString(props[\"title\"]);\n const description = asString(props[\"description\"]);\n if (context.surface === \"modal\") {\n const combined = [titleText, description].filter(Boolean).join(\"\\n\");\n return [\n buildAlertBlock(\n clampText(combined, ALERT_TEXT_CAP, \"Alert\", \"text\", context),\n tone,\n ),\n ];\n }\n warn(\n context,\n \"fallback\",\n \"Alert\",\n \"Alert was rendered with message-surface fallback blocks.\",\n );\n return [\n ...buildAlertFallback(\n titleText,\n clampText(\n description,\n SECTION_TEXT_CAP,\n \"Alert\",\n \"description\",\n context,\n ),\n tone,\n ),\n ];\n }\n case \"Card\":\n return convertCard(element, context, depth);\n case \"Carousel\":\n return convertCarousel(element, context, depth);\n case \"Col\":\n case \"Box\":\n return convertSequence(element.children, context, depth + 1);\n case \"Row\":\n return (\n contextRow(element, context) ??\n convertSequence(element.children, context, depth + 1)\n );\n case \"Spacer\":\n case \"Icon\":\n return [];\n case \"Table\":\n return convertTable(element, context);\n case \"Markdown\": {\n const value = asString(props[\"value\"]);\n if (\n !context.markdownExhausted &&\n context.markdownCharacters + value.length <= MARKDOWN_TEXT_BUDGET\n ) {\n context.markdownCharacters += value.length;\n return [{ type: \"markdown\", text: value }];\n }\n context.markdownExhausted = true;\n warn(\n context,\n \"fallback\",\n \"Markdown\",\n `The ${MARKDOWN_TEXT_BUDGET}-character markdown budget was exceeded.`,\n );\n return [\n {\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: clampText(\n value,\n SECTION_TEXT_CAP,\n \"Markdown\",\n \"fallback text\",\n context,\n ),\n },\n },\n ];\n }\n case \"Chart\":\n warn(\n context,\n \"dropped\",\n \"Chart\",\n \"Chart was replaced by a Slack omission note.\",\n );\n return [\n {\n type: \"context\",\n elements: [{ type: \"mrkdwn\", text: \"Chart omitted on Slack.\" }],\n },\n ];\n case \"ListView\":\n return convertListView(element, context, depth);\n case \"ListViewItem\":\n return [convertListItem(element, context, depth)];\n case \"Form\":\n return [\n ...convertSequence(element.children, context, depth + 1),\n {\n type: \"actions\",\n elements: [\n buttonElement(\"Submit\", \"primary\", element.action, \"Form\", context),\n ],\n },\n ];\n default:\n warn(\n context,\n \"dropped\",\n element.type,\n \"Unknown component type was dropped.\",\n );\n return [];\n }\n};\n\nfunction convertSequence(\n node: NormalizedUINode | undefined,\n context: ConversionContext,\n depth: number,\n): SlackBlock[] {\n if (node === undefined || node === null || depth > MAX_TRAVERSAL_DEPTH) {\n return [];\n }\n const nodes = Array.isArray(node) ? node : [node];\n const blocks: SlackBlock[] = [];\n for (let index = 0; index < nodes.length;) {\n const current = nodes[index];\n if (current === undefined || current === null) {\n index += 1;\n continue;\n }\n if (isElement(current) && current.type === \"Fact\") {\n const facts: NormalizedUIElement[] = [];\n while (index < nodes.length) {\n const candidate = nodes[index];\n if (!candidate || !isElement(candidate) || candidate.type !== \"Fact\") {\n break;\n }\n facts.push(candidate);\n index += 1;\n }\n blocks.push(...convertFacts(facts, context));\n continue;\n }\n if (isElement(current) && INTERACTIVE_TYPES.has(current.type)) {\n const controls: NormalizedUIElement[] = [];\n while (index < nodes.length) {\n const candidate = nodes[index];\n if (\n !candidate ||\n !isElement(candidate) ||\n !INTERACTIVE_TYPES.has(candidate.type)\n ) {\n break;\n }\n controls.push(candidate);\n index += 1;\n }\n blocks.push(...convertActions(controls, context));\n continue;\n }\n if (typeof current === \"string\" || typeof current === \"number\") {\n blocks.push({\n type: \"section\",\n text: {\n type: \"mrkdwn\",\n text: clampText(\n String(current),\n SECTION_TEXT_CAP,\n \"Text\",\n \"value\",\n context,\n ),\n },\n });\n } else if (Array.isArray(current)) {\n blocks.push(...convertSequence(current, context, depth + 1));\n } else {\n blocks.push(\n ...convertElement(current as NormalizedUIElement, context, depth + 1),\n );\n }\n index += 1;\n }\n return blocks;\n}\n\ninterface BoundState {\n remaining: number;\n exhausted: boolean;\n}\n\ntype ClampReason = \"children\" | \"budget\" | \"cycle\" | \"depth\";\n\nfunction boundNode(\n value: unknown,\n depth: number,\n onClamp: (reason: ClampReason) => void,\n state: BoundState,\n ancestors: WeakSet<object>,\n): unknown {\n if (state.remaining <= 0) {\n if (!state.exhausted) {\n state.exhausted = true;\n onClamp(\"budget\");\n }\n return null;\n }\n state.remaining -= 1;\n if (depth > MAX_TRAVERSAL_DEPTH) {\n onClamp(\"depth\");\n return null;\n }\n if (Array.isArray(value)) {\n if (ancestors.has(value)) {\n onClamp(\"cycle\");\n return null;\n }\n ancestors.add(value);\n const bounded = Array.prototype.slice.call(\n value,\n 0,\n CHILDREN_CAP,\n ) as unknown[];\n if (value.length > CHILDREN_CAP) onClamp(\"children\");\n const result = bounded.map((item) =>\n boundNode(item, depth + 1, onClamp, state, ancestors),\n );\n ancestors.delete(value);\n return result;\n }\n if (\n value !== null &&\n typeof value === \"object\" &&\n \"children\" in (value as Record<string, unknown>)\n ) {\n if (ancestors.has(value)) {\n onClamp(\"cycle\");\n return null;\n }\n ancestors.add(value);\n const record = value as Record<string, unknown>;\n const result = {\n ...record,\n children: boundNode(\n record[\"children\"],\n depth + 1,\n onClamp,\n state,\n ancestors,\n ),\n };\n ancestors.delete(value);\n return result;\n }\n return value;\n}\n\n/**\n * Produces a bounded plain copy of a raw generative-ui spec before it\n * reaches `normalizeSpec`, whose own traversal of the root array or any\n * `children` array walks the full reported length of a hostile proxied\n * array before any per-field cap downstream ever applies. Every array\n * (root, or `children` at any depth) is capped to {@link CHILDREN_CAP}\n * entries via `Array.prototype.slice`, which bounds even a proxy with a\n * fabricated `length`; recursion itself is capped at\n * {@link MAX_TRAVERSAL_DEPTH}. `onClamp` fires once per level that was\n * truncated, receiving the reason for that truncation: `\"children\"`,\n * `\"depth\"`, `\"budget\"`, or `\"cycle\"`. The walk also spends a total budget of\n * {@link NODE_BUDGET} nodes, so shared references cannot multiply work\n * exponentially, and a node that is its own ancestor is cut to `null`.\n */\nfunction boundSpec(\n spec: unknown,\n onClamp: (reason: ClampReason) => void,\n): unknown {\n return boundNode(\n spec,\n 0,\n onClamp,\n { remaining: NODE_BUDGET, exhausted: false },\n new WeakSet(),\n );\n}\n\n/** Converts a generative-UI tree into Slack Block Kit JSON and downgrade warnings. */\nexport function toSlackBlocks(\n node: unknown,\n options?: ToSlackBlocksOptions,\n): SlackBlocksResult {\n const surface = options?.surface === \"modal\" ? \"modal\" : \"message\";\n const context: ConversionContext = {\n blockCap: surface === \"modal\" ? MODAL_BLOCK_CAP : MESSAGE_BLOCK_CAP,\n surface,\n warnings: [],\n markdownCharacters: 0,\n markdownExhausted: false,\n dataTableCharacters: 0,\n };\n try {\n const bounded = boundSpec(node, (reason) => {\n const detail =\n reason === \"budget\"\n ? `the tree was truncated after ${NODE_BUDGET} nodes.`\n : reason === \"cycle\"\n ? \"a self-referencing node was dropped.\"\n : reason === \"depth\"\n ? `nodes deeper than ${MAX_ELEMENT_DEPTH} levels were dropped.`\n : `children were clamped to ${CHILDREN_CAP} entries.`;\n warn(context, \"clamped\", \"Root\", detail);\n });\n const { root } = normalizeSpec(bounded as never);\n const converted = convertSequence(root, context, 0);\n if (converted.length <= context.blockCap) {\n return { blocks: converted, warnings: context.warnings };\n }\n const kept = converted.slice(0, context.blockCap - 1);\n const omitted = converted.length - kept.length;\n warn(\n context,\n \"clamped\",\n \"Root\",\n `${omitted} ${omitted === 1 ? \"block was\" : \"blocks were\"} omitted.`,\n );\n return {\n blocks: [...kept, buildBlockBudgetNote(omitted)],\n warnings: context.warnings,\n };\n } catch {\n return {\n blocks: [],\n warnings: [\n {\n code: \"dropped\",\n component: \"Root\",\n detail: \"Input could not be converted.\",\n },\n ],\n };\n }\n}\n"],"mappings":"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"}
@@ -157,7 +157,13 @@ type SlackBlock = SlackHeaderBlock | SlackSectionBlock | SlackContextBlock | Sla
157
157
  interface ToSlackBlocksOptions {
158
158
  readonly surface?: "message" | "modal";
159
159
  }
160
- /** A non-fatal downgrade reported during Slack conversion. */
160
+ /**
161
+ * A non-fatal note reported during Slack conversion.
162
+ *
163
+ * `clamped` removed content to fit a limit, `dropped` discarded a node or one
164
+ * of its props and may leave a placeholder note behind, and `fallback` emitted
165
+ * the node in a different form than requested.
166
+ */
161
167
  interface SlackConversionWarning {
162
168
  readonly code: "clamped" | "dropped" | "fallback";
163
169
  readonly component: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/slack/types.ts"],"mappings":";;;UAGiB;WACN;WACA;;;UAIM;WACN;WACA;;;KAIC,kBAAkB,iBAAiB;;UAG9B;WACN,MAAM;WACN;;;UAIM;WACN;WACA,MAAM;WACN;WACA;WACA;;;UAIM;WACN;WACA;WACA,kBAAkB;WAClB,cAAc;;;UAIR;WACN;WACA;WACA;;;UAIM;WACN;WACA;WACA,kBAAkB;WAClB,2BAA2B;;;UAIrB;WACN;WACA;WACA,kBAAkB;WAClB,iBAAiB;;;UAIX;WACN;WACA;WACA;WACA,cAAc;;;KAIb,qBACR,qBACA,2BACA,yBACA,yBACA;;UAGa;WACN;WACA,MAAM;;;UAIA;WACN;WACA,OAAO;WACP,kBAAkB;WAClB,YAAY;;;UAIN;WACN;WACA,mBAAmB;;;UAIb;WACN;WACA;WACA;;;UAIM;WACN;;;UAIM;WACN;WACA,mBAAmB;;;UAIb;WACN;WACA,OAAO;WACP,SAAS;;;UAIH;WACN;WACA;;;UAIM;WACN;WACA;;;KAIC,qBACR,4BACA;;;;;;;UAQa;WACN;WACA,aAAa;WACb,QAAQ;WACR,WAAW;WACX,OAAO;WACP,UAAU;WACV,mBAAmB;;;UAIb;WACN;WACA,mBAAmB;;;UAIb;WACN;WACA;WACA,yBAAyB;WACzB;;;UAIM;WACN;WACA;;;KAIC;;UAQK;WACN;WACA,MAAM;WACN,QAAQ;;;KAIP,aACR,mBACA,oBACA,oBACA,kBACA,oBACA,oBACA,kBACA,iBACA,qBACA,sBACA,qBACA;;UAGa;WACN;;;UAIM;WACN;WACA;WACA;;;UAIM;WACN,QAAQ;WACR,UAAU;;;UAIJ;WACN,OAAO;WACP,UAAU"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/slack/types.ts"],"mappings":";;;UAGiB;WACN;WACA;;;UAIM;WACN;WACA;;;KAIC,kBAAkB,iBAAiB;;UAG9B;WACN,MAAM;WACN;;;UAIM;WACN;WACA,MAAM;WACN;WACA;WACA;;;UAIM;WACN;WACA;WACA,kBAAkB;WAClB,cAAc;;;UAIR;WACN;WACA;WACA;;;UAIM;WACN;WACA;WACA,kBAAkB;WAClB,2BAA2B;;;UAIrB;WACN;WACA;WACA,kBAAkB;WAClB,iBAAiB;;;UAIX;WACN;WACA;WACA;WACA,cAAc;;;KAIb,qBACR,qBACA,2BACA,yBACA,yBACA;;UAGa;WACN;WACA,MAAM;;;UAIA;WACN;WACA,OAAO;WACP,kBAAkB;WAClB,YAAY;;;UAIN;WACN;WACA,mBAAmB;;;UAIb;WACN;WACA;WACA;;;UAIM;WACN;;;UAIM;WACN;WACA,mBAAmB;;;UAIb;WACN;WACA,OAAO;WACP,SAAS;;;UAIH;WACN;WACA;;;UAIM;WACN;WACA;;;KAIC,qBACR,4BACA;;;;;;;UAQa;WACN;WACA,aAAa;WACb,QAAQ;WACR,WAAW;WACX,OAAO;WACP,UAAU;WACV,mBAAmB;;;UAIb;WACN;WACA,mBAAmB;;;UAIb;WACN;WACA;WACA,yBAAyB;WACzB;;;UAIM;WACN;WACA;;;KAIC;;UAQK;WACN;WACA,MAAM;WACN,QAAQ;;;KAIP,aACR,mBACA,oBACA,oBACA,kBACA,oBACA,oBACA,kBACA,iBACA,qBACA,sBACA,qBACA;;UAGa;WACN;;;;;;;;;UAUM;WACN;WACA;WACA;;;UAIM;WACN,QAAQ;WACR,UAAU;;;UAIJ;WACN,OAAO;WACP,UAAU"}
@@ -6,8 +6,15 @@ declare const AC_VERSION = "1.5";
6
6
  declare const ADAPTIVE_CARD_SCHEMA = "http://adaptivecards.io/schemas/adaptive-card.json";
7
7
  /** The `contentType` stamped on every emitted attachment. */
8
8
  declare const ATTACHMENT_CONTENT_TYPE = "application/vnd.microsoft.card.adaptive";
9
- /** The maximum normalized traversal depth. */
9
+ /** The maximum traversal depth, counted in visited values, not tree levels. */
10
10
  declare const MAX_TRAVERSAL_DEPTH = 64;
11
+ /**
12
+ * The element-nesting ceiling a caller observes. `boundNode` visits an element
13
+ * and its `children` array separately, so one level of nesting spends two of
14
+ * {@link MAX_TRAVERSAL_DEPTH}; it runs before the conversion walk and is never
15
+ * the looser of the two. A root array costs one more level than a root object.
16
+ */
17
+ declare const MAX_ELEMENT_DEPTH: number;
11
18
  /**
12
19
  * The maximum number of entries kept at any single level (root, or a
13
20
  * `children` array at any depth) of the raw spec, before it ever reaches
@@ -53,5 +60,5 @@ declare function buildSubmitData(action: unknown): TeamsSubmitData;
53
60
  /** Builds an Action.Submit carrying `action` in the `aui` envelope. */
54
61
  declare function buildSubmitAction(title: string, action: unknown, mode?: "secondary"): TeamsSubmitAction;
55
62
  //#endregion
56
- export { AC_VERSION, ADAPTIVE_CARD_SCHEMA, ATTACHMENT_CONTENT_TYPE, CAROUSEL_ATTACHMENT_CAP, CHILDREN_CAP, CHOICE_OPTION_CAP, ClampReason, MAX_TRAVERSAL_DEPTH, NODE_BUDGET, PAYLOAD_SOFT_CAP, PRIMARY_ACTION_CAP, TABLE_COLUMN_CAP, TABLE_ROW_CAP, buildAttachment, buildCard, buildSubmitAction, buildSubmitData, clampReasonDetail, utf8ByteLength };
63
+ export { AC_VERSION, ADAPTIVE_CARD_SCHEMA, ATTACHMENT_CONTENT_TYPE, CAROUSEL_ATTACHMENT_CAP, CHILDREN_CAP, CHOICE_OPTION_CAP, ClampReason, MAX_ELEMENT_DEPTH, MAX_TRAVERSAL_DEPTH, NODE_BUDGET, PAYLOAD_SOFT_CAP, PRIMARY_ACTION_CAP, TABLE_COLUMN_CAP, TABLE_ROW_CAP, buildAttachment, buildCard, buildSubmitAction, buildSubmitData, clampReasonDetail, utf8ByteLength };
57
64
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","names":[],"sources":["../../src/teams/constants.ts"],"mappings":";;;cASa;;cAGA;;cAIA;;cAIA;;;;;;;;cASA;;;;;;;;cASA;KAED;iBAEI,kBAAkB,QAAQ;;cAY7B;;cAGA;;cAGA;;cAGA;;cAGA;;cAGA;;iBAKG,eAAe;;iBAKf,UAAU,MAAM,qBAAqB;;iBAWrC,gBAAgB,MAAM,oBAAoB;;;;;;iBAY1C,gBAAgB,kBAAkB;;iBAalC,kBACd,eACA,iBACA,qBACC"}
1
+ {"version":3,"file":"constants.d.ts","names":[],"sources":["../../src/teams/constants.ts"],"mappings":";;;cASa;;cAGA;;cAIA;;cAIA;;;;;;;cAQA;;;;;;;;cASA;;;;;;;;cASA;KAED;iBAEI,kBAAkB,QAAQ;;cAY7B;;cAGA;;cAGA;;cAGA;;cAGA;;cAGA;;iBAKG,eAAe;;iBAKf,UAAU,MAAM,qBAAqB;;iBAWrC,gBAAgB,MAAM,oBAAoB;;;;;;iBAY1C,gBAAgB,kBAAkB;;iBAalC,kBACd,eACA,iBACA,qBACC"}
@@ -5,9 +5,16 @@ const AC_VERSION = "1.5";
5
5
  const ADAPTIVE_CARD_SCHEMA = "http://adaptivecards.io/schemas/adaptive-card.json";
6
6
  /** The `contentType` stamped on every emitted attachment. */
7
7
  const ATTACHMENT_CONTENT_TYPE = "application/vnd.microsoft.card.adaptive";
8
- /** The maximum normalized traversal depth. */
8
+ /** The maximum traversal depth, counted in visited values, not tree levels. */
9
9
  const MAX_TRAVERSAL_DEPTH = 64;
10
10
  /**
11
+ * The element-nesting ceiling a caller observes. `boundNode` visits an element
12
+ * and its `children` array separately, so one level of nesting spends two of
13
+ * {@link MAX_TRAVERSAL_DEPTH}; it runs before the conversion walk and is never
14
+ * the looser of the two. A root array costs one more level than a root object.
15
+ */
16
+ const MAX_ELEMENT_DEPTH = 32;
17
+ /**
11
18
  * The maximum number of entries kept at any single level (root, or a
12
19
  * `children` array at any depth) of the raw spec, before it ever reaches
13
20
  * `normalizeSpec`. Bounds a hostile array's reported `length` so the pre-pass
@@ -26,7 +33,7 @@ const NODE_BUDGET = 5e3;
26
33
  function clampReasonDetail(reason) {
27
34
  if (reason === "budget") return `the tree was truncated after ${NODE_BUDGET} nodes.`;
28
35
  if (reason === "cycle") return "a self-referencing node was dropped.";
29
- if (reason === "depth") return `nodes deeper than 64 levels were dropped.`;
36
+ if (reason === "depth") return `nodes deeper than 32 levels were dropped.`;
30
37
  return `children were clamped to 200 entries.`;
31
38
  }
32
39
  /** The maximum number of attachments produced from a root-level Carousel. */
@@ -87,6 +94,6 @@ function buildSubmitAction(title, action, mode) {
87
94
  };
88
95
  }
89
96
  //#endregion
90
- export { AC_VERSION, ADAPTIVE_CARD_SCHEMA, ATTACHMENT_CONTENT_TYPE, CAROUSEL_ATTACHMENT_CAP, CHILDREN_CAP, CHOICE_OPTION_CAP, MAX_TRAVERSAL_DEPTH, NODE_BUDGET, PAYLOAD_SOFT_CAP, PRIMARY_ACTION_CAP, TABLE_COLUMN_CAP, TABLE_ROW_CAP, buildAttachment, buildCard, buildSubmitAction, buildSubmitData, clampReasonDetail, utf8ByteLength };
97
+ export { AC_VERSION, ADAPTIVE_CARD_SCHEMA, ATTACHMENT_CONTENT_TYPE, CAROUSEL_ATTACHMENT_CAP, CHILDREN_CAP, CHOICE_OPTION_CAP, MAX_ELEMENT_DEPTH, MAX_TRAVERSAL_DEPTH, NODE_BUDGET, PAYLOAD_SOFT_CAP, PRIMARY_ACTION_CAP, TABLE_COLUMN_CAP, TABLE_ROW_CAP, buildAttachment, buildCard, buildSubmitAction, buildSubmitData, clampReasonDetail, utf8ByteLength };
91
98
 
92
99
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","names":[],"sources":["../../src/teams/constants.ts"],"sourcesContent":["import type {\n TeamsAdaptiveCard,\n TeamsCardAttachment,\n TeamsCardElement,\n TeamsSubmitAction,\n TeamsSubmitData,\n} from \"./types\";\n\n/** The Adaptive Card schema version emitted by {@link toAdaptiveCard}: the Teams desktop/web cap (mobile clients cap at 1.2). */\nexport const AC_VERSION = \"1.5\";\n\n/** The `$schema` URL stamped on every emitted card. */\nexport const ADAPTIVE_CARD_SCHEMA =\n \"http://adaptivecards.io/schemas/adaptive-card.json\";\n\n/** The `contentType` stamped on every emitted attachment. */\nexport const ATTACHMENT_CONTENT_TYPE =\n \"application/vnd.microsoft.card.adaptive\";\n\n/** The maximum normalized traversal depth. */\nexport const MAX_TRAVERSAL_DEPTH = 64;\n\n/**\n * The maximum number of entries kept at any single level (root, or a\n * `children` array at any depth) of the raw spec, before it ever reaches\n * `normalizeSpec`. Bounds a hostile array's reported `length` so the pre-pass\n * can never be made to walk further than this regardless of what the array\n * claims about itself.\n */\nexport const CHILDREN_CAP = 200;\n\n/**\n * The total number of nodes {@link boundSpec} will visit across one walk,\n * regardless of how many times a shared reference recurs. Bounds the\n * combinatorial work a DAG of shared or self-referential nodes would\n * otherwise force even though each individual array stays within\n * {@link CHILDREN_CAP}.\n */\nexport const NODE_BUDGET = 5000;\n\nexport type ClampReason = \"children\" | \"budget\" | \"cycle\" | \"depth\";\n\nexport function clampReasonDetail(reason: ClampReason): string {\n if (reason === \"budget\") {\n return `the tree was truncated after ${NODE_BUDGET} nodes.`;\n }\n if (reason === \"cycle\") return \"a self-referencing node was dropped.\";\n if (reason === \"depth\") {\n return `nodes deeper than ${MAX_TRAVERSAL_DEPTH} levels were dropped.`;\n }\n return `children were clamped to ${CHILDREN_CAP} entries.`;\n}\n\n/** The maximum number of attachments produced from a root-level Carousel. */\nexport const CAROUSEL_ATTACHMENT_CAP = 10;\n\n/** The maximum number of primary-mode actions in one ActionSet; extras are set to secondary mode instead of being dropped. */\nexport const PRIMARY_ACTION_CAP = 6;\n\n/** The maximum number of data rows in a Table. */\nexport const TABLE_ROW_CAP = 100;\n\n/** The maximum number of columns in a Table. */\nexport const TABLE_COLUMN_CAP = 20;\n\n/** The maximum number of choices in a Select or RadioGroup. */\nexport const CHOICE_OPTION_CAP = 100;\n\n/** The soft byte-count budget for a serialized card, past which a warning notes Teams' 100 KB bot message limit (the card is never truncated to fit it). */\nexport const PAYLOAD_SOFT_CAP = 80000;\n\nconst textEncoder = new TextEncoder();\n\n/** Measures the wire size of `value` in UTF-8 bytes rather than UTF-16 code units. */\nexport function utf8ByteLength(value: string): number {\n return textEncoder.encode(value).byteLength;\n}\n\n/** Builds the fixed-shape Adaptive Card envelope around a converted body. */\nexport function buildCard(body: TeamsCardElement[]): TeamsAdaptiveCard {\n return {\n $schema: ADAPTIVE_CARD_SCHEMA,\n type: \"AdaptiveCard\",\n version: AC_VERSION,\n body,\n actions: [],\n };\n}\n\n/** Wraps a card as a bot-framework message attachment. */\nexport function buildAttachment(card: TeamsAdaptiveCard): TeamsCardAttachment {\n return { contentType: ATTACHMENT_CONTENT_TYPE, content: card };\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\n/**\n * Nests an IR action under an `aui` key so `decodeSubmitData` can invert it\n * back out of the merged `activity.value` object a Teams bot receives,\n * alongside whatever other named inputs shared the card.\n */\nexport function buildSubmitData(action: unknown): TeamsSubmitData {\n if (!isRecord(action)) return { aui: { type: \"action\" } };\n const { type: rawType, ...payload } = action;\n const type = typeof rawType === \"string\" ? rawType : \"action\";\n return {\n aui: {\n type,\n ...(Object.keys(payload).length > 0 ? { payload } : {}),\n },\n };\n}\n\n/** Builds an Action.Submit carrying `action` in the `aui` envelope. */\nexport function buildSubmitAction(\n title: string,\n action: unknown,\n mode?: \"secondary\",\n): TeamsSubmitAction {\n return {\n type: \"Action.Submit\",\n title,\n data: buildSubmitData(action),\n ...(mode !== undefined ? { mode } : {}),\n };\n}\n"],"mappings":";;AASA,MAAa,aAAa;;AAG1B,MAAa,uBACX;;AAGF,MAAa,0BACX;;AAGF,MAAa,sBAAsB;;;;;;;;AASnC,MAAa,eAAe;;;;;;;;AAS5B,MAAa,cAAc;AAI3B,SAAgB,kBAAkB,QAA6B;CAC7D,IAAI,WAAW,UACb,OAAO,gCAAgC,YAAY;CAErD,IAAI,WAAW,SAAS,OAAO;CAC/B,IAAI,WAAW,SACb,OAAO;CAET,OAAO;AACT;;AAGA,MAAa,0BAA0B;;AAGvC,MAAa,qBAAqB;;AAGlC,MAAa,gBAAgB;;AAG7B,MAAa,mBAAmB;;AAGhC,MAAa,oBAAoB;;AAGjC,MAAa,mBAAmB;AAEhC,MAAM,cAAc,IAAI,YAAY;;AAGpC,SAAgB,eAAe,OAAuB;CACpD,OAAO,YAAY,OAAO,KAAK,CAAC,CAAC;AACnC;;AAGA,SAAgB,UAAU,MAA6C;CACrE,OAAO;EACL,SAAS;EACT,MAAM;EACN,SAAA;EACA;EACA,SAAS,CAAC;CACZ;AACF;;AAGA,SAAgB,gBAAgB,MAA8C;CAC5E,OAAO;EAAE,aAAa;EAAyB,SAAS;CAAK;AAC/D;AAEA,MAAM,YAAY,UAChB,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;;;;;;AAOrE,SAAgB,gBAAgB,QAAkC;CAChE,IAAI,CAAC,SAAS,MAAM,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,EAAE;CACxD,MAAM,EAAE,MAAM,SAAS,GAAG,YAAY;CAEtC,OAAO,EACL,KAAK;EACH,MAHS,OAAO,YAAY,WAAW,UAAU;EAIjD,GAAI,OAAO,KAAK,OAAO,CAAC,CAAC,SAAS,IAAI,EAAE,QAAQ,IAAI,CAAC;CACvD,EACF;AACF;;AAGA,SAAgB,kBACd,OACA,QACA,MACmB;CACnB,OAAO;EACL,MAAM;EACN;EACA,MAAM,gBAAgB,MAAM;EAC5B,GAAI,SAAS,KAAA,IAAY,EAAE,KAAK,IAAI,CAAC;CACvC;AACF"}
1
+ {"version":3,"file":"constants.js","names":[],"sources":["../../src/teams/constants.ts"],"sourcesContent":["import type {\n TeamsAdaptiveCard,\n TeamsCardAttachment,\n TeamsCardElement,\n TeamsSubmitAction,\n TeamsSubmitData,\n} from \"./types\";\n\n/** The Adaptive Card schema version emitted by {@link toAdaptiveCard}: the Teams desktop/web cap (mobile clients cap at 1.2). */\nexport const AC_VERSION = \"1.5\";\n\n/** The `$schema` URL stamped on every emitted card. */\nexport const ADAPTIVE_CARD_SCHEMA =\n \"http://adaptivecards.io/schemas/adaptive-card.json\";\n\n/** The `contentType` stamped on every emitted attachment. */\nexport const ATTACHMENT_CONTENT_TYPE =\n \"application/vnd.microsoft.card.adaptive\";\n\n/** The maximum traversal depth, counted in visited values, not tree levels. */\nexport const MAX_TRAVERSAL_DEPTH = 64;\n\n/**\n * The element-nesting ceiling a caller observes. `boundNode` visits an element\n * and its `children` array separately, so one level of nesting spends two of\n * {@link MAX_TRAVERSAL_DEPTH}; it runs before the conversion walk and is never\n * the looser of the two. A root array costs one more level than a root object.\n */\nexport const MAX_ELEMENT_DEPTH = MAX_TRAVERSAL_DEPTH / 2;\n\n/**\n * The maximum number of entries kept at any single level (root, or a\n * `children` array at any depth) of the raw spec, before it ever reaches\n * `normalizeSpec`. Bounds a hostile array's reported `length` so the pre-pass\n * can never be made to walk further than this regardless of what the array\n * claims about itself.\n */\nexport const CHILDREN_CAP = 200;\n\n/**\n * The total number of nodes {@link boundSpec} will visit across one walk,\n * regardless of how many times a shared reference recurs. Bounds the\n * combinatorial work a DAG of shared or self-referential nodes would\n * otherwise force even though each individual array stays within\n * {@link CHILDREN_CAP}.\n */\nexport const NODE_BUDGET = 5000;\n\nexport type ClampReason = \"children\" | \"budget\" | \"cycle\" | \"depth\";\n\nexport function clampReasonDetail(reason: ClampReason): string {\n if (reason === \"budget\") {\n return `the tree was truncated after ${NODE_BUDGET} nodes.`;\n }\n if (reason === \"cycle\") return \"a self-referencing node was dropped.\";\n if (reason === \"depth\") {\n return `nodes deeper than ${MAX_ELEMENT_DEPTH} levels were dropped.`;\n }\n return `children were clamped to ${CHILDREN_CAP} entries.`;\n}\n\n/** The maximum number of attachments produced from a root-level Carousel. */\nexport const CAROUSEL_ATTACHMENT_CAP = 10;\n\n/** The maximum number of primary-mode actions in one ActionSet; extras are set to secondary mode instead of being dropped. */\nexport const PRIMARY_ACTION_CAP = 6;\n\n/** The maximum number of data rows in a Table. */\nexport const TABLE_ROW_CAP = 100;\n\n/** The maximum number of columns in a Table. */\nexport const TABLE_COLUMN_CAP = 20;\n\n/** The maximum number of choices in a Select or RadioGroup. */\nexport const CHOICE_OPTION_CAP = 100;\n\n/** The soft byte-count budget for a serialized card, past which a warning notes Teams' 100 KB bot message limit (the card is never truncated to fit it). */\nexport const PAYLOAD_SOFT_CAP = 80000;\n\nconst textEncoder = new TextEncoder();\n\n/** Measures the wire size of `value` in UTF-8 bytes rather than UTF-16 code units. */\nexport function utf8ByteLength(value: string): number {\n return textEncoder.encode(value).byteLength;\n}\n\n/** Builds the fixed-shape Adaptive Card envelope around a converted body. */\nexport function buildCard(body: TeamsCardElement[]): TeamsAdaptiveCard {\n return {\n $schema: ADAPTIVE_CARD_SCHEMA,\n type: \"AdaptiveCard\",\n version: AC_VERSION,\n body,\n actions: [],\n };\n}\n\n/** Wraps a card as a bot-framework message attachment. */\nexport function buildAttachment(card: TeamsAdaptiveCard): TeamsCardAttachment {\n return { contentType: ATTACHMENT_CONTENT_TYPE, content: card };\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\n/**\n * Nests an IR action under an `aui` key so `decodeSubmitData` can invert it\n * back out of the merged `activity.value` object a Teams bot receives,\n * alongside whatever other named inputs shared the card.\n */\nexport function buildSubmitData(action: unknown): TeamsSubmitData {\n if (!isRecord(action)) return { aui: { type: \"action\" } };\n const { type: rawType, ...payload } = action;\n const type = typeof rawType === \"string\" ? rawType : \"action\";\n return {\n aui: {\n type,\n ...(Object.keys(payload).length > 0 ? { payload } : {}),\n },\n };\n}\n\n/** Builds an Action.Submit carrying `action` in the `aui` envelope. */\nexport function buildSubmitAction(\n title: string,\n action: unknown,\n mode?: \"secondary\",\n): TeamsSubmitAction {\n return {\n type: \"Action.Submit\",\n title,\n data: buildSubmitData(action),\n ...(mode !== undefined ? { mode } : {}),\n };\n}\n"],"mappings":";;AASA,MAAa,aAAa;;AAG1B,MAAa,uBACX;;AAGF,MAAa,0BACX;;AAGF,MAAa,sBAAsB;;;;;;;AAQnC,MAAa,oBAAA;;;;;;;;AASb,MAAa,eAAe;;;;;;;;AAS5B,MAAa,cAAc;AAI3B,SAAgB,kBAAkB,QAA6B;CAC7D,IAAI,WAAW,UACb,OAAO,gCAAgC,YAAY;CAErD,IAAI,WAAW,SAAS,OAAO;CAC/B,IAAI,WAAW,SACb,OAAO;CAET,OAAO;AACT;;AAGA,MAAa,0BAA0B;;AAGvC,MAAa,qBAAqB;;AAGlC,MAAa,gBAAgB;;AAG7B,MAAa,mBAAmB;;AAGhC,MAAa,oBAAoB;;AAGjC,MAAa,mBAAmB;AAEhC,MAAM,cAAc,IAAI,YAAY;;AAGpC,SAAgB,eAAe,OAAuB;CACpD,OAAO,YAAY,OAAO,KAAK,CAAC,CAAC;AACnC;;AAGA,SAAgB,UAAU,MAA6C;CACrE,OAAO;EACL,SAAS;EACT,MAAM;EACN,SAAA;EACA;EACA,SAAS,CAAC;CACZ;AACF;;AAGA,SAAgB,gBAAgB,MAA8C;CAC5E,OAAO;EAAE,aAAa;EAAyB,SAAS;CAAK;AAC/D;AAEA,MAAM,YAAY,UAChB,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;;;;;;AAOrE,SAAgB,gBAAgB,QAAkC;CAChE,IAAI,CAAC,SAAS,MAAM,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,EAAE;CACxD,MAAM,EAAE,MAAM,SAAS,GAAG,YAAY;CAEtC,OAAO,EACL,KAAK;EACH,MAHS,OAAO,YAAY,WAAW,UAAU;EAIjD,GAAI,OAAO,KAAK,OAAO,CAAC,CAAC,SAAS,IAAI,EAAE,QAAQ,IAAI,CAAC;CACvD,EACF;AACF;;AAGA,SAAgB,kBACd,OACA,QACA,MACmB;CACnB,OAAO;EACL,MAAM;EACN;EACA,MAAM,gBAAgB,MAAM;EAC5B,GAAI,SAAS,KAAA,IAAY,EAAE,KAAK,IAAI,CAAC;CACvC;AACF"}
@@ -6,6 +6,14 @@ interface ConversionContext {
6
6
  readonly warnings: TeamsConversionWarning[];
7
7
  usedInputIds: Set<string>;
8
8
  }
9
+ /**
10
+ * Converts a child whose output is thrown away, and reports whether anything
11
+ * was lost with it. A scratch context keeps the ids it claims out of the card,
12
+ * where they would rename a control that survives; only its `dropped` warnings
13
+ * are forwarded, because those describe the tree the caller wrote, while a
14
+ * clamp or a rename would describe content never delivered.
15
+ */
16
+ declare const discardedChild: (child: NormalizedUINode, context: ConversionContext, depth: number) => boolean;
9
17
  declare function convertElement(element: NormalizedUIElement, context: ConversionContext, depth: number): TeamsCardElement[];
10
18
  /**
11
19
  * Walks a normalized sequence into card body elements. Nested arrays are
@@ -26,7 +34,7 @@ declare function convertSequence(node: NormalizedUINode | undefined, context: Co
26
34
  declare function convertRootToCard(root: NormalizedUINode | readonly NormalizedUINode[], context: ConversionContext): TeamsAdaptiveCard;
27
35
  /**
28
36
  * Converts a generative-ui tree into a Microsoft Teams Adaptive Card and
29
- * non-fatal downgrade warnings. Sizes, weights, and colors map to Adaptive
37
+ * non-fatal conversion warnings. Sizes, weights, and colors map to Adaptive
30
38
  * Card's semantic enums rather than raw values. An Input/Select/RadioGroup/
31
39
  * Checkbox/DatePicker whose id would be the reserved {@link RESERVED_INPUT_ID}
32
40
  * is renamed with a warning (see `decodeSubmitData`). Never throws: an
@@ -35,5 +43,5 @@ declare function convertRootToCard(root: NormalizedUINode | readonly NormalizedU
35
43
  */
36
44
  declare function toAdaptiveCard(node: unknown, _options?: ToAdaptiveCardOptions): AdaptiveCardResult;
37
45
  //#endregion
38
- export { ConversionContext, convertElement, convertRootToCard, convertSequence, toAdaptiveCard };
46
+ export { ConversionContext, convertElement, convertRootToCard, convertSequence, discardedChild, toAdaptiveCard };
39
47
  //# sourceMappingURL=toAdaptiveCard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"toAdaptiveCard.d.ts","names":[],"sources":["../../src/teams/toAdaptiveCard.ts"],"mappings":";;;;UAsCiB;WACN,UAAU;EACnB,cAAc;;iBAuTA,eACd,SAAS,qBACT,SAAS,mBACT,gBACC;;;;;;;;;;;;;;;iBAkRa,gBACd,MAAM,8BACN,SAAS,mBACT,gBACC;;iBA0Ea,kBACd,MAAM,4BAA4B,oBAClC,SAAS,oBACR;;;;;;;;;;iBAyBa,eACd,eACA,WAAW,wBACV"}
1
+ {"version":3,"file":"toAdaptiveCard.d.ts","names":[],"sources":["../../src/teams/toAdaptiveCard.ts"],"mappings":";;;;UAsCiB;WACN,UAAU;EACnB,cAAc;;;;;;;;;cAkVH,iBACX,OAAO,kBACP,SAAS,mBACT;iBAac,eACd,SAAS,qBACT,SAAS,mBACT,gBACC;;;;;;;;;;;;;;;iBAuSa,gBACd,MAAM,8BACN,SAAS,mBACT,gBACC;;iBA0Ea,kBACd,MAAM,4BAA4B,oBAClC,SAAS,oBACR;;;;;;;;;;iBAyBa,eACd,eACA,WAAW,wBACV"}
@@ -1,5 +1,5 @@
1
1
  import { normalizeSpec } from "../ir.js";
2
- import { buildCard, buildSubmitAction, clampReasonDetail, utf8ByteLength } from "./constants.js";
2
+ import { PAYLOAD_SOFT_CAP, buildCard, buildSubmitAction, clampReasonDetail, utf8ByteLength } from "./constants.js";
3
3
  import { boundSpec } from "./boundSpec.js";
4
4
  //#region src/teams/toAdaptiveCard.ts
5
5
  const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -51,8 +51,8 @@ function reservedSafeId(id, component, context) {
51
51
  candidate = `${base}_${n}`;
52
52
  n += 1;
53
53
  }
54
- if (reserved) warn(context, "clamped", component, `the input id "${RESERVED_INPUT_ID}" collides with the submit envelope's reserved key and was renamed to "${candidate}".`);
55
- else if (candidate !== base) warn(context, "clamped", component, `the input id "${base}" was already used on this card and was renamed to "${candidate}".`);
54
+ if (reserved) warn(context, "fallback", component, `the input id "${RESERVED_INPUT_ID}" collides with the submit envelope's reserved key and was renamed to "${candidate}".`);
55
+ else if (candidate !== base) warn(context, "fallback", component, `the input id "${base}" was already used on this card and was renamed to "${candidate}".`);
56
56
  context.usedInputIds.add(candidate);
57
57
  return candidate;
58
58
  }
@@ -107,6 +107,8 @@ const choicesFrom = (optionsProp, component, context) => {
107
107
  const choice = toChoice(item);
108
108
  if (choice !== void 0) choices.push(choice);
109
109
  }
110
+ const dropped = items.length - choices.length;
111
+ if (dropped > 0) warn(context, "dropped", component, `${dropped} ${dropped === 1 ? "option was" : "options were"} dropped for want of a string value.`);
110
112
  return choices;
111
113
  };
112
114
  function convertFacts(facts) {
@@ -119,7 +121,7 @@ function convertFacts(facts) {
119
121
  };
120
122
  }
121
123
  function convertButtons(buttons, context) {
122
- if (buttons.length > 6) warn(context, "clamped", "Button", `actions beyond 6 were set to secondary mode.`);
124
+ if (buttons.length > 6) warn(context, "fallback", "Button", `actions beyond 6 were set to secondary mode.`);
123
125
  return {
124
126
  type: "ActionSet",
125
127
  actions: buttons.map((button, index) => buildSubmitAction(asString(button.props["label"]), button.action, index >= 6 ? "secondary" : void 0))
@@ -156,6 +158,8 @@ function convertTable(props, context) {
156
158
  const { items: rawRows, truncated: rowsTruncated } = clampArray(props["rows"], 100);
157
159
  if (columnsTruncated) warn(context, "clamped", "Table", `columns were clamped to 20 entries.`);
158
160
  if (rowsTruncated) warn(context, "clamped", "Table", `rows were clamped to 100 entries.`);
161
+ const unlabeled = rawColumns.filter((column) => !isRecord(column) || typeof column["label"] !== "string").length;
162
+ if (unlabeled > 0) warn(context, "dropped", "Table", `${unlabeled} column ${unlabeled === 1 ? "header was" : "headers were"} left blank for want of a string label.`);
159
163
  const hasColumns = rawColumns.length > 0;
160
164
  const columns = rawColumns.map(() => ({ width: 1 }));
161
165
  const headerRow = hasColumns ? {
@@ -188,6 +192,24 @@ function convertListViewItem(element, context, depth) {
188
192
  ...selectAction !== void 0 ? { selectAction } : {}
189
193
  };
190
194
  }
195
+ /**
196
+ * Converts a child whose output is thrown away, and reports whether anything
197
+ * was lost with it. A scratch context keeps the ids it claims out of the card,
198
+ * where they would rename a control that survives; only its `dropped` warnings
199
+ * are forwarded, because those describe the tree the caller wrote, while a
200
+ * clamp or a rename would describe content never delivered.
201
+ */
202
+ const discardedChild = (child, context, depth) => {
203
+ const scratch = {
204
+ ...context,
205
+ warnings: [],
206
+ usedInputIds: new Set(context.usedInputIds)
207
+ };
208
+ const produced = convertSequence(child, scratch, depth).length > 0;
209
+ const lost = scratch.warnings.filter((warning) => warning.code === "dropped");
210
+ context.warnings.push(...lost);
211
+ return produced || lost.length > 0;
212
+ };
191
213
  function convertElement(element, context, depth) {
192
214
  const { props } = element;
193
215
  switch (element.type) {
@@ -328,7 +350,7 @@ function convertElement(element, context, depth) {
328
350
  }];
329
351
  case "Row": {
330
352
  const children = normalizedList(element.children);
331
- if (children.length > 3) warn(context, "clamped", "Row", "Teams recommends at most 3 columns.");
353
+ if (children.length > 3) warn(context, "advisory", "Row", "Teams recommends at most 3 columns.");
332
354
  return [{
333
355
  type: "ColumnSet",
334
356
  columns: children.map((child) => ({
@@ -340,11 +362,15 @@ function convertElement(element, context, depth) {
340
362
  }
341
363
  case "ListView": {
342
364
  const containers = [];
365
+ let discarded = 0;
343
366
  for (const child of normalizedList(element.children)) {
344
- if (!isElement(child)) continue;
345
- if (child.type === "ListViewItem") containers.push(convertListViewItem(child, context, depth + 1));
346
- else convertElement(child, context, depth + 1);
367
+ if (isElement(child) && child.type === "ListViewItem") {
368
+ containers.push(convertListViewItem(child, context, depth + 1));
369
+ continue;
370
+ }
371
+ if (discardedChild(child, context, depth + 1)) discarded += 1;
347
372
  }
373
+ if (discarded > 0) warn(context, "dropped", "ListView", `${discarded} non-item ${discarded === 1 ? "child was" : "children were"} dropped.`);
348
374
  return containers.map((container, index) => index > 0 ? {
349
375
  ...container,
350
376
  separator: true
@@ -366,9 +392,20 @@ function convertElement(element, context, depth) {
366
392
  items
367
393
  }];
368
394
  }
369
- case "Carousel":
395
+ case "Carousel": {
370
396
  warn(context, "fallback", "Carousel", "A carousel was rendered as sequential cards because it is not at the root.");
371
- return normalizedList(element.children).filter((child) => isElement(child) && child.type === "Card").flatMap((card) => convertElement(card, context, depth + 1));
397
+ const cards = [];
398
+ let droppedCards = 0;
399
+ for (const child of normalizedList(element.children)) {
400
+ if (isElement(child) && child.type === "Card") {
401
+ cards.push(child);
402
+ continue;
403
+ }
404
+ if (discardedChild(child, context, depth + 1)) droppedCards += 1;
405
+ }
406
+ if (droppedCards > 0) warn(context, "dropped", "Carousel", `${droppedCards} non-card ${droppedCards === 1 ? "child was" : "children were"} dropped.`);
407
+ return cards.flatMap((card) => convertElement(card, context, depth + 1));
408
+ }
372
409
  case "Chart":
373
410
  warn(context, "dropped", "Chart", "Chart was replaced by a Teams omission note.");
374
411
  return [textBlock("Chart omitted on Teams.", {
@@ -457,12 +494,12 @@ function convertRootToCard(root, context) {
457
494
  const body = convertSequence(root, context, 0);
458
495
  const card = buildCard(body);
459
496
  const size = utf8ByteLength(JSON.stringify(card));
460
- if (size > 8e4) warn(context, "clamped", "Root", `the card is ${size} bytes, exceeding Teams' 100 KB bot message limit.`);
497
+ if (size > 8e4) warn(context, "advisory", "Root", `the card is ${size} bytes, over the ${PAYLOAD_SOFT_CAP}-byte soft budget kept below Teams' 100 KB bot message limit.`);
461
498
  return card;
462
499
  }
463
500
  /**
464
501
  * Converts a generative-ui tree into a Microsoft Teams Adaptive Card and
465
- * non-fatal downgrade warnings. Sizes, weights, and colors map to Adaptive
502
+ * non-fatal conversion warnings. Sizes, weights, and colors map to Adaptive
466
503
  * Card's semantic enums rather than raw values. An Input/Select/RadioGroup/
467
504
  * Checkbox/DatePicker whose id would be the reserved {@link RESERVED_INPUT_ID}
468
505
  * is renamed with a warning (see `decodeSubmitData`). Never throws: an
@@ -493,6 +530,6 @@ function toAdaptiveCard(node, _options) {
493
530
  }
494
531
  }
495
532
  //#endregion
496
- export { convertElement, convertRootToCard, convertSequence, toAdaptiveCard };
533
+ export { convertElement, convertRootToCard, convertSequence, discardedChild, toAdaptiveCard };
497
534
 
498
535
  //# sourceMappingURL=toAdaptiveCard.js.map