@savvy-web/silk-effects 0.6.1 → 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,168 @@
1
+ import { ContentStructureRule } from "../remark/rules/content-structure.js";
2
+ import { HeadingHierarchyRule } from "../remark/rules/heading-hierarchy.js";
3
+ import { RequiredSectionsRule } from "../remark/rules/required-sections.js";
4
+ import { UncategorizedContentRule } from "../remark/rules/uncategorized-content.js";
5
+ import { stripFrontmatter } from "../utils/strip-frontmatter.js";
6
+ import remarkParse from "remark-parse";
7
+ import remarkStringify from "remark-stringify";
8
+ import { unified } from "unified";
9
+ import { readFileSync, readdirSync } from "node:fs";
10
+ import { join } from "node:path";
11
+
12
+ //#region src/changesets/api/linter.ts
13
+ /**
14
+ * Class-based API wrapper for changeset linting.
15
+ *
16
+ * Provides a static class interface that runs all remark-lint rules
17
+ * against changeset markdown files and returns structured diagnostics.
18
+ *
19
+ * @internal
20
+ */
21
+ /**
22
+ * Static class for linting changeset markdown files.
23
+ *
24
+ * Runs the four remark-lint rules (heading-hierarchy, required-sections,
25
+ * content-structure, uncategorized-content) against changeset markdown
26
+ * and returns structured {@link LintMessage} diagnostics.
27
+ *
28
+ * @remarks
29
+ * This class implements the pre-validation layer of the three-layer
30
+ * pipeline. It validates that changeset markdown conforms to the
31
+ * expected structure before the changelog formatter processes it.
32
+ *
33
+ * YAML frontmatter (the `---` delimited block at the top of changeset
34
+ * files containing package bump declarations) is automatically stripped
35
+ * before validation, since frontmatter is managed by Changesets itself
36
+ * and is not part of the markdown structure being validated.
37
+ *
38
+ * The class provides three granularity levels:
39
+ *
40
+ * - {@link ChangesetLinter.validateContent} -- validate a markdown string directly
41
+ * - {@link ChangesetLinter.validateFile} -- validate a single file by path
42
+ * - {@link ChangesetLinter.validate} -- validate all changeset files in a directory
43
+ *
44
+ * @example Validate a single file and report errors
45
+ * ```typescript
46
+ * import { ChangesetLinter } from "\@savvy-web/changesets";
47
+ * import type { LintMessage } from "\@savvy-web/changesets";
48
+ *
49
+ * const messages: LintMessage[] = ChangesetLinter.validateFile(
50
+ * ".changeset/brave-pandas-learn.md",
51
+ * );
52
+ *
53
+ * if (messages.length > 0) {
54
+ * for (const msg of messages) {
55
+ * console.error(`${msg.file}:${msg.line}:${msg.column} [${msg.rule}] ${msg.message}`);
56
+ * }
57
+ * process.exitCode = 1;
58
+ * }
59
+ * ```
60
+ *
61
+ * @example Validate all changesets in a directory
62
+ * ```typescript
63
+ * import { ChangesetLinter } from "\@savvy-web/changesets";
64
+ * import type { LintMessage } from "\@savvy-web/changesets";
65
+ *
66
+ * const allMessages: LintMessage[] = ChangesetLinter.validate(".changeset");
67
+ *
68
+ * const errorsByFile = new Map<string, LintMessage[]>();
69
+ * for (const msg of allMessages) {
70
+ * const existing = errorsByFile.get(msg.file) ?? [];
71
+ * existing.push(msg);
72
+ * errorsByFile.set(msg.file, existing);
73
+ * }
74
+ *
75
+ * for (const [file, msgs] of errorsByFile) {
76
+ * console.error(`${file}: ${msgs.length} issue(s)`);
77
+ * }
78
+ * ```
79
+ *
80
+ * @example Validate markdown content directly (useful in tests)
81
+ * ```typescript
82
+ * import { ChangesetLinter } from "\@savvy-web/changesets";
83
+ * import type { LintMessage } from "\@savvy-web/changesets";
84
+ *
85
+ * const content = [
86
+ * "---",
87
+ * '"\@savvy-web/core": patch',
88
+ * "---",
89
+ * "",
90
+ * "## Bug Fixes",
91
+ * "",
92
+ * "Fixed an edge case in token validation.",
93
+ * ].join("\n");
94
+ *
95
+ * const messages: LintMessage[] = ChangesetLinter.validateContent(content);
96
+ * // messages.length === 0 (valid changeset)
97
+ * ```
98
+ *
99
+ * @see {@link LintMessage} for the diagnostic message shape
100
+ * @see {@link Categories} for the valid section headings checked by the rules
101
+ *
102
+ * @public
103
+ */
104
+ var ChangesetLinter = class ChangesetLinter {
105
+ /* v8 ignore next -- private constructor prevents direct instantiation */
106
+ constructor() {}
107
+ /**
108
+ * Validate a single changeset file by path.
109
+ *
110
+ * @remarks
111
+ * Reads the file synchronously, strips YAML frontmatter, and runs all
112
+ * four lint rules. The file path is preserved in each returned
113
+ * {@link LintMessage} for error reporting.
114
+ *
115
+ * @param filePath - Absolute or relative path to the changeset `.md` file
116
+ * @returns Array of {@link LintMessage} diagnostics (empty if the file is valid)
117
+ */
118
+ static validateFile(filePath) {
119
+ const raw = readFileSync(filePath, "utf-8");
120
+ return ChangesetLinter.validateContent(raw, filePath);
121
+ }
122
+ /**
123
+ * Validate a markdown string directly.
124
+ *
125
+ * @remarks
126
+ * Strips YAML frontmatter (if present) and runs all four lint rules
127
+ * against the remaining content. This method is useful for validating
128
+ * changeset content that is already in memory, such as in test suites
129
+ * or editor integrations.
130
+ *
131
+ * @param content - Raw markdown content (may include YAML frontmatter)
132
+ * @param filePath - File path for error reporting; defaults to `"<input>"`
133
+ * when validating in-memory content
134
+ * @returns Array of {@link LintMessage} diagnostics (empty if the content is valid)
135
+ */
136
+ static validateContent(content, filePath = "<input>") {
137
+ const body = stripFrontmatter(content);
138
+ return unified().use(remarkParse).use(remarkStringify).use(HeadingHierarchyRule).use(RequiredSectionsRule).use(ContentStructureRule).use(UncategorizedContentRule).processSync(body).messages.map((msg) => ({
139
+ file: filePath,
140
+ /* v8 ignore next 3 -- ruleId/line/column fallbacks; remark-lint always provides these */
141
+ rule: msg.ruleId ?? msg.source ?? "unknown",
142
+ line: msg.line ?? 1,
143
+ column: msg.column ?? 1,
144
+ message: msg.message
145
+ }));
146
+ }
147
+ /**
148
+ * Validate all changeset `.md` files in a directory.
149
+ *
150
+ * @remarks
151
+ * Scans the directory for `*.md` files (excluding `README.md`) and runs
152
+ * {@link ChangesetLinter.validateFile} on each. Results are aggregated
153
+ * into a single array. The directory is read synchronously.
154
+ *
155
+ * This is the method used by the Effect CLI's `lint` and `check`
156
+ * subcommands to validate the `.changeset/` directory.
157
+ *
158
+ * @param dir - Path to the directory containing changeset files
159
+ * (typically `.changeset/`)
160
+ * @returns Aggregated array of {@link LintMessage} diagnostics from all files
161
+ */
162
+ static validate(dir) {
163
+ return readdirSync(dir).filter((f) => f.endsWith(".md") && f !== "README.md").flatMap((filename) => ChangesetLinter.validateFile(join(dir, filename)));
164
+ }
165
+ };
166
+
167
+ //#endregion
168
+ export { ChangesetLinter };
@@ -0,0 +1,140 @@
1
+ import { ContributorFootnotesPlugin } from "../remark/plugins/contributor-footnotes.js";
2
+ import { DeduplicateItemsPlugin } from "../remark/plugins/deduplicate-items.js";
3
+ import { IssueLinkRefsPlugin } from "../remark/plugins/issue-link-refs.js";
4
+ import { MergeSectionsPlugin } from "../remark/plugins/merge-sections.js";
5
+ import { NormalizeFormatPlugin } from "../remark/plugins/normalize-format.js";
6
+ import { ReorderSectionsPlugin } from "../remark/plugins/reorder-sections.js";
7
+ import remarkGfm from "remark-gfm";
8
+ import remarkParse from "remark-parse";
9
+ import remarkStringify from "remark-stringify";
10
+ import { unified } from "unified";
11
+ import { readFileSync, writeFileSync } from "node:fs";
12
+
13
+ //#region src/changesets/api/transformer.ts
14
+ /**
15
+ * Class-based API wrapper for changelog transformation.
16
+ *
17
+ * Provides a static class interface that runs all remark transform
18
+ * plugins against CHANGELOG markdown content as the post-processing
19
+ * layer of the three-layer pipeline.
20
+ *
21
+ * @internal
22
+ */
23
+ /**
24
+ * Static class for post-processing CHANGELOG.md files.
25
+ *
26
+ * Implements the third layer of the three-layer pipeline by running
27
+ * six remark transform plugins in a fixed order to clean up, normalize,
28
+ * and enhance changelog output produced by the formatter layer.
29
+ *
30
+ * @remarks
31
+ * The six plugins run in this order:
32
+ *
33
+ * 1. **MergeSectionsPlugin** -- merges duplicate section headings (e.g., two
34
+ * "Features" sections from separate changesets are combined into one)
35
+ * 2. **ReorderSectionsPlugin** -- reorders sections by category priority
36
+ * (Breaking Changes first, Other last) using the {@link Categories} priority values
37
+ * 3. **DeduplicateItemsPlugin** -- removes duplicate list items within a section
38
+ * 4. **ContributorFootnotesPlugin** -- converts inline contributor mentions
39
+ * into footnote references for cleaner formatting
40
+ * 5. **IssueLinkRefsPlugin** -- converts inline issue/PR links into markdown
41
+ * reference-style links collected at the bottom of the document
42
+ * 6. **NormalizeFormatPlugin** -- applies consistent formatting (spacing,
43
+ * trailing newlines, heading levels)
44
+ *
45
+ * The transformer operates on the full CHANGELOG.md content (all versions),
46
+ * not just the latest release block. It is idempotent -- running it multiple
47
+ * times produces the same output.
48
+ *
49
+ * @example Transform changelog content in memory
50
+ * ```typescript
51
+ * import { ChangelogTransformer } from "\@savvy-web/changesets";
52
+ *
53
+ * const rawChangelog = [
54
+ * "# Changelog",
55
+ * "",
56
+ * "## 1.2.0",
57
+ * "",
58
+ * "### Features",
59
+ * "",
60
+ * "- Added new auth endpoint",
61
+ * "",
62
+ * "### Features",
63
+ * "",
64
+ * "- Added rate limiting",
65
+ * ].join("\n");
66
+ *
67
+ * const cleaned: string = ChangelogTransformer.transformContent(rawChangelog);
68
+ * // Duplicate "Features" sections are merged into one
69
+ * ```
70
+ *
71
+ * @example Transform a CHANGELOG.md file in-place
72
+ * ```typescript
73
+ * import { ChangelogTransformer } from "\@savvy-web/changesets";
74
+ *
75
+ * // Reads, transforms, and writes back to the same path
76
+ * ChangelogTransformer.transformFile("CHANGELOG.md");
77
+ * ```
78
+ *
79
+ * @example Check for changes without writing (dry-run pattern)
80
+ * ```typescript
81
+ * import { readFileSync } from "node:fs";
82
+ * import { ChangelogTransformer } from "\@savvy-web/changesets";
83
+ *
84
+ * const original: string = readFileSync("CHANGELOG.md", "utf-8");
85
+ * const transformed: string = ChangelogTransformer.transformContent(original);
86
+ *
87
+ * if (original !== transformed) {
88
+ * console.error("CHANGELOG.md needs transformation");
89
+ * process.exitCode = 1;
90
+ * }
91
+ * ```
92
+ *
93
+ * @see {@link Categories} for the priority order used by ReorderSectionsPlugin
94
+ * @see {@link ChangesetLinter} for the pre-validation layer (layer 1)
95
+ * @see {@link Changelog} for the formatter layer (layer 2)
96
+ *
97
+ * @public
98
+ */
99
+ var ChangelogTransformer = class ChangelogTransformer {
100
+ /* v8 ignore next -- private constructor prevents direct instantiation */
101
+ constructor() {}
102
+ /**
103
+ * Transform CHANGELOG markdown content by running all six transform plugins.
104
+ *
105
+ * @remarks
106
+ * The input is parsed with `remark-parse` and `remark-gfm` (for table
107
+ * support), processed through all six plugins in order, and stringified
108
+ * back to markdown. The operation is synchronous and idempotent.
109
+ *
110
+ * @param content - Raw CHANGELOG markdown string (may contain multiple
111
+ * version blocks, GFM tables, footnotes, and reference links)
112
+ * @returns The transformed markdown string with sections merged, reordered,
113
+ * deduplicated, and normalized
114
+ */
115
+ static transformContent(content) {
116
+ const file = unified().use(remarkParse).use(remarkGfm).use(MergeSectionsPlugin).use(ReorderSectionsPlugin).use(DeduplicateItemsPlugin).use(ContributorFootnotesPlugin).use(IssueLinkRefsPlugin).use(NormalizeFormatPlugin).use(remarkStringify).processSync(content);
117
+ return String(file);
118
+ }
119
+ /**
120
+ * Transform a CHANGELOG file in-place.
121
+ *
122
+ * @remarks
123
+ * Reads the file synchronously, runs all transform plugins via
124
+ * {@link ChangelogTransformer.transformContent}, and writes the result
125
+ * back to the same path. The file is overwritten atomically (single
126
+ * `writeFileSync` call).
127
+ *
128
+ * This is the method used by the Effect CLI's `transform` subcommand
129
+ * when invoked without the `--dry-run` or `--check` flags.
130
+ *
131
+ * @param filePath - Absolute or relative path to the CHANGELOG.md file
132
+ */
133
+ static transformFile(filePath) {
134
+ const content = readFileSync(filePath, "utf-8");
135
+ writeFileSync(filePath, ChangelogTransformer.transformContent(content), "utf-8");
136
+ }
137
+ };
138
+
139
+ //#endregion
140
+ export { ChangelogTransformer };
@@ -0,0 +1,299 @@
1
+ //#region src/changesets/categories/index.ts
2
+ /**
3
+ * Breaking changes -- backward-incompatible changes.
4
+ *
5
+ * @remarks
6
+ * Priority 1 (highest). This category has no associated commit types;
7
+ * it is resolved by the `!` suffix on any conventional commit type
8
+ * (e.g., `feat!:`, `fix!:`). Use {@link resolveCommitType} with
9
+ * `breaking: true` to map to this category.
10
+ *
11
+ * @internal
12
+ */
13
+ const BREAKING_CHANGES = {
14
+ heading: "Breaking Changes",
15
+ priority: 1,
16
+ commitTypes: [],
17
+ description: "Backward-incompatible changes"
18
+ };
19
+ /**
20
+ * Features -- new functionality.
21
+ *
22
+ * @remarks
23
+ * Priority 2. Maps from the `feat` conventional commit type.
24
+ *
25
+ * @internal
26
+ */
27
+ const FEATURES = {
28
+ heading: "Features",
29
+ priority: 2,
30
+ commitTypes: ["feat"],
31
+ description: "New functionality"
32
+ };
33
+ /**
34
+ * Bug Fixes -- bug corrections.
35
+ *
36
+ * @remarks
37
+ * Priority 3. Maps from the `fix` conventional commit type.
38
+ *
39
+ * @internal
40
+ */
41
+ const BUG_FIXES = {
42
+ heading: "Bug Fixes",
43
+ priority: 3,
44
+ commitTypes: ["fix"],
45
+ description: "Bug corrections"
46
+ };
47
+ /**
48
+ * Performance -- performance improvements.
49
+ *
50
+ * @remarks
51
+ * Priority 4. Maps from the `perf` conventional commit type.
52
+ *
53
+ * @internal
54
+ */
55
+ const PERFORMANCE = {
56
+ heading: "Performance",
57
+ priority: 4,
58
+ commitTypes: ["perf"],
59
+ description: "Performance improvements"
60
+ };
61
+ /**
62
+ * Documentation -- documentation changes.
63
+ *
64
+ * @remarks
65
+ * Priority 5. Maps from the `docs` conventional commit type.
66
+ *
67
+ * @internal
68
+ */
69
+ const DOCUMENTATION = {
70
+ heading: "Documentation",
71
+ priority: 5,
72
+ commitTypes: ["docs"],
73
+ description: "Documentation changes"
74
+ };
75
+ /**
76
+ * Refactoring -- code restructuring without behavior change.
77
+ *
78
+ * @remarks
79
+ * Priority 6. Maps from the `refactor` conventional commit type.
80
+ *
81
+ * @internal
82
+ */
83
+ const REFACTORING = {
84
+ heading: "Refactoring",
85
+ priority: 6,
86
+ commitTypes: ["refactor"],
87
+ description: "Code restructuring"
88
+ };
89
+ /**
90
+ * Tests -- test additions or modifications.
91
+ *
92
+ * @remarks
93
+ * Priority 7. Maps from the `test` conventional commit type.
94
+ *
95
+ * @internal
96
+ */
97
+ const TESTS = {
98
+ heading: "Tests",
99
+ priority: 7,
100
+ commitTypes: ["test"],
101
+ description: "Test additions or modifications"
102
+ };
103
+ /**
104
+ * Build System -- build configuration changes.
105
+ *
106
+ * @remarks
107
+ * Priority 8. Maps from the `build` conventional commit type.
108
+ *
109
+ * @internal
110
+ */
111
+ const BUILD_SYSTEM = {
112
+ heading: "Build System",
113
+ priority: 8,
114
+ commitTypes: ["build"],
115
+ description: "Build configuration changes"
116
+ };
117
+ /**
118
+ * CI -- continuous integration changes.
119
+ *
120
+ * @remarks
121
+ * Priority 9. Maps from the `ci` conventional commit type.
122
+ *
123
+ * @internal
124
+ */
125
+ const CI = {
126
+ heading: "CI",
127
+ priority: 9,
128
+ commitTypes: ["ci"],
129
+ description: "Continuous integration changes"
130
+ };
131
+ /**
132
+ * Dependencies -- dependency updates.
133
+ *
134
+ * @remarks
135
+ * Priority 10. Maps from the `deps` conventional commit type and also
136
+ * from `chore(deps)` via the {@link resolveCommitType} function.
137
+ *
138
+ * @internal
139
+ */
140
+ const DEPENDENCIES = {
141
+ heading: "Dependencies",
142
+ priority: 10,
143
+ commitTypes: ["deps"],
144
+ description: "Dependency updates"
145
+ };
146
+ /**
147
+ * Maintenance -- general maintenance tasks.
148
+ *
149
+ * @remarks
150
+ * Priority 11. Maps from the `chore` and `style` conventional commit types.
151
+ * Note that `chore(deps)` is redirected to {@link DEPENDENCIES} by
152
+ * {@link resolveCommitType}.
153
+ *
154
+ * @internal
155
+ */
156
+ const MAINTENANCE = {
157
+ heading: "Maintenance",
158
+ priority: 11,
159
+ commitTypes: ["chore", "style"],
160
+ description: "General maintenance"
161
+ };
162
+ /**
163
+ * Reverts -- reverted changes.
164
+ *
165
+ * @remarks
166
+ * Priority 12. Maps from the `revert` conventional commit type.
167
+ *
168
+ * @internal
169
+ */
170
+ const REVERTS = {
171
+ heading: "Reverts",
172
+ priority: 12,
173
+ commitTypes: ["revert"],
174
+ description: "Reverted changes"
175
+ };
176
+ /**
177
+ * Other -- uncategorized changes.
178
+ *
179
+ * @remarks
180
+ * Priority 13 (lowest). This category has no associated commit types;
181
+ * it serves as the fallback for any commit type not recognized by the
182
+ * category system. Resolved by {@link resolveCommitType} when no other
183
+ * category matches.
184
+ *
185
+ * @internal
186
+ */
187
+ const OTHER = {
188
+ heading: "Other",
189
+ priority: 13,
190
+ commitTypes: [],
191
+ description: "Uncategorized changes"
192
+ };
193
+ /**
194
+ * All 13 categories ordered by priority (ascending).
195
+ *
196
+ * @remarks
197
+ * This array provides the canonical ordering for section headings in
198
+ * CHANGELOG output. Breaking Changes appear first, Other appears last.
199
+ * The array is frozen (`as const`) to prevent accidental mutation.
200
+ *
201
+ * @internal
202
+ */
203
+ const CATEGORIES = [
204
+ BREAKING_CHANGES,
205
+ FEATURES,
206
+ BUG_FIXES,
207
+ PERFORMANCE,
208
+ DOCUMENTATION,
209
+ REFACTORING,
210
+ TESTS,
211
+ BUILD_SYSTEM,
212
+ CI,
213
+ DEPENDENCIES,
214
+ MAINTENANCE,
215
+ REVERTS,
216
+ OTHER
217
+ ];
218
+ /**
219
+ * Set of all valid section heading strings (case-insensitive lookup).
220
+ * Used by Layer 1 (remark-lint) to validate changeset headings.
221
+ */
222
+ const headingToCategory = new Map(CATEGORIES.map((cat) => [cat.heading.toLowerCase(), cat]));
223
+ /**
224
+ * Map from commit type string to its category.
225
+ * Handles standard commit types. For `chore(deps)` and breaking `!` suffix,
226
+ * use {@link resolveCommitType} instead.
227
+ */
228
+ const commitTypeToCategory = /* @__PURE__ */ new Map();
229
+ for (const cat of CATEGORIES) for (const commitType of cat.commitTypes) commitTypeToCategory.set(commitType, cat);
230
+ /**
231
+ * Resolve a conventional commit type (with optional scope and `!` suffix) to a category.
232
+ *
233
+ * @remarks
234
+ * This function implements the full commit-type-to-category mapping logic,
235
+ * including special cases:
236
+ * - Any commit with `breaking: true` maps to {@link BREAKING_CHANGES}
237
+ * - `chore` with scope `deps` maps to {@link DEPENDENCIES} (not Maintenance)
238
+ * - Unrecognized types fall through to {@link OTHER}
239
+ *
240
+ * @param type - The commit type (e.g., `"feat"`, `"fix"`, `"chore"`)
241
+ * @param scope - Optional scope (e.g., `"deps"` in `chore(deps):`)
242
+ * @param breaking - Whether the commit has a `!` suffix indicating a breaking change
243
+ * @returns The resolved section category
244
+ *
245
+ * @internal
246
+ */
247
+ function resolveCommitType(type, scope, breaking) {
248
+ if (breaking) return BREAKING_CHANGES;
249
+ if (type === "chore" && scope === "deps") return DEPENDENCIES;
250
+ return commitTypeToCategory.get(type) ?? OTHER;
251
+ }
252
+ /**
253
+ * Look up a category by its section heading text.
254
+ *
255
+ * @remarks
256
+ * Comparison is case-insensitive: `"features"`, `"Features"`, and
257
+ * `"FEATURES"` all match the Features category.
258
+ *
259
+ * @param heading - The heading text (e.g., `"Features"`, `"Bug Fixes"`)
260
+ * @returns The matching category, or `undefined` if not recognized
261
+ *
262
+ * @internal
263
+ */
264
+ function fromHeading(heading) {
265
+ return headingToCategory.get(heading.toLowerCase());
266
+ }
267
+ /**
268
+ * Check whether a heading string matches a known category.
269
+ *
270
+ * @remarks
271
+ * Comparison is case-insensitive. Useful for validating that a markdown
272
+ * heading in a changeset file corresponds to a recognized section.
273
+ *
274
+ * @param heading - The heading text to check
275
+ * @returns `true` if the heading matches a known category
276
+ *
277
+ * @internal
278
+ */
279
+ function isValidHeading(heading) {
280
+ return headingToCategory.has(heading.toLowerCase());
281
+ }
282
+ /**
283
+ * Get all valid section heading strings.
284
+ *
285
+ * @remarks
286
+ * Returns the headings in priority order, matching the order of {@link CATEGORIES}.
287
+ * Useful for generating help text or validation error messages that list
288
+ * accepted headings.
289
+ *
290
+ * @returns Array of heading strings (e.g., `["Breaking Changes", "Features", ...]`)
291
+ *
292
+ * @internal
293
+ */
294
+ function allHeadings() {
295
+ return CATEGORIES.map((cat) => cat.heading);
296
+ }
297
+
298
+ //#endregion
299
+ export { BREAKING_CHANGES, BUG_FIXES, BUILD_SYSTEM, CATEGORIES, CI, DEPENDENCIES, DOCUMENTATION, FEATURES, MAINTENANCE, OTHER, PERFORMANCE, REFACTORING, REVERTS, TESTS, allHeadings, fromHeading, isValidHeading, resolveCommitType };
@@ -0,0 +1,66 @@
1
+ import { Schema } from "effect";
2
+
3
+ //#region src/changesets/categories/types.ts
4
+ /**
5
+ * Section category schema and type definitions.
6
+ *
7
+ * @remarks
8
+ * Section categories define how changes are grouped and ordered in release
9
+ * notes and CHANGELOGs. Each category maps conventional commit types to a
10
+ * display heading with a priority for ordering. Categories are used across
11
+ * all three processing layers: remark-lint pre-validation (Layer 1),
12
+ * changelog formatting (Layer 2), and remark-transform post-processing
13
+ * (Layer 3).
14
+ *
15
+ * @see {@link CATEGORIES} for the predefined list of 13 section categories
16
+ * @see {@link resolveCommitType} for mapping commit types to categories
17
+ * @see {@link https://effect.website/docs/schema/introduction | Effect Schema documentation}
18
+ *
19
+ * @packageDocumentation
20
+ */
21
+ /**
22
+ * Schema for a section category that defines how changes are grouped in release notes.
23
+ *
24
+ * @remarks
25
+ * A section category has four fields:
26
+ * - `heading` -- the display heading used in CHANGELOG output (e.g., `"Features"`, `"Bug Fixes"`)
27
+ * - `priority` -- an integer controlling display order (lower = higher priority)
28
+ * - `commitTypes` -- conventional commit type prefixes that map to this category (e.g., `["feat"]`)
29
+ * - `description` -- a brief human-readable description for documentation
30
+ *
31
+ * Categories with an empty `commitTypes` array are resolved through other means:
32
+ * `BREAKING_CHANGES` is resolved via the `!` suffix on any commit type, and
33
+ * `OTHER` is the fallback for unrecognized types.
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * import { Schema } from "effect";
38
+ * import { SectionCategorySchema } from "@savvy-web/changesets";
39
+ * import type { SectionCategory } from "@savvy-web/changesets";
40
+ *
41
+ * const category: SectionCategory = Schema.decodeUnknownSync(SectionCategorySchema)({
42
+ * heading: "Features",
43
+ * priority: 2,
44
+ * commitTypes: ["feat"],
45
+ * description: "New functionality",
46
+ * });
47
+ * ```
48
+ *
49
+ * @see {@link SectionCategory} for the inferred TypeScript type
50
+ * @see {@link CATEGORIES} for all predefined categories
51
+ *
52
+ * @public
53
+ */
54
+ const SectionCategorySchema = Schema.Struct({
55
+ /** Display heading used in CHANGELOG output. */
56
+ heading: Schema.String,
57
+ /** Priority for ordering (lower = higher priority). */
58
+ priority: Schema.Number,
59
+ /** Conventional commit types that map to this category. */
60
+ commitTypes: Schema.Array(Schema.String),
61
+ /** Brief description for documentation. */
62
+ description: Schema.String
63
+ });
64
+
65
+ //#endregion
66
+ export { SectionCategorySchema };