@ai-matrx/content-ir 0.13.0 → 0.14.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/CHANGELOG.md +71 -0
- package/dist/index.cjs +227 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +226 -21
- package/dist/index.js.map +1 -1
- package/dist/source.cjs +227 -20
- package/dist/source.cjs.map +1 -1
- package/dist/source.d.cts +44 -8
- package/dist/source.d.ts +44 -8
- package/dist/source.js +226 -21
- package/dist/source.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,76 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.14.0 — 2026-09-25
|
|
4
|
+
|
|
5
|
+
**`/source` — RC-B3 polish: the re-verification residuals, and the verifier's
|
|
6
|
+
attacks made permanent** (common-docs
|
|
7
|
+
`projects/rich-content-unification/evidence/verify-RC-B3.md` § Re-verification).
|
|
8
|
+
Each residual had a failing test first (R1–R3 red on 0.13.0; the new
|
|
9
|
+
cross-language vectors fail 10 cases on the old Python twin):
|
|
10
|
+
|
|
11
|
+
- **R1** — an insertion carrying an exact copy of the next island at its
|
|
12
|
+
expected offset no longer causes a false `integrity` refusal: an island's
|
|
13
|
+
candidate positions are scored by how much of the original text still
|
|
14
|
+
surrounds them.
|
|
15
|
+
- **R2** — `tokenizeSource(text, { streaming: true })`: the last,
|
|
16
|
+
still-unterminated line closes no fence (a bare ```` ``` ```` there may grow
|
|
17
|
+
into ```` ```bash ````). Stored text omits the option.
|
|
18
|
+
- **R3** — `listIslands` loops instead of spreading, so a block with 150k
|
|
19
|
+
inline islands saves without a `RangeError`.
|
|
20
|
+
- **R4** — the fence rule defines its whitespace: `FENCE_WHITESPACE`
|
|
21
|
+
(CommonMark ASCII whitespace) and `trimFenceLine`, which every caller uses
|
|
22
|
+
in place of a language's `trim()`. The vectors carry U+FEFF, U+0085,
|
|
23
|
+
U+00A0, U+001C–U+001F and U+2028 lines; aidream matrx-ai's Python twin
|
|
24
|
+
gained `FENCE_WHITESPACE` / `trim_fence_line` and passes them.
|
|
25
|
+
- **R5** — tilde and odd-whitespace fences: matrx-frontend's
|
|
26
|
+
`source-covers-renderer` property test proves the tokenizer locks at least
|
|
27
|
+
every fenced block the renderer draws (both now share `trimFenceLine`).
|
|
28
|
+
- **Permanent attack suite** — `__tests__/source-attack.test.ts`: seeded fuzz
|
|
29
|
+
of rejoin identity, offsets (independently counted), surrogate safety,
|
|
30
|
+
no-edit/identity saves, splice verdicts, island refusals, misaligned spans,
|
|
31
|
+
streaming prefixes (settled islands never move) and pathological timing
|
|
32
|
+
(budget + linear scaling), bounded in CI; `SOURCE_ATTACK=long` runs the
|
|
33
|
+
verifier-scale version.
|
|
34
|
+
|
|
35
|
+
**Consumer action:** fence-line classification callers trim with
|
|
36
|
+
`trimFenceLine(line)` (matrx-frontend's splitters do as of this release).
|
|
37
|
+
|
|
38
|
+
**`/source` — islands for the extended markdown syntax** (RC-B8, common-docs
|
|
39
|
+
`projects/rich-content-unification/REGISTER.md`). The renderer now draws
|
|
40
|
+
callouts, directives, front matter, footnotes and wikilinks; an editor must
|
|
41
|
+
never re-serialize their grammar, so each is now an island:
|
|
42
|
+
|
|
43
|
+
- `directive` (block) — a generic-directive container `:::name[label]{attrs}`
|
|
44
|
+
… `:::` with nesting counted (tabs, columns, details, figures, callouts,
|
|
45
|
+
TOC), a leaf `::name` line (`meta.leaf`), or a MkDocs admonition
|
|
46
|
+
`!!! type "Title"` / `??? type` with its indented body (`meta.syntax: "mkdocs"`).
|
|
47
|
+
An unclosed container runs to the end with `complete: false` (streaming).
|
|
48
|
+
- `callout` (block) — a blockquote opening `> [!TYPE]` (GitHub alert /
|
|
49
|
+
Obsidian callout; `meta.name`, `meta.fold`).
|
|
50
|
+
- `footnote_def` (block) — `[^id]: …` with its continuation lines (`meta.id`).
|
|
51
|
+
- `front_matter` now also recognizes TOML (`+++`); `meta.format` is `yaml` | `toml`.
|
|
52
|
+
- `wikilink` (inline) — `[[Page]]`, `[[Page|alias]]`, `![[Page]]`
|
|
53
|
+
(`meta.target`, `meta.embed`); never across a line, never inside code.
|
|
54
|
+
|
|
55
|
+
CSV/TSV fences were already `fence` islands (`meta.lang`). Guard:
|
|
56
|
+
`__tests__/source-markdown-syntax-islands.test.ts` (7 of 10 red on 0.13.0).
|
|
57
|
+
|
|
58
|
+
**Consumer action:** an exhaustive `switch` over `IslandType` /
|
|
59
|
+
`BlockIslandType` gains the new members; nothing else changes (joinSource is
|
|
60
|
+
still byte-identical, the corpus check stays green).
|
|
61
|
+
|
|
62
|
+
**Single-dollar math: single-letter math shapes are math** (RC-B2 verification,
|
|
63
|
+
common-docs `projects/rich-content-unification/evidence/verify-RC-B2.md`).
|
|
64
|
+
`isSingleDollarMath` now also accepts a shape built only from single-letter
|
|
65
|
+
identifiers (primes, standard function names), digits, parentheses, commas and
|
|
66
|
+
`- * /` that applies, primes or juxtaposes them — `$f(g(x))$`, `$u v$`,
|
|
67
|
+
`$f'(x)$`, `$(x, y)$`, `$ln(x)$`. Words keep it text (`$Tipping (fee)$`), and
|
|
68
|
+
the pandoc spacing / digit rules still keep currency (`$5 and $10`, `$5-$10`)
|
|
69
|
+
as text. Guard: `__tests__/single-dollar-math.test.ts` (5 of 12 red before).
|
|
70
|
+
|
|
71
|
+
**Consumer action:** none — adopt the release; matrx-frontend's math
|
|
72
|
+
normalizer and `/source` tokenizer pick the rule up through the import.
|
|
73
|
+
|
|
3
74
|
## 0.13.0 — 2026-09-25
|
|
4
75
|
|
|
5
76
|
**`/source` — fixes from the RC-B3 independent verification**
|
package/dist/index.cjs
CHANGED
|
@@ -5184,11 +5184,22 @@ var NESTING_FENCE_LANGUAGES = /* @__PURE__ */ new Set([
|
|
|
5184
5184
|
function fenceNestsInnerFences(language) {
|
|
5185
5185
|
return !!language && NESTING_FENCE_LANGUAGES.has(language.toLowerCase());
|
|
5186
5186
|
}
|
|
5187
|
+
var FENCE_WHITESPACE = " \n\r\f\v";
|
|
5188
|
+
function isFenceWhitespace(ch) {
|
|
5189
|
+
return ch !== void 0 && ch !== "" && FENCE_WHITESPACE.includes(ch);
|
|
5190
|
+
}
|
|
5191
|
+
function trimFenceLine(line) {
|
|
5192
|
+
let start = 0;
|
|
5193
|
+
let end = line.length;
|
|
5194
|
+
while (start < end && isFenceWhitespace(line[start])) start++;
|
|
5195
|
+
while (end > start && isFenceWhitespace(line[end - 1])) end--;
|
|
5196
|
+
return start === 0 && end === line.length ? line : line.slice(start, end);
|
|
5197
|
+
}
|
|
5187
5198
|
function classifyInnerFenceLine(trimmed, openTicks, nests, nestedDepth) {
|
|
5188
5199
|
let ticks = 0;
|
|
5189
5200
|
while (ticks < trimmed.length && trimmed[ticks] === "`") ticks++;
|
|
5190
5201
|
if (ticks < 3) return "content";
|
|
5191
|
-
const info = trimmed.slice(ticks)
|
|
5202
|
+
const info = trimFenceLine(trimmed.slice(ticks));
|
|
5192
5203
|
if (info === "") {
|
|
5193
5204
|
if (ticks < openTicks) return "content";
|
|
5194
5205
|
if (nests && nestedDepth > 0) return "close-nested";
|
|
@@ -5201,13 +5212,42 @@ function classifyInnerFenceLine(trimmed, openTicks, nests, nestedDepth) {
|
|
|
5201
5212
|
// source/math.ts
|
|
5202
5213
|
var TEX_SIGNAL = /\\[A-Za-z]+|[\\^_{}=<>+]/;
|
|
5203
5214
|
var LONE_VARIABLE = /^[A-Za-z](?:'|[0-9])?$/;
|
|
5215
|
+
var MATH_SHAPE_CHARS = /^[A-Za-z0-9'\s(),.*\/-]+$/;
|
|
5216
|
+
var FUNCTION_NAMES = /* @__PURE__ */ new Set([
|
|
5217
|
+
"sin",
|
|
5218
|
+
"cos",
|
|
5219
|
+
"tan",
|
|
5220
|
+
"sec",
|
|
5221
|
+
"csc",
|
|
5222
|
+
"cot",
|
|
5223
|
+
"log",
|
|
5224
|
+
"ln",
|
|
5225
|
+
"exp",
|
|
5226
|
+
"lim",
|
|
5227
|
+
"max",
|
|
5228
|
+
"min",
|
|
5229
|
+
"det",
|
|
5230
|
+
"gcd",
|
|
5231
|
+
"mod",
|
|
5232
|
+
"arg",
|
|
5233
|
+
"sinh",
|
|
5234
|
+
"cosh",
|
|
5235
|
+
"tanh"
|
|
5236
|
+
]);
|
|
5237
|
+
function isMathShape(content) {
|
|
5238
|
+
if (!MATH_SHAPE_CHARS.test(content)) return false;
|
|
5239
|
+
const words = content.match(/[A-Za-z]+/g);
|
|
5240
|
+
if (!words) return false;
|
|
5241
|
+
if (!words.every((w) => w.length === 1 || FUNCTION_NAMES.has(w))) return false;
|
|
5242
|
+
return /[A-Za-z]'*\(|[A-Za-z]'|\b[A-Za-z]\s+[A-Za-z]\b|\([^)]*[A-Za-z][^)]*\)/.test(content);
|
|
5243
|
+
}
|
|
5204
5244
|
function isSingleDollarMath(content, after) {
|
|
5205
5245
|
if (!content || content.length > 400) return false;
|
|
5206
5246
|
if (/^\s/.test(content) || /\s$/.test(content)) return false;
|
|
5207
5247
|
if (after !== void 0 && /[0-9]/.test(after)) return false;
|
|
5208
5248
|
if (LONE_VARIABLE.test(content)) return true;
|
|
5209
5249
|
if (/^[0-9]/.test(content)) return /\\[A-Za-z]+|[\\^_{}]/.test(content);
|
|
5210
|
-
return TEX_SIGNAL.test(content);
|
|
5250
|
+
return TEX_SIGNAL.test(content) || isMathShape(content);
|
|
5211
5251
|
}
|
|
5212
5252
|
function singleDollarMathEnd(text, open, limit = text.length) {
|
|
5213
5253
|
const bound = Math.min(limit, open + 403);
|
|
@@ -5443,6 +5483,10 @@ var XmlContainerTracker = class {
|
|
|
5443
5483
|
|
|
5444
5484
|
// source/tokenize.ts
|
|
5445
5485
|
var NOT_WHITESPACE = /\S/;
|
|
5486
|
+
var DIRECTIVE_OPEN = /^(:{2,})([A-Za-z][\w-]*)/;
|
|
5487
|
+
var MKDOCS_OPEN = /^(!!!|\?\?\?\+?)[ \t]+([A-Za-z][\w-]*)/;
|
|
5488
|
+
var CALLOUT_OPEN = /^>[ \t]*\[!([A-Za-z][\w-]*)\]([+-])?/;
|
|
5489
|
+
var FOOTNOTE_DEF = /^\[\^([^\]\s]+)\]:/;
|
|
5446
5490
|
function countStructuralBraces(source) {
|
|
5447
5491
|
let opens = 0;
|
|
5448
5492
|
let closes = 0;
|
|
@@ -5570,8 +5614,9 @@ function isSplitterTreeLine(line) {
|
|
|
5570
5614
|
}
|
|
5571
5615
|
var MEDIA_REF_PREFIXES = ["[Image URL:", "[Video URL:", "[Audio URL:"];
|
|
5572
5616
|
var Tokenizer = class {
|
|
5573
|
-
constructor(text) {
|
|
5617
|
+
constructor(text, streaming = false) {
|
|
5574
5618
|
this.text = text;
|
|
5619
|
+
this.streaming = streaming;
|
|
5575
5620
|
this.n = text.length;
|
|
5576
5621
|
this.mathPairs = pairDisplayMath(text);
|
|
5577
5622
|
let start = 0;
|
|
@@ -5590,6 +5635,7 @@ var Tokenizer = class {
|
|
|
5590
5635
|
}
|
|
5591
5636
|
}
|
|
5592
5637
|
text;
|
|
5638
|
+
streaming;
|
|
5593
5639
|
n;
|
|
5594
5640
|
lineStarts = [];
|
|
5595
5641
|
contentEnds = [];
|
|
@@ -5627,6 +5673,10 @@ var Tokenizer = class {
|
|
|
5627
5673
|
}
|
|
5628
5674
|
return lo < list.length ? list[lo] : -1;
|
|
5629
5675
|
}
|
|
5676
|
+
/** Streaming only: the final line, still waiting for its terminator. */
|
|
5677
|
+
isOpenTail(k) {
|
|
5678
|
+
return this.streaming && k === this.lineStarts.length - 1 && this.lineEnds[k] === this.contentEnds[k];
|
|
5679
|
+
}
|
|
5630
5680
|
get lineCount() {
|
|
5631
5681
|
return this.lineStarts.length;
|
|
5632
5682
|
}
|
|
@@ -5708,6 +5758,22 @@ var Tokenizer = class {
|
|
|
5708
5758
|
const fm = this.frontMatter(li, t);
|
|
5709
5759
|
if (fm) return fm;
|
|
5710
5760
|
}
|
|
5761
|
+
if (indent <= 3 && t.startsWith("::")) {
|
|
5762
|
+
const directive = this.directive(p, li, t);
|
|
5763
|
+
if (directive) return directive;
|
|
5764
|
+
}
|
|
5765
|
+
if (indent === 0 && (t.startsWith("!!!") || t.startsWith("???"))) {
|
|
5766
|
+
const admonition = this.mkdocsAdmonition(p, li, t);
|
|
5767
|
+
if (admonition) return admonition;
|
|
5768
|
+
}
|
|
5769
|
+
if (indent <= 3 && t.startsWith(">")) {
|
|
5770
|
+
const callout = this.callout(p, li, t);
|
|
5771
|
+
if (callout) return callout;
|
|
5772
|
+
}
|
|
5773
|
+
if (atParaStart && indent <= 3 && t.startsWith("[^")) {
|
|
5774
|
+
const footnote = this.footnoteDefinition(p, li, t);
|
|
5775
|
+
if (footnote) return footnote;
|
|
5776
|
+
}
|
|
5711
5777
|
if (t.startsWith("```")) return this.backtickFence(p, li, t);
|
|
5712
5778
|
if (indent <= 3 && t.startsWith("~~~")) {
|
|
5713
5779
|
const tilde = this.tildeFence(p, li, t);
|
|
@@ -5745,19 +5811,110 @@ var Tokenizer = class {
|
|
|
5745
5811
|
return { type, start, end, complete, meta };
|
|
5746
5812
|
}
|
|
5747
5813
|
frontMatter(li, t) {
|
|
5748
|
-
|
|
5814
|
+
const fence = this.lineText(li);
|
|
5815
|
+
if (fence !== "---" && fence !== "+++" || t !== fence) return null;
|
|
5816
|
+
const toml = fence === "+++";
|
|
5749
5817
|
let sawContent = false;
|
|
5750
5818
|
for (let k = li + 1; k < this.lineCount; k++) {
|
|
5751
5819
|
const line = this.lineText(k);
|
|
5752
|
-
if (line ===
|
|
5753
|
-
return sawContent ? this.island("front_matter", 0, this.contentEnds[k], true) : null;
|
|
5820
|
+
if (line === fence || !toml && line === "...") {
|
|
5821
|
+
return sawContent ? this.island("front_matter", 0, this.contentEnds[k], true, { format: toml ? "toml" : "yaml" }) : null;
|
|
5754
5822
|
}
|
|
5755
5823
|
if (!NOT_WHITESPACE.test(line)) continue;
|
|
5756
|
-
|
|
5824
|
+
const shaped = toml ? /^\s*(?:[A-Za-z0-9_."-]+\s*=|\[|#)/.test(line) : /^(?:[A-Za-z0-9_"'-][^:]*:(?:\s|$)|\s+\S|-\s|#)/.test(line);
|
|
5825
|
+
if (!shaped) return null;
|
|
5757
5826
|
sawContent = true;
|
|
5758
5827
|
}
|
|
5759
5828
|
return null;
|
|
5760
5829
|
}
|
|
5830
|
+
/**
|
|
5831
|
+
* `:::name[label]{attrs}` … closing `:::` (a colon run at least as long as
|
|
5832
|
+
* the opener's, nesting counted), or a leaf `::name[…]{…}` line. The whole
|
|
5833
|
+
* container is ONE island: its grammar (fence lengths, labels, attributes,
|
|
5834
|
+
* nested tabs) is exactly what a visual editor would re-serialize wrongly.
|
|
5835
|
+
*/
|
|
5836
|
+
directive(p, li, t) {
|
|
5837
|
+
const open = DIRECTIVE_OPEN.exec(t);
|
|
5838
|
+
if (!open) return null;
|
|
5839
|
+
const colons = open[1].length;
|
|
5840
|
+
const name = open[2];
|
|
5841
|
+
if (colons === 2) {
|
|
5842
|
+
return this.island("directive", p, this.contentEnds[li], true, { name, leaf: true });
|
|
5843
|
+
}
|
|
5844
|
+
const stack = [colons];
|
|
5845
|
+
for (let k = li + 1; k < this.lineCount; k++) {
|
|
5846
|
+
const line = this.lineText(k).trim();
|
|
5847
|
+
const inner = DIRECTIVE_OPEN.exec(line);
|
|
5848
|
+
if (inner && inner[1].length >= 3) {
|
|
5849
|
+
stack.push(inner[1].length);
|
|
5850
|
+
continue;
|
|
5851
|
+
}
|
|
5852
|
+
const close = /^(:{3,})\s*$/.exec(line);
|
|
5853
|
+
if (close && close[1].length >= stack[stack.length - 1]) {
|
|
5854
|
+
stack.pop();
|
|
5855
|
+
if (stack.length === 0) return this.island("directive", p, this.contentEnds[k], true, { name });
|
|
5856
|
+
}
|
|
5857
|
+
}
|
|
5858
|
+
return this.island("directive", p, this.n, false, { name });
|
|
5859
|
+
}
|
|
5860
|
+
/** MkDocs `!!! type "Title"` / `??? type` / `???+ type` and its indented (or blank-separated indented) body. */
|
|
5861
|
+
mkdocsAdmonition(p, li, t) {
|
|
5862
|
+
const open = MKDOCS_OPEN.exec(t);
|
|
5863
|
+
if (!open) return null;
|
|
5864
|
+
let last = li;
|
|
5865
|
+
for (let k = li + 1; k < this.lineCount; k++) {
|
|
5866
|
+
const line = this.lineText(k);
|
|
5867
|
+
if (/^(?: {4}|\t)/.test(line) && NOT_WHITESPACE.test(line)) {
|
|
5868
|
+
last = k;
|
|
5869
|
+
continue;
|
|
5870
|
+
}
|
|
5871
|
+
if (!NOT_WHITESPACE.test(line)) continue;
|
|
5872
|
+
break;
|
|
5873
|
+
}
|
|
5874
|
+
return this.island("directive", p, this.contentEnds[last], true, {
|
|
5875
|
+
name: open[2].toLowerCase(),
|
|
5876
|
+
syntax: "mkdocs"
|
|
5877
|
+
});
|
|
5878
|
+
}
|
|
5879
|
+
/** A blockquote whose first line is `> [!TYPE]…` — every following `>` line belongs to it. */
|
|
5880
|
+
callout(p, li, t) {
|
|
5881
|
+
const open = CALLOUT_OPEN.exec(t);
|
|
5882
|
+
if (!open) return null;
|
|
5883
|
+
let last = li;
|
|
5884
|
+
for (let k = li + 1; k < this.lineCount; k++) {
|
|
5885
|
+
if (!/^ {0,3}>/.test(this.lineText(k))) break;
|
|
5886
|
+
last = k;
|
|
5887
|
+
}
|
|
5888
|
+
return this.island("callout", p, this.contentEnds[last], true, {
|
|
5889
|
+
name: open[1].toLowerCase(),
|
|
5890
|
+
...open[2] ? { fold: open[2] === "-" ? "closed" : "open" } : {}
|
|
5891
|
+
});
|
|
5892
|
+
}
|
|
5893
|
+
/** `[^id]: text` plus its continuation lines (indented, or lazy text before a blank line). */
|
|
5894
|
+
footnoteDefinition(p, li, t) {
|
|
5895
|
+
const open = FOOTNOTE_DEF.exec(t);
|
|
5896
|
+
if (!open) return null;
|
|
5897
|
+
let last = li;
|
|
5898
|
+
for (let k = li + 1; k < this.lineCount; k++) {
|
|
5899
|
+
const line = this.lineText(k);
|
|
5900
|
+
if (!NOT_WHITESPACE.test(line)) {
|
|
5901
|
+
let j = k + 1;
|
|
5902
|
+
while (j < this.lineCount && !NOT_WHITESPACE.test(this.lineText(j))) j++;
|
|
5903
|
+
if (j < this.lineCount && /^(?: {4}|\t)/.test(this.lineText(j))) {
|
|
5904
|
+
k = j - 1;
|
|
5905
|
+
continue;
|
|
5906
|
+
}
|
|
5907
|
+
break;
|
|
5908
|
+
}
|
|
5909
|
+
if (/^(?: {4}|\t)/.test(line)) {
|
|
5910
|
+
last = k;
|
|
5911
|
+
continue;
|
|
5912
|
+
}
|
|
5913
|
+
if (FOOTNOTE_DEF.test(line.trimStart()) || this.detectBlock(this.lineStarts[k], k, false)) break;
|
|
5914
|
+
last = k;
|
|
5915
|
+
}
|
|
5916
|
+
return this.island("footnote_def", p, this.contentEnds[last], true, { id: open[1] });
|
|
5917
|
+
}
|
|
5761
5918
|
/**
|
|
5762
5919
|
* Backtick fence — the renderer splitter's close rules: JSON-string aware for
|
|
5763
5920
|
* ```json, and THE nested-fence rule (source/fence-nesting.ts) for markdown
|
|
@@ -5786,8 +5943,9 @@ var Tokenizer = class {
|
|
|
5786
5943
|
let nestedDepth = 0;
|
|
5787
5944
|
let sawNested = false;
|
|
5788
5945
|
for (let k = li + 1; k < this.lineCount; k++) {
|
|
5946
|
+
if (this.isOpenTail(k)) continue;
|
|
5789
5947
|
const line = this.lineText(k);
|
|
5790
|
-
const trimmedLine = line
|
|
5948
|
+
const trimmedLine = trimFenceLine(line);
|
|
5791
5949
|
if (trimmedLine.startsWith("```")) {
|
|
5792
5950
|
const at2 = line.indexOf("```");
|
|
5793
5951
|
if (isJson) {
|
|
@@ -5817,7 +5975,7 @@ var Tokenizer = class {
|
|
|
5817
5975
|
}
|
|
5818
5976
|
}
|
|
5819
5977
|
const closeTicks = backtickRunLength(line, at);
|
|
5820
|
-
const after = line.slice(at + closeTicks)
|
|
5978
|
+
const after = trimFenceLine(line.slice(at + closeTicks));
|
|
5821
5979
|
if (closeTicks >= openTicks && after === "" && nestedDepth === 0) return k;
|
|
5822
5980
|
if (isJson) state = jsonStringState(line, 0, line.length, state);
|
|
5823
5981
|
continue;
|
|
@@ -5844,6 +6002,7 @@ var Tokenizer = class {
|
|
|
5844
6002
|
if (normalized && SPECIAL_CODE_LANGUAGES.has(normalized)) meta.special = normalized;
|
|
5845
6003
|
const close = new RegExp(`^ {0,3}~{${ticks},}[ \\t]*$`);
|
|
5846
6004
|
for (let k = li + 1; k < this.lineCount; k++) {
|
|
6005
|
+
if (this.isOpenTail(k)) continue;
|
|
5847
6006
|
if (close.test(this.lineText(k))) {
|
|
5848
6007
|
return this.island("fence", p, this.contentEnds[k], true, meta);
|
|
5849
6008
|
}
|
|
@@ -6233,6 +6392,21 @@ var Tokenizer = class {
|
|
|
6233
6392
|
i++;
|
|
6234
6393
|
continue;
|
|
6235
6394
|
}
|
|
6395
|
+
if (ch === "[" && text[i + 1] === "[" || ch === "!" && text[i + 1] === "[" && text[i + 2] === "[") {
|
|
6396
|
+
const open = ch === "!" ? i + 3 : i + 2;
|
|
6397
|
+
const close = within("]]", open);
|
|
6398
|
+
const newline = this.next("\n", open);
|
|
6399
|
+
const inner = close === -1 ? "" : text.slice(open, close);
|
|
6400
|
+
if (close !== -1 && (newline === -1 || newline > close) && inner.trim() && !/[[\]]/.test(inner)) {
|
|
6401
|
+
const cut = inner.search(/\\?\|/);
|
|
6402
|
+
const target = (cut < 0 ? inner : inner.slice(0, cut)).trim();
|
|
6403
|
+
inlines.push(
|
|
6404
|
+
this.island("wikilink", i, close + 2, true, ch === "!" ? { target, embed: true } : { target })
|
|
6405
|
+
);
|
|
6406
|
+
i = close + 2;
|
|
6407
|
+
continue;
|
|
6408
|
+
}
|
|
6409
|
+
}
|
|
6236
6410
|
if (ch === "[") {
|
|
6237
6411
|
const media = MEDIA_REF_PREFIXES.find((prefix) => text.startsWith(prefix, i));
|
|
6238
6412
|
if (media) {
|
|
@@ -6314,8 +6488,8 @@ var Tokenizer = class {
|
|
|
6314
6488
|
return { inlines, breakIsland: null, orphan: false };
|
|
6315
6489
|
}
|
|
6316
6490
|
};
|
|
6317
|
-
function tokenizeSource(text) {
|
|
6318
|
-
const raw = new Tokenizer(text).run();
|
|
6491
|
+
function tokenizeSource(text, options = {}) {
|
|
6492
|
+
const raw = new Tokenizer(text, options.streaming ?? false).run();
|
|
6319
6493
|
const cp = buildCodePointIndex(text);
|
|
6320
6494
|
const inline = (island) => ({
|
|
6321
6495
|
islandType: island.type,
|
|
@@ -6364,7 +6538,7 @@ function listIslands(blocks) {
|
|
|
6364
6538
|
raw: block.raw
|
|
6365
6539
|
});
|
|
6366
6540
|
} else {
|
|
6367
|
-
|
|
6541
|
+
for (const inline of block.inlines) out.push(inline);
|
|
6368
6542
|
}
|
|
6369
6543
|
}
|
|
6370
6544
|
return out;
|
|
@@ -6400,15 +6574,37 @@ function islandEdit(island, text) {
|
|
|
6400
6574
|
function blockRangeEdit(first, last, text) {
|
|
6401
6575
|
return { start: first.start, end: last.end, text };
|
|
6402
6576
|
}
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6577
|
+
var CONTEXT_WINDOW = 64;
|
|
6578
|
+
function locateIsland(text, raw, from, expected, expectedFromEnd, oldText, oldStart, oldEnd) {
|
|
6579
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
6580
|
+
if (expected >= from && text.startsWith(raw, expected)) candidates.add(expected);
|
|
6581
|
+
if (expectedFromEnd >= from && text.startsWith(raw, expectedFromEnd)) candidates.add(expectedFromEnd);
|
|
6406
6582
|
const after = text.indexOf(raw, Math.max(from, expected));
|
|
6583
|
+
if (after !== -1) candidates.add(after);
|
|
6407
6584
|
const before = expected > from ? text.lastIndexOf(raw, expected) : -1;
|
|
6408
|
-
|
|
6409
|
-
if (
|
|
6410
|
-
if (
|
|
6411
|
-
|
|
6585
|
+
if (before >= from) candidates.add(before);
|
|
6586
|
+
if (candidates.size === 0) return text.indexOf(raw, from);
|
|
6587
|
+
if (candidates.size === 1) return candidates.values().next().value;
|
|
6588
|
+
let best = -1;
|
|
6589
|
+
let bestScore = -1;
|
|
6590
|
+
for (const at of candidates) {
|
|
6591
|
+
let score = 0;
|
|
6592
|
+
for (let k = 0; k < CONTEXT_WINDOW; k++) {
|
|
6593
|
+
const n = text[at + raw.length + k];
|
|
6594
|
+
if (n === void 0 || n !== oldText[oldEnd + k]) break;
|
|
6595
|
+
score++;
|
|
6596
|
+
}
|
|
6597
|
+
for (let k = 1; k <= CONTEXT_WINDOW; k++) {
|
|
6598
|
+
const n = text[at - k];
|
|
6599
|
+
if (n === void 0 || n !== oldText[oldStart - k]) break;
|
|
6600
|
+
score++;
|
|
6601
|
+
}
|
|
6602
|
+
if (score > bestScore || score === bestScore && at === expected) {
|
|
6603
|
+
best = at;
|
|
6604
|
+
bestScore = score;
|
|
6605
|
+
}
|
|
6606
|
+
}
|
|
6607
|
+
return best;
|
|
6412
6608
|
}
|
|
6413
6609
|
function commonPrefix(a, b) {
|
|
6414
6610
|
const max = Math.min(a.length, b.length);
|
|
@@ -6485,7 +6681,16 @@ function spliceSave(original, edits, options = {}) {
|
|
|
6485
6681
|
let oldCursor2 = edit.start;
|
|
6486
6682
|
let newCursor2 = 0;
|
|
6487
6683
|
for (const island of inside) {
|
|
6488
|
-
const at = locateIsland(
|
|
6684
|
+
const at = locateIsland(
|
|
6685
|
+
edit.text,
|
|
6686
|
+
island.raw,
|
|
6687
|
+
newCursor2,
|
|
6688
|
+
newCursor2 + (island.start - oldCursor2),
|
|
6689
|
+
edit.text.length - (edit.end - island.start),
|
|
6690
|
+
original.slice(edit.start, edit.end),
|
|
6691
|
+
island.start - edit.start,
|
|
6692
|
+
island.end - edit.start
|
|
6693
|
+
);
|
|
6489
6694
|
if (at === -1) {
|
|
6490
6695
|
const name = island.meta.tag ?? island.meta.name ?? island.meta.lang ?? "";
|
|
6491
6696
|
throw new SourceSpliceError(
|
|
@@ -6628,6 +6833,7 @@ exports.CAPABILITY_BY_CLASS = CAPABILITY_BY_CLASS;
|
|
|
6628
6833
|
exports.CLASSES = CLASSES;
|
|
6629
6834
|
exports.DIRECTIVE_VERSION = DIRECTIVE_VERSION;
|
|
6630
6835
|
exports.DirectiveDecodeError = DirectiveDecodeError;
|
|
6836
|
+
exports.FENCE_WHITESPACE = FENCE_WHITESPACE;
|
|
6631
6837
|
exports.IN_CONTENT_CLASSES = IN_CONTENT_CLASSES;
|
|
6632
6838
|
exports.IR_ENVELOPE_CACHE_VERSION = IR_ENVELOPE_CACHE_VERSION;
|
|
6633
6839
|
exports.IR_ENVELOPE_KEY = IR_ENVELOPE_KEY;
|
|
@@ -6759,6 +6965,7 @@ exports.titleCaseToken = titleCaseToken;
|
|
|
6759
6965
|
exports.toCodePointOffset = toCodePointOffset;
|
|
6760
6966
|
exports.toUtf16Offset = toUtf16Offset;
|
|
6761
6967
|
exports.tokenizeSource = tokenizeSource;
|
|
6968
|
+
exports.trimFenceLine = trimFenceLine;
|
|
6762
6969
|
exports.tryDecodeDirective = tryDecodeDirective;
|
|
6763
6970
|
exports.tryDecodeDirectiveContent = tryDecodeDirectiveContent;
|
|
6764
6971
|
exports.validateBlockSchemaSavePlan = validateBlockSchemaSavePlan;
|