@ai-matrx/content-ir 0.14.3 → 0.15.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 +38 -0
- package/dist/index.cjs +37 -3
- 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 +36 -4
- package/dist/index.js.map +1 -1
- package/dist/source.cjs +37 -3
- package/dist/source.cjs.map +1 -1
- package/dist/source.d.cts +19 -1
- package/dist/source.d.ts +19 -1
- package/dist/source.js +36 -4
- package/dist/source.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,44 @@ Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
|
6
6
|
`git diff npm/content-ir-core/v0.14.2..npm/content-ir-core/v0.14.3 -- apps/shared/content-ir-core`).
|
|
7
7
|
No source changes intended and no consumer action required.
|
|
8
8
|
|
|
9
|
+
## 0.15.0 — 2026-09-25
|
|
10
|
+
|
|
11
|
+
**`/source` — the attack suite stands alone; the last twin drift and flicker
|
|
12
|
+
closed** (verify-RC-B3 § Re-verification (0.14.2): G1–G3, R4′, R2′). Proof:
|
|
13
|
+
the verifier's six planted mutations (M1 skip the in-range island check, M2
|
|
14
|
+
integrity default off, M3 streaming ignored, M4 code-point offset off by one,
|
|
15
|
+
M5 unbounded `}}` scan, M6 `islandEdit` accepts any span) each turn
|
|
16
|
+
`__tests__/source-attack.test.ts` red ON ITS OWN; unmutated code is green in
|
|
17
|
+
CI and in four consecutive `SOURCE_ATTACK=long` runs on a heavily loaded host.
|
|
18
|
+
|
|
19
|
+
- **G1** — the streaming check covers every complete island in every prefix,
|
|
20
|
+
the LAST one included, keyed by span AND block/inline; only line-grown
|
|
21
|
+
constructs (tree, callout, footnote, HTML block, directive) may grow as the
|
|
22
|
+
trailing block. A third message carries the R2′ case and the extended syntax.
|
|
23
|
+
- **G2** — save verdicts are checked against an independent oracle (which
|
|
24
|
+
islands survive in the expected text), exactly: `island_edit` vs
|
|
25
|
+
`integrity` vs accepted; two-sided edits must map an island exactly (never
|
|
26
|
+
through a deleted range); mangling must be `island_edit`; forged
|
|
27
|
+
`island:true` spans must be `misaligned`.
|
|
28
|
+
- **G3** — timing compares the MINIMUM of interleaved runs at a 4× size step
|
|
29
|
+
(linear ≈ 4×, quadratic ≈ 16×, bound 8×) and fails only if every one of
|
|
30
|
+
three attempts breaches.
|
|
31
|
+
- **`island_edit` is now also the verdict** when an island inside the edited
|
|
32
|
+
range survives only as ordinary text (its bytes matched a non-island copy,
|
|
33
|
+
or the edit turned it into code) — previously reported as `integrity`.
|
|
34
|
+
`integrity` now means an island OUTSIDE the edited ranges was disturbed.
|
|
35
|
+
- **R2′** — streaming: a `$$` block whose closer sits on the still-open last
|
|
36
|
+
line is not settled (the rest of that line decides block vs inline); a
|
|
37
|
+
closing `$` that is the last byte so far is not yet math.
|
|
38
|
+
- **R4′** — `FENCE_OPENER_WHITESPACE` + `parseFenceOpener`: the opener parse
|
|
39
|
+
(JavaScript `trim()` / `/\s/`, written out — proven equal to `/\s/` over
|
|
40
|
+
every BMP code point) shared with the Python twin, whose block detector now
|
|
41
|
+
reads `markdown\u001f` as the renderer does. Vectors gained an `openers`
|
|
42
|
+
section and four exotic-whitespace documents (old Python: red).
|
|
43
|
+
|
|
44
|
+
**Consumer action:** code that branches on `SourceSpliceError.code` treats a
|
|
45
|
+
carried island that no longer stands as `island_edit` (it was `integrity`).
|
|
46
|
+
|
|
9
47
|
## 0.14.2 — 2026-09-25
|
|
10
48
|
|
|
11
49
|
**`/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++;
|
|
@@ -5955,8 +5973,7 @@ var Tokenizer = class {
|
|
|
5955
5973
|
*/
|
|
5956
5974
|
backtickFence(p, li, t) {
|
|
5957
5975
|
const openTicks = backtickRunLength(t, 0);
|
|
5958
|
-
const
|
|
5959
|
-
const lang = info.split(/\s/)[0] ?? "";
|
|
5976
|
+
const lang = parseFenceOpener(t)?.lang ?? "";
|
|
5960
5977
|
const isJson = lang === "json";
|
|
5961
5978
|
const normalized = normalizeFenceLanguage(lang);
|
|
5962
5979
|
const meta = { fence: "`", ticks: openTicks, lang };
|
|
@@ -6086,6 +6103,7 @@ var Tokenizer = class {
|
|
|
6086
6103
|
const endLine = this.lineOf(close);
|
|
6087
6104
|
const endCe = this.contentEnds[endLine];
|
|
6088
6105
|
if (endLine !== li && !this.isBlank(end, endCe)) return null;
|
|
6106
|
+
if (this.isOpenTail(endLine)) return this.island("math_block", p, this.n, false, { delimiter: "$$" });
|
|
6089
6107
|
return this.island("math_block", p, end, true, { delimiter: "$$" });
|
|
6090
6108
|
}
|
|
6091
6109
|
/** `<artifact …>`, `<decision …>`, … at the start of a line. */
|
|
@@ -6415,7 +6433,7 @@ var Tokenizer = class {
|
|
|
6415
6433
|
continue;
|
|
6416
6434
|
}
|
|
6417
6435
|
const end = singleDollarMathEnd(text, i, pe);
|
|
6418
|
-
if (end > 0) {
|
|
6436
|
+
if (end > 0 && !(this.streaming && end === this.n)) {
|
|
6419
6437
|
inlines.push(this.island("math_inline", i, end, true, { delimiter: "$" }));
|
|
6420
6438
|
i = end;
|
|
6421
6439
|
continue;
|
|
@@ -6825,11 +6843,17 @@ function spliceSave(original, edits, options = {}) {
|
|
|
6825
6843
|
else newIslandsByStart.set(island.start, [island.raw]);
|
|
6826
6844
|
}
|
|
6827
6845
|
const disturbed = [];
|
|
6846
|
+
const carried = (island) => sorted.some((edit) => !edit.island && island.start >= edit.start && island.end <= edit.end);
|
|
6847
|
+
const changedInside = [];
|
|
6828
6848
|
for (const island of islands) {
|
|
6829
6849
|
if (replacedIslands.has(`${island.start}:${island.end}`)) continue;
|
|
6830
6850
|
const mappedStart = mapPosition(changes, island.start, { assoc: 1 }).pos;
|
|
6831
6851
|
const candidates = newIslandsByStart.get(mappedStart);
|
|
6832
6852
|
if (!candidates || !candidates.includes(island.raw)) {
|
|
6853
|
+
if (carried(island)) {
|
|
6854
|
+
changedInside.push(island);
|
|
6855
|
+
continue;
|
|
6856
|
+
}
|
|
6833
6857
|
disturbed.push({
|
|
6834
6858
|
islandType: island.islandType,
|
|
6835
6859
|
inline: island.inline,
|
|
@@ -6839,6 +6863,14 @@ function spliceSave(original, edits, options = {}) {
|
|
|
6839
6863
|
});
|
|
6840
6864
|
}
|
|
6841
6865
|
}
|
|
6866
|
+
if (changedInside.length > 0) {
|
|
6867
|
+
const first = changedInside[0];
|
|
6868
|
+
const name = first.meta.tag ?? first.meta.name ?? first.meta.lang ?? "";
|
|
6869
|
+
throw new SourceSpliceError(
|
|
6870
|
+
"island_edit",
|
|
6871
|
+
`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()`
|
|
6872
|
+
);
|
|
6873
|
+
}
|
|
6842
6874
|
const integrity = {
|
|
6843
6875
|
ok: bytesOutsideEditsIdentical && disturbed.length === 0,
|
|
6844
6876
|
bytesOutsideEditsIdentical,
|
|
@@ -6896,6 +6928,7 @@ exports.CAPABILITY_BY_CLASS = CAPABILITY_BY_CLASS;
|
|
|
6896
6928
|
exports.CLASSES = CLASSES;
|
|
6897
6929
|
exports.DIRECTIVE_VERSION = DIRECTIVE_VERSION;
|
|
6898
6930
|
exports.DirectiveDecodeError = DirectiveDecodeError;
|
|
6931
|
+
exports.FENCE_OPENER_WHITESPACE = FENCE_OPENER_WHITESPACE;
|
|
6899
6932
|
exports.FENCE_WHITESPACE = FENCE_WHITESPACE;
|
|
6900
6933
|
exports.IN_CONTENT_CLASSES = IN_CONTENT_CLASSES;
|
|
6901
6934
|
exports.IR_ENVELOPE_CACHE_VERSION = IR_ENVELOPE_CACHE_VERSION;
|
|
@@ -6998,6 +7031,7 @@ exports.nounTitleColumn = nounTitleColumn;
|
|
|
6998
7031
|
exports.openParseSession = openParseSession;
|
|
6999
7032
|
exports.pairDisplayMath = pairDisplayMath;
|
|
7000
7033
|
exports.parseDirectiveSlug = parseDirectiveSlug;
|
|
7034
|
+
exports.parseFenceOpener = parseFenceOpener;
|
|
7001
7035
|
exports.readEnvelope = readEnvelope;
|
|
7002
7036
|
exports.readNodeOutcomeValue = readNodeOutcomeValue;
|
|
7003
7037
|
exports.readObjectKind = readObjectKind;
|