@savvy-web/changesets 0.4.2 → 0.5.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/cjs/index.d.cts CHANGED
@@ -20,124 +20,311 @@ import { ModCompWithPackage } from '@changesets/types';
20
20
  import { NewChangesetWithCommit } from '@changesets/types';
21
21
  import type { Root } from 'mdast';
22
22
  import { Schema } from 'effect';
23
+ import type { Table } from 'mdast';
23
24
  import { VersionType as VersionType_2 } from '@changesets/types';
24
25
  import { YieldableError } from 'effect/Cause';
25
26
 
26
27
  /**
27
- * Static class wrapper for category operations.
28
+ * Static class wrapper for section category operations.
28
29
  *
29
30
  * Provides methods for resolving conventional commit types to changelog
30
- * section categories and validating section headings.
31
+ * section categories, validating section headings, and accessing the
32
+ * canonical set of 13 section categories used throughout the pipeline.
31
33
  *
32
- * @example
34
+ * @remarks
35
+ * This class wraps the pure functions and constants from the internal
36
+ * `categories/` module. Each {@link SectionCategory} is defined by the
37
+ * {@link SectionCategorySchema} Effect Schema, which provides runtime
38
+ * validation at system boundaries. The class itself is stateless; all
39
+ * methods and properties are `static`.
40
+ *
41
+ * Categories control how changelog entries are grouped and ordered.
42
+ * Lower priority numbers appear first in the output. The 13 built-in
43
+ * categories cover all conventional commit types plus special cases
44
+ * like dependency updates and breaking changes.
45
+ *
46
+ * @example Looking up categories from commit metadata
33
47
  * ```typescript
34
48
  * import { Categories } from "\@savvy-web/changesets";
35
49
  * import type { SectionCategory } from "\@savvy-web/changesets";
36
50
  *
37
- * const cat: SectionCategory = Categories.fromCommitType("feat");
38
- * console.log(cat.heading); // "Features"
39
- * console.log(cat.priority); // 2
51
+ * // Resolve a conventional commit type to its category
52
+ * const feature: SectionCategory = Categories.fromCommitType("feat");
53
+ * // feature.heading === "Features"
54
+ * // feature.priority === 2
55
+ *
56
+ * // Breaking changes always win regardless of commit type
57
+ * const breaking: SectionCategory = Categories.fromCommitType("fix", undefined, true);
58
+ * // breaking.heading === "Breaking Changes"
59
+ * // breaking.priority === 1
60
+ *
61
+ * // The special scope "deps" on "chore" maps to Dependencies
62
+ * const deps: SectionCategory = Categories.fromCommitType("chore", "deps");
63
+ * // deps.heading === "Dependencies"
64
+ * ```
65
+ *
66
+ * @example Validating section headings
67
+ * ```typescript
68
+ * import { Categories } from "\@savvy-web/changesets";
69
+ *
70
+ * const headings: readonly string[] = Categories.allHeadings();
71
+ * // ["Breaking Changes", "Features", "Bug Fixes", ...]
72
+ *
73
+ * const valid: boolean = Categories.isValidHeading("Bug Fixes");
74
+ * // true
75
+ *
76
+ * const unknown: boolean = Categories.isValidHeading("Miscellaneous");
77
+ * // false
78
+ * ```
79
+ *
80
+ * @example Reverse-lookup from heading text
81
+ * ```typescript
82
+ * import { Categories } from "\@savvy-web/changesets";
83
+ * import type { SectionCategory } from "\@savvy-web/changesets";
40
84
  *
41
- * const isValid: boolean = Categories.isValidHeading("Bug Fixes");
42
- * console.log(isValid); // true
85
+ * const category: SectionCategory | undefined = Categories.fromHeading("Features");
86
+ * if (category) {
87
+ * const commitTypes: readonly string[] = category.commitTypes;
88
+ * // ["feat"]
89
+ * }
43
90
  * ```
44
91
  *
45
- * @see {@link SectionCategory} for the category shape
92
+ * @see {@link SectionCategory} for the category shape (heading, priority, commitTypes, description)
93
+ * @see {@link SectionCategorySchema} for the Effect Schema that validates category data
46
94
  *
47
95
  * @public
48
96
  */
49
97
  export declare class Categories {
50
98
  private constructor();
51
- /** Breaking changes - backward-incompatible changes (priority 1) */
99
+ /**
100
+ * Breaking changes -- backward-incompatible changes (priority 1).
101
+ *
102
+ * @remarks
103
+ * Mapped from any commit type when the `breaking` flag is `true`.
104
+ * Always appears first in changelog output.
105
+ */
52
106
  static readonly BREAKING_CHANGES: SectionCategory;
53
- /** Features - new functionality (priority 2) */
107
+ /**
108
+ * Features -- new functionality (priority 2).
109
+ *
110
+ * @remarks
111
+ * Mapped from the `feat` commit type.
112
+ */
54
113
  static readonly FEATURES: SectionCategory;
55
- /** Bug Fixes - bug corrections (priority 3) */
114
+ /**
115
+ * Bug Fixes -- bug corrections (priority 3).
116
+ *
117
+ * @remarks
118
+ * Mapped from the `fix` commit type.
119
+ */
56
120
  static readonly BUG_FIXES: SectionCategory;
57
- /** Performance - performance improvements (priority 4) */
121
+ /**
122
+ * Performance -- performance improvements (priority 4).
123
+ *
124
+ * @remarks
125
+ * Mapped from the `perf` commit type.
126
+ */
58
127
  static readonly PERFORMANCE: SectionCategory;
59
- /** Documentation - documentation changes (priority 5) */
128
+ /**
129
+ * Documentation -- documentation changes (priority 5).
130
+ *
131
+ * @remarks
132
+ * Mapped from the `docs` commit type.
133
+ */
60
134
  static readonly DOCUMENTATION: SectionCategory;
61
- /** Refactoring - code restructuring (priority 6) */
135
+ /**
136
+ * Refactoring -- code restructuring (priority 6).
137
+ *
138
+ * @remarks
139
+ * Mapped from the `refactor` commit type.
140
+ */
62
141
  static readonly REFACTORING: SectionCategory;
63
- /** Tests - test additions or modifications (priority 7) */
142
+ /**
143
+ * Tests -- test additions or modifications (priority 7).
144
+ *
145
+ * @remarks
146
+ * Mapped from the `test` commit type.
147
+ */
64
148
  static readonly TESTS: SectionCategory;
65
- /** Build System - build configuration changes (priority 8) */
149
+ /**
150
+ * Build System -- build configuration changes (priority 8).
151
+ *
152
+ * @remarks
153
+ * Mapped from the `build` commit type.
154
+ */
66
155
  static readonly BUILD_SYSTEM: SectionCategory;
67
- /** CI - continuous integration changes (priority 9) */
156
+ /**
157
+ * CI -- continuous integration changes (priority 9).
158
+ *
159
+ * @remarks
160
+ * Mapped from the `ci` commit type.
161
+ */
68
162
  static readonly CI: SectionCategory;
69
- /** Dependencies - dependency updates (priority 10) */
163
+ /**
164
+ * Dependencies -- dependency updates (priority 10).
165
+ *
166
+ * @remarks
167
+ * Mapped from `chore(deps)` and similar dependency-scoped commits.
168
+ */
70
169
  static readonly DEPENDENCIES: SectionCategory;
71
- /** Maintenance - general maintenance (priority 11) */
170
+ /**
171
+ * Maintenance -- general maintenance (priority 11).
172
+ *
173
+ * @remarks
174
+ * Mapped from the `chore` commit type (without the `deps` scope).
175
+ */
72
176
  static readonly MAINTENANCE: SectionCategory;
73
- /** Reverts - reverted changes (priority 12) */
177
+ /**
178
+ * Reverts -- reverted changes (priority 12).
179
+ *
180
+ * @remarks
181
+ * Mapped from the `revert` commit type.
182
+ */
74
183
  static readonly REVERTS: SectionCategory;
75
- /** Other - uncategorized changes (priority 13) */
184
+ /**
185
+ * Other -- uncategorized changes (priority 13).
186
+ *
187
+ * @remarks
188
+ * Fallback category for unrecognized commit types. Always appears
189
+ * last in changelog output.
190
+ */
76
191
  static readonly OTHER: SectionCategory;
77
- /** All categories ordered by priority (ascending). */
192
+ /**
193
+ * All 13 categories ordered by priority (ascending, 1-13).
194
+ *
195
+ * @remarks
196
+ * This array is frozen and sorted from highest priority (Breaking Changes)
197
+ * to lowest (Other). Useful for iterating over categories in display order.
198
+ */
78
199
  static readonly ALL: readonly SectionCategory[];
79
200
  /**
80
- * Resolve a conventional commit type to its category.
201
+ * Resolve a conventional commit type to its section category.
81
202
  *
82
203
  * @remarks
83
- * The breaking flag takes highest precedence, always returning the
84
- * "Breaking Changes" category. The special scope `chore(deps)` maps
85
- * to "Dependencies" rather than "Maintenance". Unknown types fall
86
- * back to "Other".
204
+ * Resolution follows a precedence chain:
205
+ *
206
+ * 1. If `breaking` is `true`, always returns {@link Categories.BREAKING_CHANGES}
207
+ * 2. If `type` is `"chore"` and `scope` is `"deps"`, returns {@link Categories.DEPENDENCIES}
208
+ * 3. Otherwise, looks up `type` in the `commitTypes` arrays of all categories
209
+ * 4. Falls back to {@link Categories.OTHER} for unrecognized types
87
210
  *
88
- * @param type - The commit type (e.g., "feat", "fix", "chore")
89
- * @param scope - Optional scope (e.g., "deps" in `chore(deps):`)
90
- * @param breaking - Whether the commit has a `!` suffix
91
- * @returns The resolved section category
211
+ * @param type - The conventional commit type (e.g., `"feat"`, `"fix"`, `"chore"`)
212
+ * @param scope - Optional commit scope (e.g., `"deps"` from `chore(deps):`)
213
+ * @param breaking - Whether the commit includes a breaking change indicator (`!`)
214
+ * @returns The resolved {@link SectionCategory}
92
215
  */
93
216
  static fromCommitType(type: string, scope?: string, breaking?: boolean): SectionCategory;
94
217
  /**
95
218
  * Look up a category by its section heading text.
96
- * Comparison is case-insensitive.
97
219
  *
98
- * @param heading - The heading text (e.g., "Features", "Bug Fixes")
99
- * @returns The matching category, or `undefined` if not recognized
220
+ * @remarks
221
+ * Comparison is case-insensitive. For example, both `"Bug Fixes"` and
222
+ * `"bug fixes"` will match {@link Categories.BUG_FIXES}.
223
+ *
224
+ * @param heading - The heading text (e.g., `"Features"`, `"Bug Fixes"`)
225
+ * @returns The matching {@link SectionCategory}, or `undefined` if the heading
226
+ * does not correspond to any known category
100
227
  */
101
228
  static fromHeading(heading: string): SectionCategory | undefined;
102
229
  /**
103
- * Get all valid section heading strings.
230
+ * Get all valid section heading strings in priority order.
104
231
  *
105
- * @returns Array of heading strings in priority order
232
+ * @remarks
233
+ * Returns the `heading` field from each category in {@link Categories.ALL},
234
+ * preserving priority order. Useful for building validation sets or
235
+ * rendering category pickers.
236
+ *
237
+ * @returns Readonly array of heading strings (e.g., `["Breaking Changes", "Features", ...]`)
106
238
  */
107
239
  static allHeadings(): readonly string[];
108
240
  /**
109
241
  * Check whether a heading string matches a known category.
110
- * Comparison is case-insensitive.
111
242
  *
112
- * @param heading - The heading text to check
113
- * @returns `true` if the heading matches a known category
243
+ * @remarks
244
+ * Comparison is case-insensitive. Equivalent to
245
+ * `Categories.fromHeading(heading) !== undefined`.
246
+ *
247
+ * @param heading - The heading text to validate
248
+ * @returns `true` if the heading matches a known category, `false` otherwise
114
249
  */
115
250
  static isValidHeading(heading: string): boolean;
116
251
  }
117
252
 
118
253
  /**
119
- * Static class wrapper for changelog operations.
254
+ * Static class wrapper for changelog formatting operations.
120
255
  *
121
256
  * Delegates to the Changesets-compatible `getReleaseLine` and
122
- * `getDependencyReleaseLine` functions with Effect-based internals.
257
+ * `getDependencyReleaseLine` functions. Internally, these use the
258
+ * {@link ChangelogService} Effect service layer, which coordinates
259
+ * the {@link GitHubService} (for commit/PR metadata) and
260
+ * {@link MarkdownService} (for AST manipulation) to produce
261
+ * structured changelog entries.
123
262
  *
124
- * @example
263
+ * @remarks
264
+ * This class provides the same formatting capabilities as the
265
+ * `\@savvy-web/changesets/changelog` subpath export, but through a
266
+ * class-based API rather than the Changesets default-export convention.
267
+ * The underlying formatter parses conventional commit prefixes from the
268
+ * changeset summary to determine the section category (via
269
+ * {@link Categories}), resolves GitHub metadata (authors, PR links,
270
+ * commit hashes), and produces markdown output grouped by category.
271
+ *
272
+ * The `options` parameter must include a `repo` field in `"owner/repo"`
273
+ * format (e.g., `"savvy-web/changesets"`) so that GitHub links can be
274
+ * constructed. Additional options are defined by {@link ChangesetOptionsSchema}.
275
+ *
276
+ * @example Formatting a single changeset release line
125
277
  * ```typescript
126
278
  * import { Changelog } from "\@savvy-web/changesets";
127
279
  *
128
280
  * const changeset = {
129
281
  * id: "brave-pandas-learn",
130
- * summary: "feat: add authentication system",
282
+ * summary: "feat: add token refresh endpoint",
131
283
  * releases: [{ name: "\@savvy-web/auth", type: "minor" as const }],
132
- * commit: "abc1234567890",
284
+ * commit: "abc1234567890abcdef1234567890abcdef123456",
133
285
  * };
134
286
  *
135
- * const line = await Changelog.formatReleaseLine(changeset, "minor", {
287
+ * const line: string = await Changelog.formatReleaseLine(changeset, "minor", {
136
288
  * repo: "savvy-web/auth",
137
289
  * });
290
+ * // line contains a markdown bullet under the "Features" section heading
291
+ * ```
292
+ *
293
+ * @example Formatting dependency update lines
294
+ * ```typescript
295
+ * import { Changelog } from "\@savvy-web/changesets";
296
+ *
297
+ * const changesets = [
298
+ * {
299
+ * id: "cool-dogs-fly",
300
+ * summary: "chore(deps): update effect to 3.20.0",
301
+ * releases: [{ name: "\@savvy-web/core", type: "patch" as const }],
302
+ * commit: "def4567890abcdef1234567890abcdef456789ab",
303
+ * },
304
+ * ];
305
+ *
306
+ * const dependenciesUpdated = [
307
+ * {
308
+ * name: "\@savvy-web/utils",
309
+ * type: "patch" as const,
310
+ * oldVersion: "1.2.0",
311
+ * newVersion: "1.2.1",
312
+ * changesets: ["cool-dogs-fly"],
313
+ * packageJson: { name: "\@savvy-web/utils", version: "1.2.1" },
314
+ * },
315
+ * ];
316
+ *
317
+ * const depLines: string = await Changelog.formatDependencyReleaseLine(
318
+ * changesets,
319
+ * dependenciesUpdated,
320
+ * { repo: "savvy-web/core" },
321
+ * );
322
+ * // depLines contains a markdown table of dependency changes
138
323
  * ```
139
324
  *
140
- * @see {@link Categories} for resolving commit types to section categories
325
+ * @see {@link ChangelogService} for the underlying Effect service
326
+ * @see {@link Categories} for how commit types map to section headings
327
+ * @see {@link ChangesetOptionsSchema} for the full options schema
141
328
  *
142
329
  * @public
143
330
  */
@@ -146,30 +333,67 @@ export declare class Changelog {
146
333
  /**
147
334
  * Format a single changeset into a changelog release line.
148
335
  *
149
- * @param changeset - The changeset to format
150
- * @param versionType - The semantic version bump type
151
- * @param options - Configuration with `repo` in `owner/repo` format
152
- * @returns Formatted markdown string
336
+ * @remarks
337
+ * Parses the changeset summary for a conventional commit prefix
338
+ * (e.g., `"feat: ..."`, `"fix!: ..."`), resolves the corresponding
339
+ * {@link SectionCategory}, and produces a markdown bullet item with
340
+ * optional GitHub metadata (author, PR link, commit hash).
341
+ *
342
+ * @param changeset - The changeset to format, including its `id`, `summary`,
343
+ * `releases` array, and optional `commit` hash
344
+ * @param versionType - The semantic version bump type (`"major"`, `"minor"`, or `"patch"`)
345
+ * @param options - Configuration object; must include `repo` in `"owner/repo"` format.
346
+ * Pass `null` to use defaults (no GitHub link resolution).
347
+ * @returns A promise resolving to the formatted markdown string
153
348
  */
154
349
  static formatReleaseLine(changeset: NewChangesetWithCommit, versionType: VersionType_2, options: Record<string, unknown> | null): Promise<string>;
155
350
  /**
156
- * Format dependency update release lines.
351
+ * Format dependency update release lines into a markdown table.
352
+ *
353
+ * @remarks
354
+ * Generates a structured dependency table showing package names,
355
+ * version transitions, and dependency types. The table is placed
356
+ * under the "Dependencies" section heading in the changelog.
157
357
  *
158
- * @param changesets - Changesets that caused dependency updates
159
- * @param dependenciesUpdated - Dependencies that were updated
160
- * @param options - Configuration with `repo` in `owner/repo` format
161
- * @returns Formatted markdown string
358
+ * @param changesets - The changesets that triggered the dependency updates
359
+ * @param dependenciesUpdated - Array of updated dependencies with their
360
+ * old/new versions and package metadata
361
+ * @param options - Configuration object; must include `repo` in `"owner/repo"` format.
362
+ * Pass `null` to use defaults.
363
+ * @returns A promise resolving to the formatted markdown string containing
364
+ * the dependency update table
162
365
  */
163
366
  static formatDependencyReleaseLine(changesets: NewChangesetWithCommit[], dependenciesUpdated: ModCompWithPackage[], options: Record<string, unknown> | null): Promise<string>;
164
367
  }
165
368
 
166
369
  /**
167
- * Service for changelog formatting.
370
+ * Effect service tag for changelog formatting.
371
+ *
372
+ * Provides dependency-injected access to the two Changesets API formatter
373
+ * functions: `formatReleaseLine` and `formatDependencyReleaseLine`.
168
374
  *
169
375
  * @remarks
170
- * This service tag defines the interface. Use the Changesets API entry point
171
- * (`\@savvy-web/changesets/changelog`) or the {@link Changelog} class for
172
- * the concrete implementation.
376
+ * This is an abstract service tag it has no default `Layer`. The concrete
377
+ * implementation is the `\@savvy-web/changesets/changelog` subpath export,
378
+ * which wires the formatting logic through `Effect.runPromise` for the
379
+ * Changesets CLI. For direct Effect usage, build your own layer or use
380
+ * the class-based `Changelog` wrapper.
381
+ *
382
+ * @example
383
+ * ```typescript
384
+ * import { Effect } from "effect";
385
+ * import type { ChangesetOptions } from "\@savvy-web/changesets";
386
+ * import { ChangelogService, GitHubLive, MarkdownLive } from "\@savvy-web/changesets";
387
+ *
388
+ * const program = Effect.gen(function* () {
389
+ * const changelog = yield* ChangelogService;
390
+ * const line = yield* changelog.formatReleaseLine(changeset, "minor", options);
391
+ * return line;
392
+ * });
393
+ * ```
394
+ *
395
+ * @see {@link ChangelogServiceShape} for the service interface
396
+ * @see {@link ChangelogServiceBase} for the api-extractor base class
173
397
  *
174
398
  * @public
175
399
  */
@@ -191,12 +415,37 @@ export declare const ChangelogServiceBase: Context.TagClass<ChangelogService, "C
191
415
  /**
192
416
  * Service interface for changelog formatting.
193
417
  *
194
- * @internal
418
+ * Describes the two operations a `ChangelogService` implementation must
419
+ * provide: formatting individual release lines and formatting dependency
420
+ * update tables.
421
+ *
422
+ * @remarks
423
+ * Both methods return `Effect.Effect` values that require additional
424
+ * services in their environment (`R` channel). `formatReleaseLine` needs
425
+ * both {@link GitHubService} (for commit metadata) and {@link MarkdownService}
426
+ * (for markdown parsing), while `formatDependencyReleaseLine` only needs
427
+ * {@link GitHubService}.
428
+ *
429
+ * @public
195
430
  */
196
431
  export declare interface ChangelogServiceShape {
197
- /** Format a single changeset release line. */
432
+ /**
433
+ * Format a single changeset into a markdown release line.
434
+ *
435
+ * @param changeset - The changeset to format, including its commit hash and summary
436
+ * @param versionType - The semantic version bump type (`major`, `minor`, or `patch`)
437
+ * @param options - Validated changeset configuration options (must include `repo`)
438
+ * @returns An `Effect` that resolves to a formatted markdown string
439
+ */
198
440
  readonly formatReleaseLine: (changeset: NewChangesetWithCommit, versionType: VersionType_2, options: ChangesetOptions) => Effect.Effect<string, never, GitHubService | MarkdownService>;
199
- /** Format dependency update release lines. */
441
+ /**
442
+ * Format dependency update release lines as a markdown table.
443
+ *
444
+ * @param changesets - The changesets that triggered the dependency updates
445
+ * @param dependenciesUpdated - The list of updated dependencies with version info
446
+ * @param options - Validated changeset configuration options (must include `repo`)
447
+ * @returns An `Effect` that resolves to a formatted markdown table string, or empty string if no updates
448
+ */
200
449
  readonly formatDependencyReleaseLine: (changesets: NewChangesetWithCommit[], dependenciesUpdated: ModCompWithPackage[], options: ChangesetOptions) => Effect.Effect<string, never, GitHubService>;
201
450
  }
202
451
 
@@ -204,43 +453,116 @@ export declare interface ChangelogServiceShape {
204
453
  * Class-based API wrapper for changelog transformation.
205
454
  *
206
455
  * Provides a static class interface that runs all remark transform
207
- * plugins against CHANGELOG markdown content.
456
+ * plugins against CHANGELOG markdown content as the post-processing
457
+ * layer of the three-layer pipeline.
458
+ *
459
+ * @internal
208
460
  */
209
461
  /**
210
- * Static class for transforming CHANGELOG.md files.
462
+ * Static class for post-processing CHANGELOG.md files.
211
463
  *
212
- * Runs all six remark transform plugins in the correct order:
213
- * merge-sections, reorder-sections, deduplicate-items,
214
- * contributor-footnotes, issue-link-refs, normalize-format.
464
+ * Implements the third layer of the three-layer pipeline by running
465
+ * six remark transform plugins in a fixed order to clean up, normalize,
466
+ * and enhance changelog output produced by the formatter layer.
215
467
  *
216
- * @example
468
+ * @remarks
469
+ * The six plugins run in this order:
470
+ *
471
+ * 1. **MergeSectionsPlugin** -- merges duplicate section headings (e.g., two
472
+ * "Features" sections from separate changesets are combined into one)
473
+ * 2. **ReorderSectionsPlugin** -- reorders sections by category priority
474
+ * (Breaking Changes first, Other last) using the {@link Categories} priority values
475
+ * 3. **DeduplicateItemsPlugin** -- removes duplicate list items within a section
476
+ * 4. **ContributorFootnotesPlugin** -- converts inline contributor mentions
477
+ * into footnote references for cleaner formatting
478
+ * 5. **IssueLinkRefsPlugin** -- converts inline issue/PR links into markdown
479
+ * reference-style links collected at the bottom of the document
480
+ * 6. **NormalizeFormatPlugin** -- applies consistent formatting (spacing,
481
+ * trailing newlines, heading levels)
482
+ *
483
+ * The transformer operates on the full CHANGELOG.md content (all versions),
484
+ * not just the latest release block. It is idempotent -- running it multiple
485
+ * times produces the same output.
486
+ *
487
+ * @example Transform changelog content in memory
217
488
  * ```typescript
218
489
  * import { ChangelogTransformer } from "\@savvy-web/changesets";
219
490
  *
220
- * // Transform a string
221
- * const cleaned = ChangelogTransformer.transformContent(rawMarkdown);
491
+ * const rawChangelog = [
492
+ * "# Changelog",
493
+ * "",
494
+ * "## 1.2.0",
495
+ * "",
496
+ * "### Features",
497
+ * "",
498
+ * "- Added new auth endpoint",
499
+ * "",
500
+ * "### Features",
501
+ * "",
502
+ * "- Added rate limiting",
503
+ * ].join("\n");
504
+ *
505
+ * const cleaned: string = ChangelogTransformer.transformContent(rawChangelog);
506
+ * // Duplicate "Features" sections are merged into one
507
+ * ```
508
+ *
509
+ * @example Transform a CHANGELOG.md file in-place
510
+ * ```typescript
511
+ * import { ChangelogTransformer } from "\@savvy-web/changesets";
222
512
  *
223
- * // Transform a file in-place
513
+ * // Reads, transforms, and writes back to the same path
224
514
  * ChangelogTransformer.transformFile("CHANGELOG.md");
225
515
  * ```
226
516
  *
517
+ * @example Check for changes without writing (dry-run pattern)
518
+ * ```typescript
519
+ * import { readFileSync } from "node:fs";
520
+ * import { ChangelogTransformer } from "\@savvy-web/changesets";
521
+ *
522
+ * const original: string = readFileSync("CHANGELOG.md", "utf-8");
523
+ * const transformed: string = ChangelogTransformer.transformContent(original);
524
+ *
525
+ * if (original !== transformed) {
526
+ * console.error("CHANGELOG.md needs transformation");
527
+ * process.exitCode = 1;
528
+ * }
529
+ * ```
530
+ *
531
+ * @see {@link Categories} for the priority order used by ReorderSectionsPlugin
532
+ * @see {@link ChangesetLinter} for the pre-validation layer (layer 1)
533
+ * @see {@link Changelog} for the formatter layer (layer 2)
534
+ *
227
535
  * @public
228
536
  */
229
537
  export declare class ChangelogTransformer {
230
538
  private constructor();
231
539
  /**
232
- * Transform CHANGELOG markdown content by running all transform plugins.
540
+ * Transform CHANGELOG markdown content by running all six transform plugins.
541
+ *
542
+ * @remarks
543
+ * The input is parsed with `remark-parse` and `remark-gfm` (for table
544
+ * support), processed through all six plugins in order, and stringified
545
+ * back to markdown. The operation is synchronous and idempotent.
233
546
  *
234
- * @param content - Raw CHANGELOG markdown string
235
- * @returns The transformed markdown string
547
+ * @param content - Raw CHANGELOG markdown string (may contain multiple
548
+ * version blocks, GFM tables, footnotes, and reference links)
549
+ * @returns The transformed markdown string with sections merged, reordered,
550
+ * deduplicated, and normalized
236
551
  */
237
552
  static transformContent(content: string): string;
238
553
  /**
239
554
  * Transform a CHANGELOG file in-place.
240
555
  *
241
- * Reads the file, runs all transform plugins, and writes the result back.
556
+ * @remarks
557
+ * Reads the file synchronously, runs all transform plugins via
558
+ * {@link ChangelogTransformer.transformContent}, and writes the result
559
+ * back to the same path. The file is overwritten atomically (single
560
+ * `writeFileSync` call).
561
+ *
562
+ * This is the method used by the Effect CLI's `transform` subcommand
563
+ * when invoked without the `--dry-run` or `--check` flags.
242
564
  *
243
- * @param filePath - Path to the CHANGELOG.md file
565
+ * @param filePath - Absolute or relative path to the CHANGELOG.md file
244
566
  */
245
567
  static transformFile(filePath: string): void;
246
568
  }
@@ -248,60 +570,142 @@ export declare class ChangelogTransformer {
248
570
  /**
249
571
  * Inferred type for {@link ChangesetSchema}.
250
572
  *
573
+ * @remarks
574
+ * Use this interface when you need to type a variable or parameter as a
575
+ * decoded changeset object. It is structurally equivalent to the output
576
+ * of `Schema.decodeUnknownSync(ChangesetSchema)(...)`.
577
+ *
251
578
  * @public
252
579
  */
253
580
  export declare interface Changeset extends Schema.Schema.Type<typeof ChangesetSchema> {
254
581
  }
255
582
 
256
583
  /**
257
- * Static class for linting changeset files.
584
+ * Static class for linting changeset markdown files.
258
585
  *
259
586
  * Runs the four remark-lint rules (heading-hierarchy, required-sections,
260
587
  * content-structure, uncategorized-content) against changeset markdown
261
- * and returns structured diagnostic messages.
588
+ * and returns structured {@link LintMessage} diagnostics.
262
589
  *
263
- * @example
590
+ * @remarks
591
+ * This class implements the pre-validation layer of the three-layer
592
+ * pipeline. It validates that changeset markdown conforms to the
593
+ * expected structure before the changelog formatter processes it.
594
+ *
595
+ * YAML frontmatter (the `---` delimited block at the top of changeset
596
+ * files containing package bump declarations) is automatically stripped
597
+ * before validation, since frontmatter is managed by Changesets itself
598
+ * and is not part of the markdown structure being validated.
599
+ *
600
+ * The class provides three granularity levels:
601
+ *
602
+ * - {@link ChangesetLinter.validateContent} -- validate a markdown string directly
603
+ * - {@link ChangesetLinter.validateFile} -- validate a single file by path
604
+ * - {@link ChangesetLinter.validate} -- validate all changeset files in a directory
605
+ *
606
+ * @example Validate a single file and report errors
607
+ * ```typescript
608
+ * import { ChangesetLinter } from "\@savvy-web/changesets";
609
+ * import type { LintMessage } from "\@savvy-web/changesets";
610
+ *
611
+ * const messages: LintMessage[] = ChangesetLinter.validateFile(
612
+ * ".changeset/brave-pandas-learn.md",
613
+ * );
614
+ *
615
+ * if (messages.length > 0) {
616
+ * for (const msg of messages) {
617
+ * console.error(`${msg.file}:${msg.line}:${msg.column} [${msg.rule}] ${msg.message}`);
618
+ * }
619
+ * process.exitCode = 1;
620
+ * }
621
+ * ```
622
+ *
623
+ * @example Validate all changesets in a directory
264
624
  * ```typescript
265
625
  * import { ChangesetLinter } from "\@savvy-web/changesets";
266
626
  * import type { LintMessage } from "\@savvy-web/changesets";
267
627
  *
268
- * const messages: LintMessage[] = ChangesetLinter.validateFile("path/to/changeset.md");
269
- * for (const msg of messages) {
270
- * console.log(`${msg.file}:${msg.line}:${msg.column} ${msg.rule} ${msg.message}`);
628
+ * const allMessages: LintMessage[] = ChangesetLinter.validate(".changeset");
629
+ *
630
+ * const errorsByFile = new Map<string, LintMessage[]>();
631
+ * for (const msg of allMessages) {
632
+ * const existing = errorsByFile.get(msg.file) ?? [];
633
+ * existing.push(msg);
634
+ * errorsByFile.set(msg.file, existing);
635
+ * }
636
+ *
637
+ * for (const [file, msgs] of errorsByFile) {
638
+ * console.error(`${file}: ${msgs.length} issue(s)`);
271
639
  * }
272
640
  * ```
273
641
  *
642
+ * @example Validate markdown content directly (useful in tests)
643
+ * ```typescript
644
+ * import { ChangesetLinter } from "\@savvy-web/changesets";
645
+ * import type { LintMessage } from "\@savvy-web/changesets";
646
+ *
647
+ * const content = [
648
+ * "---",
649
+ * '"\@savvy-web/core": patch',
650
+ * "---",
651
+ * "",
652
+ * "## Bug Fixes",
653
+ * "",
654
+ * "Fixed an edge case in token validation.",
655
+ * ].join("\n");
656
+ *
657
+ * const messages: LintMessage[] = ChangesetLinter.validateContent(content);
658
+ * // messages.length === 0 (valid changeset)
659
+ * ```
660
+ *
661
+ * @see {@link LintMessage} for the diagnostic message shape
662
+ * @see {@link Categories} for the valid section headings checked by the rules
663
+ *
274
664
  * @public
275
665
  */
276
666
  export declare class ChangesetLinter {
277
667
  private constructor();
278
668
  /**
279
- * Validate a single changeset file.
669
+ * Validate a single changeset file by path.
280
670
  *
281
- * Reads the file, strips YAML frontmatter, and runs all lint rules.
671
+ * @remarks
672
+ * Reads the file synchronously, strips YAML frontmatter, and runs all
673
+ * four lint rules. The file path is preserved in each returned
674
+ * {@link LintMessage} for error reporting.
282
675
  *
283
- * @param filePath - Path to the changeset `.md` file
284
- * @returns Array of lint messages (empty if valid)
676
+ * @param filePath - Absolute or relative path to the changeset `.md` file
677
+ * @returns Array of {@link LintMessage} diagnostics (empty if the file is valid)
285
678
  */
286
679
  static validateFile(filePath: string): LintMessage[];
287
680
  /**
288
681
  * Validate a markdown string directly.
289
682
  *
290
- * Strips YAML frontmatter and runs all lint rules.
683
+ * @remarks
684
+ * Strips YAML frontmatter (if present) and runs all four lint rules
685
+ * against the remaining content. This method is useful for validating
686
+ * changeset content that is already in memory, such as in test suites
687
+ * or editor integrations.
291
688
  *
292
- * @param content - Raw markdown content (may include frontmatter)
293
- * @param filePath - File path for error reporting (defaults to `"<input>"`)
294
- * @returns Array of lint messages (empty if valid)
689
+ * @param content - Raw markdown content (may include YAML frontmatter)
690
+ * @param filePath - File path for error reporting; defaults to `"<input>"`
691
+ * when validating in-memory content
692
+ * @returns Array of {@link LintMessage} diagnostics (empty if the content is valid)
295
693
  */
296
694
  static validateContent(content: string, filePath?: string): LintMessage[];
297
695
  /**
298
696
  * Validate all changeset `.md` files in a directory.
299
697
  *
300
- * Scans for `*.md` files (excluding `README.md`) and runs
301
- * {@link ChangesetLinter.validateFile} on each.
698
+ * @remarks
699
+ * Scans the directory for `*.md` files (excluding `README.md`) and runs
700
+ * {@link ChangesetLinter.validateFile} on each. Results are aggregated
701
+ * into a single array. The directory is read synchronously.
702
+ *
703
+ * This is the method used by the Effect CLI's `lint` and `check`
704
+ * subcommands to validate the `.changeset/` directory.
302
705
  *
303
- * @param dir - Directory path to scan
304
- * @returns Aggregated lint messages from all files
706
+ * @param dir - Path to the directory containing changeset files
707
+ * (typically `.changeset/`)
708
+ * @returns Aggregated array of {@link LintMessage} diagnostics from all files
305
709
  */
306
710
  static validate(dir: string): LintMessage[];
307
711
  }
@@ -309,6 +713,9 @@ export declare class ChangesetLinter {
309
713
  /**
310
714
  * Inferred type for {@link ChangesetOptionsSchema}.
311
715
  *
716
+ * @remarks
717
+ * The `repo` field is always present; all other fields are optional.
718
+ *
312
719
  * @public
313
720
  */
314
721
  export declare interface ChangesetOptions extends Schema.Schema.Type<typeof ChangesetOptionsSchema> {
@@ -317,6 +724,34 @@ export declare interface ChangesetOptions extends Schema.Schema.Type<typeof Chan
317
724
  /**
318
725
  * Schema for changeset configuration options.
319
726
  *
727
+ * @remarks
728
+ * The `repo` field is required; all other fields are optional with sensible
729
+ * defaults applied by the changelog formatter at runtime. The `versionFiles`
730
+ * option allows specifying additional JSON files (beyond `package.json`)
731
+ * whose version fields should be updated during `changeset version`.
732
+ *
733
+ * @example
734
+ * ```typescript
735
+ * import { Schema } from "effect";
736
+ * import { ChangesetOptionsSchema } from "@savvy-web/changesets";
737
+ * import type { ChangesetOptions } from "@savvy-web/changesets";
738
+ *
739
+ * const options: ChangesetOptions = Schema.decodeUnknownSync(ChangesetOptionsSchema)({
740
+ * repo: "savvy-web/changesets",
741
+ * commitLinks: true,
742
+ * prLinks: true,
743
+ * issueLinks: true,
744
+ * issuePrefixes: ["#", "GH-"],
745
+ * versionFiles: [
746
+ * { glob: "manifest.json", paths: ["$.version"] },
747
+ * ],
748
+ * });
749
+ * ```
750
+ *
751
+ * @see {@link ChangesetOptions} for the inferred TypeScript type
752
+ * @see {@link validateChangesetOptions} for Effect-idiomatic validation with detailed error messages
753
+ * @see {@link VersionFilesSchema} for the `versionFiles` entry format
754
+ *
320
755
  * @public
321
756
  */
322
757
  export declare const ChangesetOptionsSchema: Schema.Struct<{
@@ -340,6 +775,29 @@ export declare const ChangesetOptionsSchema: Schema.Struct<{
340
775
  /**
341
776
  * Schema for a changeset object.
342
777
  *
778
+ * @remarks
779
+ * Represents a single changeset entry as consumed by the changelog formatter.
780
+ * The `summary` is the human-readable description, `id` is a unique identifier
781
+ * (typically the changeset filename without extension), and `commit` is the
782
+ * optional git SHA that introduced the changeset.
783
+ *
784
+ * @example
785
+ * ```typescript
786
+ * import { Schema } from "effect";
787
+ * import { ChangesetSchema } from "@savvy-web/changesets";
788
+ * import type { Changeset } from "@savvy-web/changesets";
789
+ *
790
+ * const changeset: Changeset = Schema.decodeUnknownSync(ChangesetSchema)({
791
+ * summary: "Add retry logic to API client",
792
+ * id: "brave-dogs-laugh",
793
+ * commit: "a1b2c3d",
794
+ * });
795
+ * ```
796
+ *
797
+ * @see {@link Changeset} for the inferred TypeScript type
798
+ * @see {@link ChangesetSummarySchema} for summary validation rules
799
+ * @see {@link CommitHashSchema} for commit hash format requirements
800
+ *
343
801
  * @public
344
802
  */
345
803
  export declare const ChangesetSchema: Schema.Struct<{
@@ -352,7 +810,26 @@ export declare const ChangesetSchema: Schema.Struct<{
352
810
  }>;
353
811
 
354
812
  /**
355
- * Schema for a changeset summary (1-1000 characters).
813
+ * Schema for a changeset summary (1--1000 characters).
814
+ *
815
+ * @remarks
816
+ * Enforces that every changeset has a non-empty summary and caps length
817
+ * at 1000 characters. Longer descriptions should go in the changeset body,
818
+ * not the summary line. Validation messages guide users toward correct usage.
819
+ *
820
+ * @example
821
+ * ```typescript
822
+ * import { Schema } from "effect";
823
+ * import { ChangesetSummarySchema } from "@savvy-web/changesets";
824
+ *
825
+ * // Succeeds — valid summary
826
+ * const summary = Schema.decodeUnknownSync(ChangesetSummarySchema)(
827
+ * "Fix authentication timeout in login flow"
828
+ * );
829
+ *
830
+ * // Throws ParseError — empty string
831
+ * Schema.decodeUnknownSync(ChangesetSummarySchema)("");
832
+ * ```
356
833
  *
357
834
  * @public
358
835
  */
@@ -361,7 +838,30 @@ export declare const ChangesetSummarySchema: Schema.filter<Schema.filter<typeof
361
838
  /**
362
839
  * Changeset file validation failure.
363
840
  *
364
- * Carries structured issue details with JSON-path and message per issue.
841
+ * @remarks
842
+ * Raised when a changeset markdown file fails structural validation
843
+ * (e.g., missing summary, invalid heading, malformed dependency table).
844
+ * Carries an array of structured issues, each with a JSON-path pointing
845
+ * to the problematic field and a human-readable message.
846
+ *
847
+ * @example
848
+ * ```typescript
849
+ * import { Effect } from "effect";
850
+ * import { ChangesetValidationError } from "@savvy-web/changesets";
851
+ *
852
+ * declare const program: Effect.Effect<void, ChangesetValidationError>;
853
+ *
854
+ * const handled = program.pipe(
855
+ * Effect.catchTag("ChangesetValidationError", (err) => {
856
+ * for (const issue of err.issues) {
857
+ * console.error(`${issue.path}: ${issue.message}`);
858
+ * }
859
+ * return Effect.void;
860
+ * }),
861
+ * );
862
+ * ```
863
+ *
864
+ * @see {@link ChangesetSchema} for the schema that drives validation
365
865
  *
366
866
  * @public
367
867
  */
@@ -380,7 +880,7 @@ export declare class ChangesetValidationError extends ChangesetValidationErrorBa
380
880
  }
381
881
 
382
882
  /**
383
- * Base class for ChangesetValidationError.
883
+ * Base class for {@link ChangesetValidationError}.
384
884
  *
385
885
  * @privateRemarks
386
886
  * This export is required for api-extractor documentation generation.
@@ -396,6 +896,34 @@ export declare const ChangesetValidationErrorBase: new <A extends Record<string,
396
896
  /**
397
897
  * Schema for a git commit hash (at least 7 lowercase hex characters).
398
898
  *
899
+ * @remarks
900
+ * Accepts both abbreviated (7-character) and full (40-character) SHA-1 hashes.
901
+ * Only lowercase hexadecimal characters are allowed; uppercase letters will
902
+ * fail validation. This matches the output of `git rev-parse --short` and
903
+ * `git log --format=%h`.
904
+ *
905
+ * @example
906
+ * ```typescript
907
+ * import { Schema } from "effect";
908
+ * import { CommitHashSchema } from "@savvy-web/changesets";
909
+ *
910
+ * // Succeeds — abbreviated hash
911
+ * const short = Schema.decodeUnknownSync(CommitHashSchema)("a1b2c3d");
912
+ *
913
+ * // Succeeds — full 40-character SHA
914
+ * const full = Schema.decodeUnknownSync(CommitHashSchema)(
915
+ * "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
916
+ * );
917
+ *
918
+ * // Throws ParseError — too short
919
+ * Schema.decodeUnknownSync(CommitHashSchema)("a1b2c3");
920
+ *
921
+ * // Throws ParseError — uppercase not allowed
922
+ * Schema.decodeUnknownSync(CommitHashSchema)("A1B2C3D");
923
+ * ```
924
+ *
925
+ * @see {@link ChangesetSchema} which uses this for the optional `commit` field
926
+ *
399
927
  * @public
400
928
  */
401
929
  export declare const CommitHashSchema: Schema.filter<typeof Schema.String>;
@@ -403,7 +931,27 @@ export declare const CommitHashSchema: Schema.filter<typeof Schema.String>;
403
931
  /**
404
932
  * Invalid or missing configuration.
405
933
  *
934
+ * @remarks
935
+ * Raised when the changeset configuration (typically from `.changeset/config.json`)
936
+ * is missing required fields, has invalid values, or cannot be parsed. The `field`
937
+ * property identifies which configuration field is problematic, and `reason`
938
+ * provides an actionable message.
939
+ *
940
+ * @example
941
+ * ```typescript
942
+ * import { Effect } from "effect";
943
+ * import { ConfigurationError, validateChangesetOptions } from "@savvy-web/changesets";
944
+ *
945
+ * const program = validateChangesetOptions({ repo: "invalid" }).pipe(
946
+ * Effect.catchTag("ConfigurationError", (err) => {
947
+ * console.error(`Field "${err.field}": ${err.reason}`);
948
+ * return Effect.fail(err);
949
+ * }),
950
+ * );
951
+ * ```
952
+ *
406
953
  * @see {@link ChangesetOptionsSchema} for the expected configuration shape
954
+ * @see {@link validateChangesetOptions} for the validation function that produces this error
407
955
  *
408
956
  * @public
409
957
  */
@@ -417,7 +965,7 @@ export declare class ConfigurationError extends ConfigurationErrorBase<{
417
965
  }
418
966
 
419
967
  /**
420
- * Base class for ConfigurationError.
968
+ * Base class for {@link ConfigurationError}.
421
969
  *
422
970
  * @privateRemarks
423
971
  * This export is required for api-extractor documentation generation.
@@ -431,70 +979,464 @@ export declare const ConfigurationErrorBase: new <A extends Record<string, any>
431
979
  } & Readonly<A>;
432
980
 
433
981
  /**
434
- * Inferred type for {@link DependencyTypeSchema}.
982
+ * Inferred type for {@link DependencyActionSchema}.
435
983
  *
436
- * @public
437
- */
438
- export declare type DependencyType = typeof DependencyTypeSchema.Type;
439
-
440
- /**
441
- * Schema for npm dependency types.
984
+ * @remarks
985
+ * One of `"added"`, `"updated"`, or `"removed"`.
442
986
  *
443
987
  * @public
444
988
  */
445
- export declare const DependencyTypeSchema: Schema.Literal<["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]>;
989
+ export declare type DependencyAction = typeof DependencyActionSchema.Type;
446
990
 
447
991
  /**
448
- * Inferred type for {@link DependencyUpdateSchema}.
992
+ * Valid dependency table actions.
449
993
  *
450
- * @public
451
- */
452
- export declare interface DependencyUpdate extends Schema.Schema.Type<typeof DependencyUpdateSchema> {
453
- }
454
-
455
- /**
456
- * Schema for a dependency update entry.
994
+ * @remarks
995
+ * Represents the three possible operations on a dependency: `"added"` for
996
+ * new dependencies, `"updated"` for version changes, and `"removed"` for
997
+ * deletions. Used in the "Action" column of dependency tables.
998
+ *
999
+ * @example
1000
+ * ```typescript
1001
+ * import { Schema } from "effect";
1002
+ * import { DependencyActionSchema } from "@savvy-web/changesets";
1003
+ * import type { DependencyAction } from "@savvy-web/changesets";
1004
+ *
1005
+ * const action: DependencyAction = Schema.decodeUnknownSync(DependencyActionSchema)("updated");
1006
+ * ```
457
1007
  *
458
1008
  * @public
459
1009
  */
460
- export declare const DependencyUpdateSchema: Schema.Struct<{
461
- /** Package name (must be non-empty). */
462
- name: Schema.refine<string, typeof Schema.String>;
463
- /** npm dependency type. */
464
- type: Schema.Literal<["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]>;
465
- /** Previous version string. */
466
- oldVersion: typeof Schema.String;
467
- /** New version string. */
468
- newVersion: typeof Schema.String;
469
- }>;
1010
+ export declare const DependencyActionSchema: Schema.Literal<["added", "updated", "removed"]>;
470
1011
 
471
1012
  /**
472
- * GitHub API request failure.
1013
+ * Static class for dependency table manipulation.
1014
+ *
1015
+ * Wraps the internal utility functions that operate on dependency tables --
1016
+ * the structured markdown tables that appear in the "Dependencies" section
1017
+ * of changelogs. Each row in a dependency table represents a single package
1018
+ * change with its name, type, action, and version transition.
473
1019
  *
474
1020
  * @remarks
475
- * Use {@link GitHubApiError.isRetryable} to determine whether a retry
476
- * strategy should be applied. Rate-limited responses (403/429) and
477
- * server errors (5xx) are considered retryable.
1021
+ * The typical workflow for processing dependency tables is:
478
1022
  *
479
- * @public
480
- */
481
- export declare class GitHubApiError extends GitHubApiErrorBase<{
482
- /** The API operation that failed (e.g., "getInfo"). */
483
- readonly operation: string;
484
- /** HTTP status code, if available. */
485
- readonly statusCode?: number | undefined;
486
- /** Human-readable failure reason. */
487
- readonly reason: string;
488
- }> {
489
- get message(): string;
490
- /** Whether this error is a rate-limit response (403 or 429). */
491
- get isRateLimited(): boolean;
492
- /** Whether this error is eligible for retry (server errors or rate limits). */
1023
+ * 1. **Parse** an mdast `Table` node into typed {@link DependencyTableRow} objects
1024
+ * 2. **Collapse** duplicate rows (same package updated multiple times) into single entries
1025
+ * 3. **Sort** rows by dependency type, then alphabetically by package name
1026
+ * 4. **Serialize** back to an mdast `Table` node or directly to a markdown string
1027
+ *
1028
+ * The {@link DependencyTable.aggregate} method combines the collapse and sort
1029
+ * steps into a single call, which is the most common usage pattern.
1030
+ *
1031
+ * Each {@link DependencyTableRow} is validated by the {@link DependencyTableRowSchema}
1032
+ * Effect Schema at system boundaries, ensuring that dependency names, types,
1033
+ * actions, and version strings conform to expected formats.
1034
+ *
1035
+ * @example Parse, aggregate, and serialize a dependency table
1036
+ * ```typescript
1037
+ * import { DependencyTable } from "\@savvy-web/changesets";
1038
+ * import type { DependencyTableRow } from "\@savvy-web/changesets";
1039
+ * import type { Table } from "mdast";
1040
+ *
1041
+ * // Given an mdast Table node from a parsed CHANGELOG
1042
+ * declare const tableNode: Table;
1043
+ *
1044
+ * // Parse into typed rows
1045
+ * const rows: DependencyTableRow[] = DependencyTable.parse(tableNode);
1046
+ *
1047
+ * // Collapse duplicates and sort by type, then name
1048
+ * const aggregated: DependencyTableRow[] = DependencyTable.aggregate(rows);
1049
+ *
1050
+ * // Serialize back to an mdast Table node for further AST manipulation
1051
+ * const outputNode: Table = DependencyTable.serialize(aggregated);
1052
+ *
1053
+ * // Or serialize directly to a markdown string
1054
+ * const markdown: string = DependencyTable.toMarkdown(aggregated);
1055
+ * ```
1056
+ *
1057
+ * @example Step-by-step collapse and sort
1058
+ * ```typescript
1059
+ * import { DependencyTable } from "\@savvy-web/changesets";
1060
+ * import type { DependencyTableRow } from "\@savvy-web/changesets";
1061
+ *
1062
+ * declare const rows: DependencyTableRow[];
1063
+ *
1064
+ * // Collapse duplicate entries (same package appears multiple times)
1065
+ * const collapsed: DependencyTableRow[] = DependencyTable.collapse(rows);
1066
+ *
1067
+ * // Sort by dependency type, then alphabetically by name
1068
+ * const sorted: DependencyTableRow[] = DependencyTable.sort(collapsed);
1069
+ * ```
1070
+ *
1071
+ * @see {@link DependencyTableRow} for the row shape (dependency, type, action, from, to)
1072
+ * @see {@link DependencyTableRowSchema} for the Effect Schema that validates row data
1073
+ * @see {@link DependencyTableSchema} for the non-empty array schema
1074
+ *
1075
+ * @public
1076
+ */
1077
+ export declare class DependencyTable {
1078
+ private constructor();
1079
+ /**
1080
+ * Parse an mdast `Table` node into typed dependency table rows.
1081
+ *
1082
+ * @remarks
1083
+ * Extracts the text content from each table cell and maps it to the
1084
+ * corresponding {@link DependencyTableRow} fields. The first row is
1085
+ * treated as the header and skipped. Rows that do not have the expected
1086
+ * number of columns are ignored.
1087
+ *
1088
+ * @param tableNode - An mdast `Table` node from a parsed markdown AST
1089
+ * @returns Array of {@link DependencyTableRow} objects, one per data row
1090
+ */
1091
+ static parse(tableNode: Table): DependencyTableRow[];
1092
+ /**
1093
+ * Serialize typed dependency table rows into an mdast `Table` node.
1094
+ *
1095
+ * @remarks
1096
+ * Produces a well-formed mdast `Table` with a header row
1097
+ * (`Dependency | Type | Action | From | To`) followed by one data row
1098
+ * per input entry. The resulting node can be inserted into a remark AST
1099
+ * for further processing or stringification.
1100
+ *
1101
+ * @param rows - Array of {@link DependencyTableRow} objects to serialize
1102
+ * @returns An mdast `Table` node ready for AST insertion
1103
+ */
1104
+ static serialize(rows: DependencyTableRow[]): Table;
1105
+ /**
1106
+ * Serialize typed dependency table rows directly to a markdown string.
1107
+ *
1108
+ * @remarks
1109
+ * Convenience method that combines {@link DependencyTable.serialize} with
1110
+ * remark stringification. Produces a GFM-compatible markdown table string.
1111
+ *
1112
+ * @param rows - Array of {@link DependencyTableRow} objects to render
1113
+ * @returns A markdown string containing the formatted table
1114
+ */
1115
+ static toMarkdown(rows: DependencyTableRow[]): string;
1116
+ /**
1117
+ * Collapse duplicate dependency rows into single entries.
1118
+ *
1119
+ * @remarks
1120
+ * When a package appears in multiple rows (e.g., updated in separate
1121
+ * changesets), this method merges them by keeping the earliest `from`
1122
+ * version and the latest `to` version, producing a single row that
1123
+ * represents the net change.
1124
+ *
1125
+ * @param rows - Array of {@link DependencyTableRow} objects, possibly with duplicates
1126
+ * @returns A new array with duplicate packages collapsed into single rows
1127
+ */
1128
+ static collapse(rows: DependencyTableRow[]): DependencyTableRow[];
1129
+ /**
1130
+ * Sort dependency rows by action, type, and package name.
1131
+ *
1132
+ * @remarks
1133
+ * Applies a three-level stable sort:
1134
+ * 1. **Action** — `removed` first, then `updated`, then `added`
1135
+ * 2. **Type** — alphabetically (e.g., `config` before `dependency`)
1136
+ * 3. **Dependency name** — alphabetically within each action+type group
1137
+ *
1138
+ * @param rows - Array of {@link DependencyTableRow} objects to sort
1139
+ * @returns A new array sorted by action, type, then name
1140
+ */
1141
+ static sort(rows: DependencyTableRow[]): DependencyTableRow[];
1142
+ /**
1143
+ * Collapse duplicate rows and then sort the result.
1144
+ *
1145
+ * @remarks
1146
+ * Equivalent to calling {@link DependencyTable.collapse} followed by
1147
+ * {@link DependencyTable.sort}. This is the recommended method for
1148
+ * preparing dependency table data for final output, as it produces
1149
+ * a clean, deduplicated, and consistently ordered table.
1150
+ *
1151
+ * @param rows - Array of {@link DependencyTableRow} objects to aggregate
1152
+ * @returns A new array with duplicates collapsed and rows sorted
1153
+ */
1154
+ static aggregate(rows: DependencyTableRow[]): DependencyTableRow[];
1155
+ }
1156
+
1157
+ /**
1158
+ * Inferred type for {@link DependencyTableRowSchema}.
1159
+ *
1160
+ * @public
1161
+ */
1162
+ export declare interface DependencyTableRow extends Schema.Schema.Type<typeof DependencyTableRowSchema> {
1163
+ }
1164
+
1165
+ /**
1166
+ * Schema for a single dependency table row.
1167
+ *
1168
+ * @remarks
1169
+ * Represents one row of a dependency update table in a CHANGELOG.
1170
+ * Each row captures the dependency name, its type, the change action,
1171
+ * and the "from" and "to" version strings. For added dependencies the
1172
+ * `from` field is an em dash; for removed dependencies the `to` field
1173
+ * is an em dash.
1174
+ *
1175
+ * @example
1176
+ * ```typescript
1177
+ * import { Schema } from "effect";
1178
+ * import { DependencyTableRowSchema } from "@savvy-web/changesets";
1179
+ * import type { DependencyTableRow } from "@savvy-web/changesets";
1180
+ *
1181
+ * const row: DependencyTableRow = Schema.decodeUnknownSync(DependencyTableRowSchema)({
1182
+ * dependency: "effect",
1183
+ * type: "dependency",
1184
+ * action: "updated",
1185
+ * from: "3.18.0",
1186
+ * to: "3.19.1",
1187
+ * });
1188
+ *
1189
+ * // Newly added dependency — "from" is em dash
1190
+ * const addedRow: DependencyTableRow = Schema.decodeUnknownSync(DependencyTableRowSchema)({
1191
+ * dependency: "@effect/cli",
1192
+ * type: "dependency",
1193
+ * action: "added",
1194
+ * from: "\u2014",
1195
+ * to: "0.50.0",
1196
+ * });
1197
+ * ```
1198
+ *
1199
+ * @see {@link DependencyTableRow} for the inferred TypeScript type
1200
+ * @see {@link DependencyTableSchema} for a non-empty array of rows
1201
+ *
1202
+ * @public
1203
+ */
1204
+ export declare const DependencyTableRowSchema: Schema.Struct<{
1205
+ /** Package or toolchain name. */
1206
+ dependency: Schema.filter<typeof Schema.String>;
1207
+ /** Dependency type. */
1208
+ type: Schema.Literal<["dependency", "devDependency", "peerDependency", "optionalDependency", "workspace", "config"]>;
1209
+ /** Change action. */
1210
+ action: Schema.Literal<["added", "updated", "removed"]>;
1211
+ /** Previous version (em dash for added). */
1212
+ from: Schema.filter<typeof Schema.String>;
1213
+ /** New version (em dash for removed). */
1214
+ to: Schema.filter<typeof Schema.String>;
1215
+ }>;
1216
+
1217
+ /**
1218
+ * Schema for a dependency table (non-empty array of rows).
1219
+ *
1220
+ * @remarks
1221
+ * Validates that the table contains at least one row. Used to represent
1222
+ * the full dependency update table in a changeset or CHANGELOG entry.
1223
+ *
1224
+ * @example
1225
+ * ```typescript
1226
+ * import { Schema } from "effect";
1227
+ * import { DependencyTableSchema } from "@savvy-web/changesets";
1228
+ *
1229
+ * const table = Schema.decodeUnknownSync(DependencyTableSchema)([
1230
+ * {
1231
+ * dependency: "effect",
1232
+ * type: "dependency",
1233
+ * action: "updated",
1234
+ * from: "3.18.0",
1235
+ * to: "3.19.1",
1236
+ * },
1237
+ * {
1238
+ * dependency: "typescript",
1239
+ * type: "config",
1240
+ * action: "updated",
1241
+ * from: "5.6.0",
1242
+ * to: "5.7.2",
1243
+ * },
1244
+ * ]);
1245
+ *
1246
+ * // Throws ParseError — empty array
1247
+ * Schema.decodeUnknownSync(DependencyTableSchema)([]);
1248
+ * ```
1249
+ *
1250
+ * @see {@link DependencyTableRowSchema} for the individual row schema
1251
+ *
1252
+ * @public
1253
+ */
1254
+ export declare const DependencyTableSchema: Schema.filter<Schema.Array$<Schema.Struct<{
1255
+ /** Package or toolchain name. */
1256
+ dependency: Schema.filter<typeof Schema.String>;
1257
+ /** Dependency type. */
1258
+ type: Schema.Literal<["dependency", "devDependency", "peerDependency", "optionalDependency", "workspace", "config"]>;
1259
+ /** Change action. */
1260
+ action: Schema.Literal<["added", "updated", "removed"]>;
1261
+ /** Previous version (em dash for added). */
1262
+ from: Schema.filter<typeof Schema.String>;
1263
+ /** New version (em dash for removed). */
1264
+ to: Schema.filter<typeof Schema.String>;
1265
+ }>>>;
1266
+
1267
+ /**
1268
+ * Inferred type for {@link DependencyTableTypeSchema}.
1269
+ *
1270
+ * @remarks
1271
+ * One of `"dependency"`, `"devDependency"`, `"peerDependency"`,
1272
+ * `"optionalDependency"`, `"workspace"`, or `"config"`.
1273
+ *
1274
+ * @public
1275
+ */
1276
+ export declare type DependencyTableType = typeof DependencyTableTypeSchema.Type;
1277
+
1278
+ /**
1279
+ * Extended dependency types for table format.
1280
+ *
1281
+ * @remarks
1282
+ * Unlike {@link DependencyTypeSchema} (which uses plural npm field names like
1283
+ * `"dependencies"`), this schema uses singular forms (`"dependency"`) and adds
1284
+ * two additional types: `"workspace"` for monorepo workspace references and
1285
+ * `"config"` for configuration toolchain updates (e.g., ESLint, TypeScript).
1286
+ *
1287
+ * @example
1288
+ * ```typescript
1289
+ * import { Schema } from "effect";
1290
+ * import { DependencyTableTypeSchema } from "@savvy-web/changesets";
1291
+ * import type { DependencyTableType } from "@savvy-web/changesets";
1292
+ *
1293
+ * const tableType: DependencyTableType = Schema.decodeUnknownSync(
1294
+ * DependencyTableTypeSchema
1295
+ * )("workspace");
1296
+ * ```
1297
+ *
1298
+ * @see {@link DependencyTypeSchema} for the plural npm-field variant
1299
+ *
1300
+ * @public
1301
+ */
1302
+ export declare const DependencyTableTypeSchema: Schema.Literal<["dependency", "devDependency", "peerDependency", "optionalDependency", "workspace", "config"]>;
1303
+
1304
+ /**
1305
+ * Inferred type for {@link DependencyTypeSchema}.
1306
+ *
1307
+ * @remarks
1308
+ * One of `"dependencies"`, `"devDependencies"`, `"peerDependencies"`,
1309
+ * or `"optionalDependencies"`.
1310
+ *
1311
+ * @public
1312
+ */
1313
+ export declare type DependencyType = typeof DependencyTypeSchema.Type;
1314
+
1315
+ /**
1316
+ * Schema for npm dependency types.
1317
+ *
1318
+ * @remarks
1319
+ * Represents the four standard `package.json` dependency fields using their
1320
+ * plural key names as they appear in the manifest. For the singular,
1321
+ * table-oriented variant that includes `workspace` and `config` types,
1322
+ * see {@link DependencyTableTypeSchema}.
1323
+ *
1324
+ * @example
1325
+ * ```typescript
1326
+ * import { Schema } from "effect";
1327
+ * import { DependencyTypeSchema } from "@savvy-web/changesets";
1328
+ * import type { DependencyType } from "@savvy-web/changesets";
1329
+ *
1330
+ * const depType: DependencyType = Schema.decodeUnknownSync(DependencyTypeSchema)(
1331
+ * "devDependencies"
1332
+ * );
1333
+ * ```
1334
+ *
1335
+ * @see {@link DependencyTableTypeSchema} for the extended singular-form variant
1336
+ *
1337
+ * @public
1338
+ */
1339
+ export declare const DependencyTypeSchema: Schema.Literal<["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]>;
1340
+
1341
+ /**
1342
+ * Inferred type for {@link DependencyUpdateSchema}.
1343
+ *
1344
+ * @remarks
1345
+ * Use this interface when you need to type a variable or parameter as a
1346
+ * decoded dependency update object.
1347
+ *
1348
+ * @public
1349
+ */
1350
+ export declare interface DependencyUpdate extends Schema.Schema.Type<typeof DependencyUpdateSchema> {
1351
+ }
1352
+
1353
+ /**
1354
+ * Schema for a dependency update entry.
1355
+ *
1356
+ * @remarks
1357
+ * Represents a single dependency version change as reported by
1358
+ * the Changesets API. Captures the package name, which dependency
1359
+ * field it belongs to, and the old and new version strings.
1360
+ *
1361
+ * @example
1362
+ * ```typescript
1363
+ * import { Schema } from "effect";
1364
+ * import { DependencyUpdateSchema } from "@savvy-web/changesets";
1365
+ * import type { DependencyUpdate } from "@savvy-web/changesets";
1366
+ *
1367
+ * const update: DependencyUpdate = Schema.decodeUnknownSync(DependencyUpdateSchema)({
1368
+ * name: "effect",
1369
+ * type: "dependencies",
1370
+ * oldVersion: "3.18.0",
1371
+ * newVersion: "3.19.1",
1372
+ * });
1373
+ * ```
1374
+ *
1375
+ * @see {@link DependencyUpdate} for the inferred TypeScript type
1376
+ * @see {@link DependencyTableRowSchema} for the table-formatted variant
1377
+ *
1378
+ * @public
1379
+ */
1380
+ export declare const DependencyUpdateSchema: Schema.Struct<{
1381
+ /** Package name (must be non-empty). */
1382
+ name: Schema.refine<string, typeof Schema.String>;
1383
+ /** npm dependency type. */
1384
+ type: Schema.Literal<["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]>;
1385
+ /** Previous version string. */
1386
+ oldVersion: typeof Schema.String;
1387
+ /** New version string. */
1388
+ newVersion: typeof Schema.String;
1389
+ }>;
1390
+
1391
+ /**
1392
+ * GitHub API request failure.
1393
+ *
1394
+ * @remarks
1395
+ * Raised when a call to the GitHub API (via `\@changesets/get-github-info`)
1396
+ * fails due to network issues, authentication errors, rate limiting, or
1397
+ * server errors. Use the {@link GitHubApiError.isRetryable | isRetryable}
1398
+ * property to determine whether a retry strategy should be applied.
1399
+ * Rate-limited responses (403/429) and server errors (5xx) are considered
1400
+ * retryable.
1401
+ *
1402
+ * @example
1403
+ * ```typescript
1404
+ * import { Effect, Schedule } from "effect";
1405
+ * import { GitHubApiError } from "@savvy-web/changesets";
1406
+ *
1407
+ * declare const program: Effect.Effect<void, GitHubApiError>;
1408
+ *
1409
+ * const withRetry = program.pipe(
1410
+ * Effect.catchTag("GitHubApiError", (err) => {
1411
+ * if (err.isRetryable) {
1412
+ * return Effect.retry(program, Schedule.exponential("1 second"));
1413
+ * }
1414
+ * return Effect.fail(err);
1415
+ * }),
1416
+ * );
1417
+ * ```
1418
+ *
1419
+ * @see {@link GitHubService} for the Effect service that may produce this error
1420
+ *
1421
+ * @public
1422
+ */
1423
+ export declare class GitHubApiError extends GitHubApiErrorBase<{
1424
+ /** The API operation that failed (e.g., `"getInfo"`). */
1425
+ readonly operation: string;
1426
+ /** HTTP status code, if available. */
1427
+ readonly statusCode?: number | undefined;
1428
+ /** Human-readable failure reason. */
1429
+ readonly reason: string;
1430
+ }> {
1431
+ get message(): string;
1432
+ /** Whether this error is a rate-limit response (403 or 429). */
1433
+ get isRateLimited(): boolean;
1434
+ /** Whether this error is eligible for retry (server errors or rate limits). */
493
1435
  get isRetryable(): boolean;
494
1436
  }
495
1437
 
496
1438
  /**
497
- * Base class for GitHubApiError.
1439
+ * Base class for {@link GitHubApiError}.
498
1440
  *
499
1441
  * @privateRemarks
500
1442
  * This export is required for api-extractor documentation generation.
@@ -510,6 +1452,27 @@ export declare const GitHubApiErrorBase: new <A extends Record<string, any> = {}
510
1452
  /**
511
1453
  * Structured result from the GitHub commit info API.
512
1454
  *
1455
+ * @remarks
1456
+ * Represents the data returned by `\@changesets/get-github-info` for a
1457
+ * single commit. Includes the commit author's GitHub username, the
1458
+ * associated pull request number (if any), and pre-formatted markdown
1459
+ * links for use in changelog entries.
1460
+ *
1461
+ * @example
1462
+ * ```typescript
1463
+ * import type { GitHubCommitInfo } from "\@savvy-web/changesets";
1464
+ *
1465
+ * const info: GitHubCommitInfo = {
1466
+ * user: "octocat",
1467
+ * pull: 42,
1468
+ * links: {
1469
+ * commit: "[`abc1234`](https://github.com/owner/repo/commit/abc1234)",
1470
+ * pull: "[#42](https://github.com/owner/repo/pull/42)",
1471
+ * user: "[\@octocat](https://github.com/octocat)",
1472
+ * },
1473
+ * };
1474
+ * ```
1475
+ *
513
1476
  * @public
514
1477
  */
515
1478
  export declare interface GitHubCommitInfo {
@@ -531,13 +1494,48 @@ export declare interface GitHubCommitInfo {
531
1494
  /**
532
1495
  * Inferred type for {@link GitHubInfoSchema}.
533
1496
  *
1497
+ * @remarks
1498
+ * Contains optional `user` (GitHub username), optional `pull` (PR number),
1499
+ * and a required `links` object with pre-formatted commit, pull, and user
1500
+ * links.
1501
+ *
534
1502
  * @public
535
1503
  */
536
1504
  export declare interface GitHubInfo extends Schema.Schema.Type<typeof GitHubInfoSchema> {
537
1505
  }
538
1506
 
539
1507
  /**
540
- * Schema for a GitHub info response from \@changesets/get-github-info.
1508
+ * Schema for a GitHub info response from `\@changesets/get-github-info`.
1509
+ *
1510
+ * @remarks
1511
+ * Represents the structured data returned when querying GitHub for commit
1512
+ * metadata. The `user` and `pull` fields are optional because not every
1513
+ * commit is associated with a pull request or a known GitHub user (e.g.,
1514
+ * bot commits or squash-merged commits without a linked PR).
1515
+ *
1516
+ * The `links` object contains pre-formatted markdown or URL strings for
1517
+ * the commit, pull request, and user profile -- ready for insertion into
1518
+ * CHANGELOG entries.
1519
+ *
1520
+ * @example
1521
+ * ```typescript
1522
+ * import { Schema } from "effect";
1523
+ * import { GitHubInfoSchema } from "@savvy-web/changesets";
1524
+ * import type { GitHubInfo } from "@savvy-web/changesets";
1525
+ *
1526
+ * const info: GitHubInfo = Schema.decodeUnknownSync(GitHubInfoSchema)({
1527
+ * user: "octocat",
1528
+ * pull: 42,
1529
+ * links: {
1530
+ * commit: "[`a1b2c3d`](https://github.com/owner/repo/commit/a1b2c3d)",
1531
+ * pull: "[#42](https://github.com/owner/repo/pull/42)",
1532
+ * user: "[@octocat](https://github.com/octocat)",
1533
+ * },
1534
+ * });
1535
+ * ```
1536
+ *
1537
+ * @see {@link GitHubInfo} for the inferred TypeScript type
1538
+ * @see {@link GitHubService} for the Effect service that produces these values
541
1539
  *
542
1540
  * @public
543
1541
  */
@@ -558,17 +1556,91 @@ export declare const GitHubInfoSchema: Schema.Struct<{
558
1556
  }>;
559
1557
 
560
1558
  /**
561
- * Live layer that delegates to \@changesets/get-github-info.
1559
+ * Production layer for {@link GitHubService}.
1560
+ *
1561
+ * Delegates to `\@changesets/get-github-info` to fetch commit metadata
1562
+ * from the GitHub REST API. Requires a `GITHUB_TOKEN` environment variable
1563
+ * to be set for authenticated requests.
1564
+ *
1565
+ * @remarks
1566
+ * This layer is used by the `\@savvy-web/changesets/changelog` entry point
1567
+ * to resolve commit hashes into PR numbers and author attribution. It is
1568
+ * composed with {@link MarkdownLive} in the changelog formatter's
1569
+ * `MainLayer`.
1570
+ *
1571
+ * @example
1572
+ * ```typescript
1573
+ * import { Effect } from "effect";
1574
+ * import { GitHubService, GitHubLive } from "\@savvy-web/changesets";
1575
+ *
1576
+ * const program = Effect.gen(function* () {
1577
+ * const github = yield* GitHubService;
1578
+ * return yield* github.getInfo({ commit: "abc1234", repo: "owner/repo" });
1579
+ * });
1580
+ *
1581
+ * Effect.runPromise(program.pipe(Effect.provide(GitHubLive)));
1582
+ * ```
562
1583
  *
563
1584
  * @public
564
1585
  */
565
1586
  export declare const GitHubLive: Layer.Layer<GitHubService, never, never>;
566
1587
 
567
1588
  /**
568
- * Service for GitHub API operations.
1589
+ * Effect service tag for GitHub API operations.
1590
+ *
1591
+ * Provides dependency-injected access to GitHub commit metadata lookups.
1592
+ * Use `yield* GitHubService` inside an `Effect.gen` block to obtain the
1593
+ * service instance.
1594
+ *
1595
+ * @remarks
1596
+ * This tag follows the standard Effect `Context.Tag` pattern. Two layers
1597
+ * are provided out of the box:
1598
+ *
1599
+ * - {@link GitHubLive} — production layer backed by the GitHub REST API
1600
+ * - {@link makeGitHubTest} — factory for deterministic test layers
1601
+ *
1602
+ * @example
1603
+ * ```typescript
1604
+ * import { Effect, Layer } from "effect";
1605
+ * import { GitHubService, GitHubLive } from "\@savvy-web/changesets";
1606
+ *
1607
+ * const program = Effect.gen(function* () {
1608
+ * const github = yield* GitHubService;
1609
+ * const info = yield* github.getInfo({
1610
+ * commit: "abc1234567890",
1611
+ * repo: "savvy-web/changesets",
1612
+ * });
1613
+ * console.log(info.user, info.pull, info.links);
1614
+ * });
569
1615
  *
1616
+ * // Provide the live layer and run
1617
+ * Effect.runPromise(program.pipe(Effect.provide(GitHubLive)));
1618
+ * ```
1619
+ *
1620
+ * @example Creating a test layer with canned responses
1621
+ * ```typescript
1622
+ * import { Effect } from "effect";
1623
+ * import type { GitHubCommitInfo } from "\@savvy-web/changesets";
1624
+ * import { GitHubService, makeGitHubTest } from "\@savvy-web/changesets";
1625
+ *
1626
+ * const testResponses = new Map<string, GitHubCommitInfo>([
1627
+ * ["abc1234", { user: "octocat", pull: 42, links: { pull: "#42", user: "\@octocat" } }],
1628
+ * ]);
1629
+ *
1630
+ * const TestLayer = makeGitHubTest(testResponses);
1631
+ *
1632
+ * const program = Effect.gen(function* () {
1633
+ * const github = yield* GitHubService;
1634
+ * return yield* github.getInfo({ commit: "abc1234", repo: "owner/repo" });
1635
+ * });
1636
+ *
1637
+ * Effect.runPromise(program.pipe(Effect.provide(TestLayer)));
1638
+ * ```
1639
+ *
1640
+ * @see {@link GitHubServiceShape} for the service interface
570
1641
  * @see {@link GitHubLive} for the production layer
571
1642
  * @see {@link makeGitHubTest} for creating test layers
1643
+ * @see {@link GitHubServiceBase} for the api-extractor base class
572
1644
  *
573
1645
  * @public
574
1646
  */
@@ -590,10 +1662,25 @@ export declare const GitHubServiceBase: Context.TagClass<GitHubService, "GitHubS
590
1662
  /**
591
1663
  * Service interface for GitHub API operations.
592
1664
  *
593
- * @internal
1665
+ * Describes the single `getInfo` operation that resolves a commit hash to
1666
+ * its associated GitHub metadata (pull-request number, author, and links).
1667
+ *
1668
+ * @remarks
1669
+ * The `getInfo` method may fail with a {@link GitHubApiError} when the
1670
+ * GitHub API is unreachable or the commit is not found. Callers should
1671
+ * handle this error channel — the changelog formatters recover gracefully
1672
+ * by omitting attribution when the call fails.
1673
+ *
1674
+ * @public
594
1675
  */
595
1676
  export declare interface GitHubServiceShape {
596
- /** Fetch commit info (author, PR, links) for a given commit. */
1677
+ /**
1678
+ * Fetch commit metadata from the GitHub API.
1679
+ *
1680
+ * @param params - The commit hash and repository identifier. Must include
1681
+ * `commit` (full SHA-1 hash) and `repo` (in `owner/repo` format).
1682
+ * @returns An `Effect` that resolves to {@link GitHubCommitInfo} or fails with {@link GitHubApiError}
1683
+ */
597
1684
  readonly getInfo: (params: {
598
1685
  commit: string;
599
1686
  repo: string;
@@ -603,6 +1690,25 @@ export declare interface GitHubServiceShape {
603
1690
  /**
604
1691
  * Schema for a GitHub issue or PR number (positive integer).
605
1692
  *
1693
+ * @remarks
1694
+ * Built on {@link PositiveInteger}, this schema adds GitHub-specific
1695
+ * annotations for documentation tooling. Issue and PR numbers in GitHub
1696
+ * are always positive integers starting from 1.
1697
+ *
1698
+ * @example
1699
+ * ```typescript
1700
+ * import { Schema } from "effect";
1701
+ * import { IssueNumberSchema } from "@savvy-web/changesets";
1702
+ *
1703
+ * // Succeeds
1704
+ * const prNum = Schema.decodeUnknownSync(IssueNumberSchema)(42);
1705
+ *
1706
+ * // Throws ParseError — zero is not a valid issue number
1707
+ * Schema.decodeUnknownSync(IssueNumberSchema)(0);
1708
+ * ```
1709
+ *
1710
+ * @see {@link GitHubInfoSchema} which uses this for the `pull` field
1711
+ *
606
1712
  * @public
607
1713
  */
608
1714
  export declare const IssueNumberSchema: Schema.refine<number, Schema.filter<typeof Schema.Number>>;
@@ -610,8 +1716,29 @@ export declare const IssueNumberSchema: Schema.refine<number, Schema.filter<type
610
1716
  /**
611
1717
  * Schema for a JSONPath expression starting with `$.`.
612
1718
  *
1719
+ * @remarks
613
1720
  * Supports property access (`$.foo.bar`), array wildcard (`$.foo[*].bar`),
614
- * and array index access (`$.foo[0].bar`).
1721
+ * and array index access (`$.foo[0].bar`). The expression must begin with
1722
+ * `$.` followed by at least one property segment.
1723
+ *
1724
+ * @example
1725
+ * ```typescript
1726
+ * import { Schema } from "effect";
1727
+ * import { JsonPathSchema } from "@savvy-web/changesets";
1728
+ *
1729
+ * // Succeeds — simple property access
1730
+ * Schema.decodeUnknownSync(JsonPathSchema)("$.version");
1731
+ *
1732
+ * // Succeeds — nested property access
1733
+ * Schema.decodeUnknownSync(JsonPathSchema)("$.metadata.version");
1734
+ *
1735
+ * // Throws ParseError — missing "$." prefix
1736
+ * Schema.decodeUnknownSync(JsonPathSchema)("version");
1737
+ * ```
1738
+ *
1739
+ * @see {@link VersionFileConfigSchema} which uses this for the `paths` field
1740
+ *
1741
+ * @public
615
1742
  */
616
1743
  export declare const JsonPathSchema: Schema.filter<typeof Schema.String>;
617
1744
 
@@ -619,38 +1746,141 @@ export declare const JsonPathSchema: Schema.filter<typeof Schema.String>;
619
1746
  * Class-based API wrapper for changeset linting.
620
1747
  *
621
1748
  * Provides a static class interface that runs all remark-lint rules
622
- * against changeset markdown files.
1749
+ * against changeset markdown files and returns structured diagnostics.
1750
+ *
1751
+ * @internal
623
1752
  */
624
1753
  /**
625
- * A single lint message from validation.
1754
+ * A single lint diagnostic message produced by changeset validation.
1755
+ *
1756
+ * @remarks
1757
+ * Each `LintMessage` corresponds to one remark-lint rule violation found
1758
+ * during changeset validation. Messages include source location information
1759
+ * (file, line, column) for integration with editors, CI reporters, and
1760
+ * the Effect CLI's `lint` and `check` commands.
1761
+ *
1762
+ * The four rules that produce lint messages are:
1763
+ *
1764
+ * - **heading-hierarchy** -- ensures headings follow a valid nesting order
1765
+ * - **required-sections** -- checks that mandatory sections are present
1766
+ * - **content-structure** -- validates the structure of section content
1767
+ * - **uncategorized-content** -- flags content outside recognized section headings
626
1768
  *
627
1769
  * @public
628
1770
  */
629
1771
  export declare interface LintMessage {
630
- /** File path that was validated. */
1772
+ /**
1773
+ * File path that was validated.
1774
+ *
1775
+ * @remarks
1776
+ * Set to the actual filesystem path when using {@link ChangesetLinter.validateFile}
1777
+ * or {@link ChangesetLinter.validate}. Defaults to `"<input>"` when using
1778
+ * {@link ChangesetLinter.validateContent} without an explicit path.
1779
+ */
631
1780
  file: string;
632
- /** Rule name that produced the message. */
1781
+ /**
1782
+ * Identifier of the remark-lint rule that produced this message.
1783
+ *
1784
+ * @remarks
1785
+ * Corresponds to one of the four built-in rules: `"heading-hierarchy"`,
1786
+ * `"required-sections"`, `"content-structure"`, or `"uncategorized-content"`.
1787
+ * Falls back to `"unknown"` if the underlying vfile message has no rule ID.
1788
+ */
633
1789
  rule: string;
634
- /** Line number (1-based). */
1790
+ /**
1791
+ * Line number where the issue was detected (1-based).
1792
+ *
1793
+ * @remarks
1794
+ * Line numbers are relative to the content after YAML frontmatter
1795
+ * stripping. Defaults to `1` if the underlying rule does not provide
1796
+ * position information.
1797
+ */
635
1798
  line: number;
636
- /** Column number (1-based). */
1799
+ /**
1800
+ * Column number where the issue was detected (1-based).
1801
+ *
1802
+ * @remarks
1803
+ * Defaults to `1` if the underlying rule does not provide position
1804
+ * information.
1805
+ */
637
1806
  column: number;
638
- /** Human-readable message. */
1807
+ /**
1808
+ * Human-readable description of the lint violation.
1809
+ *
1810
+ * @remarks
1811
+ * Suitable for display in terminal output, editor diagnostics, or
1812
+ * CI annotations. Includes enough context to understand the issue
1813
+ * without referencing the source file.
1814
+ */
639
1815
  message: string;
640
1816
  }
641
1817
 
642
1818
  /**
643
- * Create a test layer with pre-configured responses keyed by commit hash.
1819
+ * Create a test layer for {@link GitHubService} with pre-configured responses.
1820
+ *
1821
+ * Returns a `Layer` that resolves commit hashes from the provided `Map`.
1822
+ * Lookups for commits not present in the map fail with a
1823
+ * {@link GitHubApiError}.
1824
+ *
1825
+ * @remarks
1826
+ * This helper is the recommended way to test code that depends on
1827
+ * `GitHubService` without making real API calls. Provide the layer
1828
+ * via `Effect.provide` in your test setup.
1829
+ *
1830
+ * @param responses - A `Map` of full commit hash to {@link GitHubCommitInfo} objects
1831
+ * @returns A `Layer` providing the {@link GitHubService} with deterministic responses
1832
+ *
1833
+ * @example
1834
+ * ```typescript
1835
+ * import { Effect } from "effect";
1836
+ * import type { GitHubCommitInfo } from "\@savvy-web/changesets";
1837
+ * import { GitHubService, makeGitHubTest } from "\@savvy-web/changesets";
1838
+ *
1839
+ * const responses = new Map<string, GitHubCommitInfo>([
1840
+ * ["abc1234", { user: "octocat", pull: 42, links: { pull: "#42", user: "\@octocat" } }],
1841
+ * ]);
644
1842
  *
645
- * @param responses - A map of commit hash to {@link GitHubCommitInfo}
646
- * @returns A Layer providing the {@link GitHubService}
1843
+ * const TestGitHub = makeGitHubTest(responses);
1844
+ *
1845
+ * const program = Effect.gen(function* () {
1846
+ * const github = yield* GitHubService;
1847
+ * return yield* github.getInfo({ commit: "abc1234", repo: "owner/repo" });
1848
+ * });
1849
+ *
1850
+ * // In a Vitest test:
1851
+ * const result = await Effect.runPromise(program.pipe(Effect.provide(TestGitHub)));
1852
+ * // result.user === "octocat"
1853
+ * ```
647
1854
  *
648
1855
  * @public
649
1856
  */
650
1857
  export declare function makeGitHubTest(responses: Map<string, GitHubCommitInfo>): Layer.Layer<GitHubService>;
651
1858
 
652
1859
  /**
653
- * Live layer wrapping the remark-pipeline functions.
1860
+ * Production layer for {@link MarkdownService}.
1861
+ *
1862
+ * Wraps the remark-pipeline utility functions (`parseMarkdown` and
1863
+ * `stringifyMarkdown`) in `Effect.sync` for use in Effect programs.
1864
+ * Both operations are synchronous and infallible.
1865
+ *
1866
+ * @remarks
1867
+ * This layer is composed with {@link GitHubLive} in the
1868
+ * `\@savvy-web/changesets/changelog` entry point to form the `MainLayer`
1869
+ * that powers the Changesets API integration.
1870
+ *
1871
+ * @example
1872
+ * ```typescript
1873
+ * import { Effect, Layer } from "effect";
1874
+ * import { MarkdownService, MarkdownLive } from "\@savvy-web/changesets";
1875
+ *
1876
+ * const program = Effect.gen(function* () {
1877
+ * const md = yield* MarkdownService;
1878
+ * const tree = yield* md.parse("**bold** text");
1879
+ * return yield* md.stringify(tree);
1880
+ * });
1881
+ *
1882
+ * Effect.runPromise(program.pipe(Effect.provide(MarkdownLive)));
1883
+ * ```
654
1884
  *
655
1885
  * @public
656
1886
  */
@@ -659,6 +1889,28 @@ export declare const MarkdownLive: Layer.Layer<MarkdownService, never, never>;
659
1889
  /**
660
1890
  * Markdown parsing failure.
661
1891
  *
1892
+ * @remarks
1893
+ * Raised when a markdown file cannot be parsed into an AST by the unified/remark
1894
+ * pipeline. Carries optional source file location information (line, column)
1895
+ * for diagnostic display.
1896
+ *
1897
+ * @example
1898
+ * ```typescript
1899
+ * import { Effect } from "effect";
1900
+ * import { MarkdownParseError } from "@savvy-web/changesets";
1901
+ *
1902
+ * declare const program: Effect.Effect<string, MarkdownParseError>;
1903
+ *
1904
+ * const handled = program.pipe(
1905
+ * Effect.catchTag("MarkdownParseError", (err) => {
1906
+ * console.error(err.message);
1907
+ * return Effect.succeed("");
1908
+ * }),
1909
+ * );
1910
+ * ```
1911
+ *
1912
+ * @see {@link MarkdownService} for the Effect service that may produce this error
1913
+ *
662
1914
  * @public
663
1915
  */
664
1916
  export declare class MarkdownParseError extends MarkdownParseErrorBase<{
@@ -675,7 +1927,7 @@ export declare class MarkdownParseError extends MarkdownParseErrorBase<{
675
1927
  }
676
1928
 
677
1929
  /**
678
- * Base class for MarkdownParseError.
1930
+ * Base class for {@link MarkdownParseError}.
679
1931
  *
680
1932
  * @privateRemarks
681
1933
  * This export is required for api-extractor documentation generation.
@@ -689,9 +1941,36 @@ export declare const MarkdownParseErrorBase: new <A extends Record<string, any>
689
1941
  } & Readonly<A>;
690
1942
 
691
1943
  /**
692
- * Service for markdown parsing and stringification.
1944
+ * Effect service tag for markdown parsing and stringification.
1945
+ *
1946
+ * Provides dependency-injected access to markdown parse/stringify operations.
1947
+ * Use `yield* MarkdownService` inside an `Effect.gen` block to obtain the
1948
+ * service instance.
1949
+ *
1950
+ * @remarks
1951
+ * This tag follows the standard Effect `Context.Tag` pattern. The
1952
+ * {@link MarkdownLive} layer provides the default implementation backed by
1953
+ * the remark/unified pipeline. For testing, you can supply a custom layer
1954
+ * via `Layer.succeed(MarkdownService, { parse: ..., stringify: ... })`.
693
1955
  *
1956
+ * @example
1957
+ * ```typescript
1958
+ * import { Effect } from "effect";
1959
+ * import { MarkdownService, MarkdownLive } from "\@savvy-web/changesets";
1960
+ *
1961
+ * const program = Effect.gen(function* () {
1962
+ * const md = yield* MarkdownService;
1963
+ * const tree = yield* md.parse("# Hello\n\nWorld");
1964
+ * const output = yield* md.stringify(tree);
1965
+ * console.log(output); // "# Hello\n\nWorld\n"
1966
+ * });
1967
+ *
1968
+ * Effect.runPromise(program.pipe(Effect.provide(MarkdownLive)));
1969
+ * ```
1970
+ *
1971
+ * @see {@link MarkdownServiceShape} for the service interface
694
1972
  * @see {@link MarkdownLive} for the production layer
1973
+ * @see {@link MarkdownServiceBase} for the api-extractor base class
695
1974
  *
696
1975
  * @public
697
1976
  */
@@ -713,18 +1992,57 @@ export declare const MarkdownServiceBase: Context.TagClass<MarkdownService, "Mar
713
1992
  /**
714
1993
  * Service interface for markdown parsing and stringification.
715
1994
  *
716
- * @internal
1995
+ * Describes the two operations a `MarkdownService` implementation must
1996
+ * provide: parsing markdown text into an mdast AST and stringifying an
1997
+ * mdast AST back to markdown text.
1998
+ *
1999
+ * @remarks
2000
+ * Both operations are infallible (no error channel) because the underlying
2001
+ * remark pipeline is configured to handle all valid markdown input. The
2002
+ * `Root` type comes from the `mdast` package and represents the root node
2003
+ * of a markdown abstract syntax tree.
2004
+ *
2005
+ * @public
717
2006
  */
718
2007
  export declare interface MarkdownServiceShape {
719
- /** Parse a markdown string into an mdast AST. */
2008
+ /**
2009
+ * Parse a markdown string into an mdast abstract syntax tree.
2010
+ *
2011
+ * @param content - Raw markdown text to parse
2012
+ * @returns An `Effect` that resolves to an mdast `Root` node
2013
+ */
720
2014
  readonly parse: (content: string) => Effect.Effect<Root>;
721
- /** Stringify an mdast AST back to markdown. */
2015
+ /**
2016
+ * Stringify an mdast abstract syntax tree back to markdown text.
2017
+ *
2018
+ * @param tree - The mdast `Root` node to serialize
2019
+ * @returns An `Effect` that resolves to a markdown string
2020
+ */
722
2021
  readonly stringify: (tree: Root) => Effect.Effect<string>;
723
2022
  }
724
2023
 
725
2024
  /**
726
2025
  * A non-empty string schema.
727
2026
  *
2027
+ * @remarks
2028
+ * Validates that a string has at least one character. Used as the base
2029
+ * constraint for package names, dependency identifiers, and other fields
2030
+ * that must not be blank.
2031
+ *
2032
+ * @example
2033
+ * ```typescript
2034
+ * import { Schema } from "effect";
2035
+ * import { NonEmptyString } from "@savvy-web/changesets";
2036
+ *
2037
+ * // Succeeds — non-empty string
2038
+ * const name = Schema.decodeUnknownSync(NonEmptyString)("react");
2039
+ *
2040
+ * // Throws ParseError — empty string
2041
+ * Schema.decodeUnknownSync(NonEmptyString)("");
2042
+ * ```
2043
+ *
2044
+ * @see {@link ChangesetSummarySchema} for a length-bounded variant used for changeset summaries
2045
+ *
728
2046
  * @public
729
2047
  */
730
2048
  export declare const NonEmptyString: Schema.filter<typeof Schema.String>;
@@ -732,6 +2050,28 @@ export declare const NonEmptyString: Schema.filter<typeof Schema.String>;
732
2050
  /**
733
2051
  * A positive integer schema.
734
2052
  *
2053
+ * @remarks
2054
+ * Validates that a number is both an integer and strictly greater than zero.
2055
+ * Used as the base constraint for GitHub issue numbers and similar
2056
+ * identifiers that must be positive whole numbers.
2057
+ *
2058
+ * @example
2059
+ * ```typescript
2060
+ * import { Schema } from "effect";
2061
+ * import { PositiveInteger } from "@savvy-web/changesets";
2062
+ *
2063
+ * // Succeeds — positive integer
2064
+ * const issueNum = Schema.decodeUnknownSync(PositiveInteger)(42);
2065
+ *
2066
+ * // Throws ParseError — zero is not positive
2067
+ * Schema.decodeUnknownSync(PositiveInteger)(0);
2068
+ *
2069
+ * // Throws ParseError — floats are not integers
2070
+ * Schema.decodeUnknownSync(PositiveInteger)(3.14);
2071
+ * ```
2072
+ *
2073
+ * @see {@link IssueNumberSchema} which builds on this for GitHub issue/PR numbers
2074
+ *
735
2075
  * @public
736
2076
  */
737
2077
  export declare const PositiveInteger: Schema.filter<Schema.filter<typeof Schema.Number>>;
@@ -739,15 +2079,35 @@ export declare const PositiveInteger: Schema.filter<Schema.filter<typeof Schema.
739
2079
  /**
740
2080
  * Schema for a GitHub repository in `owner/repo` format.
741
2081
  *
2082
+ * @remarks
2083
+ * Validates that the string matches the `owner/repository` format used
2084
+ * by GitHub. Both the owner and repository segments accept alphanumeric
2085
+ * characters, dots, underscores, and hyphens.
2086
+ *
2087
+ * @example
2088
+ * ```typescript
2089
+ * import { Schema } from "effect";
2090
+ * import { RepoSchema } from "@savvy-web/changesets";
2091
+ *
2092
+ * // Succeeds
2093
+ * Schema.decodeUnknownSync(RepoSchema)("microsoft/vscode");
2094
+ *
2095
+ * // Throws ParseError — missing slash
2096
+ * Schema.decodeUnknownSync(RepoSchema)("vscode");
2097
+ * ```
2098
+ *
2099
+ * @see {@link ChangesetOptionsSchema} which uses this for the `repo` field
2100
+ *
742
2101
  * @public
743
2102
  */
744
2103
  export declare const RepoSchema: Schema.filter<typeof Schema.String>;
745
2104
 
746
2105
  /**
747
2106
  * A section category defines how changes are grouped in release notes.
748
- * Used across all three processing layers.
749
2107
  *
750
- * Inferred from {@link SectionCategorySchema}.
2108
+ * @remarks
2109
+ * Inferred from {@link SectionCategorySchema}. Use this type to annotate
2110
+ * variables and parameters that hold category data.
751
2111
  *
752
2112
  * @public
753
2113
  */
@@ -756,9 +2116,34 @@ export declare interface SectionCategory extends Schema.Schema.Type<typeof Secti
756
2116
 
757
2117
  /**
758
2118
  * Schema for a section category that defines how changes are grouped in release notes.
759
- * Used across all three processing layers.
760
2119
  *
761
- * Provides runtime validation and type inference via Effect Schema.
2120
+ * @remarks
2121
+ * A section category has four fields:
2122
+ * - `heading` -- the display heading used in CHANGELOG output (e.g., `"Features"`, `"Bug Fixes"`)
2123
+ * - `priority` -- an integer controlling display order (lower = higher priority)
2124
+ * - `commitTypes` -- conventional commit type prefixes that map to this category (e.g., `["feat"]`)
2125
+ * - `description` -- a brief human-readable description for documentation
2126
+ *
2127
+ * Categories with an empty `commitTypes` array are resolved through other means:
2128
+ * `BREAKING_CHANGES` is resolved via the `!` suffix on any commit type, and
2129
+ * `OTHER` is the fallback for unrecognized types.
2130
+ *
2131
+ * @example
2132
+ * ```typescript
2133
+ * import { Schema } from "effect";
2134
+ * import { SectionCategorySchema } from "@savvy-web/changesets";
2135
+ * import type { SectionCategory } from "@savvy-web/changesets";
2136
+ *
2137
+ * const category: SectionCategory = Schema.decodeUnknownSync(SectionCategorySchema)({
2138
+ * heading: "Features",
2139
+ * priority: 2,
2140
+ * commitTypes: ["feat"],
2141
+ * description: "New functionality",
2142
+ * });
2143
+ * ```
2144
+ *
2145
+ * @see {@link SectionCategory} for the inferred TypeScript type
2146
+ * @see {@link CATEGORIES} for all predefined categories
762
2147
  *
763
2148
  * @public
764
2149
  */
@@ -776,7 +2161,33 @@ export declare const SectionCategorySchema: Schema.Struct<{
776
2161
  /**
777
2162
  * Schema accepting either a plain URL or a markdown link `[text](url)`.
778
2163
  *
779
- * Used for GitHub API responses from \@changesets/get-github-info.
2164
+ * @remarks
2165
+ * The `\@changesets/get-github-info` vendor module returns links in two
2166
+ * possible formats depending on context: a bare URL string or a markdown
2167
+ * link like `[#42](https://github.com/owner/repo/pull/42)`. This schema
2168
+ * accepts both, validating that the URL portion is parseable by the
2169
+ * `URL` constructor.
2170
+ *
2171
+ * @example
2172
+ * ```typescript
2173
+ * import { Schema } from "effect";
2174
+ * import { UrlOrMarkdownLinkSchema } from "@savvy-web/changesets";
2175
+ *
2176
+ * // Succeeds — plain URL
2177
+ * Schema.decodeUnknownSync(UrlOrMarkdownLinkSchema)(
2178
+ * "https://github.com/owner/repo/pull/42"
2179
+ * );
2180
+ *
2181
+ * // Succeeds — markdown link
2182
+ * Schema.decodeUnknownSync(UrlOrMarkdownLinkSchema)(
2183
+ * "[#42](https://github.com/owner/repo/pull/42)"
2184
+ * );
2185
+ *
2186
+ * // Throws ParseError — not a URL or markdown link
2187
+ * Schema.decodeUnknownSync(UrlOrMarkdownLinkSchema)("not-a-url");
2188
+ * ```
2189
+ *
2190
+ * @see {@link GitHubInfoSchema} which uses this for commit, pull, and user links
780
2191
  *
781
2192
  * @public
782
2193
  */
@@ -785,7 +2196,25 @@ export declare const UrlOrMarkdownLinkSchema: Schema.filter<typeof Schema.String
785
2196
  /**
786
2197
  * Schema for a GitHub username.
787
2198
  *
788
- * Rules: alphanumeric and hyphens, cannot start/end with hyphen.
2199
+ * @remarks
2200
+ * Validates against GitHub's username rules: alphanumeric characters and
2201
+ * hyphens only, cannot start or end with a hyphen. Does not enforce the
2202
+ * 39-character maximum length since GitHub may change that limit.
2203
+ *
2204
+ * @example
2205
+ * ```typescript
2206
+ * import { Schema } from "effect";
2207
+ * import { UsernameSchema } from "@savvy-web/changesets";
2208
+ *
2209
+ * // Succeeds
2210
+ * Schema.decodeUnknownSync(UsernameSchema)("octocat");
2211
+ * Schema.decodeUnknownSync(UsernameSchema)("my-user-123");
2212
+ *
2213
+ * // Throws ParseError — starts with hyphen
2214
+ * Schema.decodeUnknownSync(UsernameSchema)("-invalid");
2215
+ * ```
2216
+ *
2217
+ * @see {@link GitHubInfoSchema} which uses this for the `user` field
789
2218
  *
790
2219
  * @public
791
2220
  */
@@ -793,12 +2222,43 @@ export declare const UsernameSchema: Schema.filter<typeof Schema.String>;
793
2222
 
794
2223
  /**
795
2224
  * Inferred type for {@link VersionFileConfigSchema}.
2225
+ *
2226
+ * @public
796
2227
  */
797
2228
  export declare interface VersionFileConfig extends Schema.Schema.Type<typeof VersionFileConfigSchema> {
798
2229
  }
799
2230
 
800
2231
  /**
801
2232
  * Schema for a single version file configuration entry.
2233
+ *
2234
+ * @remarks
2235
+ * Each entry specifies a glob pattern that matches one or more JSON files
2236
+ * and an optional array of JSONPath expressions indicating which fields
2237
+ * within those files contain version numbers. When `paths` is omitted,
2238
+ * it defaults to `["$.version"]` at runtime.
2239
+ *
2240
+ * @example
2241
+ * ```typescript
2242
+ * import { Schema } from "effect";
2243
+ * import { VersionFileConfigSchema } from "@savvy-web/changesets";
2244
+ * import type { VersionFileConfig } from "@savvy-web/changesets";
2245
+ *
2246
+ * const config: VersionFileConfig = Schema.decodeUnknownSync(VersionFileConfigSchema)({
2247
+ * glob: "manifest.json",
2248
+ * paths: ["$.version", "$.metadata.version"],
2249
+ * });
2250
+ *
2251
+ * // Paths are optional — defaults to ["$.version"] at runtime
2252
+ * const simpleConfig: VersionFileConfig = Schema.decodeUnknownSync(VersionFileConfigSchema)({
2253
+ * glob: "**\/deno.json",
2254
+ * });
2255
+ * ```
2256
+ *
2257
+ * @see {@link VersionFileConfig} for the inferred TypeScript type
2258
+ * @see {@link VersionFilesSchema} for the array of entries
2259
+ * @see {@link JsonPathSchema} for JSONPath validation rules
2260
+ *
2261
+ * @public
802
2262
  */
803
2263
  export declare const VersionFileConfigSchema: Schema.Struct<{
804
2264
  /** Glob pattern to match JSON files. */
@@ -810,8 +2270,32 @@ export declare const VersionFileConfigSchema: Schema.Struct<{
810
2270
  /**
811
2271
  * Version file update failure.
812
2272
  *
813
- * Raised when a JSON file targeted by the `versionFiles` config cannot
814
- * be read, parsed, or updated at the specified JSONPath.
2273
+ * @remarks
2274
+ * Raised when a JSON file targeted by the `versionFiles` configuration option
2275
+ * cannot be read, parsed, or updated at the specified JSONPath. Common causes
2276
+ * include missing files, invalid JSON, or JSONPath expressions that do not
2277
+ * match any field in the target file.
2278
+ *
2279
+ * @example
2280
+ * ```typescript
2281
+ * import { Effect } from "effect";
2282
+ * import { VersionFileError } from "@savvy-web/changesets";
2283
+ *
2284
+ * declare const program: Effect.Effect<void, VersionFileError>;
2285
+ *
2286
+ * const handled = program.pipe(
2287
+ * Effect.catchTag("VersionFileError", (err) => {
2288
+ * console.error(`Failed to update ${err.filePath}: ${err.reason}`);
2289
+ * if (err.jsonPath) {
2290
+ * console.error(` at JSONPath: ${err.jsonPath}`);
2291
+ * }
2292
+ * return Effect.void;
2293
+ * }),
2294
+ * );
2295
+ * ```
2296
+ *
2297
+ * @see {@link VersionFilesSchema} for the configuration that drives version file updates
2298
+ * @see {@link JsonPathSchema} for JSONPath expression validation
815
2299
  *
816
2300
  * @public
817
2301
  */
@@ -827,7 +2311,7 @@ export declare class VersionFileError extends VersionFileErrorBase<{
827
2311
  }
828
2312
 
829
2313
  /**
830
- * Base class for VersionFileError.
2314
+ * Base class for {@link VersionFileError}.
831
2315
  *
832
2316
  * @privateRemarks
833
2317
  * This export is required for api-extractor documentation generation.
@@ -842,6 +2326,26 @@ export declare const VersionFileErrorBase: new <A extends Record<string, any> =
842
2326
 
843
2327
  /**
844
2328
  * Schema for the `versionFiles` array.
2329
+ *
2330
+ * @remarks
2331
+ * An array of {@link VersionFileConfigSchema} entries. Each entry targets
2332
+ * a set of JSON files via glob and specifies which JSONPath expressions
2333
+ * point to version fields that should be updated.
2334
+ *
2335
+ * @example
2336
+ * ```typescript
2337
+ * import { Schema } from "effect";
2338
+ * import { VersionFilesSchema } from "@savvy-web/changesets";
2339
+ *
2340
+ * const versionFiles = Schema.decodeUnknownSync(VersionFilesSchema)([
2341
+ * { glob: "manifest.json", paths: ["$.version"] },
2342
+ * { glob: "deno.json" },
2343
+ * ]);
2344
+ * ```
2345
+ *
2346
+ * @see {@link ChangesetOptionsSchema} where this is used as an optional field
2347
+ *
2348
+ * @public
845
2349
  */
846
2350
  export declare const VersionFilesSchema: Schema.Array$<Schema.Struct<{
847
2351
  /** Glob pattern to match JSON files. */
@@ -850,9 +2354,40 @@ export declare const VersionFilesSchema: Schema.Array$<Schema.Struct<{
850
2354
  paths: Schema.optional<Schema.Array$<Schema.filter<typeof Schema.String>>>;
851
2355
  }>>;
852
2356
 
2357
+ /**
2358
+ * Version string or em dash (U+2014) sentinel for added/removed entries.
2359
+ *
2360
+ * @remarks
2361
+ * Accepts either a semver-like version string (with optional `~` or `^`
2362
+ * prefix and pre-release/build suffixes) or the em dash character `\u2014`
2363
+ * which serves as a sentinel: the "From" column uses `\u2014` for newly
2364
+ * added dependencies, and the "To" column uses it for removed ones.
2365
+ *
2366
+ * @example
2367
+ * ```typescript
2368
+ * import { Schema } from "effect";
2369
+ * import { VersionOrEmptySchema } from "@savvy-web/changesets";
2370
+ *
2371
+ * // Succeeds — semver version
2372
+ * Schema.decodeUnknownSync(VersionOrEmptySchema)("^3.19.1");
2373
+ *
2374
+ * // Succeeds — em dash sentinel for "no version"
2375
+ * Schema.decodeUnknownSync(VersionOrEmptySchema)("\u2014");
2376
+ *
2377
+ * // Throws ParseError — arbitrary text
2378
+ * Schema.decodeUnknownSync(VersionOrEmptySchema)("latest");
2379
+ * ```
2380
+ *
2381
+ * @public
2382
+ */
2383
+ export declare const VersionOrEmptySchema: Schema.filter<typeof Schema.String>;
2384
+
853
2385
  /**
854
2386
  * Inferred type for {@link VersionTypeSchema}.
855
2387
  *
2388
+ * @remarks
2389
+ * One of `"major"`, `"minor"`, `"patch"`, or `"none"`.
2390
+ *
856
2391
  * @public
857
2392
  */
858
2393
  export declare type VersionType = typeof VersionTypeSchema.Type;
@@ -860,6 +2395,22 @@ export declare type VersionType = typeof VersionTypeSchema.Type;
860
2395
  /**
861
2396
  * Semantic version bump type.
862
2397
  *
2398
+ * @remarks
2399
+ * Represents the four possible version bump levels used by Changesets:
2400
+ * - `"major"` -- breaking changes (e.g., 1.x.x to 2.0.0)
2401
+ * - `"minor"` -- new features (e.g., 1.1.x to 1.2.0)
2402
+ * - `"patch"` -- bug fixes (e.g., 1.1.1 to 1.1.2)
2403
+ * - `"none"` -- no version bump (internal changes only)
2404
+ *
2405
+ * @example
2406
+ * ```typescript
2407
+ * import { Schema } from "effect";
2408
+ * import { VersionTypeSchema } from "@savvy-web/changesets";
2409
+ * import type { VersionType } from "@savvy-web/changesets";
2410
+ *
2411
+ * const bump: VersionType = Schema.decodeUnknownSync(VersionTypeSchema)("minor");
2412
+ * ```
2413
+ *
863
2414
  * @public
864
2415
  */
865
2416
  export declare const VersionTypeSchema: Schema.Literal<["major", "minor", "patch", "none"]>;