@stream-mdx/core 0.0.3 → 0.1.1
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/CHANGELOG.md +12 -0
- package/README.md +6 -0
- package/dist/index.cjs +305 -46
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +304 -46
- package/dist/inline-parser.cjs +76 -3
- package/dist/inline-parser.d.cts +5 -0
- package/dist/inline-parser.d.ts +5 -0
- package/dist/inline-parser.mjs +76 -3
- package/dist/mixed-content.cjs +130 -9
- package/dist/mixed-content.d.cts +16 -2
- package/dist/mixed-content.d.ts +16 -2
- package/dist/mixed-content.mjs +130 -9
- package/dist/streaming/inline-streaming.cjs +99 -34
- package/dist/streaming/inline-streaming.d.cts +13 -2
- package/dist/streaming/inline-streaming.d.ts +13 -2
- package/dist/streaming/inline-streaming.mjs +98 -34
- package/dist/types.d.cts +41 -2
- package/dist/types.d.ts +41 -2
- package/dist/worker-html-sanitizer.cjs +16 -2
- package/dist/worker-html-sanitizer.mjs +16 -2
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @stream-mdx/core
|
|
2
2
|
|
|
3
|
+
## 0.1.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Refine streaming scheduling and list layout, add worker append batching/debug state support, and refresh docs/README examples.
|
|
8
|
+
|
|
9
|
+
## 0.1.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 294e557: Release StreamMDX 0.1.0 across all published packages.
|
|
14
|
+
|
|
3
15
|
## 0.0.3
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -41,3 +41,9 @@ export function makeConfig(overrides?: Partial<typeof DEFAULT_BACKPRESSURE_CONFI
|
|
|
41
41
|
|
|
42
42
|
- API reference: `docs/PUBLIC_API.md`
|
|
43
43
|
- Security model: `docs/SECURITY_MODEL.md`
|
|
44
|
+
|
|
45
|
+
## Related packages
|
|
46
|
+
|
|
47
|
+
- `@stream-mdx/react` for the React renderer
|
|
48
|
+
- `@stream-mdx/worker` for hosted worker bundles
|
|
49
|
+
- `@stream-mdx/mermaid` for Mermaid diagram rendering (optional)
|
package/dist/index.cjs
CHANGED
|
@@ -72,6 +72,7 @@ __export(index_exports, {
|
|
|
72
72
|
inlineNodesToPlainText: () => inlineNodesToPlainText,
|
|
73
73
|
isLikelyMdxComponent: () => isLikelyMdxComponent,
|
|
74
74
|
normalizeBlockquoteText: () => normalizeBlockquoteText,
|
|
75
|
+
normalizeFormatAnticipation: () => normalizeFormatAnticipation,
|
|
75
76
|
normalizeLang: () => normalizeLang,
|
|
76
77
|
parseCodeFenceInfo: () => parseCodeFenceInfo,
|
|
77
78
|
prepareInlineStreamingContent: () => prepareInlineStreamingContent,
|
|
@@ -290,6 +291,36 @@ function filterAllowedAttributes(attrs) {
|
|
|
290
291
|
}
|
|
291
292
|
|
|
292
293
|
// src/inline-parser.ts
|
|
294
|
+
function ensureGlobal(pattern) {
|
|
295
|
+
if (pattern.global) return pattern;
|
|
296
|
+
const flags = pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`;
|
|
297
|
+
return new RegExp(pattern.source, flags);
|
|
298
|
+
}
|
|
299
|
+
function findLastMatch(pattern, value) {
|
|
300
|
+
const re = ensureGlobal(pattern);
|
|
301
|
+
let match = null;
|
|
302
|
+
let next;
|
|
303
|
+
while ((next = re.exec(value)) !== null) {
|
|
304
|
+
match = next;
|
|
305
|
+
}
|
|
306
|
+
return match;
|
|
307
|
+
}
|
|
308
|
+
function findMatchAfter(pattern, value, startIndex) {
|
|
309
|
+
const re = ensureGlobal(pattern);
|
|
310
|
+
re.lastIndex = Math.max(0, startIndex);
|
|
311
|
+
return re.exec(value);
|
|
312
|
+
}
|
|
313
|
+
function isSamePattern(a, b) {
|
|
314
|
+
return a.source === b.source && a.flags === b.flags;
|
|
315
|
+
}
|
|
316
|
+
function countMatches(pattern, value) {
|
|
317
|
+
const re = ensureGlobal(pattern);
|
|
318
|
+
let count = 0;
|
|
319
|
+
while (re.exec(value)) {
|
|
320
|
+
count += 1;
|
|
321
|
+
}
|
|
322
|
+
return count;
|
|
323
|
+
}
|
|
293
324
|
var InlineParser = class {
|
|
294
325
|
constructor(options = {}) {
|
|
295
326
|
this.plugins = [];
|
|
@@ -337,6 +368,42 @@ var InlineParser = class {
|
|
|
337
368
|
}
|
|
338
369
|
return result;
|
|
339
370
|
}
|
|
371
|
+
/**
|
|
372
|
+
* Streaming regex anticipation helper. Returns an append string if a plugin
|
|
373
|
+
* declares an incomplete match at the end of the buffer.
|
|
374
|
+
*/
|
|
375
|
+
getRegexAnticipationAppend(content) {
|
|
376
|
+
if (!content || this.plugins.length === 0) {
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
for (const plugin of this.plugins) {
|
|
380
|
+
if (!("re" in plugin)) continue;
|
|
381
|
+
const regexPlugin = plugin;
|
|
382
|
+
const anticipation = regexPlugin.anticipation;
|
|
383
|
+
if (!anticipation) continue;
|
|
384
|
+
const maxScanChars = Number.isFinite(anticipation.maxScanChars ?? Number.NaN) ? Math.max(1, anticipation.maxScanChars ?? 0) : 240;
|
|
385
|
+
const scan = content.slice(Math.max(0, content.length - maxScanChars));
|
|
386
|
+
if (regexPlugin.fastCheck && !regexPlugin.fastCheck(scan)) {
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
const lastStart = findLastMatch(anticipation.start, scan);
|
|
390
|
+
if (!lastStart) continue;
|
|
391
|
+
if (isSamePattern(anticipation.start, anticipation.end)) {
|
|
392
|
+
const occurrences = countMatches(anticipation.start, scan);
|
|
393
|
+
if (occurrences % 2 === 0) {
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
} else {
|
|
397
|
+
const startIndex = lastStart.index + lastStart[0].length;
|
|
398
|
+
const hasEnd = Boolean(findMatchAfter(anticipation.end, scan, startIndex));
|
|
399
|
+
if (hasEnd) continue;
|
|
400
|
+
}
|
|
401
|
+
const appendValue = typeof anticipation.append === "function" ? anticipation.append(lastStart, content) : anticipation.append;
|
|
402
|
+
if (!appendValue) continue;
|
|
403
|
+
return appendValue;
|
|
404
|
+
}
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
340
407
|
/**
|
|
341
408
|
* Clear the memoization cache
|
|
342
409
|
*/
|
|
@@ -465,9 +532,16 @@ var InlineParser = class {
|
|
|
465
532
|
this.registerPlugin({
|
|
466
533
|
id: "citations",
|
|
467
534
|
priority: 10,
|
|
468
|
-
re: /\[\^([^\]]+)\]|@cite\{([^}]+)\}/g,
|
|
469
|
-
toNode: (match) => ({ kind: "citation", id: match[1] || match[2] }),
|
|
470
|
-
fastCheck: (text) => text.indexOf("@") !== -1 || text.indexOf("[^") !== -1
|
|
535
|
+
re: /\[\^([^\]\n]+)\]|@cite\{([^}\n]+)\}|\{cite:([^}\n]+)\}/g,
|
|
536
|
+
toNode: (match) => ({ kind: "citation", id: match[1] || match[2] || match[3] }),
|
|
537
|
+
fastCheck: (text) => text.indexOf("@cite") !== -1 || text.indexOf("[^") !== -1 || text.indexOf("{cite:") !== -1,
|
|
538
|
+
anticipation: {
|
|
539
|
+
start: /@cite\{|\{cite:/g,
|
|
540
|
+
end: /\}/g,
|
|
541
|
+
full: /@cite\{[^}\n]+?\}|\{cite:[^}\n]+?\}/g,
|
|
542
|
+
append: "}",
|
|
543
|
+
maxScanChars: 120
|
|
544
|
+
}
|
|
471
545
|
});
|
|
472
546
|
this.registerPlugin({
|
|
473
547
|
id: "mentions",
|
|
@@ -567,9 +641,23 @@ var rehypeParse = __toESM(require("rehype-parse"), 1);
|
|
|
567
641
|
var rehypeSanitize = __toESM(require("rehype-sanitize"), 1);
|
|
568
642
|
var rehypeStringify = __toESM(require("rehype-stringify"), 1);
|
|
569
643
|
var import_unified = require("unified");
|
|
570
|
-
var
|
|
644
|
+
var rehypeSanitizeModule = rehypeSanitize;
|
|
645
|
+
var defaultSchema = rehypeSanitizeModule.defaultSchema;
|
|
646
|
+
var resolvePlugin = (mod) => {
|
|
647
|
+
if (typeof mod === "function") return mod;
|
|
648
|
+
if (mod && typeof mod.default === "function") {
|
|
649
|
+
return mod.default;
|
|
650
|
+
}
|
|
651
|
+
if (mod && typeof mod.default?.default === "function") {
|
|
652
|
+
return mod.default?.default;
|
|
653
|
+
}
|
|
654
|
+
return mod;
|
|
655
|
+
};
|
|
656
|
+
var rehypeParsePlugin = resolvePlugin(rehypeParse);
|
|
657
|
+
var rehypeSanitizePlugin = resolvePlugin(rehypeSanitizeModule);
|
|
658
|
+
var rehypeStringifyPlugin = resolvePlugin(rehypeStringify);
|
|
571
659
|
var SANITIZED_SCHEMA = createSchema();
|
|
572
|
-
var sanitizeProcessor = (0, import_unified.unified)().use(
|
|
660
|
+
var sanitizeProcessor = (0, import_unified.unified)().use(rehypeParsePlugin, { fragment: true }).use(rehypeSanitizePlugin, SANITIZED_SCHEMA).use(rehypeStringifyPlugin).freeze();
|
|
573
661
|
function sanitizeHtmlInWorker(html) {
|
|
574
662
|
if (!html) return "";
|
|
575
663
|
try {
|
|
@@ -685,9 +773,27 @@ function mergeAttributes(existing, additions) {
|
|
|
685
773
|
}
|
|
686
774
|
|
|
687
775
|
// src/mixed-content.ts
|
|
688
|
-
|
|
776
|
+
var DEFAULT_INLINE_HTML_AUTOCLOSE_TAGS = /* @__PURE__ */ new Set([
|
|
777
|
+
"span",
|
|
778
|
+
"em",
|
|
779
|
+
"strong",
|
|
780
|
+
"code",
|
|
781
|
+
"kbd",
|
|
782
|
+
"del",
|
|
783
|
+
"s",
|
|
784
|
+
"mark",
|
|
785
|
+
"sub",
|
|
786
|
+
"sup",
|
|
787
|
+
"i",
|
|
788
|
+
"b",
|
|
789
|
+
"u",
|
|
790
|
+
"small",
|
|
791
|
+
"abbr",
|
|
792
|
+
"a"
|
|
793
|
+
]);
|
|
794
|
+
function extractMixedContentSegments(raw, baseOffset, parseInline, options) {
|
|
689
795
|
if (!raw) return [];
|
|
690
|
-
const initial = splitByTagSegments(raw, baseOffset, parseInline);
|
|
796
|
+
const initial = splitByTagSegments(raw, baseOffset, parseInline, options);
|
|
691
797
|
const expanded = [];
|
|
692
798
|
for (const segment of initial) {
|
|
693
799
|
if (segment.kind === "text") {
|
|
@@ -698,22 +804,58 @@ function extractMixedContentSegments(raw, baseOffset, parseInline) {
|
|
|
698
804
|
}
|
|
699
805
|
return mergeAdjacentTextSegments(expanded, parseInline);
|
|
700
806
|
}
|
|
701
|
-
function splitByTagSegments(source, baseOffset, parseInline) {
|
|
807
|
+
function splitByTagSegments(source, baseOffset, parseInline, options) {
|
|
702
808
|
const segments = [];
|
|
703
809
|
const lowerSource = source.toLowerCase();
|
|
704
810
|
const tagPattern = /<([A-Za-z][\w:-]*)([^<>]*?)\/?>/g;
|
|
705
811
|
let cursor = 0;
|
|
706
812
|
let match = tagPattern.exec(source);
|
|
707
813
|
const baseIsFinite = typeof baseOffset === "number" && Number.isFinite(baseOffset);
|
|
814
|
+
const htmlAllowTags = normalizeHtmlAllowlist(options?.html?.allowTags);
|
|
815
|
+
const htmlAutoClose = options?.html?.autoClose === true;
|
|
816
|
+
const htmlMaxNewlines = normalizeNewlineLimit(options?.html?.maxNewlines);
|
|
817
|
+
const mdxAutoClose = options?.mdx?.autoClose === true;
|
|
818
|
+
const mdxMaxNewlines = normalizeNewlineLimit(options?.mdx?.maxNewlines);
|
|
819
|
+
const mdxAllowlist = normalizeComponentAllowlist(options?.mdx?.componentAllowlist);
|
|
708
820
|
while (match !== null) {
|
|
709
821
|
const start = match.index;
|
|
710
822
|
const tagName = match[1];
|
|
711
823
|
const matchText = match[0];
|
|
712
|
-
const
|
|
824
|
+
const tagNameLower = tagName.toLowerCase();
|
|
825
|
+
const isSelfClosing = matchText.endsWith("/>") || isVoidHtmlTag(tagNameLower);
|
|
826
|
+
const mdxCandidate = isLikelyMdxComponent(tagName);
|
|
827
|
+
const mdxAllowed = mdxCandidate && (!mdxAllowlist || mdxAllowlist.has(tagName));
|
|
828
|
+
if (mdxCandidate && mdxAllowlist && !mdxAllowed) {
|
|
829
|
+
tagPattern.lastIndex = start + 1;
|
|
830
|
+
match = tagPattern.exec(source);
|
|
831
|
+
continue;
|
|
832
|
+
}
|
|
713
833
|
let end = tagPattern.lastIndex;
|
|
714
|
-
if (!isSelfClosing && !
|
|
834
|
+
if (!isSelfClosing && !mdxAllowed) {
|
|
715
835
|
const closingIndex = findClosingHtmlTag(lowerSource, tagName.toLowerCase(), end);
|
|
716
836
|
if (closingIndex === -1) {
|
|
837
|
+
if (htmlAutoClose && htmlAllowTags.has(tagNameLower)) {
|
|
838
|
+
const tail = source.slice(end);
|
|
839
|
+
const newlineCount = countNewlines(tail, htmlMaxNewlines + 1);
|
|
840
|
+
if (newlineCount <= htmlMaxNewlines) {
|
|
841
|
+
if (start > cursor) {
|
|
842
|
+
const absoluteFrom = baseIsFinite ? baseOffset + cursor : void 0;
|
|
843
|
+
const absoluteTo = baseIsFinite ? baseOffset + start : void 0;
|
|
844
|
+
pushTextSegment(segments, source.slice(cursor, start), absoluteFrom, absoluteTo, parseInline);
|
|
845
|
+
}
|
|
846
|
+
const rawSegment2 = source.slice(start);
|
|
847
|
+
const closedValue = `${rawSegment2}</${tagName}>`;
|
|
848
|
+
const segment2 = {
|
|
849
|
+
kind: "html",
|
|
850
|
+
value: closedValue,
|
|
851
|
+
range: createSegmentRange(baseOffset, start, source.length),
|
|
852
|
+
sanitized: sanitizeHtmlInWorker(closedValue)
|
|
853
|
+
};
|
|
854
|
+
segments.push(segment2);
|
|
855
|
+
cursor = source.length;
|
|
856
|
+
break;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
717
859
|
tagPattern.lastIndex = start + 1;
|
|
718
860
|
match = tagPattern.exec(source);
|
|
719
861
|
continue;
|
|
@@ -725,8 +867,8 @@ function splitByTagSegments(source, baseOffset, parseInline) {
|
|
|
725
867
|
const absoluteTo = baseIsFinite ? baseOffset + start : void 0;
|
|
726
868
|
pushTextSegment(segments, source.slice(cursor, start), absoluteFrom, absoluteTo, parseInline);
|
|
727
869
|
}
|
|
728
|
-
|
|
729
|
-
const kind =
|
|
870
|
+
let rawSegment = source.slice(start, end);
|
|
871
|
+
const kind = mdxAllowed ? "mdx" : "html";
|
|
730
872
|
const segment = {
|
|
731
873
|
kind,
|
|
732
874
|
value: rawSegment,
|
|
@@ -735,6 +877,17 @@ function splitByTagSegments(source, baseOffset, parseInline) {
|
|
|
735
877
|
if (kind === "html") {
|
|
736
878
|
segment.sanitized = sanitizeHtmlInWorker(rawSegment);
|
|
737
879
|
} else {
|
|
880
|
+
const tail = source.slice(end);
|
|
881
|
+
const newlineCount = countNewlines(tail, mdxMaxNewlines + 1);
|
|
882
|
+
if (mdxAutoClose && newlineCount > mdxMaxNewlines) {
|
|
883
|
+
tagPattern.lastIndex = start + 1;
|
|
884
|
+
match = tagPattern.exec(source);
|
|
885
|
+
continue;
|
|
886
|
+
}
|
|
887
|
+
if (mdxAutoClose && !rawSegment.endsWith("/>")) {
|
|
888
|
+
rawSegment = selfCloseTag(rawSegment);
|
|
889
|
+
segment.value = rawSegment;
|
|
890
|
+
}
|
|
738
891
|
segment.status = "pending";
|
|
739
892
|
}
|
|
740
893
|
segments.push(segment);
|
|
@@ -859,6 +1012,48 @@ var VOID_HTML_TAGS = /* @__PURE__ */ new Set(["br", "hr", "img", "meta", "input"
|
|
|
859
1012
|
function isVoidHtmlTag(tagName) {
|
|
860
1013
|
return VOID_HTML_TAGS.has(tagName.toLowerCase());
|
|
861
1014
|
}
|
|
1015
|
+
function normalizeNewlineLimit(value) {
|
|
1016
|
+
if (!Number.isFinite(value ?? Number.NaN)) {
|
|
1017
|
+
return 2;
|
|
1018
|
+
}
|
|
1019
|
+
return Math.max(0, value ?? 0);
|
|
1020
|
+
}
|
|
1021
|
+
function normalizeHtmlAllowlist(value) {
|
|
1022
|
+
if (!value) return DEFAULT_INLINE_HTML_AUTOCLOSE_TAGS;
|
|
1023
|
+
const tags = /* @__PURE__ */ new Set();
|
|
1024
|
+
for (const tag of value) {
|
|
1025
|
+
if (tag) {
|
|
1026
|
+
tags.add(tag.toLowerCase());
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
return tags.size > 0 ? tags : DEFAULT_INLINE_HTML_AUTOCLOSE_TAGS;
|
|
1030
|
+
}
|
|
1031
|
+
function normalizeComponentAllowlist(value) {
|
|
1032
|
+
if (!value) return null;
|
|
1033
|
+
const tags = /* @__PURE__ */ new Set();
|
|
1034
|
+
for (const tag of value) {
|
|
1035
|
+
if (tag) tags.add(tag);
|
|
1036
|
+
}
|
|
1037
|
+
return tags.size > 0 ? tags : null;
|
|
1038
|
+
}
|
|
1039
|
+
function countNewlines(value, limit) {
|
|
1040
|
+
let count = 0;
|
|
1041
|
+
for (let i = 0; i < value.length; i++) {
|
|
1042
|
+
if (value.charCodeAt(i) === 10) {
|
|
1043
|
+
count += 1;
|
|
1044
|
+
if (limit !== void 0 && count >= limit) {
|
|
1045
|
+
return count;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
return count;
|
|
1050
|
+
}
|
|
1051
|
+
function selfCloseTag(rawTag) {
|
|
1052
|
+
if (rawTag.endsWith("/>")) return rawTag;
|
|
1053
|
+
const closeIndex = rawTag.lastIndexOf(">");
|
|
1054
|
+
if (closeIndex === -1) return rawTag;
|
|
1055
|
+
return `${rawTag.slice(0, closeIndex)}/>`;
|
|
1056
|
+
}
|
|
862
1057
|
function isLikelyMdxComponent(tagName) {
|
|
863
1058
|
const first = tagName.charAt(0);
|
|
864
1059
|
return first.toUpperCase() === first && first.toLowerCase() !== first;
|
|
@@ -2811,63 +3006,126 @@ var CustomStreamingMatcher = class {
|
|
|
2811
3006
|
};
|
|
2812
3007
|
|
|
2813
3008
|
// src/streaming/inline-streaming.ts
|
|
3009
|
+
var DEFAULT_FORMAT_ANTICIPATION = {
|
|
3010
|
+
inline: false,
|
|
3011
|
+
mathInline: false,
|
|
3012
|
+
mathBlock: false,
|
|
3013
|
+
html: false,
|
|
3014
|
+
mdx: false,
|
|
3015
|
+
regex: false
|
|
3016
|
+
};
|
|
3017
|
+
function normalizeFormatAnticipation(input) {
|
|
3018
|
+
if (input === true) {
|
|
3019
|
+
return { ...DEFAULT_FORMAT_ANTICIPATION, inline: true };
|
|
3020
|
+
}
|
|
3021
|
+
if (!input) {
|
|
3022
|
+
return { ...DEFAULT_FORMAT_ANTICIPATION };
|
|
3023
|
+
}
|
|
3024
|
+
return {
|
|
3025
|
+
inline: input.inline ?? false,
|
|
3026
|
+
mathInline: input.mathInline ?? false,
|
|
3027
|
+
mathBlock: input.mathBlock ?? false,
|
|
3028
|
+
html: input.html ?? false,
|
|
3029
|
+
mdx: input.mdx ?? false,
|
|
3030
|
+
regex: input.regex ?? false
|
|
3031
|
+
};
|
|
3032
|
+
}
|
|
2814
3033
|
function prepareInlineStreamingContent(content, options) {
|
|
2815
|
-
const enableAnticipation = Boolean(options?.formatAnticipation);
|
|
2816
3034
|
const enableMath = options?.math !== false;
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
3035
|
+
const anticipation = normalizeFormatAnticipation(options?.formatAnticipation);
|
|
3036
|
+
const enableInlineAnticipation = anticipation.inline;
|
|
3037
|
+
const enableMathInlineAnticipation = anticipation.mathInline;
|
|
3038
|
+
const enableMathBlockAnticipation = anticipation.mathBlock;
|
|
3039
|
+
const stack = [];
|
|
3040
|
+
const toggleToken = (token) => {
|
|
3041
|
+
const last = stack[stack.length - 1];
|
|
3042
|
+
if (last === token) {
|
|
3043
|
+
stack.pop();
|
|
3044
|
+
} else {
|
|
3045
|
+
stack.push(token);
|
|
3046
|
+
}
|
|
3047
|
+
};
|
|
3048
|
+
let mathDisplayOpen = false;
|
|
3049
|
+
let mathDisplayCrossedNewline = false;
|
|
2822
3050
|
for (let i = 0; i < content.length; i++) {
|
|
2823
3051
|
const code = content.charCodeAt(i);
|
|
2824
|
-
if (code ===
|
|
2825
|
-
|
|
3052
|
+
if (code === 10 || code === 13) {
|
|
3053
|
+
if (mathDisplayOpen) {
|
|
3054
|
+
mathDisplayCrossedNewline = true;
|
|
3055
|
+
}
|
|
2826
3056
|
continue;
|
|
2827
3057
|
}
|
|
2828
3058
|
if (code === 96) {
|
|
2829
|
-
|
|
3059
|
+
toggleToken("code");
|
|
3060
|
+
continue;
|
|
3061
|
+
}
|
|
3062
|
+
if (code === 126 && i + 1 < content.length && content.charCodeAt(i + 1) === 126) {
|
|
3063
|
+
toggleToken("strike");
|
|
3064
|
+
i += 1;
|
|
2830
3065
|
continue;
|
|
2831
3066
|
}
|
|
2832
3067
|
if (code === 42) {
|
|
2833
3068
|
if (i + 1 < content.length && content.charCodeAt(i + 1) === 42) {
|
|
2834
|
-
|
|
2835
|
-
starCount += 2;
|
|
3069
|
+
toggleToken("strong");
|
|
2836
3070
|
i += 1;
|
|
2837
3071
|
} else {
|
|
2838
|
-
|
|
3072
|
+
toggleToken("em");
|
|
2839
3073
|
}
|
|
2840
3074
|
continue;
|
|
2841
3075
|
}
|
|
2842
|
-
if (code ===
|
|
2843
|
-
if (i + 1 < content.length && content.charCodeAt(i + 1) ===
|
|
2844
|
-
|
|
3076
|
+
if (enableMath && code === 36) {
|
|
3077
|
+
if (i + 1 < content.length && content.charCodeAt(i + 1) === 36) {
|
|
3078
|
+
toggleToken("math-display");
|
|
3079
|
+
if (mathDisplayOpen) {
|
|
3080
|
+
mathDisplayOpen = false;
|
|
3081
|
+
mathDisplayCrossedNewline = false;
|
|
3082
|
+
} else {
|
|
3083
|
+
mathDisplayOpen = true;
|
|
3084
|
+
mathDisplayCrossedNewline = false;
|
|
3085
|
+
}
|
|
2845
3086
|
i += 1;
|
|
3087
|
+
} else {
|
|
3088
|
+
toggleToken("math-inline");
|
|
2846
3089
|
}
|
|
2847
3090
|
}
|
|
2848
3091
|
}
|
|
2849
|
-
const
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
return { kind: "parse", status: "complete", content, appended: "" };
|
|
3092
|
+
const hasIncompleteFormatting = stack.some((token) => token === "code" || token === "strike" || token === "strong" || token === "em");
|
|
3093
|
+
const hasIncompleteMathInline = stack.includes("math-inline");
|
|
3094
|
+
const hasIncompleteMathDisplay = stack.includes("math-display");
|
|
3095
|
+
const hasIncompleteMath = hasIncompleteMathInline || hasIncompleteMathDisplay;
|
|
3096
|
+
if (enableMath && hasIncompleteMath) {
|
|
3097
|
+
if (hasIncompleteMathInline && !enableMathInlineAnticipation) {
|
|
3098
|
+
return { kind: "raw", status: "raw", reason: "incomplete-math" };
|
|
3099
|
+
}
|
|
3100
|
+
if (hasIncompleteMathDisplay && (!enableMathBlockAnticipation || mathDisplayCrossedNewline)) {
|
|
3101
|
+
return { kind: "raw", status: "raw", reason: "incomplete-math" };
|
|
3102
|
+
}
|
|
2861
3103
|
}
|
|
2862
|
-
if (!
|
|
3104
|
+
if (hasIncompleteFormatting && !enableInlineAnticipation) {
|
|
2863
3105
|
return { kind: "raw", status: "raw", reason: "incomplete-formatting" };
|
|
2864
3106
|
}
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
3107
|
+
if (!hasIncompleteFormatting && !hasIncompleteMath) {
|
|
3108
|
+
return { kind: "parse", status: "complete", content, appended: "" };
|
|
3109
|
+
}
|
|
3110
|
+
const appendForToken = (token) => {
|
|
3111
|
+
switch (token) {
|
|
3112
|
+
case "code":
|
|
3113
|
+
return "`";
|
|
3114
|
+
case "strike":
|
|
3115
|
+
return "~~";
|
|
3116
|
+
case "strong":
|
|
3117
|
+
return "**";
|
|
3118
|
+
case "em":
|
|
3119
|
+
return "*";
|
|
3120
|
+
case "math-inline":
|
|
3121
|
+
return "$";
|
|
3122
|
+
case "math-display":
|
|
3123
|
+
return "$$";
|
|
3124
|
+
default:
|
|
3125
|
+
return "";
|
|
3126
|
+
}
|
|
3127
|
+
};
|
|
3128
|
+
const appended = stack.slice().reverse().map((token) => appendForToken(token)).join("");
|
|
2871
3129
|
return { kind: "parse", status: "anticipated", content: content + appended, appended };
|
|
2872
3130
|
}
|
|
2873
3131
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -2914,6 +3172,7 @@ function prepareInlineStreamingContent(content, options) {
|
|
|
2914
3172
|
inlineNodesToPlainText,
|
|
2915
3173
|
isLikelyMdxComponent,
|
|
2916
3174
|
normalizeBlockquoteText,
|
|
3175
|
+
normalizeFormatAnticipation,
|
|
2917
3176
|
normalizeLang,
|
|
2918
3177
|
parseCodeFenceInfo,
|
|
2919
3178
|
prepareInlineStreamingContent,
|
package/dist/index.d.cts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { Block, NodeSnapshot, InlineNode, Patch } from './types.cjs';
|
|
2
|
-
export { ASTInlinePlugin, CoalescingMetrics, CompiledMdxModule, InlineHtmlDescriptor, InlinePlugin, LANGUAGE_ALIASES, MixedContentSegment, NodePath, PATCH_ROOT_ID, PatchMetrics, PerformanceMetrics, ProtectedRange, ProtectedRangeKind, RegexInlinePlugin, SetPropsBatchEntry, WorkerErrorPayload, WorkerIn, WorkerOut, WorkerPhase } from './types.cjs';
|
|
2
|
+
export { ASTInlinePlugin, CoalescingMetrics, CompiledMdxModule, FormatAnticipationConfig, InlineHtmlDescriptor, InlinePlugin, LANGUAGE_ALIASES, MixedContentSegment, NodePath, PATCH_ROOT_ID, PatchMetrics, PerformanceMetrics, ProtectedRange, ProtectedRangeKind, RegexAnticipationPattern, RegexInlinePlugin, SetPropsBatchEntry, WorkerErrorPayload, WorkerIn, WorkerOut, WorkerPhase } from './types.cjs';
|
|
3
3
|
export { HighlightedLine, dedentIndentedCode, extractCodeLines, extractCodeWrapperAttributes, extractHighlightedLines, stripCodeFence } from './code-highlighting.cjs';
|
|
4
4
|
export { PerformanceTimer, StringBuffer, applyUpdate, debounce, detectMDX, generateBlockId, getBlockKey, normalizeBlockquoteText, normalizeLang, parseCodeFenceInfo, removeHeadingMarkers } from './utils.cjs';
|
|
5
|
-
export { extractMixedContentSegments, findClosingHtmlTag, isLikelyMdxComponent } from './mixed-content.cjs';
|
|
5
|
+
export { MixedContentAutoCloseHtmlOptions, MixedContentAutoCloseMdxOptions, MixedContentOptions, extractMixedContentSegments, findClosingHtmlTag, isLikelyMdxComponent } from './mixed-content.cjs';
|
|
6
6
|
export { CSP_HEADERS, SanitizationPolicy, createSanitizationConfig, createTrustedHTML, initializeSecurity, initializeTrustedTypesPolicy, sanitizeCodeHTML, sanitizeHTML, sanitizeMathHTML, sanitizeURL } from './security.cjs';
|
|
7
7
|
export { InlineParseOptions, InlineParser, InlineParserOptions, applyASTPlugin, applyRegexPlugin } from './inline-parser.cjs';
|
|
8
8
|
export { sanitizeHtmlInWorker } from './worker-html-sanitizer.cjs';
|
|
9
9
|
export { BackpressureConfig, DEFAULT_BACKPRESSURE_CONFIG, calculateRawCredit, calculateSmoothedCredit, clampCredit, computeHeavyPatchBudget, smoothCredit } from './perf/backpressure.cjs';
|
|
10
10
|
export { CoalesceConfig, DEFAULT_COALESCE_CONFIG, coalescePatches, coalescePatchesLinear, coalescePatchesQuadratic, coalescePatchesWithMetrics } from './perf/patch-coalescing.cjs';
|
|
11
11
|
export { CustomStreamingMatcher, MatchResult } from './streaming/custom-matcher.cjs';
|
|
12
|
-
export { InlineStreamingInlineStatus, InlineStreamingPrepareResult, prepareInlineStreamingContent } from './streaming/inline-streaming.cjs';
|
|
12
|
+
export { InlineStreamingInlineStatus, InlineStreamingPrepareResult, NormalizedFormatAnticipation, normalizeFormatAnticipation, prepareInlineStreamingContent } from './streaming/inline-streaming.cjs';
|
|
13
13
|
import 'dompurify';
|
|
14
14
|
|
|
15
15
|
declare function cloneBlock(block: Block): Block;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { Block, NodeSnapshot, InlineNode, Patch } from './types.js';
|
|
2
|
-
export { ASTInlinePlugin, CoalescingMetrics, CompiledMdxModule, InlineHtmlDescriptor, InlinePlugin, LANGUAGE_ALIASES, MixedContentSegment, NodePath, PATCH_ROOT_ID, PatchMetrics, PerformanceMetrics, ProtectedRange, ProtectedRangeKind, RegexInlinePlugin, SetPropsBatchEntry, WorkerErrorPayload, WorkerIn, WorkerOut, WorkerPhase } from './types.js';
|
|
2
|
+
export { ASTInlinePlugin, CoalescingMetrics, CompiledMdxModule, FormatAnticipationConfig, InlineHtmlDescriptor, InlinePlugin, LANGUAGE_ALIASES, MixedContentSegment, NodePath, PATCH_ROOT_ID, PatchMetrics, PerformanceMetrics, ProtectedRange, ProtectedRangeKind, RegexAnticipationPattern, RegexInlinePlugin, SetPropsBatchEntry, WorkerErrorPayload, WorkerIn, WorkerOut, WorkerPhase } from './types.js';
|
|
3
3
|
export { HighlightedLine, dedentIndentedCode, extractCodeLines, extractCodeWrapperAttributes, extractHighlightedLines, stripCodeFence } from './code-highlighting.js';
|
|
4
4
|
export { PerformanceTimer, StringBuffer, applyUpdate, debounce, detectMDX, generateBlockId, getBlockKey, normalizeBlockquoteText, normalizeLang, parseCodeFenceInfo, removeHeadingMarkers } from './utils.js';
|
|
5
|
-
export { extractMixedContentSegments, findClosingHtmlTag, isLikelyMdxComponent } from './mixed-content.js';
|
|
5
|
+
export { MixedContentAutoCloseHtmlOptions, MixedContentAutoCloseMdxOptions, MixedContentOptions, extractMixedContentSegments, findClosingHtmlTag, isLikelyMdxComponent } from './mixed-content.js';
|
|
6
6
|
export { CSP_HEADERS, SanitizationPolicy, createSanitizationConfig, createTrustedHTML, initializeSecurity, initializeTrustedTypesPolicy, sanitizeCodeHTML, sanitizeHTML, sanitizeMathHTML, sanitizeURL } from './security.js';
|
|
7
7
|
export { InlineParseOptions, InlineParser, InlineParserOptions, applyASTPlugin, applyRegexPlugin } from './inline-parser.js';
|
|
8
8
|
export { sanitizeHtmlInWorker } from './worker-html-sanitizer.js';
|
|
9
9
|
export { BackpressureConfig, DEFAULT_BACKPRESSURE_CONFIG, calculateRawCredit, calculateSmoothedCredit, clampCredit, computeHeavyPatchBudget, smoothCredit } from './perf/backpressure.js';
|
|
10
10
|
export { CoalesceConfig, DEFAULT_COALESCE_CONFIG, coalescePatches, coalescePatchesLinear, coalescePatchesQuadratic, coalescePatchesWithMetrics } from './perf/patch-coalescing.js';
|
|
11
11
|
export { CustomStreamingMatcher, MatchResult } from './streaming/custom-matcher.js';
|
|
12
|
-
export { InlineStreamingInlineStatus, InlineStreamingPrepareResult, prepareInlineStreamingContent } from './streaming/inline-streaming.js';
|
|
12
|
+
export { InlineStreamingInlineStatus, InlineStreamingPrepareResult, NormalizedFormatAnticipation, normalizeFormatAnticipation, prepareInlineStreamingContent } from './streaming/inline-streaming.js';
|
|
13
13
|
import 'dompurify';
|
|
14
14
|
|
|
15
15
|
declare function cloneBlock(block: Block): Block;
|