slackmark 0.2.0 → 0.3.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.js CHANGED
@@ -127,48 +127,6 @@ var StandardBudgetLedger = class {
127
127
  this.charts = 0;
128
128
  this.totalChars = 0;
129
129
  }
130
- tryReserveBlocks(count) {
131
- if (this.blocks + count > this.maxBlocks) {
132
- return false;
133
- }
134
- this.blocks += count;
135
- return true;
136
- }
137
- tryReserveTableChars(chars) {
138
- if (this.tableChars + chars > TABLE_CELL_CHARS_MAX) {
139
- return false;
140
- }
141
- this.tableChars += chars;
142
- return true;
143
- }
144
- tryReserveDataTableChars(chars) {
145
- if (this.dataTableChars + chars > DATA_TABLE_CELL_CHARS_MAX) {
146
- return false;
147
- }
148
- this.dataTableChars += chars;
149
- return true;
150
- }
151
- tryReserveMarkdownChars(chars) {
152
- if (this.markdownChars + chars > MARKDOWN_CUMULATIVE_MAX_CHARS) {
153
- return false;
154
- }
155
- this.markdownChars += chars;
156
- return true;
157
- }
158
- tryReserveChart() {
159
- if (this.enforceChartCap && this.charts + 1 > DATA_VISUALIZATION_MAX_PER_MESSAGE) {
160
- return false;
161
- }
162
- this.charts += 1;
163
- return true;
164
- }
165
- tryReserveTotalChars(chars) {
166
- if (this.totalChars + chars > MESSAGE_TEXT_SAFETY_MAX_CHARS) {
167
- return false;
168
- }
169
- this.totalChars += chars;
170
- return true;
171
- }
172
130
  tryReserveClaim(claim) {
173
131
  if (claim === void 0) {
174
132
  return true;
@@ -193,9 +151,6 @@ var StandardBudgetLedger = class {
193
151
  this.totalChars = nextTotal;
194
152
  return true;
195
153
  }
196
- remainingBlocks() {
197
- return this.maxBlocks - this.blocks;
198
- }
199
154
  snapshot() {
200
155
  return {
201
156
  blocks: this.blocks,
@@ -258,30 +213,6 @@ var ConversionContext = class _ConversionContext {
258
213
  this.precedingHeading = deps.precedingHeading;
259
214
  this.path = deps.path;
260
215
  }
261
- withPath(segment) {
262
- return new _ConversionContext({
263
- config: this.config,
264
- budget: this.budget,
265
- degradations: this.degradations,
266
- definitions: this.definitions,
267
- footnotes: this.footnotes,
268
- footnoteOrder: this.footnoteOrder,
269
- precedingHeading: this.precedingHeading,
270
- path: this.path.length === 0 ? segment : `${this.path}/${segment}`
271
- });
272
- }
273
- withPrecedingHeading(heading) {
274
- return new _ConversionContext({
275
- config: this.config,
276
- budget: this.budget,
277
- degradations: this.degradations,
278
- definitions: this.definitions,
279
- footnotes: this.footnotes,
280
- footnoteOrder: this.footnoteOrder,
281
- precedingHeading: heading,
282
- path: this.path
283
- });
284
- }
285
216
  withDegradations(degradations) {
286
217
  return new _ConversionContext({
287
218
  config: this.config,
@@ -684,6 +615,15 @@ function inlinesToPlain(elements) {
684
615
  case "channel":
685
616
  parts.push(`#${el.channel_id}`);
686
617
  break;
618
+ case "usergroup":
619
+ parts.push(`@${el.usergroup_id}`);
620
+ break;
621
+ case "broadcast":
622
+ parts.push(`@${el.range}`);
623
+ break;
624
+ case "date":
625
+ parts.push(el.fallback ?? el.format);
626
+ break;
687
627
  default:
688
628
  break;
689
629
  }
@@ -737,18 +677,12 @@ var RemarkMarkdownParser = class {
737
677
  // src/mermaid/mermaid-pie-parser.ts
738
678
  var COMMENT_RE = /%%[^\n]{0,500}/g;
739
679
  var TITLE_RE = /^\s*pie\b(?:\s+showData)?(?:\s+title\s+(.+))?\s*$/i;
740
- var SHOW_DATA_RE = /\bshowData\b/i;
741
680
  var ENTRY_RE = /^\s*"([^"]{1,200})"\s*:\s*(-?\d+(?:\.\d+)?)\s*$/;
742
681
  var MermaidPieParser = class {
743
- diagramType;
744
- constructor() {
745
- this.diagramType = "pie";
746
- }
747
682
  parse(source) {
748
683
  const cleaned = source.replace(COMMENT_RE, "");
749
684
  const lines = cleaned.split(/\r?\n/);
750
685
  let title;
751
- let showData = false;
752
686
  const segments = [];
753
687
  let seenHeader = false;
754
688
  for (const raw of lines) {
@@ -761,7 +695,6 @@ var MermaidPieParser = class {
761
695
  return { ok: false, reason: "Not a pie diagram" };
762
696
  }
763
697
  seenHeader = true;
764
- showData = SHOW_DATA_RE.test(line);
765
698
  const tm = TITLE_RE.exec(line);
766
699
  const titlePart = tm?.[1]?.trim();
767
700
  if (titlePart !== void 0 && titlePart.length > 0) {
@@ -794,7 +727,6 @@ var MermaidPieParser = class {
794
727
  const ast = {
795
728
  kind: "pie",
796
729
  title,
797
- showData,
798
730
  segments
799
731
  };
800
732
  return { ok: true, value: ast };
@@ -807,10 +739,6 @@ var NUM = String.raw`-?\d+(?:\.\d+)?`;
807
739
  var NUM_LIST_RE = new RegExp(String.raw`\[\s*((?:${NUM}\s*,\s*)*${NUM})\s*\]`);
808
740
  var STR_LIST_RE = /\[\s*((?:"[^"]{0,200}"\s*,\s*)*"[^"]{0,200}")\s*\]/;
809
741
  var MermaidXyChartParser = class {
810
- diagramType;
811
- constructor() {
812
- this.diagramType = "xychart-beta";
813
- }
814
742
  parse(source) {
815
743
  const cleaned = source.replace(COMMENT_RE2, "");
816
744
  const lines = cleaned.split(/\r?\n/);
@@ -818,8 +746,6 @@ var MermaidXyChartParser = class {
818
746
  let categories = [];
819
747
  let xLabel;
820
748
  let yLabel;
821
- let yMin;
822
- let yMax;
823
749
  const bars = [];
824
750
  const linesSeries = [];
825
751
  let seenHeader = false;
@@ -883,10 +809,6 @@ var MermaidXyChartParser = class {
883
809
  ).exec(line);
884
810
  if (ym !== null) {
885
811
  yLabel = ym[1];
886
- if (ym[2] !== void 0 && ym[3] !== void 0) {
887
- yMin = Number(ym[2]);
888
- yMax = Number(ym[3]);
889
- }
890
812
  continue;
891
813
  }
892
814
  return { ok: false, reason: `Invalid y-axis: ${line}` };
@@ -924,8 +846,6 @@ var MermaidXyChartParser = class {
924
846
  categories,
925
847
  xLabel,
926
848
  yLabel,
927
- yMin,
928
- yMax,
929
849
  bars,
930
850
  lines: linesSeries
931
851
  };
@@ -1299,6 +1219,7 @@ var IMAGE_EXT_RE = /\.(png|jpe?g|gif)(?:\?|#|$)/i;
1299
1219
  function emitPhrasing(nodes, ctx, baseStyle = {}) {
1300
1220
  const elements = [];
1301
1221
  const hoistedImages = [];
1222
+ const work = [...nodes];
1302
1223
  let pendingAnchor;
1303
1224
  const flushPendingAnchor = () => {
1304
1225
  if (pendingAnchor === void 0) {
@@ -1313,7 +1234,24 @@ function emitPhrasing(nodes, ctx, baseStyle = {}) {
1313
1234
  });
1314
1235
  pendingAnchor = void 0;
1315
1236
  };
1316
- for (const node of nodes) {
1237
+ for (let i = 0; i < work.length; i += 1) {
1238
+ const node = work[i];
1239
+ if (pendingAnchor === void 0) {
1240
+ const reassembled = reassembleSlackLabeledLink(work, i, baseStyle);
1241
+ if (reassembled !== void 0) {
1242
+ if (reassembled.leadingText.value.length > 0) {
1243
+ elements.push(...emitTextWithMentionsAndEmoji(reassembled.leadingText, baseStyle, ctx));
1244
+ }
1245
+ elements.push(reassembled.link);
1246
+ if (reassembled.trailingText.value.length > 0) {
1247
+ work.splice(i, 3, reassembled.trailingText);
1248
+ } else {
1249
+ work.splice(i, 3);
1250
+ }
1251
+ i -= 1;
1252
+ continue;
1253
+ }
1254
+ }
1317
1255
  if (pendingAnchor !== void 0) {
1318
1256
  if (node.type === "html" && /^<\/a\s*>$/i.test(node.value.trim())) {
1319
1257
  flushPendingAnchor();
@@ -1332,6 +1270,11 @@ function emitPhrasing(nodes, ctx, baseStyle = {}) {
1332
1270
  }
1333
1271
  if (node.type === "html") {
1334
1272
  const trimmed = node.value.trim();
1273
+ const entityInlines = slackEntityRichTextInlines(trimmed, baseStyle);
1274
+ if (entityInlines !== void 0) {
1275
+ elements.push(...entityInlines);
1276
+ continue;
1277
+ }
1335
1278
  const openOnly = /^<a\b([^>]{0,500})>\s*$/i.exec(trimmed);
1336
1279
  if (openOnly !== null) {
1337
1280
  const hrefMatch = /href\s*=\s*["']([^"']{0,3000})["']/i.exec(openOnly[1] ?? "");
@@ -1600,6 +1543,93 @@ function styleOrUndefined(style) {
1600
1543
  }
1601
1544
  return void 0;
1602
1545
  }
1546
+ var SLACK_ENTITY_CANDIDATE_RE = /<([@#!][^<>]{1,340}|(?:https?:|mailto:)[^<>|]{1,3000}\|[^<>]{1,300})>/gi;
1547
+ var SLACK_USER_RE = /^@([UW][A-Z0-9]{1,60})(?:\|[^>]{0,300})?$/;
1548
+ var SLACK_CHANNEL_RE = /^#([CDG][A-Z0-9]{1,60})(?:\|[^>]{0,300})?$/;
1549
+ var SLACK_USERGROUP_RE = /^!subteam\^(S[A-Z0-9]{1,60})(?:\|[^>]{0,300})?$/;
1550
+ var SLACK_BROADCAST_RE = /^!(here|channel|everyone)(?:\|[^>]{0,300})?$/;
1551
+ var SLACK_DATE_RE = /^!date\^(\d{1,13})\^([^^|]{1,300})(?:\^([^^|]{1,3000}))?(?:\|([^]{0,300}))?$/;
1552
+ var SLACK_LINK_RE = /^((?:https?:|mailto:)[^|]{1,3000})\|([^]{1,300})$/i;
1553
+ function slackEntityElement(inner, style) {
1554
+ const labeledLink = SLACK_LINK_RE.exec(inner);
1555
+ if (labeledLink?.[1] !== void 0 && labeledLink[2] !== void 0) {
1556
+ if (!isValidLinkUrl(labeledLink[1])) {
1557
+ return void 0;
1558
+ }
1559
+ return {
1560
+ type: "link",
1561
+ url: labeledLink[1],
1562
+ text: labeledLink[2],
1563
+ style: styleOrUndefined(style)
1564
+ };
1565
+ }
1566
+ const user = SLACK_USER_RE.exec(inner);
1567
+ if (user?.[1] !== void 0) {
1568
+ return { type: "user", user_id: user[1], style: styleOrUndefined(style) };
1569
+ }
1570
+ const channel = SLACK_CHANNEL_RE.exec(inner);
1571
+ if (channel?.[1] !== void 0) {
1572
+ return { type: "channel", channel_id: channel[1], style: styleOrUndefined(style) };
1573
+ }
1574
+ const usergroup = SLACK_USERGROUP_RE.exec(inner);
1575
+ if (usergroup?.[1] !== void 0) {
1576
+ return { type: "usergroup", usergroup_id: usergroup[1], style: styleOrUndefined(style) };
1577
+ }
1578
+ const broadcast = SLACK_BROADCAST_RE.exec(inner);
1579
+ if (broadcast !== null) {
1580
+ const range = broadcast[1];
1581
+ if (range === "here" || range === "channel" || range === "everyone") {
1582
+ return { type: "broadcast", range, style: styleOrUndefined(style) };
1583
+ }
1584
+ }
1585
+ const date = SLACK_DATE_RE.exec(inner);
1586
+ if (date?.[1] !== void 0 && date[2] !== void 0) {
1587
+ const timestamp = Number(date[1]);
1588
+ if (Number.isSafeInteger(timestamp)) {
1589
+ return {
1590
+ type: "date",
1591
+ timestamp,
1592
+ format: date[2],
1593
+ url: date[3],
1594
+ fallback: date[4],
1595
+ style: styleOrUndefined(style)
1596
+ };
1597
+ }
1598
+ }
1599
+ return void 0;
1600
+ }
1601
+ function slackEntitySplit(value, style, emit) {
1602
+ SLACK_ENTITY_CANDIDATE_RE.lastIndex = 0;
1603
+ let match = SLACK_ENTITY_CANDIDATE_RE.exec(value);
1604
+ if (match === null) {
1605
+ return void 0;
1606
+ }
1607
+ const out = [];
1608
+ let last = 0;
1609
+ let sawEntity = false;
1610
+ while (match !== null) {
1611
+ const element = slackEntityElement(match[1] ?? "", style);
1612
+ if (element !== void 0) {
1613
+ sawEntity = true;
1614
+ if (match.index > last) {
1615
+ out.push(...emit(value.slice(last, match.index)));
1616
+ }
1617
+ out.push(element);
1618
+ last = match.index + match[0].length;
1619
+ }
1620
+ match = SLACK_ENTITY_CANDIDATE_RE.exec(value);
1621
+ }
1622
+ if (!sawEntity) {
1623
+ return void 0;
1624
+ }
1625
+ if (last < value.length) {
1626
+ out.push(...emit(value.slice(last)));
1627
+ }
1628
+ return out;
1629
+ }
1630
+ function slackEntityRichTextInlines(value, style) {
1631
+ return slackEntitySplit(value, style, (slice) => emitTextWithEmoji(slice, style));
1632
+ }
1603
1633
  var MENTION_RE = /(?<![A-Za-z0-9._-])@([a-zA-Z0-9._-]{1,64})|(?<![A-Za-z0-9_-])#([a-zA-Z0-9_-]{1,80})/g;
1604
1634
  function phrasingNeedsMentionResolution(nodes, ctx) {
1605
1635
  const resolvers = ctx.config.mentionResolvers;
@@ -1611,13 +1641,23 @@ function phrasingNeedsMentionResolution(nodes, ctx) {
1611
1641
  return MENTION_RE.test(plain);
1612
1642
  }
1613
1643
  function emitTextWithMentionsAndEmoji(node, style, ctx) {
1644
+ const viaEntities = slackEntitySplit(
1645
+ node.value,
1646
+ style,
1647
+ (slice) => emitMentionsAndEmoji(slice, style, ctx)
1648
+ );
1649
+ if (viaEntities !== void 0) {
1650
+ return viaEntities;
1651
+ }
1652
+ return emitMentionsAndEmoji(node.value, style, ctx);
1653
+ }
1654
+ function emitMentionsAndEmoji(value, style, ctx) {
1614
1655
  const resolvers = ctx.config.mentionResolvers;
1615
1656
  const canUser = resolvers?.resolveUser !== void 0;
1616
1657
  const canChannel = resolvers?.resolveChannel !== void 0;
1617
1658
  if (!canUser && !canChannel) {
1618
- return emitTextWithEmoji(node.value, style);
1659
+ return emitTextWithEmoji(value, style);
1619
1660
  }
1620
- const value = node.value;
1621
1661
  const out = [];
1622
1662
  MENTION_RE.lastIndex = 0;
1623
1663
  let last = 0;
@@ -1685,9 +1725,53 @@ function emitTextWithEmoji(value, style) {
1685
1725
  }
1686
1726
  return out;
1687
1727
  }
1728
+ function reassembleSlackLabeledLink(work, i, style) {
1729
+ const lead = work[i];
1730
+ const linkNode = work[i + 1];
1731
+ const tail = work[i + 2];
1732
+ if (lead?.type !== "text" || !lead.value.endsWith("<")) {
1733
+ return void 0;
1734
+ }
1735
+ if (linkNode?.type !== "link" || tail?.type !== "text") {
1736
+ return void 0;
1737
+ }
1738
+ const url = linkNode.url;
1739
+ const pipeIndex = url.indexOf("|");
1740
+ if (pipeIndex <= 0 || !HTTP_URL_RE.test(url)) {
1741
+ return void 0;
1742
+ }
1743
+ if (phrasingToPlain(linkNode.children) !== url) {
1744
+ return void 0;
1745
+ }
1746
+ const closeIndex = tail.value.indexOf(">");
1747
+ if (closeIndex === -1) {
1748
+ return void 0;
1749
+ }
1750
+ const realUrl = url.slice(0, pipeIndex);
1751
+ if (!isValidLinkUrl(realUrl)) {
1752
+ return void 0;
1753
+ }
1754
+ const label = `${url.slice(pipeIndex + 1)}${tail.value.slice(0, closeIndex)}`;
1755
+ if (label.length === 0) {
1756
+ return void 0;
1757
+ }
1758
+ return {
1759
+ leadingText: { type: "text", value: lead.value.slice(0, -1) },
1760
+ link: { type: "link", url: realUrl, text: label, style: styleOrUndefined(style) },
1761
+ trailingText: { type: "text", value: tail.value.slice(closeIndex + 1) }
1762
+ };
1763
+ }
1688
1764
  function emitLink(node, ctx, style) {
1689
1765
  const url = node.url;
1690
1766
  const text = phrasingToPlain(node.children);
1767
+ const pipeIndex = url.indexOf("|");
1768
+ if (pipeIndex > 0 && HTTP_URL_RE.test(url) && text === url) {
1769
+ const realUrl = url.slice(0, pipeIndex);
1770
+ const label = url.slice(pipeIndex + 1);
1771
+ if (label.length > 0 && isValidLinkUrl(realUrl)) {
1772
+ return [{ type: "link", url: realUrl, text: label, style: styleOrUndefined(style) }];
1773
+ }
1774
+ }
1691
1775
  if (!isValidLinkUrl(url)) {
1692
1776
  ctx.degradations.add({
1693
1777
  nodeType: "link",
@@ -2158,6 +2242,20 @@ ${body}`, ctx, "html.details.section")
2158
2242
  }
2159
2243
  ];
2160
2244
  }
2245
+ const entityInlines = slackEntityRichTextInlines(html.value.trim(), {});
2246
+ if (entityInlines !== void 0) {
2247
+ return [
2248
+ {
2249
+ budget: { blocks: 1 },
2250
+ emit: () => [
2251
+ {
2252
+ type: "rich_text",
2253
+ elements: [{ type: "rich_text_section", elements: entityInlines }]
2254
+ }
2255
+ ]
2256
+ }
2257
+ ];
2258
+ }
2161
2259
  return [
2162
2260
  {
2163
2261
  budget: { blocks: 1 },
@@ -3383,12 +3481,10 @@ export {
3383
3481
  CapabilityPresetName,
3384
3482
  ConfigurationError,
3385
3483
  ConversionContext,
3386
- ConversionStrategy as ConversionStrategies,
3387
3484
  DeterministicPlainTextRenderer,
3388
3485
  MermaidPieParser,
3389
3486
  MermaidXyChartParser,
3390
3487
  MessageAssembler,
3391
- OverflowMode as OverflowModes,
3392
3488
  RecordingDegradationCollector,
3393
3489
  RemarkMarkdownParser,
3394
3490
  SlackMarkConverter,