@savvy-web/silk-effects 0.6.0 → 1.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 (155) hide show
  1. package/README.md +48 -17
  2. package/_virtual/_rolldown/runtime.js +18 -0
  3. package/changesets/api/categories.js +247 -0
  4. package/changesets/api/changelog.js +134 -0
  5. package/changesets/api/dependency-table.js +163 -0
  6. package/changesets/api/linter.js +168 -0
  7. package/changesets/api/transformer.js +140 -0
  8. package/changesets/categories/index.js +299 -0
  9. package/changesets/categories/types.js +66 -0
  10. package/changesets/changelog/formatting.js +119 -0
  11. package/changesets/changelog/getDependencyReleaseLine.js +114 -0
  12. package/changesets/changelog/getReleaseLine.js +122 -0
  13. package/changesets/changelog/index.js +99 -0
  14. package/changesets/constants.js +43 -0
  15. package/changesets/errors.js +305 -0
  16. package/changesets/index.js +146 -0
  17. package/changesets/markdownlint/index.js +29 -0
  18. package/changesets/markdownlint/rules/content-structure.js +98 -0
  19. package/changesets/markdownlint/rules/dependency-table-format.js +170 -0
  20. package/changesets/markdownlint/rules/heading-hierarchy.js +61 -0
  21. package/changesets/markdownlint/rules/required-sections.js +54 -0
  22. package/changesets/markdownlint/rules/uncategorized-content.js +54 -0
  23. package/changesets/markdownlint/rules/utils.js +30 -0
  24. package/changesets/remark/plugins/aggregate-dependency-tables.js +47 -0
  25. package/changesets/remark/plugins/contributor-footnotes.js +123 -0
  26. package/changesets/remark/plugins/deduplicate-items.js +30 -0
  27. package/changesets/remark/plugins/issue-link-refs.js +58 -0
  28. package/changesets/remark/plugins/merge-sections.js +43 -0
  29. package/changesets/remark/plugins/normalize-format.js +47 -0
  30. package/changesets/remark/plugins/reorder-sections.js +34 -0
  31. package/changesets/remark/presets.js +119 -0
  32. package/changesets/remark/rules/content-structure.js +22 -0
  33. package/changesets/remark/rules/dependency-table-format.js +40 -0
  34. package/changesets/remark/rules/heading-hierarchy.js +19 -0
  35. package/changesets/remark/rules/required-sections.js +17 -0
  36. package/changesets/remark/rules/uncategorized-content.js +31 -0
  37. package/changesets/schemas/changeset.js +146 -0
  38. package/changesets/schemas/dependency-table.js +189 -0
  39. package/changesets/schemas/git.js +69 -0
  40. package/changesets/schemas/github.js +175 -0
  41. package/changesets/schemas/options.js +182 -0
  42. package/changesets/schemas/package-scope.js +128 -0
  43. package/changesets/schemas/primitives.js +72 -0
  44. package/changesets/schemas/version-files.js +151 -0
  45. package/changesets/services/branch-analyzer.js +278 -0
  46. package/changesets/services/changelog.js +50 -0
  47. package/changesets/services/config-inspector.js +390 -0
  48. package/changesets/services/github.js +178 -0
  49. package/changesets/services/markdown.js +106 -0
  50. package/changesets/services/workspace-snapshot.js +182 -0
  51. package/changesets/utils/commit-parser.js +80 -0
  52. package/changesets/utils/dep-diff.js +77 -0
  53. package/changesets/utils/dependency-table.js +347 -0
  54. package/changesets/utils/issue-refs.js +101 -0
  55. package/changesets/utils/jsonpath.js +175 -0
  56. package/changesets/utils/logger.js +50 -0
  57. package/changesets/utils/markdown-link.js +57 -0
  58. package/changesets/utils/publishability.js +39 -0
  59. package/changesets/utils/remark-pipeline.js +79 -0
  60. package/changesets/utils/section-parser.js +94 -0
  61. package/changesets/utils/strip-frontmatter.js +46 -0
  62. package/changesets/utils/version-blocks.js +108 -0
  63. package/changesets/utils/version-files.js +336 -0
  64. package/changesets/utils/worktree-snapshot.js +142 -0
  65. package/changesets/vendor/github-info.js +55 -0
  66. package/commitlint/config/factory.js +69 -0
  67. package/commitlint/config/plugins.js +227 -0
  68. package/commitlint/config/rules.js +155 -0
  69. package/commitlint/config/schema.js +46 -0
  70. package/commitlint/detection/dco.js +53 -0
  71. package/commitlint/detection/scopes.js +45 -0
  72. package/commitlint/formatter/format.js +85 -0
  73. package/commitlint/formatter/messages.js +79 -0
  74. package/commitlint/hook/diagnostics/branch.js +36 -0
  75. package/commitlint/hook/diagnostics/cache.js +37 -0
  76. package/commitlint/hook/diagnostics/commitlint-config.js +36 -0
  77. package/commitlint/hook/diagnostics/open-issues.js +56 -0
  78. package/commitlint/hook/diagnostics/package-manager.js +51 -0
  79. package/commitlint/hook/diagnostics/signing.js +107 -0
  80. package/commitlint/hook/envelope.js +46 -0
  81. package/commitlint/hook/output.js +45 -0
  82. package/commitlint/hook/parse-bash-command.js +105 -0
  83. package/commitlint/hook/rules/closes-trailer.js +31 -0
  84. package/commitlint/hook/rules/forbidden-content.js +32 -0
  85. package/commitlint/hook/rules/plan-leakage.js +36 -0
  86. package/commitlint/hook/rules/signing-flag-conflict.js +25 -0
  87. package/commitlint/hook/rules/soft-wrap.js +37 -0
  88. package/commitlint/hook/rules/types.js +14 -0
  89. package/commitlint/hook/rules/verbosity.js +31 -0
  90. package/commitlint/hook/silence-logger.js +39 -0
  91. package/commitlint/index.js +146 -0
  92. package/commitlint/prompt/config.js +91 -0
  93. package/commitlint/prompt/emojis.js +74 -0
  94. package/commitlint/prompt/prompter.js +135 -0
  95. package/commitlint/static.js +73 -0
  96. package/errors/BiomeSyncError.js +21 -0
  97. package/errors/ChangesetConfigError.js +20 -0
  98. package/errors/ConfigNotFoundError.js +21 -0
  99. package/errors/SectionParseError.js +16 -0
  100. package/errors/SectionValidationError.js +16 -0
  101. package/errors/SectionWriteError.js +16 -0
  102. package/errors/TagFormatError.js +20 -0
  103. package/errors/ToolNotFoundError.js +11 -0
  104. package/errors/ToolResolutionError.js +11 -0
  105. package/errors/ToolVersionMismatchError.js +11 -0
  106. package/errors/VersioningDetectionError.js +20 -0
  107. package/errors/WorkspaceAnalysisError.js +21 -0
  108. package/index.d.ts +9743 -8380
  109. package/index.js +36 -6657
  110. package/lint/Handler.js +39 -0
  111. package/lint/cli/sections.js +65 -0
  112. package/lint/cli/templates/markdownlint.gen.js +183 -0
  113. package/lint/config/Preset.js +152 -0
  114. package/lint/config/createConfig.js +89 -0
  115. package/lint/handlers/Biome.js +179 -0
  116. package/lint/handlers/Markdown.js +139 -0
  117. package/lint/handlers/PackageJson.js +130 -0
  118. package/lint/handlers/PnpmWorkspace.js +141 -0
  119. package/lint/handlers/ShellScripts.js +58 -0
  120. package/lint/handlers/TypeScript.js +134 -0
  121. package/lint/handlers/Yaml.js +167 -0
  122. package/lint/index.js +52 -0
  123. package/lint/utils/Command.js +285 -0
  124. package/lint/utils/Filter.js +100 -0
  125. package/lint/utils/Workspace.js +86 -0
  126. package/package.json +52 -63
  127. package/schemas/CommentStyle.js +16 -0
  128. package/schemas/ResolvedTool.js +63 -0
  129. package/schemas/SavvySections.js +113 -0
  130. package/schemas/SectionBlock.js +70 -0
  131. package/schemas/SectionDefinition.js +121 -0
  132. package/schemas/SectionResults.js +12 -0
  133. package/schemas/TagStrategySchemas.js +18 -0
  134. package/schemas/ToolDefinition.js +39 -0
  135. package/schemas/ToolResults.js +14 -0
  136. package/schemas/VersioningSchemas.js +95 -0
  137. package/schemas/WorkspaceAnalysisSchemas.js +190 -0
  138. package/services/BiomeSchemaSync.js +133 -0
  139. package/services/ChangesetConfig.js +78 -0
  140. package/services/ChangesetConfigReader.js +106 -0
  141. package/services/ConfigDiscovery.js +71 -0
  142. package/services/ManagedSection.js +288 -0
  143. package/services/SilkPublishability.js +193 -0
  144. package/services/SilkWorkspaceAnalyzer.js +213 -0
  145. package/services/TagStrategy.js +54 -0
  146. package/services/ToolDiscovery.js +229 -0
  147. package/services/VersioningStrategy.js +67 -0
  148. package/tsdoc-metadata.json +11 -11
  149. package/turbo/digest.js +127 -0
  150. package/turbo/errors.js +48 -0
  151. package/turbo/index.js +32 -0
  152. package/turbo/schemas/DryRun.js +57 -0
  153. package/turbo/schemas/results.js +61 -0
  154. package/turbo/services/TurboInspector.js +100 -0
  155. package/utils/ToolCommand.js +40 -0
@@ -0,0 +1,119 @@
1
+ import { extractUrlFromMarkdown } from "../utils/markdown-link.js";
2
+
3
+ //#region src/changesets/changelog/formatting.ts
4
+ /**
5
+ * Issue reference categories and their display labels.
6
+ *
7
+ * Used by {@link formatChangelogEntry} to render issue links grouped
8
+ * by their relationship to the change (Closes, Fixes, Refs).
9
+ *
10
+ * @internal
11
+ */
12
+ const ISSUE_CATEGORIES = [
13
+ {
14
+ key: "closes",
15
+ label: "Closes"
16
+ },
17
+ {
18
+ key: "fixes",
19
+ label: "Fixes"
20
+ },
21
+ {
22
+ key: "refs",
23
+ label: "Refs"
24
+ }
25
+ ];
26
+ /**
27
+ * Format a changelog entry into a markdown string with GitHub links.
28
+ *
29
+ * Produces a commit-link prefix (shortened to 7 characters) followed by the
30
+ * summary text and any issue references, each rendered as GitHub links.
31
+ *
32
+ * @remarks
33
+ * The output does **not** include a leading `- ` list marker — the caller
34
+ * is responsible for wrapping the result in a markdown list item. Issue
35
+ * references are appended on a new paragraph (double newline) when present,
36
+ * grouped by category: "Closes", "Fixes", "Refs".
37
+ *
38
+ * Output format examples:
39
+ *
40
+ * With commit: `[short-hash](commit-url) Summary text`
41
+ *
42
+ * With issues: `Summary text` followed by `Closes: [#1](issue-url)`
43
+ *
44
+ * With both: `[short-hash](commit-url) Summary text` followed by `Fixes: [#2](issue-url)`
45
+ *
46
+ * @param entry - The changelog entry containing commit, summary, and issue data
47
+ * @param options - Must include `repo` in `owner/repo` format for link generation
48
+ * @returns Formatted markdown string (without leading `- `)
49
+ *
50
+ * @internal
51
+ */
52
+ function formatChangelogEntry(entry, options) {
53
+ const parts = [];
54
+ if (entry.commit) {
55
+ const shortHash = entry.commit.substring(0, 7);
56
+ parts.push(`[\`${shortHash}\`](https://github.com/${options.repo}/commit/${entry.commit})`);
57
+ }
58
+ parts.push(entry.summary.trim());
59
+ const issueLinks = [];
60
+ for (const { key, label } of ISSUE_CATEGORIES) {
61
+ const numbers = entry.issues[key];
62
+ if (numbers.length > 0) {
63
+ const links = numbers.map((num) => `[#${num}](https://github.com/${options.repo}/issues/${num})`);
64
+ issueLinks.push(`${label}: ${links.join(", ")}`);
65
+ }
66
+ }
67
+ if (issueLinks.length > 0) parts.push(`\n\n${issueLinks.join(". ")}`);
68
+ return parts.join(" ");
69
+ }
70
+ /**
71
+ * Format pull-request reference and user attribution for a changelog entry.
72
+ *
73
+ * Appends a PR link and/or a "Thanks \@user!" attribution suffix to a
74
+ * changelog line. The output includes a leading space when non-empty, so it
75
+ * can be directly concatenated after the main entry text.
76
+ *
77
+ * @remarks
78
+ * The `links` parameter may contain either plain URLs or markdown-formatted
79
+ * links (e.g., `[#42](https://...)`). This function handles both formats
80
+ * via {@link extractUrlFromMarkdown}, which strips the markdown link syntax
81
+ * and extracts the raw URL.
82
+ *
83
+ * Output format examples:
84
+ *
85
+ * PR only: `[#42](pr-url)`
86
+ *
87
+ * PR without link: `(#42)`
88
+ *
89
+ * User only: `Thanks user!`
90
+ *
91
+ * Both: `[#42](pr-url) Thanks [user](profile-url)!`
92
+ *
93
+ * Neither: empty string
94
+ *
95
+ * @param pr - Pull request number (omit if no PR is associated)
96
+ * @param user - GitHub username of the contributor (omit if unknown)
97
+ * @param links - Optional pre-formatted links for the PR and user from the GitHub API
98
+ * @returns Formatted attribution string (with leading space) or empty string
99
+ *
100
+ * @internal
101
+ */
102
+ function formatPRAndUserAttribution(pr, user, links) {
103
+ let prReference = "";
104
+ if (pr) if (links?.pull) {
105
+ const pullUrl = extractUrlFromMarkdown(links.pull);
106
+ prReference = ` [#${String(pr)}](${pullUrl})`;
107
+ } else prReference = ` (#${String(pr)})`;
108
+ if (user) {
109
+ if (links?.user) {
110
+ const userUrl = extractUrlFromMarkdown(links.user);
111
+ return `${prReference} Thanks [@${user}](${userUrl})!`;
112
+ }
113
+ return `${prReference} Thanks @${user}!`;
114
+ }
115
+ return prReference;
116
+ }
117
+
118
+ //#endregion
119
+ export { formatChangelogEntry, formatPRAndUserAttribution };
@@ -0,0 +1,114 @@
1
+ import { GitHubService } from "../services/github.js";
2
+ import { serializeDependencyTableToMarkdown } from "../utils/dependency-table.js";
3
+ import { Effect } from "effect";
4
+
5
+ //#region src/changesets/changelog/getDependencyReleaseLine.ts
6
+ /**
7
+ * Core formatter: getDependencyReleaseLine.
8
+ *
9
+ * Formats dependency update entries as a structured markdown table showing
10
+ * each updated dependency's name, type, action, and version range.
11
+ *
12
+ * @remarks
13
+ * This formatter is the companion to {@link getReleaseLine}. While
14
+ * `getReleaseLine` handles individual changeset entries, this module
15
+ * handles the "Updated dependencies" section that Changesets appends
16
+ * when a package's dependencies are bumped as part of a release.
17
+ *
18
+ * The output is a GFM (GitHub Flavored Markdown) table with columns:
19
+ * `Dependency`, `Type`, `Action`, `From`, `To`. The dependency type
20
+ * is inferred from the consuming package's `package.json` fields
21
+ * (`dependencies`, `devDependencies`, `peerDependencies`,
22
+ * `optionalDependencies`) via the {@link inferDependencyType} helper.
23
+ *
24
+ * ### Dependency type inference
25
+ *
26
+ * The {@link FIELD_MAP} constant defines the precedence order for
27
+ * inferring the dependency type. The first matching field wins:
28
+ *
29
+ * 1. `dependencies` maps to `"dependency"`
30
+ * 2. `devDependencies` maps to `"devDependency"`
31
+ * 3. `peerDependencies` maps to `"peerDependency"`
32
+ * 4. `optionalDependencies` maps to `"optionalDependency"`
33
+ *
34
+ * If the dependency is not found in any field, it defaults to `"dependency"`.
35
+ *
36
+ * @see {@link getReleaseLine} for the companion individual changeset formatter
37
+ * @see {@link serializeDependencyTableToMarkdown} for the table serialization utility
38
+ *
39
+ * @internal
40
+ */
41
+ /**
42
+ * Field-to-type mapping for inferring dependency type from `package.json`.
43
+ *
44
+ * Maps `package.json` dependency fields to their corresponding
45
+ * `DependencyTableType` values. The order defines precedence when a
46
+ * dependency name appears in multiple fields (first match wins).
47
+ *
48
+ * @internal
49
+ */
50
+ const FIELD_MAP = [
51
+ ["dependencies", "dependency"],
52
+ ["devDependencies", "devDependency"],
53
+ ["peerDependencies", "peerDependency"],
54
+ ["optionalDependencies", "optionalDependency"]
55
+ ];
56
+ /**
57
+ * Infer the dependency table type from a package's `package.json`.
58
+ *
59
+ * Inspects the consuming package's `package.json` to determine which
60
+ * dependency field contains the given dependency name. Returns the
61
+ * corresponding `DependencyTableType` value, or `"dependency"` as a
62
+ * fallback when the dependency is not found in any standard field.
63
+ *
64
+ * @param dep - The dependency update with its associated `packageJson`
65
+ * @returns The inferred dependency type for display in the table
66
+ *
67
+ * @internal
68
+ */
69
+ function inferDependencyType(dep) {
70
+ const pkg = dep.packageJson;
71
+ for (const [field, type] of FIELD_MAP) {
72
+ const section = pkg[field];
73
+ if (typeof section === "object" && section !== null && dep.name in section) return type;
74
+ }
75
+ return "dependency";
76
+ }
77
+ /**
78
+ * Format dependency release lines as a structured markdown table.
79
+ *
80
+ * This is the core Effect program that implements the `getDependencyReleaseLine`
81
+ * contract from the Changesets API. It requires {@link GitHubService} in its
82
+ * environment (currently unused but kept for API contract stability).
83
+ *
84
+ * @remarks
85
+ * The function maps each `ModCompWithPackage` entry to a `DependencyTableRow`,
86
+ * inferring the dependency type from the consuming package's `package.json`,
87
+ * then delegates to `serializeDependencyTableToMarkdown` for GFM table
88
+ * rendering. Returns an empty string when no dependencies were updated.
89
+ *
90
+ * The `_changesets` and `_options` parameters are part of the Changesets API
91
+ * contract but are not used in the table format. They are retained for
92
+ * interface compatibility.
93
+ *
94
+ * @param _changesets - Changesets that caused the dependency updates (unused in table format)
95
+ * @param dependenciesUpdated - The list of dependencies that were updated, including old/new versions
96
+ * @param _options - Validated configuration options (unused in table format)
97
+ * @returns An `Effect` that resolves to a formatted markdown table string, or empty string if no dependencies were updated
98
+ */
99
+ function getDependencyReleaseLine(_changesets, dependenciesUpdated, _options) {
100
+ return Effect.gen(function* () {
101
+ if (dependenciesUpdated.length === 0) return "";
102
+ yield* GitHubService;
103
+ return serializeDependencyTableToMarkdown(dependenciesUpdated.map((dep) => ({
104
+ dependency: dep.name,
105
+ type: inferDependencyType(dep),
106
+ action: "updated",
107
+ from: dep.oldVersion,
108
+ to: dep.newVersion
109
+ })));
110
+ });
111
+ }
112
+
113
+ //#endregion
114
+ export { getDependencyReleaseLine };
@@ -0,0 +1,122 @@
1
+ import { resolveCommitType } from "../categories/index.js";
2
+ import { GitHubService } from "../services/github.js";
3
+ import { GitHubInfoSchema } from "../schemas/github.js";
4
+ import { parseCommitMessage } from "../utils/commit-parser.js";
5
+ import { parseIssueReferences } from "../utils/issue-refs.js";
6
+ import { logWarning } from "../utils/logger.js";
7
+ import { parseChangesetSections } from "../utils/section-parser.js";
8
+ import { formatChangelogEntry, formatPRAndUserAttribution } from "./formatting.js";
9
+ import { Effect, Schema } from "effect";
10
+
11
+ //#region src/changesets/changelog/getReleaseLine.ts
12
+ /**
13
+ * Core formatter: getReleaseLine.
14
+ *
15
+ * Formats a single changeset into a changelog entry. This is the primary
16
+ * formatting function in the changelog pipeline, responsible for transforming
17
+ * a `NewChangesetWithCommit` into structured markdown output.
18
+ *
19
+ * @remarks
20
+ * The formatter supports two distinct input modes:
21
+ *
22
+ * 1. **Section-aware changesets** — When the changeset summary contains h2
23
+ * headings (e.g., `## Features`, `## Bug Fixes`), each section is rendered
24
+ * as an h3 heading with commit-linked list items beneath it. This mode
25
+ * produces multi-line output suitable for rich changelogs.
26
+ *
27
+ * 2. **Flat-text changesets** — When the summary is plain text without section
28
+ * headings, the formatter falls back to backward-compatible single-line
29
+ * output. The conventional commit type is resolved via `resolveCommitType`
30
+ * to determine the changelog category.
31
+ *
32
+ * In both modes, the formatter:
33
+ * - Fetches GitHub metadata (PR number, author) via {@link GitHubService}
34
+ * - Generates shortened commit hash links (`[abc1234](...)`)
35
+ * - Extracts and renders issue references (Closes, Fixes, Refs)
36
+ * - Appends PR and user attribution when available
37
+ *
38
+ * @see {@link formatChangelogEntry} for the low-level entry formatting
39
+ * @see {@link formatPRAndUserAttribution} for attribution suffix generation
40
+ * @see {@link getDependencyReleaseLine} for the companion dependency formatter
41
+ *
42
+ * @internal
43
+ */
44
+ /**
45
+ * Format a single changeset into a markdown release line.
46
+ *
47
+ * This is the core Effect program that implements the `getReleaseLine` contract
48
+ * from the Changesets API. It requires {@link GitHubService} in its environment
49
+ * for resolving commit metadata.
50
+ *
51
+ * @remarks
52
+ * The formatting pipeline proceeds in seven steps:
53
+ *
54
+ * 1. **Fetch GitHub info** — If the changeset has a commit hash, query
55
+ * the {@link GitHubService} for PR number, author, and links. Failures
56
+ * are caught and logged as warnings (attribution is simply omitted).
57
+ * 2. **Parse sections** — Attempt to split the summary into h2-delimited
58
+ * sections using `parseChangesetSections`.
59
+ * 3. **Parse conventional commit** — Extract the commit type, scope, and
60
+ * breaking flag from the first line of the summary.
61
+ * 4. **Extract issue references** — Parse the body for `Closes #N`,
62
+ * `Fixes #N`, and `Refs #N` patterns.
63
+ * 5. **Build attribution** — Format PR link and user credit from GitHub info.
64
+ * 6. **Section-aware output** — If sections were found, render each as an
65
+ * h3 heading with commit-linked list items.
66
+ * 7. **Flat-text fallback** — Otherwise, produce a single `- entry` line
67
+ * with the resolved category heading.
68
+ *
69
+ * @param changeset - The changeset to format, including commit hash and summary text
70
+ * @param versionType - The semantic version bump type (`major`, `minor`, or `patch`),
71
+ * used as fallback when no conventional commit type is detected
72
+ * @param options - Validated configuration options (must include `repo` in `owner/repo` format)
73
+ * @returns An `Effect` that resolves to a formatted markdown string
74
+ */
75
+ function getReleaseLine(changeset, versionType, options) {
76
+ return Effect.gen(function* () {
77
+ let commitInfo = null;
78
+ if (changeset.commit) commitInfo = yield* (yield* GitHubService).getInfo({
79
+ commit: changeset.commit,
80
+ repo: options.repo
81
+ }).pipe(Effect.flatMap((raw) => Schema.decodeUnknown(GitHubInfoSchema)(raw).pipe(Effect.map(() => raw), Effect.catchAll(() => Effect.succeed(raw)))), Effect.catchAll((error) => {
82
+ logWarning("Could not fetch GitHub info for commit:", changeset.commit ?? "", String(error));
83
+ return Effect.succeed(null);
84
+ }));
85
+ const parsed = parseChangesetSections(changeset.summary);
86
+ const firstLine = changeset.summary.split("\n")[0];
87
+ const commitMsg = parseCommitMessage(firstLine);
88
+ const issueRefs = parseIssueReferences(changeset.summary.split("\n").slice(1).join("\n"));
89
+ const attribution = commitInfo ? formatPRAndUserAttribution(commitInfo.pull ?? void 0, commitInfo.user ?? void 0, commitInfo.links) : "";
90
+ if (parsed.sections.length > 0) {
91
+ const lines = [];
92
+ if (parsed.preamble) {
93
+ lines.push(parsed.preamble);
94
+ lines.push("");
95
+ }
96
+ for (const section of parsed.sections) {
97
+ lines.push(`### ${section.category.heading}`);
98
+ lines.push("");
99
+ const commitPrefix = changeset.commit ? `[\`${changeset.commit.substring(0, 7)}\`](https://github.com/${options.repo}/commit/${changeset.commit}) ` : "";
100
+ if (section.content) {
101
+ const contentLines = section.content.split("\n");
102
+ const firstContentLine = contentLines[0];
103
+ if (firstContentLine.startsWith("- ") || firstContentLine.startsWith("* ")) {
104
+ lines.push(`${firstContentLine.substring(0, 2)}${commitPrefix}${firstContentLine.substring(2)}`);
105
+ lines.push(...contentLines.slice(1));
106
+ } else lines.push(`- ${commitPrefix}${section.content}`);
107
+ }
108
+ lines.push("");
109
+ }
110
+ return `${lines.join("\n").trimEnd()}${attribution}`;
111
+ }
112
+ return `- ${formatChangelogEntry({
113
+ type: resolveCommitType(commitMsg.type ?? versionType, commitMsg.scope, commitMsg.breaking).heading,
114
+ summary: changeset.summary,
115
+ issues: issueRefs,
116
+ ...changeset.commit ? { commit: changeset.commit } : {}
117
+ }, { repo: options.repo })}${attribution}`;
118
+ });
119
+ }
120
+
121
+ //#endregion
122
+ export { getReleaseLine };
@@ -0,0 +1,99 @@
1
+ import { validateChangesetOptions } from "../schemas/options.js";
2
+ import { GitHubLive } from "../services/github.js";
3
+ import { MarkdownLive } from "../services/markdown.js";
4
+ import { getDependencyReleaseLine } from "./getDependencyReleaseLine.js";
5
+ import { getReleaseLine } from "./getReleaseLine.js";
6
+ import { Effect, Layer } from "effect";
7
+
8
+ //#region src/changesets/changelog/index.ts
9
+ /**
10
+ * Changesets API changelog formatter — `\@savvy-web/changesets/changelog`
11
+ *
12
+ * This module is the default export consumed by the Changesets CLI via the
13
+ * `changelog` field in `.changeset/config.json`. It implements the
14
+ * `ChangelogFunctions` interface from `\@changesets/types`, wiring the
15
+ * Effect-based formatting pipeline into the async API that Changesets expects.
16
+ *
17
+ * @remarks
18
+ * The module composes two Effect programs — {@link getReleaseLine} and
19
+ * {@link getDependencyReleaseLine} — and runs each through
20
+ * `Effect.runPromise` with a merged layer of {@link GitHubLive} (for commit
21
+ * metadata) and {@link MarkdownLive} (for mdast parsing). Options are
22
+ * validated at the boundary via `validateChangesetOptions` before being
23
+ * passed to the formatters.
24
+ *
25
+ * ### Configuration
26
+ *
27
+ * Add the following to your `.changeset/config.json`:
28
+ *
29
+ * ```json
30
+ * {
31
+ * "changelog": ["\@savvy-web/changesets/changelog", { "repo": "savvy-web/package-name" }]
32
+ * }
33
+ * ```
34
+ *
35
+ * The `repo` option is **required** and must be in `owner/repo` format.
36
+ *
37
+ * ### Pipeline
38
+ *
39
+ * 1. **Options validation** — the raw `options` object from the Changesets
40
+ * config is decoded through `ChangesetOptionsSchema`.
41
+ * 2. **Release line formatting** — each changeset is formatted by
42
+ * `getReleaseLine`, which resolves GitHub metadata, parses sections,
43
+ * and produces structured markdown with commit links and attribution.
44
+ * 3. **Dependency table formatting** — bulk dependency updates are
45
+ * formatted by `getDependencyReleaseLine` into a markdown table.
46
+ *
47
+ * @example Configuring in `.changeset/config.json`
48
+ * ```json
49
+ * {
50
+ * "$schema": "https://unpkg.com/\@changesets/config\@3.1.1/schema.json",
51
+ * "changelog": ["\@savvy-web/changesets/changelog", { "repo": "savvy-web/my-package" }],
52
+ * "commit": false,
53
+ * "access": "public"
54
+ * }
55
+ * ```
56
+ *
57
+ * @see {@link getReleaseLine} in `./getReleaseLine.ts` for individual changeset formatting
58
+ * @see {@link getDependencyReleaseLine} in `./getDependencyReleaseLine.ts` for dependency table formatting
59
+ *
60
+ * @packageDocumentation
61
+ */
62
+ /**
63
+ * Combined layer providing all services needed by the formatters.
64
+ *
65
+ * Merges {@link GitHubLive} and {@link MarkdownLive} into a single layer
66
+ * that satisfies the environment requirements of both `getReleaseLine`
67
+ * (which needs `GitHubService` and `MarkdownService`) and
68
+ * `getDependencyReleaseLine` (which needs `GitHubService`).
69
+ *
70
+ * @internal
71
+ */
72
+ const MainLayer = Layer.mergeAll(GitHubLive, MarkdownLive);
73
+ /**
74
+ * Changesets API `ChangelogFunctions` implementation.
75
+ *
76
+ * This object satisfies the `ChangelogFunctions` contract from
77
+ * `\@changesets/types`. Each method validates options, runs the
78
+ * corresponding Effect program with the merged service layer, and
79
+ * returns a `Promise<string>`.
80
+ *
81
+ * @internal
82
+ */
83
+ const changelogFunctions = {
84
+ getReleaseLine: async (changeset, versionType, options) => {
85
+ const program = Effect.gen(function* () {
86
+ return yield* getReleaseLine(changeset, versionType, yield* validateChangesetOptions(options));
87
+ });
88
+ return Effect.runPromise(program.pipe(Effect.provide(MainLayer)));
89
+ },
90
+ getDependencyReleaseLine: async (changesets, dependenciesUpdated, options) => {
91
+ const program = Effect.gen(function* () {
92
+ return yield* getDependencyReleaseLine(changesets, dependenciesUpdated, yield* validateChangesetOptions(options));
93
+ });
94
+ return Effect.runPromise(program.pipe(Effect.provide(MainLayer)));
95
+ }
96
+ };
97
+
98
+ //#endregion
99
+ export { changelogFunctions as default };
@@ -0,0 +1,43 @@
1
+ //#region src/changesets/constants.ts
2
+ /**
3
+ * Constants for changeset lint rule documentation URLs.
4
+ *
5
+ * @remarks
6
+ * Each lint rule in the \@savvy-web/changesets remark-lint plugin has a
7
+ * corresponding documentation page hosted on GitHub. These URLs are
8
+ * referenced in lint diagnostic messages to help users understand and
9
+ * resolve validation errors.
10
+ *
11
+ * Rule codes follow the `CSH` prefix convention:
12
+ * - `CSH001` -- Changeset heading validation
13
+ * - `CSH002` -- Changeset summary validation
14
+ * - `CSH003` -- Dependency table structure validation
15
+ * - `CSH004` -- Changeset body content validation
16
+ * - `CSH005` -- Frontmatter validation
17
+ *
18
+ * @internal
19
+ * @packageDocumentation
20
+ */
21
+ /** Base URL for rule documentation on GitHub. */
22
+ const DOCS_BASE = "https://github.com/savvy-web/changesets/blob/main/docs/rules";
23
+ /**
24
+ * Documentation URLs for each changeset lint rule.
25
+ *
26
+ * @remarks
27
+ * Keyed by rule code (`CSH001`--`CSH005`). Each value is a full URL
28
+ * pointing to the rule's documentation page on GitHub. Used by the
29
+ * remark-lint plugins to attach documentation links to diagnostic
30
+ * messages.
31
+ *
32
+ * @internal
33
+ */
34
+ const RULE_DOCS = {
35
+ CSH001: `${DOCS_BASE}/CSH001.md`,
36
+ CSH002: `${DOCS_BASE}/CSH002.md`,
37
+ CSH003: `${DOCS_BASE}/CSH003.md`,
38
+ CSH004: `${DOCS_BASE}/CSH004.md`,
39
+ CSH005: `${DOCS_BASE}/CSH005.md`
40
+ };
41
+
42
+ //#endregion
43
+ export { RULE_DOCS };