slackmark 0.2.0 → 0.4.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/adapters/html-block-adapter.d.ts.map +1 -1
- package/dist/adapters/inline.d.ts +11 -1
- package/dist/adapters/inline.d.ts.map +1 -1
- package/dist/adapters/mermaid-fence-adapter.d.ts.map +1 -1
- package/dist/adapters/paragraph-adapter.d.ts.map +1 -1
- package/dist/adapters/table-adapter.d.ts.map +1 -1
- package/dist/assemble/message-assembler.d.ts.map +1 -1
- package/dist/blocks/types.d.ts +70 -29
- package/dist/blocks/types.d.ts.map +1 -1
- package/dist/budget/conversion-context.d.ts +0 -2
- package/dist/budget/conversion-context.d.ts.map +1 -1
- package/dist/budget/standard-budget-ledger.d.ts +0 -7
- package/dist/budget/standard-budget-ledger.d.ts.map +1 -1
- package/dist/capabilities/capabilities.d.ts +2 -4
- package/dist/capabilities/capabilities.d.ts.map +1 -1
- package/dist/converter/slackmark-converter.d.ts.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +686 -286
- package/dist/index.js.map +1 -1
- package/dist/mermaid/mermaid-pie-parser.d.ts +0 -2
- package/dist/mermaid/mermaid-pie-parser.d.ts.map +1 -1
- package/dist/mermaid/mermaid-xychart-parser.d.ts +0 -2
- package/dist/mermaid/mermaid-xychart-parser.d.ts.map +1 -1
- package/dist/types.d.ts +1 -15
- package/dist/types.d.ts.map +1 -1
- package/dist/utf16.d.ts +20 -0
- package/dist/utf16.d.ts.map +1 -0
- package/package.json +2 -8
package/dist/index.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var ConfigurationError = class extends Error {
|
|
3
|
+
name;
|
|
4
|
+
constructor(message, options) {
|
|
5
|
+
super(message, options?.cause !== void 0 ? { cause: options.cause } : void 0);
|
|
6
|
+
this.name = "ConfigurationError";
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
|
|
1
10
|
// src/capabilities/capabilities.ts
|
|
2
11
|
var CapabilityPresetName = {
|
|
3
12
|
Latest: "latest",
|
|
@@ -59,12 +68,13 @@ function resolveCapabilities(input, overrides) {
|
|
|
59
68
|
if (input === void 0) {
|
|
60
69
|
base = CAPABILITY_PRESETS.latest;
|
|
61
70
|
} else if (typeof input === "string") {
|
|
62
|
-
const preset = CAPABILITY_PRESETS[input];
|
|
71
|
+
const preset = isPresetName(input) ? CAPABILITY_PRESETS[input] : void 0;
|
|
63
72
|
if (preset === void 0) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
73
|
+
throw new ConfigurationError(
|
|
74
|
+
`Unknown capability preset ${JSON.stringify(input)}; use one of: ${Object.keys(CAPABILITY_PRESETS).join(", ")}.`
|
|
75
|
+
);
|
|
67
76
|
}
|
|
77
|
+
base = preset;
|
|
68
78
|
} else if (isFullFlags(input)) {
|
|
69
79
|
base = input;
|
|
70
80
|
} else {
|
|
@@ -75,6 +85,9 @@ function resolveCapabilities(input, overrides) {
|
|
|
75
85
|
}
|
|
76
86
|
return Object.freeze({ ...base, ...overrides });
|
|
77
87
|
}
|
|
88
|
+
function isPresetName(value) {
|
|
89
|
+
return Object.hasOwn(CAPABILITY_PRESETS, value);
|
|
90
|
+
}
|
|
78
91
|
function isFullFlags(value) {
|
|
79
92
|
return typeof value.richText === "boolean" && typeof value.markdownBlock === "boolean" && typeof value.tableBlock === "boolean" && typeof value.preformattedLanguage === "boolean" && typeof value.headerLevel === "boolean" && typeof value.dataTable === "boolean" && typeof value.dataVisualization === "boolean" && typeof value.containerBlock === "boolean";
|
|
80
93
|
}
|
|
@@ -127,48 +140,6 @@ var StandardBudgetLedger = class {
|
|
|
127
140
|
this.charts = 0;
|
|
128
141
|
this.totalChars = 0;
|
|
129
142
|
}
|
|
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
143
|
tryReserveClaim(claim) {
|
|
173
144
|
if (claim === void 0) {
|
|
174
145
|
return true;
|
|
@@ -193,9 +164,6 @@ var StandardBudgetLedger = class {
|
|
|
193
164
|
this.totalChars = nextTotal;
|
|
194
165
|
return true;
|
|
195
166
|
}
|
|
196
|
-
remainingBlocks() {
|
|
197
|
-
return this.maxBlocks - this.blocks;
|
|
198
|
-
}
|
|
199
167
|
snapshot() {
|
|
200
168
|
return {
|
|
201
169
|
blocks: this.blocks,
|
|
@@ -258,30 +226,6 @@ var ConversionContext = class _ConversionContext {
|
|
|
258
226
|
this.precedingHeading = deps.precedingHeading;
|
|
259
227
|
this.path = deps.path;
|
|
260
228
|
}
|
|
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
229
|
withDegradations(degradations) {
|
|
286
230
|
return new _ConversionContext({
|
|
287
231
|
config: this.config,
|
|
@@ -367,6 +311,29 @@ function assertNever(value, message) {
|
|
|
367
311
|
throw new Error(`${detail}: ${JSON.stringify(value)}`);
|
|
368
312
|
}
|
|
369
313
|
|
|
314
|
+
// src/utf16.ts
|
|
315
|
+
var HIGH_SURROGATE_MIN = 55296;
|
|
316
|
+
var HIGH_SURROGATE_MAX = 56319;
|
|
317
|
+
var LOW_SURROGATE_MIN = 56320;
|
|
318
|
+
var LOW_SURROGATE_MAX = 57343;
|
|
319
|
+
function surrogateSafeEnd(text, end) {
|
|
320
|
+
if (end <= 0) {
|
|
321
|
+
return 0;
|
|
322
|
+
}
|
|
323
|
+
if (end >= text.length) {
|
|
324
|
+
return text.length;
|
|
325
|
+
}
|
|
326
|
+
const before = text.charCodeAt(end - 1);
|
|
327
|
+
const after = text.charCodeAt(end);
|
|
328
|
+
if (before >= HIGH_SURROGATE_MIN && before <= HIGH_SURROGATE_MAX && after >= LOW_SURROGATE_MIN && after <= LOW_SURROGATE_MAX) {
|
|
329
|
+
return end - 1;
|
|
330
|
+
}
|
|
331
|
+
return end;
|
|
332
|
+
}
|
|
333
|
+
function sliceSurrogateSafe(text, max) {
|
|
334
|
+
return text.slice(0, surrogateSafeEnd(text, max));
|
|
335
|
+
}
|
|
336
|
+
|
|
370
337
|
// src/types.ts
|
|
371
338
|
var OverflowMode = {
|
|
372
339
|
Degrade: "degrade",
|
|
@@ -467,7 +434,7 @@ var MessageAssembler = class {
|
|
|
467
434
|
reason: "Notification text truncated to 40k safety limit",
|
|
468
435
|
path: ctx.path
|
|
469
436
|
});
|
|
470
|
-
return { blocks, text: raw
|
|
437
|
+
return { blocks, text: sliceSurrogateSafe(raw, MESSAGE_TEXT_SAFETY_MAX_CHARS) };
|
|
471
438
|
}
|
|
472
439
|
};
|
|
473
440
|
function buildFootnoteBlocks(entries, degradations) {
|
|
@@ -486,7 +453,7 @@ function buildFootnoteBlocks(entries, degradations) {
|
|
|
486
453
|
reason: "Footnote context text truncated to 3000 chars",
|
|
487
454
|
path: "footnote"
|
|
488
455
|
});
|
|
489
|
-
elements.push({ type: "mrkdwn", text: note
|
|
456
|
+
elements.push({ type: "mrkdwn", text: sliceSurrogateSafe(note, SECTION_TEXT_MAX_CHARS) });
|
|
490
457
|
} else {
|
|
491
458
|
elements.push({ type: "mrkdwn", text: note });
|
|
492
459
|
}
|
|
@@ -684,12 +651,116 @@ function inlinesToPlain(elements) {
|
|
|
684
651
|
case "channel":
|
|
685
652
|
parts.push(`#${el.channel_id}`);
|
|
686
653
|
break;
|
|
654
|
+
case "usergroup":
|
|
655
|
+
parts.push(`@${el.usergroup_id}`);
|
|
656
|
+
break;
|
|
657
|
+
case "broadcast":
|
|
658
|
+
parts.push(`@${el.range}`);
|
|
659
|
+
break;
|
|
660
|
+
case "date":
|
|
661
|
+
parts.push(el.fallback ?? formatDateTokensUtc(el.timestamp, el.format));
|
|
662
|
+
break;
|
|
687
663
|
default:
|
|
688
664
|
break;
|
|
689
665
|
}
|
|
690
666
|
}
|
|
691
667
|
return parts.join("");
|
|
692
668
|
}
|
|
669
|
+
var DATE_TOKEN_RE = /\{([a-z_]+)\}/g;
|
|
670
|
+
var MONTHS = [
|
|
671
|
+
"January",
|
|
672
|
+
"February",
|
|
673
|
+
"March",
|
|
674
|
+
"April",
|
|
675
|
+
"May",
|
|
676
|
+
"June",
|
|
677
|
+
"July",
|
|
678
|
+
"August",
|
|
679
|
+
"September",
|
|
680
|
+
"October",
|
|
681
|
+
"November",
|
|
682
|
+
"December"
|
|
683
|
+
];
|
|
684
|
+
var WEEKDAYS = [
|
|
685
|
+
"Sunday",
|
|
686
|
+
"Monday",
|
|
687
|
+
"Tuesday",
|
|
688
|
+
"Wednesday",
|
|
689
|
+
"Thursday",
|
|
690
|
+
"Friday",
|
|
691
|
+
"Saturday"
|
|
692
|
+
];
|
|
693
|
+
function pad2(value) {
|
|
694
|
+
return String(value).padStart(2, "0");
|
|
695
|
+
}
|
|
696
|
+
function ordinal(value) {
|
|
697
|
+
const remainder = value % 100;
|
|
698
|
+
if (remainder >= 11 && remainder <= 13) {
|
|
699
|
+
return `${String(value)}th`;
|
|
700
|
+
}
|
|
701
|
+
switch (value % 10) {
|
|
702
|
+
case 1:
|
|
703
|
+
return `${String(value)}st`;
|
|
704
|
+
case 2:
|
|
705
|
+
return `${String(value)}nd`;
|
|
706
|
+
case 3:
|
|
707
|
+
return `${String(value)}rd`;
|
|
708
|
+
default:
|
|
709
|
+
return `${String(value)}th`;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
function dateToken(token, date) {
|
|
713
|
+
const day = date.getUTCDate();
|
|
714
|
+
const month = MONTHS[date.getUTCMonth()] ?? "";
|
|
715
|
+
const shortMonth = month.slice(0, 3);
|
|
716
|
+
const weekday = WEEKDAYS[date.getUTCDay()] ?? "";
|
|
717
|
+
const year = date.getUTCFullYear();
|
|
718
|
+
switch (token) {
|
|
719
|
+
case "date_num":
|
|
720
|
+
return `${String(year)}-${pad2(date.getUTCMonth() + 1)}-${pad2(day)}`;
|
|
721
|
+
// Slack renders {date_slash} month-first: 02/18/2014 (docs example for
|
|
722
|
+
// timestamp 1392734382).
|
|
723
|
+
case "date_slash":
|
|
724
|
+
return `${pad2(date.getUTCMonth() + 1)}/${pad2(day)}/${String(year)}`;
|
|
725
|
+
case "date_long":
|
|
726
|
+
case "date_long_pretty":
|
|
727
|
+
return `${weekday}, ${month} ${ordinal(day)}, ${String(year)}`;
|
|
728
|
+
case "date_long_full":
|
|
729
|
+
return `${month} ${String(day)}, ${String(year)}`;
|
|
730
|
+
// {date} renders "February 18th, 2014" (ordinal + year), not "February 18".
|
|
731
|
+
case "date":
|
|
732
|
+
case "date_pretty":
|
|
733
|
+
return `${month} ${ordinal(day)}, ${String(year)}`;
|
|
734
|
+
case "date_short":
|
|
735
|
+
case "date_short_pretty":
|
|
736
|
+
return `${shortMonth} ${String(day)}, ${String(year)}`;
|
|
737
|
+
case "time":
|
|
738
|
+
return `${pad2(date.getUTCHours())}:${pad2(date.getUTCMinutes())}`;
|
|
739
|
+
case "time_secs":
|
|
740
|
+
return `${pad2(date.getUTCHours())}:${pad2(date.getUTCMinutes())}:${pad2(date.getUTCSeconds())}`;
|
|
741
|
+
default:
|
|
742
|
+
return void 0;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
function formatDateTokensUtc(timestamp, format) {
|
|
746
|
+
const date = new Date(timestamp * 1e3);
|
|
747
|
+
if (!Number.isFinite(timestamp) || Number.isNaN(date.getTime())) {
|
|
748
|
+
return String(timestamp);
|
|
749
|
+
}
|
|
750
|
+
let unsupported = false;
|
|
751
|
+
const formatted = format.replace(DATE_TOKEN_RE, (_match, token) => {
|
|
752
|
+
const replacement = dateToken(token, date);
|
|
753
|
+
if (replacement === void 0) {
|
|
754
|
+
unsupported = true;
|
|
755
|
+
return "";
|
|
756
|
+
}
|
|
757
|
+
return replacement;
|
|
758
|
+
});
|
|
759
|
+
if (format.length === 0 || unsupported) {
|
|
760
|
+
return date.toISOString().slice(0, 10);
|
|
761
|
+
}
|
|
762
|
+
return formatted;
|
|
763
|
+
}
|
|
693
764
|
function tableToPlain(rows) {
|
|
694
765
|
const lines = [];
|
|
695
766
|
for (const row of rows) {
|
|
@@ -737,18 +808,12 @@ var RemarkMarkdownParser = class {
|
|
|
737
808
|
// src/mermaid/mermaid-pie-parser.ts
|
|
738
809
|
var COMMENT_RE = /%%[^\n]{0,500}/g;
|
|
739
810
|
var TITLE_RE = /^\s*pie\b(?:\s+showData)?(?:\s+title\s+(.+))?\s*$/i;
|
|
740
|
-
var SHOW_DATA_RE = /\bshowData\b/i;
|
|
741
811
|
var ENTRY_RE = /^\s*"([^"]{1,200})"\s*:\s*(-?\d+(?:\.\d+)?)\s*$/;
|
|
742
812
|
var MermaidPieParser = class {
|
|
743
|
-
diagramType;
|
|
744
|
-
constructor() {
|
|
745
|
-
this.diagramType = "pie";
|
|
746
|
-
}
|
|
747
813
|
parse(source) {
|
|
748
814
|
const cleaned = source.replace(COMMENT_RE, "");
|
|
749
815
|
const lines = cleaned.split(/\r?\n/);
|
|
750
816
|
let title;
|
|
751
|
-
let showData = false;
|
|
752
817
|
const segments = [];
|
|
753
818
|
let seenHeader = false;
|
|
754
819
|
for (const raw of lines) {
|
|
@@ -761,7 +826,6 @@ var MermaidPieParser = class {
|
|
|
761
826
|
return { ok: false, reason: "Not a pie diagram" };
|
|
762
827
|
}
|
|
763
828
|
seenHeader = true;
|
|
764
|
-
showData = SHOW_DATA_RE.test(line);
|
|
765
829
|
const tm = TITLE_RE.exec(line);
|
|
766
830
|
const titlePart = tm?.[1]?.trim();
|
|
767
831
|
if (titlePart !== void 0 && titlePart.length > 0) {
|
|
@@ -794,7 +858,6 @@ var MermaidPieParser = class {
|
|
|
794
858
|
const ast = {
|
|
795
859
|
kind: "pie",
|
|
796
860
|
title,
|
|
797
|
-
showData,
|
|
798
861
|
segments
|
|
799
862
|
};
|
|
800
863
|
return { ok: true, value: ast };
|
|
@@ -807,10 +870,6 @@ var NUM = String.raw`-?\d+(?:\.\d+)?`;
|
|
|
807
870
|
var NUM_LIST_RE = new RegExp(String.raw`\[\s*((?:${NUM}\s*,\s*)*${NUM})\s*\]`);
|
|
808
871
|
var STR_LIST_RE = /\[\s*((?:"[^"]{0,200}"\s*,\s*)*"[^"]{0,200}")\s*\]/;
|
|
809
872
|
var MermaidXyChartParser = class {
|
|
810
|
-
diagramType;
|
|
811
|
-
constructor() {
|
|
812
|
-
this.diagramType = "xychart-beta";
|
|
813
|
-
}
|
|
814
873
|
parse(source) {
|
|
815
874
|
const cleaned = source.replace(COMMENT_RE2, "");
|
|
816
875
|
const lines = cleaned.split(/\r?\n/);
|
|
@@ -818,8 +877,6 @@ var MermaidXyChartParser = class {
|
|
|
818
877
|
let categories = [];
|
|
819
878
|
let xLabel;
|
|
820
879
|
let yLabel;
|
|
821
|
-
let yMin;
|
|
822
|
-
let yMax;
|
|
823
880
|
const bars = [];
|
|
824
881
|
const linesSeries = [];
|
|
825
882
|
let seenHeader = false;
|
|
@@ -883,10 +940,6 @@ var MermaidXyChartParser = class {
|
|
|
883
940
|
).exec(line);
|
|
884
941
|
if (ym !== null) {
|
|
885
942
|
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
943
|
continue;
|
|
891
944
|
}
|
|
892
945
|
return { ok: false, reason: `Invalid y-axis: ${line}` };
|
|
@@ -924,8 +977,6 @@ var MermaidXyChartParser = class {
|
|
|
924
977
|
categories,
|
|
925
978
|
xLabel,
|
|
926
979
|
yLabel,
|
|
927
|
-
yMin,
|
|
928
|
-
yMax,
|
|
929
980
|
bars,
|
|
930
981
|
lines: linesSeries
|
|
931
982
|
};
|
|
@@ -949,15 +1000,6 @@ function parseNumberList(inner) {
|
|
|
949
1000
|
// src/converter/slackmark-converter.ts
|
|
950
1001
|
import { toString } from "mdast-util-to-string";
|
|
951
1002
|
|
|
952
|
-
// src/errors.ts
|
|
953
|
-
var ConfigurationError = class extends Error {
|
|
954
|
-
name;
|
|
955
|
-
constructor(message, options) {
|
|
956
|
-
super(message, options?.cause !== void 0 ? { cause: options.cause } : void 0);
|
|
957
|
-
this.name = "ConfigurationError";
|
|
958
|
-
}
|
|
959
|
-
};
|
|
960
|
-
|
|
961
1003
|
// src/converter/resolve-config.ts
|
|
962
1004
|
function resolveConfig(defaults, overrides) {
|
|
963
1005
|
const merged = {
|
|
@@ -1047,7 +1089,7 @@ var SlackMarkConverter = class {
|
|
|
1047
1089
|
enableMath: config.enableMath,
|
|
1048
1090
|
enableFrontmatter: config.enableFrontmatter
|
|
1049
1091
|
});
|
|
1050
|
-
const { definitions, footnotes, footnoteOrder } = collectRefs(tree);
|
|
1092
|
+
const { definitions, footnotes, footnoteOrder } = collectRefs(tree, degradations);
|
|
1051
1093
|
let precedingHeading;
|
|
1052
1094
|
const emitted = [];
|
|
1053
1095
|
let index = 0;
|
|
@@ -1072,11 +1114,13 @@ var SlackMarkConverter = class {
|
|
|
1072
1114
|
emitted.push(...blocks);
|
|
1073
1115
|
index += 1;
|
|
1074
1116
|
}
|
|
1117
|
+
const footnoteNumbers = /* @__PURE__ */ new Map();
|
|
1118
|
+
footnoteOrder.forEach((id, i) => footnoteNumbers.set(id, i + 1));
|
|
1075
1119
|
const footnoteEntries = footnoteOrder.map((id, i) => {
|
|
1076
1120
|
const def = footnotes.get(id);
|
|
1077
1121
|
return {
|
|
1078
1122
|
n: i + 1,
|
|
1079
|
-
text: def !== void 0 ?
|
|
1123
|
+
text: def !== void 0 ? footnoteBodyText(def, footnoteNumbers).trim() : id
|
|
1080
1124
|
};
|
|
1081
1125
|
});
|
|
1082
1126
|
const footnoteBlocks = buildFootnoteBlocks(footnoteEntries, degradations);
|
|
@@ -1185,7 +1229,7 @@ function safeNotificationText(plainText, markdown, degradations) {
|
|
|
1185
1229
|
reason: "Notification text truncated to 40k safety limit",
|
|
1186
1230
|
path: "root"
|
|
1187
1231
|
});
|
|
1188
|
-
return raw
|
|
1232
|
+
return sliceSurrogateSafe(raw, MESSAGE_TEXT_SAFETY_MAX_CHARS);
|
|
1189
1233
|
}
|
|
1190
1234
|
function clampSectionFallback(text, degradations) {
|
|
1191
1235
|
if (text.length <= SECTION_TEXT_MAX_CHARS) {
|
|
@@ -1198,17 +1242,38 @@ function clampSectionFallback(text, degradations) {
|
|
|
1198
1242
|
reason: "Section text truncated to 3000 chars",
|
|
1199
1243
|
path: "root"
|
|
1200
1244
|
});
|
|
1201
|
-
return text
|
|
1245
|
+
return sliceSurrogateSafe(text, SECTION_TEXT_MAX_CHARS);
|
|
1202
1246
|
}
|
|
1203
|
-
function collectRefs(tree) {
|
|
1247
|
+
function collectRefs(tree, degradations) {
|
|
1204
1248
|
const definitions = /* @__PURE__ */ new Map();
|
|
1205
1249
|
const footnotes = /* @__PURE__ */ new Map();
|
|
1206
1250
|
for (const node of tree.children) {
|
|
1207
1251
|
if (node.type === "definition") {
|
|
1208
|
-
|
|
1252
|
+
const key = node.identifier.toLowerCase();
|
|
1253
|
+
if (definitions.has(key)) {
|
|
1254
|
+
degradations.add({
|
|
1255
|
+
nodeType: "definition",
|
|
1256
|
+
from: node.identifier,
|
|
1257
|
+
to: "ignored",
|
|
1258
|
+
reason: "Duplicate link reference definition; first definition wins",
|
|
1259
|
+
path: "root"
|
|
1260
|
+
});
|
|
1261
|
+
} else {
|
|
1262
|
+
definitions.set(key, node);
|
|
1263
|
+
}
|
|
1209
1264
|
}
|
|
1210
1265
|
if (node.type === "footnoteDefinition") {
|
|
1211
|
-
footnotes.
|
|
1266
|
+
if (footnotes.has(node.identifier)) {
|
|
1267
|
+
degradations.add({
|
|
1268
|
+
nodeType: "footnoteDefinition",
|
|
1269
|
+
from: node.identifier,
|
|
1270
|
+
to: "ignored",
|
|
1271
|
+
reason: "Duplicate footnote definition; first definition wins",
|
|
1272
|
+
path: "root"
|
|
1273
|
+
});
|
|
1274
|
+
} else {
|
|
1275
|
+
footnotes.set(node.identifier, node);
|
|
1276
|
+
}
|
|
1212
1277
|
}
|
|
1213
1278
|
}
|
|
1214
1279
|
const footnoteOrder = [];
|
|
@@ -1219,8 +1284,46 @@ function collectRefs(tree) {
|
|
|
1219
1284
|
footnoteOrder.push(id);
|
|
1220
1285
|
}
|
|
1221
1286
|
});
|
|
1287
|
+
for (let i = 0; i < footnoteOrder.length; i += 1) {
|
|
1288
|
+
const def = footnotes.get(footnoteOrder[i] ?? "");
|
|
1289
|
+
if (def === void 0) {
|
|
1290
|
+
continue;
|
|
1291
|
+
}
|
|
1292
|
+
for (const child of def.children) {
|
|
1293
|
+
walkFootnoteReferences(child, (id) => {
|
|
1294
|
+
if (!seen.has(id)) {
|
|
1295
|
+
seen.add(id);
|
|
1296
|
+
footnoteOrder.push(id);
|
|
1297
|
+
}
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1222
1301
|
return { definitions, footnotes, footnoteOrder };
|
|
1223
1302
|
}
|
|
1303
|
+
function footnoteBodyText(node, numbers) {
|
|
1304
|
+
if (node === null || typeof node !== "object") {
|
|
1305
|
+
return "";
|
|
1306
|
+
}
|
|
1307
|
+
const record = node;
|
|
1308
|
+
if (record.type === "footnoteReference" && typeof record.identifier === "string") {
|
|
1309
|
+
const n = numbers.get(record.identifier);
|
|
1310
|
+
return n !== void 0 ? `[${String(n)}]` : `[^${record.identifier}]`;
|
|
1311
|
+
}
|
|
1312
|
+
if (typeof record.value === "string") {
|
|
1313
|
+
return record.value;
|
|
1314
|
+
}
|
|
1315
|
+
if (typeof record.alt === "string") {
|
|
1316
|
+
return record.alt;
|
|
1317
|
+
}
|
|
1318
|
+
if (record.children !== void 0) {
|
|
1319
|
+
const parts = [];
|
|
1320
|
+
for (const child of record.children) {
|
|
1321
|
+
parts.push(footnoteBodyText(child, numbers));
|
|
1322
|
+
}
|
|
1323
|
+
return parts.join("");
|
|
1324
|
+
}
|
|
1325
|
+
return "";
|
|
1326
|
+
}
|
|
1224
1327
|
function walkFootnoteReferences(node, onRef) {
|
|
1225
1328
|
if (node === null || typeof node !== "object") {
|
|
1226
1329
|
return;
|
|
@@ -1244,8 +1347,11 @@ function chunkMarkdown(text, max) {
|
|
|
1244
1347
|
return [text.length > 0 ? text : " "];
|
|
1245
1348
|
}
|
|
1246
1349
|
const out = [];
|
|
1247
|
-
|
|
1248
|
-
|
|
1350
|
+
let i = 0;
|
|
1351
|
+
while (i < text.length) {
|
|
1352
|
+
const end = surrogateSafeEnd(text, i + max);
|
|
1353
|
+
out.push(text.slice(i, end));
|
|
1354
|
+
i = end;
|
|
1249
1355
|
}
|
|
1250
1356
|
return out;
|
|
1251
1357
|
}
|
|
@@ -1255,65 +1361,43 @@ import { toString as toString3 } from "mdast-util-to-string";
|
|
|
1255
1361
|
|
|
1256
1362
|
// src/adapters/inline.ts
|
|
1257
1363
|
import { toString as toString2 } from "mdast-util-to-string";
|
|
1258
|
-
var EMOJI_SHORTCODE_RE =
|
|
1259
|
-
var KNOWN_SLACK_EMOJI = /* @__PURE__ */ new Set([
|
|
1260
|
-
"smile",
|
|
1261
|
-
"grinning",
|
|
1262
|
-
"wink",
|
|
1263
|
-
"blush",
|
|
1264
|
-
"wave",
|
|
1265
|
-
"thumbsup",
|
|
1266
|
-
"thumbsdown",
|
|
1267
|
-
"tada",
|
|
1268
|
-
"rocket",
|
|
1269
|
-
"fire",
|
|
1270
|
-
"heart",
|
|
1271
|
-
"eyes",
|
|
1272
|
-
"thinking_face",
|
|
1273
|
-
"sweat_smile",
|
|
1274
|
-
"joy",
|
|
1275
|
-
"sob",
|
|
1276
|
-
"clap",
|
|
1277
|
-
"100",
|
|
1278
|
-
"white_check_mark",
|
|
1279
|
-
"x",
|
|
1280
|
-
"warning",
|
|
1281
|
-
"bulb",
|
|
1282
|
-
"memo",
|
|
1283
|
-
"mag",
|
|
1284
|
-
"link",
|
|
1285
|
-
"gear",
|
|
1286
|
-
"bug",
|
|
1287
|
-
"sparkles",
|
|
1288
|
-
"+1",
|
|
1289
|
-
"-1",
|
|
1290
|
-
"ok_hand",
|
|
1291
|
-
"pray",
|
|
1292
|
-
"star",
|
|
1293
|
-
"zap",
|
|
1294
|
-
"check",
|
|
1295
|
-
"heavy_check_mark"
|
|
1296
|
-
]);
|
|
1364
|
+
var EMOJI_SHORTCODE_RE = /(?<![A-Za-z0-9]):([a-z0-9_+-]{1,64}):(?![A-Za-z0-9])/g;
|
|
1297
1365
|
var HTTP_URL_RE = /^(https?:|mailto:)/i;
|
|
1298
1366
|
var IMAGE_EXT_RE = /\.(png|jpe?g|gif)(?:\?|#|$)/i;
|
|
1299
1367
|
function emitPhrasing(nodes, ctx, baseStyle = {}) {
|
|
1300
1368
|
const elements = [];
|
|
1301
1369
|
const hoistedImages = [];
|
|
1370
|
+
const work = [...nodes];
|
|
1302
1371
|
let pendingAnchor;
|
|
1372
|
+
const htmlStyles = [];
|
|
1373
|
+
const activeStyle = () => htmlStyles[htmlStyles.length - 1] ?? baseStyle;
|
|
1303
1374
|
const flushPendingAnchor = () => {
|
|
1304
1375
|
if (pendingAnchor === void 0) {
|
|
1305
1376
|
return;
|
|
1306
1377
|
}
|
|
1307
|
-
|
|
1308
|
-
elements.push({
|
|
1309
|
-
type: "link",
|
|
1310
|
-
url: pendingAnchor.href,
|
|
1311
|
-
text: label.length > 0 ? label : pendingAnchor.href,
|
|
1312
|
-
style: styleOrUndefined(pendingAnchor.style)
|
|
1313
|
-
});
|
|
1378
|
+
elements.push(anchorInline(pendingAnchor, ctx));
|
|
1314
1379
|
pendingAnchor = void 0;
|
|
1315
1380
|
};
|
|
1316
|
-
for (
|
|
1381
|
+
for (let i = 0; i < work.length; i += 1) {
|
|
1382
|
+
const node = work[i];
|
|
1383
|
+
if (pendingAnchor === void 0) {
|
|
1384
|
+
const reassembled = reassembleSlackLabeledLink(work, i, activeStyle());
|
|
1385
|
+
if (reassembled !== void 0) {
|
|
1386
|
+
if (reassembled.leadingText.value.length > 0) {
|
|
1387
|
+
elements.push(
|
|
1388
|
+
...emitTextWithMentionsAndEmoji(reassembled.leadingText, activeStyle(), ctx)
|
|
1389
|
+
);
|
|
1390
|
+
}
|
|
1391
|
+
elements.push(reassembled.link);
|
|
1392
|
+
if (reassembled.trailingText.value.length > 0) {
|
|
1393
|
+
work.splice(i, 3, reassembled.trailingText);
|
|
1394
|
+
} else {
|
|
1395
|
+
work.splice(i, 3);
|
|
1396
|
+
}
|
|
1397
|
+
i -= 1;
|
|
1398
|
+
continue;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1317
1401
|
if (pendingAnchor !== void 0) {
|
|
1318
1402
|
if (node.type === "html" && /^<\/a\s*>$/i.test(node.value.trim())) {
|
|
1319
1403
|
flushPendingAnchor();
|
|
@@ -1332,32 +1416,54 @@ function emitPhrasing(nodes, ctx, baseStyle = {}) {
|
|
|
1332
1416
|
}
|
|
1333
1417
|
if (node.type === "html") {
|
|
1334
1418
|
const trimmed = node.value.trim();
|
|
1419
|
+
const entityInlines = slackEntityRichTextInlines(trimmed, activeStyle());
|
|
1420
|
+
if (entityInlines !== void 0) {
|
|
1421
|
+
elements.push(...entityInlines);
|
|
1422
|
+
continue;
|
|
1423
|
+
}
|
|
1335
1424
|
const openOnly = /^<a\b([^>]{0,500})>\s*$/i.exec(trimmed);
|
|
1336
1425
|
if (openOnly !== null) {
|
|
1337
1426
|
const hrefMatch = /href\s*=\s*["']([^"']{0,3000})["']/i.exec(openOnly[1] ?? "");
|
|
1338
1427
|
const href = hrefMatch?.[1];
|
|
1339
|
-
if (href !== void 0
|
|
1340
|
-
pendingAnchor = {
|
|
1428
|
+
if (href !== void 0) {
|
|
1429
|
+
pendingAnchor = {
|
|
1430
|
+
href,
|
|
1431
|
+
style: activeStyle(),
|
|
1432
|
+
textParts: [],
|
|
1433
|
+
valid: isValidLinkUrl(href)
|
|
1434
|
+
};
|
|
1341
1435
|
continue;
|
|
1342
1436
|
}
|
|
1343
1437
|
}
|
|
1344
|
-
|
|
1438
|
+
const openTag = HTML_STYLE_OPEN_RE.exec(trimmed);
|
|
1439
|
+
if (openTag !== null) {
|
|
1440
|
+
htmlStyles.push(htmlTagStyle(activeStyle(), openTag[1] ?? ""));
|
|
1441
|
+
continue;
|
|
1442
|
+
}
|
|
1443
|
+
if (HTML_STYLE_CLOSE_RE.test(trimmed)) {
|
|
1444
|
+
htmlStyles.pop();
|
|
1445
|
+
continue;
|
|
1446
|
+
}
|
|
1447
|
+
elements.push(...emitInlineHtml(node.value, activeStyle(), ctx));
|
|
1345
1448
|
continue;
|
|
1346
1449
|
}
|
|
1347
1450
|
switch (node.type) {
|
|
1348
1451
|
case "text": {
|
|
1349
|
-
elements.push(...emitTextWithMentionsAndEmoji(node,
|
|
1452
|
+
elements.push(...emitTextWithMentionsAndEmoji(node, activeStyle(), ctx));
|
|
1350
1453
|
break;
|
|
1351
1454
|
}
|
|
1352
1455
|
case "strong": {
|
|
1353
|
-
const inner = emitPhrasing(node.children, ctx, {
|
|
1456
|
+
const inner = emitPhrasing(node.children, ctx, {
|
|
1457
|
+
...activeStyle(),
|
|
1458
|
+
bold: true
|
|
1459
|
+
});
|
|
1354
1460
|
elements.push(...inner.elements);
|
|
1355
1461
|
hoistedImages.push(...inner.hoistedImages);
|
|
1356
1462
|
break;
|
|
1357
1463
|
}
|
|
1358
1464
|
case "emphasis": {
|
|
1359
1465
|
const inner = emitPhrasing(node.children, ctx, {
|
|
1360
|
-
...
|
|
1466
|
+
...activeStyle(),
|
|
1361
1467
|
italic: true
|
|
1362
1468
|
});
|
|
1363
1469
|
elements.push(...inner.elements);
|
|
@@ -1365,7 +1471,7 @@ function emitPhrasing(nodes, ctx, baseStyle = {}) {
|
|
|
1365
1471
|
break;
|
|
1366
1472
|
}
|
|
1367
1473
|
case "delete": {
|
|
1368
|
-
const inner = emitPhrasing(node.children, ctx, { ...
|
|
1474
|
+
const inner = emitPhrasing(node.children, ctx, { ...activeStyle(), strike: true });
|
|
1369
1475
|
elements.push(...inner.elements);
|
|
1370
1476
|
hoistedImages.push(...inner.hoistedImages);
|
|
1371
1477
|
break;
|
|
@@ -1374,21 +1480,21 @@ function emitPhrasing(nodes, ctx, baseStyle = {}) {
|
|
|
1374
1480
|
const el = {
|
|
1375
1481
|
type: "text",
|
|
1376
1482
|
text: node.value,
|
|
1377
|
-
style: styleOrUndefined({ ...
|
|
1483
|
+
style: styleOrUndefined({ ...activeStyle(), code: true })
|
|
1378
1484
|
};
|
|
1379
1485
|
elements.push(el);
|
|
1380
1486
|
break;
|
|
1381
1487
|
}
|
|
1382
1488
|
case "break": {
|
|
1383
|
-
elements.push({ type: "text", text: "\n", style: styleOrUndefined(
|
|
1489
|
+
elements.push({ type: "text", text: "\n", style: styleOrUndefined(activeStyle()) });
|
|
1384
1490
|
break;
|
|
1385
1491
|
}
|
|
1386
1492
|
case "link": {
|
|
1387
|
-
elements.push(...emitLink(node, ctx,
|
|
1493
|
+
elements.push(...emitLink(node, ctx, activeStyle()));
|
|
1388
1494
|
break;
|
|
1389
1495
|
}
|
|
1390
1496
|
case "linkReference": {
|
|
1391
|
-
elements.push(...emitLinkReference(node, ctx,
|
|
1497
|
+
elements.push(...emitLinkReference(node, ctx, activeStyle()));
|
|
1392
1498
|
break;
|
|
1393
1499
|
}
|
|
1394
1500
|
case "image": {
|
|
@@ -1403,22 +1509,33 @@ function emitPhrasing(nodes, ctx, baseStyle = {}) {
|
|
|
1403
1509
|
type: "link",
|
|
1404
1510
|
url,
|
|
1405
1511
|
text: node.alt && node.alt.length > 0 ? node.alt : url,
|
|
1406
|
-
style: styleOrUndefined(
|
|
1512
|
+
style: styleOrUndefined(activeStyle())
|
|
1407
1513
|
});
|
|
1408
1514
|
} else {
|
|
1515
|
+
const linkable = isValidLinkUrl(url);
|
|
1409
1516
|
ctx.degradations.add({
|
|
1410
1517
|
nodeType: "image",
|
|
1411
1518
|
from: "image",
|
|
1412
|
-
to: "link",
|
|
1519
|
+
to: linkable ? "link" : "text",
|
|
1413
1520
|
reason: "Image URL is not a public http(s) png/jpg/gif \u22643000 chars",
|
|
1414
1521
|
path: ctx.path
|
|
1415
1522
|
});
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1523
|
+
const label = node.alt && node.alt.length > 0 ? node.alt : url.length > 0 ? url : "image";
|
|
1524
|
+
if (linkable) {
|
|
1525
|
+
elements.push({
|
|
1526
|
+
type: "link",
|
|
1527
|
+
url,
|
|
1528
|
+
text: label,
|
|
1529
|
+
style: styleOrUndefined(activeStyle())
|
|
1530
|
+
});
|
|
1531
|
+
} else {
|
|
1532
|
+
const suffix = url.length > 0 && url !== label ? ` (${url})` : "";
|
|
1533
|
+
elements.push({
|
|
1534
|
+
type: "text",
|
|
1535
|
+
text: `${label}${suffix}`,
|
|
1536
|
+
style: styleOrUndefined(activeStyle())
|
|
1537
|
+
});
|
|
1538
|
+
}
|
|
1422
1539
|
}
|
|
1423
1540
|
break;
|
|
1424
1541
|
}
|
|
@@ -1428,7 +1545,7 @@ function emitPhrasing(nodes, ctx, baseStyle = {}) {
|
|
|
1428
1545
|
elements.push({
|
|
1429
1546
|
type: "text",
|
|
1430
1547
|
text: node.alt ?? node.identifier,
|
|
1431
|
-
style: styleOrUndefined(
|
|
1548
|
+
style: styleOrUndefined(activeStyle())
|
|
1432
1549
|
});
|
|
1433
1550
|
break;
|
|
1434
1551
|
}
|
|
@@ -1442,7 +1559,7 @@ function emitPhrasing(nodes, ctx, baseStyle = {}) {
|
|
|
1442
1559
|
}
|
|
1443
1560
|
],
|
|
1444
1561
|
ctx,
|
|
1445
|
-
|
|
1562
|
+
activeStyle()
|
|
1446
1563
|
);
|
|
1447
1564
|
elements.push(...asImage.elements);
|
|
1448
1565
|
hoistedImages.push(...asImage.hoistedImages);
|
|
@@ -1454,7 +1571,7 @@ function emitPhrasing(nodes, ctx, baseStyle = {}) {
|
|
|
1454
1571
|
elements.push({
|
|
1455
1572
|
type: "text",
|
|
1456
1573
|
text: `[${String(n)}]`,
|
|
1457
|
-
style: styleOrUndefined(
|
|
1574
|
+
style: styleOrUndefined(activeStyle())
|
|
1458
1575
|
});
|
|
1459
1576
|
break;
|
|
1460
1577
|
}
|
|
@@ -1462,7 +1579,7 @@ function emitPhrasing(nodes, ctx, baseStyle = {}) {
|
|
|
1462
1579
|
elements.push({
|
|
1463
1580
|
type: "text",
|
|
1464
1581
|
text: node.value,
|
|
1465
|
-
style: styleOrUndefined({ ...
|
|
1582
|
+
style: styleOrUndefined({ ...activeStyle(), code: true })
|
|
1466
1583
|
});
|
|
1467
1584
|
ctx.degradations.add({
|
|
1468
1585
|
nodeType: "inlineMath",
|
|
@@ -1476,7 +1593,7 @@ function emitPhrasing(nodes, ctx, baseStyle = {}) {
|
|
|
1476
1593
|
default: {
|
|
1477
1594
|
const text = toString2(node);
|
|
1478
1595
|
if (text.length > 0) {
|
|
1479
|
-
elements.push({ type: "text", text, style: styleOrUndefined(
|
|
1596
|
+
elements.push({ type: "text", text, style: styleOrUndefined(activeStyle()) });
|
|
1480
1597
|
}
|
|
1481
1598
|
break;
|
|
1482
1599
|
}
|
|
@@ -1545,7 +1662,7 @@ function truncateLabel(label, max) {
|
|
|
1545
1662
|
if (label.length <= max) {
|
|
1546
1663
|
return { text: label, truncated: false };
|
|
1547
1664
|
}
|
|
1548
|
-
return { text: `${label
|
|
1665
|
+
return { text: `${sliceSurrogateSafe(label, Math.max(0, max - 1))}${ELLIPSIS}`, truncated: true };
|
|
1549
1666
|
}
|
|
1550
1667
|
function hoistedImageBlock(h, ctx) {
|
|
1551
1668
|
return {
|
|
@@ -1566,7 +1683,7 @@ function clampSectionText(text, ctx, nodeType) {
|
|
|
1566
1683
|
reason: "Section text truncated to 3000 chars",
|
|
1567
1684
|
path: ctx.path
|
|
1568
1685
|
});
|
|
1569
|
-
return text
|
|
1686
|
+
return sliceSurrogateSafe(text, SECTION_TEXT_MAX_CHARS);
|
|
1570
1687
|
}
|
|
1571
1688
|
function preformattedRichText(text, language) {
|
|
1572
1689
|
return {
|
|
@@ -1592,7 +1709,7 @@ function clampImageText(value, ctx, field) {
|
|
|
1592
1709
|
reason: `Image ${field} truncated to ${String(IMAGE_ALT_MAX_CHARS)} chars`,
|
|
1593
1710
|
path: ctx.path
|
|
1594
1711
|
});
|
|
1595
|
-
return value
|
|
1712
|
+
return sliceSurrogateSafe(value, IMAGE_ALT_MAX_CHARS);
|
|
1596
1713
|
}
|
|
1597
1714
|
function styleOrUndefined(style) {
|
|
1598
1715
|
if (style.bold === true || style.italic === true || style.strike === true || style.code === true || style.underline === true) {
|
|
@@ -1600,7 +1717,94 @@ function styleOrUndefined(style) {
|
|
|
1600
1717
|
}
|
|
1601
1718
|
return void 0;
|
|
1602
1719
|
}
|
|
1603
|
-
var
|
|
1720
|
+
var SLACK_ENTITY_CANDIDATE_RE = /<([@#!][^<>]{1,340}|(?:https?:|mailto:)[^<>|]{1,3000}\|[^<>]{1,300})>/gi;
|
|
1721
|
+
var SLACK_USER_RE = /^@([UW][A-Z0-9]{1,60})(?:\|[^>]{0,300})?$/;
|
|
1722
|
+
var SLACK_CHANNEL_RE = /^#([CDG][A-Z0-9]{1,60})(?:\|[^>]{0,300})?$/;
|
|
1723
|
+
var SLACK_USERGROUP_RE = /^!subteam\^(S[A-Z0-9]{1,60})(?:\|[^>]{0,300})?$/;
|
|
1724
|
+
var SLACK_BROADCAST_RE = /^!(here|channel|everyone)(?:\|[^>]{0,300})?$/;
|
|
1725
|
+
var SLACK_DATE_RE = /^!date\^(\d{1,13})\^([^^|]{1,300})(?:\^([^^|]{1,3000}))?(?:\|([^]{0,300}))?$/;
|
|
1726
|
+
var SLACK_LINK_RE = /^((?:https?:|mailto:)[^|]{1,3000})\|([^]{1,300})$/i;
|
|
1727
|
+
function slackEntityElement(inner, style) {
|
|
1728
|
+
const labeledLink = SLACK_LINK_RE.exec(inner);
|
|
1729
|
+
if (labeledLink?.[1] !== void 0 && labeledLink[2] !== void 0) {
|
|
1730
|
+
if (!isValidLinkUrl(labeledLink[1])) {
|
|
1731
|
+
return void 0;
|
|
1732
|
+
}
|
|
1733
|
+
return {
|
|
1734
|
+
type: "link",
|
|
1735
|
+
url: labeledLink[1],
|
|
1736
|
+
text: labeledLink[2],
|
|
1737
|
+
style: styleOrUndefined(style)
|
|
1738
|
+
};
|
|
1739
|
+
}
|
|
1740
|
+
const user = SLACK_USER_RE.exec(inner);
|
|
1741
|
+
if (user?.[1] !== void 0) {
|
|
1742
|
+
return { type: "user", user_id: user[1], style: styleOrUndefined(style) };
|
|
1743
|
+
}
|
|
1744
|
+
const channel = SLACK_CHANNEL_RE.exec(inner);
|
|
1745
|
+
if (channel?.[1] !== void 0) {
|
|
1746
|
+
return { type: "channel", channel_id: channel[1], style: styleOrUndefined(style) };
|
|
1747
|
+
}
|
|
1748
|
+
const usergroup = SLACK_USERGROUP_RE.exec(inner);
|
|
1749
|
+
if (usergroup?.[1] !== void 0) {
|
|
1750
|
+
return { type: "usergroup", usergroup_id: usergroup[1], style: styleOrUndefined(style) };
|
|
1751
|
+
}
|
|
1752
|
+
const broadcast = SLACK_BROADCAST_RE.exec(inner);
|
|
1753
|
+
if (broadcast !== null) {
|
|
1754
|
+
const range = broadcast[1];
|
|
1755
|
+
if (range === "here" || range === "channel" || range === "everyone") {
|
|
1756
|
+
return { type: "broadcast", range, style: styleOrUndefined(style) };
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
const date = SLACK_DATE_RE.exec(inner);
|
|
1760
|
+
if (date?.[1] !== void 0 && date[2] !== void 0) {
|
|
1761
|
+
const timestamp = Number(date[1]);
|
|
1762
|
+
if (Number.isSafeInteger(timestamp)) {
|
|
1763
|
+
return {
|
|
1764
|
+
type: "date",
|
|
1765
|
+
timestamp,
|
|
1766
|
+
format: date[2],
|
|
1767
|
+
url: date[3],
|
|
1768
|
+
fallback: date[4],
|
|
1769
|
+
style: styleOrUndefined(style)
|
|
1770
|
+
};
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
return void 0;
|
|
1774
|
+
}
|
|
1775
|
+
function slackEntitySplit(value, style, emit) {
|
|
1776
|
+
SLACK_ENTITY_CANDIDATE_RE.lastIndex = 0;
|
|
1777
|
+
let match = SLACK_ENTITY_CANDIDATE_RE.exec(value);
|
|
1778
|
+
if (match === null) {
|
|
1779
|
+
return void 0;
|
|
1780
|
+
}
|
|
1781
|
+
const out = [];
|
|
1782
|
+
let last = 0;
|
|
1783
|
+
let sawEntity = false;
|
|
1784
|
+
while (match !== null) {
|
|
1785
|
+
const element = slackEntityElement(match[1] ?? "", style);
|
|
1786
|
+
if (element !== void 0) {
|
|
1787
|
+
sawEntity = true;
|
|
1788
|
+
if (match.index > last) {
|
|
1789
|
+
out.push(...emit(value.slice(last, match.index)));
|
|
1790
|
+
}
|
|
1791
|
+
out.push(element);
|
|
1792
|
+
last = match.index + match[0].length;
|
|
1793
|
+
}
|
|
1794
|
+
match = SLACK_ENTITY_CANDIDATE_RE.exec(value);
|
|
1795
|
+
}
|
|
1796
|
+
if (!sawEntity) {
|
|
1797
|
+
return void 0;
|
|
1798
|
+
}
|
|
1799
|
+
if (last < value.length) {
|
|
1800
|
+
out.push(...emit(value.slice(last)));
|
|
1801
|
+
}
|
|
1802
|
+
return out;
|
|
1803
|
+
}
|
|
1804
|
+
function slackEntityRichTextInlines(value, style) {
|
|
1805
|
+
return slackEntitySplit(value, style, (slice) => emitTextWithEmoji(slice, style));
|
|
1806
|
+
}
|
|
1807
|
+
var MENTION_RE = /(?<![A-Za-z0-9._-])@([a-zA-Z0-9_-](?:[a-zA-Z0-9._-]{0,62}[a-zA-Z0-9_-])?)|(?<![A-Za-z0-9_-])#([a-zA-Z0-9_-]{1,80})/g;
|
|
1604
1808
|
function phrasingNeedsMentionResolution(nodes, ctx) {
|
|
1605
1809
|
const resolvers = ctx.config.mentionResolvers;
|
|
1606
1810
|
if (resolvers?.resolveUser === void 0 && resolvers?.resolveChannel === void 0) {
|
|
@@ -1611,13 +1815,23 @@ function phrasingNeedsMentionResolution(nodes, ctx) {
|
|
|
1611
1815
|
return MENTION_RE.test(plain);
|
|
1612
1816
|
}
|
|
1613
1817
|
function emitTextWithMentionsAndEmoji(node, style, ctx) {
|
|
1818
|
+
const viaEntities = slackEntitySplit(
|
|
1819
|
+
node.value,
|
|
1820
|
+
style,
|
|
1821
|
+
(slice) => emitMentionsAndEmoji(slice, style, ctx)
|
|
1822
|
+
);
|
|
1823
|
+
if (viaEntities !== void 0) {
|
|
1824
|
+
return viaEntities;
|
|
1825
|
+
}
|
|
1826
|
+
return emitMentionsAndEmoji(node.value, style, ctx);
|
|
1827
|
+
}
|
|
1828
|
+
function emitMentionsAndEmoji(value, style, ctx) {
|
|
1614
1829
|
const resolvers = ctx.config.mentionResolvers;
|
|
1615
1830
|
const canUser = resolvers?.resolveUser !== void 0;
|
|
1616
1831
|
const canChannel = resolvers?.resolveChannel !== void 0;
|
|
1617
1832
|
if (!canUser && !canChannel) {
|
|
1618
|
-
return emitTextWithEmoji(
|
|
1833
|
+
return emitTextWithEmoji(value, style);
|
|
1619
1834
|
}
|
|
1620
|
-
const value = node.value;
|
|
1621
1835
|
const out = [];
|
|
1622
1836
|
MENTION_RE.lastIndex = 0;
|
|
1623
1837
|
let last = 0;
|
|
@@ -1667,13 +1881,8 @@ function emitTextWithEmoji(value, style) {
|
|
|
1667
1881
|
if (start > last) {
|
|
1668
1882
|
out.push({ type: "text", text: value.slice(last, start), style: styleOrUndefined(style) });
|
|
1669
1883
|
}
|
|
1670
|
-
const
|
|
1671
|
-
|
|
1672
|
-
const emoji = { type: "emoji", name };
|
|
1673
|
-
out.push(emoji);
|
|
1674
|
-
} else {
|
|
1675
|
-
out.push({ type: "text", text: match[0], style: styleOrUndefined(style) });
|
|
1676
|
-
}
|
|
1884
|
+
const emoji = { type: "emoji", name: match[1] ?? "" };
|
|
1885
|
+
out.push(emoji);
|
|
1677
1886
|
last = start + match[0].length;
|
|
1678
1887
|
match = EMOJI_SHORTCODE_RE.exec(value);
|
|
1679
1888
|
}
|
|
@@ -1685,9 +1894,53 @@ function emitTextWithEmoji(value, style) {
|
|
|
1685
1894
|
}
|
|
1686
1895
|
return out;
|
|
1687
1896
|
}
|
|
1897
|
+
function reassembleSlackLabeledLink(work, i, style) {
|
|
1898
|
+
const lead = work[i];
|
|
1899
|
+
const linkNode = work[i + 1];
|
|
1900
|
+
const tail = work[i + 2];
|
|
1901
|
+
if (lead?.type !== "text" || !lead.value.endsWith("<")) {
|
|
1902
|
+
return void 0;
|
|
1903
|
+
}
|
|
1904
|
+
if (linkNode?.type !== "link" || tail?.type !== "text") {
|
|
1905
|
+
return void 0;
|
|
1906
|
+
}
|
|
1907
|
+
const url = linkNode.url;
|
|
1908
|
+
const pipeIndex = url.indexOf("|");
|
|
1909
|
+
if (pipeIndex <= 0 || !HTTP_URL_RE.test(url)) {
|
|
1910
|
+
return void 0;
|
|
1911
|
+
}
|
|
1912
|
+
if (phrasingToPlain(linkNode.children) !== url) {
|
|
1913
|
+
return void 0;
|
|
1914
|
+
}
|
|
1915
|
+
const closeIndex = tail.value.indexOf(">");
|
|
1916
|
+
if (closeIndex === -1) {
|
|
1917
|
+
return void 0;
|
|
1918
|
+
}
|
|
1919
|
+
const realUrl = url.slice(0, pipeIndex);
|
|
1920
|
+
if (!isValidLinkUrl(realUrl)) {
|
|
1921
|
+
return void 0;
|
|
1922
|
+
}
|
|
1923
|
+
const label = `${url.slice(pipeIndex + 1)}${tail.value.slice(0, closeIndex)}`;
|
|
1924
|
+
if (label.length === 0) {
|
|
1925
|
+
return void 0;
|
|
1926
|
+
}
|
|
1927
|
+
return {
|
|
1928
|
+
leadingText: { type: "text", value: lead.value.slice(0, -1) },
|
|
1929
|
+
link: { type: "link", url: realUrl, text: label, style: styleOrUndefined(style) },
|
|
1930
|
+
trailingText: { type: "text", value: tail.value.slice(closeIndex + 1) }
|
|
1931
|
+
};
|
|
1932
|
+
}
|
|
1688
1933
|
function emitLink(node, ctx, style) {
|
|
1689
1934
|
const url = node.url;
|
|
1690
1935
|
const text = phrasingToPlain(node.children);
|
|
1936
|
+
const pipeIndex = url.indexOf("|");
|
|
1937
|
+
if (pipeIndex > 0 && HTTP_URL_RE.test(url) && text === url) {
|
|
1938
|
+
const realUrl = url.slice(0, pipeIndex);
|
|
1939
|
+
const label = url.slice(pipeIndex + 1);
|
|
1940
|
+
if (label.length > 0 && isValidLinkUrl(realUrl)) {
|
|
1941
|
+
return [{ type: "link", url: realUrl, text: label, style: styleOrUndefined(style) }];
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1691
1944
|
if (!isValidLinkUrl(url)) {
|
|
1692
1945
|
ctx.degradations.add({
|
|
1693
1946
|
nodeType: "link",
|
|
@@ -1696,7 +1949,7 @@ function emitLink(node, ctx, style) {
|
|
|
1696
1949
|
reason: "Invalid or oversized URL",
|
|
1697
1950
|
path: ctx.path
|
|
1698
1951
|
});
|
|
1699
|
-
const suffix = url.length > 0 ? ` (${url})` : "";
|
|
1952
|
+
const suffix = url.length > 0 && url !== text ? ` (${url})` : "";
|
|
1700
1953
|
return [{ type: "text", text: `${text}${suffix}`, style: styleOrUndefined(style) }];
|
|
1701
1954
|
}
|
|
1702
1955
|
const link = {
|
|
@@ -1733,9 +1986,45 @@ function isValidLinkUrl(url) {
|
|
|
1733
1986
|
if (url.length === 0 || url.length > IMAGE_URL_MAX_CHARS) {
|
|
1734
1987
|
return false;
|
|
1735
1988
|
}
|
|
1736
|
-
return HTTP_URL_RE.test(url)
|
|
1989
|
+
return HTTP_URL_RE.test(url);
|
|
1990
|
+
}
|
|
1991
|
+
function anchorInline(anchor, ctx) {
|
|
1992
|
+
const label = anchor.textParts.join("");
|
|
1993
|
+
if (anchor.valid) {
|
|
1994
|
+
return {
|
|
1995
|
+
type: "link",
|
|
1996
|
+
url: anchor.href,
|
|
1997
|
+
text: label.length > 0 ? label : anchor.href,
|
|
1998
|
+
style: styleOrUndefined(anchor.style)
|
|
1999
|
+
};
|
|
2000
|
+
}
|
|
2001
|
+
ctx.degradations.add({
|
|
2002
|
+
nodeType: "link",
|
|
2003
|
+
from: "link",
|
|
2004
|
+
to: "text",
|
|
2005
|
+
reason: "Invalid or oversized URL",
|
|
2006
|
+
path: ctx.path
|
|
2007
|
+
});
|
|
2008
|
+
const base = label.length > 0 ? label : anchor.href;
|
|
2009
|
+
const suffix = label.length > 0 && anchor.href.length > 0 && anchor.href !== label ? ` (${anchor.href})` : "";
|
|
2010
|
+
return { type: "text", text: `${base}${suffix}`, style: styleOrUndefined(anchor.style) };
|
|
2011
|
+
}
|
|
2012
|
+
var HTML_STYLE_OPEN_RE = /^<(b|strong|i|em|s|del|code)(\s+[^>]{0,200})?>$/i;
|
|
2013
|
+
var HTML_STYLE_CLOSE_RE = /^<\/(b|strong|i|em|s|del|code)\s*>$/i;
|
|
2014
|
+
function htmlTagStyle(base, tag) {
|
|
2015
|
+
const t = tag.toLowerCase();
|
|
2016
|
+
if (t === "b" || t === "strong") {
|
|
2017
|
+
return { ...base, bold: true };
|
|
2018
|
+
}
|
|
2019
|
+
if (t === "i" || t === "em") {
|
|
2020
|
+
return { ...base, italic: true };
|
|
2021
|
+
}
|
|
2022
|
+
if (t === "s" || t === "del") {
|
|
2023
|
+
return { ...base, strike: true };
|
|
2024
|
+
}
|
|
2025
|
+
return { ...base, code: true };
|
|
1737
2026
|
}
|
|
1738
|
-
var INLINE_TAG_RE = /<\/?(b|strong|i|em|s|del|code|br|a)(\s+[^>]{0,200})
|
|
2027
|
+
var INLINE_TAG_RE = /<\/?(b|strong|i|em|s|del|code|br|a)(\s+[^>]{0,200})?\s*\/?>|<[^>]{0,200}>|[^<]+|</gi;
|
|
1739
2028
|
function emitInlineHtml(html, style, ctx) {
|
|
1740
2029
|
const out = [];
|
|
1741
2030
|
const stack = [];
|
|
@@ -1743,11 +2032,15 @@ function emitInlineHtml(html, style, ctx) {
|
|
|
1743
2032
|
let openAnchor;
|
|
1744
2033
|
INLINE_TAG_RE.lastIndex = 0;
|
|
1745
2034
|
let m = INLINE_TAG_RE.exec(html);
|
|
1746
|
-
let
|
|
2035
|
+
let strippedUnknown = false;
|
|
1747
2036
|
while (m !== null) {
|
|
1748
|
-
matched = true;
|
|
1749
2037
|
const token = m[0];
|
|
1750
|
-
if (token.startsWith("<")) {
|
|
2038
|
+
if (token.startsWith("<") && token.length > 1 && m[1] === void 0) {
|
|
2039
|
+
strippedUnknown = true;
|
|
2040
|
+
m = INLINE_TAG_RE.exec(html);
|
|
2041
|
+
continue;
|
|
2042
|
+
}
|
|
2043
|
+
if (token.startsWith("<") && m[1] !== void 0) {
|
|
1751
2044
|
const tag = (m[1] ?? "").toLowerCase();
|
|
1752
2045
|
const closing = token.startsWith("</");
|
|
1753
2046
|
if (tag === "br") {
|
|
@@ -1758,13 +2051,7 @@ function emitInlineHtml(html, style, ctx) {
|
|
|
1758
2051
|
}
|
|
1759
2052
|
} else if (closing) {
|
|
1760
2053
|
if (tag === "a" && openAnchor !== void 0) {
|
|
1761
|
-
|
|
1762
|
-
out.push({
|
|
1763
|
-
type: "link",
|
|
1764
|
-
url: openAnchor.href,
|
|
1765
|
-
text: label.length > 0 ? label : openAnchor.href,
|
|
1766
|
-
style: styleOrUndefined(openAnchor.style)
|
|
1767
|
-
});
|
|
2054
|
+
out.push(anchorInline(openAnchor, ctx));
|
|
1768
2055
|
openAnchor = void 0;
|
|
1769
2056
|
}
|
|
1770
2057
|
stack.pop();
|
|
@@ -1773,8 +2060,11 @@ function emitInlineHtml(html, style, ctx) {
|
|
|
1773
2060
|
const hrefMatch = /href\s*=\s*["']([^"']{0,3000})["']/i.exec(token);
|
|
1774
2061
|
const href = hrefMatch?.[1];
|
|
1775
2062
|
stack.push({ tag, style: current });
|
|
1776
|
-
if (href !== void 0
|
|
1777
|
-
openAnchor
|
|
2063
|
+
if (href !== void 0) {
|
|
2064
|
+
if (openAnchor !== void 0) {
|
|
2065
|
+
out.push(anchorInline(openAnchor, ctx));
|
|
2066
|
+
}
|
|
2067
|
+
openAnchor = { href, style: current, textParts: [], valid: isValidLinkUrl(href) };
|
|
1778
2068
|
}
|
|
1779
2069
|
} else {
|
|
1780
2070
|
const next = { ...current };
|
|
@@ -1795,15 +2085,9 @@ function emitInlineHtml(html, style, ctx) {
|
|
|
1795
2085
|
m = INLINE_TAG_RE.exec(html);
|
|
1796
2086
|
}
|
|
1797
2087
|
if (openAnchor !== void 0) {
|
|
1798
|
-
|
|
1799
|
-
out.push({
|
|
1800
|
-
type: "link",
|
|
1801
|
-
url: openAnchor.href,
|
|
1802
|
-
text: label.length > 0 ? label : openAnchor.href,
|
|
1803
|
-
style: styleOrUndefined(openAnchor.style)
|
|
1804
|
-
});
|
|
2088
|
+
out.push(anchorInline(openAnchor, ctx));
|
|
1805
2089
|
}
|
|
1806
|
-
if (
|
|
2090
|
+
if (strippedUnknown) {
|
|
1807
2091
|
ctx.degradations.add({
|
|
1808
2092
|
nodeType: "html",
|
|
1809
2093
|
from: "html",
|
|
@@ -1811,10 +2095,6 @@ function emitInlineHtml(html, style, ctx) {
|
|
|
1811
2095
|
reason: "Unrecognized inline HTML stripped to text",
|
|
1812
2096
|
path: ctx.path
|
|
1813
2097
|
});
|
|
1814
|
-
const stripped = html.replace(/<[^>]{0,200}>/g, "");
|
|
1815
|
-
if (stripped.length > 0) {
|
|
1816
|
-
out.push({ type: "text", text: stripped, style: styleOrUndefined(style) });
|
|
1817
|
-
}
|
|
1818
2098
|
}
|
|
1819
2099
|
return out;
|
|
1820
2100
|
}
|
|
@@ -2046,10 +2326,13 @@ function emitPlainHeader(heading, withLevel) {
|
|
|
2046
2326
|
const block = {
|
|
2047
2327
|
type: "header",
|
|
2048
2328
|
text: { type: "plain_text", text: plain.length > 0 ? plain : " " },
|
|
2049
|
-
level: withLevel ? heading.depth : void 0
|
|
2329
|
+
level: withLevel ? headerLevelOf(heading.depth) : void 0
|
|
2050
2330
|
};
|
|
2051
2331
|
return [block];
|
|
2052
2332
|
}
|
|
2333
|
+
function headerLevelOf(depth) {
|
|
2334
|
+
return depth === 1 || depth === 2 || depth === 3 || depth === 4 ? depth : void 0;
|
|
2335
|
+
}
|
|
2053
2336
|
function emitRichHeading(heading, ctx, hoistImages) {
|
|
2054
2337
|
const plain = phrasingToPlain(heading.children);
|
|
2055
2338
|
const depth = heading.depth;
|
|
@@ -2158,6 +2441,20 @@ ${body}`, ctx, "html.details.section")
|
|
|
2158
2441
|
}
|
|
2159
2442
|
];
|
|
2160
2443
|
}
|
|
2444
|
+
const entityInlines = slackEntityRichTextInlines(html.value.trim(), {});
|
|
2445
|
+
if (entityInlines !== void 0) {
|
|
2446
|
+
return [
|
|
2447
|
+
{
|
|
2448
|
+
budget: { blocks: 1 },
|
|
2449
|
+
emit: () => [
|
|
2450
|
+
{
|
|
2451
|
+
type: "rich_text",
|
|
2452
|
+
elements: [{ type: "rich_text_section", elements: entityInlines }]
|
|
2453
|
+
}
|
|
2454
|
+
]
|
|
2455
|
+
}
|
|
2456
|
+
];
|
|
2457
|
+
}
|
|
2161
2458
|
return [
|
|
2162
2459
|
{
|
|
2163
2460
|
budget: { blocks: 1 },
|
|
@@ -2198,7 +2495,7 @@ function clampContainerTitle(text, ctx) {
|
|
|
2198
2495
|
reason: `Container title truncated to ${String(CONTAINER_TITLE_MAX_CHARS)} chars`,
|
|
2199
2496
|
path: ctx.path
|
|
2200
2497
|
});
|
|
2201
|
-
return text
|
|
2498
|
+
return sliceSurrogateSafe(text, CONTAINER_TITLE_MAX_CHARS);
|
|
2202
2499
|
}
|
|
2203
2500
|
|
|
2204
2501
|
// src/adapters/list-adapter.ts
|
|
@@ -2278,27 +2575,51 @@ function emitListTree(list, indent, ctx) {
|
|
|
2278
2575
|
});
|
|
2279
2576
|
effectiveIndent = RICH_TEXT_LIST_INDENT_MAX;
|
|
2280
2577
|
}
|
|
2281
|
-
const
|
|
2282
|
-
const
|
|
2578
|
+
const ordered = list.ordered === true;
|
|
2579
|
+
const startOffset = ordered && list.start !== null && list.start !== void 0 && list.start > 1 ? list.start - 1 : 0;
|
|
2580
|
+
const out = [];
|
|
2283
2581
|
const hoistedImages = [];
|
|
2284
|
-
|
|
2582
|
+
let pendingSections = [];
|
|
2583
|
+
const flush = (lastIndex) => {
|
|
2584
|
+
if (pendingSections.length === 0) {
|
|
2585
|
+
return;
|
|
2586
|
+
}
|
|
2587
|
+
const chunkStartIndex = lastIndex - pendingSections.length + 1;
|
|
2588
|
+
const offset = startOffset + chunkStartIndex;
|
|
2589
|
+
out.push({
|
|
2590
|
+
type: "rich_text_list",
|
|
2591
|
+
style: ordered ? "ordered" : "bullet",
|
|
2592
|
+
elements: pendingSections,
|
|
2593
|
+
indent: effectiveIndent,
|
|
2594
|
+
offset: ordered && offset > 0 ? offset : void 0
|
|
2595
|
+
});
|
|
2596
|
+
pendingSections = [];
|
|
2597
|
+
};
|
|
2598
|
+
for (const [index, item] of list.children.entries()) {
|
|
2285
2599
|
const { section, childLists, hoistedImages: itemImages } = emitListItem(item, ctx);
|
|
2286
|
-
|
|
2600
|
+
pendingSections.push(section);
|
|
2287
2601
|
hoistedImages.push(...itemImages);
|
|
2602
|
+
if (childLists.length === 0) {
|
|
2603
|
+
continue;
|
|
2604
|
+
}
|
|
2605
|
+
flush(index);
|
|
2288
2606
|
for (const child of childLists) {
|
|
2289
2607
|
const nestedResult = emitListTree(child, effectiveIndent + 1, ctx);
|
|
2290
|
-
|
|
2608
|
+
out.push(...nestedResult.elements);
|
|
2291
2609
|
hoistedImages.push(...nestedResult.hoistedImages);
|
|
2292
2610
|
}
|
|
2293
2611
|
}
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2612
|
+
flush(list.children.length - 1);
|
|
2613
|
+
if (out.length === 0) {
|
|
2614
|
+
out.push({
|
|
2615
|
+
type: "rich_text_list",
|
|
2616
|
+
style: ordered ? "ordered" : "bullet",
|
|
2617
|
+
elements: [{ type: "rich_text_section", elements: [{ type: "text", text: " " }] }],
|
|
2618
|
+
indent: effectiveIndent,
|
|
2619
|
+
offset: void 0
|
|
2620
|
+
});
|
|
2621
|
+
}
|
|
2622
|
+
return { elements: out, hoistedImages };
|
|
2302
2623
|
}
|
|
2303
2624
|
function collectListItemPhrasing(item) {
|
|
2304
2625
|
const phrasing = [];
|
|
@@ -2615,7 +2936,19 @@ async function tryRenderers(source, ctx) {
|
|
|
2615
2936
|
if (!renderer.canRender(req)) {
|
|
2616
2937
|
continue;
|
|
2617
2938
|
}
|
|
2618
|
-
|
|
2939
|
+
let result;
|
|
2940
|
+
try {
|
|
2941
|
+
result = await renderer.render(req);
|
|
2942
|
+
} catch (err) {
|
|
2943
|
+
ctx.degradations.add({
|
|
2944
|
+
nodeType: "code.mermaid",
|
|
2945
|
+
from: "image.render",
|
|
2946
|
+
to: "skip",
|
|
2947
|
+
reason: `Renderer failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
2948
|
+
path: ctx.path
|
|
2949
|
+
});
|
|
2950
|
+
continue;
|
|
2951
|
+
}
|
|
2619
2952
|
if (result === null) {
|
|
2620
2953
|
continue;
|
|
2621
2954
|
}
|
|
@@ -2647,7 +2980,19 @@ async function tryRenderers(source, ctx) {
|
|
|
2647
2980
|
});
|
|
2648
2981
|
continue;
|
|
2649
2982
|
}
|
|
2650
|
-
|
|
2983
|
+
let uploaded;
|
|
2984
|
+
try {
|
|
2985
|
+
uploaded = await uploader.upload(result);
|
|
2986
|
+
} catch (err) {
|
|
2987
|
+
ctx.degradations.add({
|
|
2988
|
+
nodeType: "code.mermaid",
|
|
2989
|
+
from: "image.bytes",
|
|
2990
|
+
to: "skip",
|
|
2991
|
+
reason: `Slack upload failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
2992
|
+
path: ctx.path
|
|
2993
|
+
});
|
|
2994
|
+
continue;
|
|
2995
|
+
}
|
|
2651
2996
|
return {
|
|
2652
2997
|
type: "image",
|
|
2653
2998
|
alt_text: clampImageText(result.altText, ctx, "alt_text"),
|
|
@@ -2710,11 +3055,11 @@ function emitParagraph(paragraph, emitCtx, loneImage, hoistImages) {
|
|
|
2710
3055
|
const img = loneImage;
|
|
2711
3056
|
if (isPublicImageUrl(img.url)) {
|
|
2712
3057
|
const altRaw = img.alt ?? "image";
|
|
2713
|
-
const
|
|
3058
|
+
const alt2 = clampImageText(altRaw.length > 0 ? altRaw : "image", emitCtx, "alt_text");
|
|
2714
3059
|
const block = {
|
|
2715
3060
|
type: "image",
|
|
2716
3061
|
image_url: img.url,
|
|
2717
|
-
alt_text:
|
|
3062
|
+
alt_text: alt2,
|
|
2718
3063
|
title: img.title !== null && img.title !== void 0 && img.title.length > 0 ? {
|
|
2719
3064
|
type: "plain_text",
|
|
2720
3065
|
text: clampImageText(img.title, emitCtx, "title")
|
|
@@ -2722,20 +3067,23 @@ function emitParagraph(paragraph, emitCtx, loneImage, hoistImages) {
|
|
|
2722
3067
|
};
|
|
2723
3068
|
return [block];
|
|
2724
3069
|
}
|
|
3070
|
+
const linkable = isValidLinkUrl(img.url);
|
|
2725
3071
|
emitCtx.degradations.add({
|
|
2726
3072
|
nodeType: "image",
|
|
2727
3073
|
from: "image",
|
|
2728
|
-
to: "section.link",
|
|
3074
|
+
to: linkable ? "section.link" : "section.text",
|
|
2729
3075
|
reason: "Non-public or invalid image URL",
|
|
2730
3076
|
path: emitCtx.path
|
|
2731
3077
|
});
|
|
2732
|
-
const
|
|
3078
|
+
const alt = img.alt ?? "";
|
|
3079
|
+
const label = alt.length > 0 ? alt : img.url.length > 0 ? img.url : "image";
|
|
3080
|
+
const fallbackText = linkable ? `<${img.url}|${label}>` : img.url.length > 0 && img.url !== label ? `${label} (${img.url})` : label;
|
|
2733
3081
|
return [
|
|
2734
3082
|
{
|
|
2735
3083
|
type: "section",
|
|
2736
3084
|
text: {
|
|
2737
3085
|
type: "mrkdwn",
|
|
2738
|
-
text: clampSectionText(
|
|
3086
|
+
text: clampSectionText(fallbackText, emitCtx, "section")
|
|
2739
3087
|
}
|
|
2740
3088
|
}
|
|
2741
3089
|
];
|
|
@@ -2795,14 +3143,15 @@ var TableAdapter = class {
|
|
|
2795
3143
|
return node.type === "table";
|
|
2796
3144
|
}
|
|
2797
3145
|
plan(node, ctx) {
|
|
2798
|
-
const table = node;
|
|
3146
|
+
const table = normalizeTableRows(node, ctx);
|
|
2799
3147
|
const rowCount = table.children.length;
|
|
2800
3148
|
const colCount = table.children[0]?.children.length ?? 0;
|
|
2801
|
-
const cellChars = countEmittedCellChars(table, ctx);
|
|
2802
3149
|
if (colCount > TABLE_MAX_COLS) {
|
|
3150
|
+
const cellChars = countEmittedCellChars(table, ctx, "table");
|
|
2803
3151
|
return markdownThenAscii(table, cellChars, "Table exceeds 20 columns");
|
|
2804
3152
|
}
|
|
2805
3153
|
if (rowCount <= TABLE_PREFERRED_MAX_ROWS) {
|
|
3154
|
+
const cellChars = countEmittedCellChars(table, ctx, "table");
|
|
2806
3155
|
return [
|
|
2807
3156
|
{
|
|
2808
3157
|
requires: ["tableBlock"],
|
|
@@ -2817,11 +3166,12 @@ var TableAdapter = class {
|
|
|
2817
3166
|
const dataRows = Math.max(0, rowCount - 1);
|
|
2818
3167
|
if (dataRows <= DATA_TABLE_MAX_DATA_ROWS) {
|
|
2819
3168
|
const preferredTable = truncateRows(table, TABLE_PREFERRED_MAX_ROWS);
|
|
2820
|
-
const preferredChars = countEmittedCellChars(preferredTable, ctx);
|
|
3169
|
+
const preferredChars = countEmittedCellChars(preferredTable, ctx, "table");
|
|
3170
|
+
const dataTableChars = countEmittedCellChars(table, ctx, "data_table");
|
|
2821
3171
|
return [
|
|
2822
3172
|
{
|
|
2823
3173
|
requires: ["dataTable"],
|
|
2824
|
-
budget: { blocks: 1, dataTableChars
|
|
3174
|
+
budget: { blocks: 1, dataTableChars },
|
|
2825
3175
|
emit: (emitCtx) => {
|
|
2826
3176
|
emitCtx.degradations.add({
|
|
2827
3177
|
nodeType: "table",
|
|
@@ -2847,11 +3197,11 @@ var TableAdapter = class {
|
|
|
2847
3197
|
return [buildTableBlock(preferredTable, emitCtx)];
|
|
2848
3198
|
}
|
|
2849
3199
|
},
|
|
2850
|
-
...markdownThenAscii(table,
|
|
3200
|
+
...markdownThenAscii(table, dataTableChars, "native table unavailable")
|
|
2851
3201
|
];
|
|
2852
3202
|
}
|
|
2853
3203
|
const dataChunk = truncateRows(table, DATA_TABLE_MAX_DATA_ROWS + 1);
|
|
2854
|
-
const dataChunkChars = countEmittedCellChars(dataChunk, ctx);
|
|
3204
|
+
const dataChunkChars = countEmittedCellChars(dataChunk, ctx, "data_table");
|
|
2855
3205
|
return [
|
|
2856
3206
|
{
|
|
2857
3207
|
requires: ["dataTable"],
|
|
@@ -2867,10 +3217,37 @@ var TableAdapter = class {
|
|
|
2867
3217
|
return [buildDataTable(dataChunk, emitCtx)];
|
|
2868
3218
|
}
|
|
2869
3219
|
},
|
|
2870
|
-
...markdownThenAscii(table,
|
|
3220
|
+
...markdownThenAscii(table, dataChunkChars, "oversized table")
|
|
2871
3221
|
];
|
|
2872
3222
|
}
|
|
2873
3223
|
};
|
|
3224
|
+
function normalizeTableRows(table, ctx) {
|
|
3225
|
+
const colCount = table.children[0]?.children.length ?? 0;
|
|
3226
|
+
if (colCount === 0 || table.children.every((row) => row.children.length === colCount)) {
|
|
3227
|
+
return table;
|
|
3228
|
+
}
|
|
3229
|
+
const children = table.children.map((row, rowIndex) => {
|
|
3230
|
+
if (row.children.length === colCount) {
|
|
3231
|
+
return row;
|
|
3232
|
+
}
|
|
3233
|
+
if (row.children.length > colCount) {
|
|
3234
|
+
ctx.degradations.add({
|
|
3235
|
+
nodeType: "table",
|
|
3236
|
+
from: `row[${String(rowIndex)}]: ${String(row.children.length)} cells`,
|
|
3237
|
+
to: `${String(colCount)} cells`,
|
|
3238
|
+
reason: "Cells beyond the header column count dropped (GFM ignores the excess)",
|
|
3239
|
+
path: ctx.path
|
|
3240
|
+
});
|
|
3241
|
+
return { ...row, children: row.children.slice(0, colCount) };
|
|
3242
|
+
}
|
|
3243
|
+
const padding = Array.from(
|
|
3244
|
+
{ length: colCount - row.children.length },
|
|
3245
|
+
() => ({ type: "tableCell", children: [] })
|
|
3246
|
+
);
|
|
3247
|
+
return { ...row, children: [...row.children, ...padding] };
|
|
3248
|
+
});
|
|
3249
|
+
return { ...table, children };
|
|
3250
|
+
}
|
|
2874
3251
|
function markdownThenAscii(table, cellChars, reason) {
|
|
2875
3252
|
const md = toPipeMarkdown(table);
|
|
2876
3253
|
return [
|
|
@@ -2916,7 +3293,7 @@ function buildTableBlock(table, ctx) {
|
|
|
2916
3293
|
}
|
|
2917
3294
|
function buildDataTable(table, ctx) {
|
|
2918
3295
|
const captionRaw = ctx.precedingHeading !== void 0 && ctx.precedingHeading.length > 0 ? ctx.precedingHeading : "Table";
|
|
2919
|
-
const caption = captionRaw
|
|
3296
|
+
const caption = sliceSurrogateSafe(captionRaw, 150);
|
|
2920
3297
|
if (captionRaw.length > 150) {
|
|
2921
3298
|
ctx.degradations.add({
|
|
2922
3299
|
nodeType: "table",
|
|
@@ -2927,12 +3304,7 @@ function buildDataTable(table, ctx) {
|
|
|
2927
3304
|
});
|
|
2928
3305
|
}
|
|
2929
3306
|
const rows = table.children.map(
|
|
2930
|
-
(row, rowIndex) => row.children.map((cell) =>
|
|
2931
|
-
if (rowIndex === 0) {
|
|
2932
|
-
return { type: "raw_text", text: toString5(cell) };
|
|
2933
|
-
}
|
|
2934
|
-
return cellToSlack(cell, ctx, false);
|
|
2935
|
-
})
|
|
3307
|
+
(row, rowIndex) => row.children.map((cell) => cellForDataTable(cell, ctx, rowIndex))
|
|
2936
3308
|
);
|
|
2937
3309
|
return {
|
|
2938
3310
|
type: "data_table",
|
|
@@ -2942,25 +3314,31 @@ function buildDataTable(table, ctx) {
|
|
|
2942
3314
|
row_header_column_index: 0
|
|
2943
3315
|
};
|
|
2944
3316
|
}
|
|
3317
|
+
function cellForDataTable(cell, ctx, rowIndex) {
|
|
3318
|
+
if (rowIndex === 0) {
|
|
3319
|
+
return { type: "raw_text", text: toString5(cell) };
|
|
3320
|
+
}
|
|
3321
|
+
return cellToSlack(cell, ctx, false);
|
|
3322
|
+
}
|
|
2945
3323
|
function cellToSlack(cell, ctx, header) {
|
|
2946
|
-
const
|
|
2947
|
-
const
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
3324
|
+
const inline = emitPhrasing(cell.children, ctx);
|
|
3325
|
+
const single = inline.elements.length === 1 ? inline.elements[0] : void 0;
|
|
3326
|
+
const isEmpty = inline.elements.length === 0;
|
|
3327
|
+
const isSingleUnstyledText = single?.type === "text" && single.style === void 0;
|
|
3328
|
+
const plainText = isEmpty ? toString5(cell) : isSingleUnstyledText ? single.text : void 0;
|
|
3329
|
+
if (plainText !== void 0) {
|
|
3330
|
+
const trimmed = plainText.trim();
|
|
3331
|
+
if (!header && /^-?\d+(\.\d+)?$/.test(trimmed)) {
|
|
3332
|
+
return { type: "raw_number", value: Number(trimmed), text: trimmed };
|
|
2954
3333
|
}
|
|
2955
|
-
return { type: "raw_text", text:
|
|
3334
|
+
return { type: "raw_text", text: plainText };
|
|
2956
3335
|
}
|
|
2957
|
-
const inline = emitPhrasing(cell.children, ctx);
|
|
2958
3336
|
const rich = {
|
|
2959
3337
|
type: "rich_text",
|
|
2960
3338
|
elements: [
|
|
2961
3339
|
{
|
|
2962
3340
|
type: "rich_text_section",
|
|
2963
|
-
elements: inline.elements
|
|
3341
|
+
elements: inline.elements
|
|
2964
3342
|
}
|
|
2965
3343
|
]
|
|
2966
3344
|
};
|
|
@@ -2987,12 +3365,13 @@ function columnSettings(table) {
|
|
|
2987
3365
|
}
|
|
2988
3366
|
return settings;
|
|
2989
3367
|
}
|
|
2990
|
-
function countEmittedCellChars(table, ctx) {
|
|
3368
|
+
function countEmittedCellChars(table, ctx, mode) {
|
|
2991
3369
|
const probe = ctx.withDegradations(new RecordingDegradationCollector());
|
|
2992
3370
|
let n = 0;
|
|
2993
3371
|
for (const [rowIndex, row] of table.children.entries()) {
|
|
2994
3372
|
for (const cell of row.children) {
|
|
2995
|
-
|
|
3373
|
+
const emitted = mode === "data_table" ? cellForDataTable(cell, probe, rowIndex) : cellToSlack(cell, probe, rowIndex === 0);
|
|
3374
|
+
n += slackCellCharCount(emitted);
|
|
2996
3375
|
}
|
|
2997
3376
|
}
|
|
2998
3377
|
return n;
|
|
@@ -3115,27 +3494,50 @@ function emitUnicodeTaskTree(list, indent, ctx) {
|
|
|
3115
3494
|
});
|
|
3116
3495
|
effectiveIndent = RICH_TEXT_LIST_INDENT_MAX;
|
|
3117
3496
|
}
|
|
3118
|
-
const
|
|
3119
|
-
|
|
3497
|
+
const out = [];
|
|
3498
|
+
let pendingSections = [];
|
|
3499
|
+
const flush = () => {
|
|
3500
|
+
if (pendingSections.length === 0) {
|
|
3501
|
+
return;
|
|
3502
|
+
}
|
|
3503
|
+
out.push({
|
|
3504
|
+
type: "rich_text_list",
|
|
3505
|
+
style: "bullet",
|
|
3506
|
+
elements: pendingSections,
|
|
3507
|
+
indent: effectiveIndent
|
|
3508
|
+
});
|
|
3509
|
+
pendingSections = [];
|
|
3510
|
+
};
|
|
3120
3511
|
for (const item of list.children) {
|
|
3121
3512
|
const prefix = item.checked === true ? "\u2611 " : item.checked === false ? "\u2610 " : "";
|
|
3122
|
-
|
|
3513
|
+
pendingSections.push({
|
|
3123
3514
|
type: "rich_text_section",
|
|
3124
3515
|
elements: [{ type: "text", text: `${prefix}${itemText(item)}` }]
|
|
3125
3516
|
});
|
|
3517
|
+
const childLists = [];
|
|
3126
3518
|
for (const child of item.children) {
|
|
3127
3519
|
if (child.type === "list") {
|
|
3128
|
-
|
|
3520
|
+
childLists.push(child);
|
|
3129
3521
|
}
|
|
3130
3522
|
}
|
|
3523
|
+
if (childLists.length === 0) {
|
|
3524
|
+
continue;
|
|
3525
|
+
}
|
|
3526
|
+
flush();
|
|
3527
|
+
for (const child of childLists) {
|
|
3528
|
+
out.push(...emitUnicodeTaskTree(child, effectiveIndent + 1, ctx));
|
|
3529
|
+
}
|
|
3131
3530
|
}
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3531
|
+
flush();
|
|
3532
|
+
if (out.length === 0) {
|
|
3533
|
+
out.push({
|
|
3534
|
+
type: "rich_text_list",
|
|
3535
|
+
style: "bullet",
|
|
3536
|
+
elements: [{ type: "rich_text_section", elements: [{ type: "text", text: " " }] }],
|
|
3537
|
+
indent: effectiveIndent
|
|
3538
|
+
});
|
|
3539
|
+
}
|
|
3540
|
+
return out;
|
|
3139
3541
|
}
|
|
3140
3542
|
|
|
3141
3543
|
// src/adapters/thematic-break-adapter.ts
|
|
@@ -3383,12 +3785,10 @@ export {
|
|
|
3383
3785
|
CapabilityPresetName,
|
|
3384
3786
|
ConfigurationError,
|
|
3385
3787
|
ConversionContext,
|
|
3386
|
-
ConversionStrategy as ConversionStrategies,
|
|
3387
3788
|
DeterministicPlainTextRenderer,
|
|
3388
3789
|
MermaidPieParser,
|
|
3389
3790
|
MermaidXyChartParser,
|
|
3390
3791
|
MessageAssembler,
|
|
3391
|
-
OverflowMode as OverflowModes,
|
|
3392
3792
|
RecordingDegradationCollector,
|
|
3393
3793
|
RemarkMarkdownParser,
|
|
3394
3794
|
SlackMarkConverter,
|