@effected/markdown 0.3.0 → 0.4.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/Frontmatter.js CHANGED
@@ -69,22 +69,43 @@ var FrontmatterEncodeError = class extends Schema.TaggedErrorClass()("Frontmatte
69
69
  }
70
70
  };
71
71
  /**
72
+ * Why a frontmatter decoder found no capture on a document.
73
+ *
74
+ * @remarks
75
+ * `"absent"` — the source genuinely has no frontmatter block.
76
+ * `"captureDisabled"` — the source opens with a well-formed frontmatter
77
+ * block, but the document carries no capture node: it was parsed without
78
+ * `frontmatter: true` (the toggle defaults off), so spec-correct CommonMark
79
+ * read the fences as ordinary content.
80
+ *
81
+ * @public
82
+ */
83
+ const FrontmatterMissingReason = Schema.Literals(["absent", "captureDisabled"]);
84
+ /**
72
85
  * Indicates that a document handed to a frontmatter decoder carries no
73
86
  * frontmatter capture.
74
87
  *
75
88
  * @remarks
76
- * Raised when the document genuinely has no frontmatter block — including
77
- * when it has one in the source but was parsed with the capture toggle off
78
- * (`MarkdownParseOptions.frontmatter` defaults to `false`). Cause-free: there
79
- * is nothing to diagnose beyond the absence itself. Consumers wanting
80
- * optional semantics can `Effect.catchTag("FrontmatterMissingError", ...)`
81
- * to a default.
89
+ * `reason` distinguishes the two ways this happens: `"absent"` when the
90
+ * source genuinely has no frontmatter block, `"captureDisabled"` when the
91
+ * source opens with one but the document was parsed with the capture toggle
92
+ * off (`MarkdownParseOptions.frontmatter` defaults to `false`) — the fix
93
+ * there is parsing with `frontmatter: true`, not editing the document. The
94
+ * discriminant is computed from the same structural signal
95
+ * `MarkdownDocument.hasFrontmatterBlock` exposes, so the error and the
96
+ * accessor can never disagree.
97
+ *
98
+ * Cause-free: there is nothing to diagnose beyond the absence itself.
99
+ * Consumers wanting optional semantics can
100
+ * `Effect.catchTag("FrontmatterMissingError", ...)` to a default.
82
101
  *
83
102
  * @public
84
103
  */
85
- var FrontmatterMissingError = class extends Schema.TaggedErrorClass()("FrontmatterMissingError", {}) {
104
+ var FrontmatterMissingError = class extends Schema.TaggedErrorClass()("FrontmatterMissingError", {
105
+ /** Why there is no capture: no block at all, or capture left off. */
106
+ reason: FrontmatterMissingReason }) {
86
107
  get message() {
87
- return "the document has no frontmatter capture; was it parsed with `frontmatter: true`?";
108
+ return this.reason === "captureDisabled" ? "the source opens with a frontmatter block, but it was not captured; parse with `frontmatter: true`" : "the document has no frontmatter block";
88
109
  }
89
110
  };
90
111
  /**
@@ -157,7 +178,8 @@ var MarkdownFrontmatter = class MarkdownFrontmatter {
157
178
  * The decoder reads the document's frontmatter capture (parse with
158
179
  * `frontmatter: true` — the toggle defaults off), decodes its raw value
159
180
  * through the codec, then validates the data against `schema`. Each stage
160
- * fails typed: no capture is {@link FrontmatterMissingError}, a
181
+ * fails typed: no capture is {@link FrontmatterMissingError} (its `reason`
182
+ * says whether the block is genuinely absent or capture was left off), a
161
183
  * wrong-format codec is {@link FrontmatterFormatMismatchError}, unparseable
162
184
  * content is {@link FrontmatterDecodeError}, and schema-invalid data is
163
185
  * {@link FrontmatterValidationError} carrying the structured issue.
@@ -178,7 +200,7 @@ var MarkdownFrontmatter = class MarkdownFrontmatter {
178
200
  static schema(schema, codec) {
179
201
  return (document) => {
180
202
  const node = document.frontmatter;
181
- return node === void 0 ? Effect.fail(new FrontmatterMissingError()) : codec.decode(node).pipe(Effect.flatMap((data) => Schema.decodeUnknownEffect(schema)(data).pipe(Effect.catchTag("SchemaError", (error) => Effect.fail(new FrontmatterValidationError({ issue: error.issue }))))));
203
+ return node === void 0 ? Effect.fail(new FrontmatterMissingError({ reason: document.hasFrontmatterBlock ? "captureDisabled" : "absent" })) : codec.decode(node).pipe(Effect.flatMap((data) => Schema.decodeUnknownEffect(schema)(data).pipe(Effect.catchTag("SchemaError", (error) => Effect.fail(new FrontmatterValidationError({ issue: error.issue }))))));
182
204
  };
183
205
  }
184
206
  /**
@@ -269,4 +291,4 @@ var MarkdownFrontmatter = class MarkdownFrontmatter {
269
291
  };
270
292
 
271
293
  //#endregion
272
- export { FrontmatterDecodeError, FrontmatterEncodeError, FrontmatterFormatMismatchError, FrontmatterMissingError, FrontmatterValidationError, MarkdownFrontmatter };
294
+ export { FrontmatterDecodeError, FrontmatterEncodeError, FrontmatterFormatMismatchError, FrontmatterMissingError, FrontmatterMissingReason, FrontmatterValidationError, MarkdownFrontmatter };
package/Markdown.js CHANGED
@@ -28,7 +28,9 @@ const MarkdownDialect = Schema.Literals(["commonmark", "gfm"]);
28
28
  * spec-conformant reading must hold unless a consumer opts in. Enabled, the
29
29
  * engine captures `---`/`+++`/`---json` blocks at offset 0 into a raw
30
30
  * `Frontmatter` head node — decoding it is the frontmatter codec modules'
31
- * job.
31
+ * job. Parsed with the toggle off, such a document reports no frontmatter —
32
+ * `MarkdownDocument.hasFrontmatterBlock` tells "no block in the source"
33
+ * apart from "parsed with capture off".
32
34
  *
33
35
  * @public
34
36
  */
@@ -181,10 +183,14 @@ var Markdown = class Markdown {
181
183
  * @remarks
182
184
  * Canonical serialization: fidelity fields (marker characters, fence
183
185
  * style, heading spelling) win when present; documented canonical
184
- * defaults apply when absent. The output re-parses to a render-equivalent
185
- * document — the corpus-pinned contract — but is not a byte-level
186
- * round-trip of any original source; surgical editing goes through the
187
- * offset-splice edit layer instead.
186
+ * defaults apply when absent. One default surprises: a language-less
187
+ * `Code` node with no explicit `fenceChar` serializes as an
188
+ * **indented** code block — set `fenceChar` (or a `lang`) on the node for
189
+ * a fence, or normalize afterward with
190
+ * `MarkdownFormattingOptions.codeBlockStyle: "fenced"`. The output
191
+ * re-parses to a render-equivalent document — the corpus-pinned
192
+ * contract — but is not a byte-level round-trip of any original source;
193
+ * surgical editing goes through the offset-splice edit layer instead.
188
194
  *
189
195
  * Failure is rare by design, symmetric with parse: only a hardening-guard
190
196
  * trip on a tree nesting past the depth cap fails, and only synthesized
@@ -1,6 +1,8 @@
1
1
  import { MarkdownRange } from "./MarkdownEdit.js";
2
2
  import { Definition, Root } from "./MarkdownNode.js";
3
+ import { preprocessLines } from "./internal/preprocess.js";
3
4
  import { normalizeLabelText } from "./internal/references.js";
5
+ import { scanFrontmatter } from "./internal/blocks/frontmatter.js";
4
6
  import { MarkdownDiagnostic } from "./MarkdownDiagnostic.js";
5
7
  import { parsePassResult } from "./Markdown.js";
6
8
  import { Effect, Result, Schema } from "effect";
@@ -164,13 +166,38 @@ var MarkdownDocument = class MarkdownDocument extends Schema.Class("MarkdownDocu
164
166
  * only ever sit at the head of `root.children` (the capture fires at most
165
167
  * once, at offset 0), so the tree is the single source of truth and the
166
168
  * accessor can never disagree with it. `undefined` covers both a document
167
- * with no frontmatter block and one parsed with the capture toggle off.
169
+ * with no frontmatter block and one parsed with the capture toggle off —
170
+ * {@link MarkdownDocument.hasFrontmatterBlock} tells the two apart.
168
171
  */
169
172
  get frontmatter() {
170
173
  const head = this.root.children[0];
171
174
  return head !== void 0 && head.type === "frontmatter" ? head : void 0;
172
175
  }
173
176
  /**
177
+ * Whether the source opens with a well-formed frontmatter block —
178
+ * regardless of how the document was parsed.
179
+ *
180
+ * @remarks
181
+ * The structural signal that disambiguates the `frontmatter` accessor's
182
+ * `undefined`: capture is opt-in (`MarkdownParseOptions.frontmatter`
183
+ * defaults off), so a document whose source visibly starts with `---` can
184
+ * still carry no capture node — spec-correct CommonMark reads the fences as
185
+ * a thematic break plus content. `frontmatter === undefined` with
186
+ * `hasFrontmatterBlock === true` means exactly "parsed with capture off";
187
+ * with `false` it means the source genuinely has no block.
188
+ *
189
+ * Derived, not stored: the getter runs the **same** pre-scan the parser
190
+ * runs when capture is enabled (the closed fence grammar — `---` yaml,
191
+ * `+++` toml, `---json` json; an unclosed fence is not frontmatter), so it
192
+ * is `true` exactly when parsing this source with `frontmatter: true`
193
+ * would produce a capture node, and the two can never drift. Like the
194
+ * other navigation accessors it recomputes per access — bind it once when
195
+ * checking repeatedly.
196
+ */
197
+ get hasFrontmatterBlock() {
198
+ return scanFrontmatter(preprocessLines(this.source), this.source) !== null;
199
+ }
200
+ /**
174
201
  * Every heading in the document, in document order, wherever it sits —
175
202
  * including headings nested inside blockquotes and list items.
176
203
  *
package/MarkdownFormat.js CHANGED
@@ -5,6 +5,19 @@ import { Effect, Result, Schema } from "effect";
5
5
 
6
6
  //#region src/MarkdownFormat.ts
7
7
  /**
8
+ * The two ways CommonMark spells a code block: `fenced` (a backtick or tilde
9
+ * fence) and `indented` (four-space indentation).
10
+ *
11
+ * @remarks
12
+ * A formatting target for {@link MarkdownFormattingOptions}, not a node
13
+ * fidelity field: on a {@link Code} node the two spellings are told apart by
14
+ * the presence or absence of `fenceChar`/`fenceLength`, and a language-less
15
+ * node with neither serializes as an **indented** block by default.
16
+ *
17
+ * @public
18
+ */
19
+ const CodeBlockStyle = Schema.Literals(["fenced", "indented"]);
20
+ /**
8
21
  * Options controlling formatting: which concrete-syntax markers to normalize,
9
22
  * plus the parse knobs (`dialect`, `frontmatter`) the formatter parses the
10
23
  * source with (same defaults as `Markdown.parse`). Every marker option is
@@ -12,7 +25,25 @@ import { Effect, Result, Schema } from "effect";
12
25
  *
13
26
  * Day-one scope is marker normalization only — heading style, bullet
14
27
  * character, emphasis/strong marker, fence character, thematic-break
15
- * character. Content is never rewritten, rewrapped or reflowed.
28
+ * character, code-block style. Content is never rewritten, rewrapped or
29
+ * reflowed.
30
+ *
31
+ * @remarks
32
+ * `codeBlockStyle` is the opt-in that converts between the two code-block
33
+ * spellings for **language-less** blocks (a block with a `lang` has no
34
+ * indented spelling and is never touched). It exists because the default
35
+ * surprises: absent this option, a language-less code block keeps its
36
+ * spelling — and a language-less {@link Code} node with no explicit
37
+ * `fenceChar` **serializes as an indented block** through
38
+ * `Markdown.stringify`. `"fenced"` rewrites indented blocks to fences (the
39
+ * fence character follows `fenceChar` when also set, backtick otherwise);
40
+ * `"indented"` rewrites language-less fences to indented form where provably
41
+ * safe. Like every other option, hazardous conversions are skipped, never
42
+ * attempted: both directions apply to root-level, flush-left blocks only,
43
+ * and the `"indented"` direction additionally skips blocks whose indented
44
+ * spelling would lazily continue a paragraph, be absorbed into a preceding
45
+ * list or footnote definition, merge with an adjacent code block, or fail
46
+ * representability (empty, or blank first/last lines).
16
47
  *
17
48
  * @public
18
49
  */
@@ -23,7 +54,8 @@ var MarkdownFormattingOptions = class extends Schema.Class("MarkdownFormattingOp
23
54
  bulletChar: Schema.optionalKey(BulletChar),
24
55
  emphasisChar: Schema.optionalKey(EmphasisChar),
25
56
  fenceChar: Schema.optionalKey(FenceChar),
26
- thematicBreakChar: Schema.optionalKey(ThematicBreakChar)
57
+ thematicBreakChar: Schema.optionalKey(ThematicBreakChar),
58
+ codeBlockStyle: Schema.optionalKey(CodeBlockStyle)
27
59
  }) {};
28
60
  /**
29
61
  * Error codes `MarkdownFormat.modify` can fail with.
@@ -193,6 +225,51 @@ const formatFence = (source, emit, node, target) => {
193
225
  while (end - closeLength > start + openLength && source[end - closeLength - 1] === fidelity) closeLength++;
194
226
  if (closeLength >= 3) emit.push(node, end - closeLength, closeLength, target.repeat(newLength));
195
227
  };
228
+ /** A line that is empty or holds only spaces and tabs. */
229
+ const BLANK_LINE = /^[ \t]*$/;
230
+ /** Whether a sibling is a code block with no info string (either spelling). */
231
+ const isLanguagelessCode = (node) => node.type === "code" && node.lang === void 0 && node.meta === void 0;
232
+ /**
233
+ * Convert a language-less code block toward the requested style. Returns
234
+ * whether a whole-block conversion edit was emitted, so the caller can keep
235
+ * `formatFence` off a span this conversion already rewrote.
236
+ *
237
+ * Both directions are whole-block rewrites, so both are restricted to
238
+ * root-level, flush-left blocks — a container's continuation-line prefix
239
+ * (`> `, list indentation) is not reproducible by a single splice. The
240
+ * indented direction carries the re-parse hazards an indented block is
241
+ * subject to and skips each one: it cannot follow a non-blank line (lazy
242
+ * paragraph continuation), a list or footnote definition absorbs it as item
243
+ * continuation, an adjacent language-less code block would merge with it
244
+ * across the blank line, and an empty value or one with blank first/last
245
+ * lines (or interior whitespace-only lines, which blankness would erase) has
246
+ * no indented spelling at all.
247
+ */
248
+ const formatCodeBlockStyle = (source, emit, node, parent, siblings, index, target, fenceChar) => {
249
+ if (node.lang !== void 0 || node.meta !== void 0) return false;
250
+ if (parent === void 0 || parent.type !== "root") return false;
251
+ const { start, end } = spanOf(node);
252
+ if (lineStartOf(source, start) !== start) return false;
253
+ const value = node.value.endsWith("\n") ? node.value.slice(0, -1) : node.value;
254
+ if (target === "fenced") {
255
+ if (node.fenceChar !== void 0) return false;
256
+ const char = fenceChar ?? "`";
257
+ const fence = char.repeat(Math.max(3, longestRun(value, char) + 1));
258
+ emit.push(node, start, end - start, `${fence}\n${value}\n${fence}`);
259
+ return true;
260
+ }
261
+ if (node.fenceChar === void 0) return false;
262
+ const lines = value === "" ? [] : value.split("\n");
263
+ if (lines.length === 0 || BLANK_LINE.test(lines[0]) || BLANK_LINE.test(lines[lines.length - 1])) return false;
264
+ if (lines.some((line) => line !== "" && BLANK_LINE.test(line))) return false;
265
+ if (!previousLineBlank(source, start)) return false;
266
+ const previous = index > 0 ? siblings[index - 1] : void 0;
267
+ const next = index + 1 < siblings.length ? siblings[index + 1] : void 0;
268
+ if (previous !== void 0 && (previous.type === "list" || previous.type === "footnoteDefinition")) return false;
269
+ if (previous !== void 0 && isLanguagelessCode(previous) || next !== void 0 && isLanguagelessCode(next)) return false;
270
+ emit.push(node, start, end - start, lines.map((line) => line === "" ? "" : ` ${line}`).join("\n"));
271
+ return true;
272
+ };
196
273
  const FLOW_TYPES = /* @__PURE__ */ new Set([
197
274
  "blockquote",
198
275
  "code",
@@ -279,12 +356,18 @@ var MarkdownFormat = class MarkdownFormat {
279
356
  /**
280
357
  * Compute marker-normalization edits per the requested
281
358
  * {@link MarkdownFormattingOptions}: heading style, bullet character,
282
- * emphasis/strong marker, fence character and thematic-break character,
283
- * each only where the node's concrete syntax differs from the target and
284
- * the rewrite is provably safe. Content is never touched. `range`
285
- * restricts edits to the nodes intersecting it (the owning-node
286
- * intersection posture, matching toml). Non-mutating — apply with
287
- * `MarkdownEdit.applyAll` (or use {@link MarkdownFormat.formatToString}).
359
+ * emphasis/strong marker, fence character, thematic-break character and
360
+ * code-block style, each only where the node's concrete syntax differs
361
+ * from the target and the rewrite is provably safe. Content is never
362
+ * touched. `range` restricts edits to the nodes intersecting it (the
363
+ * owning-node intersection posture, matching toml). Non-mutating — apply
364
+ * with `MarkdownEdit.applyAll` (or use
365
+ * {@link MarkdownFormat.formatToString}).
366
+ *
367
+ * Note the code-block default: absent `codeBlockStyle`, a language-less
368
+ * code block keeps whichever spelling it has — `fenceChar` alone
369
+ * normalizes existing fences and deliberately leaves indented blocks
370
+ * indented.
288
371
  */
289
372
  static format(text, range, options) {
290
373
  const parsed = Markdown.parseResult(text, MarkdownParseOptions.make({
@@ -293,12 +376,14 @@ var MarkdownFormat = class MarkdownFormat {
293
376
  }));
294
377
  if (Result.isFailure(parsed)) return [];
295
378
  const emit = new FormatEmitter(text);
296
- walk(parsed.success, void 0, [], 0, (node, _parent, siblings, index) => {
379
+ walk(parsed.success, void 0, [], 0, (node, parent, siblings, index) => {
297
380
  if (options?.thematicBreakChar !== void 0 && node.type === "thematicBreak") formatThematicBreak(text, emit, node, options.thematicBreakChar);
298
381
  if (options?.headingStyle !== void 0 && node.type === "heading") formatHeading(text, emit, node, options.headingStyle);
299
382
  if (options?.bulletChar !== void 0 && node.type === "list") formatList(text, emit, node, siblings, index, options.bulletChar);
300
383
  if (options?.emphasisChar !== void 0 && isEmphasisLike(node)) formatEmphasis(text, emit, node, siblings, index, options.emphasisChar);
301
- if (options?.fenceChar !== void 0 && node.type === "code") formatFence(text, emit, node, options.fenceChar);
384
+ if (node.type === "code") {
385
+ if (!(options?.codeBlockStyle !== void 0 && formatCodeBlockStyle(text, emit, node, parent, siblings, index, options.codeBlockStyle, options.fenceChar)) && options?.fenceChar !== void 0) formatFence(text, emit, node, options.fenceChar);
386
+ }
302
387
  });
303
388
  return (range === void 0 ? emit.edits : emit.edits.filter((edit) => Math.max(edit.nodeStart, range.offset) <= Math.min(edit.nodeEnd, range.offset + range.length))).map((edit) => MarkdownEdit.make({
304
389
  offset: edit.offset,
@@ -377,4 +462,4 @@ var MarkdownFormat = class MarkdownFormat {
377
462
  };
378
463
 
379
464
  //#endregion
380
- export { MarkdownFormat, MarkdownFormattingOptions, MarkdownModificationError, MarkdownModificationErrorCode };
465
+ export { CodeBlockStyle, MarkdownFormat, MarkdownFormattingOptions, MarkdownModificationError, MarkdownModificationErrorCode };
package/MarkdownNode.js CHANGED
@@ -347,6 +347,12 @@ var ThematicBreak = class extends Schema.Class("ThematicBreak")({
347
347
  * for fenced blocks and **absent for indented blocks** — their absence is how
348
348
  * the two are told apart on the way back out.
349
349
  *
350
+ * A consequence worth knowing before it surprises: a language-less `Code`
351
+ * node with no explicit `fenceChar` **serializes as an indented block**
352
+ * through `Markdown.stringify`. To get a fence, set `fenceChar` on the node
353
+ * (or a `lang`, which forces one), or format the emitted source with
354
+ * `MarkdownFormattingOptions.codeBlockStyle: "fenced"`.
355
+ *
350
356
  * @public
351
357
  */
352
358
  var Code = class extends Schema.Class("Code")({
package/README.md CHANGED
@@ -106,7 +106,7 @@ Effect.runPromise(program).then(console.log);
106
106
 
107
107
  `find` walks the tree in document pre-order and returns the document's own node, so the match feeds `modify` by identity. Replacements are literal strings or node fragments, and both render through the canonical stringifier, so a modified document re-parses cleanly by construction — you cannot splice in raw markdown that reopens a fence or breaks a table.
108
108
 
109
- `MarkdownFormat.format` handles the other half: conservative marker normalization, never content rewriting. It converts heading style, bullet character, emphasis marker, fence character and thematic-break character, and skips any conversion that would not be safe rather than attempting it cleverly:
109
+ `MarkdownFormat.format` handles the other half: conservative marker normalization, never content rewriting. It converts heading style, bullet character, emphasis marker, fence character, thematic-break character and code-block style, and skips any conversion that would not be safe rather than attempting it cleverly:
110
110
 
111
111
  ```ts
112
112
  import { MarkdownFormat, MarkdownFormattingOptions } from "@effected/markdown";
@@ -136,6 +136,8 @@ console.log(MarkdownFormat.format(source, undefined, options));
136
136
 
137
137
  `format` is pure and total, and the edits are non-mutating data — hand them to `MarkdownEdit.applyAll`, or send them to an editor as a text-edit payload.
138
138
 
139
+ `codeBlockStyle` is the option to know about before you hit its default: absent it, a language-less code block keeps whichever spelling it already has, and a language-less `Code` node with no `fenceChar` serializes as an *indented* block. `"fenced"` rewrites indented blocks to fences, `"indented"` goes the other way where the result provably re-parses the same, and both directions apply to root-level flush-left blocks only. A block with a language has no indented spelling and is never touched.
140
+
139
141
  ## Frontmatter
140
142
 
141
143
  Frontmatter capture is opt-in, because enabling it changes how a document opening with `---` parses: CommonMark reads `---\ntitle: x\n---` as a thematic break and a setext heading, and that spec-conformant reading holds unless you ask for something else. Turn it on and compose your schema with a codec for typed gray-matter parity:
@@ -166,6 +168,8 @@ Effect.runPromise(program).then(console.log);
166
168
 
167
169
  Each stage fails typed and separately: no capture is a `FrontmatterMissingError` (catch the tag for optional semantics), a codec handed the wrong fence is a `FrontmatterFormatMismatchError`, unparseable content is a `FrontmatterDecodeError` carrying the format package's own positioned failure structurally, and schema-invalid data is a `FrontmatterValidationError` carrying the structured issue tree rather than a stringified rendering.
168
170
 
171
+ Because capture is opt-in, "there is no frontmatter" has two meanings, and `FrontmatterMissingError.reason` tells them apart: `"absent"` when the source genuinely has no block, `"captureDisabled"` when it opens with one but was parsed with the toggle off — where the fix is a parse option, not a document edit. `MarkdownDocument.hasFrontmatterBlock` answers the same question directly, by re-running the parser's own pre-scan, so the accessor and the error can never disagree.
172
+
169
173
  The three codecs — `YamlFrontmatter` (`---`), `TomlFrontmatter` (`+++`) and `JsonFrontmatter` (`---json`) — are free-standing named exports, one module each, deliberately never collected into a namespace object. Naming one codec is what pulls in its format engine, so a JSON-frontmatter consumer never pays for the yaml parser.
170
174
 
171
175
  Frontmatter blocks can also describe their own schema. `SchemaResolver.classify` sorts a `$schema` value into a tagged union — `ByUrl`, `ByPath`, `Inline` and `ByName` — and `SchemaResolver.fromRegistry` resolves `ByName` declarations like `skill@2.1.0` against schemas you register. URLs, paths and inline documents are carried as data and never fetched: this is a pure package and it performs no IO.
@@ -192,7 +196,33 @@ Effect.runPromise(program).then(console.log);
192
196
  // [ "./CHANGELOG.md", "https://example.com/docs" ]
193
197
  ```
194
198
 
195
- `headings` lists every heading wherever it sits, including inside blockquotes and list items. `sections` are delimited by root-level headings only, and each section's range spans its subsections, so the edit layer can splice a whole section out in one edit.
199
+ `headings` lists every heading wherever it sits, including inside blockquotes and list items. `sections` are delimited by root-level headings only, and each section's range spans its subsections, so the edit layer can splice a whole section out in one edit. `firstSection` and `sectionByHeading` find one `DocumentSection` without scanning `sections` yourself — the second matches on an exact trimmed heading string, a `RegExp`, or a predicate, and both accept `{ depth }` to restrict to one heading level:
200
+
201
+ ```ts
202
+ import { MarkdownDocument } from "@effected/markdown";
203
+ import { Effect } from "effect";
204
+
205
+ const source = `# Changelog
206
+
207
+ ## 1.2.3
208
+
209
+ Fixed a bug.
210
+
211
+ ## 1.2.2
212
+
213
+ Initial release.
214
+ `;
215
+
216
+ const program = Effect.gen(function* () {
217
+ const doc = yield* MarkdownDocument.parse(source);
218
+ return doc.sectionByHeading("1.2.3", { depth: 2 })?.body.trim();
219
+ });
220
+
221
+ Effect.runPromise(program).then(console.log);
222
+ // Fixed a bug.
223
+ ```
224
+
225
+ `DocumentSection.body` is the section without its heading, and it is deliberately untrimmed — exactly the bytes `bodyRange` describes — so callers write `.body.trim()` when they want the tidy form.
196
226
 
197
227
  For anything the accessors do not cover, `find` and `findAll` walk the whole tree in document pre-order. A string selector matches the node's `type` and narrows the result — `doc.findAll("heading")` is `ReadonlyArray<Heading>`, `doc.find("table")` is `Table | undefined` — and a type-guard predicate narrows the same way. The nodes come back by identity, so `doc.findAll("heading")[1]` addresses the second heading for `MarkdownFormat.modify` without raw child indexing.
198
228
 
@@ -234,13 +264,13 @@ if (Result.isSuccess(parsed)) {
234
264
  ## Features
235
265
 
236
266
  - `Markdown` — `parse`/`stringify` as `Effect`s with typed `MarkdownParseError`/`MarkdownStringifyError` channels, the pure `parseResult`/`stringifyResult` twins for synchronous callers, and the `MarkdownFromString` two-way codec.
237
- - `MarkdownDocument` — source, tree, diagnostics and the link-definition index, plus the derived `headings`, `sections` and `links` accessors, the `find`/`findAll` tree queries over type-narrowed selectors, and the `frontmatter` capture.
267
+ - `MarkdownDocument` — source, tree, diagnostics and the link-definition index, plus the derived `headings`, `sections` and `links` accessors, the `firstSection`/`sectionByHeading` finders over a `DocumentSection` (heading, depth, range, body), the `find`/`findAll` tree queries over type-narrowed selectors, and the `frontmatter` capture with `hasFrontmatterBlock` to tell an absent block from an uncaptured one.
238
268
  - The mdast-shaped node classes — the CommonMark types plus GFM's `delete`, `table`, `tableRow`, `tableCell`, `footnoteDefinition`, `footnoteReference` and task-list `checked`, each carrying unist positions with byte offsets and this package's fidelity fields. `position` defaults to the zero-width synthetic sentinel, so a replacement fragment constructs in one line — `Text.make({ value: "shipped" })`.
239
269
  - `MarkdownEdit` / `MarkdownRange` (with `applyAll`) — the non-mutating text-edit vocabulary, field-identical to `@effected/jsonc`'s, `@effected/yaml`'s and `@effected/toml`'s.
240
270
  - `MarkdownFormat` — `format`/`formatToString` compute conservative marker-normalization edits; `modify`/`modifyToString` replace a node by identity through the canonical stringifier.
241
271
  - `MarkdownVisitor` — walk a parsed tree as a lazy `Stream` of `Enter`/`Exit` events with child-index paths and depth.
242
272
  - `Mdast` — `toMdast` projects to plain mdast JSON; `fromMdast`/`fromMdastResult` admit a foreign mdast tree with validation.
243
- - `MarkdownFrontmatter.schema` plus the `YamlFrontmatter`, `TomlFrontmatter` and `JsonFrontmatter` codecs — typed frontmatter decoding over optional per-format peers, with four separately catchable failure modes.
273
+ - `MarkdownFrontmatter.schema` plus the `YamlFrontmatter`, `TomlFrontmatter` and `JsonFrontmatter` codecs — typed frontmatter decoding over optional per-format peers, with four separately catchable failure modes and a `FrontmatterMissingReason` on the absent one.
244
274
  - `SchemaResolver` — classify a frontmatter `$schema` declaration into `ByUrl`/`ByPath`/`Inline`/`ByName` and resolve names against a registry, with no IO and no dependencies.
245
275
  - `MarkdownDiagnostic` — the structured diagnostic (`code`, `message`, `offset`, `length`, `line`, `character`) every typed error carries, shaped identically to the sibling packages'.
246
276
 
package/index.d.ts CHANGED
@@ -388,6 +388,12 @@ declare const Code_base: Schema.Class<Code, Schema.Struct<{
388
388
  * for fenced blocks and **absent for indented blocks** — their absence is how
389
389
  * the two are told apart on the way back out.
390
390
  *
391
+ * A consequence worth knowing before it surprises: a language-less `Code`
392
+ * node with no explicit `fenceChar` **serializes as an indented block**
393
+ * through `Markdown.stringify`. To get a fence, set `fenceChar` on the node
394
+ * (or a `lang`, which forces one), or format the emitted source with
395
+ * `MarkdownFormattingOptions.codeBlockStyle: "fenced"`.
396
+ *
391
397
  * @public
392
398
  */
393
399
  declare class Code extends Code_base {}
@@ -808,7 +814,9 @@ declare const MarkdownParseOptions_base: Schema.Class<MarkdownParseOptions, Sche
808
814
  * spec-conformant reading must hold unless a consumer opts in. Enabled, the
809
815
  * engine captures `---`/`+++`/`---json` blocks at offset 0 into a raw
810
816
  * `Frontmatter` head node — decoding it is the frontmatter codec modules'
811
- * job.
817
+ * job. Parsed with the toggle off, such a document reports no frontmatter —
818
+ * `MarkdownDocument.hasFrontmatterBlock` tells "no block in the source"
819
+ * apart from "parsed with capture off".
812
820
  *
813
821
  * @public
814
822
  */
@@ -917,10 +925,14 @@ declare class Markdown {
917
925
  * @remarks
918
926
  * Canonical serialization: fidelity fields (marker characters, fence
919
927
  * style, heading spelling) win when present; documented canonical
920
- * defaults apply when absent. The output re-parses to a render-equivalent
921
- * document — the corpus-pinned contract — but is not a byte-level
922
- * round-trip of any original source; surgical editing goes through the
923
- * offset-splice edit layer instead.
928
+ * defaults apply when absent. One default surprises: a language-less
929
+ * `Code` node with no explicit `fenceChar` serializes as an
930
+ * **indented** code block — set `fenceChar` (or a `lang`) on the node for
931
+ * a fence, or normalize afterward with
932
+ * `MarkdownFormattingOptions.codeBlockStyle: "fenced"`. The output
933
+ * re-parses to a render-equivalent document — the corpus-pinned
934
+ * contract — but is not a byte-level round-trip of any original source;
935
+ * surgical editing goes through the offset-splice edit layer instead.
924
936
  *
925
937
  * Failure is rare by design, symmetric with parse: only a hardening-guard
926
938
  * trip on a tree nesting past the depth cap fails, and only synthesized
@@ -1202,9 +1214,32 @@ declare class MarkdownDocument extends MarkdownDocument_base {
1202
1214
  * only ever sit at the head of `root.children` (the capture fires at most
1203
1215
  * once, at offset 0), so the tree is the single source of truth and the
1204
1216
  * accessor can never disagree with it. `undefined` covers both a document
1205
- * with no frontmatter block and one parsed with the capture toggle off.
1217
+ * with no frontmatter block and one parsed with the capture toggle off —
1218
+ * {@link MarkdownDocument.hasFrontmatterBlock} tells the two apart.
1206
1219
  */
1207
1220
  get frontmatter(): Frontmatter | undefined;
1221
+ /**
1222
+ * Whether the source opens with a well-formed frontmatter block —
1223
+ * regardless of how the document was parsed.
1224
+ *
1225
+ * @remarks
1226
+ * The structural signal that disambiguates the `frontmatter` accessor's
1227
+ * `undefined`: capture is opt-in (`MarkdownParseOptions.frontmatter`
1228
+ * defaults off), so a document whose source visibly starts with `---` can
1229
+ * still carry no capture node — spec-correct CommonMark reads the fences as
1230
+ * a thematic break plus content. `frontmatter === undefined` with
1231
+ * `hasFrontmatterBlock === true` means exactly "parsed with capture off";
1232
+ * with `false` it means the source genuinely has no block.
1233
+ *
1234
+ * Derived, not stored: the getter runs the **same** pre-scan the parser
1235
+ * runs when capture is enabled (the closed fence grammar — `---` yaml,
1236
+ * `+++` toml, `---json` json; an unclosed fence is not frontmatter), so it
1237
+ * is `true` exactly when parsing this source with `frontmatter: true`
1238
+ * would produce a capture node, and the two can never drift. Like the
1239
+ * other navigation accessors it recomputes per access — bind it once when
1240
+ * checking repeatedly.
1241
+ */
1242
+ get hasFrontmatterBlock(): boolean;
1208
1243
  /**
1209
1244
  * Every heading in the document, in document order, wherever it sits —
1210
1245
  * including headings nested inside blockquotes and list items.
@@ -1441,18 +1476,46 @@ interface FrontmatterCodec {
1441
1476
  /** Serialize data into a frontmatter body, without fences. */
1442
1477
  readonly encode: (data: unknown) => Effect.Effect<string, FrontmatterEncodeError>;
1443
1478
  }
1444
- declare const FrontmatterMissingError_base: Schema.Class<FrontmatterMissingError, Schema.TaggedStruct<"FrontmatterMissingError", {}>, import("effect/Cause").YieldableError>;
1479
+ /**
1480
+ * Why a frontmatter decoder found no capture on a document.
1481
+ *
1482
+ * @remarks
1483
+ * `"absent"` — the source genuinely has no frontmatter block.
1484
+ * `"captureDisabled"` — the source opens with a well-formed frontmatter
1485
+ * block, but the document carries no capture node: it was parsed without
1486
+ * `frontmatter: true` (the toggle defaults off), so spec-correct CommonMark
1487
+ * read the fences as ordinary content.
1488
+ *
1489
+ * @public
1490
+ */
1491
+ declare const FrontmatterMissingReason: Schema.Literals<readonly ["absent", "captureDisabled"]>;
1492
+ /**
1493
+ * Why a frontmatter decoder found no capture on a document.
1494
+ *
1495
+ * @public
1496
+ */
1497
+ type FrontmatterMissingReason = typeof FrontmatterMissingReason.Type;
1498
+ declare const FrontmatterMissingError_base: Schema.Class<FrontmatterMissingError, Schema.TaggedStruct<"FrontmatterMissingError", {
1499
+ /** Why there is no capture: no block at all, or capture left off. */
1500
+ readonly reason: Schema.Literals<readonly ["absent", "captureDisabled"]>;
1501
+ }>, import("effect/Cause").YieldableError>;
1445
1502
  /**
1446
1503
  * Indicates that a document handed to a frontmatter decoder carries no
1447
1504
  * frontmatter capture.
1448
1505
  *
1449
1506
  * @remarks
1450
- * Raised when the document genuinely has no frontmatter block — including
1451
- * when it has one in the source but was parsed with the capture toggle off
1452
- * (`MarkdownParseOptions.frontmatter` defaults to `false`). Cause-free: there
1453
- * is nothing to diagnose beyond the absence itself. Consumers wanting
1454
- * optional semantics can `Effect.catchTag("FrontmatterMissingError", ...)`
1455
- * to a default.
1507
+ * `reason` distinguishes the two ways this happens: `"absent"` when the
1508
+ * source genuinely has no frontmatter block, `"captureDisabled"` when the
1509
+ * source opens with one but the document was parsed with the capture toggle
1510
+ * off (`MarkdownParseOptions.frontmatter` defaults to `false`) — the fix
1511
+ * there is parsing with `frontmatter: true`, not editing the document. The
1512
+ * discriminant is computed from the same structural signal
1513
+ * `MarkdownDocument.hasFrontmatterBlock` exposes, so the error and the
1514
+ * accessor can never disagree.
1515
+ *
1516
+ * Cause-free: there is nothing to diagnose beyond the absence itself.
1517
+ * Consumers wanting optional semantics can
1518
+ * `Effect.catchTag("FrontmatterMissingError", ...)` to a default.
1456
1519
  *
1457
1520
  * @public
1458
1521
  */
@@ -1516,7 +1579,8 @@ declare class MarkdownFrontmatter {
1516
1579
  * The decoder reads the document's frontmatter capture (parse with
1517
1580
  * `frontmatter: true` — the toggle defaults off), decodes its raw value
1518
1581
  * through the codec, then validates the data against `schema`. Each stage
1519
- * fails typed: no capture is {@link FrontmatterMissingError}, a
1582
+ * fails typed: no capture is {@link FrontmatterMissingError} (its `reason`
1583
+ * says whether the block is genuinely absent or capture was left off), a
1520
1584
  * wrong-format codec is {@link FrontmatterFormatMismatchError}, unparseable
1521
1585
  * content is {@link FrontmatterDecodeError}, and schema-invalid data is
1522
1586
  * {@link FrontmatterValidationError} carrying the structured issue.
@@ -1866,6 +1930,25 @@ type MarkdownRangeLike = MarkdownRange | {
1866
1930
  readonly offset: number;
1867
1931
  readonly length: number;
1868
1932
  };
1933
+ /**
1934
+ * The two ways CommonMark spells a code block: `fenced` (a backtick or tilde
1935
+ * fence) and `indented` (four-space indentation).
1936
+ *
1937
+ * @remarks
1938
+ * A formatting target for {@link MarkdownFormattingOptions}, not a node
1939
+ * fidelity field: on a {@link Code} node the two spellings are told apart by
1940
+ * the presence or absence of `fenceChar`/`fenceLength`, and a language-less
1941
+ * node with neither serializes as an **indented** block by default.
1942
+ *
1943
+ * @public
1944
+ */
1945
+ declare const CodeBlockStyle: Schema.Literals<readonly ["fenced", "indented"]>;
1946
+ /**
1947
+ * The union of all code-block-style string literals.
1948
+ *
1949
+ * @public
1950
+ */
1951
+ type CodeBlockStyle = typeof CodeBlockStyle.Type;
1869
1952
  declare const MarkdownFormattingOptions_base: Schema.Class<MarkdownFormattingOptions, Schema.Struct<{
1870
1953
  readonly dialect: Schema.optionalKey<Schema.Literals<readonly ["commonmark", "gfm"]>>;
1871
1954
  readonly frontmatter: Schema.optionalKey<Schema.Boolean>;
@@ -1874,6 +1957,7 @@ declare const MarkdownFormattingOptions_base: Schema.Class<MarkdownFormattingOpt
1874
1957
  readonly emphasisChar: Schema.optionalKey<Schema.Literals<readonly ["*", "_"]>>;
1875
1958
  readonly fenceChar: Schema.optionalKey<Schema.Literals<readonly ["`", "~"]>>;
1876
1959
  readonly thematicBreakChar: Schema.optionalKey<Schema.Literals<readonly ["-", "_", "*"]>>;
1960
+ readonly codeBlockStyle: Schema.optionalKey<Schema.Literals<readonly ["fenced", "indented"]>>;
1877
1961
  }>, {}>;
1878
1962
  /**
1879
1963
  * Options controlling formatting: which concrete-syntax markers to normalize,
@@ -1883,7 +1967,25 @@ declare const MarkdownFormattingOptions_base: Schema.Class<MarkdownFormattingOpt
1883
1967
  *
1884
1968
  * Day-one scope is marker normalization only — heading style, bullet
1885
1969
  * character, emphasis/strong marker, fence character, thematic-break
1886
- * character. Content is never rewritten, rewrapped or reflowed.
1970
+ * character, code-block style. Content is never rewritten, rewrapped or
1971
+ * reflowed.
1972
+ *
1973
+ * @remarks
1974
+ * `codeBlockStyle` is the opt-in that converts between the two code-block
1975
+ * spellings for **language-less** blocks (a block with a `lang` has no
1976
+ * indented spelling and is never touched). It exists because the default
1977
+ * surprises: absent this option, a language-less code block keeps its
1978
+ * spelling — and a language-less {@link Code} node with no explicit
1979
+ * `fenceChar` **serializes as an indented block** through
1980
+ * `Markdown.stringify`. `"fenced"` rewrites indented blocks to fences (the
1981
+ * fence character follows `fenceChar` when also set, backtick otherwise);
1982
+ * `"indented"` rewrites language-less fences to indented form where provably
1983
+ * safe. Like every other option, hazardous conversions are skipped, never
1984
+ * attempted: both directions apply to root-level, flush-left blocks only,
1985
+ * and the `"indented"` direction additionally skips blocks whose indented
1986
+ * spelling would lazily continue a paragraph, be absorbed into a preceding
1987
+ * list or footnote definition, merge with an adjacent code block, or fail
1988
+ * representability (empty, or blank first/last lines).
1887
1989
  *
1888
1990
  * @public
1889
1991
  */
@@ -1941,12 +2043,18 @@ declare class MarkdownFormat {
1941
2043
  /**
1942
2044
  * Compute marker-normalization edits per the requested
1943
2045
  * {@link MarkdownFormattingOptions}: heading style, bullet character,
1944
- * emphasis/strong marker, fence character and thematic-break character,
1945
- * each only where the node's concrete syntax differs from the target and
1946
- * the rewrite is provably safe. Content is never touched. `range`
1947
- * restricts edits to the nodes intersecting it (the owning-node
1948
- * intersection posture, matching toml). Non-mutating — apply with
1949
- * `MarkdownEdit.applyAll` (or use {@link MarkdownFormat.formatToString}).
2046
+ * emphasis/strong marker, fence character, thematic-break character and
2047
+ * code-block style, each only where the node's concrete syntax differs
2048
+ * from the target and the rewrite is provably safe. Content is never
2049
+ * touched. `range` restricts edits to the nodes intersecting it (the
2050
+ * owning-node intersection posture, matching toml). Non-mutating — apply
2051
+ * with `MarkdownEdit.applyAll` (or use
2052
+ * {@link MarkdownFormat.formatToString}).
2053
+ *
2054
+ * Note the code-block default: absent `codeBlockStyle`, a language-less
2055
+ * code block keeps whichever spelling it has — `fenceChar` alone
2056
+ * normalizes existing fences and deliberately leaves indented blocks
2057
+ * indented.
1950
2058
  */
1951
2059
  static format(text: string, range?: MarkdownRangeLike, options?: MarkdownFormattingOptions): ReadonlyArray<MarkdownEdit>;
1952
2060
  /**
@@ -2292,5 +2400,5 @@ declare const TomlFrontmatter: FrontmatterCodec;
2292
2400
  */
2293
2401
  declare const YamlFrontmatter: FrontmatterCodec;
2294
2402
  //#endregion
2295
- export { Blockquote, Break, BreakStyle, BulletChar, Code, Definition, Delete, type DocumentHeading, type DocumentLink, DocumentSection, Emphasis, EmphasisChar, FenceChar, FlowContent, FootnoteDefinition, FootnoteReference, Frontmatter, type FrontmatterCodec, FrontmatterContent, FrontmatterDecodeError, FrontmatterEncodeError, FrontmatterFormat, FrontmatterFormatMismatchError, FrontmatterMissingError, type FrontmatterResolveError, type FrontmatterSchemaError, type FrontmatterSchemaResolver, FrontmatterValidationError, type FrontmatterWriteError, Heading, HeadingDepth, HeadingStyle, Html, Image, ImageReference, InlineCode, JsonFrontmatter, Link, type LinkBearingNode, LinkReference, List, ListContent, ListDelimiter, ListItem, Markdown, MarkdownDiagnostic, MarkdownDialect, MarkdownDocument, MarkdownEdit, MarkdownFormat, MarkdownFormattingOptions, MarkdownFrontmatter, MarkdownModificationError, MarkdownModificationErrorCode, MarkdownNode, type MarkdownNodeOfType, type MarkdownNodeType, MarkdownParseError, MarkdownParseErrorCode, MarkdownParseOptions, type MarkdownPath, MarkdownRange, type MarkdownRangeLike, type MarkdownSegment, MarkdownStringifyError, MarkdownVisitor, MarkdownVisitorEvent, Mdast, MdastDecodeError, type MdastNode, Paragraph, PhrasingContent, Point, Position, ReferenceType, Root, RowContent, SchemaDeclaration, SchemaDeclarationByName, SchemaDeclarationByPath, SchemaDeclarationByUrl, SchemaDeclarationInline, SchemaDeclarationInvalidError, SchemaDeclarationMissingError, SchemaNameUnknownError, SchemaResolver, SchemaVersionUnresolvableError, type SectionHeadingMatch, type SectionQueryOptions, Strong, Table, TableAlign, TableCell, TableContent, TableRow, Text, ThematicBreak, ThematicBreakChar, TomlFrontmatter, YamlFrontmatter };
2403
+ export { Blockquote, Break, BreakStyle, BulletChar, Code, CodeBlockStyle, Definition, Delete, type DocumentHeading, type DocumentLink, DocumentSection, Emphasis, EmphasisChar, FenceChar, FlowContent, FootnoteDefinition, FootnoteReference, Frontmatter, type FrontmatterCodec, FrontmatterContent, FrontmatterDecodeError, FrontmatterEncodeError, FrontmatterFormat, FrontmatterFormatMismatchError, FrontmatterMissingError, FrontmatterMissingReason, type FrontmatterResolveError, type FrontmatterSchemaError, type FrontmatterSchemaResolver, FrontmatterValidationError, type FrontmatterWriteError, Heading, HeadingDepth, HeadingStyle, Html, Image, ImageReference, InlineCode, JsonFrontmatter, Link, type LinkBearingNode, LinkReference, List, ListContent, ListDelimiter, ListItem, Markdown, MarkdownDiagnostic, MarkdownDialect, MarkdownDocument, MarkdownEdit, MarkdownFormat, MarkdownFormattingOptions, MarkdownFrontmatter, MarkdownModificationError, MarkdownModificationErrorCode, MarkdownNode, type MarkdownNodeOfType, type MarkdownNodeType, MarkdownParseError, MarkdownParseErrorCode, MarkdownParseOptions, type MarkdownPath, MarkdownRange, type MarkdownRangeLike, type MarkdownSegment, MarkdownStringifyError, MarkdownVisitor, MarkdownVisitorEvent, Mdast, MdastDecodeError, type MdastNode, Paragraph, PhrasingContent, Point, Position, ReferenceType, Root, RowContent, SchemaDeclaration, SchemaDeclarationByName, SchemaDeclarationByPath, SchemaDeclarationByUrl, SchemaDeclarationInline, SchemaDeclarationInvalidError, SchemaDeclarationMissingError, SchemaNameUnknownError, SchemaResolver, SchemaVersionUnresolvableError, type SectionHeadingMatch, type SectionQueryOptions, Strong, Table, TableAlign, TableCell, TableContent, TableRow, Text, ThematicBreak, ThematicBreakChar, TomlFrontmatter, YamlFrontmatter };
2296
2404
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -1,15 +1,15 @@
1
1
  import { MarkdownEdit, MarkdownRange } from "./MarkdownEdit.js";
2
2
  import { Blockquote, Break, BreakStyle, BulletChar, Code, Definition, Delete, Emphasis, EmphasisChar, FenceChar, FlowContent, FootnoteDefinition, FootnoteReference, Frontmatter, FrontmatterContent, FrontmatterFormat, Heading, HeadingDepth, HeadingStyle, Html, Image, ImageReference, InlineCode, Link, LinkReference, List, ListContent, ListDelimiter, ListItem, MarkdownNode, Paragraph, PhrasingContent, Point, Position, ReferenceType, Root, RowContent, Strong, Table, TableAlign, TableCell, TableContent, TableRow, Text, ThematicBreak, ThematicBreakChar } from "./MarkdownNode.js";
3
- import { FrontmatterDecodeError, FrontmatterEncodeError, FrontmatterFormatMismatchError, FrontmatterMissingError, FrontmatterValidationError, MarkdownFrontmatter } from "./Frontmatter.js";
3
+ import { FrontmatterDecodeError, FrontmatterEncodeError, FrontmatterFormatMismatchError, FrontmatterMissingError, FrontmatterMissingReason, FrontmatterValidationError, MarkdownFrontmatter } from "./Frontmatter.js";
4
4
  import { SchemaDeclaration, SchemaDeclarationByName, SchemaDeclarationByPath, SchemaDeclarationByUrl, SchemaDeclarationInline, SchemaDeclarationInvalidError, SchemaDeclarationMissingError, SchemaNameUnknownError, SchemaResolver, SchemaVersionUnresolvableError } from "./FrontmatterResolver.js";
5
5
  import { JsonFrontmatter } from "./JsonFrontmatter.js";
6
6
  import { MarkdownDiagnostic, MarkdownParseErrorCode } from "./MarkdownDiagnostic.js";
7
7
  import { Markdown, MarkdownDialect, MarkdownParseError, MarkdownParseOptions, MarkdownStringifyError } from "./Markdown.js";
8
8
  import { DocumentSection, MarkdownDocument } from "./MarkdownDocument.js";
9
- import { MarkdownFormat, MarkdownFormattingOptions, MarkdownModificationError, MarkdownModificationErrorCode } from "./MarkdownFormat.js";
9
+ import { CodeBlockStyle, MarkdownFormat, MarkdownFormattingOptions, MarkdownModificationError, MarkdownModificationErrorCode } from "./MarkdownFormat.js";
10
10
  import { MarkdownVisitor, MarkdownVisitorEvent } from "./MarkdownVisitor.js";
11
11
  import { Mdast, MdastDecodeError } from "./Mdast.js";
12
12
  import { TomlFrontmatter } from "./TomlFrontmatter.js";
13
13
  import { YamlFrontmatter } from "./YamlFrontmatter.js";
14
14
 
15
- export { Blockquote, Break, BreakStyle, BulletChar, Code, Definition, Delete, DocumentSection, Emphasis, EmphasisChar, FenceChar, FlowContent, FootnoteDefinition, FootnoteReference, Frontmatter, FrontmatterContent, FrontmatterDecodeError, FrontmatterEncodeError, FrontmatterFormat, FrontmatterFormatMismatchError, FrontmatterMissingError, FrontmatterValidationError, Heading, HeadingDepth, HeadingStyle, Html, Image, ImageReference, InlineCode, JsonFrontmatter, Link, LinkReference, List, ListContent, ListDelimiter, ListItem, Markdown, MarkdownDiagnostic, MarkdownDialect, MarkdownDocument, MarkdownEdit, MarkdownFormat, MarkdownFormattingOptions, MarkdownFrontmatter, MarkdownModificationError, MarkdownModificationErrorCode, MarkdownNode, MarkdownParseError, MarkdownParseErrorCode, MarkdownParseOptions, MarkdownRange, MarkdownStringifyError, MarkdownVisitor, MarkdownVisitorEvent, Mdast, MdastDecodeError, Paragraph, PhrasingContent, Point, Position, ReferenceType, Root, RowContent, SchemaDeclaration, SchemaDeclarationByName, SchemaDeclarationByPath, SchemaDeclarationByUrl, SchemaDeclarationInline, SchemaDeclarationInvalidError, SchemaDeclarationMissingError, SchemaNameUnknownError, SchemaResolver, SchemaVersionUnresolvableError, Strong, Table, TableAlign, TableCell, TableContent, TableRow, Text, ThematicBreak, ThematicBreakChar, TomlFrontmatter, YamlFrontmatter };
15
+ export { Blockquote, Break, BreakStyle, BulletChar, Code, CodeBlockStyle, Definition, Delete, DocumentSection, Emphasis, EmphasisChar, FenceChar, FlowContent, FootnoteDefinition, FootnoteReference, Frontmatter, FrontmatterContent, FrontmatterDecodeError, FrontmatterEncodeError, FrontmatterFormat, FrontmatterFormatMismatchError, FrontmatterMissingError, FrontmatterMissingReason, FrontmatterValidationError, Heading, HeadingDepth, HeadingStyle, Html, Image, ImageReference, InlineCode, JsonFrontmatter, Link, LinkReference, List, ListContent, ListDelimiter, ListItem, Markdown, MarkdownDiagnostic, MarkdownDialect, MarkdownDocument, MarkdownEdit, MarkdownFormat, MarkdownFormattingOptions, MarkdownFrontmatter, MarkdownModificationError, MarkdownModificationErrorCode, MarkdownNode, MarkdownParseError, MarkdownParseErrorCode, MarkdownParseOptions, MarkdownRange, MarkdownStringifyError, MarkdownVisitor, MarkdownVisitorEvent, Mdast, MdastDecodeError, Paragraph, PhrasingContent, Point, Position, ReferenceType, Root, RowContent, SchemaDeclaration, SchemaDeclarationByName, SchemaDeclarationByPath, SchemaDeclarationByUrl, SchemaDeclarationInline, SchemaDeclarationInvalidError, SchemaDeclarationMissingError, SchemaNameUnknownError, SchemaResolver, SchemaVersionUnresolvableError, Strong, Table, TableAlign, TableCell, TableContent, TableRow, Text, ThematicBreak, ThematicBreakChar, TomlFrontmatter, YamlFrontmatter };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effected/markdown",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "private": false,
5
5
  "description": "CommonMark and GFM markdown parse, edit and transform schemas for Effect",
6
6
  "keywords": [