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.cjs CHANGED
@@ -33,24 +33,34 @@ module.exports = __toCommonJS(src_exports);
33
33
 
34
34
  // src/errors.ts
35
35
  var SlackblockValidationError = class extends Error {
36
- constructor(message, path, rule) {
37
- super(`${path}: ${message}`);
36
+ constructor(issue) {
37
+ super(`${issue.path}: ${issue.message}`);
38
38
  __publicField(this, "path");
39
39
  __publicField(this, "rule");
40
+ __publicField(this, "subcode");
41
+ __publicField(this, "component");
42
+ __publicField(this, "field");
43
+ __publicField(this, "issue");
40
44
  this.name = "SlackblockValidationError";
41
- this.path = path;
42
- this.rule = rule;
45
+ this.path = issue.path;
46
+ this.rule = issue.rule;
47
+ this.subcode = issue.subcode;
48
+ this.component = issue.component;
49
+ this.field = issue.field;
50
+ this.issue = issue;
43
51
  }
44
52
  };
45
53
 
46
54
  // src/utils/validation-context.ts
47
55
  var context = {
48
56
  mode: "warn",
49
- path: []
57
+ path: [],
58
+ reporter: void 0
50
59
  };
51
- var initContext = (mode) => {
60
+ var initContext = (mode, reporter) => {
52
61
  context.mode = mode;
53
62
  context.path = [];
63
+ context.reporter = reporter;
54
64
  };
55
65
  var pushPath = (segment) => {
56
66
  context.path.push(segment);
@@ -59,26 +69,63 @@ var popPath = () => {
59
69
  context.path.pop();
60
70
  };
61
71
  var getPath = () => context.path.join(" > ");
62
- var report = (message, rule) => {
72
+ var getCurrentComponent = () => {
73
+ for (let index = context.path.length - 1; index >= 0; index--) {
74
+ const segment = context.path[index];
75
+ if (segment !== "Message") {
76
+ return segment;
77
+ }
78
+ }
79
+ return context.path.at(-1);
80
+ };
81
+ var toIssue = (input) => {
82
+ var _a;
83
+ return {
84
+ ...input,
85
+ path: getPath(),
86
+ component: (_a = input.component) != null ? _a : getCurrentComponent()
87
+ };
88
+ };
89
+ var report = (input) => {
63
90
  if (context.mode === "off") {
64
91
  return;
65
92
  }
66
- const path = getPath();
93
+ const issue = toIssue(input);
67
94
  if (context.mode === "warn") {
68
- console.warn(`[slackblock] ${path}: ${message}`);
95
+ if (context.reporter) {
96
+ context.reporter(issue);
97
+ } else {
98
+ console.warn(`[slackblock] ${issue.path}: ${issue.message}`);
99
+ }
69
100
  return;
70
101
  }
71
- throw new SlackblockValidationError(message, path, rule);
102
+ throw new SlackblockValidationError(issue);
72
103
  };
73
104
 
74
105
  // src/utils/validation.ts
75
106
  var toKebab = (name) => name.replaceAll(/([A-Z])/g, "-$1").toLowerCase();
107
+ var isMissing = (value) => {
108
+ if (value === void 0 || value === null) {
109
+ return true;
110
+ }
111
+ if (typeof value === "string") {
112
+ return value === "";
113
+ }
114
+ if (Array.isArray(value)) {
115
+ return value.length === 0;
116
+ }
117
+ return false;
118
+ };
76
119
  var warnIfTooLong = (name, value, max) => {
77
120
  if (!value) {
78
121
  return;
79
122
  }
80
123
  if (value.length > max) {
81
- report(`${name} exceeds ${max} characters.`, "value-too-long");
124
+ report({
125
+ message: `${name} exceeds ${max} characters.`,
126
+ rule: "too-long",
127
+ subcode: "value-too-long"
128
+ });
82
129
  }
83
130
  };
84
131
  var warnIfTooMany = (name, values, max) => {
@@ -86,22 +133,42 @@ var warnIfTooMany = (name, values, max) => {
86
133
  return;
87
134
  }
88
135
  if (values.length > max) {
89
- report(`${name} exceeds ${max} items.`, "too-many-items");
136
+ report({
137
+ message: `${name} exceeds ${max} items.`,
138
+ rule: "too-many",
139
+ subcode: "too-many-items"
140
+ });
90
141
  }
91
142
  };
92
143
  var requireField = (fieldName, value) => {
93
- if (value === void 0 || value === null || value === "") {
94
- report(`${fieldName} is required.`, `${toKebab(fieldName)}-required`);
144
+ if (isMissing(value)) {
145
+ report({
146
+ message: `${fieldName} is required.`,
147
+ rule: "required-field",
148
+ subcode: `${toKebab(fieldName)}-required`,
149
+ field: fieldName
150
+ });
95
151
  }
96
152
  };
153
+ var requireOneOf = (fieldNames, values) => {
154
+ if (values.some((value) => !isMissing(value))) {
155
+ return;
156
+ }
157
+ report({
158
+ message: `At least one of ${fieldNames.join(" or ")} is required.`,
159
+ rule: "invalid-structure",
160
+ subcode: `${fieldNames.map((fieldName) => toKebab(fieldName)).join("-or-")}-required`
161
+ });
162
+ };
97
163
 
98
164
  // src/transformers/block/text.ts
99
165
  var transformText = (element) => {
100
166
  const { plainText, children, emoji, verbatim } = element.props;
101
167
  const res = {
102
168
  type: plainText ? "plain_text" : "mrkdwn",
103
- text: children
169
+ text: children != null ? children : ""
104
170
  };
171
+ requireField("text", children);
105
172
  if (typeof children === "string") {
106
173
  warnIfTooLong("Text", children, 3e3);
107
174
  }
@@ -144,7 +211,12 @@ var registry_default = Transformers;
144
211
  var transform = (element) => {
145
212
  const type = get_type_default(element);
146
213
  if (!registry_default[type]) {
147
- report(`No transformer for component type '${type}'.`, "unknown-type");
214
+ report({
215
+ message: `No transformer for component type '${type}'.`,
216
+ rule: "unsupported-child",
217
+ subcode: "unknown-type",
218
+ component: type
219
+ });
148
220
  return {};
149
221
  }
150
222
  pushPath(type);
@@ -169,7 +241,10 @@ var MAX_ACTIONS_ELEMENTS = 25;
169
241
  var MAX_CONTEXT_ELEMENTS = 10;
170
242
  var MAX_CONFIRM_TITLE = 100;
171
243
  var MAX_CONFIRM_TEXT = 300;
244
+ var MAX_CONFIRM_BUTTON_TEXT = 30;
172
245
  var MAX_BUTTON_TEXT = 75;
246
+ var MAX_BUTTON_ACCESSIBILITY_LABEL = 75;
247
+ var MAX_BUTTON_URL = 3e3;
173
248
  var MAX_BUTTON_VALUE = 2e3;
174
249
  var MAX_OPTION_TEXT = 75;
175
250
  var MAX_OPTION_VALUE = 150;
@@ -177,10 +252,19 @@ var MAX_OPTION_DESCRIPTION = 75;
177
252
  var MAX_OPTION_URL = 3e3;
178
253
  var MAX_OPTION_GROUP_LABEL = 75;
179
254
  var MAX_OPTION_GROUP_OPTIONS = 100;
255
+ var MAX_SELECT_OPTIONS = 100;
256
+ var MAX_SELECT_OPTION_GROUPS = 100;
180
257
  var MAX_IMAGE_URL = 3e3;
181
258
  var MAX_IMAGE_ALT_TEXT = 2e3;
182
259
  var MAX_IMAGE_TITLE = 2e3;
260
+ var MAX_INPUT_LABEL = 2e3;
261
+ var MAX_INPUT_HINT = 2e3;
262
+ var MAX_CHECKBOX_OPTIONS = 10;
263
+ var MAX_RADIO_OPTIONS = 10;
264
+ var MAX_OVERFLOW_OPTIONS = 5;
183
265
  var MAX_VIDEO_DESCRIPTION = 200;
266
+ var MAX_VIDEO_TITLE = 200;
267
+ var MAX_VIDEO_AUTHOR_NAME = 50;
184
268
  var MAX_BLOCK_ID_LENGTH = 255;
185
269
  var MAX_MESSAGE_TEXT = 4e4;
186
270
  var RECOMMENDED_MESSAGE_TEXT = 4e3;
@@ -200,14 +284,20 @@ function jsx(type, props) {
200
284
  // src/transformers/block/confirmation.tsx
201
285
  var transformConfirmation = (child) => {
202
286
  const { title, confirm, deny, children } = child.props;
287
+ requireField("title", title);
288
+ requireField("confirm", confirm);
289
+ requireField("deny", deny);
290
+ requireField("text", children);
203
291
  warnIfTooLong("Confirm title", title, MAX_CONFIRM_TITLE);
204
- const text = transform(children);
292
+ warnIfTooLong("Confirm confirm", confirm, MAX_CONFIRM_BUTTON_TEXT);
293
+ warnIfTooLong("Confirm deny", deny, MAX_CONFIRM_BUTTON_TEXT);
294
+ const text = transform(children != null ? children : /* @__PURE__ */ jsx(Text, { plainText: true, children: "" }));
205
295
  warnIfTooLong("Confirm text", text.text, MAX_CONFIRM_TEXT);
206
296
  const res = {
207
- title: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: title })),
297
+ title: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: title != null ? title : "" })),
208
298
  text,
209
- confirm: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: confirm })),
210
- deny: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: deny }))
299
+ confirm: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: confirm != null ? confirm : "" })),
300
+ deny: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: deny != null ? deny : "" }))
211
301
  };
212
302
  return res;
213
303
  };
@@ -217,14 +307,17 @@ var confirmation_default = transformConfirmation;
217
307
  var transformButton = (child) => {
218
308
  const { actionId, children, url, value, style, confirm, accessibilityLabel } = child.props;
219
309
  requireField("actionId", actionId);
310
+ requireField("text", children);
220
311
  warnIfTooLong("Button action_id", actionId, MAX_ACTION_ID_LENGTH);
221
312
  if (typeof children === "string") {
222
313
  warnIfTooLong("Button text", children, MAX_BUTTON_TEXT);
223
314
  }
315
+ warnIfTooLong("Button url", url, MAX_BUTTON_URL);
224
316
  warnIfTooLong("Button value", value, MAX_BUTTON_VALUE);
317
+ warnIfTooLong("Button accessibility_label", accessibilityLabel, MAX_BUTTON_ACCESSIBILITY_LABEL);
225
318
  const res = {
226
319
  type: "button",
227
- text: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children })),
320
+ text: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: children != null ? children : "" })),
228
321
  action_id: actionId
229
322
  };
230
323
  if (url) {
@@ -249,10 +342,14 @@ var button_default = transformButton;
249
342
  // src/transformers/block/image.ts
250
343
  var transformImage = (child) => {
251
344
  const { url, alt } = child.props;
345
+ requireField("url", url);
346
+ requireField("alt", alt);
347
+ warnIfTooLong("Image image_url", url, MAX_IMAGE_URL);
348
+ warnIfTooLong("Image alt_text", alt, MAX_IMAGE_ALT_TEXT);
252
349
  return {
253
350
  type: "image",
254
- image_url: url,
255
- alt_text: alt
351
+ image_url: url != null ? url : "",
352
+ alt_text: alt != null ? alt : ""
256
353
  };
257
354
  };
258
355
  var image_default = transformImage;
@@ -285,34 +382,63 @@ var transformContainer = (child) => {
285
382
  var container_default = transformContainer;
286
383
 
287
384
  // src/transformers/layout/section.ts
385
+ var toTextElement = (value) => {
386
+ if (typeof value === "string") {
387
+ return {
388
+ children: [],
389
+ type: Text,
390
+ props: { children: value }
391
+ };
392
+ }
393
+ return value;
394
+ };
395
+ var normalizeFields = (values) => {
396
+ if (!values) {
397
+ return [];
398
+ }
399
+ return Array.isArray(values) ? values : [values];
400
+ };
288
401
  var transformSection = (element) => {
289
- const { text, blockId, children, accessory } = element.props;
402
+ const {
403
+ text,
404
+ fields,
405
+ blockId,
406
+ children,
407
+ accessory,
408
+ expand
409
+ } = element.props;
410
+ const normalizedFields = [...normalizeFields(fields), ...normalizeFields(children)];
290
411
  warnIfTooLong("block_id", blockId, MAX_BLOCK_ID_LENGTH);
291
412
  const res = {
292
- type: "section",
293
- text: transform(text)
413
+ type: "section"
294
414
  };
415
+ if (text !== void 0) {
416
+ res.text = transform(toTextElement(text));
417
+ }
295
418
  if (blockId) {
296
419
  res.block_id = blockId;
297
420
  }
298
421
  if (accessory) {
299
422
  res.accessory = transform(accessory);
300
423
  }
301
- if (children) {
424
+ if (expand !== void 0) {
425
+ res.expand = expand;
426
+ }
427
+ if (normalizedFields.length > 0) {
302
428
  res.fields = [];
303
- let fields = children;
304
- if (!Array.isArray(fields)) {
305
- fields = [fields];
306
- }
307
- for (const field of fields) {
429
+ for (const field of normalizedFields) {
308
430
  if (field) {
309
- const t = transform(field);
431
+ const t = transform(toTextElement(field));
310
432
  warnIfTooLong("Section field text", t.text, MAX_SECTION_FIELD_TEXT);
311
433
  res.fields.push(t);
312
434
  }
313
435
  }
314
436
  warnIfTooMany("Section fields", res.fields, MAX_SECTION_FIELDS);
437
+ if (res.fields.length === 0) {
438
+ delete res.fields;
439
+ }
315
440
  }
441
+ requireOneOf(["text", "fields"], [res.text, res.fields]);
316
442
  return res;
317
443
  };
318
444
  var section_default = transformSection;
@@ -321,10 +447,8 @@ var section_default = transformSection;
321
447
  var transformActions = (child) => {
322
448
  const { children, blockId } = child.props;
323
449
  warnIfTooLong("block_id", blockId, MAX_BLOCK_ID_LENGTH);
324
- let elements = children;
325
- if (!Array.isArray(elements)) {
326
- elements = [elements];
327
- }
450
+ const elements = children ? Array.isArray(children) ? children : [children] : [];
451
+ requireField("elements", elements);
328
452
  const res = {
329
453
  type: "actions",
330
454
  elements: elements.map((element) => transform(element))
@@ -341,10 +465,8 @@ var actions_default = transformActions;
341
465
  var transformContext = (child) => {
342
466
  const { children, blockId } = child.props;
343
467
  warnIfTooLong("block_id", blockId, MAX_BLOCK_ID_LENGTH);
344
- let elements = children;
345
- if (!Array.isArray(elements)) {
346
- elements = [elements];
347
- }
468
+ const elements = children ? Array.isArray(children) ? children : [children] : [];
469
+ requireField("elements", elements);
348
470
  const res = {
349
471
  type: "context",
350
472
  elements: elements.map((element) => transform(element))
@@ -372,11 +494,12 @@ var divider_default = transformDivider;
372
494
  // src/transformers/layout/file.ts
373
495
  var transformFile = (child) => {
374
496
  const { externalId, blockId } = child.props;
497
+ requireField("externalId", externalId);
375
498
  warnIfTooLong("block_id", blockId, 255);
376
499
  const res = {
377
500
  type: "file",
378
501
  source: "remote",
379
- external_id: externalId
502
+ external_id: externalId != null ? externalId : ""
380
503
  };
381
504
  if (blockId) {
382
505
  res.block_id = blockId;
@@ -393,7 +516,7 @@ var transformHeader = (child) => {
393
516
  warnIfTooLong("block_id", blockId, MAX_BLOCK_ID_LENGTH);
394
517
  const res = {
395
518
  type: "header",
396
- text: transform(/* @__PURE__ */ jsx(Text, { plainText: true, emoji, children: text }))
519
+ text: transform(/* @__PURE__ */ jsx(Text, { plainText: true, emoji, children: text != null ? text : "" }))
397
520
  };
398
521
  if (blockId) {
399
522
  res.block_id = blockId;
@@ -405,13 +528,15 @@ var header_default = transformHeader;
405
528
  // src/transformers/layout/image.tsx
406
529
  var transformImageLayout = (child) => {
407
530
  const { url, alt, title, blockId } = child.props;
531
+ requireField("url", url);
532
+ requireField("alt", alt);
408
533
  warnIfTooLong("Image image_url", url, MAX_IMAGE_URL);
409
534
  warnIfTooLong("Image alt_text", alt, MAX_IMAGE_ALT_TEXT);
410
535
  warnIfTooLong("block_id", blockId, MAX_BLOCK_ID_LENGTH);
411
536
  const res = {
412
537
  type: "image",
413
- image_url: url,
414
- alt_text: alt
538
+ image_url: url != null ? url : "",
539
+ alt_text: alt != null ? alt : ""
415
540
  };
416
541
  if (title) {
417
542
  warnIfTooLong("Image title", title, MAX_IMAGE_TITLE);
@@ -427,11 +552,16 @@ var image_default2 = transformImageLayout;
427
552
  // src/transformers/layout/input.tsx
428
553
  var transformInput = (child) => {
429
554
  const { label, element, hint, optional, blockId } = child.props;
430
- warnIfTooLong("block_id", blockId, 255);
555
+ const resolvedElement = element ? transform(element) : { type: "plain_text_input", action_id: "" };
556
+ requireField("label", label);
557
+ requireField("element", element);
558
+ warnIfTooLong("Input label", label, MAX_INPUT_LABEL);
559
+ warnIfTooLong("Input hint", hint, MAX_INPUT_HINT);
560
+ warnIfTooLong("block_id", blockId, MAX_BLOCK_ID_LENGTH);
431
561
  const res = {
432
562
  type: "input",
433
- label: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: label })),
434
- element: transform(element)
563
+ label: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: label != null ? label : "" })),
564
+ element: resolvedElement
435
565
  };
436
566
  if (hint) {
437
567
  res.hint = transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: hint }));
@@ -488,10 +618,12 @@ var toBlockElements = (children) => {
488
618
  // src/transformers/layout/rich-text.ts
489
619
  var transformRichText = (child) => {
490
620
  const { elements, children, blockId } = child.props;
621
+ const richTextElements = elements != null ? elements : toBlockElements(children);
491
622
  warnIfTooLong("block_id", blockId, 255);
623
+ requireField("elements", richTextElements);
492
624
  const res = {
493
625
  type: "rich_text",
494
- elements: elements != null ? elements : toBlockElements(children)
626
+ elements: richTextElements
495
627
  };
496
628
  if (blockId) {
497
629
  res.block_id = blockId;
@@ -514,13 +646,19 @@ var transformVideo = (child) => {
514
646
  providerIconUrl,
515
647
  blockId
516
648
  } = child.props;
649
+ requireField("title", title);
650
+ requireField("videoUrl", videoUrl);
651
+ requireField("thumbnailUrl", thumbnailUrl);
652
+ requireField("altText", altText);
653
+ warnIfTooLong("Video title", title, MAX_VIDEO_TITLE);
654
+ warnIfTooLong("Video author_name", authorName, MAX_VIDEO_AUTHOR_NAME);
517
655
  warnIfTooLong("block_id", blockId, MAX_BLOCK_ID_LENGTH);
518
656
  const res = {
519
657
  type: "video",
520
- title: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: title })),
521
- video_url: videoUrl,
522
- thumbnail_url: thumbnailUrl,
523
- alt_text: altText
658
+ title: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: title != null ? title : "" })),
659
+ video_url: videoUrl != null ? videoUrl : "",
660
+ thumbnail_url: thumbnailUrl != null ? thumbnailUrl : "",
661
+ alt_text: altText != null ? altText : ""
524
662
  };
525
663
  if (titleUrl) {
526
664
  res.title_url = titleUrl;
@@ -557,9 +695,10 @@ var transformTextInput = (child) => {
557
695
  focusOnLoad,
558
696
  dispatchActionConfig
559
697
  } = child.props;
560
- warnIfTooLong("TextInput action_id", actionId, 255);
698
+ requireField("actionId", actionId);
699
+ warnIfTooLong("TextInput action_id", actionId, MAX_ACTION_ID_LENGTH);
561
700
  if (placeholder) {
562
- warnIfTooLong("TextInput placeholder", placeholder, 150);
701
+ warnIfTooLong("TextInput placeholder", placeholder, MAX_PLACEHOLDER_LENGTH);
563
702
  }
564
703
  const res = {
565
704
  type: "plain_text_input",
@@ -648,7 +787,12 @@ var transformDatePicker = (child) => {
648
787
  }
649
788
  if (initialDate) {
650
789
  if (!isValidDateString(initialDate)) {
651
- report("Date must be valid and in format YYYY-MM-DD.", "invalid-date-format");
790
+ report({
791
+ message: "Date must be valid and in format YYYY-MM-DD.",
792
+ rule: "invalid-format",
793
+ subcode: "invalid-date-format",
794
+ field: "initialDate"
795
+ });
652
796
  }
653
797
  res.initial_date = initialDate;
654
798
  }
@@ -667,15 +811,14 @@ var transformCheckboxes = (child) => {
667
811
  const { actionId, children, initialOptions, confirm, focusOnLoad } = child.props;
668
812
  requireField("actionId", actionId);
669
813
  warnIfTooLong("Checkboxes action_id", actionId, MAX_ACTION_ID_LENGTH);
670
- let elements = children;
671
- if (!Array.isArray(elements)) {
672
- elements = [elements];
673
- }
814
+ const elements = Array.isArray(children) ? children : [children];
815
+ requireField("options", elements);
674
816
  const res = {
675
817
  type: "checkboxes",
676
818
  action_id: actionId,
677
819
  options: elements.map((element) => transform(element))
678
820
  };
821
+ warnIfTooMany("Checkboxes options", res.options, MAX_CHECKBOX_OPTIONS);
679
822
  if (initialOptions && initialOptions.length > 0) {
680
823
  res.initial_options = initialOptions.map((option) => transform(option));
681
824
  }
@@ -731,6 +874,31 @@ var assignStaticOptions = (elements, result) => {
731
874
  result.option_groups = elements.map((element) => transform(element));
732
875
  }
733
876
  };
877
+ var applyConversationSettings = (result, settings) => {
878
+ const { defaultToCurrentConversation, responseUrlEnabled, filter } = settings;
879
+ if (defaultToCurrentConversation !== void 0) {
880
+ result.default_to_current_conversation = defaultToCurrentConversation;
881
+ }
882
+ if (responseUrlEnabled !== void 0) {
883
+ result.response_url_enabled = responseUrlEnabled;
884
+ }
885
+ if (!filter) {
886
+ return;
887
+ }
888
+ const filterValue = {};
889
+ if (filter.include && filter.include.length > 0) {
890
+ filterValue.include = filter.include;
891
+ }
892
+ if (filter.excludeExternalSharedChannels !== void 0) {
893
+ filterValue.exclude_external_shared_channels = filter.excludeExternalSharedChannels;
894
+ }
895
+ if (filter.excludeBotUsers !== void 0) {
896
+ filterValue.exclude_bot_users = filter.excludeBotUsers;
897
+ }
898
+ if (Object.keys(filterValue).length > 0) {
899
+ result.filter = filterValue;
900
+ }
901
+ };
734
902
  var applyInitialSelections = (type, isMulti, result, initialValues) => {
735
903
  const { initialOptions, initialUsers, initialConversations, initialChannels } = initialValues;
736
904
  switch (type) {
@@ -805,12 +973,15 @@ var transformSelect = (child) => {
805
973
  warnIfTooLong("Select placeholder", placeholder, MAX_PLACEHOLDER_LENGTH);
806
974
  const result = {
807
975
  type: typeString,
808
- placeholder: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: placeholder })),
976
+ placeholder: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: placeholder != null ? placeholder : "" })),
809
977
  action_id: actionId
810
978
  };
811
979
  const elements = normalizeElements(children);
812
980
  if (type === selectTypes.STATIC) {
813
- assignStaticOptions(elements, result);
981
+ requireField("options", elements);
982
+ if (elements.length > 0) {
983
+ assignStaticOptions(elements, result);
984
+ }
814
985
  }
815
986
  if (confirm) {
816
987
  result.confirm = transform(confirm);
@@ -831,27 +1002,13 @@ var transformSelect = (child) => {
831
1002
  result.min_query_length = minQueryLength;
832
1003
  }
833
1004
  if (type === selectTypes.CONVERSATION) {
834
- if (defaultToCurrentConversation !== void 0) {
835
- result.default_to_current_conversation = defaultToCurrentConversation;
836
- }
837
- if (responseUrlEnabled !== void 0) {
838
- result.response_url_enabled = responseUrlEnabled;
839
- }
840
- if (filter) {
841
- const filterValue = {};
842
- if (filter.include && filter.include.length > 0) {
843
- filterValue.include = filter.include;
844
- }
845
- if (filter.excludeExternalSharedChannels !== void 0) {
846
- filterValue.exclude_external_shared_channels = filter.excludeExternalSharedChannels;
847
- }
848
- if (filter.excludeBotUsers !== void 0) {
849
- filterValue.exclude_bot_users = filter.excludeBotUsers;
850
- }
851
- if (Object.keys(filterValue).length > 0) {
852
- result.filter = filterValue;
853
- }
854
- }
1005
+ applyConversationSettings(result, { defaultToCurrentConversation, responseUrlEnabled, filter });
1006
+ }
1007
+ if (result.options) {
1008
+ warnIfTooMany("Select options", result.options, MAX_SELECT_OPTIONS);
1009
+ }
1010
+ if (result.option_groups) {
1011
+ warnIfTooMany("Select option_groups", result.option_groups, MAX_SELECT_OPTION_GROUPS);
855
1012
  }
856
1013
  return result;
857
1014
  };
@@ -860,13 +1017,14 @@ var select_default = transformSelect;
860
1017
  // src/transformers/input/option.tsx
861
1018
  var transformOption = (child) => {
862
1019
  const { children: text, value, url, description } = child.props;
1020
+ requireField("text", text);
863
1021
  requireField("value", value);
864
1022
  warnIfTooLong("Option text", text, MAX_OPTION_TEXT);
865
1023
  warnIfTooLong("Option value", value, MAX_OPTION_VALUE);
866
1024
  warnIfTooLong("Option description", description, MAX_OPTION_DESCRIPTION);
867
1025
  warnIfTooLong("Option url", url, MAX_OPTION_URL);
868
1026
  const res = {
869
- text: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: text })),
1027
+ text: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: text != null ? text : "" })),
870
1028
  value
871
1029
  };
872
1030
  if (description) {
@@ -882,14 +1040,13 @@ var option_default = transformOption;
882
1040
  // src/transformers/input/option-group.tsx
883
1041
  var transformOptionGroup = (child) => {
884
1042
  const { label, children } = child.props;
1043
+ const options = Array.isArray(children) ? children : [children];
1044
+ requireField("label", label);
1045
+ requireField("options", options);
885
1046
  warnIfTooLong("OptionGroup label", label, MAX_OPTION_GROUP_LABEL);
886
- let options = children;
887
- if (!Array.isArray(options)) {
888
- options = [options];
889
- }
890
1047
  warnIfTooMany("OptionGroup options", options, MAX_OPTION_GROUP_OPTIONS);
891
1048
  return {
892
- label: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: label })),
1049
+ label: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: label != null ? label : "" })),
893
1050
  options: options.map((option) => transform(option))
894
1051
  };
895
1052
  };
@@ -898,16 +1055,16 @@ var option_group_default = transformOptionGroup;
898
1055
  // src/transformers/input/overflow.ts
899
1056
  var transformOverflow = (child) => {
900
1057
  const { actionId, children, confirm } = child.props;
901
- warnIfTooLong("Overflow action_id", actionId, 255);
902
- let elements = children;
903
- if (!Array.isArray(elements)) {
904
- elements = [elements];
905
- }
1058
+ const elements = Array.isArray(children) ? children : [children];
1059
+ requireField("actionId", actionId);
1060
+ requireField("options", elements);
1061
+ warnIfTooLong("Overflow action_id", actionId, MAX_ACTION_ID_LENGTH);
906
1062
  const res = {
907
1063
  type: "overflow",
908
1064
  action_id: actionId,
909
1065
  options: elements.map((element) => transform(element))
910
1066
  };
1067
+ warnIfTooMany("Overflow options", res.options, MAX_OVERFLOW_OPTIONS);
911
1068
  if (confirm) {
912
1069
  res.confirm = transform(confirm);
913
1070
  }
@@ -920,15 +1077,14 @@ var transformRadioGroup = (child) => {
920
1077
  const { actionId, children, initialOption, confirm, focusOnLoad } = child.props;
921
1078
  requireField("actionId", actionId);
922
1079
  warnIfTooLong("RadioGroup action_id", actionId, MAX_ACTION_ID_LENGTH);
923
- let elements = children;
924
- if (!Array.isArray(elements)) {
925
- elements = [elements];
926
- }
1080
+ const elements = Array.isArray(children) ? children : [children];
1081
+ requireField("options", elements);
927
1082
  const res = {
928
1083
  type: "radio_buttons",
929
1084
  action_id: actionId,
930
1085
  options: elements.map((element) => transform(element))
931
1086
  };
1087
+ warnIfTooMany("RadioGroup options", res.options, MAX_RADIO_OPTIONS);
932
1088
  if (initialOption) {
933
1089
  res.initial_option = transform(initialOption);
934
1090
  }
@@ -968,7 +1124,12 @@ var transformTimePicker = (child) => {
968
1124
  }
969
1125
  if (initialTime) {
970
1126
  if (!isValidTimeString(initialTime)) {
971
- report("Time must be valid and in format HH:MM.", "invalid-time-format");
1127
+ report({
1128
+ message: "Time must be valid and in format HH:MM.",
1129
+ rule: "invalid-format",
1130
+ subcode: "invalid-time-format",
1131
+ field: "initialTime"
1132
+ });
972
1133
  }
973
1134
  res.initial_time = initialTime;
974
1135
  }
@@ -1194,7 +1355,12 @@ var parseChildren = (children) => {
1194
1355
  popPath();
1195
1356
  }
1196
1357
  } else if (type !== "null") {
1197
- report(`No transformer for component type '${type}'.`, "unknown-type");
1358
+ report({
1359
+ message: `No transformer for component type '${type}'.`,
1360
+ rule: "unsupported-child",
1361
+ subcode: "unknown-type",
1362
+ component: type
1363
+ });
1198
1364
  }
1199
1365
  }
1200
1366
  if (transformedBlocks.length === 0) {
@@ -1207,7 +1373,7 @@ var parser_default = parseChildren;
1207
1373
  // src/renderer/index.ts
1208
1374
  var renderToBlocks = (element, options) => {
1209
1375
  var _a, _b;
1210
- initContext((_a = options == null ? void 0 : options.validate) != null ? _a : "warn");
1376
+ initContext((_a = options == null ? void 0 : options.validate) != null ? _a : "warn", options == null ? void 0 : options.onValidation);
1211
1377
  const child = typeof element === "object" && element !== null && !Array.isArray(element) && element.type === "fragment" ? element.props.children : element;
1212
1378
  const result = parser_default(child);
1213
1379
  return (_b = result.blocks) != null ? _b : [];
@@ -1240,7 +1406,7 @@ var applyMessageMetadata = (json, properties) => {
1240
1406
  };
1241
1407
  function render(element, options) {
1242
1408
  var _a, _b, _c, _d, _e;
1243
- initContext((_a = options == null ? void 0 : options.validate) != null ? _a : "warn");
1409
+ initContext((_a = options == null ? void 0 : options.validate) != null ? _a : "warn", options == null ? void 0 : options.onValidation);
1244
1410
  const properties = element.props;
1245
1411
  const typeName = get_type_default(element);
1246
1412
  if (typeName !== "Message") {