sfora-cli 0.10.0 → 0.11.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.
Files changed (68) hide show
  1. package/README.md +139 -0
  2. package/dist/SforaFs.js +8 -6
  3. package/dist/api-client.d.ts +243 -4
  4. package/dist/api-client.js +248 -20
  5. package/dist/block-commands.d.ts +84 -0
  6. package/dist/block-commands.js +155 -0
  7. package/dist/cli.js +317 -26
  8. package/dist/format/blockSplice.d.ts +135 -0
  9. package/dist/format/blockSplice.js +330 -0
  10. package/dist/format/blocks/dropClosure.d.ts +10 -1
  11. package/dist/format/blocks/dropClosure.js +11 -1
  12. package/dist/format/callout.d.ts +69 -7
  13. package/dist/format/callout.js +112 -15
  14. package/dist/format/checklist.js +11 -4
  15. package/dist/format/formatAxes.d.ts +228 -0
  16. package/dist/format/formatAxes.js +454 -0
  17. package/dist/format/index.d.ts +1 -0
  18. package/dist/format/index.js +4 -0
  19. package/dist/format/lineGeometry.d.ts +34 -4
  20. package/dist/format/lineGeometry.js +140 -40
  21. package/dist/format/lint/appliesTo.d.ts +92 -0
  22. package/dist/format/lint/appliesTo.js +369 -0
  23. package/dist/format/lint/config.d.ts +106 -0
  24. package/dist/format/lint/config.js +205 -0
  25. package/dist/format/lint/fixAll.d.ts +62 -0
  26. package/dist/format/lint/fixAll.js +107 -0
  27. package/dist/format/lint/frontmatterSchema.d.ts +181 -0
  28. package/dist/format/lint/frontmatterSchema.js +660 -0
  29. package/dist/format/lint/index.d.ts +34 -5
  30. package/dist/format/lint/index.js +34 -5
  31. package/dist/format/lint/lintSource.d.ts +27 -7
  32. package/dist/format/lint/lintSource.js +67 -33
  33. package/dist/format/lint/rules/frontmatter-schema.d.ts +2 -0
  34. package/dist/format/lint/rules/frontmatter-schema.js +92 -0
  35. package/dist/format/lint/rules/index.d.ts +2 -1
  36. package/dist/format/lint/rules/index.js +7 -1
  37. package/dist/format/lint/rules/malformed-callout.js +25 -16
  38. package/dist/format/lint/rules/malformed-checklist.js +8 -3
  39. package/dist/format/lint/rules/malformed-frontmatter.js +6 -1
  40. package/dist/format/lint/severity.d.ts +15 -0
  41. package/dist/format/lint/severity.js +50 -0
  42. package/dist/format/lint/textEdits.d.ts +86 -0
  43. package/dist/format/lint/textEdits.js +162 -0
  44. package/dist/format/lint/types.d.ts +44 -8
  45. package/dist/format/markdown/slug.d.ts +28 -0
  46. package/dist/format/markdown/slug.js +63 -0
  47. package/dist/format/plaintext.js +13 -3
  48. package/dist/format/sheetCellSpans.d.ts +95 -0
  49. package/dist/format/sheetCellSpans.js +223 -0
  50. package/dist/format/sheetSelection.d.ts +136 -0
  51. package/dist/format/sheetSelection.js +282 -0
  52. package/dist/format/textStats.d.ts +23 -0
  53. package/dist/format/textStats.js +80 -0
  54. package/dist/format/wikiLinks.d.ts +60 -1
  55. package/dist/format/wikiLinks.js +195 -9
  56. package/dist/index.d.ts +26 -1
  57. package/dist/index.js +20 -3
  58. package/dist/opener.d.ts +23 -0
  59. package/dist/opener.js +26 -0
  60. package/dist/render.d.ts +132 -0
  61. package/dist/render.js +208 -0
  62. package/dist/shell-commands.d.ts +34 -0
  63. package/dist/shell-commands.js +108 -0
  64. package/dist/watch.d.ts +79 -0
  65. package/dist/watch.js +113 -0
  66. package/dist/web-url.d.ts +39 -0
  67. package/dist/web-url.js +63 -0
  68. package/package.json +1 -1
@@ -0,0 +1,454 @@
1
+ /**
2
+ * The list. Sorted by id, because a list nobody can scan is not reviewable and
3
+ * reviewable is the entire point.
4
+ */
5
+ export const FORMAT_AXES = [
6
+ {
7
+ id: "blank-lines:doc-edge",
8
+ layer: "blank-runs",
9
+ dataKeys: ["sourceEdgeBlankLines"],
10
+ rationale: "blank runs at the document's own two ends; `emitMarkdown` strips the terminator and writes no leading run, so without the plan an author's deliberate breathing room at the top or bottom of a body is gone on the first save",
11
+ witnesses: [
12
+ {
13
+ a: "A",
14
+ b: "A\n\n\n",
15
+ note: "a bare body against one with a trailing blank run",
16
+ },
17
+ ],
18
+ },
19
+ {
20
+ id: "blank-lines:interior",
21
+ layer: "blank-runs",
22
+ dataKeys: ["sourceBlankLinesBefore"],
23
+ rationale: "how many blank lines the author left between two root blocks; mdast-util-to-markdown writes exactly one and mdast has no field for the rest, so a double gap used as a section break closes on every save",
24
+ witnesses: [
25
+ {
26
+ a: "A\n\nB",
27
+ b: "A\n\n\nB",
28
+ note: "one blank line between two paragraphs against two",
29
+ },
30
+ ],
31
+ },
32
+ {
33
+ id: "block-gap:after-heading",
34
+ layer: "mdast-geometry",
35
+ captures: ["heading.sourceContiguousNext"],
36
+ rationale: "whether a heading is followed by a blank line before its first block; remark writes one unconditionally, so without this axis every contiguous heading in a document reflows on the first save",
37
+ witnesses: [
38
+ {
39
+ a: "# T\nbody",
40
+ b: "# T\n\nbody",
41
+ note: "the gap between the heading and the paragraph under it",
42
+ },
43
+ ],
44
+ },
45
+ {
46
+ id: "blockquote:marker-spacing",
47
+ layer: "mdast-geometry",
48
+ captures: ["blockquote.sourceMarkerSpacings"],
49
+ rationale: "how many spaces followed each `>`; CommonMark strips one and the rest are the author's, per line",
50
+ witnesses: [
51
+ {
52
+ a: "> q",
53
+ b: ">q",
54
+ note: "one space after the marker against none",
55
+ },
56
+ ],
57
+ },
58
+ {
59
+ id: "code-block:style",
60
+ layer: "mdast-geometry",
61
+ captures: ["code.sourceStyle"],
62
+ rationale: "fenced against indented; the same four lines of code, spelled two ways that CommonMark reads identically",
63
+ witnesses: [
64
+ {
65
+ a: "```\nx\n```",
66
+ b: " x",
67
+ note: "a fence against a four-space indent — the widest spelling gap on the list",
68
+ },
69
+ ],
70
+ },
71
+ {
72
+ id: "code-fence:char",
73
+ layer: "mdast-geometry",
74
+ captures: ["code.sourceFenceChar"],
75
+ rationale: "backtick against tilde; the choice matters to an author whose body contains the other one",
76
+ witnesses: [
77
+ {
78
+ a: "```\nx\n```",
79
+ b: "~~~\nx\n~~~",
80
+ note: "the fence character",
81
+ },
82
+ ],
83
+ },
84
+ {
85
+ id: "code-fence:info-padding",
86
+ layer: "mdast-geometry",
87
+ captures: ["code.sourceInfoPadding"],
88
+ rationale: "spaces between the fence and its info string; CommonMark discards them and some authors line their fences up with them",
89
+ witnesses: [
90
+ {
91
+ a: "```ts\nx\n```",
92
+ b: "``` ts\nx\n```",
93
+ note: "the gap before the language",
94
+ },
95
+ ],
96
+ },
97
+ {
98
+ id: "code-fence:length",
99
+ layer: "mdast-geometry",
100
+ captures: ["code.sourceFenceLength"],
101
+ rationale: "how many fence characters; three is the minimum and more is a deliberate widening around a body that contains a closer-shaped line",
102
+ witnesses: [
103
+ {
104
+ a: "```\nx\n```",
105
+ b: "````\nx\n````",
106
+ note: "three characters against four",
107
+ },
108
+ ],
109
+ },
110
+ {
111
+ id: "definition:layout",
112
+ layer: "mdast-geometry",
113
+ captures: ["definition.sourceLayout"],
114
+ rationale: "whether a link reference definition sits on one line or wraps after the colon",
115
+ witnesses: [
116
+ {
117
+ a: "[a]: u",
118
+ b: "[a]:\n u",
119
+ note: "the destination on the label's line against the line below it",
120
+ },
121
+ ],
122
+ },
123
+ {
124
+ id: "definition:title-quote",
125
+ layer: "mdast-geometry",
126
+ captures: ["definition.sourceTitleMarker"],
127
+ rationale: "which of the three title containers a definition used — `\"`, `'` or `()`",
128
+ witnesses: [
129
+ {
130
+ a: '[a]: u "t"',
131
+ b: "[a]: u 't'",
132
+ note: "double quotes against single",
133
+ },
134
+ ],
135
+ },
136
+ {
137
+ id: "emphasis:delimiter",
138
+ layer: "mdast-geometry",
139
+ captures: ["emphasis.sourceDelimiter"],
140
+ rationale: "`*` against `_` for a single-delimiter emphasis run",
141
+ witnesses: [
142
+ { a: "*em*", b: "_em_", note: "the delimiter character" },
143
+ ],
144
+ },
145
+ {
146
+ id: "hard-break:style",
147
+ layer: "mdast-geometry",
148
+ captures: ["break.sourceStyle"],
149
+ rationale: "a hard break spelled as a trailing backslash or as two trailing spaces; one of them is invisible in every editor ever written",
150
+ witnesses: [
151
+ {
152
+ a: "a\\\nb",
153
+ b: "a \nb",
154
+ note: "backslash against two spaces",
155
+ },
156
+ ],
157
+ },
158
+ {
159
+ id: "heading:style",
160
+ layer: "mdast-geometry",
161
+ captures: ["heading.sourceStyle"],
162
+ rationale: "atx (`# T`) against setext (`T` over `=`); the same heading, and the only two spellings CommonMark has for depth one and two",
163
+ witnesses: [
164
+ {
165
+ a: "# T",
166
+ b: "T\n=",
167
+ note: "atx against setext — moves the underline length with it, which is the next axis's business",
168
+ },
169
+ ],
170
+ },
171
+ {
172
+ id: "heading:trailing-hashes",
173
+ layer: "mdast-geometry",
174
+ captures: ["heading.sourceTrailingHashes"],
175
+ rationale: "the optional closing run on an atx heading, which CommonMark discards entirely",
176
+ witnesses: [
177
+ { a: "# T", b: "# T #", note: "the closing hash" },
178
+ ],
179
+ },
180
+ {
181
+ id: "inline-code:fence-length",
182
+ layer: "mdast-geometry",
183
+ captures: ["inlineCode.sourceFenceLength"],
184
+ rationale: "how many backticks open and close a code span; more than one is how an author writes a backtick inside one",
185
+ witnesses: [
186
+ { a: "`x`", b: "``x``", note: "one backtick against two" },
187
+ ],
188
+ },
189
+ {
190
+ id: "inline-code:padding",
191
+ layer: "mdast-geometry",
192
+ captures: ["inlineCode.sourcePadded"],
193
+ rationale: "the one space either side that CommonMark strips, which is how `` ` `` itself is written inside a span",
194
+ witnesses: [
195
+ { a: "`x`", b: "` x `", note: "the stripped padding" },
196
+ ],
197
+ },
198
+ {
199
+ id: "lexical:backslash-escape",
200
+ layer: "mdast-geometry",
201
+ captures: ["text.escapedChars"],
202
+ rationale: "where the author put a backslash in front of a character that did not need one; the value is the same string either way, and re-deriving the escapes from it is how a serializer starts escaping things nobody typed",
203
+ witnesses: [
204
+ {
205
+ a: "a\\*b",
206
+ b: "a*b",
207
+ note: "an escaped asterisk against a bare one that opens nothing",
208
+ },
209
+ ],
210
+ },
211
+ {
212
+ id: "link:angle-url",
213
+ layer: "mdast-geometry",
214
+ captures: ["link.sourceUrlForm"],
215
+ rationale: "`[a](<u>)` against `[a](u)`; the angle form is what an author reaches for when the destination has a space in it, and keeping it is how the habit survives an edit elsewhere",
216
+ witnesses: [
217
+ { a: "[a](u)", b: "[a](<u>)", note: "the angle brackets around the destination" },
218
+ ],
219
+ },
220
+ {
221
+ id: "link:autolink-form",
222
+ layer: "mdast-geometry",
223
+ captures: ["link.sourceStyle"],
224
+ rationale: "a bare GFM literal autolink against the same url in angle brackets; both are one link node with the same href",
225
+ witnesses: [
226
+ {
227
+ a: "https://e.com",
228
+ b: "<https://e.com>",
229
+ note: "the gfm literal form against the angle form",
230
+ },
231
+ ],
232
+ },
233
+ {
234
+ id: "link:title-quote",
235
+ layer: "mdast-geometry",
236
+ captures: ["link.sourceTitleMarker"],
237
+ rationale: "which of the three title containers an inline link used — `\"`, `'` or `()`",
238
+ witnesses: [
239
+ { a: '[a](u "t")', b: "[a](u 't')", note: "double quotes against single" },
240
+ ],
241
+ },
242
+ {
243
+ id: "list:bullet-marker",
244
+ layer: "mdast-geometry",
245
+ captures: ["list.bulletMarker"],
246
+ rationale: "`-`, `*` or `+`; mdast-util-to-markdown picks one globally and alternates it between adjacent lists, so without the axis a document converges on whichever the serializer likes",
247
+ witnesses: [
248
+ { a: "- a", b: "* a", note: "the bullet character" },
249
+ ],
250
+ },
251
+ {
252
+ id: "list:item-marker-spacing",
253
+ layer: "mdast-geometry",
254
+ captures: ["listItem.sourceMarkerSpacing"],
255
+ rationale: "spaces between a list marker and its content; one to four all mean the same item and set different continuation indents",
256
+ witnesses: [
257
+ { a: "- a", b: "- a", note: "one space after the bullet against three" },
258
+ ],
259
+ },
260
+ {
261
+ id: "list:item-ordinal",
262
+ layer: "mdast-geometry",
263
+ captures: ["listItem.sourceOrdinal"],
264
+ rationale: "the number each ordered item was actually typed with; mdast keeps the list's `start` and nothing else, so `1. 1. 1.` and `1. 2. 3.` are the same tree",
265
+ witnesses: [
266
+ {
267
+ a: "1. a\n1. b",
268
+ b: "1. a\n2. b",
269
+ note: "all-ones against counting up",
270
+ },
271
+ ],
272
+ },
273
+ {
274
+ id: "list:ordered-delimiter",
275
+ layer: "mdast-geometry",
276
+ captures: ["list.listMarkerDelimiter"],
277
+ rationale: "`1.` against `1)` for an ordered list",
278
+ witnesses: [
279
+ { a: "1. a", b: "1) a", note: "the character after the ordinal" },
280
+ ],
281
+ },
282
+ {
283
+ id: "setext:underline-length",
284
+ layer: "mdast-geometry",
285
+ captures: ["heading.sourceUnderlineLength"],
286
+ rationale: "how long the `=` or `-` run under a setext heading is; one character is enough and authors rule the whole width",
287
+ witnesses: [
288
+ { a: "T\n=", b: "T\n===", note: "the underline run" },
289
+ ],
290
+ },
291
+ {
292
+ id: "strike:delimiter",
293
+ layer: "mdast-geometry",
294
+ captures: ["delete.sourceDelimiter"],
295
+ rationale: "GFM's `~x~` against `~~x~~`; the single-tilde form is not in every reader's dialect, which is why the capture lands and the replay is a rendering decision",
296
+ witnesses: [
297
+ { a: "~~x~~", b: "~x~", note: "two tildes against one" },
298
+ ],
299
+ },
300
+ {
301
+ id: "strong:delimiter",
302
+ layer: "mdast-geometry",
303
+ captures: ["strong.sourceDelimiter"],
304
+ rationale: "`**` against `__` for a double-delimiter run",
305
+ witnesses: [
306
+ { a: "**s**", b: "__s__", note: "the delimiter run" },
307
+ ],
308
+ },
309
+ {
310
+ id: "structured-block:entry-bullet",
311
+ layer: "structured-block",
312
+ ledgerReasons: ["entry-bullet-marker"],
313
+ rationale: "a status or chat entry may be authored bare, with `-`, or with `*`; the block grammars strip the marker before reading the entry, so all three parse to the same timeline and which one was typed is a spelling rather than content",
314
+ witnesses: [
315
+ {
316
+ lang: "chat",
317
+ a: "- @mara: on it\n",
318
+ b: "* @mara: on it\n",
319
+ note: "the entry bullet — the id the wave-1 drop ledger already spends",
320
+ },
321
+ {
322
+ lang: "status",
323
+ a: "state: building\n- @ada: on it\n",
324
+ b: "state: building\n* @ada: on it\n",
325
+ note: "the same axis one block language over, because the ledger entry is not chat-specific",
326
+ },
327
+ ],
328
+ },
329
+ {
330
+ id: "table:cell-padding",
331
+ layer: "mdast-geometry",
332
+ captures: ["tableCell.sourcePadding"],
333
+ rationale: "the spaces inside each cell's pipes, which is what makes a hand-aligned table a grid instead of a wall",
334
+ witnesses: [
335
+ {
336
+ a: "| a |\n| - |",
337
+ b: "|a|\n| - |",
338
+ note: "padded cells against tight ones",
339
+ },
340
+ ],
341
+ },
342
+ {
343
+ id: "table:delimiter-dashes",
344
+ layer: "mdast-geometry",
345
+ captures: ["table.sourceDashCounts"],
346
+ rationale: "how many dashes each delimiter cell holds; one is legal and authors widen them to the column",
347
+ witnesses: [
348
+ {
349
+ a: "| a |\n| - |",
350
+ b: "| a |\n| --- |",
351
+ note: "a one-dash delimiter against a three-dash one",
352
+ },
353
+ ],
354
+ },
355
+ {
356
+ id: "table:delimiter-padding",
357
+ layer: "mdast-geometry",
358
+ captures: ["table.sourceAlignmentPadding"],
359
+ rationale: "the spaces around the dashes in the delimiter row, captured apart from the dash count because the two move independently",
360
+ witnesses: [
361
+ {
362
+ a: "| a |\n|---|",
363
+ b: "| a |\n| --- |",
364
+ note: "the same three dashes, padded and not",
365
+ },
366
+ ],
367
+ },
368
+ {
369
+ id: "table:outer-pipes",
370
+ layer: "mdast-geometry",
371
+ captures: ["table.sourceOuterPipes"],
372
+ rationale: "whether the rows carry their leading and trailing pipes; GFM makes both optional and the pipe-less form is a real authoring style",
373
+ witnesses: [
374
+ {
375
+ a: "| a | b |\n| :-- | --: |\n| 1 | 2 |",
376
+ b: "a | b\n:-- | --:\n1 | 2",
377
+ note: "edge pipes against none — the alignment colons keep the delimiter row from reading as a setext underline",
378
+ },
379
+ ],
380
+ },
381
+ {
382
+ id: "task-list:checkbox-char",
383
+ layer: "mdast-geometry",
384
+ captures: ["listItem.sourceCheckboxChar"],
385
+ rationale: "`[x]` against `[X]`; GFM checks the box either way and one of them is what the author's other tool writes",
386
+ witnesses: [
387
+ { a: "- [x] a", b: "- [X] a", note: "the checkbox character's case" },
388
+ ],
389
+ },
390
+ {
391
+ id: "thematic-break:marker",
392
+ layer: "mdast-geometry",
393
+ captures: ["thematicBreak.sourceRaw"],
394
+ rationale: "the whole authored run — `***`, `---`, `- - -`, `___` and every spaced variant are one thematic break",
395
+ witnesses: [
396
+ { a: "***", b: "- - -", note: "the marker run, verbatim" },
397
+ ],
398
+ },
399
+ ];
400
+ /** Every id on the list, for a cheap membership test. */
401
+ export const FORMAT_AXIS_IDS = new Set(FORMAT_AXES.map((axis) => axis.id));
402
+ /**
403
+ * The axis with this id.
404
+ *
405
+ * Throws rather than returning undefined: every call site here is spending an
406
+ * id that the type system already proved exists, so a miss means the list and
407
+ * the union disagree, which is a bug in this file and not a case a caller
408
+ * should be writing a branch for.
409
+ */
410
+ export function formatAxis(id) {
411
+ const found = FORMAT_AXES.find((axis) => axis.id === id);
412
+ if (!found)
413
+ throw new Error(`no format axis with id ${id}`);
414
+ return found;
415
+ }
416
+ /**
417
+ * The axis that owns a captured geometry key, spelled `<nodeType>.<dataKey>`.
418
+ *
419
+ * Undefined here is the interesting answer, not an error: it means the capture
420
+ * layer records a spelling the vocabulary cannot name, which is exactly what
421
+ * the closure test is looking for.
422
+ */
423
+ export function axisForCapture(key) {
424
+ return FORMAT_AXES.find((axis) => axis.layer === "mdast-geometry" && axis.captures.includes(key));
425
+ }
426
+ /** Every `<nodeType>.<dataKey>` the vocabulary claims, deduplicated. */
427
+ export function claimedCaptureKeys() {
428
+ const out = new Set();
429
+ for (const axis of FORMAT_AXES) {
430
+ if (axis.layer !== "mdast-geometry")
431
+ continue;
432
+ for (const key of axis.captures)
433
+ out.add(key);
434
+ }
435
+ return [...out].sort();
436
+ }
437
+ /**
438
+ * Every blank-run `data` key the vocabulary claims, deduplicated.
439
+ *
440
+ * Kept apart from {@link claimedCaptureKeys} rather than merged into it because
441
+ * the two are spelled differently on purpose — one carries a node type and the
442
+ * other cannot — and a single list would have to pick one spelling and be wrong
443
+ * about half its entries.
444
+ */
445
+ export function claimedBlankRunKeys() {
446
+ const out = new Set();
447
+ for (const axis of FORMAT_AXES) {
448
+ if (axis.layer !== "blank-runs")
449
+ continue;
450
+ for (const key of axis.dataKeys)
451
+ out.add(key);
452
+ }
453
+ return [...out].sort();
454
+ }
@@ -24,4 +24,5 @@ export * from "./lint/index.js";
24
24
  export * from "./blocks/structured-block-schema.js";
25
25
  export * from "./blocks/markdown-block-catalog.js";
26
26
  export * from "./blocks/dropClosure.js";
27
+ export * from "./formatAxes.js";
27
28
  export * from "./wayfinder.js";
@@ -26,6 +26,10 @@ export * from "./lint/index.js";
26
26
  export * from "./blocks/structured-block-schema.js";
27
27
  export * from "./blocks/markdown-block-catalog.js";
28
28
  export * from "./blocks/dropClosure.js";
29
+ // The format-DOF vocabulary the ledger above spends (card #288). It is data
30
+ // and types only — no parser, no walker — so it rides the CLI sync alongside
31
+ // the ledger that would not compile without it.
32
+ export * from "./formatAxes.js";
29
33
  export * from "./wayfinder.js";
30
34
  // htmlToMarkdown and parseMarkdownAst are deliberately NOT in the barrel:
31
35
  // they carry the package's only heavy dependencies (unified/rehype/remark)
@@ -26,6 +26,16 @@ export declare function frontmatterExtent(lines: readonly string[]): {
26
26
  open: number;
27
27
  close: number;
28
28
  } | null;
29
+ /**
30
+ * The first line of the document's markdown: 0, or the line after a leading
31
+ * frontmatter block. A frontmatter block that never closes takes the whole
32
+ * document with it, which is what a reader sees too.
33
+ *
34
+ * Lint and the mask both need this number and must not each compute it: one of
35
+ * them being wrong about where the YAML ends is exactly how a `<!--` in a
36
+ * summary field opens a comment over a whole file.
37
+ */
38
+ export declare function markdownStart(lines: readonly string[]): number;
29
39
  /**
30
40
  * Every fenced code block in the document, in order. `from` skips the
31
41
  * frontmatter block, whose contents are not markdown either.
@@ -48,17 +58,37 @@ export declare function codeSpanMask(line: string): boolean[];
48
58
  * length of the line it masks, so `masked[i][k]` and `lines[i][k]` are the
49
59
  * same byte position.
50
60
  *
51
- * `from` skips a leading frontmatter block for the FENCE scan only, mirroring
52
- * {@link fenceMask} — whether the frontmatter itself renders is the caller's
53
- * question, and lint already answers it with `inFrontmatter`.
61
+ * `from` is where the document's markdown begins — everything above it is a
62
+ * frontmatter block, which is not markdown at all and is returned verbatim for
63
+ * whoever does read it (lint, with `inFrontmatter`). Scanning it would be
64
+ * worse than useless: a `<!--` in a YAML value would open a comment over the
65
+ * whole document. Callers holding a BODY pass 0 — a leading `---` there is a
66
+ * thematic break, not frontmatter — and `maskNonRenderingContexts` works the
67
+ * boundary out with `markdownStart`.
68
+ *
69
+ * A fence decides the whole line, but only where nothing is open already: a
70
+ * ``` line inside an unterminated comment is comment text, and the document
71
+ * below the `-->` renders. That ordering is CommonMark's — an HTML block ends
72
+ * at its closing condition, not at the next thing that looks like a fence —
73
+ * and it is the one place fence geometry does not go first. Where the line is
74
+ * clear, `fenceRegionAt` answers, so there is still exactly one idea in the
75
+ * package of where a fence starts and ends.
76
+ *
77
+ * Lines are whatever the caller split; a bare `\r` inside one is not a line
78
+ * ending here. `maskNonRenderingContexts` splits the way CommonMark does.
54
79
  */
55
80
  export declare function maskNonRenderingLines(lines: readonly string[], from?: number): string[];
56
81
  /**
57
82
  * The whole source, masked. `masked.length === source.length` always: the two
58
83
  * strings are the same document, one of them with the non-markdown blanked
59
84
  * out, and every offset holds across both.
85
+ *
86
+ * This is the DOCUMENT entry point — cm-lint and `demoteNonRenderingLinks`
87
+ * hand it whole files — so the frontmatter boundary is worked out here rather
88
+ * than assumed to be 0. Pass `from` explicitly when the caller knows better:
89
+ * 0 says "these bytes are all markdown", which is what a body is.
60
90
  */
61
- export declare function maskNonRenderingContexts(source: string): string;
91
+ export declare function maskNonRenderingContexts(source: string, from?: number): string;
62
92
  /**
63
93
  * True when the source span `[start, end)` holds bytes but no rendering ones —
64
94
  * the predicate a consumer asks before trusting anything it found by scanning.