@assistant-ui/react-generative-ui 0.0.6 → 0.0.8

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 (201) hide show
  1. package/dist/JSONGenerativeUI.client.d.ts +4 -2
  2. package/dist/JSONGenerativeUI.client.d.ts.map +1 -1
  3. package/dist/JSONGenerativeUI.client.js +9 -2
  4. package/dist/JSONGenerativeUI.client.js.map +1 -1
  5. package/dist/JSONGenerativeUI.server.d.ts +0 -1
  6. package/dist/JSONGenerativeUI.server.d.ts.map +1 -1
  7. package/dist/JSONGenerativeUI.shared.d.ts +8 -1
  8. package/dist/JSONGenerativeUI.shared.d.ts.map +1 -1
  9. package/dist/JSONGenerativeUI.shared.js.map +1 -1
  10. package/dist/actionRegistry.d.ts +32 -0
  11. package/dist/actionRegistry.d.ts.map +1 -0
  12. package/dist/actionRegistry.js +41 -0
  13. package/dist/actionRegistry.js.map +1 -0
  14. package/dist/buildPresentParameters.d.ts +5 -5
  15. package/dist/buildPresentParameters.d.ts.map +1 -1
  16. package/dist/buildPresentParameters.js +20 -6
  17. package/dist/buildPresentParameters.js.map +1 -1
  18. package/dist/constants.d.ts.map +1 -1
  19. package/dist/defineGenerativeComponents.d.ts +0 -1
  20. package/dist/defineGenerativeComponents.d.ts.map +1 -1
  21. package/dist/generativeUIToJSX.d.ts +15 -10
  22. package/dist/generativeUIToJSX.d.ts.map +1 -1
  23. package/dist/generativeUIToJSX.js +47 -17
  24. package/dist/generativeUIToJSX.js.map +1 -1
  25. package/dist/index.d.ts +8 -4
  26. package/dist/index.js +5 -2
  27. package/dist/index.server.d.ts +13 -0
  28. package/dist/index.server.js +10 -0
  29. package/dist/index.shared.d.ts +11 -0
  30. package/dist/index.shared.js +9 -0
  31. package/dist/ir.d.ts +115 -0
  32. package/dist/ir.d.ts.map +1 -0
  33. package/dist/ir.js +175 -0
  34. package/dist/ir.js.map +1 -0
  35. package/dist/node_modules/.pnpm/@types_json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts +3 -3
  36. package/dist/node_modules/.pnpm/@types_json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts.map +1 -1
  37. package/dist/renderGenerativeUI.d.ts +5 -5
  38. package/dist/renderGenerativeUI.d.ts.map +1 -1
  39. package/dist/renderGenerativeUI.js +18 -45
  40. package/dist/renderGenerativeUI.js.map +1 -1
  41. package/dist/slack/constants.d.ts +111 -0
  42. package/dist/slack/constants.d.ts.map +1 -0
  43. package/dist/slack/constants.js +168 -0
  44. package/dist/slack/constants.js.map +1 -0
  45. package/dist/slack/decodeBlockAction.d.ts +11 -0
  46. package/dist/slack/decodeBlockAction.d.ts.map +1 -0
  47. package/dist/slack/decodeBlockAction.js +40 -0
  48. package/dist/slack/decodeBlockAction.js.map +1 -0
  49. package/dist/slack/fromSlackBlocks.d.ts +30 -0
  50. package/dist/slack/fromSlackBlocks.d.ts.map +1 -0
  51. package/dist/slack/fromSlackBlocks.js +447 -0
  52. package/dist/slack/fromSlackBlocks.js.map +1 -0
  53. package/dist/slack/toSlackBlocks.d.ts +7 -0
  54. package/dist/slack/toSlackBlocks.d.ts.map +1 -0
  55. package/dist/slack/toSlackBlocks.js +649 -0
  56. package/dist/slack/toSlackBlocks.js.map +1 -0
  57. package/dist/slack/types.d.ts +178 -0
  58. package/dist/slack/types.d.ts.map +1 -0
  59. package/dist/slack/types.js +0 -0
  60. package/dist/slack.d.ts +5 -0
  61. package/dist/slack.js +4 -0
  62. package/dist/teams/boundSpec.d.ts +20 -0
  63. package/dist/teams/boundSpec.d.ts.map +1 -0
  64. package/dist/teams/boundSpec.js +67 -0
  65. package/dist/teams/boundSpec.js.map +1 -0
  66. package/dist/teams/constants.d.ts +57 -0
  67. package/dist/teams/constants.d.ts.map +1 -0
  68. package/dist/teams/constants.js +92 -0
  69. package/dist/teams/constants.js.map +1 -0
  70. package/dist/teams/decodeSubmitData.d.ts +25 -0
  71. package/dist/teams/decodeSubmitData.d.ts.map +1 -0
  72. package/dist/teams/decodeSubmitData.js +44 -0
  73. package/dist/teams/decodeSubmitData.js.map +1 -0
  74. package/dist/teams/toAdaptiveCard.d.ts +39 -0
  75. package/dist/teams/toAdaptiveCard.d.ts.map +1 -0
  76. package/dist/teams/toAdaptiveCard.js +496 -0
  77. package/dist/teams/toAdaptiveCard.js.map +1 -0
  78. package/dist/teams/toTeamsAttachments.d.ts +13 -0
  79. package/dist/teams/toTeamsAttachments.d.ts.map +1 -0
  80. package/dist/teams/toTeamsAttachments.js +95 -0
  81. package/dist/teams/toTeamsAttachments.js.map +1 -0
  82. package/dist/teams/types.d.ts +196 -0
  83. package/dist/teams/types.d.ts.map +1 -0
  84. package/dist/teams/types.js +0 -0
  85. package/dist/teams.d.ts +5 -0
  86. package/dist/teams.js +4 -0
  87. package/dist/types.d.ts +41 -34
  88. package/dist/types.d.ts.map +1 -1
  89. package/dist/vocabulary/alert.d.ts +28 -0
  90. package/dist/vocabulary/alert.d.ts.map +1 -0
  91. package/dist/vocabulary/alert.js +58 -0
  92. package/dist/vocabulary/alert.js.map +1 -0
  93. package/dist/vocabulary/collectFormValues.d.ts +23 -0
  94. package/dist/vocabulary/collectFormValues.d.ts.map +1 -0
  95. package/dist/vocabulary/collectFormValues.js +32 -0
  96. package/dist/vocabulary/collectFormValues.js.map +1 -0
  97. package/dist/vocabulary/data.d.ts +52 -0
  98. package/dist/vocabulary/data.d.ts.map +1 -0
  99. package/dist/vocabulary/data.js +294 -0
  100. package/dist/vocabulary/data.js.map +1 -0
  101. package/dist/vocabulary/dispatch.d.ts +11 -0
  102. package/dist/vocabulary/dispatch.d.ts.map +1 -0
  103. package/dist/vocabulary/dispatch.js +21 -0
  104. package/dist/vocabulary/dispatch.js.map +1 -0
  105. package/dist/vocabulary/fact.d.ts +15 -0
  106. package/dist/vocabulary/fact.d.ts.map +1 -0
  107. package/dist/vocabulary/fact.js +24 -0
  108. package/dist/vocabulary/fact.js.map +1 -0
  109. package/dist/vocabulary/form.d.ts +14 -0
  110. package/dist/vocabulary/form.d.ts.map +1 -0
  111. package/dist/vocabulary/form.js +23 -0
  112. package/dist/vocabulary/form.js.map +1 -0
  113. package/dist/vocabulary/icon.d.ts +44 -0
  114. package/dist/vocabulary/icon.d.ts.map +1 -0
  115. package/dist/vocabulary/icon.js +272 -0
  116. package/dist/vocabulary/icon.js.map +1 -0
  117. package/dist/vocabulary/index.d.ts +15 -0
  118. package/dist/vocabulary/index.d.ts.map +1 -0
  119. package/dist/vocabulary/index.js +36 -0
  120. package/dist/vocabulary/index.js.map +1 -0
  121. package/dist/vocabulary/interactive.d.ts +93 -0
  122. package/dist/vocabulary/interactive.d.ts.map +1 -0
  123. package/dist/vocabulary/interactive.js +172 -0
  124. package/dist/vocabulary/interactive.js.map +1 -0
  125. package/dist/vocabulary/layout.d.ts +82 -0
  126. package/dist/vocabulary/layout.d.ts.map +1 -0
  127. package/dist/vocabulary/layout.js +136 -0
  128. package/dist/vocabulary/layout.js.map +1 -0
  129. package/dist/vocabulary/list.d.ts +17 -0
  130. package/dist/vocabulary/list.d.ts.map +1 -0
  131. package/dist/vocabulary/list.js +48 -0
  132. package/dist/vocabulary/list.js.map +1 -0
  133. package/dist/vocabulary/media.d.ts +28 -0
  134. package/dist/vocabulary/media.d.ts.map +1 -0
  135. package/dist/vocabulary/media.js +42 -0
  136. package/dist/vocabulary/media.js.map +1 -0
  137. package/dist/vocabulary/text.d.ts +60 -0
  138. package/dist/vocabulary/text.d.ts.map +1 -0
  139. package/dist/vocabulary/text.js +48 -0
  140. package/dist/vocabulary/text.js.map +1 -0
  141. package/package.json +21 -13
  142. package/src/JSONGenerativeUI.client.tsx +10 -2
  143. package/src/JSONGenerativeUI.shared.ts +8 -0
  144. package/src/actionRegistry.test.ts +90 -0
  145. package/src/actionRegistry.ts +88 -0
  146. package/src/buildPresentParameters.ts +38 -16
  147. package/src/generativeUIToJSX.test.ts +186 -0
  148. package/src/generativeUIToJSX.ts +112 -21
  149. package/src/index.server.ts +2 -0
  150. package/src/index.shared.ts +61 -0
  151. package/src/index.ts +2 -26
  152. package/src/ir.test.ts +203 -0
  153. package/src/ir.ts +279 -0
  154. package/src/renderGenerativeUI.test.tsx +69 -2
  155. package/src/renderGenerativeUI.tsx +49 -82
  156. package/src/slack/constants.ts +217 -0
  157. package/src/slack/decodeBlockAction.test.ts +135 -0
  158. package/src/slack/decodeBlockAction.ts +66 -0
  159. package/src/slack/fromSlackBlocks.test.ts +1145 -0
  160. package/src/slack/fromSlackBlocks.ts +709 -0
  161. package/src/slack/toSlackBlocks.test.ts +1936 -0
  162. package/src/slack/toSlackBlocks.ts +1319 -0
  163. package/src/slack/types.ts +230 -0
  164. package/src/slack.ts +37 -0
  165. package/src/teams/boundSpec.ts +102 -0
  166. package/src/teams/constants.ts +127 -0
  167. package/src/teams/decodeSubmitData.test.ts +148 -0
  168. package/src/teams/decodeSubmitData.ts +57 -0
  169. package/src/teams/toAdaptiveCard.test.ts +1410 -0
  170. package/src/teams/toAdaptiveCard.ts +762 -0
  171. package/src/teams/toTeamsAttachments.test.ts +29 -0
  172. package/src/teams/toTeamsAttachments.ts +137 -0
  173. package/src/teams/types.ts +237 -0
  174. package/src/teams.ts +34 -0
  175. package/src/types.ts +57 -42
  176. package/src/vocabulary/alert.test.tsx +136 -0
  177. package/src/vocabulary/alert.tsx +66 -0
  178. package/src/vocabulary/collectFormValues.test.ts +127 -0
  179. package/src/vocabulary/collectFormValues.ts +58 -0
  180. package/src/vocabulary/data.test.tsx +567 -0
  181. package/src/vocabulary/data.tsx +494 -0
  182. package/src/vocabulary/dispatch.ts +22 -0
  183. package/src/vocabulary/fact.test.tsx +15 -0
  184. package/src/vocabulary/fact.tsx +22 -0
  185. package/src/vocabulary/form.test.tsx +112 -0
  186. package/src/vocabulary/form.tsx +34 -0
  187. package/src/vocabulary/icon.test.tsx +64 -0
  188. package/src/vocabulary/icon.tsx +174 -0
  189. package/src/vocabulary/index.test.ts +63 -0
  190. package/src/vocabulary/index.ts +33 -0
  191. package/src/vocabulary/interactive.dispatch.test.tsx +352 -0
  192. package/src/vocabulary/interactive.test.tsx +248 -0
  193. package/src/vocabulary/interactive.tsx +269 -0
  194. package/src/vocabulary/layout.test.tsx +289 -0
  195. package/src/vocabulary/layout.tsx +229 -0
  196. package/src/vocabulary/list.test.tsx +195 -0
  197. package/src/vocabulary/list.tsx +48 -0
  198. package/src/vocabulary/media.test.tsx +85 -0
  199. package/src/vocabulary/media.tsx +56 -0
  200. package/src/vocabulary/text.test.tsx +73 -0
  201. package/src/vocabulary/text.tsx +59 -0
@@ -0,0 +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":"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"}
@@ -0,0 +1,178 @@
1
+ import { UIElement } from "../ir.js";
2
+ //#region src/slack/types.d.ts
3
+ /** A Slack plain-text composition object. */
4
+ interface SlackPlainText {
5
+ readonly type: "plain_text";
6
+ readonly text: string;
7
+ }
8
+ /** A Slack mrkdwn composition object. */
9
+ interface SlackMrkdwnText {
10
+ readonly type: "mrkdwn";
11
+ readonly text: string;
12
+ }
13
+ /** A text composition object accepted by card and alert text fields. */
14
+ type SlackTextObject = SlackPlainText | SlackMrkdwnText;
15
+ /** A selectable Slack option. */
16
+ interface SlackOption {
17
+ readonly text: SlackPlainText;
18
+ readonly value: string;
19
+ }
20
+ /** A Slack button element. */
21
+ interface SlackButtonElement {
22
+ readonly type: "button";
23
+ readonly text: SlackPlainText;
24
+ readonly action_id: string;
25
+ readonly value?: string;
26
+ readonly style?: "primary" | "danger";
27
+ }
28
+ /** A Slack static-select element. */
29
+ interface SlackStaticSelectElement {
30
+ readonly type: "static_select";
31
+ readonly action_id: string;
32
+ readonly options: readonly SlackOption[];
33
+ readonly placeholder?: SlackPlainText;
34
+ }
35
+ /** A Slack date-picker element. */
36
+ interface SlackDatePickerElement {
37
+ readonly type: "datepicker";
38
+ readonly action_id: string;
39
+ readonly initial_date?: string;
40
+ }
41
+ /** A Slack checkbox-group element. */
42
+ interface SlackCheckboxesElement {
43
+ readonly type: "checkboxes";
44
+ readonly action_id: string;
45
+ readonly options: readonly SlackOption[];
46
+ readonly initial_options?: readonly SlackOption[];
47
+ }
48
+ /** A Slack radio-button element. */
49
+ interface SlackRadioButtonsElement {
50
+ readonly type: "radio_buttons";
51
+ readonly action_id: string;
52
+ readonly options: readonly SlackOption[];
53
+ readonly initial_option?: SlackOption;
54
+ }
55
+ /** A Slack plain-text input element. */
56
+ interface SlackPlainTextInputElement {
57
+ readonly type: "plain_text_input";
58
+ readonly action_id: string;
59
+ readonly multiline?: boolean;
60
+ readonly placeholder?: SlackPlainText;
61
+ }
62
+ /** An element accepted by a Slack actions block. */
63
+ type SlackActionElement = SlackButtonElement | SlackStaticSelectElement | SlackDatePickerElement | SlackCheckboxesElement | SlackRadioButtonsElement;
64
+ /** A Slack header block. */
65
+ interface SlackHeaderBlock {
66
+ readonly type: "header";
67
+ readonly text: SlackPlainText;
68
+ }
69
+ /** A Slack section block. */
70
+ interface SlackSectionBlock {
71
+ readonly type: "section";
72
+ readonly text?: SlackMrkdwnText;
73
+ readonly fields?: readonly SlackMrkdwnText[];
74
+ readonly accessory?: SlackButtonElement;
75
+ }
76
+ /** A Slack context block. */
77
+ interface SlackContextBlock {
78
+ readonly type: "context";
79
+ readonly elements: readonly SlackMrkdwnText[];
80
+ }
81
+ /** A Slack image block. */
82
+ interface SlackImageBlock {
83
+ readonly type: "image";
84
+ readonly image_url: string;
85
+ readonly alt_text: string;
86
+ }
87
+ /** A Slack divider block. */
88
+ interface SlackDividerBlock {
89
+ readonly type: "divider";
90
+ }
91
+ /** A Slack actions block. */
92
+ interface SlackActionsBlock {
93
+ readonly type: "actions";
94
+ readonly elements: readonly SlackActionElement[];
95
+ }
96
+ /** A Slack input block. */
97
+ interface SlackInputBlock {
98
+ readonly type: "input";
99
+ readonly label: SlackPlainText;
100
+ readonly element: SlackPlainTextInputElement;
101
+ }
102
+ /** A text cell in a Slack data-table block. */
103
+ interface SlackDataTableRawTextCell {
104
+ readonly type: "raw_text";
105
+ readonly text: string;
106
+ }
107
+ /** A numeric cell in a Slack data-table block. */
108
+ interface SlackDataTableRawNumberCell {
109
+ readonly type: "raw_number";
110
+ readonly text: string;
111
+ }
112
+ /** A cell value in a Slack data-table block. */
113
+ type SlackDataTableCell = SlackDataTableRawTextCell | SlackDataTableRawNumberCell;
114
+ /**
115
+ * A Slack card block. A card does not nest arbitrary blocks: content is
116
+ * limited to a hero image, title, subtitle, body, subtext, and up to three
117
+ * action buttons. At least one of `hero_image`, `title`, `actions`, or `body`
118
+ * is required.
119
+ */
120
+ interface SlackCardBlock {
121
+ readonly type: "card";
122
+ readonly hero_image?: SlackImageBlock;
123
+ readonly title?: SlackTextObject;
124
+ readonly subtitle?: SlackTextObject;
125
+ readonly body?: SlackTextObject;
126
+ readonly subtext?: SlackTextObject;
127
+ readonly actions?: readonly SlackButtonElement[];
128
+ }
129
+ /** A Slack carousel block: a horizontally scrollable group of card blocks. */
130
+ interface SlackCarouselBlock {
131
+ readonly type: "carousel";
132
+ readonly elements: readonly SlackCardBlock[];
133
+ }
134
+ /** A Slack data-table block. The first entry in `rows` is the header row. */
135
+ interface SlackDataTableBlock {
136
+ readonly type: "data_table";
137
+ readonly caption: string;
138
+ readonly rows: readonly (readonly SlackDataTableCell[])[];
139
+ readonly page_size?: number;
140
+ }
141
+ /** A Slack markdown block. */
142
+ interface SlackMarkdownBlock {
143
+ readonly type: "markdown";
144
+ readonly text: string;
145
+ }
146
+ /** The severity level accepted by a Slack alert block. */
147
+ type SlackAlertLevel = "default" | "info" | "warning" | "error" | "success";
148
+ /** A Slack alert block. Alert blocks are only supported in modals. */
149
+ interface SlackAlertBlock {
150
+ readonly type: "alert";
151
+ readonly text: SlackTextObject;
152
+ readonly level?: SlackAlertLevel;
153
+ }
154
+ /** A block emitted by the Slack converter. */
155
+ type SlackBlock = SlackHeaderBlock | SlackSectionBlock | SlackContextBlock | SlackImageBlock | SlackDividerBlock | SlackActionsBlock | SlackInputBlock | SlackCardBlock | SlackCarouselBlock | SlackDataTableBlock | SlackMarkdownBlock | SlackAlertBlock;
156
+ /** Options that select the target Slack surface. */
157
+ interface ToSlackBlocksOptions {
158
+ readonly surface?: "message" | "modal";
159
+ }
160
+ /** A non-fatal downgrade reported during Slack conversion. */
161
+ interface SlackConversionWarning {
162
+ readonly code: "clamped" | "dropped" | "fallback";
163
+ readonly component: string;
164
+ readonly detail: string;
165
+ }
166
+ /** The blocks and non-fatal warnings produced by Slack conversion. */
167
+ interface SlackBlocksResult {
168
+ readonly blocks: SlackBlock[];
169
+ readonly warnings: SlackConversionWarning[];
170
+ }
171
+ /** The IR nodes and non-fatal warnings produced by converting Slack Block Kit JSON back into generative UI. */
172
+ interface FromSlackBlocksResult {
173
+ readonly nodes: UIElement[];
174
+ readonly warnings: SlackConversionWarning[];
175
+ }
176
+ //#endregion
177
+ export { FromSlackBlocksResult, SlackActionElement, SlackActionsBlock, SlackAlertBlock, SlackAlertLevel, SlackBlock, SlackBlocksResult, SlackButtonElement, SlackCardBlock, SlackCarouselBlock, SlackCheckboxesElement, SlackContextBlock, SlackConversionWarning, SlackDataTableBlock, SlackDataTableCell, SlackDataTableRawNumberCell, SlackDataTableRawTextCell, SlackDatePickerElement, SlackDividerBlock, SlackHeaderBlock, SlackImageBlock, SlackInputBlock, SlackMarkdownBlock, SlackMrkdwnText, SlackOption, SlackPlainText, SlackPlainTextInputElement, SlackRadioButtonsElement, SlackSectionBlock, SlackStaticSelectElement, SlackTextObject, ToSlackBlocksOptions };
178
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +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"}
File without changes
@@ -0,0 +1,5 @@
1
+ import { decodeBlockAction } from "./slack/decodeBlockAction.js";
2
+ import { FromSlackBlocksResult, SlackActionElement, SlackActionsBlock, SlackAlertBlock, SlackAlertLevel, SlackBlock, SlackBlocksResult, SlackButtonElement, SlackCardBlock, SlackCarouselBlock, SlackCheckboxesElement, SlackContextBlock, SlackConversionWarning, SlackDataTableBlock, SlackDataTableCell, SlackDataTableRawNumberCell, SlackDataTableRawTextCell, SlackDatePickerElement, SlackDividerBlock, SlackHeaderBlock, SlackImageBlock, SlackInputBlock, SlackMarkdownBlock, SlackMrkdwnText, SlackOption, SlackPlainText, SlackPlainTextInputElement, SlackRadioButtonsElement, SlackSectionBlock, SlackStaticSelectElement, SlackTextObject, ToSlackBlocksOptions } from "./slack/types.js";
3
+ import { fromSlackBlocks } from "./slack/fromSlackBlocks.js";
4
+ import { toSlackBlocks } from "./slack/toSlackBlocks.js";
5
+ export { type FromSlackBlocksResult, type SlackActionElement, type SlackActionsBlock, type SlackAlertBlock, type SlackAlertLevel, type SlackBlock, type SlackBlocksResult, type SlackButtonElement, type SlackCardBlock, type SlackCarouselBlock, type SlackCheckboxesElement, type SlackContextBlock, type SlackConversionWarning, type SlackDataTableBlock, type SlackDataTableCell, type SlackDataTableRawNumberCell, type SlackDataTableRawTextCell, type SlackDatePickerElement, type SlackDividerBlock, type SlackHeaderBlock, type SlackImageBlock, type SlackInputBlock, type SlackMarkdownBlock, type SlackMrkdwnText, type SlackOption, type SlackPlainText, type SlackPlainTextInputElement, type SlackRadioButtonsElement, type SlackSectionBlock, type SlackStaticSelectElement, type SlackTextObject, type ToSlackBlocksOptions, decodeBlockAction, fromSlackBlocks, toSlackBlocks };
package/dist/slack.js ADDED
@@ -0,0 +1,4 @@
1
+ import { decodeBlockAction } from "./slack/decodeBlockAction.js";
2
+ import { fromSlackBlocks } from "./slack/fromSlackBlocks.js";
3
+ import { toSlackBlocks } from "./slack/toSlackBlocks.js";
4
+ export { decodeBlockAction, fromSlackBlocks, toSlackBlocks };
@@ -0,0 +1,20 @@
1
+ import { ClampReason } from "./constants.js";
2
+ //#region src/teams/boundSpec.d.ts
3
+ /**
4
+ * Produces a bounded plain copy of a raw generative-ui spec before it
5
+ * reaches `normalizeSpec`, whose own traversal of the root array or any
6
+ * `children` array walks the full reported length of a hostile proxied
7
+ * array before any per-field cap downstream ever applies. Every array
8
+ * (root, or `children` at any depth) is capped to {@link CHILDREN_CAP}
9
+ * entries via `Array.prototype.slice`, which bounds even a proxy with a
10
+ * fabricated `length`; recursion itself is capped at
11
+ * {@link MAX_TRAVERSAL_DEPTH}. `onClamp` fires once per level that was
12
+ * truncated, receiving the reason for that truncation: `"children"`,
13
+ * `"depth"`, `"budget"`, or `"cycle"`. The walk also spends a total budget of
14
+ * {@link NODE_BUDGET} nodes, so shared references cannot multiply work
15
+ * exponentially, and a node that is its own ancestor is cut to `null`.
16
+ */
17
+ declare function boundSpec(spec: unknown, onClamp: (reason: ClampReason) => void): unknown;
18
+ //#endregion
19
+ export { boundSpec };
20
+ //# sourceMappingURL=boundSpec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundSpec.d.ts","names":[],"sources":["../../src/teams/boundSpec.ts"],"mappings":";;;;;;;;;;;;;;;;iBA0FgB,UACd,eACA,UAAU,QAAQ"}
@@ -0,0 +1,67 @@
1
+ import { NODE_BUDGET } from "./constants.js";
2
+ //#region src/teams/boundSpec.ts
3
+ function boundNode(value, depth, onClamp, state, ancestors) {
4
+ if (state.remaining <= 0) {
5
+ if (!state.exhausted) {
6
+ state.exhausted = true;
7
+ onClamp("budget");
8
+ }
9
+ return null;
10
+ }
11
+ state.remaining -= 1;
12
+ if (depth > 64) {
13
+ onClamp("depth");
14
+ return null;
15
+ }
16
+ if (Array.isArray(value)) {
17
+ if (ancestors.has(value)) {
18
+ onClamp("cycle");
19
+ return null;
20
+ }
21
+ ancestors.add(value);
22
+ const bounded = Array.prototype.slice.call(value, 0, 200);
23
+ if (value.length > 200) onClamp("children");
24
+ const result = bounded.map((item) => boundNode(item, depth + 1, onClamp, state, ancestors));
25
+ ancestors.delete(value);
26
+ return result;
27
+ }
28
+ if (value !== null && typeof value === "object" && "children" in value) {
29
+ if (ancestors.has(value)) {
30
+ onClamp("cycle");
31
+ return null;
32
+ }
33
+ ancestors.add(value);
34
+ const record = value;
35
+ const result = {
36
+ ...record,
37
+ children: boundNode(record["children"], depth + 1, onClamp, state, ancestors)
38
+ };
39
+ ancestors.delete(value);
40
+ return result;
41
+ }
42
+ return value;
43
+ }
44
+ /**
45
+ * Produces a bounded plain copy of a raw generative-ui spec before it
46
+ * reaches `normalizeSpec`, whose own traversal of the root array or any
47
+ * `children` array walks the full reported length of a hostile proxied
48
+ * array before any per-field cap downstream ever applies. Every array
49
+ * (root, or `children` at any depth) is capped to {@link CHILDREN_CAP}
50
+ * entries via `Array.prototype.slice`, which bounds even a proxy with a
51
+ * fabricated `length`; recursion itself is capped at
52
+ * {@link MAX_TRAVERSAL_DEPTH}. `onClamp` fires once per level that was
53
+ * truncated, receiving the reason for that truncation: `"children"`,
54
+ * `"depth"`, `"budget"`, or `"cycle"`. The walk also spends a total budget of
55
+ * {@link NODE_BUDGET} nodes, so shared references cannot multiply work
56
+ * exponentially, and a node that is its own ancestor is cut to `null`.
57
+ */
58
+ function boundSpec(spec, onClamp) {
59
+ return boundNode(spec, 0, onClamp, {
60
+ remaining: NODE_BUDGET,
61
+ exhausted: false
62
+ }, /* @__PURE__ */ new WeakSet());
63
+ }
64
+ //#endregion
65
+ export { boundSpec };
66
+
67
+ //# sourceMappingURL=boundSpec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundSpec.js","names":[],"sources":["../../src/teams/boundSpec.ts"],"sourcesContent":["import {\n CHILDREN_CAP,\n MAX_TRAVERSAL_DEPTH,\n NODE_BUDGET,\n type ClampReason,\n} from \"./constants\";\n\ninterface BoundState {\n remaining: number;\n exhausted: boolean;\n}\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 */\nexport function 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"],"mappings":";;AAYA,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,SAAgB,UACd,MACA,SACS;CACT,OAAO,UACL,MACA,GACA,SACA;EAAE,WAAW;EAAa,WAAW;CAAM,mBAC3C,IAAI,QAAQ,CACd;AACF"}
@@ -0,0 +1,57 @@
1
+ import { TeamsAdaptiveCard, TeamsCardAttachment, TeamsCardElement, TeamsSubmitAction, TeamsSubmitData } from "./types.js";
2
+ //#region src/teams/constants.d.ts
3
+ /** The Adaptive Card schema version emitted by {@link toAdaptiveCard}: the Teams desktop/web cap (mobile clients cap at 1.2). */
4
+ declare const AC_VERSION = "1.5";
5
+ /** The `$schema` URL stamped on every emitted card. */
6
+ declare const ADAPTIVE_CARD_SCHEMA = "http://adaptivecards.io/schemas/adaptive-card.json";
7
+ /** The `contentType` stamped on every emitted attachment. */
8
+ declare const ATTACHMENT_CONTENT_TYPE = "application/vnd.microsoft.card.adaptive";
9
+ /** The maximum normalized traversal depth. */
10
+ declare const MAX_TRAVERSAL_DEPTH = 64;
11
+ /**
12
+ * The maximum number of entries kept at any single level (root, or a
13
+ * `children` array at any depth) of the raw spec, before it ever reaches
14
+ * `normalizeSpec`. Bounds a hostile array's reported `length` so the pre-pass
15
+ * can never be made to walk further than this regardless of what the array
16
+ * claims about itself.
17
+ */
18
+ declare const CHILDREN_CAP = 200;
19
+ /**
20
+ * The total number of nodes {@link boundSpec} will visit across one walk,
21
+ * regardless of how many times a shared reference recurs. Bounds the
22
+ * combinatorial work a DAG of shared or self-referential nodes would
23
+ * otherwise force even though each individual array stays within
24
+ * {@link CHILDREN_CAP}.
25
+ */
26
+ declare const NODE_BUDGET = 5000;
27
+ type ClampReason = "children" | "budget" | "cycle" | "depth";
28
+ declare function clampReasonDetail(reason: ClampReason): string;
29
+ /** The maximum number of attachments produced from a root-level Carousel. */
30
+ declare const CAROUSEL_ATTACHMENT_CAP = 10;
31
+ /** The maximum number of primary-mode actions in one ActionSet; extras are set to secondary mode instead of being dropped. */
32
+ declare const PRIMARY_ACTION_CAP = 6;
33
+ /** The maximum number of data rows in a Table. */
34
+ declare const TABLE_ROW_CAP = 100;
35
+ /** The maximum number of columns in a Table. */
36
+ declare const TABLE_COLUMN_CAP = 20;
37
+ /** The maximum number of choices in a Select or RadioGroup. */
38
+ declare const CHOICE_OPTION_CAP = 100;
39
+ /** 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). */
40
+ declare const PAYLOAD_SOFT_CAP = 80000;
41
+ /** Measures the wire size of `value` in UTF-8 bytes rather than UTF-16 code units. */
42
+ declare function utf8ByteLength(value: string): number;
43
+ /** Builds the fixed-shape Adaptive Card envelope around a converted body. */
44
+ declare function buildCard(body: TeamsCardElement[]): TeamsAdaptiveCard;
45
+ /** Wraps a card as a bot-framework message attachment. */
46
+ declare function buildAttachment(card: TeamsAdaptiveCard): TeamsCardAttachment;
47
+ /**
48
+ * Nests an IR action under an `aui` key so `decodeSubmitData` can invert it
49
+ * back out of the merged `activity.value` object a Teams bot receives,
50
+ * alongside whatever other named inputs shared the card.
51
+ */
52
+ declare function buildSubmitData(action: unknown): TeamsSubmitData;
53
+ /** Builds an Action.Submit carrying `action` in the `aui` envelope. */
54
+ declare function buildSubmitAction(title: string, action: unknown, mode?: "secondary"): TeamsSubmitAction;
55
+ //#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 };
57
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,92 @@
1
+ //#region src/teams/constants.ts
2
+ /** The Adaptive Card schema version emitted by {@link toAdaptiveCard}: the Teams desktop/web cap (mobile clients cap at 1.2). */
3
+ const AC_VERSION = "1.5";
4
+ /** The `$schema` URL stamped on every emitted card. */
5
+ const ADAPTIVE_CARD_SCHEMA = "http://adaptivecards.io/schemas/adaptive-card.json";
6
+ /** The `contentType` stamped on every emitted attachment. */
7
+ const ATTACHMENT_CONTENT_TYPE = "application/vnd.microsoft.card.adaptive";
8
+ /** The maximum normalized traversal depth. */
9
+ const MAX_TRAVERSAL_DEPTH = 64;
10
+ /**
11
+ * The maximum number of entries kept at any single level (root, or a
12
+ * `children` array at any depth) of the raw spec, before it ever reaches
13
+ * `normalizeSpec`. Bounds a hostile array's reported `length` so the pre-pass
14
+ * can never be made to walk further than this regardless of what the array
15
+ * claims about itself.
16
+ */
17
+ const CHILDREN_CAP = 200;
18
+ /**
19
+ * The total number of nodes {@link boundSpec} will visit across one walk,
20
+ * regardless of how many times a shared reference recurs. Bounds the
21
+ * combinatorial work a DAG of shared or self-referential nodes would
22
+ * otherwise force even though each individual array stays within
23
+ * {@link CHILDREN_CAP}.
24
+ */
25
+ const NODE_BUDGET = 5e3;
26
+ function clampReasonDetail(reason) {
27
+ if (reason === "budget") return `the tree was truncated after ${NODE_BUDGET} nodes.`;
28
+ if (reason === "cycle") return "a self-referencing node was dropped.";
29
+ if (reason === "depth") return `nodes deeper than 64 levels were dropped.`;
30
+ return `children were clamped to 200 entries.`;
31
+ }
32
+ /** The maximum number of attachments produced from a root-level Carousel. */
33
+ const CAROUSEL_ATTACHMENT_CAP = 10;
34
+ /** The maximum number of primary-mode actions in one ActionSet; extras are set to secondary mode instead of being dropped. */
35
+ const PRIMARY_ACTION_CAP = 6;
36
+ /** The maximum number of data rows in a Table. */
37
+ const TABLE_ROW_CAP = 100;
38
+ /** The maximum number of columns in a Table. */
39
+ const TABLE_COLUMN_CAP = 20;
40
+ /** The maximum number of choices in a Select or RadioGroup. */
41
+ const CHOICE_OPTION_CAP = 100;
42
+ /** 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). */
43
+ const PAYLOAD_SOFT_CAP = 8e4;
44
+ const textEncoder = new TextEncoder();
45
+ /** Measures the wire size of `value` in UTF-8 bytes rather than UTF-16 code units. */
46
+ function utf8ByteLength(value) {
47
+ return textEncoder.encode(value).byteLength;
48
+ }
49
+ /** Builds the fixed-shape Adaptive Card envelope around a converted body. */
50
+ function buildCard(body) {
51
+ return {
52
+ $schema: ADAPTIVE_CARD_SCHEMA,
53
+ type: "AdaptiveCard",
54
+ version: "1.5",
55
+ body,
56
+ actions: []
57
+ };
58
+ }
59
+ /** Wraps a card as a bot-framework message attachment. */
60
+ function buildAttachment(card) {
61
+ return {
62
+ contentType: ATTACHMENT_CONTENT_TYPE,
63
+ content: card
64
+ };
65
+ }
66
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
67
+ /**
68
+ * Nests an IR action under an `aui` key so `decodeSubmitData` can invert it
69
+ * back out of the merged `activity.value` object a Teams bot receives,
70
+ * alongside whatever other named inputs shared the card.
71
+ */
72
+ function buildSubmitData(action) {
73
+ if (!isRecord(action)) return { aui: { type: "action" } };
74
+ const { type: rawType, ...payload } = action;
75
+ return { aui: {
76
+ type: typeof rawType === "string" ? rawType : "action",
77
+ ...Object.keys(payload).length > 0 ? { payload } : {}
78
+ } };
79
+ }
80
+ /** Builds an Action.Submit carrying `action` in the `aui` envelope. */
81
+ function buildSubmitAction(title, action, mode) {
82
+ return {
83
+ type: "Action.Submit",
84
+ title,
85
+ data: buildSubmitData(action),
86
+ ...mode !== void 0 ? { mode } : {}
87
+ };
88
+ }
89
+ //#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 };
91
+
92
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +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"}
@@ -0,0 +1,25 @@
1
+ import { Action } from "../ir.js";
2
+ //#region src/teams/decodeSubmitData.d.ts
3
+ /**
4
+ * Decodes the merged `activity.value` object a Teams bot receives on
5
+ * Action.Submit, inverting the `aui` envelope {@link buildSubmitAction} nests
6
+ * the resume action under. Adaptive Cards fold every other same-card input's
7
+ * value into the same submit object keyed by its `id`, so every top-level key
8
+ * besides `aui` is collected into `$input` (omitted when there are none).
9
+ * `aui` is reserved for the envelope: `toAdaptiveCard` renames any input
10
+ * whose id would collide to an unused id derived from it before encoding, so
11
+ * a same-card input value can never land on this key. `$input` and `type`
12
+ * are reserved on the way out: a `$input` key inside the envelope payload is
13
+ * dropped so the slot only ever reflects same-card input values, and the
14
+ * envelope's own `type` wins over a payload key of the same name. Every
15
+ * collected key (in `payload` or `$input`) is kept as an own data property of
16
+ * the returned object, even a `__proto__` or `constructor` key, since the
17
+ * object is always built by spreading and `Object.fromEntries` rather than by
18
+ * keyed assignment. Envelope keys are read as own properties only, so
19
+ * prototype-inherited `aui`, `type`, or `payload` values never dispatch.
20
+ * Returns `undefined` for a missing or malformed `aui` envelope; never throws.
21
+ */
22
+ declare function decodeSubmitData(value: unknown): Action | undefined;
23
+ //#endregion
24
+ export { decodeSubmitData };
25
+ //# sourceMappingURL=decodeSubmitData.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decodeSubmitData.d.ts","names":[],"sources":["../../src/teams/decodeSubmitData.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;iBAwBgB,iBAAiB,iBAAiB"}