packages feed

slackblocks 3.2.0 → 4.0.0

raw patch · 21 files changed

+300/−39 lines, 21 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Slackblocks.Json: instance Slackblocks.Json.FromSlackJSON a => Slackblocks.Json.FromSlackJSON (GHC.Maybe.Maybe a)
+ Slackblocks.Models: matchTableCellRawNumber :: TableCell -> Maybe RawNumber
+ Slackblocks.Models: rawNumberAsTableCell :: RawNumber -> TableCell
- Slackblocks.Models: TableBlockFields :: [[TableCell]] -> Maybe [ColumnSettings] -> Maybe Text -> Map Text Value -> TableBlockFields
+ Slackblocks.Models: TableBlockFields :: [[TableCell]] -> Maybe [Maybe ColumnSettings] -> Maybe Text -> Map Text Value -> TableBlockFields
- Slackblocks.Models: [tableBlockColumnSettings] :: TableBlockFields -> Maybe [ColumnSettings]
+ Slackblocks.Models: [tableBlockColumnSettings] :: TableBlockFields -> Maybe [Maybe ColumnSettings]

Files

CHANGELOG.md view
@@ -1,5 +1,16 @@ # Changelog +## [4.0.0] — 2026-11-14++- Coordinate the ten-language 4.0.0 release with shared contract 1.6.0. Supported runtime floors are unchanged. See the 4.0.0 migration guide for Go imports, sparse-setting types and stricter validation.++- Require non-empty numeric display labels in both plain and data tables, matching the isolated Slack validation probe.++- Preserve skipped columns in sparse table column settings through native APIs and supported parsers. Null entries count toward the existing 20-column-settings limit.+- Breaking: change `tableBlockColumnSettings` entries to `Maybe ColumnSettings`: wrap existing settings in `Just` and use `Nothing` to skip a column.++- Add numeric plain-table cells and enforce Slack’s documented 10,000-character per-table and message-wide limits (spec 1.6.0). Count displayed cell text as Unicode code points, including composed blocks and attachments; ragged rows and partial column settings remain valid. Previously accepted over-limit tables now fail validation. Supported runtime floors are unchanged.+ ## [3.2.0] — 2026-10-31  - Add native layout blocks, rows and items with typed widths/alignment, closed child and fallback roles, checked nesting and receiving-surface validation (spec 1.5.0).
README.md view
@@ -4,9 +4,9 @@ encoding. Requires GHC 9.6.7 or newer within the tested compiler range.  The native API covers all 89 shared models, closed roles and enumerations in-contract 1.5.0. Haskell joins the coordinated 3.1.0 release on+contract 1.6.0. Haskell joins the coordinated 3.1.0 release on [Hackage](https://hackage.haskell.org/package/slackblocks). Add-`slackblocks >= 3.2 && < 3.3` to your application's Cabal `build-depends`,+`slackblocks >= 4.0 && < 4.1` to your application's Cabal `build-depends`, run `cabal update`, then `cabal build`. The source archive and native HTTP integration are checked on supported compiler endpoints. @@ -73,8 +73,8 @@ them into potentially enormous integers.  Run `cabal test all` from the package directory. Tests use local data and make no-Slack requests. The package version is the coordinated 3.2.0 release;-the independent shared contract version is 1.5.0.+Slack requests. The package version is the coordinated 4.0.0 release;+the independent shared contract version is 1.6.0.  The conformance suite constructs all 120 valid fixtures through native APIs and independently checks their wire objects. It also checks every invalid input at
conformance/public-api.json view
@@ -945,6 +945,7 @@       "Slackblocks.Models.matchRichTextSectionElementRichTextText",       "Slackblocks.Models.matchRichTextSectionElementRichTextUser",       "Slackblocks.Models.matchRichTextSectionElementRichTextUserGroup",+      "Slackblocks.Models.matchTableCellRawNumber",       "Slackblocks.Models.matchTableCellRawText",       "Slackblocks.Models.matchTableCellRichTextBlock",       "Slackblocks.Models.matchTextObjectMarkdownText",@@ -1037,6 +1038,7 @@       "Slackblocks.Models.radioButtonsElementOptions",       "Slackblocks.Models.rawNumber",       "Slackblocks.Models.rawNumberAsDataTableCell",+      "Slackblocks.Models.rawNumberAsTableCell",       "Slackblocks.Models.rawNumberExtensions",       "Slackblocks.Models.rawNumberText",       "Slackblocks.Models.rawNumberValue",@@ -2227,6 +2229,7 @@       "Slackblocks.Models.matchRichTextSectionElementRichTextText",       "Slackblocks.Models.matchRichTextSectionElementRichTextUser",       "Slackblocks.Models.matchRichTextSectionElementRichTextUserGroup",+      "Slackblocks.Models.matchTableCellRawNumber",       "Slackblocks.Models.matchTableCellRawText",       "Slackblocks.Models.matchTableCellRichTextBlock",       "Slackblocks.Models.matchTextObjectMarkdownText",@@ -2319,6 +2322,7 @@       "Slackblocks.Models.radioButtonsElementOptions",       "Slackblocks.Models.rawNumber",       "Slackblocks.Models.rawNumberAsDataTableCell",+      "Slackblocks.Models.rawNumberAsTableCell",       "Slackblocks.Models.rawNumberExtensions",       "Slackblocks.Models.rawNumberText",       "Slackblocks.Models.rawNumberValue",
generator/generate.py view
@@ -39,6 +39,7 @@     if kind in {"number", "double"}: return "Scientific"     if kind in {"object", "text", "enum"}: return name(field["type"])     if kind == "list" and field["type"] == "TaskCardBlock": return "[PlanTask]"+    if kind == "list" and field.get("nullableItems"): return "[Maybe " + name(field["type"]) + "]"     if kind in {"list", "textList"}: return "[" + name(field["type"]) + "]"     if kind == "rows": return "[[" + name(field["type"]) + "]]"     if kind == "stringList": return "[Text]"@@ -191,7 +192,7 @@               'import Data.Aeson (FromJSON(..), ToJSON(..), Value(..), object, (.=))','import Data.Map.Strict (Map)','import qualified Data.Map.Strict as Map',               'import Data.Scientific (Scientific)','import Data.Text (Text)','import Slackblocks.Validation','import Slackblocks.Json (FromSlackJSON(..))','import Slackblocks.Internal.Wire',               'import Slackblocks.Internal.Rules (validateFields, checkIntegerBounds, standaloneTask)','import Slackblocks.Style',-              '-- | Coordinated package version, distinct from the shared contract.', 'packageVersion :: Text', '-- | Shared Block Kit contract version implemented by this package.', 'specVersion :: Text','packageVersion = "3.2.0"', 'specVersion = '+string(json.loads((ROOT/'spec/manifest.json').read_text(encoding="utf-8"))['spec_version']),'']+              '-- | Coordinated package version, distinct from the shared contract.', 'packageVersion :: Text', '-- | Shared Block Kit contract version implemented by this package.', 'specVersion :: Text','packageVersion = "4.0.0"', 'specVersion = '+string(json.loads((ROOT/'spec/manifest.json').read_text(encoding="utf-8"))['spec_version']),'']     return '\n'.join(header+lines)+'\n'  
integrations/transport.cabal view
@@ -14,7 +14,7 @@   ghc-options: -Wall -Wcompat   build-depends:     base >=4.18 && <4.23,-    slackblocks ==3.2.0,+    slackblocks ==4.0.0,     aeson >=2.2 && <2.4,     bytestring >=0.11.5.4 && <0.13,     containers >=0.6.7 && <0.9,
slackblocks.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: slackblocks-version: 3.2.0+version: 4.0.0 synopsis: Typed and validated Slack Block Kit composition description: Immutable Block Kit values, checked construction, and Aeson encoding. homepage: https://github.com/nicklambourne/slackblocks
src/Slackblocks/Internal/Rules.hs view
@@ -100,6 +100,7 @@  rules :: Text -> Value -> Result () rules owner value = case owner of+  "RawNumber" -> check LengthExceeded [Field "text"] "raw_number.text.nonempty" (count (get "text") > 0)   "ButtonElement" -> check MissingRequired [Field "agent_prompt"] "button.agent_prompt_display.requires_agent_prompt" (not (present "agent_prompt_display") || present "agent_prompt")   "SlackIcon" -> check TypeMismatch [Field "name"] "slack_icon_names" (text (get "name") `elem` icons)   "ExternalMultiSelectElement" -> check InvalidUsage [Field "focus_on_load"] "external_multi_select.focus_on_load" (not (present "focus_on_load"))@@ -144,7 +145,9 @@   "NumberInputElement" -> do     mapM_ (\key -> when (present key) (check TypeMismatch [Field key] "number_input.decimal" (isJust (decimalParts (text (get key)))))) ["min_value", "max_value"]     when (present "min_value" && present "max_value") $ check OutOfRange [] "number_input.bounds" (compareDecimal (text (get "min_value")) (text (get "max_value")) /= Just GT)-  "TableBlock" -> indexed "rows" (\row -> check LengthExceeded [] "table.columns.max_items" (fromIntegral (length (array row)) <= limit "table.columns.max_items"))+  "TableBlock" -> do+    indexed "rows" (\row -> check LengthExceeded [] "table.columns.max_items" (fromIntegral (length (array row)) <= limit "table.columns.max_items"))+    check LengthExceeded [Field "rows"] "table.content.max_length" (tableCharacters value <= limit "table.content.max_length")   "DataTableBlock" -> dataTable value   "AxisConfig" -> do     indexed "categories" (\label -> check LengthExceeded [] "data_visualization.category_label.max_length" (count label <= limit "data_visualization.category_label.max_length"))@@ -237,10 +240,12 @@       charts = filter ((== String "data_visualization") . at "type") nested       markdown = sum [count (at "text" b) | b <- nested, at "type" b == String "markdown"]       tables = sum [tableCharacters b | b <- nested, at "type" b == String "data_table"]+      plainTables = sum [tableCharacters b | b <- nested, at "type" b == String "table"]   surface "message" [Field "blocks"] blocks   check LengthExceeded [] "message.data_visualizations.max_items" (fromIntegral (length charts) <= limit "message.data_visualizations.max_items")   check LengthExceeded [] "markdown.total_text.max_length" (markdown <= limit "markdown.total_text.max_length")   check LengthExceeded [] "data_table.total_content.max_length" (tables <= limit "data_table.total_content.max_length")+  check LengthExceeded [] "table.total_content.max_length" (plainTables <= limit "table.total_content.max_length")  tableCharacters :: Value -> Integer tableCharacters value = sum (map cellCharacters (concatMap array (array (at "rows" value))))
src/Slackblocks/Internal/Schema.hs view
@@ -100,7 +100,7 @@   _ -> []  limits :: [(Text, Integer)]-limits = [("text.min_length", 1), ("text.max_length", 3000), ("action_id.max_length", 255), ("block_id.max_length", 255), ("button.text.max_length", 75), ("button.url.max_length", 3000), ("button.value.max_length", 2000), ("button.accessibility_label.max_length", 75), ("button.agent_prompt.max_length", 4000), ("confirmation.title.max_length", 100), ("confirmation.text.max_length", 300), ("confirmation.confirm.max_length", 30), ("confirmation.deny.max_length", 30), ("option.text.max_length", 75), ("option.value.max_length", 150), ("option.description.max_length", 75), ("option.url.max_length", 3000), ("option_group.label.max_length", 75), ("option_group.options.min_items", 1), ("option_group.options.max_items", 100), ("select.placeholder.max_length", 150), ("select.options.min_items", 1), ("select.options.max_items", 100), ("select.option_groups.min_items", 1), ("select.option_groups.max_items", 100), ("select.min_query_length.min", 0), ("overflow.options.min_items", 1), ("overflow.options.max_items", 5), ("checkboxes.options.min_items", 1), ("checkboxes.options.max_items", 10), ("radio_buttons.options.min_items", 1), ("radio_buttons.options.max_items", 10), ("file_input.max_files.min", 1), ("file_input.max_files.max", 10), ("plain_text_input.min_length.min", 0), ("plain_text_input.min_length.max", 3000), ("plain_text_input.max_length.min", 1), ("plain_text_input.max_length.max", 3000), ("plain_text_input.placeholder.max_length", 150), ("email_input.placeholder.max_length", 150), ("url_input.placeholder.max_length", 150), ("number_input.placeholder.max_length", 150), ("date_picker.placeholder.max_length", 150), ("time_picker.placeholder.max_length", 150), ("rich_text_input.placeholder.max_length", 150), ("rich_text_input.min_lines.min", 1), ("rich_text_input.min_lines.max", 100), ("rich_text_input.max_lines.min", 1), ("rich_text_input.max_lines.max", 100), ("multi_select.max_selected_items.min", 1), ("conversation_filter.include.min_items", 1), ("dispatch_action_configuration.trigger_actions_on.min_items", 1), ("dispatch_action_configuration.trigger_actions_on.max_items", 2), ("actions.elements.max_items", 25), ("alert.text.max_length", 200), ("card.title.max_length", 150), ("card.subtitle.max_length", 150), ("card.body.max_length", 200), ("card.actions.max_items", 3), ("card.subtext.max_length", 200), ("carousel.elements.min_items", 1), ("carousel.elements.max_items", 10), ("container.title.max_length", 150), ("container.subtitle.max_length", 150), ("container.child_blocks.min_items", 1), ("container.child_blocks.max_items", 10), ("context.elements.max_items", 10), ("context_actions.elements.max_items", 5), ("feedback_button.text.max_length", 75), ("feedback_button.value.max_length", 2000), ("feedback_button.accessibility_label.max_length", 75), ("icon_button.value.max_length", 2000), ("icon_button.accessibility_label.max_length", 75), ("icon_button.visible_to_user_ids.max_items", 10), ("workflow_button.text.max_length", 75), ("workflow_button.accessibility_label.max_length", 75), ("table.rows.max_items", 100), ("table.columns.max_items", 20), ("table.column_settings.max_items", 20), ("data_table.rows.min_items", 2), ("data_table.rows.max_items", 201), ("data_table.columns.min_items", 1), ("data_table.columns.max_items", 20), ("data_table.page_size.min", 1), ("data_table.page_size.max", 100), ("data_table.cell_text.min_length", 1), ("data_table.content.max_length", 20000), ("data_table.total_content.max_length", 20000), ("data_table.row_header_column_index.min", 0), ("data_visualization.title.max_length", 50), ("data_visualization.segments.min_items", 1), ("data_visualization.segments.max_items", 12), ("data_visualization.segment.label.max_length", 20), ("data_visualization.segment.value.exclusive_min", 0), ("data_visualization.series.min_items", 1), ("data_visualization.series.max_items", 12), ("data_visualization.series_name.max_length", 20), ("data_visualization.data.min_items", 1), ("data_visualization.data.max_items", 20), ("data_visualization.point_label.max_length", 20), ("data_visualization.categories.min_items", 1), ("data_visualization.categories.max_items", 20), ("data_visualization.category_label.max_length", 20), ("data_visualization.axis_label.max_length", 50), ("header.text.max_length", 150), ("image.image_url.max_length", 3000), ("image.alt_text.max_length", 2000), ("image.title.max_length", 2000), ("image_element.image_url.max_length", 3000), ("image_element.alt_text.max_length", 2000), ("input.label.max_length", 2000), ("input.hint.max_length", 2000), ("markdown.text.max_length", 12000), ("markdown.total_text.max_length", 12000), ("plan.tasks.max_items", 50), ("rich_text_list.indent.min", 0), ("rich_text_list.indent.max", 8), ("rich_text_list.offset.min", 0), ("rich_text_list.border.min", 0), ("rich_text_list.border.max", 1), ("rich_text_quote.border.min", 0), ("rich_text_quote.border.max", 1), ("rich_text_preformatted.border.min", 0), ("rich_text_preformatted.border.max", 1), ("section.text.max_length", 3000), ("section.fields.max_items", 10), ("section.fields.item_max_length", 2000), ("video.alt_text.max_length", 2000), ("video.title.max_length", 200), ("video.author_name.max_length", 50), ("video.description.max_length", 200), ("video.provider_name.max_length", 50), ("video.thumbnail_url.max_length", 3000), ("video.video_url.max_length", 3000), ("video.title_url.max_length", 2000), ("video.provider_icon_url.max_length", 2000), ("message.data_visualizations.max_items", 2), ("message.channel.min_length", 1), ("message.blocks.max_items", 50), ("message.attachments.max_items", 100), ("view.blocks.max_items", 100), ("view.private_metadata.max_length", 3000), ("view.callback_id.max_length", 255), ("view.external_id.max_length", 255), ("view.title.max_length", 24), ("view.close.max_length", 24), ("view.submit.max_length", 24), ("layout.label.min_length", 1), ("layout.label.max_length", 150), ("layout.rows.min_items", 1), ("layout.rows.max_items", 5), ("layout.items.min_items", 1), ("layout.items.max_items", 3)]+limits = [("text.min_length", 1), ("text.max_length", 3000), ("action_id.max_length", 255), ("block_id.max_length", 255), ("button.text.max_length", 75), ("button.url.max_length", 3000), ("button.value.max_length", 2000), ("button.accessibility_label.max_length", 75), ("button.agent_prompt.max_length", 4000), ("confirmation.title.max_length", 100), ("confirmation.text.max_length", 300), ("confirmation.confirm.max_length", 30), ("confirmation.deny.max_length", 30), ("option.text.max_length", 75), ("option.value.max_length", 150), ("option.description.max_length", 75), ("option.url.max_length", 3000), ("option_group.label.max_length", 75), ("option_group.options.min_items", 1), ("option_group.options.max_items", 100), ("select.placeholder.max_length", 150), ("select.options.min_items", 1), ("select.options.max_items", 100), ("select.option_groups.min_items", 1), ("select.option_groups.max_items", 100), ("select.min_query_length.min", 0), ("overflow.options.min_items", 1), ("overflow.options.max_items", 5), ("checkboxes.options.min_items", 1), ("checkboxes.options.max_items", 10), ("radio_buttons.options.min_items", 1), ("radio_buttons.options.max_items", 10), ("file_input.max_files.min", 1), ("file_input.max_files.max", 10), ("plain_text_input.min_length.min", 0), ("plain_text_input.min_length.max", 3000), ("plain_text_input.max_length.min", 1), ("plain_text_input.max_length.max", 3000), ("plain_text_input.placeholder.max_length", 150), ("email_input.placeholder.max_length", 150), ("url_input.placeholder.max_length", 150), ("number_input.placeholder.max_length", 150), ("date_picker.placeholder.max_length", 150), ("time_picker.placeholder.max_length", 150), ("rich_text_input.placeholder.max_length", 150), ("rich_text_input.min_lines.min", 1), ("rich_text_input.min_lines.max", 100), ("rich_text_input.max_lines.min", 1), ("rich_text_input.max_lines.max", 100), ("multi_select.max_selected_items.min", 1), ("conversation_filter.include.min_items", 1), ("dispatch_action_configuration.trigger_actions_on.min_items", 1), ("dispatch_action_configuration.trigger_actions_on.max_items", 2), ("actions.elements.max_items", 25), ("alert.text.max_length", 200), ("card.title.max_length", 150), ("card.subtitle.max_length", 150), ("card.body.max_length", 200), ("card.actions.max_items", 3), ("card.subtext.max_length", 200), ("carousel.elements.min_items", 1), ("carousel.elements.max_items", 10), ("container.title.max_length", 150), ("container.subtitle.max_length", 150), ("container.child_blocks.min_items", 1), ("container.child_blocks.max_items", 10), ("context.elements.max_items", 10), ("context_actions.elements.max_items", 5), ("feedback_button.text.max_length", 75), ("feedback_button.value.max_length", 2000), ("feedback_button.accessibility_label.max_length", 75), ("icon_button.value.max_length", 2000), ("icon_button.accessibility_label.max_length", 75), ("icon_button.visible_to_user_ids.max_items", 10), ("workflow_button.text.max_length", 75), ("workflow_button.accessibility_label.max_length", 75), ("table.rows.max_items", 100), ("table.columns.max_items", 20), ("table.column_settings.max_items", 20), ("table.content.max_length", 10000), ("table.total_content.max_length", 10000), ("data_table.rows.min_items", 2), ("data_table.rows.max_items", 201), ("data_table.columns.min_items", 1), ("data_table.columns.max_items", 20), ("data_table.page_size.min", 1), ("data_table.page_size.max", 100), ("data_table.cell_text.min_length", 1), ("data_table.content.max_length", 20000), ("data_table.total_content.max_length", 20000), ("data_table.row_header_column_index.min", 0), ("data_visualization.title.max_length", 50), ("data_visualization.segments.min_items", 1), ("data_visualization.segments.max_items", 12), ("data_visualization.segment.label.max_length", 20), ("data_visualization.segment.value.exclusive_min", 0), ("data_visualization.series.min_items", 1), ("data_visualization.series.max_items", 12), ("data_visualization.series_name.max_length", 20), ("data_visualization.data.min_items", 1), ("data_visualization.data.max_items", 20), ("data_visualization.point_label.max_length", 20), ("data_visualization.categories.min_items", 1), ("data_visualization.categories.max_items", 20), ("data_visualization.category_label.max_length", 20), ("data_visualization.axis_label.max_length", 50), ("header.text.max_length", 150), ("image.image_url.max_length", 3000), ("image.alt_text.max_length", 2000), ("image.title.max_length", 2000), ("image_element.image_url.max_length", 3000), ("image_element.alt_text.max_length", 2000), ("input.label.max_length", 2000), ("input.hint.max_length", 2000), ("markdown.text.max_length", 12000), ("markdown.total_text.max_length", 12000), ("plan.tasks.max_items", 50), ("rich_text_list.indent.min", 0), ("rich_text_list.indent.max", 8), ("rich_text_list.offset.min", 0), ("rich_text_list.border.min", 0), ("rich_text_list.border.max", 1), ("rich_text_quote.border.min", 0), ("rich_text_quote.border.max", 1), ("rich_text_preformatted.border.min", 0), ("rich_text_preformatted.border.max", 1), ("section.text.max_length", 3000), ("section.fields.max_items", 10), ("section.fields.item_max_length", 2000), ("video.alt_text.max_length", 2000), ("video.title.max_length", 200), ("video.author_name.max_length", 50), ("video.description.max_length", 200), ("video.provider_name.max_length", 50), ("video.thumbnail_url.max_length", 3000), ("video.video_url.max_length", 3000), ("video.title_url.max_length", 2000), ("video.provider_icon_url.max_length", 2000), ("message.data_visualizations.max_items", 2), ("message.channel.min_length", 1), ("message.blocks.max_items", 50), ("message.attachments.max_items", 100), ("view.blocks.max_items", 100), ("view.private_metadata.max_length", 3000), ("view.callback_id.max_length", 255), ("view.external_id.max_length", 255), ("view.title.max_length", 24), ("view.close.max_length", 24), ("view.submit.max_length", 24), ("layout.label.min_length", 1), ("layout.label.max_length", 150), ("layout.rows.min_items", 1), ("layout.rows.max_items", 5), ("layout.items.min_items", 1), ("layout.items.max_items", 3)]  icons :: [Text] icons = ["archive", "book", "bookmark", "bot", "bug", "calendar", "call", "caret-left", "caret-right", "check", "clipboard", "code", "comment", "compass", "copy", "cube", "download", "edit", "email", "eye-closed", "eye-open", "file", "flag", "folder", "gear", "globe", "heart", "help", "image", "info", "key", "lightbulb", "link", "map", "mobile", "new-window", "pin", "plus", "refine", "refresh", "rocket", "save", "screen", "share", "sparkle", "star", "star-filled", "tag", "thumbs-down", "thumbs-up", "trash", "upload", "user", "warning"]
src/Slackblocks/Json.hs view
@@ -56,6 +56,10 @@   fromSlackJSON value@(Number number) = checkJSONNumbers value >> Right number   fromSlackJSON _ = failure TypeMismatch [] "type" "expected a number" +instance (FromSlackJSON a) => FromSlackJSON (Maybe a) where+  fromSlackJSON Null = Right Nothing+  fromSlackJSON value = Just <$> fromSlackJSON value+ instance (FromSlackJSON a) => FromSlackJSON [a] where   fromSlackJSON (Array values) = traverse (\(index, value) -> atIndex index (fromSlackJSON value)) (zip [0 ..] (V.toList values))   fromSlackJSON _ = failure TypeMismatch [] "type" "expected an array"
src/Slackblocks/Models.hs view
@@ -538,6 +538,8 @@     TableCell,     richTextBlockAsTableCell,     matchTableCellRichTextBlock,+    rawNumberAsTableCell,+    matchTableCellRawNumber,     rawTextAsTableCell,     matchTableCellRawText,     DataTableCell,@@ -753,9 +755,9 @@ -- | Shared Block Kit contract version implemented by this package. specVersion :: Text -packageVersion = "3.2.0"+packageVersion = "4.0.0" -specVersion = "1.5.0"+specVersion = "1.6.0"  -- | Severity levels supported by alert blocks. data AlertLevel@@ -1137,7 +1139,7 @@     cardBlockSubtitle :: Maybe (TextObject),     -- | Sets the main body text of the card. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @card.body@.     cardBlockBody :: Maybe (TextObject),-    -- | Adds buttons displayed at the bottom of the card, in display order. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @card.actions@.+    -- | Adds up to three buttons in serialization order. Slack controls their visual placement: buttons are right-aligned with primary last; a danger button precedes the other button in a two-button group, or sits at the card's far left with three buttons. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @card.actions@.     cardBlockActions :: Maybe ([ButtonElement]),     -- | Sets a Slack-provided icon displayed beside the title. Cannot be combined with an image icon. Optional; Nothing omits the field. Factory default: @Nothing@.     cardBlockSlackIcon :: Maybe (SlackIcon),@@ -1571,7 +1573,7 @@  instance FromJSON DividerBlock where parseJSON = checkedParser --- | A remote file previously added with the files.remote API.+-- | A retrieved remote-file representation. Register and share files through the files.remote API; Slack does not accept directly posting file blocks. -- -- <https://docs.slack.dev/reference/block-kit/blocks/file-block> newtype FileBlock = CheckedFileBlock FileBlockFields deriving (Eq, Show)@@ -1999,7 +2001,7 @@  instance FromJSON SectionBlock where parseJSON = checkedParser --- | A simple table of raw text and rich text cells.+-- | A simple table of raw text, numeric and rich text cells. Cell text is limited to 10,000 Unicode code points per table and across tables in a message. -- -- <https://docs.slack.dev/reference/block-kit/blocks/table-block> newtype TableBlock = CheckedTableBlock TableBlockFields deriving (Eq, Show)@@ -2008,8 +2010,8 @@ data TableBlockFields = TableBlockFields   { -- | Adds complete table rows in display order. Required. Shared limit: @table.rows@.     tableBlockRows :: [[TableCell]],-    -- | Adds per-column settings in column order. Provide one entry for every column. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @table.column_settings@.-    tableBlockColumnSettings :: Maybe ([ColumnSettings]),+    -- | Optional settings for leading columns, up to 20 entries. Use null to retain a column’s default settings without shifting later entries. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @table.column_settings@.+    tableBlockColumnSettings :: Maybe ([Maybe ColumnSettings]),     -- | Sets a unique identifier for this block. Slack returns it in interaction payloads, so use a stable value when you need to find the block again. Optional; Nothing omits the field. Factory default: @Nothing@. Shared limit: @block_id@.     tableBlockBlockId :: Maybe (Text),     -- | Unmodeled extension fields. Reserved and modeled keys are rejected.@@ -4753,7 +4755,7 @@  instance FromJSON PlainText where parseJSON = checkedParser --- | A numeric data table cell with display text.+-- | A numeric table or data table cell with display text. -- -- <https://docs.slack.dev/reference/block-kit/blocks/data-table-block> newtype RawNumber = CheckedRawNumber RawNumberFields deriving (Eq, Show)@@ -6508,10 +6510,11 @@ matchContextActionsElementIconButtonElement _ = Nothing  -- | Closed role: only checked members may enter this position.-data TableCell = TableCellRichTextBlock RichTextBlock | TableCellRawText RawText deriving (Eq, Show)+data TableCell = TableCellRichTextBlock RichTextBlock | TableCellRawNumber RawNumber | TableCellRawText RawText deriving (Eq, Show)  instance ToJSON TableCell where   toJSON (TableCellRichTextBlock value) = toJSON value+  toJSON (TableCellRawNumber value) = toJSON value   toJSON (TableCellRawText value) = toJSON value  instance FromSlackJSON TableCell where@@ -6519,6 +6522,7 @@     tag <- wireTag value     case tag of       "rich_text" -> TableCellRichTextBlock <$> fromSlackJSON value+      "raw_number" -> TableCellRawNumber <$> fromSlackJSON value       "raw_text" -> TableCellRawText <$> fromSlackJSON value       _ -> failure TypeMismatch [Field "type"] "role" "unsupported child type" @@ -6532,6 +6536,15 @@ matchTableCellRichTextBlock :: TableCell -> Maybe RichTextBlock matchTableCellRichTextBlock (TableCellRichTextBlock value) = Just value matchTableCellRichTextBlock _ = Nothing++-- | Place a checked RawNumber in the TableCell role. The receiving parent retains its contextual validation.+rawNumberAsTableCell :: RawNumber -> TableCell+rawNumberAsTableCell = TableCellRawNumber++-- | Return the RawNumber member when present, otherwise Nothing; no unchecked cast.+matchTableCellRawNumber :: TableCell -> Maybe RawNumber+matchTableCellRawNumber (TableCellRawNumber value) = Just value+matchTableCellRawNumber _ = Nothing  -- | Place a checked RawText in the TableCell role. The receiving parent retains its contextual validation. rawTextAsTableCell :: RawText -> TableCell
test/Constructions.hs view
@@ -7,6 +7,50 @@ import Data.Text qualified as T import Slackblocks +tableWire :: Int -> Bool -> Value+tableWire n numeric =+  object+    [ "type" .= String "table",+      "rows" .= [[object (["type" .= String (if numeric then "raw_number" else "raw_text"), "text" .= T.replicate n "😀"] <> ["value" .= (1 :: Int) | numeric])]]+    ]++nestedTableMessage :: Result MessagePayload+nestedTableMessage = do+  left <- plainTextTable 5000+  right <- plainTextTable 5001+  item <- buildLayoutItem (layoutItem (tableBlockAsLayoutChild left))+  row <- buildLayoutRow (layoutRow [item])+  layout <- buildLayoutBlock ((layoutBlock "Table" [row]) {layoutBlockFallback = Just [tableBlockAsLayoutFallback right]})+  buildMessagePayload ((messagePayload "C123") {messagePayloadBlocks = Just [layoutBlockAsBlock layout]})++nestedTableWire :: Value+nestedTableWire = object ["channel" .= String "C123", "blocks" .= [object ["type" .= String "layout", "label" .= String "Table", "rows" .= [object ["items" .= [object ["block" .= tableWire 5000 False]]]], "fallback" .= [tableWire 5001 False]]]]++numericTable :: Result TableBlock+numericTable = do+  label <- buildRawText (rawText "Count")+  count <- buildRawNumber (rawNumber 42 "42 items")+  three <- buildRawNumber (rawNumber 3 "Three")+  setting <- buildColumnSettings (columnSettings {columnSettingsAlign = Just ColumnAlignRight})+  zero <- buildRawNumber (rawNumber 0 "0")+  buildTableBlock ((tableBlock [[rawTextAsTableCell label, rawNumberAsTableCell count, rawNumberAsTableCell zero], [rawNumberAsTableCell three]]) {tableBlockColumnSettings = Just [Nothing, Just setting, Nothing], tableBlockBlockId = Just "fake_block_id"})++numericTextTable :: Int -> Result TableBlock+numericTextTable n = do+  cell <- buildRawNumber (rawNumber 1 (T.replicate n "😀"))+  buildTableBlock ((tableBlock [[rawNumberAsTableCell cell]]) {tableBlockBlockId = Just "fake_block_id"})++plainTextTable :: Int -> Result TableBlock+plainTextTable n = do+  cell <- buildRawText (rawText (T.replicate n "😀"))+  buildTableBlock ((tableBlock [[rawTextAsTableCell cell]]) {tableBlockBlockId = Just "fake_block_id"})++tableMessage :: Int -> Result MessagePayload+tableMessage n = do+  left <- plainTextTable 5000+  right <- numericTextTable n+  buildMessagePayload ((messagePayload "C123") {messagePayloadBlocks = Just [tableBlockAsBlock left, tableBlockAsBlock right]})+ valid0 :: Result Value valid0 = do   v0 <- buildMarkdownText ((markdownText "I like pretty colours") {markdownTextVerbatim = Nothing})@@ -276,7 +320,7 @@   v9 <- buildRichTextBlock ((richTextBlock [richTextSectionAsRichTextBlockElement v8]) {richTextBlockBlockId = Nothing})   v10 <- buildColumnSettings (columnSettings {columnSettingsAlign = Nothing, columnSettingsIsWrapped = Just True})   v11 <- buildColumnSettings (columnSettings {columnSettingsAlign = Just ColumnAlignRight, columnSettingsIsWrapped = Nothing})-  v12 <- buildTableBlock ((tableBlock [[rawTextAsTableCell v0, rawTextAsTableCell v1], [rawTextAsTableCell v2, richTextBlockAsTableCell v5], [rawTextAsTableCell v6, richTextBlockAsTableCell v9]]) {tableBlockColumnSettings = Just [v10, v11], tableBlockBlockId = Just "fake_block_id"})+  v12 <- buildTableBlock ((tableBlock [[rawTextAsTableCell v0, rawTextAsTableCell v1], [rawTextAsTableCell v2, richTextBlockAsTableCell v5], [rawTextAsTableCell v6, richTextBlockAsTableCell v9]]) {tableBlockColumnSettings = Just [Just v10, Just v11], tableBlockBlockId = Just "fake_block_id"})   pure (toJSON v12)  valid33 :: Result Value@@ -870,6 +914,10 @@     ("blocks/section_block_single_field_value_coercion", valid30, \value -> toJSON <$> (fromValue value :: Result SectionBlock)),     ("blocks/section_block_text_only", valid31, \value -> toJSON <$> (fromValue value :: Result SectionBlock)),     ("blocks/table_block", valid32, \value -> toJSON <$> (fromValue value :: Result TableBlock)),+    ("blocks/table_sparse_defaults", toJSON <$> sparseDefaultsTable, \value -> toJSON <$> (fromValue value :: Result TableBlock)),+    ("blocks/table_numeric", toJSON <$> numericTable, \value -> toJSON <$> (fromValue value :: Result TableBlock)),+    ("blocks/table_text_at_limit", toJSON <$> numericTextTable 10000, \value -> toJSON <$> (fromValue value :: Result TableBlock)),+    ("messages/message_table_total_at_limit", toJSON <$> tableMessage 5000, \value -> toJSON <$> (fromValue value :: Result MessagePayload)),     ("blocks/table_ragged_rows", valid33, \value -> toJSON <$> (fromValue value :: Result TableBlock)),     ("blocks/task_card_block", valid34, \value -> toJSON <$> (fromValue value :: Result TaskCardBlock)),     ("blocks/video_block_basic", valid35, \value -> toJSON <$> (fromValue value :: Result VideoBlock)),@@ -1966,8 +2014,7 @@ invalid166 :: Result () invalid166 = do   v0 <- buildRawText (rawText "A")-  v1 <- buildColumnSettings (columnSettings {columnSettingsAlign = Nothing, columnSettingsIsWrapped = Just True})-  v2 <- buildTableBlock ((tableBlock [[rawTextAsTableCell v0]]) {tableBlockColumnSettings = Just (replicate 21 v1), tableBlockBlockId = Nothing})+  v2 <- buildTableBlock ((tableBlock [[rawTextAsTableCell v0]]) {tableBlockColumnSettings = Just (replicate 21 Nothing), tableBlockBlockId = Nothing})   v2 `seq` pure ()  invalid167 :: Result ()@@ -2151,7 +2198,8 @@  invalidCases :: [(String, Maybe (Result ()), Result ())] invalidCases =-  [ ("layout-home-alert", Just invalidLayoutAudit0, void (fromValue (object ["type" .= String "home", "blocks" .= toJSON ([object ["type" .= String "layout", "label" .= String "Layout", "rows" .= toJSON ([object ["items" .= toJSON ([object ["block" .= object ["type" .= String "alert", "text" .= object ["type" .= String "plain_text", "text" .= String "Modal only"]]]] :: [Value])]] :: [Value])]] :: [Value])]) :: Result HomeTabView)),+  [ ("raw-number-empty-text", Just (void (buildRawNumber (rawNumber 0 ""))), void (fromValue (object ["type" .= String "raw_number", "value" .= (0 :: Int), "text" .= String ""]) :: Result RawNumber)),+    ("layout-home-alert", Just invalidLayoutAudit0, void (fromValue (object ["type" .= String "home", "blocks" .= toJSON ([object ["type" .= String "layout", "label" .= String "Layout", "rows" .= toJSON ([object ["items" .= toJSON ([object ["block" .= object ["type" .= String "alert", "text" .= object ["type" .= String "plain_text", "text" .= String "Modal only"]]]] :: [Value])]] :: [Value])]] :: [Value])]) :: Result HomeTabView)),     ("layout-modal-fallback-table", Just invalidLayoutAudit1, void (fromValue (object ["type" .= String "modal", "title" .= object ["type" .= String "plain_text", "text" .= String "Grid"], "blocks" .= toJSON ([object ["type" .= String "layout", "label" .= String "Layout", "rows" .= toJSON ([object ["items" .= toJSON ([object ["block" .= object ["type" .= String "divider"]]] :: [Value])]] :: [Value]), "fallback" .= toJSON ([object ["type" .= String "table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "Cell"]] :: [Value])] :: [Value])]] :: [Value])]] :: [Value])]) :: Result ModalView)),     ("layout-chart-three-items", Just invalidLayoutAudit2, void (fromValue (object ["items" .= toJSON ([object ["block" .= object ["type" .= String "divider"]], object ["block" .= object ["type" .= String "data_visualization", "title" .= String "Chart", "chart" .= object ["type" .= String "pie", "segments" .= toJSON ([object ["label" .= String "One", "value" .= Number 1]] :: [Value])]]], object ["block" .= object ["type" .= String "divider"]]] :: [Value])]) :: Result LayoutRow)),     ("layout-nested-table-total", Just invalidLayoutAudit3, void (fromValue (object ["channel" .= String "C123", "blocks" .= toJSON ([object ["type" .= String "container", "title" .= object ["type" .= String "plain_text", "text" .= String "Group"], "child_blocks" .= toJSON ([object ["type" .= String "layout", "label" .= String "Layout", "rows" .= toJSON ([object ["items" .= toJSON ([object ["block" .= object ["type" .= String "data_table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "H"]] :: [Value]), toJSON ([object ["type" .= String "raw_text", "text" .= String (T.replicate 10000 "x")]] :: [Value])] :: [Value]), "caption" .= String "Data"]]] :: [Value])]] :: [Value])], object ["type" .= String "layout", "label" .= String "Layout", "rows" .= toJSON ([object ["items" .= toJSON ([object ["block" .= object ["type" .= String "data_table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "H"]] :: [Value]), toJSON ([object ["type" .= String "raw_text", "text" .= String (T.replicate 10000 "x")]] :: [Value])] :: [Value]), "caption" .= String "Data"]]] :: [Value])]] :: [Value])]] :: [Value])]] :: [Value])]) :: Result MessagePayload)),@@ -2300,6 +2348,9 @@     ("data-table-page-size-too-small", Just invalid125, void (fromValue (object ["type" .= String "data_table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "Name"]] :: [Value]), toJSON ([object ["type" .= String "raw_text", "text" .= String "Alice"]] :: [Value])] :: [Value]), "caption" .= String "Names", "page_size" .= Number 0]) :: Result DataTableBlock)),     ("data-table-page-size-too-large", Just invalid126, void (fromValue (object ["type" .= String "data_table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "Name"]] :: [Value]), toJSON ([object ["type" .= String "raw_text", "text" .= String "Alice"]] :: [Value])] :: [Value]), "caption" .= String "Names", "page_size" .= Number 101]) :: Result DataTableBlock)),     ("data-table-cell-text-empty", Just invalid127, void (fromValue (object ["type" .= String "data_table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "Name"]] :: [Value]), toJSON ([object ["type" .= String "raw_text", "text" .= String ""]] :: [Value])] :: [Value]), "caption" .= String "Names"]) :: Result DataTableBlock)),+    ("table-layout-fallback-total-too-long", Just (void nestedTableMessage), void (fromValue nestedTableWire :: Result MessagePayload)),+    ("table-content-too-long", Just (void (numericTextTable 10001)), void (fromValue (tableWire 10001 True) :: Result TableBlock)),+    ("table-total-content-too-long", Just (void (tableMessage 5001)), void (fromValue (object ["channel" .= String "C123", "blocks" .= [tableWire 5000 False, tableWire 5001 True]]) :: Result MessagePayload)),     ("data-table-content-too-long", Just invalid128, void (fromValue (object ["type" .= String "data_table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "Name"]] :: [Value]), toJSON ([object ["type" .= String "raw_text", "text" .= String (T.replicate 20000 "x")]] :: [Value])] :: [Value]), "caption" .= String "Names"]) :: Result DataTableBlock)),     ("message-too-many-charts", Just invalid129, void (fromValue (object ["channel" .= String "C123", "blocks" .= toJSON (replicate 3 (object ["type" .= String "data_visualization", "title" .= String "Chart", "chart" .= object ["type" .= String "pie", "segments" .= toJSON ([object ["label" .= String "A", "value" .= Number 1]] :: [Value])]]))]) :: Result MessagePayload)),     ("data-visualization-title-too-long", Just invalid130, void (fromValue (object ["type" .= String "data_visualization", "title" .= String (T.replicate 51 "x"), "chart" .= object ["type" .= String "pie", "segments" .= toJSON ([object ["label" .= String "A", "value" .= Number 1]] :: [Value])]]) :: Result DataVisualizationBlock)),@@ -2338,7 +2389,7 @@     ("image-block-url-and-slack-file", Just invalid163, void (fromValue (object ["type" .= String "image", "image_url" .= String "https://example.com/image.png", "slack_file" .= object ["id" .= String "F0123ABC456"], "alt_text" .= String "Alt"]) :: Result ImageBlock)),     ("image-block-missing-source", Just invalid164, void (fromValue (object ["type" .= String "image", "alt_text" .= String "Alt"]) :: Result ImageBlock)),     ("container-no-child-blocks", Just invalid165, void (fromValue (object ["type" .= String "container", "title" .= object ["type" .= String "plain_text", "text" .= String "Container"], "child_blocks" .= toJSON ([] :: [Value])]) :: Result ContainerBlock)),-    ("table-too-many-column-settings", Just invalid166, void (fromValue (object ["type" .= String "table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "A"]] :: [Value])] :: [Value]), "column_settings" .= toJSON (replicate 21 (object ["is_wrapped" .= Bool True]))]) :: Result TableBlock)),+    ("table-too-many-column-settings", Just invalid166, void (fromValue (object ["type" .= String "table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "A"]] :: [Value])] :: [Value]), "column_settings" .= toJSON (replicate 21 Null)]) :: Result TableBlock)),     ("data-table-row-header-index-negative", Just invalid167, void (fromValue (object ["type" .= String "data_table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "Name"]] :: [Value]), toJSON ([object ["type" .= String "raw_text", "text" .= String "Alice"]] :: [Value])] :: [Value]), "caption" .= String "Names", "row_header_column_index" .= Number (-1)]) :: Result DataTableBlock)),     ("data-table-total-content-too-long", Just invalid168, void (fromValue (object ["channel" .= String "C123", "blocks" .= toJSON ([object ["type" .= String "data_table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "Name"]] :: [Value]), toJSON ([object ["type" .= String "raw_text", "text" .= String (T.replicate 9997 "x")]] :: [Value])] :: [Value]), "caption" .= String "Names"], object ["type" .= String "data_table", "rows" .= toJSON ([toJSON ([object ["type" .= String "raw_text", "text" .= String "Name"]] :: [Value]), toJSON ([object ["type" .= String "raw_text", "text" .= String (T.replicate 9997 "x")]] :: [Value])] :: [Value]), "caption" .= String "Names"]] :: [Value])]) :: Result MessagePayload)),     ("markdown-total-too-long", Just invalid169, void (fromValue (object ["channel" .= String "C123", "blocks" .= toJSON ([object ["type" .= String "markdown", "text" .= String (T.replicate 6001 "x")], object ["type" .= String "markdown", "text" .= String (T.replicate 6001 "x")]] :: [Value])]) :: Result MessagePayload)),@@ -2680,3 +2731,8 @@   v13 <- buildContainerBlock ((containerBlock [layoutBlockAsBlock v6, layoutBlockAsBlock v12]) {containerBlockTitle = Just v0})   v14 <- buildMessagePayload ((messagePayload "C123") {messagePayloadBlocks = Just [containerBlockAsBlock v13]})   v14 `seq` pure ()++sparseDefaultsTable :: Result TableBlock+sparseDefaultsTable = do+  cell <- buildRawText (rawText "Defaults")+  buildTableBlock ((tableBlock [[rawTextAsTableCell cell]]) {tableBlockColumnSettings = Just (replicate 20 Nothing), tableBlockBlockId = Just "fake_block_id"})
test/Foundation.hs view
@@ -62,5 +62,5 @@   assert "layout false preserved" $ layoutBlockIsLabelVisible (inspectLayoutBlock changed) == Just False   assert "layout typed width" $ layoutItemWidth (inspectLayoutItem item) == Just LayoutWidthWide   assert "layout native decoding" $ (fromValue (toJSON changed) :: Result LayoutBlock) == Right changed-  assert "spec identity" $ specVersion == "1.5.0"+  assert "spec identity" $ specVersion == "1.6.0"   putStrLn "Foundation construction, wire, Unicode, editing and prompt controls passed."
test/spec/coverage.json view
@@ -1,5 +1,5 @@ {-  "spec_version": "1.5.0",+  "spec_version": "1.6.0",   "capabilities": {     "blocks.actions": ["blocks/actions_block_checkboxes"],     "blocks.alert": ["blocks/alert_block"],@@ -31,7 +31,7 @@     "blocks.plan": ["blocks/plan_block"],     "blocks.rich_text": ["blocks/rich_text_block_basic"],     "blocks.section": ["blocks/section_block_text_only"],-    "blocks.table": ["blocks/table_block", "blocks/table_ragged_rows"],+    "blocks.table": ["blocks/table_block", "blocks/table_ragged_rows", "blocks/table_numeric", "blocks/table_sparse_defaults", "blocks/table_text_at_limit", "messages/message_table_total_at_limit"],     "blocks.task_card": ["blocks/task_card_block"],     "blocks.video": ["blocks/video_block_basic"],     "elements.button": ["elements/button_basic", "elements/button_text_at_limit", "elements/button_without_action_id", "elements/button_agent_prompt", "elements/button_agent_prompt_display", "elements/button_agent_prompt_at_limit", "elements/button_agent_prompt_empty"],@@ -70,7 +70,7 @@     "messages.message": ["messages/message_basic", "messages/message_two_charts"],     "messages.message_response": ["messages/message_response"],     "messages.webhook_message": ["messages/webhook_message_basic"],-    "objects.column_settings": ["blocks/table_block"],+    "objects.column_settings": ["blocks/table_block", "blocks/table_numeric", "blocks/table_sparse_defaults"],     "objects.axis_config": ["blocks/data_visualization_line"],     "objects.chart_segment": ["blocks/data_visualization_pie"],     "objects.confirmation": ["objects/confirmation_dialogue_basic"],@@ -87,7 +87,7 @@     "objects.option_group": ["objects/option_group_basic"],     "objects.plain_text": ["objects/text_plaintext_basic"],     "objects.raw_text": ["blocks/table_block"],-    "objects.raw_number": ["blocks/data_table_block"],+    "objects.raw_number": ["blocks/data_table_block", "blocks/table_numeric", "blocks/table_text_at_limit"],     "objects.slack_icon": ["blocks/card_block"],     "objects.slack_file": [       "elements/image_slack_file_id",
test/spec/fixtures/invalid/manifest.json view
@@ -1,5 +1,5 @@ {-  "spec_version": "1.5.0",+  "spec_version": "1.6.0",   "cases": [     {       "id": "text-empty",@@ -863,7 +863,7 @@       "id": "table-too-many-column-settings",       "category": "length-exceeded",       "constraint": "table.column_settings.max_items",-      "description": "A table with more than 20 column settings must be rejected"+      "description": "A table with more than 20 column settings, including skipped null entries, must be rejected"     },     {       "id": "data-table-row-header-index-negative",@@ -1266,6 +1266,30 @@       "category": "length-exceeded",       "constraint": "layout.message.nested_table_total",       "description": "Data tables inside layouts inside a container still count toward the existing message aggregate text limit."+    },+    {+      "id": "table-content-too-long",+      "category": "length-exceeded",+      "constraint": "table.content.max_length",+      "description": "A plain table with 10001 Unicode code points of cell display text must be rejected"+    },+    {+      "id": "table-total-content-too-long",+      "category": "length-exceeded",+      "constraint": "table.total_content.max_length",+      "description": "Two individually valid plain tables totaling 10001 Unicode code points in a message must be rejected"+    },+    {+      "id": "table-layout-fallback-total-too-long",+      "category": "length-exceeded",+      "constraint": "table.total_content.layout_fallback",+      "description": "A table in a layout child and another in its fallback totaling 10001 code points must be rejected at message construction"+    },+    {+      "id": "raw-number-empty-text",+      "category": "length-exceeded",+      "constraint": "raw_number.text.nonempty",+      "description": "Numeric display text must be non-empty in both plain and data tables"     }   ] }
+ test/spec/fixtures/valid/blocks/table_numeric.json view
@@ -0,0 +1,36 @@+{+  "type": "table",+  "rows": [+    [+      {+        "type": "raw_text",+        "text": "Count"+      },+      {+        "type": "raw_number",+        "value": 42,+        "text": "42 items"+      },+      {+        "type": "raw_number",+        "value": 0,+        "text": "0"+      }+    ],+    [+      {+        "type": "raw_number",+        "value": 3,+        "text": "Three"+      }+    ]+  ],+  "column_settings": [+    null,+    {+      "align": "right"+    },+    null+  ],+  "block_id": "fake_block_id"+}
+ test/spec/fixtures/valid/blocks/table_sparse_defaults.json view
@@ -0,0 +1,34 @@+{+  "type": "table",+  "rows": [+    [+      {+        "type": "raw_text",+        "text": "Defaults"+      }+    ]+  ],+  "column_settings": [+    null,+    null,+    null,+    null,+    null,+    null,+    null,+    null,+    null,+    null,+    null,+    null,+    null,+    null,+    null,+    null,+    null,+    null,+    null,+    null+  ],+  "block_id": "fake_block_id"+}
+ test/spec/fixtures/valid/blocks/table_text_at_limit.json view
@@ -0,0 +1,13 @@+{+  "type": "table",+  "rows": [+    [+      {+        "type": "raw_number",+        "value": 1,+        "text": "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀"+      }+    ]+  ],+  "block_id": "fake_block_id"+}
+ test/spec/fixtures/valid/messages/message_table_total_at_limit.json view
@@ -0,0 +1,32 @@+{+  "channel": "C123",+  "blocks": [+    {+      "type": "table",+      "rows": [+        [+          {+            "type": "raw_text",+            "text": "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀"+          }+        ]+      ],+      "block_id": "fake_block_id"+    },+    {+      "type": "table",+      "rows": [+        [+          {+            "type": "raw_number",+            "value": 1,+            "text": "😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀"+          }+        ]+      ],+      "block_id": "fake_block_id"+    }+  ],+  "text": "",+  "mrkdwn": true+}
test/spec/limits.json view
@@ -87,7 +87,9 @@   "table": {     "rows": { "max_items": 100 },     "columns": { "max_items": 20 },-    "column_settings": { "max_items": 20 }+    "column_settings": { "max_items": 20 },+    "content": { "max_length": 10000 },+    "total_content": { "max_length": 10000 }   },   "data_table": {     "rows": { "min_items": 2, "max_items": 201 },
test/spec/manifest.json view
@@ -1,5 +1,5 @@ {-  "spec_version": "1.5.0",+  "spec_version": "1.6.0",   "fixtures": [     {       "id": "attachments/attachment_multi_block",@@ -720,6 +720,30 @@       "description": "A chart has three fifths of the row weight",       "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/layout-block",       "since": "1.5.0"+    },+    {+      "id": "blocks/table_numeric",+      "description": "Numeric display text with ragged rows and partial leading-column settings",+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/table-block/",+      "since": "1.6.0"+    },+    {+      "id": "blocks/table_sparse_defaults",+      "description": "Twenty skipped column settings retain their positions and count toward the limit",+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/table-block/",+      "since": "1.6.0"+    },+    {+      "id": "blocks/table_text_at_limit",+      "description": "Numeric display text at the 10000 Unicode code-point per-table boundary",+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/table-block/",+      "since": "1.6.0"+    },+    {+      "id": "messages/message_table_total_at_limit",+      "description": "Raw and numeric text across two tables at the 10000-code-point message boundary",+      "slack_docs": "https://docs.slack.dev/reference/block-kit/blocks/table-block/",+      "since": "1.6.0"     }   ] }
test/spec/model.json view
@@ -19,7 +19,7 @@     {       "package": "block",       "name": "TableCell",-      "description": "A value that can appear as a table block cell: raw text or a rich text block."+      "description": "A value that can appear as a table block cell: raw text, a raw number, or a rich text block."     },     {       "package": "block",@@ -446,7 +446,7 @@           "wire": "actions",           "kind": "list",           "type": "ButtonElement",-          "description": "Adds buttons displayed at the bottom of the card, in display order.",+          "description": "Adds up to three buttons in serialization order. Slack controls their visual placement: buttons are right-aligned with primary last; a danger button precedes the other button in a two-button group, or sits at the card's far left with three buttons.",           "limits": "card.actions"         },         {@@ -813,7 +813,7 @@       "package": "block",       "goConstructor": "NewFileBlock",       "wireType": "file",-      "description": "A remote file previously added with the files.remote API.",+      "description": "A retrieved remote-file representation. Register and share files through the files.remote API; Slack does not accept directly posting file blocks.",       "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/file-block",       "implements": [         "LayoutChild"@@ -1177,7 +1177,7 @@       "package": "block",       "goConstructor": "NewTableBlock",       "wireType": "table",-      "description": "A simple table of raw text and rich text cells.",+      "description": "A simple table of raw text, numeric and rich text cells. Cell text is limited to 10,000 Unicode code points per table and across tables in a message.",       "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/table-block",       "implements": [         "LayoutChild",@@ -1201,8 +1201,9 @@           "wire": "column_settings",           "kind": "list",           "type": "ColumnSettings",-          "description": "Adds per-column settings in column order. Provide one entry for every column.",-          "limits": "table.column_settings"+          "description": "Optional settings for leading columns, up to 20 entries. Use null to retain a column’s default settings without shifting later entries.",+          "limits": "table.column_settings",+          "nullableItems": true         },         {           "method": "blockId",@@ -3737,9 +3738,10 @@       "package": "object",       "goConstructor": "NewRawNumber",       "wireType": "raw_number",-      "description": "A numeric data table cell with display text.",+      "description": "A numeric table or data table cell with display text.",       "docUrl": "https://docs.slack.dev/reference/block-kit/blocks/data-table-block",       "implements": [+        "TableCell",         "DataTableCell"       ],       "defaults": {},