slackblock 2.0.0-beta.3 → 3.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -4,24 +4,34 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
4
4
 
5
5
  // src/errors.ts
6
6
  var SlackblockValidationError = class extends Error {
7
- constructor(message, path, rule) {
8
- super(`${path}: ${message}`);
7
+ constructor(issue) {
8
+ super(`${issue.path}: ${issue.message}`);
9
9
  __publicField(this, "path");
10
10
  __publicField(this, "rule");
11
+ __publicField(this, "subcode");
12
+ __publicField(this, "component");
13
+ __publicField(this, "field");
14
+ __publicField(this, "issue");
11
15
  this.name = "SlackblockValidationError";
12
- this.path = path;
13
- this.rule = rule;
16
+ this.path = issue.path;
17
+ this.rule = issue.rule;
18
+ this.subcode = issue.subcode;
19
+ this.component = issue.component;
20
+ this.field = issue.field;
21
+ this.issue = issue;
14
22
  }
15
23
  };
16
24
 
17
25
  // src/utils/validation-context.ts
18
26
  var context = {
19
27
  mode: "warn",
20
- path: []
28
+ path: [],
29
+ reporter: void 0
21
30
  };
22
- var initContext = (mode) => {
31
+ var initContext = (mode, reporter) => {
23
32
  context.mode = mode;
24
33
  context.path = [];
34
+ context.reporter = reporter;
25
35
  };
26
36
  var pushPath = (segment) => {
27
37
  context.path.push(segment);
@@ -30,26 +40,63 @@ var popPath = () => {
30
40
  context.path.pop();
31
41
  };
32
42
  var getPath = () => context.path.join(" > ");
33
- var report = (message, rule) => {
43
+ var getCurrentComponent = () => {
44
+ for (let index = context.path.length - 1; index >= 0; index--) {
45
+ const segment = context.path[index];
46
+ if (segment !== "Message") {
47
+ return segment;
48
+ }
49
+ }
50
+ return context.path.at(-1);
51
+ };
52
+ var toIssue = (input) => {
53
+ var _a;
54
+ return {
55
+ ...input,
56
+ path: getPath(),
57
+ component: (_a = input.component) != null ? _a : getCurrentComponent()
58
+ };
59
+ };
60
+ var report = (input) => {
34
61
  if (context.mode === "off") {
35
62
  return;
36
63
  }
37
- const path = getPath();
64
+ const issue = toIssue(input);
38
65
  if (context.mode === "warn") {
39
- console.warn(`[slackblock] ${path}: ${message}`);
66
+ if (context.reporter) {
67
+ context.reporter(issue);
68
+ } else {
69
+ console.warn(`[slackblock] ${issue.path}: ${issue.message}`);
70
+ }
40
71
  return;
41
72
  }
42
- throw new SlackblockValidationError(message, path, rule);
73
+ throw new SlackblockValidationError(issue);
43
74
  };
44
75
 
45
76
  // src/utils/validation.ts
46
77
  var toKebab = (name) => name.replaceAll(/([A-Z])/g, "-$1").toLowerCase();
78
+ var isMissing = (value) => {
79
+ if (value === void 0 || value === null) {
80
+ return true;
81
+ }
82
+ if (typeof value === "string") {
83
+ return value === "";
84
+ }
85
+ if (Array.isArray(value)) {
86
+ return value.length === 0;
87
+ }
88
+ return false;
89
+ };
47
90
  var warnIfTooLong = (name, value, max) => {
48
91
  if (!value) {
49
92
  return;
50
93
  }
51
94
  if (value.length > max) {
52
- report(`${name} exceeds ${max} characters.`, "value-too-long");
95
+ report({
96
+ message: `${name} exceeds ${max} characters.`,
97
+ rule: "too-long",
98
+ subcode: "value-too-long"
99
+ });
53
100
  }
54
101
  };
55
102
  var warnIfTooMany = (name, values, max) => {
@@ -57,22 +104,42 @@ var warnIfTooMany = (name, values, max) => {
57
104
  return;
58
105
  }
59
106
  if (values.length > max) {
60
- report(`${name} exceeds ${max} items.`, "too-many-items");
107
+ report({
108
+ message: `${name} exceeds ${max} items.`,
109
+ rule: "too-many",
110
+ subcode: "too-many-items"
111
+ });
61
112
  }
62
113
  };
63
114
  var requireField = (fieldName, value) => {
64
- if (value === void 0 || value === null || value === "") {
65
- report(`${fieldName} is required.`, `${toKebab(fieldName)}-required`);
115
+ if (isMissing(value)) {
116
+ report({
117
+ message: `${fieldName} is required.`,
118
+ rule: "required-field",
119
+ subcode: `${toKebab(fieldName)}-required`,
120
+ field: fieldName
121
+ });
66
122
  }
67
123
  };
124
+ var requireOneOf = (fieldNames, values) => {
125
+ if (values.some((value) => !isMissing(value))) {
126
+ return;
127
+ }
128
+ report({
129
+ message: `At least one of ${fieldNames.join(" or ")} is required.`,
130
+ rule: "invalid-structure",
131
+ subcode: `${fieldNames.map((fieldName) => toKebab(fieldName)).join("-or-")}-required`
132
+ });
133
+ };
68
134
 
69
135
  // src/transformers/block/text.ts
70
136
  var transformText = (element) => {
71
137
  const { plainText, children, emoji, verbatim } = element.props;
72
138
  const res = {
73
139
  type: plainText ? "plain_text" : "mrkdwn",
74
- text: children
140
+ text: children != null ? children : ""
75
141
  };
142
+ requireField("text", children);
76
143
  if (typeof children === "string") {
77
144
  warnIfTooLong("Text", children, 3e3);
78
145
  }
@@ -115,7 +182,12 @@ var registry_default = Transformers;
115
182
  var transform = (element) => {
116
183
  const type = get_type_default(element);
117
184
  if (!registry_default[type]) {
118
- report(`No transformer for component type '${type}'.`, "unknown-type");
185
+ report({
186
+ message: `No transformer for component type '${type}'.`,
187
+ rule: "unsupported-child",
188
+ subcode: "unknown-type",
189
+ component: type
190
+ });
119
191
  return {};
120
192
  }
121
193
  pushPath(type);
@@ -140,7 +212,10 @@ var MAX_ACTIONS_ELEMENTS = 25;
140
212
  var MAX_CONTEXT_ELEMENTS = 10;
141
213
  var MAX_CONFIRM_TITLE = 100;
142
214
  var MAX_CONFIRM_TEXT = 300;
215
+ var MAX_CONFIRM_BUTTON_TEXT = 30;
143
216
  var MAX_BUTTON_TEXT = 75;
217
+ var MAX_BUTTON_ACCESSIBILITY_LABEL = 75;
218
+ var MAX_BUTTON_URL = 3e3;
144
219
  var MAX_BUTTON_VALUE = 2e3;
145
220
  var MAX_OPTION_TEXT = 75;
146
221
  var MAX_OPTION_VALUE = 150;
@@ -148,10 +223,19 @@ var MAX_OPTION_DESCRIPTION = 75;
148
223
  var MAX_OPTION_URL = 3e3;
149
224
  var MAX_OPTION_GROUP_LABEL = 75;
150
225
  var MAX_OPTION_GROUP_OPTIONS = 100;
226
+ var MAX_SELECT_OPTIONS = 100;
227
+ var MAX_SELECT_OPTION_GROUPS = 100;
151
228
  var MAX_IMAGE_URL = 3e3;
152
229
  var MAX_IMAGE_ALT_TEXT = 2e3;
153
230
  var MAX_IMAGE_TITLE = 2e3;
231
+ var MAX_INPUT_LABEL = 2e3;
232
+ var MAX_INPUT_HINT = 2e3;
233
+ var MAX_CHECKBOX_OPTIONS = 10;
234
+ var MAX_RADIO_OPTIONS = 10;
235
+ var MAX_OVERFLOW_OPTIONS = 5;
154
236
  var MAX_VIDEO_DESCRIPTION = 200;
237
+ var MAX_VIDEO_TITLE = 200;
238
+ var MAX_VIDEO_AUTHOR_NAME = 50;
155
239
  var MAX_BLOCK_ID_LENGTH = 255;
156
240
  var MAX_MESSAGE_TEXT = 4e4;
157
241
  var RECOMMENDED_MESSAGE_TEXT = 4e3;
@@ -171,14 +255,20 @@ function jsx(type, props) {
171
255
  // src/transformers/block/confirmation.tsx
172
256
  var transformConfirmation = (child) => {
173
257
  const { title, confirm, deny, children } = child.props;
258
+ requireField("title", title);
259
+ requireField("confirm", confirm);
260
+ requireField("deny", deny);
261
+ requireField("text", children);
174
262
  warnIfTooLong("Confirm title", title, MAX_CONFIRM_TITLE);
175
- const text = transform(children);
263
+ warnIfTooLong("Confirm confirm", confirm, MAX_CONFIRM_BUTTON_TEXT);
264
+ warnIfTooLong("Confirm deny", deny, MAX_CONFIRM_BUTTON_TEXT);
265
+ const text = transform(children != null ? children : /* @__PURE__ */ jsx(Text, { plainText: true, children: "" }));
176
266
  warnIfTooLong("Confirm text", text.text, MAX_CONFIRM_TEXT);
177
267
  const res = {
178
- title: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: title })),
268
+ title: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: title != null ? title : "" })),
179
269
  text,
180
- confirm: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: confirm })),
181
- deny: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: deny }))
270
+ confirm: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: confirm != null ? confirm : "" })),
271
+ deny: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: deny != null ? deny : "" }))
182
272
  };
183
273
  return res;
184
274
  };
@@ -188,14 +278,17 @@ var confirmation_default = transformConfirmation;
188
278
  var transformButton = (child) => {
189
279
  const { actionId, children, url, value, style, confirm, accessibilityLabel } = child.props;
190
280
  requireField("actionId", actionId);
281
+ requireField("text", children);
191
282
  warnIfTooLong("Button action_id", actionId, MAX_ACTION_ID_LENGTH);
192
283
  if (typeof children === "string") {
193
284
  warnIfTooLong("Button text", children, MAX_BUTTON_TEXT);
194
285
  }
286
+ warnIfTooLong("Button url", url, MAX_BUTTON_URL);
195
287
  warnIfTooLong("Button value", value, MAX_BUTTON_VALUE);
288
+ warnIfTooLong("Button accessibility_label", accessibilityLabel, MAX_BUTTON_ACCESSIBILITY_LABEL);
196
289
  const res = {
197
290
  type: "button",
198
- text: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children })),
291
+ text: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: children != null ? children : "" })),
199
292
  action_id: actionId
200
293
  };
201
294
  if (url) {
@@ -220,10 +313,14 @@ var button_default = transformButton;
220
313
  // src/transformers/block/image.ts
221
314
  var transformImage = (child) => {
222
315
  const { url, alt } = child.props;
316
+ requireField("url", url);
317
+ requireField("alt", alt);
318
+ warnIfTooLong("Image image_url", url, MAX_IMAGE_URL);
319
+ warnIfTooLong("Image alt_text", alt, MAX_IMAGE_ALT_TEXT);
223
320
  return {
224
321
  type: "image",
225
- image_url: url,
226
- alt_text: alt
322
+ image_url: url != null ? url : "",
323
+ alt_text: alt != null ? alt : ""
227
324
  };
228
325
  };
229
326
  var image_default = transformImage;
@@ -256,34 +353,63 @@ var transformContainer = (child) => {
256
353
  var container_default = transformContainer;
257
354
 
258
355
  // src/transformers/layout/section.ts
356
+ var toTextElement = (value) => {
357
+ if (typeof value === "string") {
358
+ return {
359
+ children: [],
360
+ type: Text,
361
+ props: { children: value }
362
+ };
363
+ }
364
+ return value;
365
+ };
366
+ var normalizeFields = (values) => {
367
+ if (!values) {
368
+ return [];
369
+ }
370
+ return Array.isArray(values) ? values : [values];
371
+ };
259
372
  var transformSection = (element) => {
260
- const { text, blockId, children, accessory } = element.props;
373
+ const {
374
+ text,
375
+ fields,
376
+ blockId,
377
+ children,
378
+ accessory,
379
+ expand
380
+ } = element.props;
381
+ const normalizedFields = [...normalizeFields(fields), ...normalizeFields(children)];
261
382
  warnIfTooLong("block_id", blockId, MAX_BLOCK_ID_LENGTH);
262
383
  const res = {
263
- type: "section",
264
- text: transform(text)
384
+ type: "section"
265
385
  };
386
+ if (text !== void 0) {
387
+ res.text = transform(toTextElement(text));
388
+ }
266
389
  if (blockId) {
267
390
  res.block_id = blockId;
268
391
  }
269
392
  if (accessory) {
270
393
  res.accessory = transform(accessory);
271
394
  }
272
- if (children) {
395
+ if (expand !== void 0) {
396
+ res.expand = expand;
397
+ }
398
+ if (normalizedFields.length > 0) {
273
399
  res.fields = [];
274
- let fields = children;
275
- if (!Array.isArray(fields)) {
276
- fields = [fields];
277
- }
278
- for (const field of fields) {
400
+ for (const field of normalizedFields) {
279
401
  if (field) {
280
- const t = transform(field);
402
+ const t = transform(toTextElement(field));
281
403
  warnIfTooLong("Section field text", t.text, MAX_SECTION_FIELD_TEXT);
282
404
  res.fields.push(t);
283
405
  }
284
406
  }
285
407
  warnIfTooMany("Section fields", res.fields, MAX_SECTION_FIELDS);
408
+ if (res.fields.length === 0) {
409
+ delete res.fields;
410
+ }
286
411
  }
412
+ requireOneOf(["text", "fields"], [res.text, res.fields]);
287
413
  return res;
288
414
  };
289
415
  var section_default = transformSection;
@@ -292,10 +418,8 @@ var section_default = transformSection;
292
418
  var transformActions = (child) => {
293
419
  const { children, blockId } = child.props;
294
420
  warnIfTooLong("block_id", blockId, MAX_BLOCK_ID_LENGTH);
295
- let elements = children;
296
- if (!Array.isArray(elements)) {
297
- elements = [elements];
298
- }
421
+ const elements = children ? Array.isArray(children) ? children : [children] : [];
422
+ requireField("elements", elements);
299
423
  const res = {
300
424
  type: "actions",
301
425
  elements: elements.map((element) => transform(element))
@@ -312,10 +436,8 @@ var actions_default = transformActions;
312
436
  var transformContext = (child) => {
313
437
  const { children, blockId } = child.props;
314
438
  warnIfTooLong("block_id", blockId, MAX_BLOCK_ID_LENGTH);
315
- let elements = children;
316
- if (!Array.isArray(elements)) {
317
- elements = [elements];
318
- }
439
+ const elements = children ? Array.isArray(children) ? children : [children] : [];
440
+ requireField("elements", elements);
319
441
  const res = {
320
442
  type: "context",
321
443
  elements: elements.map((element) => transform(element))
@@ -343,11 +465,12 @@ var divider_default = transformDivider;
343
465
  // src/transformers/layout/file.ts
344
466
  var transformFile = (child) => {
345
467
  const { externalId, blockId } = child.props;
468
+ requireField("externalId", externalId);
346
469
  warnIfTooLong("block_id", blockId, 255);
347
470
  const res = {
348
471
  type: "file",
349
472
  source: "remote",
350
- external_id: externalId
473
+ external_id: externalId != null ? externalId : ""
351
474
  };
352
475
  if (blockId) {
353
476
  res.block_id = blockId;
@@ -364,7 +487,7 @@ var transformHeader = (child) => {
364
487
  warnIfTooLong("block_id", blockId, MAX_BLOCK_ID_LENGTH);
365
488
  const res = {
366
489
  type: "header",
367
- text: transform(/* @__PURE__ */ jsx(Text, { plainText: true, emoji, children: text }))
490
+ text: transform(/* @__PURE__ */ jsx(Text, { plainText: true, emoji, children: text != null ? text : "" }))
368
491
  };
369
492
  if (blockId) {
370
493
  res.block_id = blockId;
@@ -376,13 +499,15 @@ var header_default = transformHeader;
376
499
  // src/transformers/layout/image.tsx
377
500
  var transformImageLayout = (child) => {
378
501
  const { url, alt, title, blockId } = child.props;
502
+ requireField("url", url);
503
+ requireField("alt", alt);
379
504
  warnIfTooLong("Image image_url", url, MAX_IMAGE_URL);
380
505
  warnIfTooLong("Image alt_text", alt, MAX_IMAGE_ALT_TEXT);
381
506
  warnIfTooLong("block_id", blockId, MAX_BLOCK_ID_LENGTH);
382
507
  const res = {
383
508
  type: "image",
384
- image_url: url,
385
- alt_text: alt
509
+ image_url: url != null ? url : "",
510
+ alt_text: alt != null ? alt : ""
386
511
  };
387
512
  if (title) {
388
513
  warnIfTooLong("Image title", title, MAX_IMAGE_TITLE);
@@ -398,11 +523,16 @@ var image_default2 = transformImageLayout;
398
523
  // src/transformers/layout/input.tsx
399
524
  var transformInput = (child) => {
400
525
  const { label, element, hint, optional, blockId } = child.props;
401
- warnIfTooLong("block_id", blockId, 255);
526
+ const resolvedElement = element ? transform(element) : { type: "plain_text_input", action_id: "" };
527
+ requireField("label", label);
528
+ requireField("element", element);
529
+ warnIfTooLong("Input label", label, MAX_INPUT_LABEL);
530
+ warnIfTooLong("Input hint", hint, MAX_INPUT_HINT);
531
+ warnIfTooLong("block_id", blockId, MAX_BLOCK_ID_LENGTH);
402
532
  const res = {
403
533
  type: "input",
404
- label: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: label })),
405
- element: transform(element)
534
+ label: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: label != null ? label : "" })),
535
+ element: resolvedElement
406
536
  };
407
537
  if (hint) {
408
538
  res.hint = transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: hint }));
@@ -459,10 +589,12 @@ var toBlockElements = (children) => {
459
589
  // src/transformers/layout/rich-text.ts
460
590
  var transformRichText = (child) => {
461
591
  const { elements, children, blockId } = child.props;
592
+ const richTextElements = elements != null ? elements : toBlockElements(children);
462
593
  warnIfTooLong("block_id", blockId, 255);
594
+ requireField("elements", richTextElements);
463
595
  const res = {
464
596
  type: "rich_text",
465
- elements: elements != null ? elements : toBlockElements(children)
597
+ elements: richTextElements
466
598
  };
467
599
  if (blockId) {
468
600
  res.block_id = blockId;
@@ -485,13 +617,19 @@ var transformVideo = (child) => {
485
617
  providerIconUrl,
486
618
  blockId
487
619
  } = child.props;
620
+ requireField("title", title);
621
+ requireField("videoUrl", videoUrl);
622
+ requireField("thumbnailUrl", thumbnailUrl);
623
+ requireField("altText", altText);
624
+ warnIfTooLong("Video title", title, MAX_VIDEO_TITLE);
625
+ warnIfTooLong("Video author_name", authorName, MAX_VIDEO_AUTHOR_NAME);
488
626
  warnIfTooLong("block_id", blockId, MAX_BLOCK_ID_LENGTH);
489
627
  const res = {
490
628
  type: "video",
491
- title: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: title })),
492
- video_url: videoUrl,
493
- thumbnail_url: thumbnailUrl,
494
- alt_text: altText
629
+ title: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: title != null ? title : "" })),
630
+ video_url: videoUrl != null ? videoUrl : "",
631
+ thumbnail_url: thumbnailUrl != null ? thumbnailUrl : "",
632
+ alt_text: altText != null ? altText : ""
495
633
  };
496
634
  if (titleUrl) {
497
635
  res.title_url = titleUrl;
@@ -528,9 +666,10 @@ var transformTextInput = (child) => {
528
666
  focusOnLoad,
529
667
  dispatchActionConfig
530
668
  } = child.props;
531
- warnIfTooLong("TextInput action_id", actionId, 255);
669
+ requireField("actionId", actionId);
670
+ warnIfTooLong("TextInput action_id", actionId, MAX_ACTION_ID_LENGTH);
532
671
  if (placeholder) {
533
- warnIfTooLong("TextInput placeholder", placeholder, 150);
672
+ warnIfTooLong("TextInput placeholder", placeholder, MAX_PLACEHOLDER_LENGTH);
534
673
  }
535
674
  const res = {
536
675
  type: "plain_text_input",
@@ -619,7 +758,12 @@ var transformDatePicker = (child) => {
619
758
  }
620
759
  if (initialDate) {
621
760
  if (!isValidDateString(initialDate)) {
622
- report("Date must be valid and in format YYYY-MM-DD.", "invalid-date-format");
761
+ report({
762
+ message: "Date must be valid and in format YYYY-MM-DD.",
763
+ rule: "invalid-format",
764
+ subcode: "invalid-date-format",
765
+ field: "initialDate"
766
+ });
623
767
  }
624
768
  res.initial_date = initialDate;
625
769
  }
@@ -638,15 +782,14 @@ var transformCheckboxes = (child) => {
638
782
  const { actionId, children, initialOptions, confirm, focusOnLoad } = child.props;
639
783
  requireField("actionId", actionId);
640
784
  warnIfTooLong("Checkboxes action_id", actionId, MAX_ACTION_ID_LENGTH);
641
- let elements = children;
642
- if (!Array.isArray(elements)) {
643
- elements = [elements];
644
- }
785
+ const elements = Array.isArray(children) ? children : [children];
786
+ requireField("options", elements);
645
787
  const res = {
646
788
  type: "checkboxes",
647
789
  action_id: actionId,
648
790
  options: elements.map((element) => transform(element))
649
791
  };
792
+ warnIfTooMany("Checkboxes options", res.options, MAX_CHECKBOX_OPTIONS);
650
793
  if (initialOptions && initialOptions.length > 0) {
651
794
  res.initial_options = initialOptions.map((option) => transform(option));
652
795
  }
@@ -702,6 +845,31 @@ var assignStaticOptions = (elements, result) => {
702
845
  result.option_groups = elements.map((element) => transform(element));
703
846
  }
704
847
  };
848
+ var applyConversationSettings = (result, settings) => {
849
+ const { defaultToCurrentConversation, responseUrlEnabled, filter } = settings;
850
+ if (defaultToCurrentConversation !== void 0) {
851
+ result.default_to_current_conversation = defaultToCurrentConversation;
852
+ }
853
+ if (responseUrlEnabled !== void 0) {
854
+ result.response_url_enabled = responseUrlEnabled;
855
+ }
856
+ if (!filter) {
857
+ return;
858
+ }
859
+ const filterValue = {};
860
+ if (filter.include && filter.include.length > 0) {
861
+ filterValue.include = filter.include;
862
+ }
863
+ if (filter.excludeExternalSharedChannels !== void 0) {
864
+ filterValue.exclude_external_shared_channels = filter.excludeExternalSharedChannels;
865
+ }
866
+ if (filter.excludeBotUsers !== void 0) {
867
+ filterValue.exclude_bot_users = filter.excludeBotUsers;
868
+ }
869
+ if (Object.keys(filterValue).length > 0) {
870
+ result.filter = filterValue;
871
+ }
872
+ };
705
873
  var applyInitialSelections = (type, isMulti, result, initialValues) => {
706
874
  const { initialOptions, initialUsers, initialConversations, initialChannels } = initialValues;
707
875
  switch (type) {
@@ -776,12 +944,15 @@ var transformSelect = (child) => {
776
944
  warnIfTooLong("Select placeholder", placeholder, MAX_PLACEHOLDER_LENGTH);
777
945
  const result = {
778
946
  type: typeString,
779
- placeholder: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: placeholder })),
947
+ placeholder: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: placeholder != null ? placeholder : "" })),
780
948
  action_id: actionId
781
949
  };
782
950
  const elements = normalizeElements(children);
783
951
  if (type === selectTypes.STATIC) {
784
- assignStaticOptions(elements, result);
952
+ requireField("options", elements);
953
+ if (elements.length > 0) {
954
+ assignStaticOptions(elements, result);
955
+ }
785
956
  }
786
957
  if (confirm) {
787
958
  result.confirm = transform(confirm);
@@ -802,27 +973,13 @@ var transformSelect = (child) => {
802
973
  result.min_query_length = minQueryLength;
803
974
  }
804
975
  if (type === selectTypes.CONVERSATION) {
805
- if (defaultToCurrentConversation !== void 0) {
806
- result.default_to_current_conversation = defaultToCurrentConversation;
807
- }
808
- if (responseUrlEnabled !== void 0) {
809
- result.response_url_enabled = responseUrlEnabled;
810
- }
811
- if (filter) {
812
- const filterValue = {};
813
- if (filter.include && filter.include.length > 0) {
814
- filterValue.include = filter.include;
815
- }
816
- if (filter.excludeExternalSharedChannels !== void 0) {
817
- filterValue.exclude_external_shared_channels = filter.excludeExternalSharedChannels;
818
- }
819
- if (filter.excludeBotUsers !== void 0) {
820
- filterValue.exclude_bot_users = filter.excludeBotUsers;
821
- }
822
- if (Object.keys(filterValue).length > 0) {
823
- result.filter = filterValue;
824
- }
825
- }
976
+ applyConversationSettings(result, { defaultToCurrentConversation, responseUrlEnabled, filter });
977
+ }
978
+ if (result.options) {
979
+ warnIfTooMany("Select options", result.options, MAX_SELECT_OPTIONS);
980
+ }
981
+ if (result.option_groups) {
982
+ warnIfTooMany("Select option_groups", result.option_groups, MAX_SELECT_OPTION_GROUPS);
826
983
  }
827
984
  return result;
828
985
  };
@@ -831,13 +988,14 @@ var select_default = transformSelect;
831
988
  // src/transformers/input/option.tsx
832
989
  var transformOption = (child) => {
833
990
  const { children: text, value, url, description } = child.props;
991
+ requireField("text", text);
834
992
  requireField("value", value);
835
993
  warnIfTooLong("Option text", text, MAX_OPTION_TEXT);
836
994
  warnIfTooLong("Option value", value, MAX_OPTION_VALUE);
837
995
  warnIfTooLong("Option description", description, MAX_OPTION_DESCRIPTION);
838
996
  warnIfTooLong("Option url", url, MAX_OPTION_URL);
839
997
  const res = {
840
- text: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: text })),
998
+ text: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: text != null ? text : "" })),
841
999
  value
842
1000
  };
843
1001
  if (description) {
@@ -853,14 +1011,13 @@ var option_default = transformOption;
853
1011
  // src/transformers/input/option-group.tsx
854
1012
  var transformOptionGroup = (child) => {
855
1013
  const { label, children } = child.props;
1014
+ const options = Array.isArray(children) ? children : [children];
1015
+ requireField("label", label);
1016
+ requireField("options", options);
856
1017
  warnIfTooLong("OptionGroup label", label, MAX_OPTION_GROUP_LABEL);
857
- let options = children;
858
- if (!Array.isArray(options)) {
859
- options = [options];
860
- }
861
1018
  warnIfTooMany("OptionGroup options", options, MAX_OPTION_GROUP_OPTIONS);
862
1019
  return {
863
- label: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: label })),
1020
+ label: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: label != null ? label : "" })),
864
1021
  options: options.map((option) => transform(option))
865
1022
  };
866
1023
  };
@@ -869,16 +1026,16 @@ var option_group_default = transformOptionGroup;
869
1026
  // src/transformers/input/overflow.ts
870
1027
  var transformOverflow = (child) => {
871
1028
  const { actionId, children, confirm } = child.props;
872
- warnIfTooLong("Overflow action_id", actionId, 255);
873
- let elements = children;
874
- if (!Array.isArray(elements)) {
875
- elements = [elements];
876
- }
1029
+ const elements = Array.isArray(children) ? children : [children];
1030
+ requireField("actionId", actionId);
1031
+ requireField("options", elements);
1032
+ warnIfTooLong("Overflow action_id", actionId, MAX_ACTION_ID_LENGTH);
877
1033
  const res = {
878
1034
  type: "overflow",
879
1035
  action_id: actionId,
880
1036
  options: elements.map((element) => transform(element))
881
1037
  };
1038
+ warnIfTooMany("Overflow options", res.options, MAX_OVERFLOW_OPTIONS);
882
1039
  if (confirm) {
883
1040
  res.confirm = transform(confirm);
884
1041
  }
@@ -891,15 +1048,14 @@ var transformRadioGroup = (child) => {
891
1048
  const { actionId, children, initialOption, confirm, focusOnLoad } = child.props;
892
1049
  requireField("actionId", actionId);
893
1050
  warnIfTooLong("RadioGroup action_id", actionId, MAX_ACTION_ID_LENGTH);
894
- let elements = children;
895
- if (!Array.isArray(elements)) {
896
- elements = [elements];
897
- }
1051
+ const elements = Array.isArray(children) ? children : [children];
1052
+ requireField("options", elements);
898
1053
  const res = {
899
1054
  type: "radio_buttons",
900
1055
  action_id: actionId,
901
1056
  options: elements.map((element) => transform(element))
902
1057
  };
1058
+ warnIfTooMany("RadioGroup options", res.options, MAX_RADIO_OPTIONS);
903
1059
  if (initialOption) {
904
1060
  res.initial_option = transform(initialOption);
905
1061
  }
@@ -939,7 +1095,12 @@ var transformTimePicker = (child) => {
939
1095
  }
940
1096
  if (initialTime) {
941
1097
  if (!isValidTimeString(initialTime)) {
942
- report("Time must be valid and in format HH:MM.", "invalid-time-format");
1098
+ report({
1099
+ message: "Time must be valid and in format HH:MM.",
1100
+ rule: "invalid-format",
1101
+ subcode: "invalid-time-format",
1102
+ field: "initialTime"
1103
+ });
943
1104
  }
944
1105
  res.initial_time = initialTime;
945
1106
  }
@@ -1165,7 +1326,12 @@ var parseChildren = (children) => {
1165
1326
  popPath();
1166
1327
  }
1167
1328
  } else if (type !== "null") {
1168
- report(`No transformer for component type '${type}'.`, "unknown-type");
1329
+ report({
1330
+ message: `No transformer for component type '${type}'.`,
1331
+ rule: "unsupported-child",
1332
+ subcode: "unknown-type",
1333
+ component: type
1334
+ });
1169
1335
  }
1170
1336
  }
1171
1337
  if (transformedBlocks.length === 0) {
@@ -1178,7 +1344,7 @@ var parser_default = parseChildren;
1178
1344
  // src/renderer/index.ts
1179
1345
  var renderToBlocks = (element, options) => {
1180
1346
  var _a, _b;
1181
- initContext((_a = options == null ? void 0 : options.validate) != null ? _a : "warn");
1347
+ initContext((_a = options == null ? void 0 : options.validate) != null ? _a : "warn", options == null ? void 0 : options.onValidation);
1182
1348
  const child = typeof element === "object" && element !== null && !Array.isArray(element) && element.type === "fragment" ? element.props.children : element;
1183
1349
  const result = parser_default(child);
1184
1350
  return (_b = result.blocks) != null ? _b : [];
@@ -1211,7 +1377,7 @@ var applyMessageMetadata = (json, properties) => {
1211
1377
  };
1212
1378
  function render(element, options) {
1213
1379
  var _a, _b, _c, _d, _e;
1214
- initContext((_a = options == null ? void 0 : options.validate) != null ? _a : "warn");
1380
+ initContext((_a = options == null ? void 0 : options.validate) != null ? _a : "warn", options == null ? void 0 : options.onValidation);
1215
1381
  const properties = element.props;
1216
1382
  const typeName = get_type_default(element);
1217
1383
  if (typeName !== "Message") {