@stream-mdx/core 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { InlineNode, MixedContentSegment } from './types.js';
1
+ import { ProtectedRange, InlineNode, MixedContentSegment } from './types.js';
2
2
 
3
3
  interface MixedContentAutoCloseHtmlOptions {
4
4
  autoClose?: boolean;
@@ -13,6 +13,8 @@ interface MixedContentAutoCloseMdxOptions {
13
13
  interface MixedContentOptions {
14
14
  html?: MixedContentAutoCloseHtmlOptions;
15
15
  mdx?: MixedContentAutoCloseMdxOptions;
16
+ protectedRanges?: ReadonlyArray<ProtectedRange>;
17
+ protectedRangeKinds?: ReadonlyArray<ProtectedRange["kind"]>;
16
18
  }
17
19
  declare function extractMixedContentSegments(raw: string, baseOffset: number | undefined, parseInline: (content: string) => InlineNode[], options?: MixedContentOptions): MixedContentSegment[];
18
20
  declare function isLikelyMdxComponent(tagName: string): boolean;
@@ -179,6 +179,8 @@ function splitByTagSegments(source, baseOffset, parseInline, options) {
179
179
  const mdxAutoClose = options?.mdx?.autoClose === true;
180
180
  const mdxMaxNewlines = normalizeNewlineLimit(options?.mdx?.maxNewlines);
181
181
  const mdxAllowlist = normalizeComponentAllowlist(options?.mdx?.componentAllowlist);
182
+ const protectedRanges = options?.protectedRanges ?? [];
183
+ const protectedKinds = protectedRanges.length ? new Set(options?.protectedRangeKinds ?? ["math-inline", "math-display", "code-inline", "code-block", "autolink"]) : null;
182
184
  while (match !== null) {
183
185
  const start = match.index;
184
186
  const tagName = match[1];
@@ -193,6 +195,18 @@ function splitByTagSegments(source, baseOffset, parseInline, options) {
193
195
  continue;
194
196
  }
195
197
  let end = tagPattern.lastIndex;
198
+ if (protectedKinds && protectedRanges.length > 0) {
199
+ const absoluteStart = baseIsFinite ? baseOffset + start : start;
200
+ const absoluteEnd = baseIsFinite ? baseOffset + end : end;
201
+ const covered = protectedRanges.some(
202
+ (range) => protectedKinds.has(range.kind) && typeof range.from === "number" && typeof range.to === "number" && range.from <= absoluteStart && range.to >= absoluteEnd
203
+ );
204
+ if (covered) {
205
+ tagPattern.lastIndex = start + 1;
206
+ match = tagPattern.exec(source);
207
+ continue;
208
+ }
209
+ }
196
210
  if (!isSelfClosing && !mdxAllowed) {
197
211
  const closingIndex = findClosingHtmlTag(lowerSource, tagName.toLowerCase(), end);
198
212
  if (closingIndex === -1) {
@@ -140,6 +140,14 @@ function mergeAppendLines(window, startIndex) {
140
140
  }
141
141
  const lines = [...base.lines ?? []];
142
142
  const highlight = Array.isArray(base.highlight) ? [...base.highlight] : [];
143
+ const tokens = Array.isArray(base.tokens) ? [...base.tokens] : [];
144
+ const diffKind = Array.isArray(base.diffKind) ? [...base.diffKind] : [];
145
+ const oldNo = Array.isArray(base.oldNo) ? [...base.oldNo] : [];
146
+ const newNo = Array.isArray(base.newNo) ? [...base.newNo] : [];
147
+ let includeTokens = Array.isArray(base.tokens);
148
+ let includeDiffKind = Array.isArray(base.diffKind);
149
+ let includeOldNo = Array.isArray(base.oldNo);
150
+ let includeNewNo = Array.isArray(base.newNo);
143
151
  const baseStart = base.startIndex;
144
152
  let expectedStart = baseStart + lines.length;
145
153
  let j = startIndex + 1;
@@ -147,6 +155,7 @@ function mergeAppendLines(window, startIndex) {
147
155
  while (j < window.length && mergedCount < APPEND_MERGE_LIMIT) {
148
156
  const next = window[j];
149
157
  if (next.op === "appendLines" && next.at.blockId === base.at.blockId && next.at.nodeId === base.at.nodeId && typeof next.startIndex === "number" && next.startIndex === expectedStart) {
158
+ const priorLineCount = lines.length;
150
159
  lines.push(...next.lines ?? []);
151
160
  const nextHighlights = Array.isArray(next.highlight) ? next.highlight : [];
152
161
  const appendedCount = next.lines?.length ?? 0;
@@ -160,6 +169,50 @@ function mergeAppendLines(window, startIndex) {
160
169
  highlight.push(null);
161
170
  }
162
171
  }
172
+ if (Array.isArray(next.tokens)) {
173
+ if (!includeTokens) {
174
+ tokens.push(...new Array(priorLineCount).fill(null));
175
+ includeTokens = true;
176
+ }
177
+ for (let idx = 0; idx < appendedCount; idx++) {
178
+ tokens.push(idx < next.tokens.length ? next.tokens[idx] ?? null : null);
179
+ }
180
+ } else if (includeTokens) {
181
+ tokens.push(...new Array(appendedCount).fill(null));
182
+ }
183
+ if (Array.isArray(next.diffKind)) {
184
+ if (!includeDiffKind) {
185
+ diffKind.push(...new Array(priorLineCount).fill(null));
186
+ includeDiffKind = true;
187
+ }
188
+ for (let idx = 0; idx < appendedCount; idx++) {
189
+ diffKind.push(idx < next.diffKind.length ? next.diffKind[idx] ?? null : null);
190
+ }
191
+ } else if (includeDiffKind) {
192
+ diffKind.push(...new Array(appendedCount).fill(null));
193
+ }
194
+ if (Array.isArray(next.oldNo)) {
195
+ if (!includeOldNo) {
196
+ oldNo.push(...new Array(priorLineCount).fill(null));
197
+ includeOldNo = true;
198
+ }
199
+ for (let idx = 0; idx < appendedCount; idx++) {
200
+ oldNo.push(idx < next.oldNo.length ? next.oldNo[idx] ?? null : null);
201
+ }
202
+ } else if (includeOldNo) {
203
+ oldNo.push(...new Array(appendedCount).fill(null));
204
+ }
205
+ if (Array.isArray(next.newNo)) {
206
+ if (!includeNewNo) {
207
+ newNo.push(...new Array(priorLineCount).fill(null));
208
+ includeNewNo = true;
209
+ }
210
+ for (let idx = 0; idx < appendedCount; idx++) {
211
+ newNo.push(idx < next.newNo.length ? next.newNo[idx] ?? null : null);
212
+ }
213
+ } else if (includeNewNo) {
214
+ newNo.push(...new Array(appendedCount).fill(null));
215
+ }
163
216
  expectedStart = baseStart + lines.length;
164
217
  mergedCount++;
165
218
  j++;
@@ -170,7 +223,11 @@ function mergeAppendLines(window, startIndex) {
170
223
  const combined = {
171
224
  ...base,
172
225
  lines,
173
- highlight: highlight.length > 0 ? highlight : void 0
226
+ highlight: highlight.length > 0 ? highlight : void 0,
227
+ tokens: includeTokens && tokens.length > 0 ? tokens : void 0,
228
+ diffKind: includeDiffKind && diffKind.length > 0 ? diffKind : void 0,
229
+ oldNo: includeOldNo && oldNo.length > 0 ? oldNo : void 0,
230
+ newNo: includeNewNo && newNo.length > 0 ? newNo : void 0
174
231
  };
175
232
  return { patch: combined, nextIndex: j };
176
233
  }
@@ -112,6 +112,14 @@ function mergeAppendLines(window, startIndex) {
112
112
  }
113
113
  const lines = [...base.lines ?? []];
114
114
  const highlight = Array.isArray(base.highlight) ? [...base.highlight] : [];
115
+ const tokens = Array.isArray(base.tokens) ? [...base.tokens] : [];
116
+ const diffKind = Array.isArray(base.diffKind) ? [...base.diffKind] : [];
117
+ const oldNo = Array.isArray(base.oldNo) ? [...base.oldNo] : [];
118
+ const newNo = Array.isArray(base.newNo) ? [...base.newNo] : [];
119
+ let includeTokens = Array.isArray(base.tokens);
120
+ let includeDiffKind = Array.isArray(base.diffKind);
121
+ let includeOldNo = Array.isArray(base.oldNo);
122
+ let includeNewNo = Array.isArray(base.newNo);
115
123
  const baseStart = base.startIndex;
116
124
  let expectedStart = baseStart + lines.length;
117
125
  let j = startIndex + 1;
@@ -119,6 +127,7 @@ function mergeAppendLines(window, startIndex) {
119
127
  while (j < window.length && mergedCount < APPEND_MERGE_LIMIT) {
120
128
  const next = window[j];
121
129
  if (next.op === "appendLines" && next.at.blockId === base.at.blockId && next.at.nodeId === base.at.nodeId && typeof next.startIndex === "number" && next.startIndex === expectedStart) {
130
+ const priorLineCount = lines.length;
122
131
  lines.push(...next.lines ?? []);
123
132
  const nextHighlights = Array.isArray(next.highlight) ? next.highlight : [];
124
133
  const appendedCount = next.lines?.length ?? 0;
@@ -132,6 +141,50 @@ function mergeAppendLines(window, startIndex) {
132
141
  highlight.push(null);
133
142
  }
134
143
  }
144
+ if (Array.isArray(next.tokens)) {
145
+ if (!includeTokens) {
146
+ tokens.push(...new Array(priorLineCount).fill(null));
147
+ includeTokens = true;
148
+ }
149
+ for (let idx = 0; idx < appendedCount; idx++) {
150
+ tokens.push(idx < next.tokens.length ? next.tokens[idx] ?? null : null);
151
+ }
152
+ } else if (includeTokens) {
153
+ tokens.push(...new Array(appendedCount).fill(null));
154
+ }
155
+ if (Array.isArray(next.diffKind)) {
156
+ if (!includeDiffKind) {
157
+ diffKind.push(...new Array(priorLineCount).fill(null));
158
+ includeDiffKind = true;
159
+ }
160
+ for (let idx = 0; idx < appendedCount; idx++) {
161
+ diffKind.push(idx < next.diffKind.length ? next.diffKind[idx] ?? null : null);
162
+ }
163
+ } else if (includeDiffKind) {
164
+ diffKind.push(...new Array(appendedCount).fill(null));
165
+ }
166
+ if (Array.isArray(next.oldNo)) {
167
+ if (!includeOldNo) {
168
+ oldNo.push(...new Array(priorLineCount).fill(null));
169
+ includeOldNo = true;
170
+ }
171
+ for (let idx = 0; idx < appendedCount; idx++) {
172
+ oldNo.push(idx < next.oldNo.length ? next.oldNo[idx] ?? null : null);
173
+ }
174
+ } else if (includeOldNo) {
175
+ oldNo.push(...new Array(appendedCount).fill(null));
176
+ }
177
+ if (Array.isArray(next.newNo)) {
178
+ if (!includeNewNo) {
179
+ newNo.push(...new Array(priorLineCount).fill(null));
180
+ includeNewNo = true;
181
+ }
182
+ for (let idx = 0; idx < appendedCount; idx++) {
183
+ newNo.push(idx < next.newNo.length ? next.newNo[idx] ?? null : null);
184
+ }
185
+ } else if (includeNewNo) {
186
+ newNo.push(...new Array(appendedCount).fill(null));
187
+ }
135
188
  expectedStart = baseStart + lines.length;
136
189
  mergedCount++;
137
190
  j++;
@@ -142,7 +195,11 @@ function mergeAppendLines(window, startIndex) {
142
195
  const combined = {
143
196
  ...base,
144
197
  lines,
145
- highlight: highlight.length > 0 ? highlight : void 0
198
+ highlight: highlight.length > 0 ? highlight : void 0,
199
+ tokens: includeTokens && tokens.length > 0 ? tokens : void 0,
200
+ diffKind: includeDiffKind && diffKind.length > 0 ? diffKind : void 0,
201
+ oldNo: includeOldNo && oldNo.length > 0 ? oldNo : void 0,
202
+ newNo: includeNewNo && newNo.length > 0 ? newNo : void 0
146
203
  };
147
204
  return { patch: combined, nextIndex: j };
148
205
  }
package/dist/types.d.cts CHANGED
@@ -47,6 +47,8 @@ type FormatAnticipationConfig = boolean | {
47
47
  mdx?: boolean;
48
48
  regex?: boolean;
49
49
  };
50
+ type CodeHighlightingMode = "final" | "incremental" | "live";
51
+ type CodeHighlightOutputMode = "html" | "tokens" | "both";
50
52
  interface InlineHtmlDescriptor {
51
53
  tagName: string;
52
54
  attributes: Record<string, string>;
@@ -109,6 +111,47 @@ type InlineNode = {
109
111
  label: string;
110
112
  number?: number;
111
113
  };
114
+ type TokenStyle = {
115
+ fg?: string;
116
+ bg?: string;
117
+ fs?: number;
118
+ };
119
+ type TokenSpan = {
120
+ t: string;
121
+ v?: {
122
+ dark?: TokenStyle;
123
+ light?: TokenStyle;
124
+ };
125
+ s?: TokenStyle;
126
+ };
127
+ type TokenLineV1 = {
128
+ spans: TokenSpan[];
129
+ };
130
+ type DiffKind = "add" | "remove" | "context" | "hunk" | "meta";
131
+ type DiffLineKind = "meta" | "hunk" | "add" | "del" | "context";
132
+ type ThemedToken = {
133
+ content: string;
134
+ color?: string | null;
135
+ fontStyle?: number | null;
136
+ };
137
+ type ThemedLine = ThemedToken[];
138
+ type DiffLine = {
139
+ kind: DiffLineKind;
140
+ oldNo?: number | null;
141
+ newNo?: number | null;
142
+ raw: string;
143
+ tokens?: ThemedLine | null;
144
+ };
145
+ type DiffBlock = {
146
+ kind: "diff";
147
+ filePath?: string | null;
148
+ language?: string | null;
149
+ lines: DiffLine[];
150
+ additions?: number | null;
151
+ deletions?: number | null;
152
+ unified?: string | null;
153
+ };
154
+ type LazyTokenizationPriority = "visible" | "prefetch";
112
155
  /**
113
156
  * Worker communication protocol
114
157
  */
@@ -124,8 +167,17 @@ type WorkerIn = {
124
167
  callouts?: boolean;
125
168
  math?: boolean;
126
169
  formatAnticipation?: FormatAnticipationConfig;
170
+ codeHighlighting?: CodeHighlightingMode;
171
+ outputMode?: CodeHighlightOutputMode;
127
172
  liveCodeHighlighting?: boolean;
173
+ liveTokenization?: boolean;
174
+ emitHighlightTokens?: boolean;
175
+ emitDiffBlocks?: boolean;
128
176
  mdxComponentNames?: string[];
177
+ lazyTokenization?: {
178
+ enabled?: boolean;
179
+ thresholdLines?: number;
180
+ };
129
181
  };
130
182
  mdx?: {
131
183
  compileMode?: "server" | "worker";
@@ -137,6 +189,12 @@ type WorkerIn = {
137
189
  type: "FINALIZE";
138
190
  } | {
139
191
  type: "DEBUG_STATE";
192
+ } | {
193
+ type: "TOKENIZE_RANGE";
194
+ blockId: string;
195
+ startLine: number;
196
+ endLine: number;
197
+ priority?: LazyTokenizationPriority;
140
198
  } | {
141
199
  type: "MDX_COMPILED";
142
200
  blockId: string;
@@ -260,6 +318,14 @@ interface PerformanceMetrics {
260
318
  appendLineBatches?: number;
261
319
  appendLineTotalLines?: number;
262
320
  appendLineMaxLines?: number;
321
+ lazyTokenization?: {
322
+ requests: number;
323
+ avgRangeLines: number;
324
+ maxRangeLines: number;
325
+ avgLatencyMs: number;
326
+ maxLatencyMs: number;
327
+ maxQueue: number;
328
+ };
263
329
  }
264
330
  /**
265
331
  * Incremental patch protocol (initial block-level implementation).
@@ -318,6 +384,10 @@ type Patch = {
318
384
  startIndex: number;
319
385
  lines: string[];
320
386
  highlight?: Array<string | null>;
387
+ tokens?: Array<TokenLineV1 | null>;
388
+ diffKind?: Array<DiffKind | null>;
389
+ oldNo?: Array<number | null>;
390
+ newNo?: Array<number | null>;
321
391
  } | {
322
392
  op: "setHTML";
323
393
  at: NodePath;
@@ -353,4 +423,4 @@ interface CoalescingMetrics {
353
423
  insertChildCoalesced: number;
354
424
  }
355
425
 
356
- export { type ASTInlinePlugin, type Block, type CoalescingMetrics, type CompiledMdxModule, type FormatAnticipationConfig, type InlineHtmlDescriptor, type InlineNode, type InlinePlugin, LANGUAGE_ALIASES, type MixedContentSegment, type NodePath, type NodeSnapshot, PATCH_ROOT_ID, type Patch, type PatchMetrics, type PerformanceMetrics, type ProtectedRange, type ProtectedRangeKind, type RegexAnticipationPattern, type RegexInlinePlugin, type SetPropsBatchEntry, type WorkerErrorPayload, type WorkerIn, type WorkerOut, type WorkerPhase };
426
+ export { type ASTInlinePlugin, type Block, type CoalescingMetrics, type CodeHighlightOutputMode, type CodeHighlightingMode, type CompiledMdxModule, type DiffBlock, type DiffKind, type DiffLine, type DiffLineKind, type FormatAnticipationConfig, type InlineHtmlDescriptor, type InlineNode, type InlinePlugin, LANGUAGE_ALIASES, type LazyTokenizationPriority, type MixedContentSegment, type NodePath, type NodeSnapshot, PATCH_ROOT_ID, type Patch, type PatchMetrics, type PerformanceMetrics, type ProtectedRange, type ProtectedRangeKind, type RegexAnticipationPattern, type RegexInlinePlugin, type SetPropsBatchEntry, type ThemedLine, type ThemedToken, type TokenLineV1, type TokenSpan, type TokenStyle, type WorkerErrorPayload, type WorkerIn, type WorkerOut, type WorkerPhase };
package/dist/types.d.ts CHANGED
@@ -47,6 +47,8 @@ type FormatAnticipationConfig = boolean | {
47
47
  mdx?: boolean;
48
48
  regex?: boolean;
49
49
  };
50
+ type CodeHighlightingMode = "final" | "incremental" | "live";
51
+ type CodeHighlightOutputMode = "html" | "tokens" | "both";
50
52
  interface InlineHtmlDescriptor {
51
53
  tagName: string;
52
54
  attributes: Record<string, string>;
@@ -109,6 +111,47 @@ type InlineNode = {
109
111
  label: string;
110
112
  number?: number;
111
113
  };
114
+ type TokenStyle = {
115
+ fg?: string;
116
+ bg?: string;
117
+ fs?: number;
118
+ };
119
+ type TokenSpan = {
120
+ t: string;
121
+ v?: {
122
+ dark?: TokenStyle;
123
+ light?: TokenStyle;
124
+ };
125
+ s?: TokenStyle;
126
+ };
127
+ type TokenLineV1 = {
128
+ spans: TokenSpan[];
129
+ };
130
+ type DiffKind = "add" | "remove" | "context" | "hunk" | "meta";
131
+ type DiffLineKind = "meta" | "hunk" | "add" | "del" | "context";
132
+ type ThemedToken = {
133
+ content: string;
134
+ color?: string | null;
135
+ fontStyle?: number | null;
136
+ };
137
+ type ThemedLine = ThemedToken[];
138
+ type DiffLine = {
139
+ kind: DiffLineKind;
140
+ oldNo?: number | null;
141
+ newNo?: number | null;
142
+ raw: string;
143
+ tokens?: ThemedLine | null;
144
+ };
145
+ type DiffBlock = {
146
+ kind: "diff";
147
+ filePath?: string | null;
148
+ language?: string | null;
149
+ lines: DiffLine[];
150
+ additions?: number | null;
151
+ deletions?: number | null;
152
+ unified?: string | null;
153
+ };
154
+ type LazyTokenizationPriority = "visible" | "prefetch";
112
155
  /**
113
156
  * Worker communication protocol
114
157
  */
@@ -124,8 +167,17 @@ type WorkerIn = {
124
167
  callouts?: boolean;
125
168
  math?: boolean;
126
169
  formatAnticipation?: FormatAnticipationConfig;
170
+ codeHighlighting?: CodeHighlightingMode;
171
+ outputMode?: CodeHighlightOutputMode;
127
172
  liveCodeHighlighting?: boolean;
173
+ liveTokenization?: boolean;
174
+ emitHighlightTokens?: boolean;
175
+ emitDiffBlocks?: boolean;
128
176
  mdxComponentNames?: string[];
177
+ lazyTokenization?: {
178
+ enabled?: boolean;
179
+ thresholdLines?: number;
180
+ };
129
181
  };
130
182
  mdx?: {
131
183
  compileMode?: "server" | "worker";
@@ -137,6 +189,12 @@ type WorkerIn = {
137
189
  type: "FINALIZE";
138
190
  } | {
139
191
  type: "DEBUG_STATE";
192
+ } | {
193
+ type: "TOKENIZE_RANGE";
194
+ blockId: string;
195
+ startLine: number;
196
+ endLine: number;
197
+ priority?: LazyTokenizationPriority;
140
198
  } | {
141
199
  type: "MDX_COMPILED";
142
200
  blockId: string;
@@ -260,6 +318,14 @@ interface PerformanceMetrics {
260
318
  appendLineBatches?: number;
261
319
  appendLineTotalLines?: number;
262
320
  appendLineMaxLines?: number;
321
+ lazyTokenization?: {
322
+ requests: number;
323
+ avgRangeLines: number;
324
+ maxRangeLines: number;
325
+ avgLatencyMs: number;
326
+ maxLatencyMs: number;
327
+ maxQueue: number;
328
+ };
263
329
  }
264
330
  /**
265
331
  * Incremental patch protocol (initial block-level implementation).
@@ -318,6 +384,10 @@ type Patch = {
318
384
  startIndex: number;
319
385
  lines: string[];
320
386
  highlight?: Array<string | null>;
387
+ tokens?: Array<TokenLineV1 | null>;
388
+ diffKind?: Array<DiffKind | null>;
389
+ oldNo?: Array<number | null>;
390
+ newNo?: Array<number | null>;
321
391
  } | {
322
392
  op: "setHTML";
323
393
  at: NodePath;
@@ -353,4 +423,4 @@ interface CoalescingMetrics {
353
423
  insertChildCoalesced: number;
354
424
  }
355
425
 
356
- export { type ASTInlinePlugin, type Block, type CoalescingMetrics, type CompiledMdxModule, type FormatAnticipationConfig, type InlineHtmlDescriptor, type InlineNode, type InlinePlugin, LANGUAGE_ALIASES, type MixedContentSegment, type NodePath, type NodeSnapshot, PATCH_ROOT_ID, type Patch, type PatchMetrics, type PerformanceMetrics, type ProtectedRange, type ProtectedRangeKind, type RegexAnticipationPattern, type RegexInlinePlugin, type SetPropsBatchEntry, type WorkerErrorPayload, type WorkerIn, type WorkerOut, type WorkerPhase };
426
+ export { type ASTInlinePlugin, type Block, type CoalescingMetrics, type CodeHighlightOutputMode, type CodeHighlightingMode, type CompiledMdxModule, type DiffBlock, type DiffKind, type DiffLine, type DiffLineKind, type FormatAnticipationConfig, type InlineHtmlDescriptor, type InlineNode, type InlinePlugin, LANGUAGE_ALIASES, type LazyTokenizationPriority, type MixedContentSegment, type NodePath, type NodeSnapshot, PATCH_ROOT_ID, type Patch, type PatchMetrics, type PerformanceMetrics, type ProtectedRange, type ProtectedRangeKind, type RegexAnticipationPattern, type RegexInlinePlugin, type SetPropsBatchEntry, type ThemedLine, type ThemedToken, type TokenLineV1, type TokenSpan, type TokenStyle, type WorkerErrorPayload, type WorkerIn, type WorkerOut, type WorkerPhase };
package/dist/utils.cjs CHANGED
@@ -91,23 +91,33 @@ function getBlockKey(block) {
91
91
  }
92
92
  function detectMDX(content, options) {
93
93
  const inlineCodeRanges = collectInlineCodeRanges(content);
94
+ const protectedRanges = options?.protectedRanges ?? [];
95
+ const baseOffset = typeof options?.baseOffset === "number" ? options.baseOffset : 0;
96
+ const protectedKinds = protectedRanges.length ? /* @__PURE__ */ new Set(["math-inline", "math-display", "code-inline", "code-block", "html-inline", "html-block", "autolink"]) : null;
94
97
  const componentPattern = /<([A-Z][\w-]*)(\s|\/?>)/g;
95
98
  let componentMatch = componentPattern.exec(content);
96
99
  while (componentMatch !== null) {
97
100
  const start = componentMatch.index;
98
101
  const end = start + componentMatch[0].length;
99
- if (!isWithinRanges(start, end, inlineCodeRanges)) {
100
- return true;
102
+ if (isWithinRanges(start, end, inlineCodeRanges)) {
103
+ componentMatch = componentPattern.exec(content);
104
+ continue;
105
+ }
106
+ if (protectedKinds) {
107
+ const absoluteStart = baseOffset + start;
108
+ const absoluteEnd = baseOffset + end;
109
+ const covered = protectedRanges.some((range) => protectedKinds.has(range.kind) && range.from <= absoluteStart && range.to >= absoluteEnd);
110
+ if (covered) {
111
+ componentMatch = componentPattern.exec(content);
112
+ continue;
113
+ }
101
114
  }
102
- componentMatch = componentPattern.exec(content);
115
+ return true;
103
116
  }
104
117
  if (/(^|\n)\s*(import|export)\s/.test(content)) {
105
118
  return true;
106
119
  }
107
120
  const expressionPattern = /\{[^{}]+\}/g;
108
- const protectedRanges = options?.protectedRanges ?? [];
109
- const baseOffset = typeof options?.baseOffset === "number" ? options.baseOffset : 0;
110
- const protectedKinds = protectedRanges.length ? /* @__PURE__ */ new Set(["math-inline", "math-display", "code-inline", "code-block"]) : null;
111
121
  for (let match = expressionPattern.exec(content); match !== null; match = expressionPattern.exec(content)) {
112
122
  const index = match.index;
113
123
  const prev = index > 0 ? content[index - 1] : "";
package/dist/utils.mjs CHANGED
@@ -55,23 +55,33 @@ function getBlockKey(block) {
55
55
  }
56
56
  function detectMDX(content, options) {
57
57
  const inlineCodeRanges = collectInlineCodeRanges(content);
58
+ const protectedRanges = options?.protectedRanges ?? [];
59
+ const baseOffset = typeof options?.baseOffset === "number" ? options.baseOffset : 0;
60
+ const protectedKinds = protectedRanges.length ? /* @__PURE__ */ new Set(["math-inline", "math-display", "code-inline", "code-block", "html-inline", "html-block", "autolink"]) : null;
58
61
  const componentPattern = /<([A-Z][\w-]*)(\s|\/?>)/g;
59
62
  let componentMatch = componentPattern.exec(content);
60
63
  while (componentMatch !== null) {
61
64
  const start = componentMatch.index;
62
65
  const end = start + componentMatch[0].length;
63
- if (!isWithinRanges(start, end, inlineCodeRanges)) {
64
- return true;
66
+ if (isWithinRanges(start, end, inlineCodeRanges)) {
67
+ componentMatch = componentPattern.exec(content);
68
+ continue;
69
+ }
70
+ if (protectedKinds) {
71
+ const absoluteStart = baseOffset + start;
72
+ const absoluteEnd = baseOffset + end;
73
+ const covered = protectedRanges.some((range) => protectedKinds.has(range.kind) && range.from <= absoluteStart && range.to >= absoluteEnd);
74
+ if (covered) {
75
+ componentMatch = componentPattern.exec(content);
76
+ continue;
77
+ }
65
78
  }
66
- componentMatch = componentPattern.exec(content);
79
+ return true;
67
80
  }
68
81
  if (/(^|\n)\s*(import|export)\s/.test(content)) {
69
82
  return true;
70
83
  }
71
84
  const expressionPattern = /\{[^{}]+\}/g;
72
- const protectedRanges = options?.protectedRanges ?? [];
73
- const baseOffset = typeof options?.baseOffset === "number" ? options.baseOffset : 0;
74
- const protectedKinds = protectedRanges.length ? /* @__PURE__ */ new Set(["math-inline", "math-display", "code-inline", "code-block"]) : null;
75
85
  for (let match = expressionPattern.exec(content); match !== null; match = expressionPattern.exec(content)) {
76
86
  const index = match.index;
77
87
  const prev = index > 0 ? content[index - 1] : "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-mdx/core",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "Core types, snapshot utilities, and perf helpers for the Streaming Markdown V2 stack",
5
5
  "license": "MIT",
6
6
  "repository": {