@ai-matrx/content-ir 0.14.3 → 0.15.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 +48 -0
- package/dist/index.cjs +45 -6
- 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 +44 -7
- package/dist/index.js.map +1 -1
- package/dist/source.cjs +45 -6
- package/dist/source.cjs.map +1 -1
- package/dist/source.d.cts +26 -3
- package/dist/source.d.ts +26 -3
- package/dist/source.js +44 -7
- package/dist/source.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,59 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.15.1
|
|
4
|
+
|
|
5
|
+
- **Fixed (`/source` `pairDisplayMath`):** a stray `$$` in prose paired with
|
|
6
|
+
the next paragraph's formula when a LaTeX command sat between them. A `$$`
|
|
7
|
+
with text beside it never crosses a blank line (TeX's rule), checked on the
|
|
8
|
+
untrimmed span; a `$$` alone on its line still opens a flow block that may
|
|
9
|
+
span blank lines. `looksLikeDisplayMath(inner, flow?)` gains the `flow`
|
|
10
|
+
flag. Same rule as `@ai-matrx/kit` `delimiter-guard` (one math dialect).
|
|
11
|
+
- Consumer action: none — refresh to latest.
|
|
12
|
+
|
|
3
13
|
## 0.14.3
|
|
4
14
|
|
|
5
15
|
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
6
16
|
`git diff npm/content-ir-core/v0.14.2..npm/content-ir-core/v0.14.3 -- apps/shared/content-ir-core`).
|
|
7
17
|
No source changes intended and no consumer action required.
|
|
8
18
|
|
|
19
|
+
## 0.15.0 — 2026-09-25
|
|
20
|
+
|
|
21
|
+
**`/source` — the attack suite stands alone; the last twin drift and flicker
|
|
22
|
+
closed** (verify-RC-B3 § Re-verification (0.14.2): G1–G3, R4′, R2′). Proof:
|
|
23
|
+
the verifier's six planted mutations (M1 skip the in-range island check, M2
|
|
24
|
+
integrity default off, M3 streaming ignored, M4 code-point offset off by one,
|
|
25
|
+
M5 unbounded `}}` scan, M6 `islandEdit` accepts any span) each turn
|
|
26
|
+
`__tests__/source-attack.test.ts` red ON ITS OWN; unmutated code is green in
|
|
27
|
+
CI and in four consecutive `SOURCE_ATTACK=long` runs on a heavily loaded host.
|
|
28
|
+
|
|
29
|
+
- **G1** — the streaming check covers every complete island in every prefix,
|
|
30
|
+
the LAST one included, keyed by span AND block/inline; only line-grown
|
|
31
|
+
constructs (tree, callout, footnote, HTML block, directive) may grow as the
|
|
32
|
+
trailing block. A third message carries the R2′ case and the extended syntax.
|
|
33
|
+
- **G2** — save verdicts are checked against an independent oracle (which
|
|
34
|
+
islands survive in the expected text), exactly: `island_edit` vs
|
|
35
|
+
`integrity` vs accepted; two-sided edits must map an island exactly (never
|
|
36
|
+
through a deleted range); mangling must be `island_edit`; forged
|
|
37
|
+
`island:true` spans must be `misaligned`.
|
|
38
|
+
- **G3** — timing compares the MINIMUM of interleaved runs at a 4× size step
|
|
39
|
+
(linear ≈ 4×, quadratic ≈ 16×, bound 8×) and fails only if every one of
|
|
40
|
+
three attempts breaches.
|
|
41
|
+
- **`island_edit` is now also the verdict** when an island inside the edited
|
|
42
|
+
range survives only as ordinary text (its bytes matched a non-island copy,
|
|
43
|
+
or the edit turned it into code) — previously reported as `integrity`.
|
|
44
|
+
`integrity` now means an island OUTSIDE the edited ranges was disturbed.
|
|
45
|
+
- **R2′** — streaming: a `$$` block whose closer sits on the still-open last
|
|
46
|
+
line is not settled (the rest of that line decides block vs inline); a
|
|
47
|
+
closing `$` that is the last byte so far is not yet math.
|
|
48
|
+
- **R4′** — `FENCE_OPENER_WHITESPACE` + `parseFenceOpener`: the opener parse
|
|
49
|
+
(JavaScript `trim()` / `/\s/`, written out — proven equal to `/\s/` over
|
|
50
|
+
every BMP code point) shared with the Python twin, whose block detector now
|
|
51
|
+
reads `markdown\u001f` as the renderer does. Vectors gained an `openers`
|
|
52
|
+
section and four exotic-whitespace documents (old Python: red).
|
|
53
|
+
|
|
54
|
+
**Consumer action:** code that branches on `SourceSpliceError.code` treats a
|
|
55
|
+
carried island that no longer stands as `island_edit` (it was `integrity`).
|
|
56
|
+
|
|
9
57
|
## 0.14.2 — 2026-09-25
|
|
10
58
|
|
|
11
59
|
**`/source` — a tilde fence locks what the renderer draws inside it**
|
package/dist/index.cjs
CHANGED
|
@@ -5195,6 +5195,24 @@ function trimFenceLine(line) {
|
|
|
5195
5195
|
while (end > start && isFenceWhitespace(line[end - 1])) end--;
|
|
5196
5196
|
return start === 0 && end === line.length ? line : line.slice(start, end);
|
|
5197
5197
|
}
|
|
5198
|
+
var FENCE_OPENER_WHITESPACE = " \n\v\f\r \xA0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF";
|
|
5199
|
+
function isOpenerWhitespace(ch) {
|
|
5200
|
+
return ch !== void 0 && ch !== "" && FENCE_OPENER_WHITESPACE.includes(ch);
|
|
5201
|
+
}
|
|
5202
|
+
function parseFenceOpener(line) {
|
|
5203
|
+
let start = 0;
|
|
5204
|
+
let end = line.length;
|
|
5205
|
+
while (start < end && isOpenerWhitespace(line[start])) start++;
|
|
5206
|
+
while (end > start && isOpenerWhitespace(line[end - 1])) end--;
|
|
5207
|
+
let ticks = 0;
|
|
5208
|
+
while (start + ticks < end && line[start + ticks] === "`") ticks++;
|
|
5209
|
+
if (ticks < 3) return null;
|
|
5210
|
+
let infoStart = start + ticks;
|
|
5211
|
+
while (infoStart < end && isOpenerWhitespace(line[infoStart])) infoStart++;
|
|
5212
|
+
let langEnd = infoStart;
|
|
5213
|
+
while (langEnd < end && !isOpenerWhitespace(line[langEnd])) langEnd++;
|
|
5214
|
+
return { ticks, lang: line.slice(infoStart, langEnd) };
|
|
5215
|
+
}
|
|
5198
5216
|
function classifyInnerFenceLine(trimmed, openTicks, nests, nestedDepth) {
|
|
5199
5217
|
let ticks = 0;
|
|
5200
5218
|
while (ticks < trimmed.length && trimmed[ticks] === "`") ticks++;
|
|
@@ -5276,15 +5294,20 @@ var STRUCTURAL_MARKDOWN = /\]\(|https?:\/\/|\*\*|(?:^|\n)[ \t]{0,3}#{1,6}[ \t]|(
|
|
|
5276
5294
|
var LATEX_COMMAND = /\\[a-zA-Z]/;
|
|
5277
5295
|
var PROSE_WORD = /[A-Za-z]{3,}/g;
|
|
5278
5296
|
var PROSE_WORD_LIMIT = 6;
|
|
5279
|
-
function
|
|
5297
|
+
function opensFlowBlock(text, open) {
|
|
5298
|
+
for (let k = open - 1; k >= 0 && text[k] !== "\n"; k--) if (text[k] !== " " && text[k] !== " ") return false;
|
|
5299
|
+
for (let k = open + 2; k < text.length && text[k] !== "\n"; k++) if (text[k] !== " " && text[k] !== " " && text[k] !== "\r") return false;
|
|
5300
|
+
return true;
|
|
5301
|
+
}
|
|
5302
|
+
function looksLikeDisplayMath(inner, flow = false) {
|
|
5280
5303
|
const s = inner.trim();
|
|
5281
5304
|
if (!s) return false;
|
|
5282
5305
|
if (STRUCTURAL_MARKDOWN.test(s)) return false;
|
|
5283
5306
|
if (s.length > MAX_MATH_SPAN) return false;
|
|
5307
|
+
if (!flow && /\n[ \t]*\n/.test(inner)) return false;
|
|
5284
5308
|
if (LATEX_COMMAND.test(s)) {
|
|
5285
5309
|
return (s.match(PROSE_WORD) ?? []).length < PROSE_WORD_LIMIT * 3;
|
|
5286
5310
|
}
|
|
5287
|
-
if (/\n[ \t]*\n/.test(s)) return false;
|
|
5288
5311
|
return (s.match(PROSE_WORD) ?? []).length < PROSE_WORD_LIMIT;
|
|
5289
5312
|
}
|
|
5290
5313
|
function codeRanges(text) {
|
|
@@ -5318,7 +5341,7 @@ function pairDisplayMath(text) {
|
|
|
5318
5341
|
const open = tokens[j];
|
|
5319
5342
|
const close = tokens[j + 1];
|
|
5320
5343
|
if (close === void 0) break;
|
|
5321
|
-
if (looksLikeDisplayMath(text.slice(open + 2, close))) {
|
|
5344
|
+
if (looksLikeDisplayMath(text.slice(open + 2, close), opensFlowBlock(text, open))) {
|
|
5322
5345
|
pairs.set(open, close);
|
|
5323
5346
|
j += 2;
|
|
5324
5347
|
continue;
|
|
@@ -5955,8 +5978,7 @@ var Tokenizer = class {
|
|
|
5955
5978
|
*/
|
|
5956
5979
|
backtickFence(p, li, t) {
|
|
5957
5980
|
const openTicks = backtickRunLength(t, 0);
|
|
5958
|
-
const
|
|
5959
|
-
const lang = info.split(/\s/)[0] ?? "";
|
|
5981
|
+
const lang = parseFenceOpener(t)?.lang ?? "";
|
|
5960
5982
|
const isJson = lang === "json";
|
|
5961
5983
|
const normalized = normalizeFenceLanguage(lang);
|
|
5962
5984
|
const meta = { fence: "`", ticks: openTicks, lang };
|
|
@@ -6086,6 +6108,7 @@ var Tokenizer = class {
|
|
|
6086
6108
|
const endLine = this.lineOf(close);
|
|
6087
6109
|
const endCe = this.contentEnds[endLine];
|
|
6088
6110
|
if (endLine !== li && !this.isBlank(end, endCe)) return null;
|
|
6111
|
+
if (this.isOpenTail(endLine)) return this.island("math_block", p, this.n, false, { delimiter: "$$" });
|
|
6089
6112
|
return this.island("math_block", p, end, true, { delimiter: "$$" });
|
|
6090
6113
|
}
|
|
6091
6114
|
/** `<artifact …>`, `<decision …>`, … at the start of a line. */
|
|
@@ -6415,7 +6438,7 @@ var Tokenizer = class {
|
|
|
6415
6438
|
continue;
|
|
6416
6439
|
}
|
|
6417
6440
|
const end = singleDollarMathEnd(text, i, pe);
|
|
6418
|
-
if (end > 0) {
|
|
6441
|
+
if (end > 0 && !(this.streaming && end === this.n)) {
|
|
6419
6442
|
inlines.push(this.island("math_inline", i, end, true, { delimiter: "$" }));
|
|
6420
6443
|
i = end;
|
|
6421
6444
|
continue;
|
|
@@ -6825,11 +6848,17 @@ function spliceSave(original, edits, options = {}) {
|
|
|
6825
6848
|
else newIslandsByStart.set(island.start, [island.raw]);
|
|
6826
6849
|
}
|
|
6827
6850
|
const disturbed = [];
|
|
6851
|
+
const carried = (island) => sorted.some((edit) => !edit.island && island.start >= edit.start && island.end <= edit.end);
|
|
6852
|
+
const changedInside = [];
|
|
6828
6853
|
for (const island of islands) {
|
|
6829
6854
|
if (replacedIslands.has(`${island.start}:${island.end}`)) continue;
|
|
6830
6855
|
const mappedStart = mapPosition(changes, island.start, { assoc: 1 }).pos;
|
|
6831
6856
|
const candidates = newIslandsByStart.get(mappedStart);
|
|
6832
6857
|
if (!candidates || !candidates.includes(island.raw)) {
|
|
6858
|
+
if (carried(island)) {
|
|
6859
|
+
changedInside.push(island);
|
|
6860
|
+
continue;
|
|
6861
|
+
}
|
|
6833
6862
|
disturbed.push({
|
|
6834
6863
|
islandType: island.islandType,
|
|
6835
6864
|
inline: island.inline,
|
|
@@ -6839,6 +6868,14 @@ function spliceSave(original, edits, options = {}) {
|
|
|
6839
6868
|
});
|
|
6840
6869
|
}
|
|
6841
6870
|
}
|
|
6871
|
+
if (changedInside.length > 0) {
|
|
6872
|
+
const first = changedInside[0];
|
|
6873
|
+
const name = first.meta.tag ?? first.meta.name ?? first.meta.lang ?? "";
|
|
6874
|
+
throw new SourceSpliceError(
|
|
6875
|
+
"island_edit",
|
|
6876
|
+
`the edit changes the protected ${first.islandType}${name ? ` (${String(name)})` : ""} island at [${first.start}, ${first.end}) \u2014 its bytes survive only as ordinary text${changedInside.length > 1 ? ` (and ${changedInside.length - 1} more)` : ""}; islands change only through islandEdit()`
|
|
6877
|
+
);
|
|
6878
|
+
}
|
|
6842
6879
|
const integrity = {
|
|
6843
6880
|
ok: bytesOutsideEditsIdentical && disturbed.length === 0,
|
|
6844
6881
|
bytesOutsideEditsIdentical,
|
|
@@ -6896,6 +6933,7 @@ exports.CAPABILITY_BY_CLASS = CAPABILITY_BY_CLASS;
|
|
|
6896
6933
|
exports.CLASSES = CLASSES;
|
|
6897
6934
|
exports.DIRECTIVE_VERSION = DIRECTIVE_VERSION;
|
|
6898
6935
|
exports.DirectiveDecodeError = DirectiveDecodeError;
|
|
6936
|
+
exports.FENCE_OPENER_WHITESPACE = FENCE_OPENER_WHITESPACE;
|
|
6899
6937
|
exports.FENCE_WHITESPACE = FENCE_WHITESPACE;
|
|
6900
6938
|
exports.IN_CONTENT_CLASSES = IN_CONTENT_CLASSES;
|
|
6901
6939
|
exports.IR_ENVELOPE_CACHE_VERSION = IR_ENVELOPE_CACHE_VERSION;
|
|
@@ -6998,6 +7036,7 @@ exports.nounTitleColumn = nounTitleColumn;
|
|
|
6998
7036
|
exports.openParseSession = openParseSession;
|
|
6999
7037
|
exports.pairDisplayMath = pairDisplayMath;
|
|
7000
7038
|
exports.parseDirectiveSlug = parseDirectiveSlug;
|
|
7039
|
+
exports.parseFenceOpener = parseFenceOpener;
|
|
7001
7040
|
exports.readEnvelope = readEnvelope;
|
|
7002
7041
|
exports.readNodeOutcomeValue = readNodeOutcomeValue;
|
|
7003
7042
|
exports.readObjectKind = readObjectKind;
|