@ai-matrx/content-ir 0.12.0 → 0.13.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/source.d.cts CHANGED
@@ -114,20 +114,30 @@ declare function blockAt(blocks: readonly SourceBlock[], offset: number): Source
114
114
  * uses to carry every live anchor through the save
115
115
  * (STORE-DESIGN §3.19 — the ProseMirror / Google Docs position-mapping
116
116
  * model), and
117
- * - an integrity report: every island outside the edited ranges must still
117
+ * - an integrity guarantee: every island not explicitly replaced must still
118
118
  * be the same island, at its mapped position, in the new text. An edit
119
- * that opens an unclosed fence and swallows a kind below it is caught
120
- * here instead of being saved silently.
119
+ * that opens an unclosed fence and swallows a kind below it is REFUSED.
120
+ *
121
+ * ISLANDS CHANGE ONLY THROUGH `islandEdit`. A prose/range edit must hand back
122
+ * every island inside its range byte-identical and in order (a visual editor
123
+ * holds them as atoms); if one is altered or dropped the save throws
124
+ * `SourceSpliceError("island_edit")` naming the island. A kind editor or code
125
+ * view changes an island with `islandEdit(island, newRaw)`.
121
126
  *
122
127
  * A no-edit save (or edits whose text equals the original block) returns the
123
128
  * original string itself — byte-identical, zero changes.
124
129
  */
125
130
 
126
- /** Replace `[start, end)` (UTF-16, on block boundaries) of the original with `text`. */
131
+ /**
132
+ * Replace `[start, end)` (UTF-16) of the original with `text`. A plain edit
133
+ * starts and ends on block boundaries; an island edit (`island: true`, made by
134
+ * `islandEdit`) names exactly one island's span.
135
+ */
127
136
  interface SourceEdit {
128
137
  readonly start: number;
129
138
  readonly end: number;
130
139
  readonly text: string;
140
+ readonly island?: boolean;
131
141
  }
132
142
  interface SourceChange {
133
143
  /** The block-aligned range the edit named, in the ORIGINAL text. */
@@ -168,7 +178,7 @@ interface SpliceResult {
168
178
  /** Tokenization of the new text. */
169
179
  readonly blocks: readonly SourceBlock[];
170
180
  }
171
- type SourceSpliceErrorCode = "out_of_range" | "misaligned" | "overlap" | "stale_blocks" | "integrity";
181
+ type SourceSpliceErrorCode = "out_of_range" | "misaligned" | "overlap" | "stale_blocks" | "island_edit" | "integrity";
172
182
  declare class SourceSpliceError extends Error {
173
183
  readonly code: SourceSpliceErrorCode;
174
184
  constructor(code: SourceSpliceErrorCode, message: string);
@@ -176,11 +186,21 @@ declare class SourceSpliceError extends Error {
176
186
  interface SpliceOptions {
177
187
  /** The tokenization of `original`, when the caller already holds it. */
178
188
  readonly blocks?: readonly SourceBlock[];
179
- /** Throw `SourceSpliceError("integrity")` instead of reporting a disturbed island. */
189
+ /**
190
+ * Default `true`: a save that disturbs any island it did not explicitly
191
+ * replace throws `SourceSpliceError("integrity")`. `false` is for TOOLING
192
+ * ONLY (diagnostic scripts that want the report instead of the refusal) —
193
+ * no editor or persistence path may pass it.
194
+ */
180
195
  readonly requireIntegrity?: boolean;
181
196
  }
182
197
  /** An edit that replaces one block's raw text. */
183
198
  declare function blockEdit(block: SourceBlock, text: string): SourceEdit;
199
+ /** The one door that changes an island: replace exactly its span with `text`. */
200
+ declare function islandEdit(island: {
201
+ readonly start: number;
202
+ readonly end: number;
203
+ }, text: string): SourceEdit;
184
204
  /** An edit that replaces a contiguous run of blocks `[first, last]`. */
185
205
  declare function blockRangeEdit(first: SourceBlock, last: SourceBlock, text: string): SourceEdit;
186
206
  /**
@@ -286,7 +306,12 @@ interface ReadXmlTag {
286
306
  value: string;
287
307
  }>;
288
308
  }
289
- declare function readXmlTag(content: string, start: number): ReadXmlTag | null;
309
+ /**
310
+ * `find(needle, from)` must behave like `content.indexOf(needle, from)`; the
311
+ * tokenizer passes a cached occurrence index so a text full of unclosed
312
+ * attribute quotes stays linear. Behaviour is identical either way.
313
+ */
314
+ declare function readXmlTag(content: string, start: number, find?: (needle: string, from: number) => number): ReadXmlTag | null;
290
315
  /**
291
316
  * Balances ONE unknown root tag line by line, ignoring lookalike tags inside
292
317
  * fenced code, inline code spans, comments and CDATA. `consumeLine` returns
@@ -303,4 +328,47 @@ declare class XmlContainerTracker {
303
328
  consumeLine(line: string, startOffset?: number): number | null;
304
329
  }
305
330
 
306
- export { type BlockIslandType, type CodePointIndex, type DisturbedIsland, type InlineIslandType, type IslandMeta, type IslandType, type MapOptions, type MappedPosition, type MappedRange, type ReadXmlTag, type SourceBlock, type SourceBlockKind, type SourceChange, type SourceEdit, type SourceIsland, type SourceSpan, SourceSpliceError, type SourceSpliceErrorCode, type SpliceIntegrity, type SpliceOptions, type SpliceResult, XmlContainerTracker, blockAt, blockEdit, blockRangeEdit, buildCodePointIndex, isSingleDollarMath, joinSource, listIslands, mapPosition, mapRange, readXmlTag, singleDollarMathEnd, spliceSave, splitsSurrogatePair, toCodePointOffset, toUtf16Offset, tokenizeSource };
331
+ /** Fence languages whose body is itself a markdown document. */
332
+ declare const NESTING_FENCE_LANGUAGES: ReadonlySet<string>;
333
+ /** True when a fence opened with this info-string language nests inner fences. */
334
+ declare function fenceNestsInnerFences(language: string | undefined | null): boolean;
335
+ type InnerFenceLine =
336
+ /** Not a fence line — ordinary content of the outer fence. */
337
+ "content"
338
+ /** ```lang inside a markdown fence — content, and one level deeper. */
339
+ | "open-nested"
340
+ /** Bare ``` that closes the innermost nested fence — content, one level up. */
341
+ | "close-nested"
342
+ /** Bare ``` that closes the OUTER fence. */
343
+ | "close-outer";
344
+ /**
345
+ * Classify one TRIMMED line inside an open backtick fence.
346
+ *
347
+ * @param trimmed the line with surrounding whitespace removed
348
+ * @param openTicks backtick count of the outer fence's opener
349
+ * @param nests whether the outer fence follows the nesting rule
350
+ * @param nestedDepth how many nested fences are open right now
351
+ */
352
+ declare function classifyInnerFenceLine(trimmed: string, openTicks: number, nests: boolean, nestedDepth: number): InnerFenceLine;
353
+
354
+ /**
355
+ * `$$` pairing — the core's rule for which `$$` delimiters are real math.
356
+ *
357
+ * Ported from `@ai-matrx/kit/delimiter-guard` (`guardMathDelimiters`), which
358
+ * every math-capable renderer runs before remark-math. Its decisions:
359
+ * - `$$` tokens outside code (``` / ~~~ fences, `inline code`) pair in
360
+ * document order, open → close;
361
+ * - a pair whose inside reads as prose or carries markdown structure is NOT
362
+ * math: the renderer neutralizes its opener and the closer is retried as
363
+ * the next opener;
364
+ * - an unpaired `$$` is inert — it renders as literal text.
365
+ * So the tokenizer protects exactly the pairs the renderer shows as math, and
366
+ * a lone `$$` (a stray closer, or an opener that never closed) never locks
367
+ * anything. Linear time: one regex pass per code shape, one sweep.
368
+ */
369
+ /** The guard's `looksLikeMath`, verbatim. */
370
+ declare function looksLikeDisplayMath(inner: string): boolean;
371
+ /** Map of `$$` opener offset → closer offset for every pair the renderer shows as math. */
372
+ declare function pairDisplayMath(text: string): Map<number, number>;
373
+
374
+ export { type BlockIslandType, type CodePointIndex, type DisturbedIsland, type InlineIslandType, type InnerFenceLine, type IslandMeta, type IslandType, type MapOptions, type MappedPosition, type MappedRange, NESTING_FENCE_LANGUAGES, type ReadXmlTag, type SourceBlock, type SourceBlockKind, type SourceChange, type SourceEdit, type SourceIsland, type SourceSpan, SourceSpliceError, type SourceSpliceErrorCode, type SpliceIntegrity, type SpliceOptions, type SpliceResult, XmlContainerTracker, blockAt, blockEdit, blockRangeEdit, buildCodePointIndex, classifyInnerFenceLine, fenceNestsInnerFences, isSingleDollarMath, islandEdit, joinSource, listIslands, looksLikeDisplayMath, mapPosition, mapRange, pairDisplayMath, readXmlTag, singleDollarMathEnd, spliceSave, splitsSurrogatePair, toCodePointOffset, toUtf16Offset, tokenizeSource };
package/dist/source.d.ts CHANGED
@@ -114,20 +114,30 @@ declare function blockAt(blocks: readonly SourceBlock[], offset: number): Source
114
114
  * uses to carry every live anchor through the save
115
115
  * (STORE-DESIGN §3.19 — the ProseMirror / Google Docs position-mapping
116
116
  * model), and
117
- * - an integrity report: every island outside the edited ranges must still
117
+ * - an integrity guarantee: every island not explicitly replaced must still
118
118
  * be the same island, at its mapped position, in the new text. An edit
119
- * that opens an unclosed fence and swallows a kind below it is caught
120
- * here instead of being saved silently.
119
+ * that opens an unclosed fence and swallows a kind below it is REFUSED.
120
+ *
121
+ * ISLANDS CHANGE ONLY THROUGH `islandEdit`. A prose/range edit must hand back
122
+ * every island inside its range byte-identical and in order (a visual editor
123
+ * holds them as atoms); if one is altered or dropped the save throws
124
+ * `SourceSpliceError("island_edit")` naming the island. A kind editor or code
125
+ * view changes an island with `islandEdit(island, newRaw)`.
121
126
  *
122
127
  * A no-edit save (or edits whose text equals the original block) returns the
123
128
  * original string itself — byte-identical, zero changes.
124
129
  */
125
130
 
126
- /** Replace `[start, end)` (UTF-16, on block boundaries) of the original with `text`. */
131
+ /**
132
+ * Replace `[start, end)` (UTF-16) of the original with `text`. A plain edit
133
+ * starts and ends on block boundaries; an island edit (`island: true`, made by
134
+ * `islandEdit`) names exactly one island's span.
135
+ */
127
136
  interface SourceEdit {
128
137
  readonly start: number;
129
138
  readonly end: number;
130
139
  readonly text: string;
140
+ readonly island?: boolean;
131
141
  }
132
142
  interface SourceChange {
133
143
  /** The block-aligned range the edit named, in the ORIGINAL text. */
@@ -168,7 +178,7 @@ interface SpliceResult {
168
178
  /** Tokenization of the new text. */
169
179
  readonly blocks: readonly SourceBlock[];
170
180
  }
171
- type SourceSpliceErrorCode = "out_of_range" | "misaligned" | "overlap" | "stale_blocks" | "integrity";
181
+ type SourceSpliceErrorCode = "out_of_range" | "misaligned" | "overlap" | "stale_blocks" | "island_edit" | "integrity";
172
182
  declare class SourceSpliceError extends Error {
173
183
  readonly code: SourceSpliceErrorCode;
174
184
  constructor(code: SourceSpliceErrorCode, message: string);
@@ -176,11 +186,21 @@ declare class SourceSpliceError extends Error {
176
186
  interface SpliceOptions {
177
187
  /** The tokenization of `original`, when the caller already holds it. */
178
188
  readonly blocks?: readonly SourceBlock[];
179
- /** Throw `SourceSpliceError("integrity")` instead of reporting a disturbed island. */
189
+ /**
190
+ * Default `true`: a save that disturbs any island it did not explicitly
191
+ * replace throws `SourceSpliceError("integrity")`. `false` is for TOOLING
192
+ * ONLY (diagnostic scripts that want the report instead of the refusal) —
193
+ * no editor or persistence path may pass it.
194
+ */
180
195
  readonly requireIntegrity?: boolean;
181
196
  }
182
197
  /** An edit that replaces one block's raw text. */
183
198
  declare function blockEdit(block: SourceBlock, text: string): SourceEdit;
199
+ /** The one door that changes an island: replace exactly its span with `text`. */
200
+ declare function islandEdit(island: {
201
+ readonly start: number;
202
+ readonly end: number;
203
+ }, text: string): SourceEdit;
184
204
  /** An edit that replaces a contiguous run of blocks `[first, last]`. */
185
205
  declare function blockRangeEdit(first: SourceBlock, last: SourceBlock, text: string): SourceEdit;
186
206
  /**
@@ -286,7 +306,12 @@ interface ReadXmlTag {
286
306
  value: string;
287
307
  }>;
288
308
  }
289
- declare function readXmlTag(content: string, start: number): ReadXmlTag | null;
309
+ /**
310
+ * `find(needle, from)` must behave like `content.indexOf(needle, from)`; the
311
+ * tokenizer passes a cached occurrence index so a text full of unclosed
312
+ * attribute quotes stays linear. Behaviour is identical either way.
313
+ */
314
+ declare function readXmlTag(content: string, start: number, find?: (needle: string, from: number) => number): ReadXmlTag | null;
290
315
  /**
291
316
  * Balances ONE unknown root tag line by line, ignoring lookalike tags inside
292
317
  * fenced code, inline code spans, comments and CDATA. `consumeLine` returns
@@ -303,4 +328,47 @@ declare class XmlContainerTracker {
303
328
  consumeLine(line: string, startOffset?: number): number | null;
304
329
  }
305
330
 
306
- export { type BlockIslandType, type CodePointIndex, type DisturbedIsland, type InlineIslandType, type IslandMeta, type IslandType, type MapOptions, type MappedPosition, type MappedRange, type ReadXmlTag, type SourceBlock, type SourceBlockKind, type SourceChange, type SourceEdit, type SourceIsland, type SourceSpan, SourceSpliceError, type SourceSpliceErrorCode, type SpliceIntegrity, type SpliceOptions, type SpliceResult, XmlContainerTracker, blockAt, blockEdit, blockRangeEdit, buildCodePointIndex, isSingleDollarMath, joinSource, listIslands, mapPosition, mapRange, readXmlTag, singleDollarMathEnd, spliceSave, splitsSurrogatePair, toCodePointOffset, toUtf16Offset, tokenizeSource };
331
+ /** Fence languages whose body is itself a markdown document. */
332
+ declare const NESTING_FENCE_LANGUAGES: ReadonlySet<string>;
333
+ /** True when a fence opened with this info-string language nests inner fences. */
334
+ declare function fenceNestsInnerFences(language: string | undefined | null): boolean;
335
+ type InnerFenceLine =
336
+ /** Not a fence line — ordinary content of the outer fence. */
337
+ "content"
338
+ /** ```lang inside a markdown fence — content, and one level deeper. */
339
+ | "open-nested"
340
+ /** Bare ``` that closes the innermost nested fence — content, one level up. */
341
+ | "close-nested"
342
+ /** Bare ``` that closes the OUTER fence. */
343
+ | "close-outer";
344
+ /**
345
+ * Classify one TRIMMED line inside an open backtick fence.
346
+ *
347
+ * @param trimmed the line with surrounding whitespace removed
348
+ * @param openTicks backtick count of the outer fence's opener
349
+ * @param nests whether the outer fence follows the nesting rule
350
+ * @param nestedDepth how many nested fences are open right now
351
+ */
352
+ declare function classifyInnerFenceLine(trimmed: string, openTicks: number, nests: boolean, nestedDepth: number): InnerFenceLine;
353
+
354
+ /**
355
+ * `$$` pairing — the core's rule for which `$$` delimiters are real math.
356
+ *
357
+ * Ported from `@ai-matrx/kit/delimiter-guard` (`guardMathDelimiters`), which
358
+ * every math-capable renderer runs before remark-math. Its decisions:
359
+ * - `$$` tokens outside code (``` / ~~~ fences, `inline code`) pair in
360
+ * document order, open → close;
361
+ * - a pair whose inside reads as prose or carries markdown structure is NOT
362
+ * math: the renderer neutralizes its opener and the closer is retried as
363
+ * the next opener;
364
+ * - an unpaired `$$` is inert — it renders as literal text.
365
+ * So the tokenizer protects exactly the pairs the renderer shows as math, and
366
+ * a lone `$$` (a stray closer, or an opener that never closed) never locks
367
+ * anything. Linear time: one regex pass per code shape, one sweep.
368
+ */
369
+ /** The guard's `looksLikeMath`, verbatim. */
370
+ declare function looksLikeDisplayMath(inner: string): boolean;
371
+ /** Map of `$$` opener offset → closer offset for every pair the renderer shows as math. */
372
+ declare function pairDisplayMath(text: string): Map<number, number>;
373
+
374
+ export { type BlockIslandType, type CodePointIndex, type DisturbedIsland, type InlineIslandType, type InnerFenceLine, type IslandMeta, type IslandType, type MapOptions, type MappedPosition, type MappedRange, NESTING_FENCE_LANGUAGES, type ReadXmlTag, type SourceBlock, type SourceBlockKind, type SourceChange, type SourceEdit, type SourceIsland, type SourceSpan, SourceSpliceError, type SourceSpliceErrorCode, type SpliceIntegrity, type SpliceOptions, type SpliceResult, XmlContainerTracker, blockAt, blockEdit, blockRangeEdit, buildCodePointIndex, classifyInnerFenceLine, fenceNestsInnerFences, isSingleDollarMath, islandEdit, joinSource, listIslands, looksLikeDisplayMath, mapPosition, mapRange, pairDisplayMath, readXmlTag, singleDollarMathEnd, spliceSave, splitsSurrogatePair, toCodePointOffset, toUtf16Offset, tokenizeSource };