@assistant-ui/react-generative-ui 0.0.15 → 0.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/convert/boundSpec.d.ts +51 -0
- package/dist/convert/boundSpec.d.ts.map +1 -0
- package/dist/convert/boundSpec.js +108 -0
- package/dist/convert/boundSpec.js.map +1 -0
- package/dist/convert/copyBounded.d.ts +28 -0
- package/dist/convert/copyBounded.d.ts.map +1 -0
- package/dist/convert/copyBounded.js +38 -0
- package/dist/convert/copyBounded.js.map +1 -0
- package/dist/convert/isElement.d.ts +6 -0
- package/dist/convert/isElement.d.ts.map +1 -0
- package/dist/convert/isElement.js +6 -0
- package/dist/convert/isElement.js.map +1 -0
- package/dist/convert/takeRun.d.ts +9 -0
- package/dist/convert/takeRun.d.ts.map +1 -0
- package/dist/convert/takeRun.js +20 -0
- package/dist/convert/takeRun.js.map +1 -0
- package/dist/slack/constants.d.ts +1 -26
- package/dist/slack/constants.d.ts.map +1 -1
- package/dist/slack/constants.js +1 -26
- package/dist/slack/constants.js.map +1 -1
- package/dist/slack/fromSlackBlocks.js +6 -10
- package/dist/slack/fromSlackBlocks.js.map +1 -1
- package/dist/slack/toSlackBlocks.d.ts.map +1 -1
- package/dist/slack/toSlackBlocks.js +28 -95
- package/dist/slack/toSlackBlocks.js.map +1 -1
- package/dist/teams/constants.d.ts +1 -28
- package/dist/teams/constants.d.ts.map +1 -1
- package/dist/teams/constants.js +1 -32
- package/dist/teams/constants.js.map +1 -1
- package/dist/teams/toAdaptiveCard.d.ts.map +1 -1
- package/dist/teams/toAdaptiveCard.js +12 -23
- package/dist/teams/toAdaptiveCard.js.map +1 -1
- package/dist/teams/toTeamsAttachments.d.ts.map +1 -1
- package/dist/teams/toTeamsAttachments.js +5 -9
- package/dist/teams/toTeamsAttachments.js.map +1 -1
- package/dist/vocabulary/list.js +4 -2
- package/dist/vocabulary/list.js.map +1 -1
- package/package.json +6 -6
- package/src/convert/boundSpec.test.ts +192 -0
- package/src/convert/boundSpec.ts +134 -0
- package/src/convert/copyBounded.test.ts +131 -0
- package/src/convert/copyBounded.ts +38 -0
- package/src/convert/isElement.ts +6 -0
- package/src/convert/takeRun.test.ts +64 -0
- package/src/convert/takeRun.ts +19 -0
- package/src/slack/constants.ts +0 -29
- package/src/slack/fromSlackBlocks.test.ts +105 -7
- package/src/slack/fromSlackBlocks.ts +9 -9
- package/src/slack/toSlackBlocks.test.ts +82 -2
- package/src/slack/toSlackBlocks.ts +68 -164
- package/src/teams/constants.ts +0 -42
- package/src/teams/toAdaptiveCard.test.ts +74 -2
- package/src/teams/toAdaptiveCard.ts +29 -36
- package/src/teams/toTeamsAttachments.ts +4 -17
- package/src/vocabulary/list.test.tsx +35 -9
- package/src/vocabulary/list.tsx +4 -2
- package/dist/teams/boundSpec.d.ts +0 -20
- package/dist/teams/boundSpec.d.ts.map +0 -1
- package/dist/teams/boundSpec.js +0 -67
- package/dist/teams/boundSpec.js.map +0 -1
- package/src/teams/boundSpec.ts +0 -102
|
@@ -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_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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"}
|
|
1
|
+
{"version":3,"file":"toSlackBlocks.js","names":[],"sources":["../../src/slack/toSlackBlocks.ts"],"sourcesContent":["import {\n MAX_TRAVERSAL_DEPTH,\n boundSpec,\n clampReasonDetail,\n} from \"../convert/boundSpec\";\nimport { copyBounded } from \"../convert/copyBounded\";\nimport { isElement } from \"../convert/isElement\";\nimport { takeRun } from \"../convert/takeRun\";\nimport {\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 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 MESSAGE_BLOCK_CAP,\n MODAL_BLOCK_CAP,\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 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 const { items: takenOptions, truncated } = copyBounded(\n rawOptions,\n SELECT_OPTION_CAP,\n );\n if (truncated) {\n warn(\n context,\n \"clamped\",\n \"Select\",\n `options were clamped to ${SELECT_OPTION_CAP} entries.`,\n );\n }\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 const { items: takenOptions, truncated } = copyBounded(\n rawOptions,\n RADIO_OPTION_CAP,\n );\n if (truncated) {\n warn(\n context,\n \"clamped\",\n \"RadioGroup\",\n `options were clamped to ${RADIO_OPTION_CAP} entries.`,\n );\n }\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 const { items: boundedButtons, truncated } = copyBounded(\n buttons,\n CARD_ACTIONS_CAP,\n );\n if (truncated) {\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 ...(boundedButtons.length > 0 ? { actions: boundedButtons } : {}),\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 const { items: boundedChildren, truncated } = copyBounded(\n children,\n CONTEXT_ELEMENT_CAP,\n );\n if (truncated) {\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: boundedChildren.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 const { items: boundedCards, truncated } = copyBounded(\n cardChildren,\n CAROUSEL_CARD_CAP,\n );\n if (truncated) {\n warn(\n context,\n \"clamped\",\n \"Carousel\",\n `cards were clamped to ${CAROUSEL_CARD_CAP} entries.`,\n );\n }\n const cards = boundedCards\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 const { items: takenColumns, truncated: columnsTruncated } = copyBounded(\n rawColumns,\n DATA_TABLE_COLUMN_CAP,\n );\n const { items: takenRows, truncated: rowsTruncated } = copyBounded(\n rawRows,\n DATA_TABLE_ROW_CAP,\n );\n if (columnsTruncated) {\n warn(\n context,\n \"clamped\",\n \"Table\",\n `columns were clamped to ${DATA_TABLE_COLUMN_CAP} entries.`,\n );\n }\n if (rowsTruncated) {\n warn(\n context,\n \"clamped\",\n \"Table\",\n `rows were clamped to ${DATA_TABLE_ROW_CAP} entries.`,\n );\n }\n\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[][] = takenRows.map((row) =>\n (Array.isArray(row)\n ? copyBounded(row, DATA_TABLE_COLUMN_CAP).items\n : []\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 { run: facts, next } = takeRun(\n nodes,\n index,\n (candidate) => candidate.type === \"Fact\",\n );\n index = next;\n blocks.push(...convertFacts(facts, context));\n continue;\n }\n if (isElement(current) && INTERACTIVE_TYPES.has(current.type)) {\n const { run: controls, next } = takeRun(nodes, index, (candidate) =>\n INTERACTIVE_TYPES.has(candidate.type),\n );\n index = next;\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\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 warn(context, \"clamped\", \"Root\", clampReasonDetail(reason)),\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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"}
|
|
@@ -6,33 +6,6 @@ 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 traversal depth, counted in visited values, not tree levels. */
|
|
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;
|
|
18
|
-
/**
|
|
19
|
-
* The maximum number of entries kept at any single level (root, or a
|
|
20
|
-
* `children` array at any depth) of the raw spec, before it ever reaches
|
|
21
|
-
* `normalizeSpec`. Bounds a hostile array's reported `length` so the pre-pass
|
|
22
|
-
* can never be made to walk further than this regardless of what the array
|
|
23
|
-
* claims about itself.
|
|
24
|
-
*/
|
|
25
|
-
declare const CHILDREN_CAP = 200;
|
|
26
|
-
/**
|
|
27
|
-
* The total number of nodes {@link boundSpec} will visit across one walk,
|
|
28
|
-
* regardless of how many times a shared reference recurs. Bounds the
|
|
29
|
-
* combinatorial work a DAG of shared or self-referential nodes would
|
|
30
|
-
* otherwise force even though each individual array stays within
|
|
31
|
-
* {@link CHILDREN_CAP}.
|
|
32
|
-
*/
|
|
33
|
-
declare const NODE_BUDGET = 5000;
|
|
34
|
-
type ClampReason = "children" | "budget" | "cycle" | "depth";
|
|
35
|
-
declare function clampReasonDetail(reason: ClampReason): string;
|
|
36
9
|
/** The maximum number of attachments produced from a root-level Carousel. */
|
|
37
10
|
declare const CAROUSEL_ATTACHMENT_CAP = 10;
|
|
38
11
|
/** The maximum number of primary-mode actions in one ActionSet; extras are set to secondary mode instead of being dropped. */
|
|
@@ -60,5 +33,5 @@ declare function buildSubmitData(action: unknown): TeamsSubmitData;
|
|
|
60
33
|
/** Builds an Action.Submit carrying `action` in the `aui` envelope. */
|
|
61
34
|
declare function buildSubmitAction(title: string, action: unknown, mode?: "secondary"): TeamsSubmitAction;
|
|
62
35
|
//#endregion
|
|
63
|
-
export { AC_VERSION, ADAPTIVE_CARD_SCHEMA, ATTACHMENT_CONTENT_TYPE, CAROUSEL_ATTACHMENT_CAP,
|
|
36
|
+
export { AC_VERSION, ADAPTIVE_CARD_SCHEMA, ATTACHMENT_CONTENT_TYPE, CAROUSEL_ATTACHMENT_CAP, CHOICE_OPTION_CAP, PAYLOAD_SOFT_CAP, PRIMARY_ACTION_CAP, TABLE_COLUMN_CAP, TABLE_ROW_CAP, buildAttachment, buildCard, buildSubmitAction, buildSubmitData, utf8ByteLength };
|
|
64
37
|
//# 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
|
|
1
|
+
{"version":3,"file":"constants.d.ts","names":[],"sources":["../../src/teams/constants.ts"],"mappings":";;;cASa;;cAGA;;cAIA;;cAIA;;cAGA;;cAGA;;cAGA;;cAGA;;cAGA;;iBAKG,eAAe;;iBAKf,UAAU,MAAM,qBAAqB;;iBAWrC,gBAAgB,MAAM,oBAAoB;;;;;;iBAY1C,gBAAgB,kBAAkB;;iBAalC,kBACd,eACA,iBACA,qBACC"}
|
package/dist/teams/constants.js
CHANGED
|
@@ -5,37 +5,6 @@ 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 traversal depth, counted in visited values, not tree levels. */
|
|
9
|
-
const MAX_TRAVERSAL_DEPTH = 64;
|
|
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
|
-
/**
|
|
18
|
-
* The maximum number of entries kept at any single level (root, or a
|
|
19
|
-
* `children` array at any depth) of the raw spec, before it ever reaches
|
|
20
|
-
* `normalizeSpec`. Bounds a hostile array's reported `length` so the pre-pass
|
|
21
|
-
* can never be made to walk further than this regardless of what the array
|
|
22
|
-
* claims about itself.
|
|
23
|
-
*/
|
|
24
|
-
const CHILDREN_CAP = 200;
|
|
25
|
-
/**
|
|
26
|
-
* The total number of nodes {@link boundSpec} will visit across one walk,
|
|
27
|
-
* regardless of how many times a shared reference recurs. Bounds the
|
|
28
|
-
* combinatorial work a DAG of shared or self-referential nodes would
|
|
29
|
-
* otherwise force even though each individual array stays within
|
|
30
|
-
* {@link CHILDREN_CAP}.
|
|
31
|
-
*/
|
|
32
|
-
const NODE_BUDGET = 5e3;
|
|
33
|
-
function clampReasonDetail(reason) {
|
|
34
|
-
if (reason === "budget") return `the tree was truncated after ${NODE_BUDGET} nodes.`;
|
|
35
|
-
if (reason === "cycle") return "a self-referencing node was dropped.";
|
|
36
|
-
if (reason === "depth") return `nodes deeper than 32 levels were dropped.`;
|
|
37
|
-
return `children were clamped to 200 entries.`;
|
|
38
|
-
}
|
|
39
8
|
/** The maximum number of attachments produced from a root-level Carousel. */
|
|
40
9
|
const CAROUSEL_ATTACHMENT_CAP = 10;
|
|
41
10
|
/** The maximum number of primary-mode actions in one ActionSet; extras are set to secondary mode instead of being dropped. */
|
|
@@ -94,6 +63,6 @@ function buildSubmitAction(title, action, mode) {
|
|
|
94
63
|
};
|
|
95
64
|
}
|
|
96
65
|
//#endregion
|
|
97
|
-
export { AC_VERSION, ADAPTIVE_CARD_SCHEMA, ATTACHMENT_CONTENT_TYPE, CAROUSEL_ATTACHMENT_CAP,
|
|
66
|
+
export { AC_VERSION, ADAPTIVE_CARD_SCHEMA, ATTACHMENT_CONTENT_TYPE, CAROUSEL_ATTACHMENT_CAP, CHOICE_OPTION_CAP, PAYLOAD_SOFT_CAP, PRIMARY_ACTION_CAP, TABLE_COLUMN_CAP, TABLE_ROW_CAP, buildAttachment, buildCard, buildSubmitAction, buildSubmitData, utf8ByteLength };
|
|
98
67
|
|
|
99
68
|
//# 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
|
|
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 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,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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toAdaptiveCard.d.ts","names":[],"sources":["../../src/teams/toAdaptiveCard.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"toAdaptiveCard.d.ts","names":[],"sources":["../../src/teams/toAdaptiveCard.ts"],"mappings":";;;;UA2CiB;WACN,UAAU;EACnB,cAAc;;;;;;;;;cAgVH,iBACX,OAAO,kBACP,SAAS,mBACT;iBAac,eACd,SAAS,qBACT,SAAS,mBACT,gBACC;;;;;;;;;;;;;;;iBAuSa,gBACd,MAAM,8BACN,SAAS,mBACT,gBACC;;iBAgEa,kBACd,MAAM,4BAA4B,oBAClC,SAAS,oBACR;;;;;;;;;;iBAyBa,eACd,eACA,WAAW,wBACV"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { normalizeSpec } from "../ir.js";
|
|
2
|
-
import {
|
|
3
|
-
import { boundSpec } from "
|
|
2
|
+
import { copyBounded } from "../convert/copyBounded.js";
|
|
3
|
+
import { boundSpec, clampReasonDetail } from "../convert/boundSpec.js";
|
|
4
|
+
import { isElement } from "../convert/isElement.js";
|
|
5
|
+
import { takeRun } from "../convert/takeRun.js";
|
|
6
|
+
import { PAYLOAD_SOFT_CAP, buildCard, buildSubmitAction, utf8ByteLength } from "./constants.js";
|
|
4
7
|
//#region src/teams/toAdaptiveCard.ts
|
|
5
8
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
|
-
const isElement = (node) => isRecord(node);
|
|
7
9
|
const asString = (value) => typeof value === "string" ? value : "";
|
|
8
10
|
const DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
|
|
9
11
|
const SUBTLE_COLORS = /* @__PURE__ */ new Set([
|
|
@@ -57,7 +59,7 @@ function reservedSafeId(id, component, context) {
|
|
|
57
59
|
return candidate;
|
|
58
60
|
}
|
|
59
61
|
/**
|
|
60
|
-
* The shared bounded-iteration primitive:
|
|
62
|
+
* The shared bounded-iteration primitive: copies `value` to `cap` entries
|
|
61
63
|
* without ever reading past that many indices, so a hostile array (sparse or
|
|
62
64
|
* proxied with a fabricated `length`) cannot stall the event loop.
|
|
63
65
|
*/
|
|
@@ -66,10 +68,7 @@ const clampArray = (value, cap) => {
|
|
|
66
68
|
items: [],
|
|
67
69
|
truncated: false
|
|
68
70
|
};
|
|
69
|
-
return
|
|
70
|
-
items: value.slice(0, cap),
|
|
71
|
-
truncated: value.length > cap
|
|
72
|
-
};
|
|
71
|
+
return copyBounded(value, cap);
|
|
73
72
|
};
|
|
74
73
|
const normalizedList = (node) => {
|
|
75
74
|
if (node === void 0 || node === null) return [];
|
|
@@ -171,7 +170,7 @@ function convertTable(props, context) {
|
|
|
171
170
|
} : void 0;
|
|
172
171
|
const dataRows = rawRows.map((row) => ({
|
|
173
172
|
type: "TableRow",
|
|
174
|
-
cells: (Array.isArray(row) ? row
|
|
173
|
+
cells: (Array.isArray(row) ? copyBounded(row, 20).items : []).map((cell) => ({
|
|
175
174
|
type: "TableCell",
|
|
176
175
|
items: [textBlock(stringifyCell(cell))]
|
|
177
176
|
}))
|
|
@@ -461,24 +460,14 @@ function convertSequence(node, context, depth) {
|
|
|
461
460
|
continue;
|
|
462
461
|
}
|
|
463
462
|
if (isElement(current) && current.type === "Fact") {
|
|
464
|
-
const facts =
|
|
465
|
-
|
|
466
|
-
const candidate = nodes[index];
|
|
467
|
-
if (!candidate || !isElement(candidate) || candidate.type !== "Fact") break;
|
|
468
|
-
facts.push(candidate);
|
|
469
|
-
index += 1;
|
|
470
|
-
}
|
|
463
|
+
const { run: facts, next } = takeRun(nodes, index, (candidate) => candidate.type === "Fact");
|
|
464
|
+
index = next;
|
|
471
465
|
emit([convertFacts(facts)]);
|
|
472
466
|
continue;
|
|
473
467
|
}
|
|
474
468
|
if (isElement(current) && current.type === "Button") {
|
|
475
|
-
const buttons =
|
|
476
|
-
|
|
477
|
-
const candidate = nodes[index];
|
|
478
|
-
if (!candidate || !isElement(candidate) || candidate.type !== "Button") break;
|
|
479
|
-
buttons.push(candidate);
|
|
480
|
-
index += 1;
|
|
481
|
-
}
|
|
469
|
+
const { run: buttons, next } = takeRun(nodes, index, (candidate) => candidate.type === "Button");
|
|
470
|
+
index = next;
|
|
482
471
|
emit([convertButtons(buttons, context)]);
|
|
483
472
|
continue;
|
|
484
473
|
}
|