slackblock 1.1.0 → 2.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
@@ -1,9 +1,9 @@
1
- var __create = Object.create;
1
+ "use strict";
2
2
  var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf;
6
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
7
7
  var __export = (target, all) => {
8
8
  for (var name in all)
9
9
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -16,33 +16,69 @@ var __copyProps = (to, from, except, desc) => {
16
16
  }
17
17
  return to;
18
18
  };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
- // If the importer is in node compatibility mode or this is not an ESM
21
- // file that has been converted to a CommonJS file using a Babel-
22
- // compatible transform (i.e. "__esModule" has not been set), then set
23
- // "default" to the CommonJS "module.exports" for node compatibility.
24
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
- mod
26
- ));
27
19
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
28
21
 
29
22
  // src/index.ts
30
23
  var src_exports = {};
31
24
  __export(src_exports, {
32
- default: () => renderer_default
25
+ SlackblockValidationError: () => SlackblockValidationError,
26
+ blockKitBuilderUrl: () => blockKitBuilderUrl,
27
+ default: () => renderer_default,
28
+ escapeMrkdwn: () => escapeMrkdwn,
29
+ renderToBlocks: () => renderToBlocks,
30
+ renderToMessage: () => render
33
31
  });
34
32
  module.exports = __toCommonJS(src_exports);
35
33
 
36
- // src/utils/validation.ts
37
- var warn = (message) => {
38
- console.warn(message);
34
+ // src/errors.ts
35
+ var SlackblockValidationError = class extends Error {
36
+ constructor(message, path, rule) {
37
+ super(`${path}: ${message}`);
38
+ __publicField(this, "path");
39
+ __publicField(this, "rule");
40
+ this.name = "SlackblockValidationError";
41
+ this.path = path;
42
+ this.rule = rule;
43
+ }
44
+ };
45
+
46
+ // src/utils/validation-context.ts
47
+ var context = {
48
+ mode: "warn",
49
+ path: []
50
+ };
51
+ var initContext = (mode) => {
52
+ context.mode = mode;
53
+ context.path = [];
39
54
  };
55
+ var pushPath = (segment) => {
56
+ context.path.push(segment);
57
+ };
58
+ var popPath = () => {
59
+ context.path.pop();
60
+ };
61
+ var getPath = () => context.path.join(" > ");
62
+ var report = (message, rule) => {
63
+ if (context.mode === "off") {
64
+ return;
65
+ }
66
+ const path = getPath();
67
+ if (context.mode === "warn") {
68
+ console.warn(`[slackblock] ${path}: ${message}`);
69
+ return;
70
+ }
71
+ throw new SlackblockValidationError(message, path, rule);
72
+ };
73
+
74
+ // src/utils/validation.ts
75
+ var toKebab = (name) => name.replaceAll(/([A-Z])/g, "-$1").toLowerCase();
40
76
  var warnIfTooLong = (name, value, max) => {
41
77
  if (!value) {
42
78
  return;
43
79
  }
44
80
  if (value.length > max) {
45
- warn(`${name} exceeds ${max} characters.`);
81
+ report(`${name} exceeds ${max} characters.`, "value-too-long");
46
82
  }
47
83
  };
48
84
  var warnIfTooMany = (name, values, max) => {
@@ -50,20 +86,18 @@ var warnIfTooMany = (name, values, max) => {
50
86
  return;
51
87
  }
52
88
  if (values.length > max) {
53
- warn(`${name} exceeds ${max} items.`);
89
+ report(`${name} exceeds ${max} items.`, "too-many-items");
90
+ }
91
+ };
92
+ var requireField = (fieldName, value) => {
93
+ if (value === void 0 || value === null || value === "") {
94
+ report(`${fieldName} is required.`, `${toKebab(fieldName)}-required`);
54
95
  }
55
96
  };
56
97
 
57
98
  // src/transformers/block/text.ts
58
99
  var transformText = (element) => {
59
- const {
60
- props: {
61
- plainText,
62
- children,
63
- emoji,
64
- verbatim
65
- }
66
- } = element;
100
+ const { plainText, children, emoji, verbatim } = element.props;
67
101
  const res = {
68
102
  type: plainText ? "plain_text" : "mrkdwn",
69
103
  text: children
@@ -81,11 +115,9 @@ var transformText = (element) => {
81
115
  };
82
116
  var text_default = transformText;
83
117
 
84
- // src/transformers/block/confirmation.tsx
85
- var import_react2 = __toESM(require("react"));
86
-
87
118
  // src/utils/get-type.ts
88
119
  var getType = (element) => {
120
+ var _a, _b;
89
121
  if (typeof element === "string") {
90
122
  return "string";
91
123
  }
@@ -100,7 +132,7 @@ var getType = (element) => {
100
132
  return type;
101
133
  }
102
134
  const { slackType, displayName, name } = type;
103
- return slackType || displayName || name || type;
135
+ return (_b = (_a = slackType != null ? slackType : displayName) != null ? _a : name) != null ? _b : type;
104
136
  };
105
137
  var get_type_default = getType;
106
138
 
@@ -112,42 +144,87 @@ var registry_default = Transformers;
112
144
  var transform = (element) => {
113
145
  const type = get_type_default(element);
114
146
  if (!registry_default[type]) {
115
- throw new Error(`No transformer exists for type '${type}'`);
147
+ report(`No transformer for component type '${type}'.`, "unknown-type");
148
+ return {};
149
+ }
150
+ pushPath(type);
151
+ try {
152
+ return registry_default[type](element);
153
+ } finally {
154
+ popPath();
116
155
  }
117
- return registry_default[type](element);
118
156
  };
119
157
 
120
158
  // src/components/block/text.tsx
121
- var import_react = __toESM(require("react"));
122
- var Text = class extends import_react.default.Component {
159
+ var Text = class {
123
160
  };
124
- Text.slackType = "Text";
161
+ __publicField(Text, "slackType", "Text");
162
+
163
+ // src/constants/limits.ts
164
+ var MAX_BLOCKS = 50;
165
+ var MAX_HEADER_TEXT = 150;
166
+ var MAX_SECTION_FIELDS = 10;
167
+ var MAX_SECTION_FIELD_TEXT = 2e3;
168
+ var MAX_ACTIONS_ELEMENTS = 25;
169
+ var MAX_CONTEXT_ELEMENTS = 10;
170
+ var MAX_CONFIRM_TITLE = 100;
171
+ var MAX_CONFIRM_TEXT = 300;
172
+ var MAX_BUTTON_TEXT = 75;
173
+ var MAX_BUTTON_VALUE = 2e3;
174
+ var MAX_OPTION_TEXT = 75;
175
+ var MAX_OPTION_VALUE = 150;
176
+ var MAX_OPTION_DESCRIPTION = 75;
177
+ var MAX_OPTION_URL = 3e3;
178
+ var MAX_OPTION_GROUP_LABEL = 75;
179
+ var MAX_OPTION_GROUP_OPTIONS = 100;
180
+ var MAX_IMAGE_URL = 3e3;
181
+ var MAX_IMAGE_ALT_TEXT = 2e3;
182
+ var MAX_IMAGE_TITLE = 2e3;
183
+ var MAX_VIDEO_DESCRIPTION = 200;
184
+ var MAX_BLOCK_ID_LENGTH = 255;
185
+ var MAX_MESSAGE_TEXT = 4e4;
186
+ var RECOMMENDED_MESSAGE_TEXT = 4e3;
187
+ var MAX_ACTION_ID_LENGTH = 255;
188
+ var MAX_PLACEHOLDER_LENGTH = 150;
189
+
190
+ // src/jsx-runtime.ts
191
+ function jsx(type, props) {
192
+ const { children } = props;
193
+ return {
194
+ type,
195
+ props,
196
+ children: Array.isArray(children) ? children : children === void 0 ? [] : [children]
197
+ };
198
+ }
125
199
 
126
200
  // src/transformers/block/confirmation.tsx
127
201
  var transformConfirmation = (child) => {
128
202
  const { title, confirm, deny, children } = child.props;
203
+ warnIfTooLong("Confirm title", title, MAX_CONFIRM_TITLE);
204
+ const text = transform(children);
205
+ warnIfTooLong("Confirm text", text.text, MAX_CONFIRM_TEXT);
129
206
  const res = {
130
- title: transform(/* @__PURE__ */ import_react2.default.createElement(Text, { plainText: true }, title)),
131
- text: transform(children),
132
- confirm: transform(/* @__PURE__ */ import_react2.default.createElement(Text, { plainText: true }, confirm)),
133
- deny: transform(/* @__PURE__ */ import_react2.default.createElement(Text, { plainText: true }, deny))
207
+ title: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: title })),
208
+ text,
209
+ confirm: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: confirm })),
210
+ deny: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: deny }))
134
211
  };
135
212
  return res;
136
213
  };
137
214
  var confirmation_default = transformConfirmation;
138
215
 
139
216
  // src/transformers/block/button.tsx
140
- var import_react3 = __toESM(require("react"));
141
217
  var transformButton = (child) => {
142
218
  const { actionId, children, url, value, style, confirm, accessibilityLabel } = child.props;
143
- warnIfTooLong("Button action_id", actionId, 255);
219
+ requireField("actionId", actionId);
220
+ warnIfTooLong("Button action_id", actionId, MAX_ACTION_ID_LENGTH);
144
221
  if (typeof children === "string") {
145
- warnIfTooLong("Button text", children, 75);
222
+ warnIfTooLong("Button text", children, MAX_BUTTON_TEXT);
146
223
  }
147
- warnIfTooLong("Button value", value, 2e3);
224
+ warnIfTooLong("Button value", value, MAX_BUTTON_VALUE);
148
225
  const res = {
149
226
  type: "button",
150
- text: transform(/* @__PURE__ */ import_react3.default.createElement(Text, { plainText: true }, children)),
227
+ text: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children })),
151
228
  action_id: actionId
152
229
  };
153
230
  if (url) {
@@ -209,15 +286,8 @@ var container_default = transformContainer;
209
286
 
210
287
  // src/transformers/layout/section.ts
211
288
  var transformSection = (element) => {
212
- const {
213
- props: {
214
- text,
215
- blockId,
216
- children,
217
- accessory
218
- }
219
- } = element;
220
- warnIfTooLong("block_id", blockId, 255);
289
+ const { text, blockId, children, accessory } = element.props;
290
+ warnIfTooLong("block_id", blockId, MAX_BLOCK_ID_LENGTH);
221
291
  const res = {
222
292
  type: "section",
223
293
  text: transform(text)
@@ -237,10 +307,11 @@ var transformSection = (element) => {
237
307
  for (const field of fields) {
238
308
  if (field) {
239
309
  const t = transform(field);
310
+ warnIfTooLong("Section field text", t.text, MAX_SECTION_FIELD_TEXT);
240
311
  res.fields.push(t);
241
312
  }
242
313
  }
243
- warnIfTooMany("Section fields", res.fields, 10);
314
+ warnIfTooMany("Section fields", res.fields, MAX_SECTION_FIELDS);
244
315
  }
245
316
  return res;
246
317
  };
@@ -249,7 +320,7 @@ var section_default = transformSection;
249
320
  // src/transformers/layout/actions.ts
250
321
  var transformActions = (child) => {
251
322
  const { children, blockId } = child.props;
252
- warnIfTooLong("block_id", blockId, 255);
323
+ warnIfTooLong("block_id", blockId, MAX_BLOCK_ID_LENGTH);
253
324
  let elements = children;
254
325
  if (!Array.isArray(elements)) {
255
326
  elements = [elements];
@@ -261,7 +332,7 @@ var transformActions = (child) => {
261
332
  if (blockId) {
262
333
  res.block_id = blockId;
263
334
  }
264
- warnIfTooMany("Actions elements", res.elements, 25);
335
+ warnIfTooMany("Actions elements", res.elements, MAX_ACTIONS_ELEMENTS);
265
336
  return res;
266
337
  };
267
338
  var actions_default = transformActions;
@@ -269,7 +340,7 @@ var actions_default = transformActions;
269
340
  // src/transformers/layout/context.ts
270
341
  var transformContext = (child) => {
271
342
  const { children, blockId } = child.props;
272
- warnIfTooLong("block_id", blockId, 255);
343
+ warnIfTooLong("block_id", blockId, MAX_BLOCK_ID_LENGTH);
273
344
  let elements = children;
274
345
  if (!Array.isArray(elements)) {
275
346
  elements = [elements];
@@ -281,7 +352,7 @@ var transformContext = (child) => {
281
352
  if (blockId) {
282
353
  res.block_id = blockId;
283
354
  }
284
- warnIfTooMany("Context elements", res.elements, 10);
355
+ warnIfTooMany("Context elements", res.elements, MAX_CONTEXT_ELEMENTS);
285
356
  return res;
286
357
  };
287
358
  var context_default = transformContext;
@@ -315,13 +386,14 @@ var transformFile = (child) => {
315
386
  var file_default = transformFile;
316
387
 
317
388
  // src/transformers/layout/header.tsx
318
- var import_react4 = __toESM(require("react"));
319
389
  var transformHeader = (child) => {
320
390
  const { text, blockId, emoji } = child.props;
321
- warnIfTooLong("block_id", blockId, 255);
391
+ requireField("text", text);
392
+ warnIfTooLong("Header text", text, MAX_HEADER_TEXT);
393
+ warnIfTooLong("block_id", blockId, MAX_BLOCK_ID_LENGTH);
322
394
  const res = {
323
395
  type: "header",
324
- text: transform(/* @__PURE__ */ import_react4.default.createElement(Text, { plainText: true, emoji }, text))
396
+ text: transform(/* @__PURE__ */ jsx(Text, { plainText: true, emoji, children: text }))
325
397
  };
326
398
  if (blockId) {
327
399
  res.block_id = blockId;
@@ -331,17 +403,19 @@ var transformHeader = (child) => {
331
403
  var header_default = transformHeader;
332
404
 
333
405
  // src/transformers/layout/image.tsx
334
- var import_react5 = __toESM(require("react"));
335
406
  var transformImageLayout = (child) => {
336
407
  const { url, alt, title, blockId } = child.props;
337
- warnIfTooLong("block_id", blockId, 255);
408
+ warnIfTooLong("Image image_url", url, MAX_IMAGE_URL);
409
+ warnIfTooLong("Image alt_text", alt, MAX_IMAGE_ALT_TEXT);
410
+ warnIfTooLong("block_id", blockId, MAX_BLOCK_ID_LENGTH);
338
411
  const res = {
339
412
  type: "image",
340
413
  image_url: url,
341
414
  alt_text: alt
342
415
  };
343
416
  if (title) {
344
- res.title = transform(/* @__PURE__ */ import_react5.default.createElement(Text, { plainText: true }, title));
417
+ warnIfTooLong("Image title", title, MAX_IMAGE_TITLE);
418
+ res.title = transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: title }));
345
419
  }
346
420
  if (blockId) {
347
421
  res.block_id = blockId;
@@ -351,17 +425,16 @@ var transformImageLayout = (child) => {
351
425
  var image_default2 = transformImageLayout;
352
426
 
353
427
  // src/transformers/layout/input.tsx
354
- var import_react6 = __toESM(require("react"));
355
428
  var transformInput = (child) => {
356
429
  const { label, element, hint, optional, blockId } = child.props;
357
430
  warnIfTooLong("block_id", blockId, 255);
358
431
  const res = {
359
432
  type: "input",
360
- label: transform(/* @__PURE__ */ import_react6.default.createElement(Text, { plainText: true }, label)),
433
+ label: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: label })),
361
434
  element: transform(element)
362
435
  };
363
436
  if (hint) {
364
- res.hint = transform(/* @__PURE__ */ import_react6.default.createElement(Text, { plainText: true }, hint));
437
+ res.hint = transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: hint }));
365
438
  }
366
439
  if (optional) {
367
440
  res.optional = true;
@@ -428,7 +501,6 @@ var transformRichText = (child) => {
428
501
  var rich_text_default = transformRichText;
429
502
 
430
503
  // src/transformers/layout/video.tsx
431
- var import_react7 = __toESM(require("react"));
432
504
  var transformVideo = (child) => {
433
505
  const {
434
506
  title,
@@ -442,10 +514,10 @@ var transformVideo = (child) => {
442
514
  providerIconUrl,
443
515
  blockId
444
516
  } = child.props;
445
- warnIfTooLong("block_id", blockId, 255);
517
+ warnIfTooLong("block_id", blockId, MAX_BLOCK_ID_LENGTH);
446
518
  const res = {
447
519
  type: "video",
448
- title: transform(/* @__PURE__ */ import_react7.default.createElement(Text, { plainText: true }, title)),
520
+ title: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: title })),
449
521
  video_url: videoUrl,
450
522
  thumbnail_url: thumbnailUrl,
451
523
  alt_text: altText
@@ -454,7 +526,8 @@ var transformVideo = (child) => {
454
526
  res.title_url = titleUrl;
455
527
  }
456
528
  if (description) {
457
- res.description = transform(/* @__PURE__ */ import_react7.default.createElement(Text, { plainText: true }, description));
529
+ warnIfTooLong("Video description", description, MAX_VIDEO_DESCRIPTION);
530
+ res.description = transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: description }));
458
531
  }
459
532
  if (authorName) {
460
533
  res.author_name = authorName;
@@ -473,7 +546,6 @@ var transformVideo = (child) => {
473
546
  var video_default = transformVideo;
474
547
 
475
548
  // src/transformers/input/text.tsx
476
- var import_react8 = __toESM(require("react"));
477
549
  var transformTextInput = (child) => {
478
550
  const {
479
551
  actionId,
@@ -494,7 +566,7 @@ var transformTextInput = (child) => {
494
566
  action_id: actionId
495
567
  };
496
568
  if (placeholder) {
497
- res.placeholder = transform(/* @__PURE__ */ import_react8.default.createElement(Text, { plainText: true }, placeholder));
569
+ res.placeholder = transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: placeholder }));
498
570
  }
499
571
  if (initial) {
500
572
  res.initial_value = initial;
@@ -523,7 +595,8 @@ var text_default2 = transformTextInput;
523
595
  // src/transformers/input/date-time-picker.ts
524
596
  var transformDateTimePicker = (child) => {
525
597
  const { actionId, initialDateTime, confirm, focusOnLoad } = child.props;
526
- warnIfTooLong("DateTimePicker action_id", actionId, 255);
598
+ requireField("actionId", actionId);
599
+ warnIfTooLong("DateTimePicker action_id", actionId, MAX_ACTION_ID_LENGTH);
527
600
  const res = {
528
601
  type: "datetimepicker",
529
602
  action_id: actionId
@@ -545,7 +618,6 @@ var transformDateTimePicker = (child) => {
545
618
  var date_time_picker_default = transformDateTimePicker;
546
619
 
547
620
  // src/transformers/input/date-picker.tsx
548
- var import_react9 = __toESM(require("react"));
549
621
  var isValidDateString = (value) => {
550
622
  const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
551
623
  if (!match) {
@@ -562,20 +634,21 @@ var isValidDateString = (value) => {
562
634
  };
563
635
  var transformDatePicker = (child) => {
564
636
  const { actionId, placeholder, initialDate, confirm, focusOnLoad } = child.props;
565
- warnIfTooLong("DatePicker action_id", actionId, 255);
637
+ requireField("actionId", actionId);
638
+ warnIfTooLong("DatePicker action_id", actionId, MAX_ACTION_ID_LENGTH);
566
639
  if (placeholder) {
567
- warnIfTooLong("DatePicker placeholder", placeholder, 150);
640
+ warnIfTooLong("DatePicker placeholder", placeholder, MAX_PLACEHOLDER_LENGTH);
568
641
  }
569
642
  const res = {
570
643
  type: "datepicker",
571
644
  action_id: actionId
572
645
  };
573
646
  if (placeholder) {
574
- res.placeholder = transform(/* @__PURE__ */ import_react9.default.createElement(Text, { plainText: true }, placeholder));
647
+ res.placeholder = transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: placeholder }));
575
648
  }
576
649
  if (initialDate) {
577
650
  if (!isValidDateString(initialDate)) {
578
- throw new Error("Date must be valid and in format YYYY-MM-DD.");
651
+ report("Date must be valid and in format YYYY-MM-DD.", "invalid-date-format");
579
652
  }
580
653
  res.initial_date = initialDate;
581
654
  }
@@ -592,7 +665,8 @@ var date_picker_default = transformDatePicker;
592
665
  // src/transformers/input/checkboxes.ts
593
666
  var transformCheckboxes = (child) => {
594
667
  const { actionId, children, initialOptions, confirm, focusOnLoad } = child.props;
595
- warnIfTooLong("Checkboxes action_id", actionId, 255);
668
+ requireField("actionId", actionId);
669
+ warnIfTooLong("Checkboxes action_id", actionId, MAX_ACTION_ID_LENGTH);
596
670
  let elements = children;
597
671
  if (!Array.isArray(elements)) {
598
672
  elements = [elements];
@@ -615,11 +689,7 @@ var transformCheckboxes = (child) => {
615
689
  };
616
690
  var checkboxes_default = transformCheckboxes;
617
691
 
618
- // src/transformers/input/select.tsx
619
- var import_react11 = __toESM(require("react"));
620
-
621
692
  // src/components/input/select.tsx
622
- var import_react10 = __toESM(require("react"));
623
693
  var selectTypes = {
624
694
  STATIC: "static",
625
695
  EXTERNAL: "external",
@@ -627,9 +697,6 @@ var selectTypes = {
627
697
  CONVERSATION: "conversation",
628
698
  CHANNEL: "channel"
629
699
  };
630
- var Select = class extends import_react10.default.Component {
631
- };
632
- Select.slackType = "Select";
633
700
 
634
701
  // src/transformers/input/select.tsx
635
702
  var OPTION = "Option";
@@ -659,11 +726,9 @@ var assignStaticOptions = (elements, result) => {
659
726
  throw new TypeError("Only allowed types are Option OR OptionGroup");
660
727
  }
661
728
  if (elementType === OPTION) {
662
- const options = elements;
663
- result.options = options.map((element) => transform(element));
729
+ result.options = elements.map((element) => transform(element));
664
730
  } else if (elementType === OPTION_GROUP) {
665
- const optionGroups = elements;
666
- result.option_groups = optionGroups.map((element) => transform(element));
731
+ result.option_groups = elements.map((element) => transform(element));
667
732
  }
668
733
  };
669
734
  var applyInitialSelections = (type, isMulti, result, initialValues) => {
@@ -734,11 +799,13 @@ var transformSelect = (child) => {
734
799
  } = child.props;
735
800
  const type = typeProperty != null ? typeProperty : selectTypes.STATIC;
736
801
  const typeString = `${multi ? MULTI_PREFIX : ""}${types[type]}`;
737
- warnIfTooLong("Select action_id", actionId, 255);
738
- warnIfTooLong("Select placeholder", placeholder, 150);
802
+ requireField("actionId", actionId);
803
+ requireField("placeholder", placeholder);
804
+ warnIfTooLong("Select action_id", actionId, MAX_ACTION_ID_LENGTH);
805
+ warnIfTooLong("Select placeholder", placeholder, MAX_PLACEHOLDER_LENGTH);
739
806
  const result = {
740
807
  type: typeString,
741
- placeholder: transform(/* @__PURE__ */ import_react11.default.createElement(Text, { plainText: true }, placeholder)),
808
+ placeholder: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: placeholder })),
742
809
  action_id: actionId
743
810
  };
744
811
  const elements = normalizeElements(children);
@@ -791,18 +858,19 @@ var transformSelect = (child) => {
791
858
  var select_default = transformSelect;
792
859
 
793
860
  // src/transformers/input/option.tsx
794
- var import_react12 = __toESM(require("react"));
795
861
  var transformOption = (child) => {
796
862
  const { children: text, value, url, description } = child.props;
797
- warnIfTooLong("Option text", text, 75);
798
- warnIfTooLong("Option value", value, 75);
799
- warnIfTooLong("Option description", description, 75);
863
+ requireField("value", value);
864
+ warnIfTooLong("Option text", text, MAX_OPTION_TEXT);
865
+ warnIfTooLong("Option value", value, MAX_OPTION_VALUE);
866
+ warnIfTooLong("Option description", description, MAX_OPTION_DESCRIPTION);
867
+ warnIfTooLong("Option url", url, MAX_OPTION_URL);
800
868
  const res = {
801
- text: transform(/* @__PURE__ */ import_react12.default.createElement(Text, { plainText: true }, text)),
869
+ text: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: text })),
802
870
  value
803
871
  };
804
872
  if (description) {
805
- res.description = transform(/* @__PURE__ */ import_react12.default.createElement(Text, { plainText: true }, description));
873
+ res.description = transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: description }));
806
874
  }
807
875
  if (url) {
808
876
  res.url = url;
@@ -812,16 +880,16 @@ var transformOption = (child) => {
812
880
  var option_default = transformOption;
813
881
 
814
882
  // src/transformers/input/option-group.tsx
815
- var import_react13 = __toESM(require("react"));
816
883
  var transformOptionGroup = (child) => {
817
884
  const { label, children } = child.props;
818
- warnIfTooLong("OptionGroup label", label, 75);
885
+ warnIfTooLong("OptionGroup label", label, MAX_OPTION_GROUP_LABEL);
819
886
  let options = children;
820
887
  if (!Array.isArray(options)) {
821
888
  options = [options];
822
889
  }
890
+ warnIfTooMany("OptionGroup options", options, MAX_OPTION_GROUP_OPTIONS);
823
891
  return {
824
- label: transform(/* @__PURE__ */ import_react13.default.createElement(Text, { plainText: true }, label)),
892
+ label: transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: label })),
825
893
  options: options.map((option) => transform(option))
826
894
  };
827
895
  };
@@ -850,7 +918,8 @@ var overflow_default = transformOverflow;
850
918
  // src/transformers/input/radio-group.ts
851
919
  var transformRadioGroup = (child) => {
852
920
  const { actionId, children, initialOption, confirm, focusOnLoad } = child.props;
853
- warnIfTooLong("RadioGroup action_id", actionId, 255);
921
+ requireField("actionId", actionId);
922
+ warnIfTooLong("RadioGroup action_id", actionId, MAX_ACTION_ID_LENGTH);
854
923
  let elements = children;
855
924
  if (!Array.isArray(elements)) {
856
925
  elements = [elements];
@@ -874,7 +943,6 @@ var transformRadioGroup = (child) => {
874
943
  var radio_group_default = transformRadioGroup;
875
944
 
876
945
  // src/transformers/input/time-picker.tsx
877
- var import_react14 = __toESM(require("react"));
878
946
  var isValidTimeString = (value) => {
879
947
  const match = /^(\d{2}):(\d{2})$/.exec(value);
880
948
  if (!match) {
@@ -886,20 +954,21 @@ var isValidTimeString = (value) => {
886
954
  };
887
955
  var transformTimePicker = (child) => {
888
956
  const { actionId, placeholder, initialTime, confirm, focusOnLoad } = child.props;
889
- warnIfTooLong("TimePicker action_id", actionId, 255);
957
+ requireField("actionId", actionId);
958
+ warnIfTooLong("TimePicker action_id", actionId, MAX_ACTION_ID_LENGTH);
890
959
  if (placeholder) {
891
- warnIfTooLong("TimePicker placeholder", placeholder, 150);
960
+ warnIfTooLong("TimePicker placeholder", placeholder, MAX_PLACEHOLDER_LENGTH);
892
961
  }
893
962
  const res = {
894
963
  type: "timepicker",
895
964
  action_id: actionId
896
965
  };
897
966
  if (placeholder) {
898
- res.placeholder = transform(/* @__PURE__ */ import_react14.default.createElement(Text, { plainText: true }, placeholder));
967
+ res.placeholder = transform(/* @__PURE__ */ jsx(Text, { plainText: true, children: placeholder }));
899
968
  }
900
969
  if (initialTime) {
901
970
  if (!isValidTimeString(initialTime)) {
902
- throw new Error("Time must be valid and in format HH:MM.");
971
+ report("Time must be valid and in format HH:MM.", "invalid-time-format");
903
972
  }
904
973
  res.initial_time = initialTime;
905
974
  }
@@ -1024,17 +1093,13 @@ var emoji_default = transformRichTextEmoji;
1024
1093
 
1025
1094
  // src/transformers/rich-text/date.ts
1026
1095
  var transformRichTextDate = (child) => {
1027
- const { timestamp, format, fallback, link } = child.props;
1028
- const res = {
1096
+ const { timestamp, format, fallback } = child.props;
1097
+ return {
1029
1098
  type: "date",
1030
1099
  timestamp,
1031
1100
  format,
1032
1101
  fallback
1033
1102
  };
1034
- if (link) {
1035
- res.link = link;
1036
- }
1037
- return res;
1038
1103
  };
1039
1104
  var date_default = transformRichTextDate;
1040
1105
 
@@ -1122,9 +1187,14 @@ var parseChildren = (children) => {
1122
1187
  const type = get_type_default(child);
1123
1188
  const transformer = registry_default[type];
1124
1189
  if (transformer) {
1125
- appendTransformed(transformer(child), transformedBlocks);
1190
+ pushPath(type);
1191
+ try {
1192
+ appendTransformed(transformer(child), transformedBlocks);
1193
+ } finally {
1194
+ popPath();
1195
+ }
1126
1196
  } else if (type !== "null") {
1127
- console.warn(`No transformer for child type '${type}' exists and will be ignored.`);
1197
+ report(`No transformer for component type '${type}'.`, "unknown-type");
1128
1198
  }
1129
1199
  }
1130
1200
  if (transformedBlocks.length === 0) {
@@ -1135,23 +1205,14 @@ var parseChildren = (children) => {
1135
1205
  var parser_default = parseChildren;
1136
1206
 
1137
1207
  // src/renderer/index.ts
1138
- var render = (element) => {
1139
- const { props: properties = {} } = element || {};
1140
- const typeName = get_type_default(element);
1141
- if (typeName !== "Message") {
1142
- throw new TypeError("Provided top-level element must be a Message type.");
1143
- }
1144
- if (!properties.children) {
1145
- throw new Error("Cannot render a Message with no children.");
1146
- }
1147
- const json = { ...parser_default(properties.children) };
1148
- if (properties.replyTo) {
1149
- json.thread_ts = properties.replyTo;
1150
- }
1151
- if (properties.markdown !== void 0) {
1152
- json.mrkdwn = properties.markdown;
1153
- }
1154
- json.text = properties.text || "";
1208
+ var renderToBlocks = (element, options) => {
1209
+ var _a, _b;
1210
+ initContext((_a = options == null ? void 0 : options.validate) != null ? _a : "warn");
1211
+ const child = typeof element === "object" && element !== null && !Array.isArray(element) && element.type === "fragment" ? element.props.children : element;
1212
+ const result = parser_default(child);
1213
+ return (_b = result.blocks) != null ? _b : [];
1214
+ };
1215
+ var applyMessageMetadata = (json, properties) => {
1155
1216
  if (properties.iconEmoji) {
1156
1217
  json.icon_emoji = properties.iconEmoji;
1157
1218
  }
@@ -1176,20 +1237,66 @@ var render = (element) => {
1176
1237
  if (properties.unfurlMedia !== void 0) {
1177
1238
  json.unfurl_media = properties.unfurlMedia;
1178
1239
  }
1179
- if (properties.color && json.blocks) {
1180
- json.attachments = [
1181
- {
1182
- fallback: json.text,
1183
- color: properties.color,
1184
- blocks: json.blocks
1185
- }
1186
- ];
1187
- delete json.blocks;
1240
+ };
1241
+ var render = (element, options) => {
1242
+ var _a, _b;
1243
+ initContext((_a = options == null ? void 0 : options.validate) != null ? _a : "warn");
1244
+ const properties = element.props;
1245
+ const typeName = get_type_default(element);
1246
+ if (typeName !== "Message") {
1247
+ throw new TypeError("Provided top-level element must be a Message type.");
1188
1248
  }
1189
- if (json.blocks) {
1190
- warnIfTooMany("Message blocks", json.blocks, 50);
1249
+ if (!properties.children) {
1250
+ throw new Error("Cannot render a Message with no children.");
1251
+ }
1252
+ pushPath("Message");
1253
+ let json;
1254
+ try {
1255
+ json = { ...parser_default(properties.children) };
1256
+ if (properties.replyTo) {
1257
+ json.thread_ts = properties.replyTo;
1258
+ }
1259
+ if (properties.markdown !== void 0) {
1260
+ json.mrkdwn = properties.markdown;
1261
+ }
1262
+ json.text = (_b = properties.text) != null ? _b : "";
1263
+ if (properties.text && properties.text.length > MAX_MESSAGE_TEXT) {
1264
+ warnIfTooLong(`Message text (Slack will truncate beyond ${MAX_MESSAGE_TEXT} chars)`, properties.text, MAX_MESSAGE_TEXT);
1265
+ } else if (properties.text) {
1266
+ warnIfTooLong("Message text (recommended max for best results)", properties.text, RECOMMENDED_MESSAGE_TEXT);
1267
+ }
1268
+ applyMessageMetadata(json, properties);
1269
+ if (properties.color && json.blocks) {
1270
+ json.attachments = [
1271
+ {
1272
+ fallback: json.text,
1273
+ color: properties.color,
1274
+ blocks: json.blocks
1275
+ }
1276
+ ];
1277
+ delete json.blocks;
1278
+ }
1279
+ if (json.blocks) {
1280
+ warnIfTooMany("Message blocks", json.blocks, MAX_BLOCKS);
1281
+ }
1282
+ } finally {
1283
+ popPath();
1191
1284
  }
1192
1285
  return json;
1193
1286
  };
1194
1287
  var renderer_default = render;
1288
+
1289
+ // src/utils/escape-mrkdwn.ts
1290
+ var escapeMrkdwn = (text) => text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll(/[*_~`]/g, "\u200B$&");
1291
+
1292
+ // src/utils/block-kit-builder.ts
1293
+ var blockKitBuilderUrl = (blocks) => `https://app.slack.com/block-kit-builder#${JSON.stringify({ blocks })}`;
1294
+ // Annotate the CommonJS export names for ESM import in node:
1295
+ 0 && (module.exports = {
1296
+ SlackblockValidationError,
1297
+ blockKitBuilderUrl,
1298
+ escapeMrkdwn,
1299
+ renderToBlocks,
1300
+ renderToMessage
1301
+ });
1195
1302
  //# sourceMappingURL=index.cjs.map