@savvy-web/silk-effects 9.6.0 → 10.0.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 (51) hide show
  1. package/changesets/api/dependency-table.js +12 -12
  2. package/changesets/api/linter.js +23 -19
  3. package/changesets/api/transformer.js +38 -33
  4. package/changesets/categories/index.js +1 -1
  5. package/changesets/categories/types.js +2 -2
  6. package/changesets/changelog/getDependencyReleaseLine.js +1 -1
  7. package/changesets/changelog/index.js +2 -2
  8. package/changesets/constants.js +1 -1
  9. package/changesets/errors.js +1 -1
  10. package/changesets/index.js +11 -40
  11. package/changesets/markdownlint/rules/content-structure.js +2 -2
  12. package/changesets/markdownlint/rules/heading-hierarchy.js +2 -2
  13. package/changesets/markdownlint/rules/required-sections.js +2 -2
  14. package/changesets/markdownlint/rules/uncategorized-content.js +2 -2
  15. package/changesets/markdownlint/rules/utils.js +6 -6
  16. package/changesets/rules/content-structure.js +44 -0
  17. package/changesets/rules/dependency-table-format.js +54 -0
  18. package/changesets/rules/heading-hierarchy.js +37 -0
  19. package/changesets/rules/required-sections.js +34 -0
  20. package/changesets/rules/types.js +20 -0
  21. package/changesets/rules/uncategorized-content.js +30 -0
  22. package/changesets/schemas/changeset.js +1 -1
  23. package/changesets/transforms/aggregate-dependency-tables.js +96 -0
  24. package/changesets/transforms/contributor-footnotes.js +290 -0
  25. package/changesets/transforms/deduplicate-items.js +47 -0
  26. package/changesets/transforms/issue-link-refs.js +47 -0
  27. package/changesets/transforms/maintenance-note.js +65 -0
  28. package/changesets/transforms/merge-sections.js +56 -0
  29. package/changesets/transforms/normalize-format.js +53 -0
  30. package/changesets/transforms/reorder-sections.js +53 -0
  31. package/changesets/utils/blocks.js +67 -0
  32. package/changesets/utils/dependency-table.js +29 -36
  33. package/changesets/utils/markdown-emit.js +11 -36
  34. package/index.d.ts +54 -1199
  35. package/package.json +10 -17
  36. package/changesets/remark/plugins/aggregate-dependency-tables.js +0 -98
  37. package/changesets/remark/plugins/contributor-footnotes.js +0 -334
  38. package/changesets/remark/plugins/deduplicate-items.js +0 -31
  39. package/changesets/remark/plugins/issue-link-refs.js +0 -57
  40. package/changesets/remark/plugins/maintenance-note.js +0 -74
  41. package/changesets/remark/plugins/merge-sections.js +0 -42
  42. package/changesets/remark/plugins/normalize-format.js +0 -46
  43. package/changesets/remark/plugins/reorder-sections.js +0 -48
  44. package/changesets/remark/presets.js +0 -118
  45. package/changesets/remark/rules/content-structure.js +0 -21
  46. package/changesets/remark/rules/dependency-table-format.js +0 -37
  47. package/changesets/remark/rules/heading-hierarchy.js +0 -18
  48. package/changesets/remark/rules/required-sections.js +0 -16
  49. package/changesets/remark/rules/uncategorized-content.js +0 -30
  50. package/changesets/utils/remark-pipeline.js +0 -35
  51. package/changesets/utils/version-blocks.js +0 -107
@@ -0,0 +1,65 @@
1
+ import { getHeadingText, getVersionBlocks, isHeadingOfDepth } from "../utils/blocks.js";
2
+ import { Heading, InlineCode, List, ListItem, Mdast, Paragraph, Root, Text } from "@effected/markdown";
3
+
4
+ // src/changesets/transforms/maintenance-note.ts
5
+ /**
6
+ * Placeholder sentence `@changesets/apply-release-plan` (>= 8.1.0) emits as the
7
+ * body of a version block that would otherwise render as a bare heading.
8
+ */
9
+ const ENGINE_PLACEHOLDER = "No changes in this release.";
10
+ /** True when the block's only content is the engine's empty-release placeholder. */
11
+ function isPlaceholderOnly(children, block) {
12
+ if (block.endIndex - block.startIndex !== 1) return false;
13
+ const node = children[block.startIndex];
14
+ return node.type === "paragraph" && Mdast.toText(node).trim() === ENGINE_PLACEHOLDER;
15
+ }
16
+ function buildNoteChildren(reason) {
17
+ if (reason.kind === "unspecified" || reason.triggers.length === 0) return [new Text({ value: "Version-only release to keep workspace versions consistent; no changes to this package." })];
18
+ const label = reason.kind === "fixed" ? "fixed version group" : "linked version group";
19
+ const nodes = [new Text({ value: "Released in lockstep with " })];
20
+ reason.triggers.forEach((trigger, index) => {
21
+ if (index > 0) nodes.push(new Text({ value: ", " }));
22
+ nodes.push(new InlineCode({ value: `${trigger.name}@${trigger.version}` }));
23
+ });
24
+ nodes.push(new Text({ value: ` (${label}).` }));
25
+ return nodes;
26
+ }
27
+ /**
28
+ * Build a transform that adds a Maintenance note to the targeted empty version block.
29
+ *
30
+ * @param options - The target version and the release reason
31
+ * @returns A transform; a no-op when the block is absent or non-empty
32
+ *
33
+ * @internal
34
+ */
35
+ function maintenanceNote(options) {
36
+ return (root) => {
37
+ const children = [...root.children];
38
+ const block = getVersionBlocks(children).find((b) => {
39
+ const node = children[b.headingIndex];
40
+ return isHeadingOfDepth(node, [2]) && getHeadingText(node) === options.version;
41
+ });
42
+ if (!block) return root;
43
+ const placeholderOnly = isPlaceholderOnly(children, block);
44
+ if (block.endIndex > block.startIndex && !placeholderOnly) return root;
45
+ const heading = new Heading({
46
+ depth: 3,
47
+ children: [new Text({ value: "Maintenance" })]
48
+ });
49
+ const list = new List({
50
+ ordered: false,
51
+ spread: false,
52
+ children: [new ListItem({
53
+ spread: false,
54
+ children: [new Paragraph({ children: buildNoteChildren(options.reason) })]
55
+ })]
56
+ });
57
+ children.splice(block.startIndex, placeholderOnly ? 1 : 0, heading, list);
58
+ return new Root({
59
+ ...root,
60
+ children
61
+ });
62
+ };
63
+ }
64
+
65
+ export { maintenanceNote };
@@ -0,0 +1,56 @@
1
+ import { fromHeading } from "../categories/index.js";
2
+ import { getBlockSections, getHeadingText, getVersionBlocks } from "../utils/blocks.js";
3
+ import { Root } from "@effected/markdown";
4
+
5
+ // src/changesets/transforms/merge-sections.ts
6
+ /**
7
+ * Changelog transform: merge duplicate h3 section headings.
8
+ *
9
+ * @remarks
10
+ * Kit transform. Groups h3 sections within each version
11
+ * block by normalized heading text (via the category system's `fromHeading`)
12
+ * and moves the content of duplicates under the first occurrence, then drops
13
+ * the duplicate headings. Runs after dependency-table aggregation and before
14
+ * section reordering.
15
+ *
16
+ * @internal
17
+ */
18
+ /**
19
+ * Merge duplicate h3 sections within each version block.
20
+ *
21
+ * @param root - The changelog tree
22
+ * @returns A tree with a single section per heading in each version block
23
+ *
24
+ * @internal
25
+ */
26
+ const mergeSections = (root) => {
27
+ const children = [...root.children];
28
+ const blocks = getVersionBlocks(children);
29
+ for (let b = blocks.length - 1; b >= 0; b--) {
30
+ const sections = getBlockSections(children, blocks[b]);
31
+ if (sections.length <= 1) continue;
32
+ const groups = /* @__PURE__ */ new Map();
33
+ for (let s = 0; s < sections.length; s++) {
34
+ const text = getHeadingText(sections[s].heading);
35
+ const category = fromHeading(text);
36
+ const key = category ? category.heading.toLowerCase() : text.toLowerCase();
37
+ const existing = groups.get(key);
38
+ if (existing) existing.push(s);
39
+ else groups.set(key, [s]);
40
+ }
41
+ if (![...groups.values()].some((indices) => indices.length > 1)) continue;
42
+ const block = blocks[b];
43
+ const rebuilt = children.slice(block.startIndex, sections[0].headingIndex);
44
+ for (const indices of groups.values()) {
45
+ const [target, ...duplicates] = indices.map((s) => sections[s]);
46
+ rebuilt.push(target.heading, ...target.contentNodes, ...duplicates.flatMap((d) => d.contentNodes));
47
+ }
48
+ children.splice(block.startIndex, block.endIndex - block.startIndex, ...rebuilt);
49
+ }
50
+ return new Root({
51
+ ...root,
52
+ children
53
+ });
54
+ };
55
+
56
+ export { mergeSections };
@@ -0,0 +1,53 @@
1
+ import { getVersionBlocks, isHeadingOfDepth } from "../utils/blocks.js";
2
+ import { Mdast, Root } from "@effected/markdown";
3
+
4
+ // src/changesets/transforms/normalize-format.ts
5
+ /**
6
+ * Changelog transform: remove empty sections and empty lists.
7
+ *
8
+ * @remarks
9
+ * Kit transform. Within each version block, drops empty
10
+ * lists and h3 sections with no content before the next h2/h3 (or the end of
11
+ * the block), along with whitespace-only paragraphs that trail them.
12
+ *
13
+ * @internal
14
+ */
15
+ /**
16
+ * Remove empty h3 sections and empty lists from each version block.
17
+ *
18
+ * @param root - The changelog tree
19
+ * @returns The normalized tree
20
+ *
21
+ * @internal
22
+ */
23
+ const normalizeFormat = (root) => {
24
+ const children = [...root.children];
25
+ const indicesToRemove = /* @__PURE__ */ new Set();
26
+ for (const block of getVersionBlocks(children)) for (let i = block.startIndex; i < block.endIndex; i++) {
27
+ const node = children[i];
28
+ if (node.type === "list" && node.children.length === 0) {
29
+ indicesToRemove.add(i);
30
+ continue;
31
+ }
32
+ if (!isHeadingOfDepth(node, [3])) continue;
33
+ let hasContent = false;
34
+ for (let j = i + 1; j < block.endIndex; j++) {
35
+ const next = children[j];
36
+ if (next.type === "paragraph" && Mdast.toText(next).trim() === "") {
37
+ indicesToRemove.add(j);
38
+ continue;
39
+ }
40
+ if (isHeadingOfDepth(next, [2, 3])) break;
41
+ if (next.type === "definition") continue;
42
+ hasContent = true;
43
+ break;
44
+ }
45
+ if (!hasContent) indicesToRemove.add(i);
46
+ }
47
+ return new Root({
48
+ ...root,
49
+ children: children.filter((_, i) => !indicesToRemove.has(i))
50
+ });
51
+ };
52
+
53
+ export { normalizeFormat };
@@ -0,0 +1,53 @@
1
+ import { fromHeading } from "../categories/index.js";
2
+ import { getBlockSections, getHeadingText, getVersionBlocks } from "../utils/blocks.js";
3
+ import { Root } from "@effected/markdown";
4
+
5
+ // src/changesets/transforms/reorder-sections.ts
6
+ /**
7
+ * Changelog transform: reorder h3 sections by category priority.
8
+ *
9
+ * @remarks
10
+ * Kit transform. Sorts sections within each version
11
+ * block so Breaking Changes come first and Other last; unknown headings sort
12
+ * after every category and a `Thanks` section is pinned to the very end.
13
+ *
14
+ * @internal
15
+ */
16
+ /** Default priority for unrecognized headings (after all known categories). */
17
+ const UNKNOWN_PRIORITY = 999;
18
+ /** Priority for the `Thanks` section; closes the version block. */
19
+ const THANKS_PRIORITY = 1e3;
20
+ /** Resolve the sort priority for a section heading. */
21
+ function headingPriority(text) {
22
+ if (text.trim().toLowerCase() === "thanks") return THANKS_PRIORITY;
23
+ return fromHeading(text)?.priority ?? UNKNOWN_PRIORITY;
24
+ }
25
+ /**
26
+ * Reorder h3 sections within each version block by category priority.
27
+ *
28
+ * @param root - The changelog tree
29
+ * @returns A tree with sections in priority order
30
+ *
31
+ * @internal
32
+ */
33
+ const reorderSections = (root) => {
34
+ const children = [...root.children];
35
+ const blocks = getVersionBlocks(children);
36
+ for (let b = blocks.length - 1; b >= 0; b--) {
37
+ const block = blocks[b];
38
+ const sections = getBlockSections(children, block);
39
+ if (sections.length <= 1) continue;
40
+ const preamble = children.slice(block.startIndex, sections[0].headingIndex);
41
+ const sorted = [...sections].sort((x, y) => headingPriority(getHeadingText(x.heading)) - headingPriority(getHeadingText(y.heading)));
42
+ if (sorted.every((s, i) => s.headingIndex === sections[i].headingIndex)) continue;
43
+ const rebuilt = [...preamble];
44
+ for (const section of sorted) rebuilt.push(section.heading, ...section.contentNodes);
45
+ children.splice(block.startIndex, block.endIndex - block.startIndex, ...rebuilt);
46
+ }
47
+ return new Root({
48
+ ...root,
49
+ children
50
+ });
51
+ };
52
+
53
+ export { reorderSections };
@@ -0,0 +1,67 @@
1
+ import { Mdast } from "@effected/markdown";
2
+
3
+ // src/changesets/utils/blocks.ts
4
+ /**
5
+ * Whether a node is a heading of one of the given depths.
6
+ *
7
+ * @param node - The node to test
8
+ * @param depths - Accepted heading depths
9
+ * @returns True (narrowing to `Heading`) when the node matches
10
+ *
11
+ * @internal
12
+ */
13
+ function isHeadingOfDepth(node, depths) {
14
+ return node.type === "heading" && depths.includes(node.depth);
15
+ }
16
+ /**
17
+ * Extract all version (h2) blocks from a children array.
18
+ *
19
+ * @param children - The root children
20
+ * @returns Version blocks in document order
21
+ *
22
+ * @internal
23
+ */
24
+ function getVersionBlocks(children) {
25
+ const headings = [];
26
+ for (let i = 0; i < children.length; i++) if (isHeadingOfDepth(children[i], [2])) headings.push(i);
27
+ return headings.map((headingIndex, n) => ({
28
+ headingIndex,
29
+ startIndex: headingIndex + 1,
30
+ endIndex: n + 1 < headings.length ? headings[n + 1] : children.length
31
+ }));
32
+ }
33
+ /**
34
+ * Extract h3 sections within a version block; content before the first h3 is excluded.
35
+ *
36
+ * @param children - The root children
37
+ * @param block - The version block to scan
38
+ * @returns Sections in document order
39
+ *
40
+ * @internal
41
+ */
42
+ function getBlockSections(children, block) {
43
+ const sections = [];
44
+ for (let i = block.startIndex; i < block.endIndex; i++) {
45
+ const node = children[i];
46
+ if (isHeadingOfDepth(node, [3])) sections.push({
47
+ heading: node,
48
+ headingIndex: i,
49
+ contentNodes: []
50
+ });
51
+ else if (sections.length > 0) sections[sections.length - 1].contentNodes.push(node);
52
+ }
53
+ return sections;
54
+ }
55
+ /**
56
+ * Plain text of a heading.
57
+ *
58
+ * @param heading - The heading node
59
+ * @returns Concatenated inline text
60
+ *
61
+ * @internal
62
+ */
63
+ function getHeadingText(heading) {
64
+ return Mdast.toText(heading);
65
+ }
66
+
67
+ export { getBlockSections, getHeadingText, getVersionBlocks, isHeadingOfDepth };
@@ -1,17 +1,18 @@
1
1
  import { DependencyTableRowSchema } from "../schemas/dependency-table.js";
2
- import { stringifyMarkdown } from "./remark-pipeline.js";
2
+ import { emitKitMarkdown } from "./markdown-emit.js";
3
3
  import { Schema } from "effect";
4
- import { toString } from "mdast-util-to-string";
4
+ import { Mdast, Root, Table, TableCell, TableRow, Text } from "@effected/markdown";
5
5
 
6
6
  // src/changesets/utils/dependency-table.ts
7
7
  /**
8
8
  * Dependency table utilities for parsing, serializing, collapsing, and sorting
9
- * dependency table rows between MDAST table nodes and typed representations.
9
+ * dependency table rows between `@effected/markdown` `Table` nodes and typed
10
+ * representations.
10
11
  *
11
12
  * @remarks
12
13
  * This module provides the low-level functional primitives that the
13
14
  * {@link DependencyTable} class wraps with a stateful, fluent API.
14
- * It operates on arrays of `DependencyTableRow` objects and MDAST `Table`
15
+ * It operates on arrays of `DependencyTableRow` objects and kit `Table`
15
16
  * nodes, converting between the two representations.
16
17
  *
17
18
  * The collapse algorithm merges rows sharing the same `dependency + type`
@@ -55,14 +56,14 @@ const COLUMN_KEYS = [
55
56
  /** Schema decoder for validating raw cell values into a typed row. */
56
57
  const decode = Schema.decodeUnknownSync(DependencyTableRowSchema);
57
58
  /**
58
- * Parse an MDAST table node into validated dependency table rows.
59
+ * Parse a kit table node into validated dependency table rows.
59
60
  *
60
61
  * @remarks
61
62
  * Validates the header row against {@link COLUMN_HEADERS} (case-insensitive),
62
63
  * then decodes each data row through the `DependencyTableRowSchema` for
63
64
  * type-safe validation of action values, version strings, and dependency types.
64
65
  *
65
- * @param table - An MDAST `Table` node (from remark-gfm)
66
+ * @param table - An `@effected/markdown` `Table` node
66
67
  * @returns Array of validated `DependencyTableRow` objects
67
68
  * @throws If the table has fewer than 2 rows (header + at least one data row)
68
69
  * @throws If the header columns do not match the expected column names
@@ -71,11 +72,11 @@ const decode = Schema.decodeUnknownSync(DependencyTableRowSchema);
71
72
  * @example
72
73
  * ```typescript
73
74
  * import { parseDependencyTable } from "../utils/dependency-table.js";
74
- * import { parseMarkdown } from "../utils/remark-pipeline.js";
75
- * import type { Table } from "mdast";
75
+ * import { parseKitMarkdown } from "../utils/markdown-emit.js";
76
+ * import type { Table } from "\@effected/markdown";
76
77
  *
77
- * const tree = parseMarkdown("| Dependency | Type | Action | From | To |\n| --- | --- | --- | --- | --- |\n| foo | dependency | added | — | 1.0.0 |");
78
- * const table = tree.children.find((n) => n.type === "table") as Table;
78
+ * const tree = parseKitMarkdown("| Dependency | Type | Action | From | To |\n| --- | --- | --- | --- | --- |\n| foo | dependency | added | — | 1.0.0 |");
79
+ * const table = tree.children.find((n): n is Table => n.type === "table");
79
80
  * const rows = parseDependencyTable(table);
80
81
  * // rows[0].dependency === "foo"
81
82
  * ```
@@ -85,14 +86,17 @@ const decode = Schema.decodeUnknownSync(DependencyTableRowSchema);
85
86
  function parseDependencyTable(table) {
86
87
  const rows = table.children;
87
88
  if (rows.length < 2) throw new Error("Dependency table must have at least one data row");
88
- const headers = rows[0].children.map((cell) => toString(cell).trim().toLowerCase());
89
+ const headers = rows[0].children.map((cell) => Mdast.toText(cell).trim().toLowerCase());
89
90
  const expected = COLUMN_HEADERS.map((h) => h.toLowerCase());
90
91
  if (headers.length !== expected.length || !headers.every((h, i) => h === expected[i])) throw new Error(`Table must have columns: ${COLUMN_HEADERS.join(", ")}. Got: ${headers.join(", ")}`);
91
92
  const result = [];
92
93
  for (let i = 1; i < rows.length; i++) {
93
94
  const cells = rows[i].children;
94
95
  const raw = {};
95
- for (let c = 0; c < COLUMN_KEYS.length; c++) raw[COLUMN_KEYS[c]] = toString(cells[c]).trim();
96
+ for (let c = 0; c < COLUMN_KEYS.length; c++) {
97
+ const cell = cells[c];
98
+ raw[COLUMN_KEYS[c]] = cell === void 0 ? "" : Mdast.toText(cell).trim();
99
+ }
96
100
  result.push(decode(raw));
97
101
  }
98
102
  return result;
@@ -113,36 +117,29 @@ function parseDependencyTable(table) {
113
117
  * through parse-and-reserialize regardless of which spelling produced them.
114
118
  *
115
119
  * @param text - The cell text content
116
- * @returns An MDAST `TableCell` node
120
+ * @returns A kit `TableCell` node
117
121
  *
118
122
  * @internal
119
123
  */
120
124
  function makeCell(text) {
121
- return {
122
- type: "tableCell",
123
- children: [{
124
- type: "text",
125
- value: text,
126
- escapeStyle: "literal"
127
- }]
128
- };
125
+ return new TableCell({ children: [new Text({
126
+ value: text,
127
+ escapeStyle: "literal"
128
+ })] });
129
129
  }
130
130
  /**
131
131
  * Create a table row from an array of cell texts.
132
132
  *
133
133
  * @param texts - Array of cell text values
134
- * @returns An MDAST `TableRow` node
134
+ * @returns A kit `TableRow` node
135
135
  *
136
136
  * @internal
137
137
  */
138
138
  function makeRow(texts) {
139
- return {
140
- type: "tableRow",
141
- children: texts.map(makeCell)
142
- };
139
+ return new TableRow({ children: texts.map(makeCell) });
143
140
  }
144
141
  /**
145
- * Serialize dependency table rows into an MDAST `Table` node.
142
+ * Serialize dependency table rows into a kit `Table` node.
146
143
  *
147
144
  * @remarks
148
145
  * Creates a well-formed GFM table with the canonical header row
@@ -150,7 +147,7 @@ function makeRow(texts) {
150
147
  * The inverse of {@link parseDependencyTable}.
151
148
  *
152
149
  * @param rows - Array of `DependencyTableRow` objects
153
- * @returns An MDAST `Table` node ready for insertion into an AST
150
+ * @returns A kit `Table` node ready for insertion into a kit tree
154
151
  *
155
152
  * @example
156
153
  * ```typescript
@@ -168,10 +165,9 @@ function makeRow(texts) {
168
165
  * @internal
169
166
  */
170
167
  function serializeDependencyTable(rows) {
171
- return {
172
- type: "table",
173
- children: [makeRow([...COLUMN_HEADERS]), ...rows.map((row) => makeRow(COLUMN_KEYS.map((key) => row[key])))]
174
- };
168
+ const headerRow = makeRow([...COLUMN_HEADERS]);
169
+ const dataRows = rows.map((row) => makeRow(COLUMN_KEYS.map((key) => row[key])));
170
+ return new Table({ children: [headerRow, ...dataRows] });
175
171
  }
176
172
  /**
177
173
  * Serialize dependency table rows to a markdown table string.
@@ -200,10 +196,7 @@ function serializeDependencyTable(rows) {
200
196
  */
201
197
  function serializeDependencyTableToMarkdown(rows) {
202
198
  const table = serializeDependencyTable(rows);
203
- return stringifyMarkdown({
204
- type: "root",
205
- children: [table]
206
- }).trim();
199
+ return emitKitMarkdown(new Root({ children: [table] })).trim();
207
200
  }
208
201
  /**
209
202
  * Sort priority for dependency actions: removed first, then updated, then added.
@@ -7,8 +7,8 @@ import { Code, Markdown, MarkdownTransform, Mdast } from "@effected/markdown";
7
7
  * into a defect.
8
8
  *
9
9
  * @remarks
10
- * The trees this pipeline emits are either parsed by our own remark pipeline
11
- * or synthesized by our own plugins, so a decode or stringify failure is a
10
+ * The trees this pipeline emits are either parsed by our own parser or
11
+ * synthesized by our own transforms, so a decode or stringify failure is a
12
12
  * programmer error, not an operational condition — the sync call sites treat
13
13
  * it as a defect with the typed error preserved as `cause`.
14
14
  *
@@ -26,18 +26,17 @@ function unwrap(result, context) {
26
26
  *
27
27
  * @remarks
28
28
  * The kit's canonical form emits a code node carrying neither `lang` nor
29
- * `fenceChar` as an INDENTED block — and `lang: null` is exactly what
30
- * remark-parse produces for a ``` fence with no info string, so without this
31
- * pass every language-less snippet in changelog content would change shape.
29
+ * `fenceChar` as an INDENTED block — and that is exactly what a ``` fence with
30
+ * no info string becomes once {@link parseKitMarkdown}'s mdast round trip
31
+ * strips fidelity fields, so without this pass every language-less snippet in
32
+ * changelog content would change shape.
32
33
  * Worse, the indented default is neighbor-dependent: the serializer takes a
33
34
  * representability escape back to a backtick fence when the indented form
34
35
  * would misparse (e.g. code immediately after a list), so the same node
35
36
  * spells differently depending on what precedes it — inconsistent raw
36
37
  * output. House policy is fenced code everywhere, so the ONE emit boundary defaults
37
- * the kit's own per-node fidelity field, `fenceChar`, post-decode. It has to
38
- * happen post-decode: `Mdast.fromMdast` drops non-mdast fields from foreign
39
- * trees, so a `fenceChar` set on the plain tree would never survive the
40
- * boundary. `MarkdownTransform.map` rebuilds only the paths to a changed
38
+ * the kit's own per-node fidelity field, `fenceChar`, at emit time.
39
+ * `MarkdownTransform.map` rebuilds only the paths to a changed
41
40
  * code node; every other subtree is returned by reference.
42
41
  *
43
42
  * @internal
@@ -49,34 +48,11 @@ function ensureFencedCode(node) {
49
48
  }) : code });
50
49
  }
51
50
  /**
52
- * Serialize a plain MDAST tree to canonical markdown.
53
- *
54
- * @remarks
55
- * The single emit chokepoint for the changesets pipeline: decodes the plain
56
- * mdast tree into `@effected/markdown`'s node classes via
57
- * `Mdast.fromMdastResult` and serializes it with `Markdown.stringifyResult`.
58
- * Synchronous on purpose — every call site is a sync string pipeline — and
59
- * failures (an undecodable node type, the nesting-depth hardening guard) are
60
- * defects on self-built trees, so they throw rather than surface typed.
61
- *
62
- * The output always ends with exactly one trailing newline, matching the
63
- * kit's document-level canonical rule.
64
- *
65
- * @param tree - The plain mdast root to serialize
66
- * @returns Canonical markdown text
67
- *
68
- * @internal
69
- */
70
- function emitMarkdown(tree) {
71
- const fenced = ensureFencedCode(unwrap(Mdast.fromMdastResult(tree), "markdown emit: mdast tree failed to decode"));
72
- return unwrap(Markdown.stringifyResult(fenced), "markdown emit: canonical stringify failed");
73
- }
74
- /**
75
51
  * Parse markdown text straight into `@effected/markdown`'s node classes (GFM
76
52
  * dialect, frontmatter capture off).
77
53
  *
78
54
  * @remarks
79
- * Synchronous for the same reason as {@link emitMarkdown}; the kit's parser
55
+ * Synchronous because every call site is a sync string pipeline; the kit's parser
80
56
  * is total over text, so a failure here is a defect (the nesting-depth
81
57
  * hardening guard), not an operational condition.
82
58
  *
@@ -93,8 +69,7 @@ function parseKitMarkdown(text) {
93
69
  * Serialize a kit root, or a single kit block node, to canonical markdown.
94
70
  *
95
71
  * @remarks
96
- * The kit-tree twin of {@link emitMarkdown}: the same fence normalization,
97
- * then `Markdown.stringifyResult`. A lone block node emits byte-identically
72
+ * Applies the fence normalization, then `Markdown.stringifyResult`. A lone block node emits byte-identically
98
73
  * to a one-child root, trailing newline included.
99
74
  *
100
75
  * @param node - The kit root or block node to serialize
@@ -106,4 +81,4 @@ function emitKitMarkdown(node) {
106
81
  return unwrap(Markdown.stringifyResult(ensureFencedCode(node)), "markdown emit: canonical stringify failed");
107
82
  }
108
83
 
109
- export { emitKitMarkdown, emitMarkdown, parseKitMarkdown };
84
+ export { emitKitMarkdown, parseKitMarkdown };