@ai-react-markdown/engine 2.5.4 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +99 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -194
- package/dist/index.d.ts +7 -194
- package/dist/index.dev.cjs +99 -11
- package/dist/index.dev.cjs.map +1 -1
- package/dist/index.dev.js +99 -11
- package/dist/index.dev.js.map +1 -1
- package/dist/index.js +99 -11
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -316,202 +316,15 @@ interface FreezeScanResult {
|
|
|
316
316
|
* re-lexing the confirmed prefix. Single-consumer; see module docs. */
|
|
317
317
|
checkpoint: FreezeScanCheckpoint;
|
|
318
318
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
htmlBalanced: boolean;
|
|
326
|
-
/** Rolling continuation-hazard verdict at emission (blocker 3). */
|
|
327
|
-
hazard: boolean;
|
|
328
|
-
/** Blocker-6: the run ending at this blank left balanced FLOATING raw
|
|
329
|
-
* remnant whose hast seam is tail-dependent; reject this candidate. */
|
|
330
|
-
seamRisk: boolean;
|
|
331
|
-
/** Blocker-4 settle verdict, decided by the NEXT confirmed line (`null`
|
|
332
|
-
* while that line hasn't confirmed — only the newest candidate can be
|
|
333
|
-
* pending). Storing the verdict instead of the line lets the checkpoint
|
|
334
|
-
* drop its lines array, which retained a full copy of the document
|
|
335
|
-
* (round-2 review: ~2-3× doc size per mounted instance). */
|
|
336
|
-
defListSettled: boolean | null;
|
|
337
|
-
}
|
|
338
|
-
interface UnresolvedRef {
|
|
339
|
-
offset: number;
|
|
340
|
-
label: string;
|
|
341
|
-
footnote: boolean;
|
|
342
|
-
}
|
|
343
|
-
/** Mutable resume state. All fields describe the scan strictly BEFORE the
|
|
344
|
-
* first unconfirmed character (`confirmedOffset`). */
|
|
319
|
+
/** Opaque resume token. Produced by one `computeFreezeBoundary` call and
|
|
320
|
+
* passed back on the next APPEND-ONLY call; the only supported operations
|
|
321
|
+
* are storing it and passing it back. The field set is an implementation
|
|
322
|
+
* detail of the scanner and changes between minor versions — the brand key
|
|
323
|
+
* (a structural string literal, safe across dual d.ts entries) is all that
|
|
324
|
+
* ships in the public type. */
|
|
345
325
|
interface FreezeScanCheckpoint {
|
|
346
|
-
|
|
347
|
-
/** Grammar-profile switches baked at creation — a checkpoint is only
|
|
348
|
-
* resumable under the exact profile that built it. */
|
|
349
|
-
mathFlow: boolean;
|
|
350
|
-
referenceTaint: boolean;
|
|
351
|
-
/** Start offset of the first line NOT yet baked into this checkpoint. */
|
|
352
|
-
confirmedOffset: number;
|
|
353
|
-
candidates: Candidate[];
|
|
354
|
-
defs: Map<string, number>;
|
|
355
|
-
footnoteDefs: Map<string, number>;
|
|
356
|
-
unresolvedRefs: UnresolvedRef[];
|
|
357
|
-
tagBalance: Map<string, number>;
|
|
358
|
-
openTotal: number;
|
|
359
|
-
commentOpen: boolean;
|
|
360
|
-
piOpen: boolean;
|
|
361
|
-
declOpen: boolean;
|
|
362
|
-
cdataOpen: boolean;
|
|
363
|
-
/** parse5 BOGUS COMMENT open inside a real html-flow run: `<!` not
|
|
364
|
-
* followed by `--` / letter / `[CDATA[`, or `</` not followed by a
|
|
365
|
-
* letter (`<!\n`, `<!-`, `</\n`, `<//`) — eaten up to the next `>`;
|
|
366
|
-
* a `</div>` inside is comment text, not a close (2026-08-19 review r2
|
|
367
|
-
* P1-3: the balance zeroed past an open div). micromark has no such
|
|
368
|
-
* construct: those bytes are just html-block content. */
|
|
369
|
-
bogusOpen: boolean;
|
|
370
|
-
/** The RAW_TEXT_ELEMENTS element currently open (its content is text to
|
|
371
|
-
* parse5): tags and comment tokens are ignored until `</name>`. */
|
|
372
|
-
rawTextOpen: string | null;
|
|
373
|
-
inFence: boolean;
|
|
374
|
-
fenceChar: string;
|
|
375
|
-
fenceLen: number;
|
|
376
|
-
inMath: boolean;
|
|
377
|
-
/** Opening dollar-run length while inMath — the close run must match it. */
|
|
378
|
-
mathFenceLen: number;
|
|
379
|
-
/** Indent (0-3 spaces) of the line that opened the current fence/math
|
|
380
|
-
* block. Not a blocker input — read by `phantomSuffixCloser` to emit a
|
|
381
|
-
* closer at the SAME indent, which closes the block whether it sits at
|
|
382
|
-
* top level (≤3 spaces are allowed there) or inside a list item whose
|
|
383
|
-
* content indent the opener line already satisfies. */
|
|
384
|
-
openIndent: number;
|
|
385
|
-
blankRun: number;
|
|
386
|
-
lastBlankStart: number;
|
|
387
|
-
/** Rolling blocker-3 verdict ("nearest decisive block start so far"). */
|
|
388
|
-
hazardVerdict: boolean;
|
|
389
|
-
/** Previous confirmed line was blank (block-start detection). */
|
|
390
|
-
prevLineBlank: boolean;
|
|
391
|
-
/** Previous confirmed line was a plain text line (def-chain detection). */
|
|
392
|
-
prevLineWasText: boolean;
|
|
393
|
-
/** Previous confirmed line registered a VALID definition (def chains). */
|
|
394
|
-
prevLineWasValidDef: boolean;
|
|
395
|
-
/** An earlier line of the current paragraph left an unpaired backtick
|
|
396
|
-
* run — masking is disabled until the paragraph ends (safety gate). */
|
|
397
|
-
/** A `[` left unclosed at the end of a paragraph line (code spans
|
|
398
|
-
* masked). micromark lets a reference label span soft line breaks, so
|
|
399
|
-
* the label may close on a LATER line where the per-line REF_RE never
|
|
400
|
-
* sees a `[…]` pair (v2.4.1 review P1: `see [foo\nbar] end` + a late
|
|
401
|
-
* `[foo bar]: /u` retargeted frozen output). Cleared wherever the
|
|
402
|
-
* paragraph ends. */
|
|
403
|
-
openBracket: {
|
|
404
|
-
offset: number;
|
|
405
|
-
text: string;
|
|
406
|
-
} | null;
|
|
407
|
-
paragraphHasUnpairedRun: boolean;
|
|
408
|
-
/** Blocker-6 pending flag: a confirmed html-flow line left balanced
|
|
409
|
-
* floating raw remnant, and no later content line has pinned the seam
|
|
410
|
-
* yet. Persists across blank lines (every candidate emitted while set
|
|
411
|
-
* has the remnant as its last frozen child); cleared by the next
|
|
412
|
-
* non-blank line that starts OUTSIDE an html-flow run. */
|
|
413
|
-
htmlSeamPending: boolean;
|
|
414
|
-
/** A line since the last blank started with `<` at block indent — an html
|
|
415
|
-
* FLOW block is (approximately) running, and it only ends at a blank
|
|
416
|
-
* line. micromark does no inline parsing there: backtick runs are
|
|
417
|
-
* literal text, so code-span masking would hide REAL tags from the
|
|
418
|
-
* balance scan (under-block — fuzz counterexample: `</details>` followed
|
|
419
|
-
* by an unblanked `` `<div>` `` line). While set, masking is skipped —
|
|
420
|
-
* which can only over-block (safe direction). */
|
|
421
|
-
htmlFlowSinceBlank: boolean;
|
|
422
|
-
/** Offset of the first fence/math OPEN suppressed by `htmlFlowSinceBlank`
|
|
423
|
-
* (Infinity = none). Whether the run really swallowed that line depends
|
|
424
|
-
* on container context the line scan cannot see (`<embed` inside a list
|
|
425
|
-
* item is a lazy paragraph line, and the glued `$$` a REAL math open —
|
|
426
|
-
* seed-20260757 under-block: the tracker's fence phase INVERTS from that
|
|
427
|
-
* line on, every later close reads as an open, and the corruption never
|
|
428
|
-
* resyncs). Candidates past this offset are rejected outright — sticky,
|
|
429
|
-
* pure over-block; candidates before it are untouched (the ambiguous
|
|
430
|
-
* region then re-parses inside the tail). The rolling hazard poison for
|
|
431
|
-
* ambiguous tag names stays, but it decays at the next decisive block
|
|
432
|
-
* start — this field is the phase-corruption backstop that does not. */
|
|
433
|
-
phasePoisonedAt: number;
|
|
434
|
-
/** Tag names of line-truncated opens (`<div` at EOL) counted into
|
|
435
|
-
* tagBalance but not yet confirmed by a later `>` — reverted at the next
|
|
436
|
-
* blank line (a tag cannot span one). See TRUNCATED_TAG_RE. */
|
|
437
|
-
pendingTruncatedTags: string[];
|
|
438
|
-
/** Line-truncated CLOSING tags (`</div` + EOL, no `>`) inside an html-flow
|
|
439
|
-
* run, waiting for a `>` on a later line of the same run. Unlike opens
|
|
440
|
-
* they are NOT counted up front: a close tag cannot carry attributes, so
|
|
441
|
-
* `para </style` (prose to micromark, still-open element to parse5 —
|
|
442
|
-
* RAWTEXT waits for the `>`) must not zero the balance (2026-08-19
|
|
443
|
-
* review P1 — the boundary crossed an open `<style>`). Confirmed and
|
|
444
|
-
* applied only when a later line of the run brings the `>` (parse5
|
|
445
|
-
* completes the end tag; micromark's block ends at the blank anyway);
|
|
446
|
-
* dropped unapplied at the blank (element stays counted — over-block).
|
|
447
|
-
* Paragraph-context truncated closes are never pended: a `>` at block
|
|
448
|
-
* indent on the next line is a blockquote to micromark, and the one
|
|
449
|
-
* shape that would complete the inline close (`</b\n >`, a 4+-space
|
|
450
|
-
* lazy continuation) is not modelled — the element stays counted,
|
|
451
|
-
* over-block. */
|
|
452
|
-
pendingTruncatedCloses: string[];
|
|
453
|
-
/** An html-flow line ended inside a tag (`<div`, `</div`, `<br` — open,
|
|
454
|
-
* close or void, with or without attributes): parse5's tokenizer is
|
|
455
|
-
* still in that tag, so on the following lines everything up to the
|
|
456
|
-
* FIRST `>` is attribute garbage — `</div>` there does NOT close
|
|
457
|
-
* anything (oracle review of 2.4.4: `<div>\n<div>\n</div\n</div>` froze
|
|
458
|
-
* past the still-open outer div; pre-existing, 1-char slices). While
|
|
459
|
-
* set: a line without `>` gets no tag scan at all; the line with the
|
|
460
|
-
* `>` completes the pending close (if any), then only its text after
|
|
461
|
-
* that `>` is scanned. Cleared there and at the blank line. */
|
|
462
|
-
tagAcrossLines: boolean;
|
|
463
|
-
/** Indent of the line that set `tagAcrossLines`. A following line that
|
|
464
|
-
* DE-INDENTS below it may have left the container (a list item's html
|
|
465
|
-
* block ends there; hast-util-raw resets the tokenizer at the li/ul
|
|
466
|
-
* boundary, so a `<div>` on that line is a real start tag, not garbage)
|
|
467
|
-
* — or may still be the same html block (root-level ` </div\n</div>`,
|
|
468
|
-
* still garbage). Unknowable here → poison (oracle 3rd pass). */
|
|
469
|
-
tagAcrossLinesIndent: number;
|
|
470
|
-
/** parse5's position inside that tag's attribute area at the end of the
|
|
471
|
-
* last scanned line (see TagAttrState). A quoted value left open swallows
|
|
472
|
-
* `>` and line endings; a `>` that arrives while `outside` ends the tag. */
|
|
473
|
-
tagAcrossLinesState: TagAttrState;
|
|
474
|
-
/** The html-flow run since the last blank REALLY started as a micromark
|
|
475
|
-
* html block (type 6 / type 1 / a paragraph-not-interrupting type 7) —
|
|
476
|
-
* as opposed to `htmlFlowSinceBlank`, which any `<tag` / `</tag` line
|
|
477
|
-
* start sets (over-approximation, fine for its over-blocking uses). Only
|
|
478
|
-
* in a real run are the bytes raw to parse5 across line endings; the
|
|
479
|
-
* cross-line-tag garbage model (`tagAcrossLines`, pended closes) is
|
|
480
|
-
* gated on it — in a paragraph starting `</i` the next line's `<div>` /
|
|
481
|
-
* `<!--` are REAL blocks (oracle re-check of 2.4.4: gating on
|
|
482
|
-
* htmlFlowSinceBlank swallowed them — a new under-block). Sticky to the
|
|
483
|
-
* blank; a type 6/1 start on a later line of a non-real run promotes it. */
|
|
484
|
-
htmlFlowReal: boolean;
|
|
485
|
-
/** The run since the last blank was OPENED by a CommonMark type-1 block
|
|
486
|
-
* (`<script` / `<pre` / `<style` / `<textarea`). Type 1 is the one block
|
|
487
|
-
* that ends at its CLOSER line rather than at a blank, so `htmlFlowReal`
|
|
488
|
-
* being "sticky to the blank" over-ran it: the line after `</script>` is
|
|
489
|
-
* a fresh PARAGRAPH to micromark, but the scanner still treated it as
|
|
490
|
-
* raw html flow — and in a real run end tags may carry attributes, so a
|
|
491
|
-
* `</div a="b">` there was counted as a REAL close. `p <div> x </div
|
|
492
|
-
* a="b"> y` after a `</script>` line therefore froze past an element the
|
|
493
|
-
* actual parse leaves OPEN, nesting the whole rest of the document
|
|
494
|
-
* inside it (2026-08-20 soak leg 2; identical for pre/style/textarea,
|
|
495
|
-
* and correct as-is for a type-6 run, which really does reach the blank).
|
|
496
|
-
* Only set when the type-1 opener is the line that STARTED the run — a
|
|
497
|
-
* `<script>` nested inside an open type-6 block must not end it. */
|
|
498
|
-
type1FlowOpen: boolean;
|
|
499
|
-
/** The currently open raw-text element (`rawTextOpen`) was opened INLINE —
|
|
500
|
-
* in paragraph context rather than in a real html-flow run. */
|
|
501
|
-
rawTextInline: boolean;
|
|
502
|
-
/** parse5 is in "script data escaped": a `<!--` has been seen inside the
|
|
503
|
-
* open `<script>`. Only meaningful while `rawTextOpen === 'script'`.
|
|
504
|
-
* Deliberately NOT cleared by `-->` — staying escaped can only poison
|
|
505
|
-
* more often, which is the over-blocking side. */
|
|
506
|
-
scriptDataEscaped: boolean;
|
|
326
|
+
readonly '~freezeScanCheckpoint'?: never;
|
|
507
327
|
}
|
|
508
|
-
/** parse5 tokenizer position inside a tag's attribute area, tracked across
|
|
509
|
-
* line endings for `tagAcrossLines` (2026-08-19 review r2 P1-2 / P2-3):
|
|
510
|
-
* `outside` = before/in an attribute name (a `"` here starts a NAME, not a
|
|
511
|
-
* value); `afterEq` = just past `=`; `unquoted` = in an unquoted value
|
|
512
|
-
* (whitespace ends it); `"` / `'` = inside a quoted value — `>` and line
|
|
513
|
-
* endings are value bytes there, only the matching quote leaves it. */
|
|
514
|
-
type TagAttrState = 'outside' | 'afterEq' | 'unquoted' | '"' | "'";
|
|
515
328
|
declare function computeFreezeBoundary(text: string, options: FreezeBoundaryOptions, resume?: FreezeScanCheckpoint | null): FreezeScanResult;
|
|
516
329
|
|
|
517
330
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -316,202 +316,15 @@ interface FreezeScanResult {
|
|
|
316
316
|
* re-lexing the confirmed prefix. Single-consumer; see module docs. */
|
|
317
317
|
checkpoint: FreezeScanCheckpoint;
|
|
318
318
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
htmlBalanced: boolean;
|
|
326
|
-
/** Rolling continuation-hazard verdict at emission (blocker 3). */
|
|
327
|
-
hazard: boolean;
|
|
328
|
-
/** Blocker-6: the run ending at this blank left balanced FLOATING raw
|
|
329
|
-
* remnant whose hast seam is tail-dependent; reject this candidate. */
|
|
330
|
-
seamRisk: boolean;
|
|
331
|
-
/** Blocker-4 settle verdict, decided by the NEXT confirmed line (`null`
|
|
332
|
-
* while that line hasn't confirmed — only the newest candidate can be
|
|
333
|
-
* pending). Storing the verdict instead of the line lets the checkpoint
|
|
334
|
-
* drop its lines array, which retained a full copy of the document
|
|
335
|
-
* (round-2 review: ~2-3× doc size per mounted instance). */
|
|
336
|
-
defListSettled: boolean | null;
|
|
337
|
-
}
|
|
338
|
-
interface UnresolvedRef {
|
|
339
|
-
offset: number;
|
|
340
|
-
label: string;
|
|
341
|
-
footnote: boolean;
|
|
342
|
-
}
|
|
343
|
-
/** Mutable resume state. All fields describe the scan strictly BEFORE the
|
|
344
|
-
* first unconfirmed character (`confirmedOffset`). */
|
|
319
|
+
/** Opaque resume token. Produced by one `computeFreezeBoundary` call and
|
|
320
|
+
* passed back on the next APPEND-ONLY call; the only supported operations
|
|
321
|
+
* are storing it and passing it back. The field set is an implementation
|
|
322
|
+
* detail of the scanner and changes between minor versions — the brand key
|
|
323
|
+
* (a structural string literal, safe across dual d.ts entries) is all that
|
|
324
|
+
* ships in the public type. */
|
|
345
325
|
interface FreezeScanCheckpoint {
|
|
346
|
-
|
|
347
|
-
/** Grammar-profile switches baked at creation — a checkpoint is only
|
|
348
|
-
* resumable under the exact profile that built it. */
|
|
349
|
-
mathFlow: boolean;
|
|
350
|
-
referenceTaint: boolean;
|
|
351
|
-
/** Start offset of the first line NOT yet baked into this checkpoint. */
|
|
352
|
-
confirmedOffset: number;
|
|
353
|
-
candidates: Candidate[];
|
|
354
|
-
defs: Map<string, number>;
|
|
355
|
-
footnoteDefs: Map<string, number>;
|
|
356
|
-
unresolvedRefs: UnresolvedRef[];
|
|
357
|
-
tagBalance: Map<string, number>;
|
|
358
|
-
openTotal: number;
|
|
359
|
-
commentOpen: boolean;
|
|
360
|
-
piOpen: boolean;
|
|
361
|
-
declOpen: boolean;
|
|
362
|
-
cdataOpen: boolean;
|
|
363
|
-
/** parse5 BOGUS COMMENT open inside a real html-flow run: `<!` not
|
|
364
|
-
* followed by `--` / letter / `[CDATA[`, or `</` not followed by a
|
|
365
|
-
* letter (`<!\n`, `<!-`, `</\n`, `<//`) — eaten up to the next `>`;
|
|
366
|
-
* a `</div>` inside is comment text, not a close (2026-08-19 review r2
|
|
367
|
-
* P1-3: the balance zeroed past an open div). micromark has no such
|
|
368
|
-
* construct: those bytes are just html-block content. */
|
|
369
|
-
bogusOpen: boolean;
|
|
370
|
-
/** The RAW_TEXT_ELEMENTS element currently open (its content is text to
|
|
371
|
-
* parse5): tags and comment tokens are ignored until `</name>`. */
|
|
372
|
-
rawTextOpen: string | null;
|
|
373
|
-
inFence: boolean;
|
|
374
|
-
fenceChar: string;
|
|
375
|
-
fenceLen: number;
|
|
376
|
-
inMath: boolean;
|
|
377
|
-
/** Opening dollar-run length while inMath — the close run must match it. */
|
|
378
|
-
mathFenceLen: number;
|
|
379
|
-
/** Indent (0-3 spaces) of the line that opened the current fence/math
|
|
380
|
-
* block. Not a blocker input — read by `phantomSuffixCloser` to emit a
|
|
381
|
-
* closer at the SAME indent, which closes the block whether it sits at
|
|
382
|
-
* top level (≤3 spaces are allowed there) or inside a list item whose
|
|
383
|
-
* content indent the opener line already satisfies. */
|
|
384
|
-
openIndent: number;
|
|
385
|
-
blankRun: number;
|
|
386
|
-
lastBlankStart: number;
|
|
387
|
-
/** Rolling blocker-3 verdict ("nearest decisive block start so far"). */
|
|
388
|
-
hazardVerdict: boolean;
|
|
389
|
-
/** Previous confirmed line was blank (block-start detection). */
|
|
390
|
-
prevLineBlank: boolean;
|
|
391
|
-
/** Previous confirmed line was a plain text line (def-chain detection). */
|
|
392
|
-
prevLineWasText: boolean;
|
|
393
|
-
/** Previous confirmed line registered a VALID definition (def chains). */
|
|
394
|
-
prevLineWasValidDef: boolean;
|
|
395
|
-
/** An earlier line of the current paragraph left an unpaired backtick
|
|
396
|
-
* run — masking is disabled until the paragraph ends (safety gate). */
|
|
397
|
-
/** A `[` left unclosed at the end of a paragraph line (code spans
|
|
398
|
-
* masked). micromark lets a reference label span soft line breaks, so
|
|
399
|
-
* the label may close on a LATER line where the per-line REF_RE never
|
|
400
|
-
* sees a `[…]` pair (v2.4.1 review P1: `see [foo\nbar] end` + a late
|
|
401
|
-
* `[foo bar]: /u` retargeted frozen output). Cleared wherever the
|
|
402
|
-
* paragraph ends. */
|
|
403
|
-
openBracket: {
|
|
404
|
-
offset: number;
|
|
405
|
-
text: string;
|
|
406
|
-
} | null;
|
|
407
|
-
paragraphHasUnpairedRun: boolean;
|
|
408
|
-
/** Blocker-6 pending flag: a confirmed html-flow line left balanced
|
|
409
|
-
* floating raw remnant, and no later content line has pinned the seam
|
|
410
|
-
* yet. Persists across blank lines (every candidate emitted while set
|
|
411
|
-
* has the remnant as its last frozen child); cleared by the next
|
|
412
|
-
* non-blank line that starts OUTSIDE an html-flow run. */
|
|
413
|
-
htmlSeamPending: boolean;
|
|
414
|
-
/** A line since the last blank started with `<` at block indent — an html
|
|
415
|
-
* FLOW block is (approximately) running, and it only ends at a blank
|
|
416
|
-
* line. micromark does no inline parsing there: backtick runs are
|
|
417
|
-
* literal text, so code-span masking would hide REAL tags from the
|
|
418
|
-
* balance scan (under-block — fuzz counterexample: `</details>` followed
|
|
419
|
-
* by an unblanked `` `<div>` `` line). While set, masking is skipped —
|
|
420
|
-
* which can only over-block (safe direction). */
|
|
421
|
-
htmlFlowSinceBlank: boolean;
|
|
422
|
-
/** Offset of the first fence/math OPEN suppressed by `htmlFlowSinceBlank`
|
|
423
|
-
* (Infinity = none). Whether the run really swallowed that line depends
|
|
424
|
-
* on container context the line scan cannot see (`<embed` inside a list
|
|
425
|
-
* item is a lazy paragraph line, and the glued `$$` a REAL math open —
|
|
426
|
-
* seed-20260757 under-block: the tracker's fence phase INVERTS from that
|
|
427
|
-
* line on, every later close reads as an open, and the corruption never
|
|
428
|
-
* resyncs). Candidates past this offset are rejected outright — sticky,
|
|
429
|
-
* pure over-block; candidates before it are untouched (the ambiguous
|
|
430
|
-
* region then re-parses inside the tail). The rolling hazard poison for
|
|
431
|
-
* ambiguous tag names stays, but it decays at the next decisive block
|
|
432
|
-
* start — this field is the phase-corruption backstop that does not. */
|
|
433
|
-
phasePoisonedAt: number;
|
|
434
|
-
/** Tag names of line-truncated opens (`<div` at EOL) counted into
|
|
435
|
-
* tagBalance but not yet confirmed by a later `>` — reverted at the next
|
|
436
|
-
* blank line (a tag cannot span one). See TRUNCATED_TAG_RE. */
|
|
437
|
-
pendingTruncatedTags: string[];
|
|
438
|
-
/** Line-truncated CLOSING tags (`</div` + EOL, no `>`) inside an html-flow
|
|
439
|
-
* run, waiting for a `>` on a later line of the same run. Unlike opens
|
|
440
|
-
* they are NOT counted up front: a close tag cannot carry attributes, so
|
|
441
|
-
* `para </style` (prose to micromark, still-open element to parse5 —
|
|
442
|
-
* RAWTEXT waits for the `>`) must not zero the balance (2026-08-19
|
|
443
|
-
* review P1 — the boundary crossed an open `<style>`). Confirmed and
|
|
444
|
-
* applied only when a later line of the run brings the `>` (parse5
|
|
445
|
-
* completes the end tag; micromark's block ends at the blank anyway);
|
|
446
|
-
* dropped unapplied at the blank (element stays counted — over-block).
|
|
447
|
-
* Paragraph-context truncated closes are never pended: a `>` at block
|
|
448
|
-
* indent on the next line is a blockquote to micromark, and the one
|
|
449
|
-
* shape that would complete the inline close (`</b\n >`, a 4+-space
|
|
450
|
-
* lazy continuation) is not modelled — the element stays counted,
|
|
451
|
-
* over-block. */
|
|
452
|
-
pendingTruncatedCloses: string[];
|
|
453
|
-
/** An html-flow line ended inside a tag (`<div`, `</div`, `<br` — open,
|
|
454
|
-
* close or void, with or without attributes): parse5's tokenizer is
|
|
455
|
-
* still in that tag, so on the following lines everything up to the
|
|
456
|
-
* FIRST `>` is attribute garbage — `</div>` there does NOT close
|
|
457
|
-
* anything (oracle review of 2.4.4: `<div>\n<div>\n</div\n</div>` froze
|
|
458
|
-
* past the still-open outer div; pre-existing, 1-char slices). While
|
|
459
|
-
* set: a line without `>` gets no tag scan at all; the line with the
|
|
460
|
-
* `>` completes the pending close (if any), then only its text after
|
|
461
|
-
* that `>` is scanned. Cleared there and at the blank line. */
|
|
462
|
-
tagAcrossLines: boolean;
|
|
463
|
-
/** Indent of the line that set `tagAcrossLines`. A following line that
|
|
464
|
-
* DE-INDENTS below it may have left the container (a list item's html
|
|
465
|
-
* block ends there; hast-util-raw resets the tokenizer at the li/ul
|
|
466
|
-
* boundary, so a `<div>` on that line is a real start tag, not garbage)
|
|
467
|
-
* — or may still be the same html block (root-level ` </div\n</div>`,
|
|
468
|
-
* still garbage). Unknowable here → poison (oracle 3rd pass). */
|
|
469
|
-
tagAcrossLinesIndent: number;
|
|
470
|
-
/** parse5's position inside that tag's attribute area at the end of the
|
|
471
|
-
* last scanned line (see TagAttrState). A quoted value left open swallows
|
|
472
|
-
* `>` and line endings; a `>` that arrives while `outside` ends the tag. */
|
|
473
|
-
tagAcrossLinesState: TagAttrState;
|
|
474
|
-
/** The html-flow run since the last blank REALLY started as a micromark
|
|
475
|
-
* html block (type 6 / type 1 / a paragraph-not-interrupting type 7) —
|
|
476
|
-
* as opposed to `htmlFlowSinceBlank`, which any `<tag` / `</tag` line
|
|
477
|
-
* start sets (over-approximation, fine for its over-blocking uses). Only
|
|
478
|
-
* in a real run are the bytes raw to parse5 across line endings; the
|
|
479
|
-
* cross-line-tag garbage model (`tagAcrossLines`, pended closes) is
|
|
480
|
-
* gated on it — in a paragraph starting `</i` the next line's `<div>` /
|
|
481
|
-
* `<!--` are REAL blocks (oracle re-check of 2.4.4: gating on
|
|
482
|
-
* htmlFlowSinceBlank swallowed them — a new under-block). Sticky to the
|
|
483
|
-
* blank; a type 6/1 start on a later line of a non-real run promotes it. */
|
|
484
|
-
htmlFlowReal: boolean;
|
|
485
|
-
/** The run since the last blank was OPENED by a CommonMark type-1 block
|
|
486
|
-
* (`<script` / `<pre` / `<style` / `<textarea`). Type 1 is the one block
|
|
487
|
-
* that ends at its CLOSER line rather than at a blank, so `htmlFlowReal`
|
|
488
|
-
* being "sticky to the blank" over-ran it: the line after `</script>` is
|
|
489
|
-
* a fresh PARAGRAPH to micromark, but the scanner still treated it as
|
|
490
|
-
* raw html flow — and in a real run end tags may carry attributes, so a
|
|
491
|
-
* `</div a="b">` there was counted as a REAL close. `p <div> x </div
|
|
492
|
-
* a="b"> y` after a `</script>` line therefore froze past an element the
|
|
493
|
-
* actual parse leaves OPEN, nesting the whole rest of the document
|
|
494
|
-
* inside it (2026-08-20 soak leg 2; identical for pre/style/textarea,
|
|
495
|
-
* and correct as-is for a type-6 run, which really does reach the blank).
|
|
496
|
-
* Only set when the type-1 opener is the line that STARTED the run — a
|
|
497
|
-
* `<script>` nested inside an open type-6 block must not end it. */
|
|
498
|
-
type1FlowOpen: boolean;
|
|
499
|
-
/** The currently open raw-text element (`rawTextOpen`) was opened INLINE —
|
|
500
|
-
* in paragraph context rather than in a real html-flow run. */
|
|
501
|
-
rawTextInline: boolean;
|
|
502
|
-
/** parse5 is in "script data escaped": a `<!--` has been seen inside the
|
|
503
|
-
* open `<script>`. Only meaningful while `rawTextOpen === 'script'`.
|
|
504
|
-
* Deliberately NOT cleared by `-->` — staying escaped can only poison
|
|
505
|
-
* more often, which is the over-blocking side. */
|
|
506
|
-
scriptDataEscaped: boolean;
|
|
326
|
+
readonly '~freezeScanCheckpoint'?: never;
|
|
507
327
|
}
|
|
508
|
-
/** parse5 tokenizer position inside a tag's attribute area, tracked across
|
|
509
|
-
* line endings for `tagAcrossLines` (2026-08-19 review r2 P1-2 / P2-3):
|
|
510
|
-
* `outside` = before/in an attribute name (a `"` here starts a NAME, not a
|
|
511
|
-
* value); `afterEq` = just past `=`; `unquoted` = in an unquoted value
|
|
512
|
-
* (whitespace ends it); `"` / `'` = inside a quoted value — `>` and line
|
|
513
|
-
* endings are value bytes there, only the matching quote leaves it. */
|
|
514
|
-
type TagAttrState = 'outside' | 'afterEq' | 'unquoted' | '"' | "'";
|
|
515
328
|
declare function computeFreezeBoundary(text: string, options: FreezeBoundaryOptions, resume?: FreezeScanCheckpoint | null): FreezeScanResult;
|
|
516
329
|
|
|
517
330
|
/**
|
package/dist/index.dev.cjs
CHANGED
|
@@ -337,6 +337,26 @@ var HTML_BREAKOUT_TAGS = /* @__PURE__ */ new Set([
|
|
|
337
337
|
"var"
|
|
338
338
|
]);
|
|
339
339
|
var HTML_INTEGRATION_POINTS = ["foreignobject", "desc", "title", "mi", "mo", "mn", "ms", "mtext", "annotation-xml"];
|
|
340
|
+
var SCOPE_BARRIER_NAMES = /* @__PURE__ */ new Set([
|
|
341
|
+
"applet",
|
|
342
|
+
"caption",
|
|
343
|
+
"html",
|
|
344
|
+
"table",
|
|
345
|
+
"td",
|
|
346
|
+
"th",
|
|
347
|
+
"marquee",
|
|
348
|
+
"object",
|
|
349
|
+
"template",
|
|
350
|
+
"mi",
|
|
351
|
+
"mo",
|
|
352
|
+
"mn",
|
|
353
|
+
"ms",
|
|
354
|
+
"mtext",
|
|
355
|
+
"annotation-xml",
|
|
356
|
+
"foreignobject",
|
|
357
|
+
"desc",
|
|
358
|
+
"title"
|
|
359
|
+
]);
|
|
340
360
|
var FOREIGN_ROOT_NAMES = ["svg", "math"];
|
|
341
361
|
var TYPE1_NAMES = /* @__PURE__ */ new Set(["script", "pre", "style", "textarea"]);
|
|
342
362
|
var RAW_TEXT_ELEMENTS = /* @__PURE__ */ new Set([
|
|
@@ -507,6 +527,7 @@ function freshCheckpoint(defListEnabled, mathFlow, referenceTaint) {
|
|
|
507
527
|
piOpen: false,
|
|
508
528
|
bogusOpen: false,
|
|
509
529
|
rawTextOpen: null,
|
|
530
|
+
openStack: [],
|
|
510
531
|
scriptDataEscaped: false,
|
|
511
532
|
declOpen: false,
|
|
512
533
|
cdataOpen: false,
|
|
@@ -630,7 +651,8 @@ function classifyBlockStart(text, indent, defListEnabled) {
|
|
|
630
651
|
function computeFreezeBoundary(text, options, resume) {
|
|
631
652
|
const mathFlow = options.mathFlow ?? true;
|
|
632
653
|
const referenceTaint = options.referenceTaint ?? true;
|
|
633
|
-
const
|
|
654
|
+
const prev = resume;
|
|
655
|
+
const cp = prev && prev.defListEnabled === options.defListEnabled && prev.mathFlow === mathFlow && prev.referenceTaint === referenceTaint && prev.confirmedOffset <= text.length ? prev : freshCheckpoint(options.defListEnabled, mathFlow, referenceTaint);
|
|
634
656
|
let start = cp.confirmedOffset;
|
|
635
657
|
let tailLine = null;
|
|
636
658
|
while (start < text.length) {
|
|
@@ -693,6 +715,14 @@ function computeFreezeBoundary(text, options, resume) {
|
|
|
693
715
|
}
|
|
694
716
|
return { boundary, checkpoint: cp };
|
|
695
717
|
}
|
|
718
|
+
function pendingFenceCloser(checkpoint) {
|
|
719
|
+
const cp = checkpoint;
|
|
720
|
+
if (cp.phasePoisonedAt !== Infinity) return "";
|
|
721
|
+
if (cp.openIndent !== 0) return "";
|
|
722
|
+
if (cp.inFence) return cp.fenceChar.repeat(cp.fenceLen);
|
|
723
|
+
if (cp.inMath) return "$".repeat(cp.mathFenceLen);
|
|
724
|
+
return "";
|
|
725
|
+
}
|
|
696
726
|
function floatingResidue(text, commentOpenAtStart) {
|
|
697
727
|
let out = "";
|
|
698
728
|
let open = commentOpenAtStart;
|
|
@@ -768,6 +798,7 @@ function processConfirmedLine(cp, ln, text) {
|
|
|
768
798
|
cp.openTotal -= count;
|
|
769
799
|
}
|
|
770
800
|
}
|
|
801
|
+
if (cp.openStack.length > 0) cp.openStack = cp.openStack.filter((n) => !FOREIGN_ROOT_NAMES.includes(n));
|
|
771
802
|
};
|
|
772
803
|
const noteBreakout = (tag, closing) => {
|
|
773
804
|
if (closing || cp.rawTextOpen !== null) return;
|
|
@@ -791,13 +822,25 @@ function processConfirmedLine(cp, ln, text) {
|
|
|
791
822
|
}
|
|
792
823
|
}
|
|
793
824
|
if (DOCUMENT_STRUCTURE_NAMES.has(tag)) cp.phasePoisonedAt = 0;
|
|
825
|
+
if (tag === "template" && !closing) cp.phasePoisonedAt = 0;
|
|
794
826
|
if (closing) {
|
|
827
|
+
let idx = -1;
|
|
828
|
+
for (let i = cp.openStack.length - 1; i >= 0; i--) {
|
|
829
|
+
if (cp.openStack[i] === tag) {
|
|
830
|
+
idx = i;
|
|
831
|
+
break;
|
|
832
|
+
}
|
|
833
|
+
if (SCOPE_BARRIER_NAMES.has(cp.openStack[i])) break;
|
|
834
|
+
}
|
|
835
|
+
if (idx === -1) return;
|
|
836
|
+
cp.openStack.splice(idx, 1);
|
|
795
837
|
const count = cp.tagBalance.get(tag) ?? 0;
|
|
796
838
|
if (count > 0) {
|
|
797
839
|
cp.tagBalance.set(tag, count - 1);
|
|
798
840
|
cp.openTotal -= 1;
|
|
799
841
|
}
|
|
800
842
|
} else {
|
|
843
|
+
cp.openStack.push(tag);
|
|
801
844
|
cp.tagBalance.set(tag, (cp.tagBalance.get(tag) ?? 0) + 1);
|
|
802
845
|
cp.openTotal += 1;
|
|
803
846
|
}
|
|
@@ -916,6 +959,9 @@ function processConfirmedLine(cp, ln, text) {
|
|
|
916
959
|
if (!cp.type1FlowOpen) {
|
|
917
960
|
cp.htmlFlowSinceBlank = false;
|
|
918
961
|
cp.htmlFlowReal = false;
|
|
962
|
+
if (cp.rawTextOpen !== null && !cp.rawTextInline) {
|
|
963
|
+
cp.phasePoisonedAt = 0;
|
|
964
|
+
}
|
|
919
965
|
}
|
|
920
966
|
cp.prevLineBlank = true;
|
|
921
967
|
cp.prevLineWasText = false;
|
|
@@ -1015,6 +1061,7 @@ ${cont(scanText)}` };
|
|
|
1015
1061
|
const poisonRawDivergence = () => {
|
|
1016
1062
|
cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start);
|
|
1017
1063
|
};
|
|
1064
|
+
let inlineRawOpenerIdx = -1;
|
|
1018
1065
|
while (pos < scanText.length) {
|
|
1019
1066
|
if (cp.piOpen) {
|
|
1020
1067
|
const c = scanText.indexOf("?>", pos);
|
|
@@ -1070,6 +1117,7 @@ ${cont(scanText)}` };
|
|
|
1070
1117
|
} else if (first === cd) {
|
|
1071
1118
|
rawSpans.push([cd, cd + 9]);
|
|
1072
1119
|
cp.cdataOpen = true;
|
|
1120
|
+
if (!isMdBlank(scanText.slice(0, cd)) || ln.indent > 3) inlineRawOpenerIdx = cd;
|
|
1073
1121
|
pos = cd + 9;
|
|
1074
1122
|
} else if (first === pi) {
|
|
1075
1123
|
if (scanText[pi + 2] === ">") {
|
|
@@ -1080,14 +1128,19 @@ ${cont(scanText)}` };
|
|
|
1080
1128
|
}
|
|
1081
1129
|
rawSpans.push([pi, pi + 2]);
|
|
1082
1130
|
cp.piOpen = true;
|
|
1131
|
+
if (!isMdBlank(scanText.slice(0, pi)) || ln.indent > 3) inlineRawOpenerIdx = pi;
|
|
1083
1132
|
pos = pi + 2;
|
|
1084
1133
|
} else {
|
|
1085
1134
|
rawSpans.push([decl, decl + 2]);
|
|
1086
1135
|
if (ln.indent <= 3 && /^doctype/i.test(scanText.slice(decl + 2))) cp.phasePoisonedAt = 0;
|
|
1087
1136
|
cp.declOpen = true;
|
|
1137
|
+
if (!isMdBlank(scanText.slice(0, decl)) || ln.indent > 3) inlineRawOpenerIdx = decl;
|
|
1088
1138
|
pos = decl + 2;
|
|
1089
1139
|
}
|
|
1090
1140
|
}
|
|
1141
|
+
if (inlineRawOpenerIdx !== -1 && (cp.piOpen || cp.declOpen || cp.cdataOpen)) {
|
|
1142
|
+
cp.phasePoisonedAt = 0;
|
|
1143
|
+
}
|
|
1091
1144
|
let tagText = scanText;
|
|
1092
1145
|
for (const [from, to] of rawSpans) {
|
|
1093
1146
|
tagText = tagText.slice(0, from) + " ".repeat(to - from) + tagText.slice(to);
|
|
@@ -1176,9 +1229,9 @@ ${cont(scanText)}` };
|
|
|
1176
1229
|
if (VOID_TAGS.has(tag) || selfClosing && honoursSelfClosing(tag)) continue;
|
|
1177
1230
|
applyTag(tag, closing);
|
|
1178
1231
|
}
|
|
1179
|
-
if (cp.commentOpen && lastCommentOpenerIdx !== -1
|
|
1180
|
-
if (!isMdBlank(tagText.slice(0, lastCommentOpenerIdx))) {
|
|
1181
|
-
cp.phasePoisonedAt =
|
|
1232
|
+
if (cp.commentOpen && lastCommentOpenerIdx !== -1) {
|
|
1233
|
+
if (!isMdBlank(tagText.slice(0, lastCommentOpenerIdx)) || ln.indent > 3) {
|
|
1234
|
+
cp.phasePoisonedAt = 0;
|
|
1182
1235
|
}
|
|
1183
1236
|
}
|
|
1184
1237
|
if (masked !== null && masked !== ln.text) {
|
|
@@ -1478,6 +1531,40 @@ function hasStrayTablePart(values) {
|
|
|
1478
1531
|
}
|
|
1479
1532
|
return false;
|
|
1480
1533
|
}
|
|
1534
|
+
var HEAD_ROUTED_NAMES = /* @__PURE__ */ new Set([
|
|
1535
|
+
"base",
|
|
1536
|
+
"basefont",
|
|
1537
|
+
"bgsound",
|
|
1538
|
+
"link",
|
|
1539
|
+
"meta",
|
|
1540
|
+
"noframes",
|
|
1541
|
+
"script",
|
|
1542
|
+
"style",
|
|
1543
|
+
"template",
|
|
1544
|
+
"title"
|
|
1545
|
+
]);
|
|
1546
|
+
var HEAD_ROUTED_RAW_TEXT_RE = /<(script|style|title|noframes)(?=[\s/>])/i;
|
|
1547
|
+
var ANY_START_TAG_RE = /<([a-z][a-z0-9-]*)(?=[\s/>])/gi;
|
|
1548
|
+
function headRoutedCaptureUnclosed(values) {
|
|
1549
|
+
if (values.length === 0) return false;
|
|
1550
|
+
const joined = values.join("\n");
|
|
1551
|
+
const opener = HEAD_ROUTED_RAW_TEXT_RE.exec(joined);
|
|
1552
|
+
if (opener === null) return false;
|
|
1553
|
+
const before = joined.slice(0, opener.index);
|
|
1554
|
+
if (/<[!?]/.test(before)) return true;
|
|
1555
|
+
ANY_START_TAG_RE.lastIndex = 0;
|
|
1556
|
+
let m;
|
|
1557
|
+
while ((m = ANY_START_TAG_RE.exec(before)) !== null) {
|
|
1558
|
+
if (!HEAD_ROUTED_NAMES.has(m[1].toLowerCase())) return false;
|
|
1559
|
+
}
|
|
1560
|
+
const name = opener[1].toLowerCase();
|
|
1561
|
+
const after = joined.slice(opener.index + opener[0].length);
|
|
1562
|
+
if (name === "script") {
|
|
1563
|
+
const close = after.search(/<\/script(?=[\s/>])/i);
|
|
1564
|
+
return close === -1 || /<!--/.test(after.slice(0, close));
|
|
1565
|
+
}
|
|
1566
|
+
return !new RegExp(`</${name}(?=[\\s/>])`, "i").test(after);
|
|
1567
|
+
}
|
|
1481
1568
|
var STRAY_SYNTHESIZED_END_TAG_RE = /<\/(?:br|p)\b/i;
|
|
1482
1569
|
function spliceTrees(input) {
|
|
1483
1570
|
const { prevMdast, prevHast, tailMdast, tailHast, content, boundary, injectionPrefix, injectedSegments } = input;
|
|
@@ -1536,11 +1623,14 @@ function spliceTrees(input) {
|
|
|
1536
1623
|
});
|
|
1537
1624
|
const tailWrapVisible = tailMdastChildren.some((child) => !isWrapInvisible(child));
|
|
1538
1625
|
if (hasStrayTablePart(prefixMdast.flatMap((c) => c.type === "html" ? [c.value] : []))) return null;
|
|
1626
|
+
const leadingHtml = [];
|
|
1539
1627
|
for (const child of tailMdastChildren) {
|
|
1540
1628
|
if (isWrapInvisible(child)) continue;
|
|
1541
1629
|
if (child.type !== "html") break;
|
|
1542
1630
|
if (STRAY_SYNTHESIZED_END_TAG_RE.test(child.value) || TABLE_PART_TAG_RE.test(child.value)) return null;
|
|
1631
|
+
leadingHtml.push(child.value);
|
|
1543
1632
|
}
|
|
1633
|
+
if (headRoutedCaptureUnclosed(leadingHtml)) return null;
|
|
1544
1634
|
const aligned = alignPrefixCut(prefixMdast, cutRegion, tailWrapVisible);
|
|
1545
1635
|
if (aligned === null) return null;
|
|
1546
1636
|
const hastChildren = aligned.children;
|
|
@@ -2184,12 +2274,10 @@ function phantomSuffixCloser(content) {
|
|
|
2184
2274
|
const endsWithNewline = content.endsWith("\n");
|
|
2185
2275
|
const confirmed = endsWithNewline ? content : content + "\n";
|
|
2186
2276
|
const { checkpoint } = computeFreezeBoundary(confirmed, { defListEnabled: false, referenceTaint: false });
|
|
2187
|
-
|
|
2188
|
-
if (
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
if (checkpoint.inMath) return `${nl}${"$".repeat(checkpoint.mathFenceLen)}`;
|
|
2192
|
-
return "";
|
|
2277
|
+
const closer = pendingFenceCloser(checkpoint);
|
|
2278
|
+
if (closer === "") return "";
|
|
2279
|
+
return endsWithNewline ? closer : `
|
|
2280
|
+
${closer}`;
|
|
2193
2281
|
}
|
|
2194
2282
|
|
|
2195
2283
|
// src/components/extractContributions.ts
|
|
@@ -2465,7 +2553,7 @@ function createRegistry(onEmpty) {
|
|
|
2465
2553
|
|
|
2466
2554
|
// src/components/pluginChain.ts
|
|
2467
2555
|
var import_rehype_katex = __toESM(require("rehype-katex"), 1);
|
|
2468
|
-
var import_rehype_raw = __toESM(require("rehype-raw"), 1);
|
|
2556
|
+
var import_rehype_raw = __toESM(require("@ai-markdown/rehype-raw"), 1);
|
|
2469
2557
|
var import_rehype_unwrap_images = __toESM(require("rehype-unwrap-images"), 1);
|
|
2470
2558
|
|
|
2471
2559
|
// src/components/rehypeUnwrapCrossChunkImages.ts
|