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